复用Widget提高build性能
来源:blog.csdn.net 发布时间:2021-03-14 14:41
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,
可以看到,点击increment Product时,Counter不会创建和rebuild,只有点击increment Counter才会重建并且rebuild
更新时间:2024-12-18 20:26
使用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,
data-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进行包裹
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,
data-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'),
),
],
),
);
}
}
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'),
)
],
),
),
),
);
}
}
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
更新时间:2024-12-18 20:26
上一篇:Flutter性能优化实践 —— UI篇 下一篇:flutter曲线纸飞机