iOS开发Flutter探索

iOS开发Flutter探索-State状态保存(10)

2020-06-20  本文已影响0人  泽泽伐木类

前言

有时候我们不希望某个页面每次打开时都重新加载,比如就我们之前的Tabbar结构的页面,每当我们在切换Tab的时候都会执行void initState(),这就意味着页面每次都会重新渲染,之所以这样就是因为我们的State状态没有保存,如下图所示:
[没有状态保存效果图]

保存State状态

给当前State类添加一个扩展(这里就用扩展这个词吧,其实类似于iOS下的Category),一个系统的扩展类AutomaticKeepAliveClientMixin,并重写wantKeepAlive方法,让一个普通的State类,具有保存状态的能力。
在Dart语法中通过使用with关键字来添加扩展:

// 通过with 添加扩展
class _PendCourseState extends State<PendCourse> with AutomaticKeepAliveClientMixin<PendCourse>{
  List<ChatUser> _datas = [];
  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;   //返回true,具备保存能力

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
      print('待上课程:initState() 执行了');
  }
}

bool get wantKeepAlive => true;之后,当前State就具备保存能力了,也就意味着重复切换Tab后,void initState()就不会重复执行了(由原来的viewWillAppear()变成了viewDidLoad())。

问题

按照上面方式修改后,发现切换Tab后void initState()依然重复执行了,这是为什么吶?这里我们看下我们之前root_page.dart里面是如何配置我们的tabbar结构的:

class _RootPageState extends State<RootPage> {

  int _currentIndex = 0;

  // ViewControllers
  List <Widget> _viewControllers = [
    PendCourse(),       //待上课程
    TakenCourse(),      //已上课程
    ExamPage(),         //水平测试
    PersonalPage(),     //我
  ];
  // tabBarItems
  List <BottomNavigationBarItem> _tabBarItems = [
    BottomNavigationBarItem(
      icon: Icon(Icons.access_alarm),
      title: Text('待上课程')
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.history),
      title: Text('已上课程')
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.perm_camera_mic),
      title: Text('水平测试')
    ),
    BottomNavigationBarItem(
      icon: Icon(Icons.person),
      title: Text('我')
    ),
  ];

  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          onTap: (int index){
            _currentIndex = index;
            setState(() {});
          },
          selectedFontSize: 12.0,
          type:BottomNavigationBarType.fixed,
          fixedColor: Colors.green,
          currentIndex: _currentIndex,
          items: _tabBarItems,
        ),
        body: _viewControllers[_currentIndex],
      ),
    );
  }
}

这里我们是通过一个_viewControllers的List,把4个子页面放在了里面,全局有一个_currentIndex,当onTap回调后后,更新_currentIndex的值,执行setState ()后,body对应的widget页面发生改变。而问题也就出在这里,当body部分发生改变时,根据Flutter的底层渲染逻辑,这里会移除掉之前的Widget,并重新创建新的Widget,我们之前在_viewControllers放的子页面,并不像iOS下是一个实例对象,存在就直接拿来使用。在Flutter 中 setState ()后界面会被重新绘制,而body部分只知道我要渲染一个什么样的widget,而该类型的widget每次都是会重新创建,这也就意味着我们在Tab切换时,每次都是重新创建,所以每次都执行了initState()
显然我们现在的方式是不合理的,那在Flutter中如何管理这样的子页面,而避免重复渲染呐?
这就要用到一个新的部件了:PageView(),内部的2个关键属性:

final PageController _controller = PageController(
    initialPage: 0,  //默认显示第0个页面
  );
  
  @override
  Widget build(BuildContext context) {
    return Container(
      child: Scaffold(
        bottomNavigationBar: BottomNavigationBar(
          onTap: (int index){
            _currentIndex = index;
            setState(() {});
            _controller.jumpToPage(index);
          },
          selectedFontSize: 12.0,
          type:BottomNavigationBarType.fixed,
          fixedColor: Colors.green,
          currentIndex: _currentIndex,
          items: _tabBarItems,
        ),
        body: PageView(
          controller: _controller,
          children: _viewControllers,
        ),
      ),
    );
  }

子页面切换通过_controller.jumpToPage(index);来实现。
这样子页面也就不会重新创建渲染了,我们的状态保存也就能正常实现了。

PageView(
    physics: NeverScrollableScrollPhysics(),
    //AlwaysScrollableScrollPhysics(),  默认方式
    //NeverScrollableScrollPhysics(),  
    controller: _controller,
    children: _viewControllers,
),

总结

学习是一个循序渐进的过程,我们总是在踩坑中不断的前行,把坑填平了也就意味着我们在这个新的东西面前立了足,就可能进行更多为什么的探索了。

上一篇 下一篇

猜你喜欢

热点阅读