So basically I have 2 widgets, Widget 1 which is a stateful widget, and Widget 2 which is also a stateful widget and is a child in Widget 1. In widget 1, when I run setState(){...}
, widget 2 is obviously rebuilt however some of the fields aren't updated.
Widget 2 Example:
class WidgetTwo extends StatefulWidget {
final String noChangeStr;
final int willChangeInt
final bool willChangeBool;
WidgetTwo({
Key? key,
required this.noChangeStr,
required this.willChangeInt,
required this.willChangeBool,
}) : super(key: key);
@override
State<WidgetTwo> createState() => _WidgetTwoState();
}
class _WidgetTwoState extends State<WidgetTwo> {
late bool _willChangeBool;
late int _willChangeInt;
@override
void initState() {
super.initState();
_willChangeBool = this.widget.willChangeBool;
_willChangeInt = this.widget.willChangeInt;
}
@override
void dispose() {
super.dispose();
}
...
}
The reason I have made the fields that WILL change final is because otherwise, flutter warns:
This class (or a class that this class inherits from) is marked as '@immutable', but one or more of its instance fields aren't final
The issue:
When setState is called in Widget 1, the initState in Widget 2 doesn't run again and so therefore _willChangeBool and _willChangeInt aren't updated to the new value. I know that initState only runs once but I thought since widget 2 is supposed to be rebuilt due to widget 1 having used setState, that the initState in Widget 2 would rerun as well.
I have also tried using didChangeDependencies()
but that results in the same issue.
Any idea on how to properly implement this? I don't think I should ignore the warning but I can't see any other way to do it. The functionality works as intended if I just set the non-final fields to not final.
Thank you!
CodePudding user response:
Here is how a WidgetOne could update its state and how a WidgetTwo could then respond to it.
import 'package:flutter/material.dart';
void main() {
runApp(const WidgetOne());
}
class WidgetOne extends StatefulWidget {
const WidgetOne({Key? key}) : super(key: key);
@override
_WidgetOneState createState() => _WidgetOneState();
}
class _WidgetOneState extends State<WidgetOne> {
String noChangeStr = 'x';
int willChangeInt = 1;
bool willChangeBool = true;
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Demo',
home: Scaffold(
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.ac_unit),
onPressed: () {
setState(() {
noChangeStr = 'a';
willChangeInt = 2;
willChangeBool = false;
});
},
),
body: WidgetTwo(
noChangeStr: noChangeStr,
willChangeBool: willChangeBool,
willChangeInt: willChangeInt,
),
),
);
}
}
class WidgetTwo extends StatefulWidget {
final String noChangeStr;
final int willChangeInt;
final bool willChangeBool;
const WidgetTwo({
Key? key,
required this.noChangeStr,
required this.willChangeInt,
required this.willChangeBool,
}) : super(key: key);
@override
State<WidgetTwo> createState() => _WidgetTwoState();
}
class _WidgetTwoState extends State<WidgetTwo> {
@override
void initState() {
super.initState();
}
@override
void didUpdateWidget(covariant WidgetTwo oldWidget) {
if (oldWidget.noChangeStr != widget.noChangeStr) {
print('noChangeStr changed!');
print('you could start an animation here or other fancy stuff.');
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
return Center(
child: Text(
'${widget.noChangeStr}, ${widget.willChangeInt}, ${widget.willChangeBool}',
));
}
}
Does this help?
The fields are not final in WidgetOne: their state is changed with setState in WidgetOne.
The fields in WidgetTwo are final: WidgetTwo is rebuild with the new values handed down from WidgetOne (and they do not change there). If you keep WidgetTwo a stateful widget, you can see the changes in the didUpdateWidget(covariant WidgetTwo oldWidget) method.