Flutter: how to create a non-final property for a StatefulWidget?

3,661

You can have variables in the State object of the StatefulWidget, not in the StatefulWidget itself.

If you need to pass the value from another widget, you can pass it and reassign it to a State variable in the initState function.

Example:

class TestButton extends StatefulWidget {
  TestButton({this.passedcolor});
  final Color passedColor;
  @override
  _TestButtonState createState() => _TestButtonState();
}

class _TestButtonState extends State<TestButton> {
  Color color;

  @override
  initState(){
   color = widget.passedColor;
   super.initState()
  }

  @override
  Widget build(BuildContext context) {
    return RaisedButton(
      onPressed: () {
        setState(() {
          color = color == Colors.red ? Colors.blue : Colors.red;
        });
      },
      child: Icon(
        Icons.add,
        size: 80,
      ),
      color: color,
    );
  }
}

and then you can update it using setState to any color you wish.

As to why the value passed in the constructor has to be final or why you can't change it is because the StatefulWidget itself is immutable and holds immutable data, but it also holds a mutable State object which is a store for all the state aka mutable data the widget requires.

Quoting from Flutter docs:

StatefulWidget instances themselves are immutable and store their mutable state either in separate State objects that are created by the createState method, or in objects to which that State subscribes, for example Stream or ChangeNotifier objects, to which references are stored in final fields on the StatefulWidget itself.

You can read more on it here: StatefulWidget class documentation on Flutter website

Share:
3,661
Pigna
Author by

Pigna

Updated on December 18, 2022

Comments

  • Pigna
    Pigna over 1 year

    Say I want to create a button that every time that it's clicked it changes color. Its starting color is required in the constructor.

    The following code works fine, but when I hover on top of TestButton I get this message: "This class (or a class which this class inherits from) is marked as '@immutable', but one or more of its instance fields are not final: TestButton.color".

    If it should be final but I need it to change, what's the solution? Why does it have be final if it works anyways?

    class TestButton extends StatefulWidget {
      TestButton({this.color});
      Color color;
      @override
      _TestButtonState createState() => _TestButtonState();
    }
    
    class _TestButtonState extends State<TestButton> {
      @override
      Widget build(BuildContext context) {
        return RaisedButton(
          onPressed: () {
            setState(() {
              widget.color = widget.color == Colors.red ? Colors.blue : Colors.red;
            });
          },
          child: Icon(
            Icons.add,
            size: 80,
          ),
          color: widget.color,
        );
      }
    }
    
  • Pigna
    Pigna about 4 years
    Thanks! I read the documentation, but I don't understand why does my code work just as well as yours? I tested it both on my phone and on an emulator. If If I shouldn't have variables in a StatefulWidget, why does it work anyways?
  • Ayman Barghout
    Ayman Barghout about 4 years
    You are welcome! I have also updated my code since it had some mistakes, I am not sure but I feel like what you are accessing and changing is not the actual value passed into the constructor but an instance of it, like passing arguments to a function and changing it from inside the function, you are not changing the actual immutable value, but it is still wrong that's why you get the warning (probably). I'll investigate further soon and let you know of my findings.