Flutter passing data up through stateless widget - flutter

I am working on a flutter app, and I have some data stored in the state of a widget. In this case it is the string title. I am wondering if I can pass this data through a parent stateless widget and into this stateless widgets parent, which is a stateful widget. If working correctly, I could pass title into the state of MyHomePage and save it into title2. Is there a way to do this or do I have to convert Widget1 into a stateful widget. The only issue with that is that I already wrote the widget, but I am curious. Here is my code. Thanks!
//main.dart
import 'package:flutter/material.dart';
import 'Widget1.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title2;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Center(
child: Widget1(),
),
);
}
}
/////////////////////////////////////////////
//Widget1.dart
Widget Widget1() {
return Widget2();
}
/////////////////////////////////
//Widget2.dart
import 'package:flutter/material.dart';
class Widget2 extends StatefulWidget {
final String title = "Hello from Widget2";
_Widget2State createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
String title = "Hello from Widget2";
#override
Widget build(BuildContext context) {
return Text('${title}');
}
}
Thanks again!

If you are not using any state management except default one then you can pass data between widgets using Navigator. Here is the code example of how to pass String from child Stateless widget (can be stateful too) to its parent widget.
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title = "";
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("FIRST WIDGET"),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text("Title from child Stateless widget: $title"),
FlatButton(
onPressed: () => _openSecondWidget(),
child: Text("OPEN SECOND WIDGET"),
)
],
),
),
);
}
void _openSecondWidget() async {
var newTitle = await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SecondWidget(),
),
);
setState(() {
title = newTitle;
});
}
}
class SecondWidget extends StatelessWidget {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("SECOND WIDGET"),
),
body: Center(
child: FlatButton(
onPressed: () {
Navigator.of(context).pop("Hi from Second Widget");
},
child: Text("GO BACK"),
),
),
);
}
}
So the button on the first widget is pushing new widget on the screen and awaits for its result. When it gets the result from the second widget I'm using setState updating display of the title variable. And second widget has just one button which removes this widget from the back stack with some parameter which is in this case String, but it can be anything else.

I assume you just want to pass data from StatelessWidget to StatefulWidget and want to access it in its State. Then try this,
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: "Flutter Demo",),
);
}
}
class MyHomePage extends StatefulWidget {
final String title;
const MyHomePage({Key key, this.title}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title), //TODO: use `widget` to access properties
),
body: Center(
child: MyWidget(title: widget.title,),
),
);
}
}
class MyWidget extends StatefulWidget {
final String title;
const MyWidget({Key key, this.title}) : super(key: key);
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
#override
Widget build(BuildContext context) {
return Text('${widget.title}');
}
}

Related

How do I call a method from a stateful widget inside of a stateful widget

I am trying to clean up my code and want to place one of my methods from a stateful widget inside of a different class that is also a stateful widget but whenever I try to call the method it does not recognize it unless the class that I am calling it from it a stateless widget. I was wondering what the best way to get around this would be without changing the class?
Here is a simple example of my problem, I am trying to call
exampleStatefulWidget.testWidget() inside of MyHomePage which is a stateful widget.
import 'package:flutter/material.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: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
#override
State<MyHomePage> createState() => _MyHomePageState();
}
ExampleStatefulWidget exampleStatefulWidget = ExampleStatefulWidget();
ExampleStatelessWidget exampleStatelessWidget = ExampleStatelessWidget();
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
exampleStatefulWidget.testWidget(), // Can not call method that is inside of a stateful widget
exampleStatelessWidget.testWidget(); // Will call method but only if inside of a stateless widget
},
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
class ExampleStatefulWidget extends StatefulWidget {
const ExampleStatefulWidget({Key? key}) : super(key: key);
#override
_ExampleStatefulWidgetState createState() => _ExampleStatefulWidgetState();
}
class _ExampleStatefulWidgetState extends State<ExampleStatefulWidget> {
MyHomePage myHomePage = MyHomePage();
Widget testWidget() {
return Container(); // Do Something
}
#override
Widget build(BuildContext context) {
return Container();
}
}
class ExampleStatelessWidget extends StatelessWidget {
const ExampleStatelessWidget({ Key? key }) : super(key: key);
Widget testWidget() {
return Container(); // Do Something
}
#override
Widget build(BuildContext context) {
return Container(
);
}
}
ExampleStatefulWidget and _ExampleStatefulWidgetState are different class, you can create instance of _ExampleStatefulWidgetState instead. And use the method.
_ExampleStatefulWidgetState exampleStatefulWidget =
_ExampleStatefulWidgetState();
In this case, uses will be like
_MyHomePageState
class _MyHomePageState extends State<MyHomePage> {
Widget? widgetFromMethod;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Test'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (widgetFromMethod != null) widgetFromMethod!,
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
exampleStatelessWidget
.testWidget(); // Will call method but only if inside of a stateless widget
final gotWidget = exampleStatefulWidget.testWidget();
setState(() {
widgetFromMethod = gotWidget;
});
},
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
_ExampleStatefulWidgetState
class _ExampleStatefulWidgetState extends State<ExampleStatefulWidget> {
MyHomePage myHomePage = MyHomePage();
Widget testWidget() {
return Container(
color: Colors.purple,
width: 100,
height: 100,
child: Text("generated method from statefull"),
); // Do Something
}
#override
Widget build(BuildContext context) {
return Container();
}
}
if _ExampleStatefulWidgetState are on separate file, make it public removing _.
it will be ExampleStatefulWidgetState
Calling a printLog method of SecondWidget (Child) from FirstWidget(Parent)
Note: This is not recommended. Use any state management lib to achive this (flutter_bloc, provider etc)
import 'package:flutter/material.dart';
class FirstWidget extends StatefulWidget {
#override
_FirstWidgetState createState() => _FirstWidgetState();
}
class _FirstWidgetState extends State<FirstWidget> {
final key = GlobalKey<_SecondWidgetState>();
#override
Widget build(BuildContext context) {
return Scaffold(
body: SafeArea(
child: Container(
alignment: Alignment.center,
child: Column(
children: [
ElevatedButton(
onPressed: () {
key.currentState?.printLog();
},
child: Text("Click"),
),
SecondWidget(key)
],
),
),
),
);
}
}
class SecondWidget extends StatefulWidget {
SecondWidget(Key key) : super(key: key);
#override
_SecondWidgetState createState() => _SecondWidgetState();
}
class _SecondWidgetState extends State<SecondWidget> {
#override
Widget build(BuildContext context) {
return Container(padding: EdgeInsets.all(20), child: Container());
}
void printLog() {
print("I am called");
}
}

Flutter: Persisting Page States

Even after reading this and this, I still can't seem to wrap my head around storing page states in Flutter.
I've built a sample app, which has a main page called MyHomePage and a second page called SecondPage. MyHomePage has a floating action button, which displays SecondPage via Navigator.push(...). The second page contains a text field with an assigned controller. I would like to preserve the text field's text after I close and reopen SecondPage.
I've tried all sorts of combinations with setting buckets, page states and keys (inspired by the links above), but I couldn't make it work.
Also I'd like to store the whole page state automatically - without the need to write/retrieve every single value manually (in case I have a lot of text fields on the page).
Here is my code:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
class MyApp extends StatelessWidget {
final PageStorageBucket _bucket = new PageStorageBucket();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PageStorage(
bucket: _bucket,
child: MyHomePage(),
)
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(context, new MaterialPageRoute(builder: (context) => new SecondPage()));
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
final _aController = TextEditingController();
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
key: mykey,
autofocus: true,
),
)
);
}
}
EDIT:
Based on Ajay's answer, I was able to greatly simplify the working code. Turns out that in order to persist widget states manually, all you need is an instance of PageStorageBucket in combination with ValueKey instances.
Here are the modifications I did to Ajay's code:
Removed the after_layout plugin (initState method is sufficient).
Removed the global PageStorageKey instance (replaced it with a local ValueKey instance in the page that needs to use it).
Removed global instance of PageStorageBucket and replaced it with a final instance in MyApp, which is passed to the pages that need it via constructor attributes.
Removed PageStorage from the component tree.
Here is the resulting code (simplest working form):
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
final bucket = PageStorageBucket();
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(bucket: bucket,),
);
}
}
class MyHomePage extends StatefulWidget {
final PageStorageBucket bucket;
const MyHomePage({Key key, this.bucket}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SecondPage(bucket: widget.bucket,)));
}
}
class SecondPage extends StatefulWidget {
final PageStorageBucket bucket;
const SecondPage({Key key, this.bucket}) : super(key: key);
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage> {
static const KEY_A = ValueKey("secondPage.A");
final _aController = TextEditingController();
#override
void initState() {
super.initState();
_aController.addListener(_updateValue);
String value = widget.bucket.readState(context, identifier: KEY_A) ?? "";
_aController.text = value;
}
_updateValue() {
widget.bucket.writeState(context, _aController.text, identifier: KEY_A);
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
autofocus: true,
),
),
);
}
}
you need to read and write the state as well.
Check out the below code.
Note: I have used after_layout to initialize the text controller.
import 'package:after_layout/after_layout.dart';
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
PageStorageKey mykey = new PageStorageKey("testkey");
final PageStorageBucket _bucket = new PageStorageBucket();
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: PageStorage(
bucket: _bucket,
child: MyHomePage(),
));
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("State demo"),
),
body: Center(
child: Column(
children: <Widget>[],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _openSecondPage,
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
_openSecondPage() {
Navigator.push(
context, new MaterialPageRoute(builder: (context) => new SecondPage()));
}
}
class SecondPage extends StatefulWidget {
#override
_SecondPageState createState() => _SecondPageState();
}
class _SecondPageState extends State<SecondPage>
with AfterLayoutMixin<SecondPage> {
final _aController = TextEditingController();
#override
void initState() {
super.initState();
_aController.addListener(_updateValue);
}
#override
void afterFirstLayout(BuildContext context) {
String value =
_bucket.readState(context, identifier: ValueKey(mykey)) ?? "";
print(value);
_aController.text = value;
}
_updateValue() {
_bucket.writeState(context, _aController.text, identifier: ValueKey(mykey));
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Second page"),
),
body: Center(
child: TextField(
controller: _aController,
key: mykey,
autofocus: true,
),
),
);
}
}

How to call method in state class in flutter?

I want to call the method from HomeState class to _MyHomePageState class.But i have no idea to do that.
this is main.dart :
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#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> {
#override
Widget build(BuildContext context) {
Home home = new Home();
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: (){}
),
);
}
}
Home class:
class Home extends StatefulWidget {
#override
State<StatefulWidget> createState() {
return HomeState();
}
}
class HomeState extends State<Home> {
int numberPrint(){
setState((){});
}
#override
Widget build(BuildContext context) {
}
}
I want to call the method numberPrint() in floatingbutton in _MyHomePageState class in main.dart.
Please help me to do that.
Please take a look at how I would have handled, this is not the best, but it would give you a better idea.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
//MyHomePage---------------------------------------------------
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
MyController controller = MyController();
#override
Widget build(BuildContext context) {
Home home = new Home(controller:controller);
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: controller.execute,
),
);
}
}
//Home---------------------------------------------------------------------
class Home extends StatefulWidget {
Home({this.controller});
final MyController controller;
#override
State<StatefulWidget> createState() {
return HomeState();
}
}
class HomeState extends State<Home> {
initState(){
widget.controller.addListener(numberPrint);
}
numberPrint(){
setState((){});
}
#override
Widget build(BuildContext context) {
}
}
//MyController--------------------------------------------------------
class MyController{
Function listener;
addListener(Function fn){
listener = fn;
}
execute(){
listener();
}
}
In general, I use some kind of page nerve center through which widgets can register their setState functions or other things such as FocusNode and even data.
For example,
class PageNerveCenter{
Function requireHomeRebuild;
}
PageNerveCenter _pageNerveCenter = PageNerveCenter();
class Home extends StatefulWidget{...}
class _HomeState extends State<Home>{
...
#override
initState(){
super.initState();
// register setState
_pageNerveCenter.requireHomeRebuild = (){
setState((){})
};
}
#override
dispose(){
// remove setState when object is disposed
_pageNerveCenter.requireHomeRebuild = (){};
}
...
}
In some other widget (same file/library),
_pageNerveCenter.requireHomeRebuild();
This approach then allows you to call a widget's setState from anywhere in the file, from inside another widget.
In my own experience, this approach has helped to easily break down my widget tree and only rebuild those widgets that actually need to rebuilt too.
I would certainly like to hear other more experienced flutter developers about their take on this approach since I am relatively new too.

Flutter updating second parent when button is pressed

I am working on a flutter app and I want to update the second level parent's state when a button is pressed. When the "PressMe" button is pressed, I want MyHomePage's state to have Widget2's title string saved in it's own state. Can someone please help me out with this? The button is a lower level widget and I want to pass the data up two levels. Thanks!
//main.dart
import 'package:flutter/material.dart';
import 'Widget1.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String title2;
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Hello"),
),
body: Center(
child: Widget1(),
),
);
}
}
///////////////////////////////
//Widget1.dart
import 'package:flutter/material.dart';
import 'Widget2.dart';
class Widget1 extends StatefulWidget {
_Widget1State createState() => _Widget1State();
}
class _Widget1State extends State<Widget1> {
#override
Widget build(BuildContext context) {
return Widget2();
}
}
///////////////////////////////
//Widget2.dart
import 'package:flutter/material.dart';
class Widget2 extends StatefulWidget {
final String title = "Hello from Widget2";
_Widget2State createState() => _Widget2State();
}
class _Widget2State extends State<Widget2> {
String title = "Hello from Widget2";
#override
Widget build(BuildContext context) {
return RaisedButton(
onPressed: null,
child: Text(
'PressMe',
style: TextStyle(fontSize: 20)
),
);
}
}
Thanks!
The easiest way to update your parent widget/class from a child is to pass down a function you create in the parent, then call that function from your child when you need to update it. However, that gets messy if you need to pass it down through multiple children. Usually in this case you'll want your parent to be a StatefulWidget and call setState inside the function you create when you assign the new title.
The next solution is to use InheritedWidgets or ChangeNotifiers.
The ideal solution would be to use some form of state management such as Provider or Bloc.

How to prevent rebuild StatelessWidget children of PageView

I've create simple PageView app to test multiple pages.
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
#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> {
#override
Widget build(BuildContext context) {
final firstPage = FirstPage(key: Key("FirstPage"));
final secondPage = SecondPage(key: Key("SecondPage"));
debugPrint("_MyHomePageState.build");
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView(
children: <Widget>[
firstPage,
secondPage,
],
),
);
}
}
class FirstPage extends StatelessWidget {
FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
}
class SecondPage extends StatelessWidget {
SecondPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint("SecondPage.build");
return Container(
child: Center(
child: Text("Second Page"),
),
);
}
}
Even thought _MyHomePageState.build has been shown only once, FirstPage.build and SecondPage.build were printed on every page changes.
What I'd like to prevent unnecessary page draw, how can I accomplish this?
You can achieve so by using
1. const keyword
Make your widgets accept to be const:
class FirstPage extends StatelessWidget {
const FirstPage({Key key}) : super(key: key);
#override
Widget build(BuildContext context) {
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
}
and call it with const keyword:
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: PageView(
children: <Widget>[
const firstPage(),
const secondPage(),
],
),
);
2. AutomaticKeepAliveClientMixin
Convert your StatelessWidget to StatefullWidget.
class FirstPage extends StatefulWidget {
FirstPage({Key key}) : super(key: key);
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage> {
#override
Widget build(BuildContext context) {
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
}
Extends AutomaticKeepAliveClientMixin on StatefullWidget created State.
class _FirstPageState extends State<FirstPage> with AutomaticKeepAliveClientMixin {
Call super on the build method.
#override
Widget build(BuildContext context) {
super.build(context);
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
Override wantKeepAlive getter with true returned value.
#override
bool get wantKeepAlive => true;
And then your widget tree won't dispose of this widget so it won't rebuild over and over.
Code Example:
class FirstPage extends StatefulWidget {
FirstPage({Key key}) : super(key: key);
#override
_FirstPageState createState() => _FirstPageState();
}
class _FirstPageState extends State<FirstPage>
with AutomaticKeepAliveClientMixin {
#override
Widget build(BuildContext context) {
super.build(context);
debugPrint("FirstPage.build");
return Container(
child: Center(
child: Text("First Page"),
),
);
}
#override
bool get wantKeepAlive => true;
}
3. MVVM Architecture with any State-management solution you like
It will save your state on ViewModel away from the View, so your UI can rebuild itself anytime it wants with no worries about your State because the ViewModel is still the same.
You should always imagine that your build() methods (for both StatefulWidget and StatelessWidget) are being called 60 times per second, so they should be simple and idempotent. Anything else should be moved into a StatefulWidget initState() and friends.
It's easy!
pageController can help you.
Just in your _MyHomePageState
Declare final pageController = PageController(keepPage: false);
And in your PageView
PageView(
controller: pageController,
children: <Widget>[
firstPage,
secondPage,
],
)
Good Luck.