1

The widget TrainsPage is added to the build graph in main.dart, when the corresponding menu button is clicked. This is done twice: once when _routes is empty and a second time when _routes is filled.

Widget pageSelector() {
   if (_selectedIndex == 2) {
      return new TrainsPage(routes: _routes);
    } else
      return Text("");
  }

In TrainsPage.dart, I have the code for the stateful widget TrainsPage.

class TrainsPage extends StatefulWidget {
  const TrainsPage({Key? key, required this.routes}) : super(key: key);

  final List<RSRoute> routes;

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

class _TrainsPageState extends State<TrainsPage> {
  List<RSRoute> _routes = List.empty();

  @override
  void initState() {
    super.initState();
    this._routes = new List<RSRoute>.from(widget.routes);

Now, the second time, TrainsPage gets called in main.dart (now with routes filled), initState() of _TrainsPageState is not called, which is responsible to read the data in routes. And because routes was empty the first time, there is nothing in display on the trains page.

Why does TrainsPage not rebuild _TrainsPageState, when it clearly got new data in the constructor?

Alexander Roehnisch
  • 675
  • 1
  • 7
  • 10
  • init state will be called in one case which is if its parameter has changed or else it assumes there is nothing to update in this Widget – nitishk72 Oct 01 '21 at 10:58
  • @nitishk72 Is there a way to force TrainsPage to rebuild _TrainsPageState? I guess the principle to delegate change down the tree of widgets would work in Jetpack Compose. Isn't it similar in Flutter? – Alexander Roehnisch Oct 01 '21 at 11:04
  • just add this `key: UniqueKey()` to your Widget `TrainsPage(key: UniqueKey(), routes: routes)` – nitishk72 Oct 01 '21 at 11:07

1 Answers1

2

This is exactly why the State exists : to keep the state of the current context alive even when the widget is rebuild.

If it was recreated each time the statefull widget is rebuild it could not keep the state of its own variables.

class MyWidget extends StatelessWidget {
  var _someStateVariable = 0;

  @override
  void build(BuildContext context){
    // here an action that increment _someStateVariable
  }

}

Here _someStateVariable would be reset to 0 at each rebuild. Or if we wanted a StateFullWidget in the first place it's because we'll update this variable later and want to keep its updated value through the multiple widget rebuilds. If you don't have such state variable to maintain maybe you don't need a StateFullWidget here.

Now to the solution to your problem : you can override didUpdateWidget instead of initstate since it will be called at each widget rebuild :

@override
  void didUpdateWidget() {
    didUpdateWidget();
   _routes = new List<RSRoute>.from(widget.routes);
}
Tanguy
  • 838
  • 4
  • 15
  • Thank you, that was helpful. For completeness, I ended up putting the init code into its own function, that becomes called from initState and didUpdateWidget. @override void didUpdateWidget(TrainsPage oldWidget) { super.didUpdateWidget(oldWidget); setState(() { init(); }); } – Alexander Roehnisch Oct 01 '21 at 11:23
  • 1
    You're welcome. For information since the widget will already be rebuild each time didUpdateWidget is called it is unnecessary and redundant to call setState in it. – Tanguy Oct 01 '21 at 11:24
  • This concept, that the State is remembered, even though the overlying Widget is rebuild, is really interesting. We can add date from above while keeping user state below. I guess, that is done through the "remember" keyword in Jetpack compose. https://stackoverflow.com/questions/65368007/what-does-jetpack-compose-remember-actually-do-how-does-it-work-under-the-hood – Alexander Roehnisch Oct 01 '21 at 11:37