flutter-performance
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseFlutter Performance Optimization
Flutter性能优化
Goal
目标
Analyzes and optimizes Flutter application performance by identifying jank, excessive rebuilds, and expensive rendering operations. Implements best practices for UI rendering, state management, and layout constraints. Utilizes Flutter DevTools, Chrome DevTools (for web), and integration tests to generate actionable performance metrics, ensuring frames render within the strict 16ms budget.
通过识别卡顿、过度重建和高开销渲染操作,分析并优化Flutter应用性能。落实UI渲染、状态管理和布局约束的最佳实践。利用Flutter DevTools、Chrome DevTools(针对Web)和集成测试生成可执行的性能指标,确保帧渲染严格控制在16ms的预算内。
Decision Logic
决策逻辑
Evaluate the target application using the following decision tree to determine the optimization path:
- Is the goal to establish a performance baseline?
- Yes: Implement an integration test using and
traceAction.TimelineSummary - No: Proceed to step 2.
- Yes: Implement an integration test using
- Is the application running on Web?
- Yes: Enable and use Chrome DevTools Performance panel.
debugProfileBuildsEnabled - No: Run the app on a physical device in mode and launch Flutter DevTools.
--profile
- Yes: Enable
- Which thread is showing jank (red bars > 16ms) in the DevTools Performance View?
- UI Thread: Optimize methods, localize
build(), usesetState()constructors, and replace string concatenation withconst.StringBuffer - Raster (GPU) Thread: Minimize ,
saveLayer(),Opacity, andClipusage. Pre-cache complex images usingImageFilter.RepaintBoundary - Both: Start by optimizing the UI thread (Dart VM), as expensive Dart code often cascades into expensive rendering.
- UI Thread: Optimize
使用以下决策树评估目标应用,确定优化路径:
- 是否要建立性能基准线?
- 是: 使用和
traceAction实现集成测试。TimelineSummary - 否: 进入步骤2。
- 是: 使用
- 应用是否运行在Web端?
- 是: 启用并使用Chrome DevTools的性能面板。
debugProfileBuildsEnabled - 否: 在物理设备上以模式运行应用,并启动Flutter DevTools。
--profile
- 是: 启用
- DevTools性能视图中哪个线程出现卡顿(红色条>16ms)?
- UI线程: 优化方法,本地化
build()调用,使用setState()构造函数,用const替代字符串拼接。StringBuffer - Raster(GPU)线程: 尽量减少、
saveLayer()、Opacity和Clip的使用。使用ImageFilter预缓存复杂图像。RepaintBoundary - 两者都有: 先从优化UI线程(Dart VM)开始,因为高开销的Dart代码通常会引发连锁反应导致渲染开销增加。
- UI线程: 优化
Instructions
操作步骤
-
Establish a Performance Baseline To measure performance programmatically, create an integration test that records a performance timeline. STOP AND ASK THE USER: "Do you want to run a baseline integration test to capture timeline metrics before optimizing?" If yes, implement the following exact driver and test implementation:test_driver/perf_driver.dart (Immutable operation):dart
import 'package:flutter_driver/flutter_driver.dart' as driver; import 'package:integration_test/integration_test_driver.dart'; Future<void> main() { return integrationDriver( responseDataCallback: (data) async { if (data != null) { final timeline = driver.Timeline.fromJson( data['scrolling_timeline'] as Map<String, dynamic>, ); final summary = driver.TimelineSummary.summarize(timeline); await summary.writeTimelineToFile( 'scrolling_timeline', pretty: true, includeSummary: true, ); } }, ); }integration_test/scrolling_test.dart:dartimport 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:your_package/main.dart'; void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Performance profiling test', (tester) async { await tester.pumpWidget(const MyApp()); final listFinder = find.byType(Scrollable); final itemFinder = find.byKey(const ValueKey('target_item')); await binding.traceAction(() async { await tester.scrollUntilVisible( itemFinder, 500.0, scrollable: listFinder, ); }, reportKey: 'scrolling_timeline'); }); }Run the test using:flutter drive --driver=test_driver/perf_driver.dart --target=integration_test/scrolling_test.dart --profile --no-dds -
Optimize UI Thread (Build Costs) If the UI thread exceeds 8ms per frame, refactor the widget tree:
- Localize State: Move calls as low in the widget tree as possible.
setState - Use : Apply
constconstructors to short-circuit rebuild traversals.const - String Building: Replace operators in loops with
+.StringBuffer
dart// BAD: Rebuilds entire widget tree setState(() { _counter++; }); // GOOD: Encapsulated state class CounterWidget extends StatefulWidget { ... } // Inside CounterWidget: setState(() { _counter++; });dart// GOOD: Efficient string building final buffer = StringBuffer(); for (int i = 0; i < 1000; i++) { buffer.write('Item $i'); } final result = buffer.toString(); - Localize State: Move
-
Optimize Raster Thread (Rendering Costs) If the Raster thread exceeds 8ms per frame, eliminate expensive painting operations:
- Replace widgets with semitransparent colors where possible.
Opacity - Replace in animations with
OpacityorAnimatedOpacity.FadeInImage - Avoid . Use
Clip.antiAliasWithSaveLayerproperties on containers instead of explicit clipping widgets.borderRadius
dart// BAD: Expensive Opacity widget Opacity( opacity: 0.5, child: Container(color: Colors.red), ) // GOOD: Semitransparent color Container(color: Colors.red.withOpacity(0.5)) - Replace
-
Fix Layout and Intrinsic Passes Identify and remove excessive layout passes caused by intrinsic operations (e.g., asking all children for their size before laying them out).
- Use lazy builders (,
ListView.builder) for long lists.GridView.builder - Avoid on scrollables unless absolutely necessary.
ShrinkWrap: true
- Use lazy builders (
-
Handle Framework Breaking Changes (Validate-and-Fix) Ensure the application complies with recent Flutter optimization changes regardingand
LayoutBuilder. These widgets no longer rebuild implicitly.OverlayEntry- Validate: Check if or
LayoutBuilderUI fails to update.OverlayEntry - Fix: Wrap the state modification triggering the update in an explicit .
setState
dart// FIX: Explicit setState for Overlay/Route changes final newLabel = await Navigator.pushNamed(context, '/bar'); setState(() { buttonLabel = newLabel; }); - Validate: Check if
-
Web-Specific Profiling If profiling for Web, inject timeline events into Chrome DevTools by adding these flags tobefore
main():runApp()dartimport 'package:flutter/widgets.dart'; import 'package:flutter/rendering.dart'; void main() { debugProfileBuildsEnabled = true; debugProfileBuildsEnabledUserWidgets = true; debugProfileLayoutsEnabled = true; debugProfilePaintsEnabled = true; runApp(const MyApp()); }STOP AND ASK THE USER: "Have you captured the Chrome DevTools performance profile? Please share the timeline event bottlenecks if you need specific refactoring."
-
建立性能基准线 要以编程方式衡量性能,创建一个记录性能时间线的集成测试。 请暂停并询问用户: “你是否要运行基准集成测试,在优化前捕获时间线指标?” 如果是,实现以下驱动和测试代码:test_driver/perf_driver.dart(不可修改操作):dart
import 'package:flutter_driver/flutter_driver.dart' as driver; import 'package:integration_test/integration_test_driver.dart'; Future<void> main() { return integrationDriver( responseDataCallback: (data) async { if (data != null) { final timeline = driver.Timeline.fromJson( data['scrolling_timeline'] as Map<String, dynamic>, ); final summary = driver.TimelineSummary.summarize(timeline); await summary.writeTimelineToFile( 'scrolling_timeline', pretty: true, includeSummary: true, ); } }, ); }integration_test/scrolling_test.dart:dartimport 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:your_package/main.dart'; void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); testWidgets('Performance profiling test', (tester) async { await tester.pumpWidget(const MyApp()); final listFinder = find.byType(Scrollable); final itemFinder = find.byKey(const ValueKey('target_item')); await binding.traceAction(() async { await tester.scrollUntilVisible( itemFinder, 500.0, scrollable: listFinder, ); }, reportKey: 'scrolling_timeline'); }); }运行测试命令:flutter drive --driver=test_driver/perf_driver.dart --target=integration_test/scrolling_test.dart --profile --no-dds -
优化UI线程(构建开销) 如果UI线程每帧耗时超过8ms,重构Widget树:
- 本地化状态: 将调用尽可能移到Widget树的底层。
setState - 使用: 应用
const构造函数以中断重建遍历。const - 字符串构建: 在循环中用替代
StringBuffer运算符。+
dart// 不良实践:重建整个Widget树 setState(() { _counter++; }); // 良好实践:封装状态 class CounterWidget extends StatefulWidget { ... } // 在CounterWidget内部: setState(() { _counter++; });dart// 良好实践:高效字符串构建 final buffer = StringBuffer(); for (int i = 0; i < 1000; i++) { buffer.write('Item $i'); } final result = buffer.toString(); - 本地化状态: 将
-
优化Raster线程(渲染开销) 如果Raster线程每帧耗时超过8ms,消除高开销绘制操作:
- 尽可能用半透明颜色替代Widget。
Opacity - 在动画中用或
AnimatedOpacity替代FadeInImage。Opacity - 避免使用。使用容器的
Clip.antiAliasWithSaveLayer属性替代显式裁剪Widget。borderRadius
dart// 不良实践:高开销Opacity Widget Opacity( opacity: 0.5, child: Container(color: Colors.red), ) // 良好实践:半透明颜色 Container(color: Colors.red.withOpacity(0.5)) - 尽可能用半透明颜色替代
-
修复布局与固有测量过程 识别并移除由固有操作(例如,在布局前询问所有子Widget的尺寸)导致的过度布局过程。
- 对长列表使用懒加载构建器(、
ListView.builder)。GridView.builder - 除非绝对必要,否则避免在可滚动Widget上使用。
ShrinkWrap: true
- 对长列表使用懒加载构建器(
-
处理框架破坏性变更(验证与修复) 确保应用符合Flutter近期关于和
LayoutBuilder的优化变更。这些Widget不再自动重建。OverlayEntry- 验证: 检查或
LayoutBuilder的UI是否无法更新。OverlayEntry - 修复: 将触发更新的状态修改操作包裹在显式的中。
setState
dart// 修复:为Overlay/Route变更添加显式setState final newLabel = await Navigator.pushNamed(context, '/bar'); setState(() { buttonLabel = newLabel; }); - 验证: 检查
-
Web端特定性能分析 如果针对Web端进行性能分析,在函数的
main()之前添加以下标志,将时间线事件注入Chrome DevTools:runApp()dartimport 'package:flutter/widgets.dart'; import 'package:flutter/rendering.dart'; void main() { debugProfileBuildsEnabled = true; debugProfileBuildsEnabledUserWidgets = true; debugProfileLayoutsEnabled = true; debugProfilePaintsEnabled = true; runApp(const MyApp()); }请暂停并询问用户: “你是否已捕获Chrome DevTools的性能分析数据?如果需要特定重构建议,请分享时间线事件中的瓶颈。”
Constraints
约束条件
- NEVER profile performance in Debug mode. Always use mode on a physical device.
--profile - NEVER override on
operator ==objects. It results in O(N²) behavior and degrades performance. Rely onWidgetcaching instead.const - NEVER put a subtree in an that does not depend on the animation. Build the static part once and pass it as the
AnimatedBuilderparameter.child - DO NOT use constructors with a concrete of children (e.g.,
List,Column) if most children are off-screen. Always useListViewconstructors for lazy loading..builder - DO NOT use unless absolutely necessary (e.g., dynamic overlapping shapes with transparency). Precalculate and cache static overlapping shapes.
saveLayer()
- 绝对不要在Debug模式下进行性能分析。始终在物理设备上使用模式。
--profile - 绝对不要在对象上重写
Widget。这会导致O(N²)的性能表现,降低应用性能。应依赖operator ==缓存。const - 绝对不要将不依赖动画的子树放入中。静态部分只需构建一次,并作为
AnimatedBuilder参数传入。child - 不要使用包含具体子Widget列表的构造函数(例如、
Column),如果大多数子Widget处于屏幕外。对于懒加载,始终使用ListView构造函数。.builder - 不要使用,除非绝对必要(例如,带有透明度的动态重叠形状)。提前计算并缓存静态重叠形状。
saveLayer()