Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter: Update state of sibling widget

Tags:

flutter

How do you update the state of a sibling widget in Flutter?

For example, if I have a rectangle widget, I can change its color from within the widget by calling setState() and changing the color variable (as the "Turn Green" button does below). But, say I wanted a button outside of the rectangle that would change its color. How do I communicate to the Rectangle that it's time to change its color, and what color to change to?

Here is my sample code. When the user presses the "Turn Blue" button, I'd like the rectangle to turn blue, but I don't have access to its state from the sibling widget.

import 'package:flutter/material.dart';

void main() {
  runApp(
    MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Hello Rectangle',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello Rectangle'),
        ),
        body: Column(
          children: <Widget>[
            HelloRectangle(),
            FlatButton(
            child: Text('Turn Blue!',
              style: TextStyle(fontSize: 40.0),
              textAlign: TextAlign.center,
            ),
            onPressed: () {
              // How to update state of the Rectangle from here?
            },
          ),
          ]
        ),
      ),
    ),
  );
}

class HelloRectangle extends StatefulWidget {
  @override
  HelloRectangleState createState() {
    return new HelloRectangleState();
  }
}

class HelloRectangleState extends State<HelloRectangle> {
  Color _color;

  @override
    void initState() {
      super.initState();
      _color = Colors.red;
    }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: _color,
        height: 400.0,
        width: 300.0,
        child: Center(
          child: FlatButton(
            child: Text('Turn Green!',
              style: TextStyle(fontSize: 40.0),
              textAlign: TextAlign.center,
            ),
            onPressed: () {
              // I can update the state from here because I'm inside the widget
              setState(() {
               _color = Colors.green;
              });
             },
          ),
        ),
      ),
    );
  }
}
like image 821
Mike Miller Avatar asked May 27 '18 16:05

Mike Miller


2 Answers

The rule of thumb is that you can't access the state of any Widget that isn't above you in the hierarchy. So, basically we need to move the state (color) up to an ancestor. Introduce a StatefulWidget that builds the Scaffold or Column and store the rectangle color there. Now the rectangle widget no longer needs to store the color, so can become a stateless widget - and you can pass the color in through the constructor. Both onPressed callbacks can now call a method on the new StatefulWidget which calls setState. (You could pass that method down to the rectangle widget, amongst other ways.)

There are two good introductions to best practise here and here.

like image 85
Richard Heap Avatar answered Oct 09 '22 02:10

Richard Heap


Here is basic example how to do it from outside.

import 'package:flutter/material.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({
    Key key,
  }) : super(key: key);

  @override
  MyAppState createState() {
    return new MyAppState();
  }
}

class MyAppState extends State<MyApp> {
  MaterialColor _color = Colors.green;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Hello Rectangle',
      home: Scaffold(
        appBar: AppBar(
          title: Text('Hello Rectangle'),
        ),
        body: Column(children: <Widget>[
          HelloRectangle(_color),
          FlatButton(
            child: Text(
              _color == Colors.green ? "Turn Blue" : "Turn Green",
              style: TextStyle(fontSize: 40.0),
              textAlign: TextAlign.center,
            ),
            onPressed: () {
              setState(() {
                _color = _color == Colors.green ? Colors.blue : Colors.green;
              });
            },
          ),
        ]),
      ),
    );
  }
}

class HelloRectangle extends StatefulWidget {
  final Color color;

  HelloRectangle(this.color);

  @override
  HelloRectangleState createState() {
    return new HelloRectangleState();
  }
}

class HelloRectangleState extends State<HelloRectangle> {
  HelloRectangleState();

  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Container(
        color: widget.color,
        height: 400.0,
        width: 300.0,
      ),
    );
  }
}
like image 7
Tree Avatar answered Oct 09 '22 02:10

Tree