I don't know how to prevent model class to update when I don't want to... I pass model class into a modal that contains a number. I'm updating the number there and if I decide to close the modal without saving then I want the number to be unchanged in the main screen so when I reopen the modal the number is back as it was before the update. However, no matter if I save or not save the number is saved.
Here is a simple copy-paste example
class MaterialScreen extends StatefulWidget {
@override
_MaterialScreenState createState() => _MaterialScreenState();
}
NumberClass myNumber = NumberClass(0);
class _MaterialScreenState extends State<MaterialScreen> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
child: Text('Open'),
onPressed: () async {
NumberClass n = await showModalBottomSheet(
context: context,
builder: (_) => Modal(
number: myNumber,
),
);
if (n != null) {
setState(() {
myNumber = n;
});
}
},
),
),
);
}
}
class Modal extends StatefulWidget {
final NumberClass number;
const Modal({this.number});
@override
_ModalState createState() => _ModalState();
}
NumberClass _newNumber;
class _ModalState extends State<Modal> {
@override
void initState() {
_newNumber = widget.number;
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_newNumber.number.toString()),
FlatButton(
child: Text('Update'),
onPressed: () {
setState(() {
_newNumber.number =
_newNumber.number + 1; // update only newNumber
});
print(_newNumber.number);
print(widget.number.number); // <== updating when it should not
},
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
OutlineButton(
child: Text('close - dont save'),
onPressed: () => Navigator.pop(context),
),
OutlineButton(
child: Text('close - save'),
onPressed: () => Navigator.pop(context, _newNumber),
),
],
)
],
);
}
}
class NumberClass {
int number;
NumberClass(this.number);
}
Here is the solution however it is not very nice one. It would definitely be nice to have an option to clone classes.
Change your NumberClass to
class NumberClass {
int number;
NumberClass({this.number});
Map<String, dynamic> toMap() {
return {'number': number};
}
factory NumberClass.fromMap(Map<String, dynamic> data) {
if (data == null) {
return null;
}
final int number = data['number'];
return NumberClass(number: number);
}
}
crate a clone and pass it to your modal class
onPressed: () async {
Map numberMap = myNumber.toMap();
NumberClass clone = NumberClass.fromMap(numberMap);
NumberClass n = await showModalBottomSheet(
context: context,
builder: (_) => Modal(
data: clone,
),
);
full code
class MaterialScreen extends StatefulWidget {
@override
_MaterialScreenState createState() => _MaterialScreenState();
}
NumberClass myNumber = NumberClass(number: 0);
class _MaterialScreenState extends State<MaterialScreen> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
child: Text('Open'),
onPressed: () async {
Map numberMap = myNumber.toMap();
NumberClass clone = NumberClass.fromMap(numberMap);
NumberClass n = await showModalBottomSheet(
context: context,
builder: (_) => Modal(
data: clone,
),
);
if (n != null) {
setState(() {
myNumber = n;
});
}
},
),
),
);
}
}
class Modal extends StatefulWidget {
final NumberClass data;
const Modal({this.data});
@override
_ModalState createState() => _ModalState();
}
NumberClass _newNumber;
class _ModalState extends State<Modal> {
@override
void initState() {
_newNumber = widget.data;
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_newNumber.number.toString()),
FlatButton(
child: Text('Update'),
onPressed: () {
setState(() {
_newNumber.number =
_newNumber.number + 1; // updating only newNumber
});
},
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
OutlineButton(
child: Text('close - dont save'),
onPressed: () => Navigator.pop(context),
),
OutlineButton(
child: Text('close - save'),
onPressed: () => Navigator.pop(context, _newNumber),
),
],
)
],
);
}
}
class NumberClass {
int number;
NumberClass({this.number});
Map<String, dynamic> toMap() {
return {'number': number};
}
factory NumberClass.fromMap(Map<String, dynamic> data) {
if (data == null) {
return null;
}
final int number = data['number'];
return NumberClass(number: number);
}
}
When you pass myNumber in the Modal, you are actually passing it by reference. This means that it is the exact same object as number in Modal and _newNumber in ModalState. Changing the attributes of any one of these will change it for all of them. What you need to do is pass only the value that will be changed into the Modal, and then update the object with the new values if it is returned.
class MaterialScreen extends StatefulWidget {
@override
_MaterialScreenState createState() => _MaterialScreenState();
}
NumberClass myNumber = NumberClass(0);
class _MaterialScreenState extends State<MaterialScreen> {
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: FlatButton(
child: Text('Open'),
onPressed: () async {
int n = await showModalBottomSheet(
context: context,
builder: (_) => Modal(
number: myNumber.number,
),
);
if (n != null) {
setState(() {
myNumber.number = n;
});
}
},
),
),
);
}
}
class Modal extends StatefulWidget {
final int number;
const Modal({this.number});
@override
_ModalState createState() => _ModalState();
}
NumberClass _newNumber;
class _ModalState extends State<Modal> {
@override
void initState() {
_newNumber = widget.number;
super.initState();
}
@override
Widget build(BuildContext context) {
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: <Widget>[
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_newNumber.number.toString()),
FlatButton(
child: Text('Update'),
onPressed: () {
setState(() {
_newNumber = _newNumber + 1;
});
},
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: <Widget>[
OutlineButton(
child: Text('close - dont save'),
onPressed: () => Navigator.pop(context),
),
OutlineButton(
child: Text('close - save'),
onPressed: () => Navigator.pop(context, _newNumber),
),
],
)
],
);
}
}
class NumberClass {
int number;
NumberClass(this.number);
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With