demo 地址: https://github.com/iotjin/jh_flutter_demo
代码不定时更新,请前往github查看最新代码
参考:
Flutter底部tab切换保持页面状态
完美解决flutter Tab/TabBar切换, TabView 页面状态保持
Flutter 三种方式实现页面切换后保持原页面状态
APP主界面每个模块的页面一般由底部tabbar+顶部导航 + 中间内容组成的。一般情况下,每个模块的页面初始化一次就可以了,每次都刷新的话不太友好。
这里说一下在tabbar中保持页面状态的方式
/// 通过 PageView + AutomaticKeepAliveClientMixin 保持页面状态(进到哪个页面,哪个页面开始初始化)
/// 在需要保持页面状态的子页面State中,继承AutomaticKeepAliveClientMixin并重写方法 wantKeepAlive => true
/// 并且它们的[build]方法必须调用super.build(context);
如果需要每次进入页面刷新得话,可以添加
Provider
状态管理
在didChangeDependencies判断currentIndex,以下代码添加了Provider
状态管理
如果需要在某个页面跳转返回到tabbar的指定页面,
Provider
create 添加到main.dart
,否则写在basetabbar中
Provider
的currentIndex
可从指定页面返回tabbar指定的index页面(退出登录的话需要改currentIndex
为0) void didChangeDependencies() {
super.didChangeDependencies();
var currentIndex = Provider.of<TabbarProvider>(context).currentIndex;
if (currentIndex == 1) {
_requestData(isShowLoading: true);
}
}
JhNavUtils.pushReplacement(context, '/home'); // 返回tabbar 主页面
Provider.of<TabbarProvider>(context, listen: false).currentIndex = 1;
import 'package:flutter/material.dart';
class TabbarProvider extends ChangeNotifier {
int _currentIndex = 0;
int get currentIndex => _currentIndex;
set currentIndex(int index) {
_currentIndex = index;
notifyListeners();
}
}
Widget build(BuildContext context) {
JhScreenUtils.init(context);
final Widget app = MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => ThemeProvider()),
ChangeNotifierProvider(create: (_) => TabbarProvider()),
],
child: Consumer<ThemeProvider>(
builder: (_, ThemeProvider provider, __) {
return _buildMaterialApp(provider);
},
));
return app;
}
/// base_tabbar.dart
///
/// Created by iotjin on 2020/03/08.
/// description: tabbar基类
import 'package:flutter/material.dart';
import 'package:badges/badges.dart';
import 'package:provider/provider.dart';
import '/jh_common/utils/jh_image_utils.dart';
import '/project/configs/colors.dart';
import '/project/provider/tabbar_provider.dart';
import '/project/provider/theme_provider.dart';
import '/project/one/one_page.dart';
import '/project/Two/two_page.dart';
import '/project/Three/three_page.dart';
import '/project/four/four_page.dart';
const double _iconWH = 24.0;
const double _fontSize = 10.0;
class BaseTabBar extends StatefulWidget {
BaseTabBar({Key? key}) : super(key: key);
_BaseTabBarState createState() => _BaseTabBarState();
}
class _BaseTabBarState extends State<BaseTabBar> {
// int _currentIndex = 0;
List<Widget> _pageList = [OnePage(), TwoPage(), ThreePage(), FourPage()];
PageController _pageController = PageController();
List<BottomNavigationBarItem> getBottomTabs(iconColor) {
return [
BottomNavigationBarItem(
label: '微信',
icon: JhLoadAssetImage('tab/nav_tab_1', width: _iconWH),
activeIcon: JhLoadAssetImage('tab/nav_tab_1_on', width: _iconWH, color: iconColor),
),
BottomNavigationBarItem(
label: '通讯录',
icon: JhLoadAssetImage('tab/nav_tab_2', width: _iconWH),
activeIcon: JhLoadAssetImage('tab/nav_tab_2_on', width: _iconWH, color: iconColor),
),
BottomNavigationBarItem(
label: '发现',
// icon: JhLoadAssetImage('tab/nav_tab_3', width: _iconWH),
activeIcon: JhLoadAssetImage('tab/nav_tab_3_on', width: _iconWH, color: iconColor),
icon: Badge(
padding: EdgeInsets.all(4),
position: BadgePosition.topEnd(top: -4, end: -4),
child: JhLoadAssetImage('tab/nav_tab_3', width: _iconWH)),
// activeIcon: Badge(
// padding: EdgeInsets.all(4),
// position: BadgePosition.topRight(top: -4, right: -4),
// child: JhLoadAssetImage('tab/nav_tab_3_on', width: _iconWH)),
),
BottomNavigationBarItem(
label: '我的',
icon: JhLoadAssetImage('tab/nav_tab_4', width: _iconWH),
activeIcon: JhLoadAssetImage('tab/nav_tab_4_on', width: _iconWH, color: iconColor),
),
];
}
void initState() {
// TODO: implement initState
super.initState();
}
void dispose() {
_pageController.dispose();
super.dispose();
}
Widget build(BuildContext context) {
// TODO: 通过ThemeProvider进行主题管理
final provider = Provider.of<ThemeProvider>(context);
var bgColor = KColors.dynamicColor(context, KColors.kTabBarBgColor, KColors.kTabBarBgDarkColor);
var normalTextColor =
KColors.dynamicColor(context, KColors.kTabBarNormalTextColor, KColors.kTabBarNormalTextDarkColor);
var selectTextColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);
var selectIconColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);
final tabbarProvider = Provider.of<TabbarProvider>(context);
/// 通过 PageView + AutomaticKeepAliveClientMixin 保持页面状态(进到哪个页面,哪个页面开始初始化)
/// 在需要保持页面状态的子页面State中,继承AutomaticKeepAliveClientMixin并重写方法 wantKeepAlive => true
/// 并且它们的[build]方法必须调用super.build(context);
return Scaffold(
body: PageView(
physics: const NeverScrollableScrollPhysics(), // 禁止滑动
controller: _pageController = PageController(initialPage: tabbarProvider.currentIndex),
children: _pageList,
onPageChanged: (int index) => tabbarProvider.currentIndex = index,
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: bgColor,
// 未选中颜色
unselectedItemColor: normalTextColor,
// 选中颜色,与fixedColor不能同时设置
// selectedItemColor: selectColor,
// 选中的颜色
fixedColor: selectTextColor,
unselectedFontSize: _fontSize,
selectedFontSize: _fontSize,
// 配置底部BaseTabBar可以有多个按钮
type: BottomNavigationBarType.fixed,
items: getBottomTabs(selectIconColor),
// 配置对应的索引值选中
currentIndex: tabbarProvider.currentIndex,
onTap: (index) => _pageController.jumpToPage(index),
),
);
}
}
如果不需要返回tabbar的指定页面,可以把
BaseTabBar
的build
方法改完以下代码
main.dart
移除ChangeNotifierProvider(create: (_) => TabbarProvider()),
Widget build(BuildContext context) {
// TODO: 通过ThemeProvider进行主题管理
final provider = Provider.of<ThemeProvider>(context);
var bgColor = KColors.dynamicColor(context, KColors.kTabBarBgColor, KColors.kTabBarBgDarkColor);
var normalTextColor =
KColors.dynamicColor(context, KColors.kTabBarNormalTextColor, KColors.kTabBarNormalTextDarkColor);
var selectTextColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);
var selectIconColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);
/// 通过 PageView + AutomaticKeepAliveClientMixin 保持页面状态(进到哪个页面,哪个页面开始初始化)
/// 在需要保持页面状态的子页面State中,继承AutomaticKeepAliveClientMixin并重写方法 wantKeepAlive => true
/// 并且它们的[build]方法必须调用super.build(context);
return ChangeNotifierProvider(
create: (_) => TabbarProvider(),
child: Scaffold(
body: PageView(
physics: const NeverScrollableScrollPhysics(), // 禁止滑动
controller: _pageController,
children: _pageList,
),
bottomNavigationBar: Consumer<TabbarProvider>(builder: (_, provider, __) {
return BottomNavigationBar(
backgroundColor: bgColor,
// 未选中颜色
unselectedItemColor: normalTextColor,
// 选中颜色,与fixedColor不能同时设置
// selectedItemColor: selectColor,
// 选中的颜色
fixedColor: selectTextColor,
unselectedFontSize: _fontSize,
selectedFontSize: _fontSize,
// 配置底部BaseTabBar可以有多个按钮
type: BottomNavigationBarType.fixed,
items: getBottomTabs(selectIconColor),
// 配置对应的索引值选中
currentIndex: provider.currentIndex,
// 配置对应的索引值选中
onTap: (int index) {
setState(() {
// 改变状态
provider.currentIndex = index;
_pageController.jumpToPage(index);
});
},
);
}),
),
);
}
这是通过IndexedStack保持页面状态的,还是改的build方法
/// 使用IndexedStack保持页面状态如下:
/// 这种方式有个小缺点:IndexedStack中管理的子页面在第一次加载时便实例化了所有的子页面State
Widget build(BuildContext context) {
// TODO: 通过ThemeProvider进行主题管理
final provider = Provider.of<ThemeProvider>(context);
var bgColor = KColors.dynamicColor(context, KColors.kTabBarBgColor, KColors.kTabBarBgDarkColor);
var normalTextColor =
KColors.dynamicColor(context, KColors.kTabBarNormalTextColor, KColors.kTabBarNormalTextDarkColor);
var selectTextColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);
var selectIconColor = KColors.dynamicColor(context, provider.getThemeColor(), KColors.kThemeColor);
return Scaffold(
body: IndexedStack(
index: _currentIndex,
children: _pageList,
),
bottomNavigationBar: BottomNavigationBar(
backgroundColor: bgColor,
// 未选中颜色
unselectedItemColor: normalTextColor,
// 选中颜色,与fixedColor不能同时设置
// selectedItemColor: selectColor,
// 选中的颜色
fixedColor: selectTextColor,
unselectedFontSize: _fontSize,
selectedFontSize: _fontSize,
// 配置底部BaseTabBar可以有多个按钮
type: BottomNavigationBarType.fixed,
items: getBottomTabs(selectIconColor),
// 配置对应的索引值选中
currentIndex: this._currentIndex,
onTap: (int index) {
setState(() {
// 改变状态
this._currentIndex = index;
});
},
),
);
}