最近在用flutter做app的开发,期间遇到了一个问题,就是当使用tab组件当时候,如果跳着点击tab(比如1->3, 2->4,1->1这种点击),则会导致中间当页面重新加载,这种体验十分不友好。
一开始是在网上找当解决方案,让页面 with AutomaticKeepAliveClientMixin, 然后 bool get wantKeepAlive => true; 但是这种方法治标不治本。当你按顺序切换tab的时候,比如 1->2, 2->3, 3->2 时页面确实不会重新加载,但是像我上面描述的1->3, 1->4,4->2 这种跳着切换时,AutomaticKeepAliveClientMixin 就不起作用了,中间的页面还是会重新加载。
于是乎本着探索的精神,终于让我发现了一个相对简单的解决方案,那就是使用 IndexedStack 部件。用这个之后,无论你怎么点击,都能够正确的保持页面的状态,并且dispose的时候,也能够正确的销毁相关组件。以下是示例代码,很简单,大家可以尝试一下:
import 'package:flutter/material.dart';
void main() => runApp(MyApp());
class MyAppextends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title:"Don't rebuilt!",
theme:ThemeData(primarySwatch: Colors.blue),
home:FirstPage(),
);
}
}
class FirstPageextends StatelessWidget {
FirstPage({Key key}) :super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(title:Text("I'm the first page")),
body:Center(
child:RaisedButton(
child:Text("go to the tab page"),
onPressed: () => Navigator.of(context).push(
MaterialPageRoute(
builder: (context) =>MyTabPage(),
),
),
),
));
}
}
class MyTabPageextends StatefulWidget {
MyTabPage({Key key}) :super(key: key);
@override
_MyTabPageState createState() =>_MyTabPageState();
}
class _MyTabPageStateextends Statewith SingleTickerProviderStateMixin {
TabController_tabController;
int_currentTabIndex =0;
List_realPages = [Tab1(),Tab2(),Tab3(),Tab4()];
//lazy init
List_tabPages = [Tab1(),Container(),Container(),Container()];
List_hasInit = [true,false,false,false];
List_tabsButton = [
Tab(child:Text("tab1", style:TextStyle(color: Colors.black))),
Tab(child:Text("tab2", style:TextStyle(color: Colors.black))),
Tab(child:Text("tab3", style:TextStyle(color: Colors.black))),
Tab(child:Text("tab4", style:TextStyle(color: Colors.black))),
];
@override
void initState() {
print("MyTabPage initState.");
super.initState();
_tabController =TabController(length:_realPages.length, vsync:this)
..addListener(() {
int selectedIndex =_tabController.index;
if (_currentTabIndex != selectedIndex) {
if (!_hasInit[selectedIndex]) {
//lazy init
_tabPages[selectedIndex] =_realPages[selectedIndex];
_hasInit[selectedIndex] =true;
}
setState(() =>_currentTabIndex = selectedIndex);
}
});
}
@override
void dispose() {
print("MyTabPage dispose.");
_tabController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar:AppBar(title:Text("Don't rebuilt!")),
body:IndexedStack(index:_currentTabIndex, children:_tabPages),
bottomNavigationBar:TabBar(controller:_tabController, tabs:_tabsButton),
);
}
}
class Tab1extends StatefulWidget {
@override
State createState() {
return _Tab1State();
}
}
class _Tab1Stateextends State {
@override
void initState() {
super.initState();
print("tab1 initState");
}
@override
void dispose() {
print("tab1 dispose");
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(body:Center(child:Text("I'm tab1")));
}
}
class Tab2extends StatefulWidget {
@override
State createState() {
return _Tab2State();
}
}
class _Tab2Stateextends State {
@override
void initState() {
super.initState();
print("tab2 initState");
}
@override
void dispose() {
print("tab2 dispose");
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(body:Center(child:Text("I'm tab2")));
}
}
class Tab3extends StatefulWidget {
@override
State createState() {
return _Tab3State();
}
}
class _Tab3Stateextends State {
@override
void initState() {
super.initState();
print("tab3 initState");
}
@override
void dispose() {
print("tab3 dispose");
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(body:Center(child:Text("I'm tab3")));
}
}
class Tab4extends StatefulWidget {
@override
State createState() {
return _Tab4State();
}
}
class _Tab4Stateextends State {
@override
void initState() {
super.initState();
print("tab4 initState");
}
@override
void dispose() {
print("tab4 dispose");
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(body:Center(child:Text("I'm tab4")));
}
}
Performing hot reload...
Syncing files to device Android SDK built for x86...
Reloaded 1 of 440 libraries in 284ms.
I/flutter (21209): MyTabPage initState.
I/flutter (21209): tab1 initState
I/flutter (21209): tab3 initState
I/flutter (21209): tab2 initState
I/flutter (21209): tab4 initState
I/flutter (21209): tab1 dispose
I/flutter (21209): tab2 dispose
I/flutter (21209): tab3 dispose
I/flutter (21209): tab4 dispose
I/flutter (21209): MyTabPage dispose.