• 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);
    
    • 1
    • 2
    • 3

    如果需要每次进入页面刷新得话,可以添加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);
        }
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    返回指定tabbar页面

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

    TabbarProvider代码

    import 'package:flutter/material.dart';
    
    class TabbarProvider extends ChangeNotifier {
      int _currentIndex = 0;
    
      int get currentIndex => _currentIndex;
    
      set currentIndex(int index) {
        _currentIndex = index;
        notifyListeners();
      }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    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;
      }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    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),
          ),
        );
      }
    }
    
    • 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
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119

    方式二:

    如果不需要返回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);
                  });
                },
              );
            }),
          ),
        );
      }
    
    • 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

    方式三:

    这是通过IndexedStack保持页面状态的,还是改的build方法

    ///  使用IndexedStack保持页面状态如下:
    ///  这种方式有个小缺点:IndexedStack中管理的子页面在第一次加载时便实例化了所有的子页面State
    
    • 1
    • 2
    
    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;
            });
          },
        ),
      );
    }
    
    • 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
  • 相关阅读:
    【光学】基于matlab模拟双孔干涉附matlab代码
    Scrum和Kanban方法的结合:Scrumban的实施指南
    spring.mvc.static-path-pattern和spring.resources.static-locations
    国内外AI programmer 大全集--持续更新
    Unbuntu-18-network-issue
    Facebook的预填问题默认可以设定哪些类型。
    LinkedList与链表
    Linux 安装 git
    c#.NET技术做到ChatGPT流式响应并实现打字机效果 实现ChatGPT的Stream传输
    C++实现基于自动机的句子分词程序
  • 原文地址:https://blog.csdn.net/iotjin/article/details/126870716