Force rebuild of a stateful child widget in flutter
Solution 1
USE THIS:
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
MyListWidget myListWidget = MyListWidget(5);
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
myListWidget = MyListWidget(count);
});
},
),
),
myListWidget,
],
));
}
}
Solution 2
I don't think the accepted answer is accurate, Flutter will retain the state of MyListWidget
because it is of the same type and in the same position in the widget tree as before.
Instead, force a widget rebuild by changing its key:
class _MyHomePageState extends State<MyHomePage> {
int count = 5;
@override
Widget build(BuildContext context) {
return Scaffold(
body: Row(
children: [
Expanded(
child: MaterialButton(
child: Text('Click me'),
color: Colors.red,
onPressed: () {
setState(() {
count++;
});
},
),
),
MyListWidget(count, key: ValueKey(count)),
],
),
);
}
}
Using a ValueKey
in this example means the state will only be recreated if count
is actually different.
Alternatively, you can listen to widget changes in State.didUpdateWidget
, where you can compare the current this.widget
with the passed in oldWidget
and update the state if necessary.
Damien
Updated on December 31, 2022Comments
-
Damien over 1 year
Let's suppose that I have a Main screen (stateful widget) where there is a variable
count
as state. In this Main screen there is a button and another stateful widget (let's call thisMyListWidget
.MyListWidget
initialize it's own widgets in theinitState
depending by the value of thecount
variable. Obviously if you change the value ofcount
and callSetState
, nothing will happen inMyListWidget
because it create the values in theinitState
. How can I force the rebuilding ofMyListWidget
? I know that in this example we can just move what we do in theinitState
in thebuild
method. But in my real problem I can't move what I do in theinitState
in thebuild
method.Here's the complete code example:
import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: MyHomePage(), ); } } class MyHomePage extends StatefulWidget { MyHomePage({Key? key}) : super(key: key); @override _MyHomePageState createState() => _MyHomePageState(); } class _MyHomePageState extends State<MyHomePage> { int count = 5; @override Widget build(BuildContext context) { return Scaffold( body: Row( children: [ Expanded( child: MaterialButton( child: Text('Click me'), color: Colors.red, onPressed: () { setState(() { count++; }); }, ), ), MyListWidget(count), ], )); } } class MyListWidget extends StatefulWidget { final int count; const MyListWidget(this.count, {Key? key}) : super(key: key); @override _MyListWidgetState createState() => _MyListWidgetState(); } class _MyListWidgetState extends State<MyListWidget> { late List<int> displayList; @override void initState() { super.initState(); displayList = List.generate(widget.count, (int index) => index); } @override Widget build(BuildContext context) { return Expanded( child: ListView.builder( itemBuilder: (BuildContext context, int index) => ListTile( title: Text(displayList[index].toString()), ), itemCount: displayList.length, ), ); } }
-
Damien over 2 yearsThank you, it was so easy!
-
gurnisht about 2 yearsElte Hupkes's answer should be the accepted one
-
Karolina Hagegård about 2 yearsAnd how would I update the state in the didUpdateWidget method? setState() or? Because I don't feel that solves the problem... The problem is that I need to update the variables that were initiated when the widget was created, and they are not in the build() method, which is what is called when setState() fires...
-
Elte Hupkes about 2 years@KarolinaHagegård
setState()
would be redundant, because Flutter will always callbuild()
afterdidUpdateWidget()
(as the documentation I linked to mentions). InsidedidUpdateWidget()
you can simply update whatever local / state variables are present. For this question's example, you would probably setdisplayList = List.generate(newWidget.count, ...etc)
indidUpdateWidget()
, but you can just inspect the widget and see what needs to be done in your case. Whatever you did ininitState()
you could do again indidUpdateWidget()
. It's not often appropriate, but it happens. -
Karolina Hagegård about 2 yearsNo, actually, that didn't do it. 😏 (And yes, I saw that setState() shouldn't be used in didUpdateWidget() after I posted my comment.) It didn't do it because the value that needed to update was final, as they have to be in the widget (unlike in the State). So what I need is for the StatefulWidget to be destroyed and rebuilt again. Is there a way to do that from within didUpdateWidget()? In any case, you'll be happy to know that the ValueKey() thing did the trick! 😊 So my problem is actually solved now, and now I'm just asking out of curiosity.
-
Karolina Hagegård about 2 yearsI actually tried sending the new widget as argument to the super method:
super.didUpdateWidget(this.widget)
but even that didn't work!... Which I think is weird... To give you the idea: This StatefulWidget is inside a list element. When a new list element comes in from an online stream listener, all the list elements shift upwards to display the new element at the bottom... but the StatefulWidgets stay in the same place! They refuse to move, but claim that they belong to the list element above... 🤦🏻♀️ Solved with ValueKey as mentioned, but I'm curios if didUpdateWidget() could do it too. -
Karolina Hagegård about 2 yearsThe reason why this doesn't work is that when the parent of a StatefulWidget rebuilds, the StatefulWidget reuses everything that's not inside its State widget. StatelessWidgets are destroyed completely and rebuilt anew, but StatefulWidgets are only partly rebuilt. See Elte Hupkes' answer above! Using the
Key key
property when calling the StatefulWidget did it for me. -
Karolina Hagegård about 2 yearsBtw (sorry for writing a lot... 😁 but) it would be great if you could update your answer to just show how to use the
Key key
property in the constructor of the StatefulWidget as well! I figured it out, but still. It's a bit tricky the first time. In any case, thanks for your help! 🙂 -
Elte Hupkes about 2 yearsIt's hard to comment without knowing your particular problem, but this is what's important: if a
build
call rebuilds the widget tree, theWidget
instances all get replaced regardless (except forconst
instances).State
instances however are kept if they're in the same place in the tree, they getdidUpdateWidget()
and thenbuild()
instead. If you change aStatefulWidget
's widget'skey
, it signals that the whole thing, including the state should be different. I'd be happy to explain further in SO chat, not really enough character space here. -
Elte Hupkes almost 2 years@KarolinaHagegård I actually missed one of your comments the last time I came here, so to answer the first one after my response for completeness: to recreate the state of a
StatefulWidget
, setting thekey
is the right approach. This will tell Flutter "even though this widget is of the same type as the widget that was here before, it's actually a different widget entirely". That will cause it to recreate it from scratch with a new state. I'm sure you'd figured that out already, though :). -
Karolina Hagegård almost 2 yearsI did. 🙂 But thanks, though! Your Key suggestion was invaluable! And this last way of explaining what it does is actually great... You should put that in the Answer!