Andres Silva
Andres Silva

Reputation: 892

How to receive data and change its value with flutter's showModalBottomSheet?

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!

Upvotes: 1

Views: 2717

Answers (2)

Andres Silva
Andres Silva

Reputation: 892

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

Upvotes: 0

Viren V Varasadiya
Viren V Varasadiya

Reputation: 27137

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);
          },
        )
      ],
    );
  }
}

Upvotes: 3

Related Questions