- 将频繁更新的组件尽可能放在叶子节点
- 尽量减少由build方法和它创建的小部件间接创建的节点数量
- 缓存不变的组件
- 尽可能用const(这和3等价)
- 必须改变深度时,考虑用globalKey
将频繁更新的组件尽可能放在叶子节点
arduino
/// * Push the state to the leaves. For example, if your page has a ticking
/// clock, rather than putting the state at the top of the page and
/// rebuilding the entire page each time the clock ticks, create a dedicated
/// clock widget that only updates itself.
ClockWidget
放置在页面的中心,这样它将在页面中心显示当前时间。通过这种方式,当时钟滴答时,只有ClockWidget
会重新构建,而不会重新构建整个页面,从而提高了性能和效率。
import
class ClockWidget extends StatefulWidget {
@override
_ClockWidgetState createState() => _ClockWidgetState();
}
class _ClockWidgetState extends State<ClockWidget> {
String currentTime = '';
@override
void initState() {
super.initState();
// 开始时钟定时器
startClockTimer();
}
@override
void dispose() {
// 取消时钟定时器
stopClockTimer();
super.dispose();
}
void startClockTimer() {
// 每秒更新一次时钟时间
Timer.periodic(Duration(seconds: 1), (timer) {
setState(() {
// 更新当前时间
currentTime = getCurrentTime();
});
});
}
void stopClockTimer() {
// 取消时钟定时器
// 如果有多个定时器,确保取消所有定时器
// timer.cancel();
}
String getCurrentTime() {
// 获取当前时间的逻辑
// 返回当前时间的字符串表示形式
}
@override
Widget build(BuildContext context) {
return Text(
currentTime,
style: TextStyle(fontSize: 20),
);
}
}
class MyPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('My Page'),
),
body: Center(
child: ClockWidget(), // 使用时钟小部件
),
);
}
}
void main() {
runApp(
MaterialApp(
home: MyPage(),
),
);
}
尽量减少由build方法和它创建的小部件间接创建的节点数量
arduino
/// * Minimize the number of nodes transitively created by the build method and
/// any widgets it creates. Ideally, a stateful widget would only create a
/// single widget, and that widget would be a [RenderObjectWidget].
/// (Obviously this isn't always practical, but the closer a widget gets to
/// this ideal, the more efficient it will be.)
缓存不变的组件
arduino
/// * If a subtree does not change, cache the widget that represents that
/// subtree and re-use it each time it can be used. To do this, assign
/// a widget to a `final` state variable and re-use it in the build method. It
/// is massively more efficient for a widget to be re-used than for a new (but
/// identically-configured) widget to be created. Another caching strategy
/// consists in extracting the mutable part of the widget into a [StatefulWidget]
/// which accepts a child parameter.
在示例中,我们创建了一个名为MyCachedWidget
的[StatefulWidget],该小部件接受一个子部件作为参数。在_MyCachedWidgetState
中,我们在initState
方法中将子部件缓存到_cachedWidget
变量中,并在build
方法中使用该缓存的小部件。这样在每次调用build
方法时,如果子树不发生变化,就会重复使用缓存的小部件,而不会重新创建一个新的小部件 。 在MyStatefulWidget
中,我们使用MyCachedWidget
来展示一个子树,并通过点击按钮来切换子树是否发生变化。当子树不变时,会重复使用缓存的小部件,否则会创建一个新的小部件。 通过这种缓存策略和提取可变部分的方法,可以有效地减少不必要的小部件创建,提高应用的性能。
import
class MyCachedWidget extends StatefulWidget {
final Widget child;
const MyCachedWidget({required this.child});
@override
_MyCachedWidgetState createState() => _MyCachedWidgetState();
}
class _MyCachedWidgetState extends State<MyCachedWidget> {
Widget? _cachedWidget; // 缓存的小部件
@override
void initState() {
super.initState();
// 初始化时,将子部件缓存起来
_cachedWidget = widget.child;
}
@override
Widget build(BuildContext context) {
return _cachedWidget ?? Container(); // 使用缓存的小部件,如果为空则返回一个空容器
}
}
class MyStatefulWidget extends StatefulWidget {
@override
_MyStatefulWidgetState createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool _isSubtreeUnchanged = true;
@override
Widget build(BuildContext context) {
return Column(
children: [
ElevatedButton(
onPressed: () {
setState(() {
_isSubtreeUnchanged = !_isSubtreeUnchanged; // 切换子树是否发生变化的标志
});
},
child: Text('Toggle Subtree'),
),
MyCachedWidget(
child: _isSubtreeUnchanged
? Text('Unchanged Subtree') // 子树不变时使用缓存的小部件
: Text('Changed Subtree'), // 子树变化时创建新的小部件
),
],
);
}
}
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: MyStatefulWidget(),
),
),
),
);
}
尽可能用const(这和3等价)
arduino
/// * Use `const` widgets where possible. (This is equivalent to caching a
/// widget and re-using it.)
const
小部件在编译时就会被创建,并在每次构建时都会重复使用相同的实例,而不是每次都创建一个新的小部件 。以下是一个示例代码,演示了如何使用const
小部件:
js
import 'package:flutter/material.dart';
class MyWidget extends StatelessWidget {
@override
Widget build(BuildContext context) {
return const Text('Hello, World!'); // 使用const小部件
}
}
void main() {
runApp(
MaterialApp(
home: Scaffold(
body: Center(
child: MyWidget(),
),
),
),
);
}
避免改变创建的子树的深度或改变子树中任何小部件的类型
arduino
/// * Avoid changing the depth of any created subtrees or changing the type of
/// any widgets in the subtree. For example, rather than returning either the
/// child or the child wrapped in an [IgnorePointer], always wrap the child
/// widget in an [IgnorePointer] and control the [IgnorePointer.ignoring]
/// property. This is because changing the depth of the subtree requires
/// rebuilding, laying out, and painting the entire subtree, whereas just
/// changing the property will require the least possible change to the
/// render tree (in the case of [IgnorePointer], for example, no layout or
/// repaint is necessary at all).
避免改变创建的子树的深度或改变子树中任何小部件的类型。例如,不要返回子节点或将子节点包装在[IgnorePointer]中,而是始终将子小部件包装在[IgnorePointer]中,并控制[IgnorePointer.ignoring]属性的值。这是因为改变子树的深度需要重建、布局和绘制整个子树,而只改变属性将需要对渲染树进行最小的更改(例如,在[IgnorePointer]的情况下,根本不需要布局或重绘)。 在Flutter中,当我们需要在小部件树中进行更改时,我们应该尽量避免改变子树的深度或改变小部件的类型。这是因为改变子树的深度会导致整个子树的重建、布局和绘制,这可能会带来性能上的开销。相反,我们应该尽量保持小部件树的稳定性,只改变小部件的属性或状态。 例如,当我们想要在某些条件下使子小部件不可点击时,我们应该始终将子小部件包装在一个[IgnorePointer]小部件中,并通过控制[IgnorePointer.ignoring]属性的值来控制是否忽略点击事件。这样做只需要对渲染树进行最小的更改,不需要重新布局或重绘整个子树,从而提高性能。 以下是一个示例代码,演示了如何使用[IgnorePointer]来控制子小部件的可点击性:
js
import 'package:flutter/material.dart';
class MyWidget extends StatefulWidget {
@override
_MyWidgetState createState() => _MyWidgetState();
}
class _MyWidgetState extends State<MyWidget> {
bool _isIgnoringPointer = false;
void toggleIgnoringPointer() {
setState(() {
_isIgnoringPointer = !_isIgnoringPointer;
});
}
@override
Widget build(BuildContext context) {
return Column(
children: [
RaisedButton(
child: Text('Toggle Ignoring Pointer'),
onPressed: toggleIgnoringPointer,
),
SizedBox(height: 20),
IgnorePointer(
ignoring: _isIgnoringPointer, // 控制 IgnorePointer 的 ignoring 属性
child: RaisedButton(
child: Text('Clickable Button'),
onPressed: () {
print('Button Clicked');
},
),
),
],
);
}
}
void main() {
runApp(
MaterialApp(
home: Scaffold(
appBar: AppBar(
title: Text('Ignore Pointer Example'),
),
body: Center(
child: MyWidget(),
),
),
),
);
}
必须改变深度时,考虑用globalKey
arduino
/// * If the depth must be changed for some reason, consider wrapping the
/// common parts of the subtrees in widgets that have a [GlobalKey] that
/// remains consistent for the life of the stateful widget. (The
/// [KeyedSubtree] widget may be useful for this purpose if no other widget
/// can conveniently be assigned the key.)
在Flutter中,如果我们必须改变子树的深度,可以考虑将子树的公共部分包装在具有一致的[GlobalKey]的小部件中。这样做的好处是,即使改变了子树的深度,具有相同[GlobalKey]的小部件仍然可以保持它们的身份不变,从而避免了整个子树的重建和布局。