1

I have implemented a showModalBottomSheet that calls a stateful widget. I would like for the stateful widget to be able to receive data from the showModalBottomSheet call, and, modify it.

Below is my parent class, the one that calls the 'showModalBottomSheet' function:

class _parentClass extends StatelessWidget {
  bool testing = false; //This is the variable that I am trying to change.

  @override
  Widget build(BuildContext context) {

    void _callModalBottomSheet() {
      showModalBottomSheet(
          context: context,
          builder: (BuildContext bc) {
            return Wrap(
              children: <Widget>[
                Container(
                  child: myStatefulWidget(testingValue: testing),
              ),
            ]);
          });

      print("Testing Value: $testing");
    }

    return Column(
      children: <Widget>[
        FlatButton(
                  child: Text("my button"),
                  onPressed: _callModalBottomSheet,
                ),
      ],
    );
  }
}

'myStatefulWidget' is actually another class implemented in a whole new file, thus, its only way to access the 'testing' variable is through its constructor (at least, the only way I know).

I have tried this, but it throws an error:

class myStatefulWidget extends StatefulWidget {
  final testingValue;

  myStatefulWidget({
    this.testingValue,
  });

  //testingValue = !testingValue; //This line throws an error!

  @override
  myStatefulWidgetState createState() => myStatefulWidgetState();
}

//...

Thank you very much for your help!

Andres Silva
  • 874
  • 1
  • 7
  • 26
  • 1
    Could you share with us what the error is all about? And you are declaring that variable as final. When a variable is final, it is meant to be not modifiable upon initial declaration. – Darsshan May 05 '20 at 05:24
  • do you want to change testing variable in parent widget when any event happen and testingValue variable change in myStatefulWidget class. am i right ? – Viren V Varasadiya May 05 '20 at 05:38
  • Your "testingValue" is final. So, you get error. Please use "bool" instead of "final". – Yuu Woods May 05 '20 at 06:38
  • YuuWoods and @Darsshan, thank you for your replies. You are correct, the variable is wrongly declared as final. After changing it to 'bool' I keep getting the same error as before: "The name 'testingValue' is already defined." (Android studio displays the error before running the program) – Andres Silva May 06 '20 at 01:59
  • @VirenVVarasadiya: I would like to implement some logic in the son widget, that modifies the testingValue within the son widget.. then, after the user clicks on 'accept', I would like to modify the value in the parent widget (if the user cancels, then I wouldn't do anything). For now, just knowing how to modify the value in the parent widget would suffice. Thanks! – Andres Silva May 06 '20 at 02:00

2 Answers2

3

You can call setState method in then method of showModalBottomSheet, so new data can be reflected in scree. you have to pass value in navigator.pop method.

Following code help you more to understand.

Full working demo:

class DeleteWidget extends StatefulWidget {
  @override
  _DeleteWidgetState createState() => _DeleteWidgetState();
}

class _DeleteWidgetState extends State<DeleteWidget> {
  bool testing = false; //This is the variable that I am trying to change.

  @override
  Widget build(BuildContext context) {
    void _callModalBottomSheet() {
      showModalBottomSheet(
          context: context,
          builder: (BuildContext bc) {
            return Wrap(children: <Widget>[
              Container(
                child: myStatefulWidget(testingValue: testing),
              ),
            ]);
          }).then((value) {
        setState(() {
          testing = value;
        });
      });

      print("Testing Value: $testing");
    }

    return Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: <Widget>[
          Text(testing.toString()),
          FlatButton(
            child: Text("my button"),
            onPressed: _callModalBottomSheet,
          ),
        ],
      ),
    );
  }
}

class myStatefulWidget extends StatefulWidget {
  final bool testingValue;
  myStatefulWidget({this.testingValue});
  @override
  _myStatefulWidgetState createState() => _myStatefulWidgetState();
}

class _myStatefulWidgetState extends State<myStatefulWidget> {
  bool index;
  @override
  void initState() {
    super.initState();
    index = widget.testingValue;
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text(index.toString()),
        RaisedButton(
          child: Text("change counter value"),
          onPressed: () {
            setState(() {
              index = !index;
            });
          },
        ),
        RaisedButton(
          child: Text("close sheet"),
          onPressed: () {
            Navigator.pop(context, index);
          },
        )
      ],
    );
  }
}
Viren V Varasadiya
  • 25,492
  • 9
  • 45
  • 61
0

I was able to solve it through 3 steps:

1) creating a function that performs the actual modification of the value in the parent widget (and sending it to the statefulWidget):

class _parentClass extends StatelessWidget {
  bool testing = false; //This is the variable that I am trying to change.
  void changeTesting(bool newValue){
    testing = newValue;
  }

  @override
  Widget build(BuildContext context) {

    void _callModalBottomSheet() {
      showModalBottomSheet(
          context: context,
          builder: (BuildContext bc) {
            return Wrap(
              children: <Widget>[
                Container(
                  child: myStatefulWidget(testingValue: testing, changeTesting: changeTesting),
              ),
            ]);
          });

      print("Testing Value: $testing");
    }

    return Column(
      children: <Widget>[
        FlatButton(
                  child: Text("my button"),
                  onPressed: _callModalBottomSheet,
                ),
      ],
    );
  }
}

2) Receiving that function in the StatefulWidget:

class myStatefulWidget extends StatefulWidget {
  final testingValue;
  final Function(bool) changeTesting;

  myStatefulWidget({
    this.testingValue,
    this.changeTesting,
  });

  @override
  myStatefulWidgetState createState() => myStatefulWidgetState();
}

And finally 3) call that function when a button is pressed in the state of the statefulwidget.

class myStatefulWidgetState extends State<myStatefulWidget> {
  @override
  Widget build(BuildContext context) {
    return FlatButton(
                    onPressed: (){widget.changeTesting(!widget.testingValue);Navigator.pop(context);},
                    child: Icon(Icons.check, color: Colors.white, size: 30.0,),
                  );
     }
  }

This particular blog post was very useful: https://alligator.io/flutter/widget-communication/

I also found another methodology that is better suited for when the data needs to be sent to many widgets down the line. The solution is called 'inheritedwidgets'. All these references were helpful (although the solution is quite complex): - The best way to passing data between widgets in Flutter - Flutter: How to correctly use an Inherited Widget? - https://medium.com/flutter-community/simple-ways-to-pass-to-and-share-data-with-widgets-pages-f8988534bd5b - https://medium.com/flutter/managing-flutter-application-state-with-inheritedwidgets-1140452befe1 - https://www.youtube.com/watch?v=pi0X-QWWfIk

Andres Silva
  • 874
  • 1
  • 7
  • 26