141
  1. For Example in the below code plus button works and able to update the text but the minus button does not.
  2. But if we press FloatingActionButton then the State is refreshed .
  3. The minus button is changing the value of the variable but not updating the state of parent widget .

enter image description here

here is code .....

import 'package:flutter/material.dart';

void main() => runApp(new MyApp());

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return new MaterialApp(
      title: 'Flutter Demo',
      theme: new ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: new MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

int number;

EdgeInsets globalMargin = const EdgeInsets.symmetric(horizontal: 20.0, vertical: 20.0);
TextStyle textStyle = const TextStyle(
  fontSize: 100.0,
  color: Colors.black,
);

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => new _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    number = number ?? 0;
  }

  @override
  Widget build(BuildContext context) {
    return new Scaffold(
      appBar: new AppBar(
        title: new Text(widget.title),
      ),
      body: new Column(
        children: <Widget>[
          new Text(
            number.toString(),
            style: textStyle,
          ),
          new GridView.count(
            crossAxisCount: 2,
            shrinkWrap: true,
            scrollDirection: Axis.vertical,
            children: <Widget>[
              new InkResponse(
                child: new Container(
                    margin: globalMargin,
                    color: Colors.green,
                    child: new Center(
                      child: new Text(
                        "+",
                        style: textStyle,
                      ),
                    )),
                onTap: () {
                  setState(() {
                    number = number + 1;
                  });
                },
              ),
              new Sub(),
            ],
          ),
        ],
      ),
      floatingActionButton: new FloatingActionButton(
        onPressed: () {
          setState(() {});
        },
        child: new Icon(Icons.update),
      ),
    );
  }
}

class Sub extends StatefulWidget {
  @override
  _SubState createState() => new _SubState();
}

class _SubState extends State<Sub> {
  @override
  Widget build(BuildContext context) {
    return new InkResponse(
      child: new Container(
          margin: globalMargin,
          color: Colors.red,
          child: new Center(
            child: new Text(
              "-",
              style: textStyle,
            ),
          )),
      onTap: () {
        setState(() {
          number = number - 1;
        });
      },
    );
  }
}
Ugurcan Yildirim
  • 5,973
  • 3
  • 42
  • 73
Ajay Kumar
  • 15,250
  • 14
  • 54
  • 53

10 Answers10

241

1.On Child Widget : add parameter Function paramter

class ChildWidget extends StatefulWidget {
  final Function() notifyParent;
  ChildWidget({Key key, @required this.notifyParent}) : super(key: key);
}

2.On Parent Widget : create a Function for the child to callback

refresh() {
  setState(() {});
}

3.On Parent Widget : pass parentFunction to Child Widget

new ChildWidget( notifyParent: refresh );  

4.On Child Widget : call the Parent Function

  widget.notifyParent();
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Mohamed Elrashid
  • 8,125
  • 6
  • 31
  • 46
  • 112
    How I can do the reverse. I want to update child from Parent – CodeGeek Mar 25 '19 at 15:39
  • @CodeGeek You can pass as property to child – Razi Kallayi Apr 30 '19 at 12:05
  • 7
    What if the child is a statefull widget and I want to update it from parent? – Sp4Rx Jun 20 '19 at 14:14
  • @sp4zRx store a refrance to the child and call a publice method on it – Mohamed Elrashid Jun 20 '19 at 15:58
  • @MohamedElrashid how can we pass an argument from child to parent? – Farhana Naaz Ansari Oct 10 '19 at 07:12
  • 2
    @Farhana refresh(argument1,argument1) {etState(() {});} | widget.notifyParent(1,2); – Mohamed Elrashid Oct 10 '19 at 19:20
  • 3
    Is this "the supposed" solution? is this what the flutter team used? I smell trouble in that code. Or maybe just my nose too sensitive :D – nafsaka Nov 18 '19 at 13:28
  • 1
    @nafsaka hi ❤ if you have a better answer please add new answer , or ask the flutter team to add one you can open an issue on github, if don't know how I'm more than happy to do it for you – Mohamed Elrashid Nov 19 '19 at 08:50
  • @CodeGeek were you able to get the solution? I have some data that keeps changing in the parent that I want to update in the bottom sheet component bottom sheet.which is also satefulwidget. I am able to get the update to the Bottom sheet stateful widget from there i am not able to update to the State class. Even though if i try to update it using some instance of state clas and calling methods inside state class and try to setState, I am getting the fallowing error: setState() or markNeedsBuild() called during build. – Pruthvi Apr 27 '20 at 13:03
  • 3
    For those of you who want to `pass` values to child widget, you can create `ValueNotifier` in parent widget, and pass it to the child widget. In your child widget, utilize `ValueListenableBuilder` to build layouts. – user482594 May 04 '20 at 06:39
  • can u help? https://stackoverflow.com/questions/61665353/widget-value-not-updating – Hoo May 08 '20 at 08:10
  • 1
    if you still have an issue please update ```final Function() notifyParent;``` to ```final Function notifyParent;``` – surga Jul 05 '20 at 11:26
  • @CodeGeek Please create a new post for "How I can do the reverse. I want to update a child from the parent" and send the link. – Arnold Parge May 11 '21 at 06:43
  • @MohamedElrashid where did you get the `setState(() {});` it says `it's not declared` seems like flutter don't know what the `setState` is. – Pretty_Girl Oct 30 '21 at 13:55
  • Hello, so how can I use setState in one ChildWidget from another ChildWidget? – Cat Dragon Aug 28 '23 at 09:48
118

Screenshot (Parent to child, Child to parent):

enter image description here

This examples shows calling a method

  1. Defined in Child widget from Parent widget.
  2. Defined in Parent widget from Child widget.

Code:

class ParentPage extends StatelessWidget {
  final GlobalKey<ChildPageState> _key = GlobalKey();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text("Parent")),
      body: Center(
        child: Column(
          children: <Widget>[
            Expanded(
              child: Container(
                color: Colors.grey,
                width: double.infinity,
                alignment: Alignment.center,
                child: ElevatedButton(
                  child: Text("Call method in child"),
                  onPressed: () => _key.currentState!.methodInChild(), // calls method in child
                ),
              ),
            ),
            Text("Above = Parent\nBelow = Child"),
            Expanded(
              child: ChildPage(
                key: _key,
                function: methodInParent,
              ),
            ),
          ],
        ),
      ),
    );
  }

  methodInParent() => Fluttertoast.showToast(msg: "Method called in parent", gravity: ToastGravity.CENTER);
}

class ChildPage extends StatefulWidget {
  final VoidCallback function;

  ChildPage({Key? key, required this.function}) : super(key: key);

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

class ChildPageState extends State<ChildPage> {
  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.teal,
      width: double.infinity,
      alignment: Alignment.center,
      child: ElevatedButton(
        child: Text("Call method in parent"),
        onPressed: () => widget.function(), // calls method in parent
      ),
    );
  }

  methodInChild() => Fluttertoast.showToast(msg: "Method called in child");
}
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
70

Old / Not recommended / Failed solutions:

  1. Solution 1: Create a global instance of _MyHomePageState. Use this instance in _SubState as _myHomePageState.setState
  2. Solution 2: No need to create a global instance. Instead, just pass the parent instance to the child widget
  3. Solution 3: Passing a callback from parent widget to child widget to update state of parent widget from child widget

Best Solution: use the package stream_mixin

As of Feb '23

class Counter with StreamMixin<int> { // CODE TO NOTICE
  Counter._();
  static Counter instance = Counter._();

  increment() {
    update((lastUpdate ?? 0) + 1);
  }

  decrement() {
    update((lastUpdate ?? 0) - 1);
  }
}



class MyHomePage extends StatelessWidget {
  const MyHomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('SO Help'),
      ),
      body: Column(
        children: <Widget>[
          StreamBuilder<int>( // CODE TO NOTICE
            initialData: 0,
            stream: Counter.instance.onChange,
            builder: (context, snapshot) {
              return Text(snapshot.data.toString());
            },
          ),
          GridView.count(
            crossAxisCount: 2,
            shrinkWrap: true,
            scrollDirection: Axis.vertical,
            children: <Widget>[
              InkResponse(
                onTap: Counter.instance.increment, // CODE TO NOTICE
                child: const Text("+"),
              ),
              const Sub(),
            ],
          ),
        ],
      ),
    );
  }
}

class Sub extends StatelessWidget {
  const Sub({super.key});

  @override
  Widget build(BuildContext context) {
    return InkResponse(
      onTap: Counter.instance.decrement, // CODE TO NOTICE
      child: const Text("-"),
    );
  }
}

Observe that:

  1. I'm using StatelessWidget, which will increase the performance. (You may choose to use StatefulWidget depending on your requirements
  2. Instead of StatefulWidget I'm using StreamBuilder, which will update only the Text widget inside the SteamBuilder. Unlike setState() updates the whole app
  3. The logic is separated from UI
CopsOnRoad
  • 237,138
  • 77
  • 654
  • 440
Arnold Parge
  • 6,684
  • 2
  • 30
  • 34
  • 4
    Works but bad. At least, use things such as `context.ancestorStateOfType` – Rémi Rousselet May 29 '18 at 08:15
  • 3
    @RémiRousselet can you please give an example of how to use it? – argamanza Jun 01 '18 at 00:22
  • using this "_myHomePageState" throws error calling it undefined but "myHomePageState" doesn't, maybe variables need to be in camelCase and can't have an underscore in front of them – Keshav Aditya R P Sep 19 '18 at 06:09
  • @KeshavAdityaR.P I have updated the code, please check. In dart underscore is to represent objects as private, if you with you can remove underscores. – Arnold Parge Sep 19 '18 at 07:29
  • This does not work if the child isn't in the same file as one would not have access to the `_state` class of the parent. – supersize May 16 '19 at 13:59
  • Remove underscore (`_`) and make life easy. – Arnold Parge May 16 '19 at 14:09
  • Instead of '_MyHomePageState' I believe you could also just do 'State' – surtyaar Sep 18 '19 at 15:29
  • Edit : I see why '_MyHomePageState' has to be passed into 'Sub' as the 'parent' as 'Sub' has to know about 'number'. I wonder if there is a way to create a base class so I can reuse the same child 'Sub' across several parent pages (ie/ '_MyHomePageStateA' and '_MyHomePageStateB' and I would be passing in '_MyHomePageStateBase' into 'Sub' – surtyaar Sep 21 '19 at 16:06
  • 3
    @RémiRousselet ancestorStateOfType method was deprecated after v1.12.1. Use `findAncestorStateOfType` instead https://api.flutter.dev/flutter/widgets/BuildContext/findAncestorStateOfType.html – Shady Mohamed Sherif Dec 26 '19 at 08:07
  • Is this also possible with the sub being a StatefulWidget? – Wanjia Jan 16 '20 at 18:25
  • 1
    @RémiRousselet this link explicitly says that is better to use a callback. – Daniel Vilela May 10 '21 at 21:03
  • 1
    @RémiRousselet I agree that using a callback is a better solution. Updated the answer accordingly. – Arnold Parge May 11 '21 at 06:38
19

I would like to extend Mohamed Elrashid answer, in case you require to pass a variable from the child widget to the parent widget

On child widget:

class ChildWidget extends StatefulWidget {
  final Function() notifyParent;
  ChildWidget({Key key, @required this.notifyParent}) : super(key: key);
}

On parent widget

void refresh(dynamic childValue) {
  setState(() {
    _parentVariable = childValue;
  });
}

On parent widget: pass the function above to the child widget

new ChildWidget( notifyParent: refresh ); 

On child widget: call the parent function with any variable from the the child widget

widget.notifyParent(childVariable);
Mohammad Ersan
  • 12,304
  • 8
  • 54
  • 77
Alvin Konda
  • 2,898
  • 1
  • 22
  • 22
  • 1
    I tried your method, however I get the warning on the `widget.notifyParent(childVariable);` line, saying: Too many positional arguments: 0 expected, but 1 found. – rb2030 Oct 14 '19 at 13:25
  • 1
    It should work. Probably you have an issue on the second part `void refresh()`. Still im not a big fan of this approach, the code is prone to errors. Good luck! – Alvin Konda Oct 14 '19 at 20:13
  • @rb2030 check it now , you should add dynamic variable in function – evals Nov 23 '19 at 10:59
  • @evals, please check again as your edit is not correct. As i have said above on my other comment, passing data to parent widget is NOT the way to work on with Flutter and should be avoided. Good luck. – Alvin Konda Nov 24 '19 at 11:51
  • @AlvinKonda well passing data worked with me, what never worked is your answer, thanks anyway :) – evals Nov 24 '19 at 14:29
  • @AlvinKonda well passing data worked with me, what never worked is your answer, thanks anyway :) – evals Nov 24 '19 at 14:29
  • The argument type 'void Function(dynamic)' can't be assigned to the parameter type 'dynamic Function() – Kalana Perera May 14 '22 at 05:13
  • changing Function() notifyParent to Function notifyParent works. – neobie Jul 22 '22 at 16:58
9

Old one but I would add my answer as per my findings:

var ancestralState = context.findAncestorStateOfType<ParentState>();
      ancestralState.setState(() {
        // here you can access public vars and update state.
        ...
      });
Aman Bindlish
  • 119
  • 1
  • 7
7
class HomePage extends StatefulWidget {

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

class HomePageState extends State<HomePage> {

  int selectedIndex = 0;

   void setSelectedIndex(int index){
     setState(() {
      selectedIndex = index;
     });
  }
}

class TestPage extends StatefulWidget {

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

class TestPageState extends State<TestPage> {
  int selectedIndex = 0;

  @override
  Widget build(BuildContext context) {
     return  GestureDetector(
                      onTap: (){

                        final HomePageState state = context.findAncestorStateOfType<HomePageState>();

                        state.setSelectedIndex(4);

                      },
                    child: Container(
                        width: 100,
                        height: 100,
                        color: Colors.green
              )
     );
  }

}
user112380
  • 339
  • 3
  • 3
5

Here is the solution which worked for me.

OUTPUT: State of Cart Widget is updated, upon addition of items.

enter image description here

Create a globalKey for the widget you want to update by calling the trigger from anywhere

final GlobalKey<CartWidgetState> cartKey = GlobalKey();

Make sure it's saved in a file have global access such that, it can be accessed from anywhere. I save it in globalClass where is save commonly used variables through the app's state.

class CartWidget extends StatefulWidget {

  CartWidget({Key key}) : super(key: key);
  @override
  CartWidgetState createState() => CartWidgetState();
}

class CartWidgetState extends State<CartWidget> {
  @override
  Widget build(BuildContext context) {
    //return your widget
    return Container();
  }
}

Call your widget from some other class.

class HomeScreen extends StatefulWidget {

  HomeScreen ({Key key}) : super(key: key);
  @override
  HomeScreenState createState() => HomeScreen State();
}

class HomeScreen State extends State<HomeScreen> {
  @override
  Widget build(BuildContext context) {
    return ListView(
              children:[
                 ChildScreen(), 
                 CartWidget(key:cartKey)
              ]
            );
  }
}



class ChildScreen extends StatefulWidget {

  ChildScreen ({Key key}) : super(key: key);
  @override
  ChildScreenState createState() => ChildScreen State();
}

class ChildScreen State extends State<ChildScreen> {
  @override
  Widget build(BuildContext context) {
    return InkWell(
              onTap: (){
                // This will update the state of your inherited widget/ class
                if (cartKey.currentState != null)
                    cartKey.currentState.setState(() {});
              },
              child: Text("Update The State of external Widget"),
           );
  }
}
Vicky Salunkhe
  • 9,869
  • 6
  • 42
  • 59
4

Although most of these previous answers will work, I suggest you explore the provider or BloC architectures, both of which have been recommended by Google.

In short, the latter will create a stream that reports to widgets in the widget tree whenever a change in the state happens and it updates all relevant views regardless of where it is updated from.

Here is a good overview you can read to learn more about the subject: https://bloclibrary.dev/#/

Jacobo Koenig
  • 11,728
  • 9
  • 40
  • 75
  • Or, You can use event-driven architecture. Easiest. Why can't we use **eventify**? Have a look here https://stackoverflow.com/questions/46057353/controlling-state-from-outside-of-a-statefulwidget/60533386#60533386answer-60533386 – Yogesh Mar 04 '20 at 19:33
  • 12
    It would be nice to have an example of this, all these solutions seem overly complicated. – live-love May 03 '20 at 12:35
0

For those who are wanting to pass a variable back and forth between child and parent without the use of third-party libraries, here is an example I wrote with two counter buttons. Granted, I don't know if this is best practice, but I wanted to provide the simplest answer I could create:

import 'package:flutter/material.dart';

const Color darkBlue = Color.fromARGB(255, 18, 32, 47);

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark().copyWith(
        scaffoldBackgroundColor: darkBlue,
      ),
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: ParentWid(),
        ),
      ),
    );
  }
}

class ParentWid extends StatefulWidget {  
  @override
  State<ParentWid> createState() => _ParentWidState();
}

class _ParentWidState extends State<ParentWid> {
  int counter = 0;
  
  void refresh(int childValue) {
    setState(() {
      counter = childValue;
    });
  }
  
  @override
  Widget build(BuildContext context) {
    return Column(children:[
       IconButton(
       onPressed: () {
         setState(() {
            counter++;
         });
       },
       icon: const Icon(Icons.add),
      ),
      ChildWid(counter, refresh)
    ]);
  }
}

class ChildWid extends StatefulWidget {
  final Function(int) notifyParent;
  final int? counter;
  const ChildWid(this.counter, this.notifyParent);

  @override
  State<ChildWid> createState() => _ChildWidState();
}

class _ChildWidState extends State<ChildWid> {
  @override
  Widget build(BuildContext context) {
    return Column(children:[
      Text(
        'Current Counter ${widget.counter ?? 0}',
        style: Theme.of(context).textTheme.headline4,
      ),
      IconButton(
       onPressed: () {
         int counterHolder = widget.counter ?? 0;
         counterHolder++;
         widget.notifyParent(counterHolder);
       },
       icon: const Icon(Icons.add),
      ),
    ]);
  }
}

Eli017
  • 31
  • 7
0

This is a little unorthodox, but it works: you keep state references of unrelated widgets in a common object and call them accordingly:

class Fuzz {
  State<A>? a;
  State<B>? b;
  int c = 0;
}



class A extends StatefulWidget {
  A(this.fuzz, {Key? key}) : super(key: key);
  Fuzz fuzz;
  @override
  State<A> createState() => _AState();
}

class _AState extends State<A> {
  @override
  void initState() {
    super.initState();
    widget.fuzz.a = this;
  }

  @override
  Widget build(BuildContext context) {
    return Center(
        child: TextButton(
      child: Text("More fuzz (${widget.fuzz.c})"),
      onPressed: () {
        widget.fuzz.b?.setState(() {
          widget.fuzz.c++;
        });
      },
    ));
  }
}

class B extends StatefulWidget {
  B(this.fuzz, {Key? key}) : super(key: key);
  Fuzz fuzz;
  @override
  State<B> createState() => _BState();
}

class _BState extends State<B> {
  @override
  void initState() {
    super.initState();
    widget.fuzz.b = this;
  }

  @override
  Widget build(BuildContext context) {
    return Center(
        child: TextButton(
      child: Text("Less fuzz (${widget.fuzz.c})"),
      onPressed: () {
        widget.fuzz.a?.setState(() {
          widget.fuzz.c--;
        });
      },
    ));
  }
}

 class TestView extends StatelessWidget {
  TestView({Key? key}) : super(key: key);
  @override
  Widget build(BuildContext context) {
    var fuzz = Fuzz();
    return Scaffold(
        backgroundColor: Colors.black,
        body: Center(
            child: Row(
          children: [Expanded(child: A(fuzz)), Expanded(child: B(fuzz))],
        )));
  }
}

Result:

enter image description here

Pavel
  • 565
  • 6
  • 19