Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flutter Stateful Widget recreates State

Tags:

flutter

I'm developing a flutter app and recognized an unexpected behavior with the state managment. I created a sample app to reproduce the behavior and you can find the code and log output below.

The app contains a simple ListView which contains 10 stateful Container (Text + Decoration). When i scroll down, each container and its container state will be created once like expected. When i scroll up again, Flutter recreates every state (but not the container widget) for each container widget which appears again on the display. I would expect that flutter would retrieve the previous state without recreating the whole state object. Am I doing something wrong here?

Sample App


Sample code:

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key) {
    print("MyHomePage constructor");
  }

  @override
  _MyHomePageState createState() {
    print("createState");
    return _MyHomePageState();
  }
}

class _MyHomePageState extends State<MyHomePage> {
  _MyHomePageState() {
    print("_MyHomePageState contructor");
  }

  void initState() {
    super.initState();

    print("_MyHomePageState initState");
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
        body: ListView.builder(
          itemBuilder: (context, index) {
            return ContainerWidget(index, key: ValueKey(index));
          },
          itemCount: 10,
        ));
  }
}

class ContainerWidget extends StatefulWidget {
  int index;

  ContainerWidget(this.index, {key}) : super(key: key) {
    print("ContainerWidget constructor for index $index");
  }

  @override
  State<StatefulWidget> createState() {
    print("ContainerWidget createState for index $index");
    return _ContainerState();
  }
}

class _ContainerState extends State<ContainerWidget> {
  _ContainerState() {
    print("_ContainerState constructor");
  }

  void initState() {
    super.initState();

    print("_ContainerState initState for index ${widget.index}");
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Center(
        child: Text("Index: ${widget.index}"),
      ),
      height: 200,
      decoration: BoxDecoration(
        border: Border(
          bottom: BorderSide(color: Colors.green),
        ),
      ),
    );
  }
}


Log-Output:

I/flutter (22400): createState
I/flutter (22400): _MyHomePageState contructor
I/flutter (22400): _MyHomePageState initState
I/flutter (22400): ContainerWidget constructor for index 0
I/flutter (22400): ContainerWidget createState for index 0
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 0
I/flutter (22400): ContainerWidget constructor for index 1
I/flutter (22400): ContainerWidget createState for index 1
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 1
I/flutter (22400): ContainerWidget constructor for index 2
I/flutter (22400): ContainerWidget createState for index 2
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 2
I/flutter (22400): ContainerWidget constructor for index 3
I/flutter (22400): ContainerWidget createState for index 3
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 3
I/flutter (22400): ContainerWidget constructor for index 4
I/flutter (22400): ContainerWidget createState for index 4
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 4
I/flutter (22400): ContainerWidget constructor for index 5
I/flutter (22400): ContainerWidget createState for index 5
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 5
I/flutter (22400): ContainerWidget createState for index 1
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 1
I/flutter (22400): ContainerWidget createState for index 0
I/flutter (22400): _ContainerState constructor
I/flutter (22400): _ContainerState initState for index 0
like image 756
0xPixelfrost Avatar asked Mar 05 '19 12:03

0xPixelfrost


People also ask

What is state in stateful widget?

Stateful Widgets: The widgets whose state can be altered once they are built are called stateful Widgets. These states are mutable and can be changed multiple times in their lifetime. This simply means the state of an app can change multiple times with different sets of variables, inputs, data.

Why is the build () method on state and not stateful widget?

Why is the build method on State, and not StatefulWidget? Putting a Widget build(BuildContext context) method on State rather than putting a Widget build(BuildContext context, State state) method on StatefulWidget gives developers more flexibility when subclassing StatefulWidget.

How do you Reinit state Flutter?

You can call YourStateClass. initState() to revert to its original initialized state. Also make sure that when you construct your state, you will want to initialize all of your variables in the void initState() function.

Why stateful widget and state separate classes?

There are multiple reasons : Widgets are immutable. Since the Stateful widget extends Widget it, therefore, must be immutable too. Splitting the declaration into two classes allows both the Stateful widget API to be immutable and State to be mutable.


1 Answers

This is expected, as the items are unmounted when they leave the screen.

If you don't want that, you'll want to use what we call "keep alive". You can do so by adding a mixin to your State class:

class _MyHomePageState extends State<MyHomePage> with AutomaticKeepAliveClientMixin {

  bool get wantKeepAlive => true;


  // ...
 }
like image 145
Rémi Rousselet Avatar answered Oct 04 '22 09:10

Rémi Rousselet