天天看點

【Flutter】複用Widget提高build性能使用const使用HoC使用效果

StatefulWidget根據state的變化會進行rebuild,所有子widget也會随之rebuild。通過diff可以避免

element

的更新,因為element的更新成本很高。相比較來說widget的建立和rebuild則要輕量的多,理論上反複進行也無傷大雅。但是如果有些子widget無需建立和rebuild時,是否可以優化掉這些多餘執行呢?

使用const

在widget前面加

const

是個一勞永逸的辦法,但是你一旦加了const,你這個widget就永遠不會更新了,除非你是在寫靜态頁面,否則你最好不要用它。

使用HoC

用過react的人都知道,react的類元件有個很重要的生命周期叫

shouldComponentUpdate

,可以在元件内部重寫這個聲明周期來進行性能優化。雖然Flutter不提供這種機制,但我們可以實行一個類似效果:

class ShouldRebuildWidget<T extends Widget> extends StatefulWidget {
  final Function shouldRebuild;
  final Function widgetProvider;
  ShouldRebuild({
    @required this.shouldRebuild,
    @required this.widgetProvider});
    
  @override
  _ShouldRebuildState createState() => _ShouldRebuildState<T>(provider);
}

class _ShouldRebuildState<T extends Widget> extends State<ShouldRebuild> {

  _ShouldRebuildState(this._provider) : super() {}

  ChildProvider _provider;

  @override
  ShouldRebuild<T> get widget => super.widget;

  T _oldWidget;
  @override
  Widget build(BuildContext context) {
    if (this._oldWidget == null || (widget.shouldRebuild == null ? true : widget.shouldRebuild(_oldWidget))) {
      this._oldWidget = _provider();
    }
    return _oldWidget;
  }
}
           

ShouldRebuildWidget

類似react的HOC,包裹真正的widget;

shouldRebuild

方法類似react的

shouldComponentUpdate

,由于flutter的state不是immutable的,無法傳回新舊兩個state進行比較,隻能傳回目前子widget進行比較,比較後發現無需建立新的子widget則傳回false。

使用效果

首先實作一個WrappedWidget,

class Counter extends StatelessWidget {
  final VoidCallback onClick;
  final int count;
  final String title;
  Counter({this.count,this.onClick,this.title});
  @override
  Widget build(BuildContext context) {
    Color color = Color.fromRGBO(Random().nextInt(256), Random().nextInt(256), Random().nextInt(256), 1);
    return AnimatedContainer(
      duration: Duration(milliseconds: 500),
      color:color,
      height: 150,
      child:Column(
        children: <Widget>[
          Text(title,style: TextStyle(fontSize: 30),),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text('counter = ${this.count}',style: TextStyle(fontSize: 43,color: Colors.white),),
            ],
          ),
          RaisedButton(
            color: color,
            textColor: Colors.white,
            elevation: 20,
            onPressed: onClick,
            child: Text('increment Counter'),
          ),
        ],
      ),
    );
  }
}
           

使用Counter時,使用ShouldBuildWidget進行包裹

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: Test(),
    );
  }
}

class Test extends StatefulWidget {
  @override
  _TestState createState() => _TestState();
}

class _TestState extends State<Test> {
  int productNum = 0;
  int counter = 0;

  _incrementCounter(){
    setState(() {
      ++counter;
    });
  }
  _incrementProduct(){
    setState(() {
      ++productNum;
    });
  }
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Container(
          constraints: BoxConstraints.expand(),
          child: Column(
            children: <Widget>[
               ShouldRebuildWidget<Counter>(
                shouldRebuild: (Counter old) {
                  return old.count != counter;
                },
                provider: () => Counter(count: counter,onClick: _incrementCounter,title: 'I am good Counter',),

              ),

              Text('productNum = $productNum',style: TextStyle(fontSize: 22,color: Colors.deepOrange),),
              RaisedButton(
                onPressed: _incrementProduct,
                child: Text('increment Product'),
              )
            ],
          ),
        ),
      ),
    );
  }
}
           
【Flutter】複用Widget提高build性能使用const使用HoC使用效果

可以看到,點選

increment Product

時,Counter不會建立和rebuild,隻有點選

increment Counter

才會重建并且rebuild

參考

https://segmentfault.com/a/1190000020264112

繼續閱讀