Flutter BottomNavigationBar 页面缓存

使用BottomNavigationBar实现页面的类似Android中Fragment切换的效果时,发现每次切换页面的时候,上个页面都会被销毁,新的页面都会重新创建,但是有时不需要重新创建,因此需要对页面进行缓存.

IndexedStack

IndexedStack可以同时创建多个Widget然后根据index展示对应的页面,关键代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);

@override
State<MainPage> createState() => _MainPageState();
}
class _MainPageState extends State<MainPage> {
var pages = [
WeiChatPage(),
DevicePage(),
MinePage()
];
var titles = ["首页","设备","我的"];
var currentIndex = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leadingWidth: 70,
leading: currentIndex == 1 ? TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.only(left: 10)),
),
onPressed: (){},
child: Text("切换设备",style: TextStyle(
color: Colors.black,
fontSize: 14
),),
) : null,
actions: currentIndex == 1 ? [
IconButton(onPressed: (){}, icon: Icon(Icons.add,color: Colors.black,))
] : null,
backgroundColor: Colors.white,
elevation: 0.2,
centerTitle: true,
title: Text(titles[currentIndex],style: TextStyle(
color: Colors.black
),),
),
body: IndexedStack( /// 在这里修改
index: currentIndex,
children: pages,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
type: BottomNavigationBarType.fixed,
onTap: (index) {
setState(() {
currentIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.messenger_outlined),
label:titles[0]),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: titles[1]),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: titles[2]),
],
),
);
}
}

PageView

使用PageView可实现只缓存某个页面,这里我使用了flukit

1
2
3
4
5
6
7
8
9
dependencies:
flutter:
sdk: flutter


# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2
flukit: ^3.0.1 #工具类

或者结合with AutomaticKeepAliveClientMixin封装 KeepAliveWrapper配合PageView实现 页面缓存!

KeepAliveWrapper.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import 'package:flutter/material.dart';

class KeepAliveWrapper extends StatefulWidget {
const KeepAliveWrapper(
{Key? key, @required this.child, this.keepAlive = true})
: super(key: key);
final Widget? child;
final bool keepAlive;
@override
State<KeepAliveWrapper> createState() => _KeepAliveWrapperState();
}

class _KeepAliveWrapperState extends State<KeepAliveWrapper>
with AutomaticKeepAliveClientMixin {
@override
Widget build(BuildContext context) {
return widget.child!;
}

@override
bool get wantKeepAlive => widget.keepAlive;
@override
void didUpdateWidget(covariant KeepAliveWrapper oldWidget) {
if (oldWidget.keepAlive != widget.keepAlive) {
// keepAlive 状态需要更新,实现在 AutomaticKeepAliveClientMixin 中
updateKeepAlive();
}
super.didUpdateWidget(oldWidget);
}
}

然后将页面使用KeepAliveWrapper包裹,如下所示,这里我只包裹了WeiChatPage页面

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
class MainPage extends StatefulWidget {
const MainPage({Key? key}) : super(key: key);

@override
State<MainPage> createState() => _MainPageState();
}

class _MainPageState extends State<MainPage> {
/// 初始化控制器
late PageController pageController;
var pages = [
KeepAliveWrapper(child: WeiChatPage(),),
DevicePage(),
MinePage()
];
var titles = ["首页","设备","我的"];
var currentIndex = 0;
@override
void initState() {
// TODO: implement initState
super.initState();
//创建控制器的实例
pageController = new PageController(
//用来配置PageView中默认显示的页面 从0开始
initialPage: 0,
//为true是保持加载的每个页面的状态
keepPage: true,
);

}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
leadingWidth: 70,
leading: currentIndex == 1 ? TextButton(
style: ButtonStyle(
padding: MaterialStateProperty.all(EdgeInsets.only(left: 10)),
),
onPressed: (){},
child: Text("切换设备",style: TextStyle(
color: Colors.black,
fontSize: 14
),),
) : null,
actions: currentIndex == 1 ? [
IconButton(onPressed: (){}, icon: Icon(Icons.add,color: Colors.black,))
] : null,
backgroundColor: Colors.white,
elevation: 0.2,
centerTitle: true,
title: Text(titles[currentIndex],style: TextStyle(
color: Colors.black
),),
),
body: PageView( /// 在这里修改
// index: currentIndex,
/// 控制跳转翻页的控制器
controller: pageController,
//禁止滑动
physics: const NeverScrollableScrollPhysics(),
/// 页面滑动
/// 这里设置 PageView 页面滑动也能
onPageChanged: (index) {
setState(() {
// 更新当前的索引值
currentIndex = index;
});
},
children: pages,
),
bottomNavigationBar: BottomNavigationBar(
currentIndex: currentIndex,
type: BottomNavigationBarType.fixed,
onTap: (index) {
// 控制 PageView 跳转到指定的页面
pageController.jumpToPage(index);
setState(() {
currentIndex = index;
});
},
items: [
BottomNavigationBarItem(
icon: Icon(Icons.messenger_outlined),
label:titles[0]),
BottomNavigationBarItem(
icon: Icon(Icons.settings),
label: titles[1]),
BottomNavigationBarItem(
icon: Icon(Icons.person),
label: titles[2]),
],
),
);
}
}