Flutter - APP主界面Tabbar保持页面状态

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中

方式一:

  • 支持保持页面状态
  • 通过改变ProvidercurrentIndex可从指定页面返回tabbar指定的index页面(退出登录的话需要改currentIndex为0)

每次进入页面刷新

  
  void didChangeDependencies() {
    
    
    super.didChangeDependencies();

    var currentIndex = Provider.of<TabbarProvider>(context).currentIndex;
    if (currentIndex == 1) {
    
    
      _requestData(isShowLoading: true);
    }
  }

返回指定tabbar页面

JhNavUtils.pushReplacement(context, '/home'); // 返回tabbar 主页面
Provider.of<TabbarProvider>(context, listen: false).currentIndex = 1;

TabbarProvider代码

import 'package:flutter/material.dart';

class TabbarProvider extends ChangeNotifier {
    
    
  int _currentIndex = 0;

  int get currentIndex => _currentIndex;

  set currentIndex(int index) {
    
    
    _currentIndex = index;
    notifyListeners();
  }
}

main.dart 代码

  
  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;
  }

BaseTabBar代码

///  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;
        });
      },
    ),
  );
}

猜你喜欢

转载自blog.csdn.net/iotjin/article/details/126870716