美文网首页Flutter圈子All in FlutterFlutter中文社区
Flutter代码锦囊---切换时页面保持状态

Flutter代码锦囊---切换时页面保持状态

作者: 何小有 | 来源:发表于2019-04-23 14:10 被阅读20次

    一个正常的Flutter项目中,通过底部导航栏(BottomNavigationBar)或者标签栏(TabBar)组件来切换页面内容,是很正常的操作。但是大家是否有发现,每次导航栏或标签栏切换页面时,之前的页面就被清理了。比如,第一个页面的列表视图(ListView)已经滑动到底部,切换到第二个页面以后再回来,第一个页面的列表又回到了顶部。

    出现上面问题的原因是,页面的状态(State)没有被保留下来,所以每次页面,都会初始化一次状态。我们可以通过自动保持活动客户端混合(AutomaticKeepAliveClientMixin)抽象类来解决这个问题。

    首先是新建一个dart文件,把Flutter应用和页面的架子搭起来,然后运行调试,确认应用可以正常跑起来。

    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Flutter Demo',
          home: MyHomePage(title: 'Flutter Demo 主页'),
        );
      }
    }
    
    class MyHomePage extends StatefulWidget {
      MyHomePage({Key key, this.title}) : super(key: key);
    
      final String title;
    
      @override
      _MyHomePageState createState() => _MyHomePageState();
    }
    
    class _MyHomePageState extends State<MyHomePage> {
      // TODO:当前页面视图的相关变量。
    
      // TODO:切换页面视图的方法。
    
      // TODO:集中管理导航项目列表和页面组件列表。
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          // TODO:页面视图的主要内容。
        );
      }
    }
    

    下面再新建一个dart文件,作为导航项对应的页面组件,并再里面放上一个列表视图(ListView)组件,用来实验后面的代码是否实现保持页面的状态(State)。

    自动保持活动客户端混合(AutomaticKeepAliveClientMixin)抽象类,为自动保持活动(AutomaticKeepAlive)的客户端提供直接可以使用的方法,与状态(State)子类一起使用,可以避免作为父组件的页面视图(PageView)组件切换时被重新绘制。

    自动保持活动客户端混合(AutomaticKeepAliveClientMixin)抽象类的想要保持活动(wantKeepAlive)属性,用于设置当前实例是否应保持活动状态,不因父组件的切换而重新绘制。这一步很关键。

    /// 自定义的导航页面组件。
    class NavigationPage extends StatefulWidget {
      @override
      _NavigationPageState createState() => _NavigationPageState();
    }
    
    /// 与自定义的导航页面组件关联的状态子类。
    class _NavigationPageState extends State<NavigationPage>
        with AutomaticKeepAliveClientMixin {
    
      @override
      bool get wantKeepAlive => true;
    
      @override
      Widget build(BuildContext context) {
        super.build(context);
        return Scaffold(
          body: ListView.builder(
            padding: EdgeInsets.all(8.0),
            itemExtent: 20.0,
            itemBuilder: (BuildContext context, int index) {
              return Text('项目 $index');
            },
          ),
        );
      }
    }
    

    接下来要配置底部导航栏(BottomNavigationBar)组件的项目(items)属性需要的项目列表,以及对应的页面组件。

      // TODO:集中管理导航项目列表和页面组件列表。
      /// 统一管理导航项目的列表。
      List<BottomNavigationBarItem> navigationItem = [
        BottomNavigationBarItem(
          icon: Icon(Icons.home),
          title: Text('首页'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.comment),
          title: Text('消息'),
        ),
        BottomNavigationBarItem(
          icon: Icon(Icons.settings),
          title: Text('设置'),
        ),
      ];
    
      /// 统一管理导航项目对应的组件列表。
      final _widgetOptions = [
        NavigationPage(),
        NavigationPage(),
        NavigationPage(),
      ];
    

    然后定义一下页面视图(PageView)组件的选择索引、控制器及资源释放函数。

      // TODO:当前页面视图的相关变量。
      /// 当前页面选择的索引。
      int _selectedIndex = 0;
    
      /// 页面控制器(`PageController`)组件,页面视图(`PageView`)的控制器。
      PageController _controller = PageController();
    
      /// 释放相关资源。
      @override
      void dispose() {
        _controller.dispose();
        super.dispose();
      }
    

    因为我们使用来页面视图(PageView)组件,而页面视图组件不会自动切换页面,因此我们要自己写一下切换页面的方法。

      // TODO:切换页面视图的方法。
      void _onItemTapped(int index) {
        // 创建底部导航栏的组件需要跟踪当前索引并调用`setState`以使用新提供的索引重建它。
        setState(() {
          _selectedIndex = index;
          // 跳到页面(`jumpToPage`)方法,更改显示在的页面视图(`PageView`)组件中页面。
          _controller.jumpToPage(index);
        });
      }
    

    最后完成脚手架(Scaffold)组件上的主体内容,把上面的内容都派上用场。

          // TODO:页面视图的主要内容。
          // 页面视图(`PageView`)组件,可逐页工作的可滚动列表,每个子项都被强制与视窗大小相同。
          body: PageView.builder(
            // 物理(`physics`)属性,页面视图应如何响应用户输入。
            // 从不可滚动滚动物理(`NeverScrollableScrollPhysics`)类,不允许用户滚动。
            physics: NeverScrollableScrollPhysics(),
            itemBuilder: (BuildContext context, int index) {
              return _widgetOptions.elementAt(index);
            },
            itemCount: _widgetOptions.length,
            // 控制器(`controller`)属性,用于控制滚动此页面视图位置的对象。
            controller: _controller,
          ),
          // 底部导航栏(`bottomNavigationBar`)属性,显示在脚手架(`Scaffold`)组件的底部。
          // 底部导航栏(`BottomNavigationBar`)组件,显示在应用程序底部的组件,
          // 用于在几个屏幕之间中进行选择,通常在三到五之间,再多就不好看了。
          bottomNavigationBar: BottomNavigationBar(
            // 项目(`items`)属性,位于底部导航栏中的交互组件,其中每一项都有一个图标和标题。
            items: navigationItem,
            // 目前的索引(`currentIndex`)属性,当前活动项的项目索引。
            currentIndex: _selectedIndex,
            // 固定颜色(`fixedColor`)属性,当BottomNavigationBarType.fixed时所选项目的颜色。
            fixedColor: Color(0xffFE7C30),
            // 在点击(`onTap`)属性,点击项目时调用的回调。
            onTap: _onItemTapped,
            // 定义底部导航栏(`BottomNavigationBar`)组件的布局和行为。
            type: BottomNavigationBarType.fixed,
          ),
    

    运行代码,实现效果如下面的图片。

    页面的状态被保持了下来

    相关文章

      网友评论

        本文标题:Flutter代码锦囊---切换时页面保持状态

        本文链接:https://www.haomeiwen.com/subject/etiopqtx.html