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'),
)
],
),
),
),
);
}
}
可以看到,點選
increment Product
時,Counter不會建立和rebuild,隻有點選
increment Counter
才會重建并且rebuild
參考
https://segmentfault.com/a/1190000020264112