Tab切换时的问题
在一个SPA的Flutter项目中可以用StatefulWidget
保存页面状态,但是当出现页面切换的情况时,例如通过底部导航栏(BottomNavigationBar
)或者标签栏(TabBar
)组件来切换页面内容,每次导航栏或标签栏切换页面时,之前的页面就被清理了。
出现上面问题的原因是,之前页面的状态(State)没有被保留下来,状态的reset导致页面发生了初始化。
我们使用TabBarView做成的页面的中,从第一页面调到第二页再返回后,
之前的状态丢失了。我们期待的效果应该是下面这样:
代码示例
通过代码展示如何实现Tab切换时的状态保存。
首先通过TabBar
和TabBarView
实现基本的APP,如下:
(参考Tab页切换)
main.dart
import 'package:flutter/material.dart';
import './page1.dart';
import './page2.dart';
import './page3.dart';
void main() => runApp(
MaterialApp(
debugShowCheckedModeBanner: false,
home: MyApp(),
),
);
class TabInfo {
String label;
Widget widget;
TabInfo(this.label, this.widget);
}
class MyApp extends StatelessWidget {
final List<TabInfo> _tabs = [
TabInfo("FIRST", Page1()),
TabInfo("SECOND", Page2()),
TabInfo("THIRD", Page3()),
];
@override
Widget build(BuildContext context) {
return DefaultTabController(
length: _tabs.length,
child: Scaffold(
appBar: AppBar(
title: Text('Tab Controller'),
bottom: PreferredSize(
child: TabBar(
isScrollable: true,
tabs: _tabs.map((TabInfo tab) {
return Tab(text: tab.label);
}).toList(),
),
preferredSize: Size.fromHeight(30.0),
),
),
body: TabBarView(children: _tabs.map((tab) => tab.widget).toList()),
),
);
}
}
page1.dart
import 'package:flutter/material.dart';
class Page1 extends StatefulWidget {
@override
State<StatefulWidget> createState() {
return _Page1State();
}
}
class Page1Params {
int counter1 = 0;
int counter2 = 0;
}
class _Page1State extends State<Page1> {
Page1Params _params = Page1Params();
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'${_params.counter1}',
style: TextStyle(
fontSize: 48,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove, size: 32.0),
onPressed: () {
setState(() {
_params.counter1--;
});
},
),
IconButton(
icon: Icon(Icons.add, size: 32.0),
onPressed: () {
setState(() {
_params.counter1++;
});
},
),
],
),
Text(
'${_params.counter2}',
style: TextStyle(
fontSize: 48,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove, size: 32.0),
onPressed: () {
setState(() {
_params.counter2--;
});
},
),
IconButton(
icon: Icon(Icons.add, size: 32.0),
onPressed: () {
setState(() {
_params.counter2++;
});
},
),
],
)
],
),
);
}
}
page2.dart 、page3.dart
page2和3简单实现如下:
import 'package:flutter/material.dart';
class Page2 extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Container();
}
}
到此为止,我们实现了上面图1的状态
使用PageStorage管理状态
Flutter提供了PageStorage
用于页面切换时的状态保存。使用方法很简单:
1. 构造函数中传入StorageKey
为Page1 添加一个带参数的构造函数,通过其将Key直接传给super,后续通过此key即可恢复状态
class Page1 extends StatefulWidget {
Page1({Key key}) : super(key: key); //增加一行
@override
State<StatefulWidget> createState() {
return _Page1State();
}
}
2. 创建widget时指定key
class MyApp extends StatelessWidget {
final List<TabInfo> _tabs = [
TabInfo(
"FIRST",
Page1(key: PageStorageKey<String>("key_Page1")) // 指定key
),
TabInfo("SECOND", Page2()),
TabInfo("THIRD", Page3()),
];
3. 读取state
重写State类的didChangeDependencies
方法, 在里面通过readState
从PageStorage
读取并恢复被保存的状态。
class _Page1State extends State<Page1> {
Page1Params _params;
@override
void didChangeDependencies() { //重写此方法
Page1Params p = PageStorage.of(context).readState(context);
if (p != null) {
_params = p;
} else {
_params = Page1Params();
}
super.didChangeDependencies();
}
didChangeDependencies
会紧跟在initState
之后被调用,便于进行state初始化
4. 保存state
相应的,在我们setState
的同时通过writeState
将状态保存进PageStorage
。
@override
Widget build(BuildContext context) {
return Container(
padding: EdgeInsets.all(20.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
Text(
'${_params.counter1}',
style: TextStyle(
fontSize: 48,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove, size: 32.0),
onPressed: () {
setState(() {
_params.counter1--;
});
PageStorage.of(context).writeState(context, _params);
},
),
IconButton(
icon: Icon(Icons.add, size: 32.0),
onPressed: () {
setState(() {
_params.counter1++;
});
PageStorage.of(context).writeState(context, _params);
},
),
],
),
Text(
'${_params.counter2}',
style: TextStyle(
fontSize: 48,
),
),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
IconButton(
icon: Icon(Icons.remove, size: 32.0),
onPressed: () {
setState(() {
_params.counter2--;
});
PageStorage.of(context).writeState(context, _params);
},
),
IconButton(
icon: Icon(Icons.add, size: 32.0),
onPressed: () {
setState(() {
_params.counter2++;
});
PageStorage.of(context).writeState(context, _params);
},
),
],
)
],
),
);
}
至此,我们可以实现期望效果
总结
总结一下通过页面切换时通过PageStorage
保存并恢复状态的一般步骤:
- StatefulWidget 类增加带
PageStorageKey
的构造函数 - 创建widget时传入
PageStorageKey
- 重写
didChangeDependencies
,通过readState
恢复state setState
时,通过writeState
保存state