I've made some test widgets to illustrate a point that I'm having difficulty with in a much more complicated widget.
I have the following widget:
class TestListWidget extends StatefulWidget {
Widget child;
TestListWidget({Widget child}) {
this.child = child;
}
@override
State<StatefulWidget> createState() {
return TestListWidgetState();
}
}
class TestListWidgetState extends State<TestListWidget>
{
Widget child;
int buttonCount = 0;
@override initState() {
child = widget.child;
}
_clickedCountButton()
{
setState(() {
buttonCount++;
});
}
@override
Widget build(BuildContext context) {
return new Column(children: [
Text("Times Hit: $buttonCount"),
ElevatedButton(onPressed: _clickedCountButton, child: Text("Update Count")),
child
]);
}
}
The above widget is being used inside the following widget:
class TestList extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new TestListState();
}
}
class TestListState extends State<TestList> {
String _testStr = "not clicked";
_clickButton()
{
setState(() {
_testStr = "CLICKED";
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test",
theme: ThemeData(
primarySwatch: Colors.blue
),
home:Scaffold(
body : testListWidget(child: Row(children: [
Text(_testStr),
ElevatedButton(onPressed: _clickButton, child: Text("Click me!"))
]))));
}
}
The issue I'm having is when the "Click me!" button is clicked, the function is called, but the text on the screen is not updated to "CLICKED!". The "Update Count" button works as intended though.
If I make the TestListWidget a stateless widget (and remove the update count button functionality) then the "Click Me!" button works as expected. Is there any way to make a child widget rebuild when passed to a stateful widget?
Your problem is quite simple. You have two variables, one is TestListWidget.child
, we'll call it stateful widget's child. The second is TestListWidgetState.child
, we'll call it state's child.
You make state's child to be equal to stateful widget's child on initState
, but initState
only runs when you first create a state, so updating the stateful widget's child will not update the state's child because initState
won't run again.
To fix this, I believe you can just completely remove state's child, and use widget.child
instead:
return new Column(children: [
Text("Times Hit: $buttonCount"),
ElevatedButton(onPressed: _clickedCountButton, child: Text("Update Count")),
widget.child
]);
Full example:
import 'package:flutter/material.dart';
void main() => runApp(TestList());
class TestListWidget extends StatefulWidget {
Widget child;
TestListWidget({required this.child});
@override
State<StatefulWidget> createState() {
return TestListWidgetState();
}
}
class TestListWidgetState extends State<TestListWidget>
{
int buttonCount = 0;
_clickedCountButton()
{
setState(() {
buttonCount++;
});
}
@override
Widget build(BuildContext context) {
return new Column(children: [
Text("Times Hit: $buttonCount"),
ElevatedButton(onPressed: _clickedCountButton, child: Text("Update Count")),
widget.child
]);
}
}
class TestList extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return new TestListState();
}
}
class TestListState extends State<TestList> {
String _testStr = "not clicked";
_clickButton()
{
setState(() {
_testStr = "CLICKED";
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "Test",
theme: ThemeData(
primarySwatch: Colors.blue
),
home:Scaffold(
body : TestListWidget(child: Row(children: [
Text(_testStr),
ElevatedButton(onPressed: _clickButton, child: Text("Click me!"))
]))));
}
}