class ConstDemo extends StatefulWidget {
@override
_ConstDemoState createState() => _ConstDemoState();
}
class _ConstDemoState extends State {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
Text('老孟'),
RaisedButton(onPressed: (){
setState(() {
});
})
],
),
);
}
}

给 **Text('老孟')** 组件加上 **const**:
const Text(‘xx’),

对比两次 **Text** 组件的重建情况,加上 **const** 后,未重建。
避免更改组件树的结构和组件的类型
----------------
有如下场景,有一个 **Text** 组件有可见和不可见两种状态,代码如下:
bool _visible = true;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
if(_visible)
Text('可见'),
Container(),
],
),
);
}
复制代码
可见时的组件树:

不可见时的组件树:

两种状态组件树结构发生变化,应该避免发生此种情况,优化如下:
Center(
child: Column(
children: [
Visibility(
visible: _visible,
child: Text('可见'),
),
Container(),
],
),
)
此时不管是可见还是不可见状态,组件树都不会发生变化,如下:

还有一种情况是根据不同的条件构建不同的组件,如下:
bool _showButton = true;
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
_showButton ? RaisedButton(onPressed: null) : Text('不显示'),
Container(),
],
),
);
}
设置为 **true** 时的组件树结构:

设置为 **false** 时的组件树结构:

看到左侧子节点由 **RaisedButton** 变为了 **Text**。
上面的情况组件树发生了更改,不管是类型发生更改,还是深度发生更改,如果无法避免,那么就将变化的组件树封装为一个 **StatefulWidget** 组件,且设置 **GlobalKey**,如下:
封装变化的部分:
class ChildWidget extends StatefulWidget {
const ChildWidget({Key key}) : super(key: key);
@override
_ChildWidgetState createState() => _ChildWidgetState();
}
class _ChildWidgetState extends State {
bool _showButton = true;
@override
Widget build(BuildContext context) {
return _showButton ? RaisedButton(onPressed: null) : Text('不显示');
}
}
构建:
class ConstDemo extends StatefulWidget {
@override
_ConstDemoState createState() => _ConstDemoState();
}
class _ConstDemoState extends State {
@override
Widget build(BuildContext context) {
return Center(
child: Column(
children: [
ChildWidget(key: GlobalKey(),),
Container(),
],
),
);
}
}
虽然通过 **GlobalKey** 提高了上面案例的性能,但我们千万不要乱用 **GlobalKey**,因为管理 **GlobalKey** 的成本很高,所以其他需要使用 **Key** 的地方建议考虑使用 **Key, ValueKey, ObjectKey, 和 UniqueKey**。
关于 **GlobalKey** 的相关说明参考:api.flutter.dev/flutter/wid…
关于ListView 的优化
--------------
ListView是我们最常用的组件之一,用于展示大量数据的列表。如果展示大量数据请使用 **ListView.builder** 或者 **ListView.separated**,千万不要直接使用如下方式:
ListView(
children: [
item,item1,item2,...
],
)
这种方式一次加载所有的组件,没有“懒加载”,消耗极大的性能。
ListView 中 **itemExtent** 属性对动态滚动到性能提升非常大,比如,有2000条数据展示,点击按钮滚动到最后,代码如下:
class ListViewDemo extends StatefulWidget {
@override
_ListViewDemoState createState() => _ListViewDemoState();
}
class _ListViewDemoState extends State {
ScrollController _controller;
@override
void initState() {
super.initState();
_controller = ScrollController();
}
@override
Widget build(BuildContext context) {
return Stack(
children: [
ListView.builder(
controller: _controller,
itemBuilder: (context, index) {
return Container(
height: 80,
alignment: Alignment.center,
color: Colors.primaries[index % Colors.primaries.length],
child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 20),),
);
},
itemCount: 2000,
),
Positioned(
child: RaisedButton(
child: Text('滚动到最后'),
onPressed: () {
_controller.jumpTo(_controller.position.maxScrollExtent);
},
))
],
);
}
}

耗时在**2秒**左右,加上 **itemExtent** 属性,修改如下:
ListView.builder(
controller: _controller,
itemBuilder: (context, index) {
return Container(
height: 80,
alignment: Alignment.center,
color: Colors.primaries[index % Colors.primaries.length],
child: Text('$index',style: TextStyle(color: Colors.white,fontSize: 20),),
);
},
itemExtent: 80,
itemCount: 2000,
)

优化后瞬间跳转到底部。
这是因为不设置 **itemExtent** 属性,将会由子组件自己决定大小,大量的计算导致UI堵塞。
关于 AnimatedBuilder TweenAnimationBuilder 的优化
--------------------------------------------
这里说的是向AnimatedBuilder 、TweenAnimationBuilder 等一类的组件的问题,这些组件都有一个共同点,带有 **builder** 且其参数中有 **child**。
以 AnimatedBuilder 为例,如果 **builder** 中构建的树中包含与动画无关的组件,将这些无关的组件当作 **child** 传递到 **builder** 中比直接在 **builder** 中构建更加有效。
比如下面的代码,直接在 **builder** 中构建子组件:
AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return Transform.rotate(
angle: animation.value,
child: FlutterLogo(size: 60,),
);
},
)
优化后的代码:
AnimatedBuilder(
animation: animation,
builder: (BuildContext context, Widget child) {
return Transform.rotate(
angle: animation.value,
child: child,
);
},
child: FlutterLogo(size: 60,),
)
谨慎地使用一些组件
---------
部分组件一定要谨慎使用,因为这些组件包含一些昂贵的操作,比如 **saveLayer()** 方法。
> 调用saveLayer()会分配一个屏幕外缓冲区。 将内容绘制到屏幕外缓冲区中可能会触发渲染目标切换,这在较早的GPU中特别慢。
另外虽然下面这些组件比较消耗性能,但并不是禁止大家使用,而是谨慎使用,如果有替代方案,考虑使用替代方法。
尤其注意,如果这些组件频繁重建(比如动画的过程),要重点优化。
Clip 类组件
--------
Clip 类组件是常用的裁剪类组件,比如:ClipOval、ClipPath、ClipRRect、ClipRect、CustomClipper。这些组件中都有 **clipBehavior** 属性,不同的值性能是不同的,
/// * [hardEdge], which is the fastest clipping, but with lower fidelity.
/// * [antiAlias], which is a little slower than [hardEdge], but with smoothed edges.
/// * [antiAliasWithSaveLayer], which is much slower than [antiAlias], and should
/// rarely be used.
越往下,速度越慢。
一些简单的圆角组件的设置可以使用 Container 实现:
Container(
height: 200,
width: 200,
decoration: BoxDecoration(
image: DecorationImage(
image: NetworkImage(
'https://flutter.github.io/assets-for-api-docs/assets/widgets/owl-2.jpg'),
fit: BoxFit.cover,
),
border: Border.all(
color: Colors.blue,
width: 2,
),
borderRadius: BorderRadius.circular(12),
),
文末
那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试
CodeChina开源项目地址:https://codechina.youkuaiyun.com/m0_60958482/android_p7
-
技能
-
面试技巧+面试题
-api-docs/assets/widgets/owl-2.jpg’),fit: BoxFit.cover, ), border: Border.all( color: Colors.blue, width: 2, ), borderRadius: BorderRadius.circular(12), ),
文末
那么对于想坚持程序员这行的真的就一点希望都没有吗?
其实不然,在互联网的大浪淘沙之下,留下的永远是最优秀的,我们考虑的不是哪个行业差哪个行业难,就逃避掉这些,无论哪个行业,都会有他的问题,但是无论哪个行业都会有站在最顶端的那群人。我们要做的就是努力提升自己,让自己站在最顶端,学历不够那就去读,知识不够那就去学。人之所以为人,不就是有解决问题的能力吗?挡住自己的由于只有自己。
Android希望=技能+面试
CodeChina开源项目地址:https://codechina.youkuaiyun.com/m0_60958482/android_p7
- 技能
[外链图片转存中…(img-OgtC0NjS-1631107610537)] - 面试技巧+面试题