Flutter项目中使用TabBarView所遇到问题,避免不必要的性能开销

1,765 阅读2分钟

记录一次在Flutter项目中用到TabBarView widget时,遇到的问题,并实现TabBarView切换页面时每个页面只initState一次,减少性能开销

**现象描述:**最近在优化flutter项目时,发现如果将每个page单独抽离成一个StatefulWidget时,当点击标签切换页面后,每个widget的initState()初始化方法都会走一遍,很不友好,因为我在initState()里面做了一次网络请求,所有导致,每次切换标签都会去进行网络请求,从而导致页面会重新刷新build一次,而这并不是用户想看到的,所有这里把优化过程记录一次。

这里没有用项目代码,用demo代码演示一次优化前的效果和优化后的效果:

  • 优化前的home代码:
import 'package:flutter/material.dart';
import 'package:tabbarview_demo/page_one_view.dart';
import 'package:tabbarview_demo/page_three_view.dart';
import 'package:tabbarview_demo/page_two_view.dart';
class HomePage extends StatefulWidget {

  @override
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> with SingleTickerProviderStateMixin {
  TabController _tabController;

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    _tabController = new TabController(length: 3, vsync: this);

  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        elevation: 0,
        automaticallyImplyLeading: false,
        title: Text("首页",style: TextStyle(fontWeight: FontWeight.bold,fontSize: 16,color: Colors.black),),
        bottom: TabBar(
          indicatorSize: TabBarIndicatorSize.label,
          controller: _tabController,
          unselectedLabelColor: Colors.orange,
          tabs: <Widget>[
            Tab(text: '标签一'),
            Tab(text: '标签二'),
            Tab(text: '标签三'),
          ],
        ),
      ),
      body: TabBarView(
        controller: _tabController,
        children: <Widget>[
          PageOneView(),
          PageTwoView(),
          PageThreeView(),
        ],
      ),
    );
  }
}

其中一个page:

import 'package:flutter/cupertino.dart';

class PageOneView extends StatefulWidget {
  @override
  _PageOneViewState createState() => _PageOneViewState();
}

class _PageOneViewState extends State<PageOneView> {

  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    print("page1标签页---initState");
  }

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text("page1标签页"),
    );
  }
}

界面大概这个样子:

这时候当你一次点击标签页1,2,3后,page对应的initState()会依次走一次,在我的想法里认为,既然已经初始化了,而已界面也没有销毁,按道理在生命周期中界面再次显示时,不应该再走initState()的方法,但是,当我继续点击标签时,出乎我的意料: 你会发现initState()一直在走,如果在initState里面做一些网络请求,或复杂计算的话,就会造成不必要的性能开销

这里主要用 AutomaticKeepAliveClientMixin 的方式来解决

优化后的每个page的代码,以pageOne的代码为例:

import 'package:flutter/cupertino.dart';

class PageOneView extends StatefulWidget {
  @override
  _PageOneViewState createState() => _PageOneViewState();
}

class _PageOneViewState extends State<PageOneView> with AutomaticKeepAliveClientMixin {

  // 1.重写 wantKeepAlive 的get
  @override
  // TODO: implement wantKeepAlive
  bool get wantKeepAlive => true;
  
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    print("page1标签页---initState");
  }

  @override
  Widget build(BuildContext context) {
    //2.在build中添加 super.build(context);
    super.build(context);
    
    return Center(
      child: Text("page1标签页"),
    );
  }
}

优化后就会发现无论怎么切换标签,每个页面的initState()方法都会走一次,得到了我们想要的效果