Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Model class updates when it should not

Tags:

flutter

dart

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);
}
like image 846
delmin Avatar asked Dec 06 '25 07:12

delmin


2 Answers

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);
  }
}
like image 75
LonelyWolf Avatar answered Dec 09 '25 16:12

LonelyWolf


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);
}
like image 37
wxker Avatar answered Dec 09 '25 16:12

wxker



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!