Stateful Widget child is not updated - flutter

There are two stateful widgets.
The state of MyHomePage contains the counter.
The MyHomePage wraps content in a second stateful widget SubPage.
The SubPage has a child widget with data from the MyHomePage.
To clarify the problem, the first textwidget which is inside of the SubPage child doesn't update when the counter changes.
The textwidget outside of the SubPage increments as expected.
What do we have to do if we want the content of the inner stateful widget updated?
We have to use a stateful widget there. In the real application this widget has a real use-case.
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: Column(
children: <Widget>[
SubPage(
child: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class SubPage extends StatefulWidget {
SubPage({Key key, this.child}) : super(key: key);
final Widget child;
#override
SubPageState createState() => new SubPageState(child);
}
class SubPageState extends State<SubPage> {
final Widget child;
SubPageState(this.child);
#override
Widget build(BuildContext context) {
print("subpage build");
return this.child;
}
}

You don't have to set child as field of state. Actually it is cause of this bug. Here working code
class SubPage extends StatefulWidget {
SubPage({Key key, this.child}) : super(key: key);
final Widget child;
#override
SubPageState createState() => new SubPageState();
}
class SubPageState extends State<SubPage> {
#override
Widget build(BuildContext context) {
return widget.child;
}
}

Related

How to update State variable of a class which extends State from another Stateful class file?

In my Flutter project, I have the default Flutter code in home screen to update a counter by floating action button press. Here's the code of that class-
main.dart
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key? key, required this.title}) : super(key: key);
String title;
#override
State<MyHomePage> createState() => MyHomePageState();
}
class MyHomePageState extends State<MyHomePage> {
int counter = 0;
void _incrementCounter() {
setState(() {
counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: UpdateWidget().showButton()
),
);
}
}
Now, I just wanted to update the counter(state variable) without making it static from a new class named UpdateWidget.
I have tried updating the variable like this way:
UpdateWidget.dart
class UpdateWidget extends StatefulWidget {
UpdateWidget({Key? key}) : super(key: key);
#override
_UpdateWidgetState createState() => _UpdateWidgetState();
GlobalKey<MyHomePageState> homeKey = GlobalKey();
Widget showButton() {
return FlatButton(
onPressed: () {
MyHomePage(key: homeKey, title: 'Hello',);
debugPrint('pressed');
homeKey.currentState?.counter = 5;
}, child: const Text('A'),
);
}
}
class _UpdateWidgetState extends State<UpdateWidget> {
double defaultScreenWidth = 400.0;
double defaultScreenHeight = 810.0;
#override
Widget build(BuildContext context) {
return const Scaffold(
body: Center(
child: Text(''),
),
);
}
}
In, UpdateWidget class, inside FlatButton onPressed block, the print message is showing but it is not updating the counter variable like the way I wanted.
So, I need a way to update this variable and show that in UI.

Flutter: Is it safe to use ValueNotifier with class static variables?

I'm trying to find a simple and clean way to manage my state in Flutter without using third party libraries. I chose to use ValueNotifier in combination with ValueListenableBuilder in order to be selective about what's being rebuilt in my app when changes occurs.
I've created a class to expose my state values using static variables so they can be used and get directly updated from anywhere without having to do any complicated gymnastic. I'm not sure it is a good practice (it looks to easy in comparison to other state management solutions) but it works.
Could you tell me why this potentially wouldn't be a viable solution?
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
final CounterState _counterState = CounterState();
#override
Widget build(BuildContext context) {
final count = _counterState.counter;
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
ValueListenableBuilder(
valueListenable: _counterState.counter,
builder: (BuildContext context, int count, _) => Text(
'parent count: $count',
style: Theme.of(context).textTheme.headline4,
)),
const ChildWidget()
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => count.value++,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class ChildWidget extends StatefulWidget {
const ChildWidget({Key? key}) : super(key: key);
#override
State<ChildWidget> createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State<ChildWidget> {
final CounterState _counterState = CounterState();
#override
Widget build(BuildContext context) {
return ValueListenableBuilder(
valueListenable: _counterState.counter,
builder: (BuildContext context, int count, _) => Center(
child: Text(
'child count: $count',
style: Theme.of(context).textTheme.headline4,
),
),
);
}
}
class CounterState {
static final ValueNotifier<int> _counter = ValueNotifier<int>(0);
ValueNotifier<int> get counter => _counter;
}

Why setState is not rebuilding my child widget?

In this counter example, reproducible on dart pad, I am expecting the child widget to rebuild when I tap on the increment button because it is in the same tree as a the build tree of CounterParentState.
That or there is something about widget tree or the setState method that I don't understand.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Controller {
late CounterParentState view;
void attach(CounterParentState v) {
this.view = v;
}
int counter = 0;
void incrementCounter() {
counter++;
this.view.applyState();
}
}
class CounterChild extends StatelessWidget {
final Controller controller;
const CounterChild({Key? key, required this.controller}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${controller.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
final controller = Controller();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterParent(
title: 'Flutter Demo Home Page',
controller: controller,
child: CounterChild(controller: controller),
),
);
}
}
class CounterParent extends StatefulWidget {
final Controller controller;
final String title;
final Widget child;
CounterParent({
Key? key,
required this.title,
required this.controller,
required this.child,
}) : super(key: key);
#override
CounterParentState createState() => CounterParentState();
}
class CounterParentState extends State<CounterParent> {
void applyState() {
setState(() {});
}
#override
void initState() {
widget.controller.attach(this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: widget.child,
floatingActionButton: FloatingActionButton(
onPressed: widget.controller.incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Why I chose this design ?
I choose this design because I wanted to be able to re-use the CounterParent and freely replace the child of the CounterParent
class CounterStylishChild extends StatelessWidget {
final Controller controller;
const CounterStylishChild({Key? key, required this.controller}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times: (And I am stylish too)',
),
Text(
'${controller.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
class MyMoreStylishApp extends StatefulWidget {
const MyMoreStylishApp({Key? key}) : super(key: key);
#override
MyMoreStylishAppState createState() => MyMoreStylishAppState();
}
class MyMoreStylishAppState extends State<MyMoreStylishApp> {
final controller = Controller();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterParent(
title: 'Counter Parent with a better looking child',
controller: controller,
child: CounterStylishChild(controller: controller),
),
);
}
}
setState is working as expected and rebuilding CounterParent, but you passed a Widget instance ("child") that is stored in CounterParent and stays constant as it's constructed in MyAppState, which is never rebuilt in your code. This is the equivalent of "storing" a widget, which some do to prevent unnecessary rebuilds.
For example, the AnimatedBuilder widget does this to prevent unnecessary rebuilds of larger children, while rebuilding the widgets that have changing values that create the animation.
The AnimatedBuilder example code may provide more understanding:
#override
Widget build(BuildContext context) {
return AnimatedBuilder(
animation: _controller,
child: Container(
width: 200.0,
height: 200.0,
color: Colors.green,
child: const Center(
child: Text('Whee!'),
),
),
builder: (BuildContext context, Widget? child) {
return Transform.rotate(
angle: _controller.value * 2.0 * math.pi,
child: child,
);
},
);
}
The "complex" child (the Container) is passed to the AnimatedBuilder, which stores the widget. It can then pass this widget as a parameter in the builder that is called rapidly. The Transform widget gets its new rotation value on every call of the builder, but the potentially "complex" child is never actually rebuilt.
This is identical to the situation you have here, except your situation is unintentional. It's inadvisable to have multiple widgets so dependent on each other and organized in such a complex manner like what you have here.
Though you don't ask for this in your question, I'll provide a fix. This may not be what you want and it may defeat the purpose of this elaborate design, but it's the simple way to make a pattern such as this function. Elaboration as to why you wanted to do this would be helpful if this solution is not satisfactory.
The CounterChild should be instantiated in the CounterParent. This way, when setState is called for the parent, a new instance of the child is create and it is rebuilt.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Controller {
late CounterParentState view;
void attach(CounterParentState v) {
this.view = v;
}
int counter = 0;
void incrementCounter() {
counter++;
this.view.applyState();
}
}
class CounterChild extends StatelessWidget {
final Controller controller;
const CounterChild({Key? key, required this.controller}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${controller.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
final controller = Controller();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterParent(
title: 'Flutter Demo Home Page',
controller: controller,
),
);
}
}
class CounterParent extends StatefulWidget {
final Controller controller;
final String title;
CounterParent({
Key? key,
required this.title,
required this.controller,
}) : super(key: key);
#override
CounterParentState createState() => CounterParentState();
}
class CounterParentState extends State<CounterParent> {
void applyState() {
setState(() {});
}
#override
void initState() {
widget.controller.attach(this);
super.initState();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: CounterChild(controller: widget.controller),
floatingActionButton: FloatingActionButton(
onPressed: widget.controller.incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
If you'd like to keep the CounterChild where it is, you might consider using InheritedNotifier:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Controller extends ChangeNotifier {
int counter = 0;
void incrementCounter() {
counter++;
notifyListeners();
}
}
class Counter extends InheritedNotifier<Controller> {
const Counter({
Key? key,
required Controller controller,
required Widget child,
}) : super(key: key, child: child, notifier: controller);
static Controller of(BuildContext context) {
final Controller? result = context.dependOnInheritedWidgetOfExactType<Counter>()?.notifier;
assert(result != null, 'No Controller found in context');
return result!;
}
#override
bool updateShouldNotify(Counter old) => notifier?.counter != old.notifier?.counter;
}
class CounterChild extends StatelessWidget {
const CounterChild({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${Counter.of(context).counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
final Controller controller = Controller();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: Counter(
controller: controller,
child: CounterParent(
title: 'Flutter Demo Home Page',
child: CounterChild(),
),
),
);
}
}
class CounterParent extends StatefulWidget {
final String title;
final Widget child;
CounterParent({
Key? key,
required this.title,
required this.child,
}) : super(key: key);
#override
CounterParentState createState() => CounterParentState();
}
class CounterParentState extends State<CounterParent> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: widget.child,
floatingActionButton: FloatingActionButton(
onPressed: Counter.of(context).incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
Or if you want to preserve more of your original structure, you need to pass your MyApp setState instead of your CounterParent setState so both the parent and child are rebuilt:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class Controller extends ChangeNotifier {
int counter = 0;
void incrementCounter() {
counter++;
notifyListeners();
}
}
class CounterChild extends StatelessWidget {
final Controller controller;
const CounterChild({Key? key, required this.controller}) : super(key: key);
#override
Widget build(BuildContext context) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'${controller.counter}',
style: Theme.of(context).textTheme.headline4,
),
],
),
);
}
}
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
MyAppState createState() => MyAppState();
}
class MyAppState extends State<MyApp> {
late final Controller controller;
#override
void initState() {
super.initState();
controller = Controller()
..addListener(()=>setState((){}));
}
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: CounterParent(
controller: controller,
title: 'Flutter Demo Home Page',
child: CounterChild(controller: controller),
),
);
}
}
class CounterParent extends StatefulWidget {
final String title;
final Widget child;
final Controller controller;
CounterParent({
Key? key,
required this.title,
required this.controller,
required this.child,
}) : super(key: key);
#override
CounterParentState createState() => CounterParentState();
}
class CounterParentState extends State<CounterParent> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: widget.child,
floatingActionButton: FloatingActionButton(
onPressed: widget.controller.incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Flutter: How to change the state of a variable in another dart file?

I am currently working on an app; I want to change the value of a String which is declared in another dart file and then pass that changed state to the stateful widget.
I.E;
I create a file called as "Body.dart" file where I have declared a String called as 'scale' who's value initially is "Empty".
Later when a button in another dart file "scale_button" is pressed, I want to assign the string scale = "Hello" in my Body.dart file. So that the stateful widget also displays the same on the screen.
You can use provider(or any other state management) package in that case. In yaml file add, provider: ^4.3.2+4
class HomeApp extends StatefulWidget {
#override
_HomeAppState createState() => _HomeAppState();
}
class _HomeAppState extends State<HomeApp> {
StringProvider _stringProvider;
#override
void initState() {
super.initState();
_stringProvider = Provider.of<StringProvider>(context, listen: false);
}
void updateString() {
_stringProvider.setString('hai');
}
#override
Widget build(BuildContext context) {
StringProvider _stringProvider = Provider.of<StringProvider>(context);
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Container(
child: Text(
_stringProvider.str,
style: TextStyle(
fontSize: 22,
),
),
),
RaisedButton(
onPressed: updateString,
child: Text('Click'),
),
],
),
),
);
}
}
// class for storing data(StringProvider.dart)
import 'package:flutter/material.dart';
class StringProvider extends ChangeNotifier { // create a common file for data
String _str = 'hello';
String get str => _str;
void setString(String st) {
_str = st;
notifyListeners();
}
}
When you create a new Flutter project the sample code of the counter shows you how to do this. Check out the comments in the next code:
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// here is passing a String to MyHomePage.
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
// And here you can see how to make the widget wait for a variable
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
Full code of Sample Counter App
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}

Flutter MaterialApp with navigatorKey and key is not restarting

When testing, press the increment button a few times to view if the app restarts.
I was trying to "restart" a toy app in Flutter. I was using the old counter example and after modifying it the behavior is not the expected.
For example, with this code:
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
UniqueKey _materialKey;
UniqueKey _homeKey;
#override
void initState() {
super.initState();
_awaitAndRun();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
key: _materialKey,
navigatorKey: _navigatorKey,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(
key: _homeKey,
title: 'Flutter Demo Home Page',
),
);
}
Future<void> _awaitAndRun() async {
print('Starting delay... Please press the button before the time ends.');
await Future<dynamic>.delayed(Duration(seconds: 5));
setState(() {
_materialKey = UniqueKey();
_homeKey = UniqueKey();
});
print('Has been the screen reloaded?');
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
The awaitAndRun function should restart the App because I am setting the _materialKey and _homeKey as new instances. As part of it, the MyHomePage widget also should be rebuilt (because the key value has changed) but it didn't.
I can understand it's because the _navigatorKey is "saving" the state of the MaterialApp but the MyHomePage should be rebuild because its key has changed!
It's even more strange because if I remove the _materialKey (as shown in the code below), the MyHomePage widget gets rebuilt.
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final GlobalKey<NavigatorState> _navigatorKey = GlobalKey<NavigatorState>();
UniqueKey _materialKey;
UniqueKey _homeKey;
#override
void initState() {
super.initState();
_awaitAndRun();
}
#override
Widget build(BuildContext context) {
return MaterialApp(
//key: _materialKey,
navigatorKey: _navigatorKey,
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: MyHomePage(
key: _homeKey,
title: 'Flutter Demo Home Page',
),
);
}
Future<void> _awaitAndRun() async {
print('Starting delay... Please press the button before the time ends.');
await Future<dynamic>.delayed(Duration(seconds: 5));
setState(() {
_materialKey = UniqueKey();
_homeKey = UniqueKey();
});
print('Has been the screen reloaded?');
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
So it doesn't have any sense for me, why does this happen? Why when I set the _materialKey the MyHomePage widget doesn't get rebuilt?
It's pretty normal. Flutter always target safe and clean state and doesn't keep any item usually time. You can use a lot of methods, so I think should provider
You need to game states keep to app state, so I created ToysGame(sample) and added count property, you can use and change this property so unaffected state changes.
Firstly wrap single provider instance to your app page.
home: Provider(
create: (context) => ToysGame(),
child: MyHomePage(
key: _homeKey,
title: 'Flutter Demo Home Page',
),
),
After doesn't keep count on in page state, you should use toys state with provider.
Text(
Provider.of<ToysGame>(context).count.toString(),
style: Theme.of(context).textTheme.headline4,
),
And you want to change value
void _incrementCounter() {
Provider.of<ToysGame>(context, listen: false).count++;
setState(() {});
}
Finally, it's done. I added on my stackhelpover repo with restart app lib name.
(You read provider package and maybe other feature help for you [ change notifier or multi-provider ])
StackHelpOver