flutter-performance

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Flutter 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:
  1. Is the goal to establish a performance baseline?
    • Yes: Implement an integration test using
      traceAction
      and
      TimelineSummary
      .
    • No: Proceed to step 2.
  2. Is the application running on Web?
    • Yes: Enable
      debugProfileBuildsEnabled
      and use Chrome DevTools Performance panel.
    • No: Run the app on a physical device in
      --profile
      mode and launch Flutter DevTools.
  3. Which thread is showing jank (red bars > 16ms) in the DevTools Performance View?
    • UI Thread: Optimize
      build()
      methods, localize
      setState()
      , use
      const
      constructors, and replace string concatenation with
      StringBuffer
      .
    • Raster (GPU) Thread: Minimize
      saveLayer()
      ,
      Opacity
      ,
      Clip
      , and
      ImageFilter
      usage. Pre-cache complex images using
      RepaintBoundary
      .
    • Both: Start by optimizing the UI thread (Dart VM), as expensive Dart code often cascades into expensive rendering.
使用以下决策树评估目标应用,确定优化路径:
  1. 是否要建立性能基准线?
    • 是: 使用
      traceAction
      TimelineSummary
      实现集成测试。
    • 否: 进入步骤2。
  2. 应用是否运行在Web端?
    • 是: 启用
      debugProfileBuildsEnabled
      并使用Chrome DevTools的性能面板。
    • 否: 在物理设备上以
      --profile
      模式运行应用,并启动Flutter DevTools。
  3. DevTools性能视图中哪个线程出现卡顿(红色条>16ms)?
    • UI线程: 优化
      build()
      方法,本地化
      setState()
      调用,使用
      const
      构造函数,用
      StringBuffer
      替代字符串拼接。
    • Raster(GPU)线程: 尽量减少
      saveLayer()
      Opacity
      Clip
      ImageFilter
      的使用。使用
      RepaintBoundary
      预缓存复杂图像。
    • 两者都有: 先从优化UI线程(Dart VM)开始,因为高开销的Dart代码通常会引发连锁反应导致渲染开销增加。

Instructions

操作步骤

  1. 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:
    dart
    import '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
  2. Optimize UI Thread (Build Costs) If the UI thread exceeds 8ms per frame, refactor the widget tree:
    • Localize State: Move
      setState
      calls as low in the widget tree as possible.
    • Use
      const
      :
      Apply
      const
      constructors to short-circuit rebuild traversals.
    • 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();
  3. Optimize Raster Thread (Rendering Costs) If the Raster thread exceeds 8ms per frame, eliminate expensive painting operations:
    • Replace
      Opacity
      widgets with semitransparent colors where possible.
    • Replace
      Opacity
      in animations with
      AnimatedOpacity
      or
      FadeInImage
      .
    • Avoid
      Clip.antiAliasWithSaveLayer
      . Use
      borderRadius
      properties on containers instead of explicit clipping widgets.
    dart
    // BAD: Expensive Opacity widget
    Opacity(
      opacity: 0.5,
      child: Container(color: Colors.red),
    )
    
    // GOOD: Semitransparent color
    Container(color: Colors.red.withOpacity(0.5))
  4. 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
      ,
      GridView.builder
      ) for long lists.
    • Avoid
      ShrinkWrap: true
      on scrollables unless absolutely necessary.
  5. Handle Framework Breaking Changes (Validate-and-Fix) Ensure the application complies with recent Flutter optimization changes regarding
    LayoutBuilder
    and
    OverlayEntry
    . These widgets no longer rebuild implicitly.
    • Validate: Check if
      LayoutBuilder
      or
      OverlayEntry
      UI fails to update.
    • 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;
    });
  6. Web-Specific Profiling If profiling for Web, inject timeline events into Chrome DevTools by adding these flags to
    main()
    before
    runApp()
    :
    dart
    import '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."
  1. 建立性能基准线 要以编程方式衡量性能,创建一个记录性能时间线的集成测试。 请暂停并询问用户: “你是否要运行基准集成测试,在优化前捕获时间线指标?” 如果是,实现以下驱动和测试代码:
    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
    dart
    import '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
  2. 优化UI线程(构建开销) 如果UI线程每帧耗时超过8ms,重构Widget树:
    • 本地化状态:
      setState
      调用尽可能移到Widget树的底层。
    • 使用
      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();
  3. 优化Raster线程(渲染开销) 如果Raster线程每帧耗时超过8ms,消除高开销绘制操作:
    • 尽可能用半透明颜色替代
      Opacity
      Widget。
    • 在动画中用
      AnimatedOpacity
      FadeInImage
      替代
      Opacity
    • 避免使用
      Clip.antiAliasWithSaveLayer
      。使用容器的
      borderRadius
      属性替代显式裁剪Widget。
    dart
    // 不良实践:高开销Opacity Widget
    Opacity(
      opacity: 0.5,
      child: Container(color: Colors.red),
    )
    
    // 良好实践:半透明颜色
    Container(color: Colors.red.withOpacity(0.5))
  4. 修复布局与固有测量过程 识别并移除由固有操作(例如,在布局前询问所有子Widget的尺寸)导致的过度布局过程。
    • 对长列表使用懒加载构建器(
      ListView.builder
      GridView.builder
      )。
    • 除非绝对必要,否则避免在可滚动Widget上使用
      ShrinkWrap: true
  5. 处理框架破坏性变更(验证与修复) 确保应用符合Flutter近期关于
    LayoutBuilder
    OverlayEntry
    的优化变更。这些Widget不再自动重建。
    • 验证: 检查
      LayoutBuilder
      OverlayEntry
      的UI是否无法更新。
    • 修复: 将触发更新的状态修改操作包裹在显式的
      setState
      中。
    dart
    // 修复:为Overlay/Route变更添加显式setState
    final newLabel = await Navigator.pushNamed(context, '/bar');
    setState(() {
      buttonLabel = newLabel;
    });
  6. Web端特定性能分析 如果针对Web端进行性能分析,在
    main()
    函数的
    runApp()
    之前添加以下标志,将时间线事件注入Chrome DevTools:
    dart
    import '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
    --profile
    mode on a physical device.
  • NEVER override
    operator ==
    on
    Widget
    objects. It results in O(N²) behavior and degrades performance. Rely on
    const
    caching instead.
  • NEVER put a subtree in an
    AnimatedBuilder
    that does not depend on the animation. Build the static part once and pass it as the
    child
    parameter.
  • DO NOT use constructors with a concrete
    List
    of children (e.g.,
    Column
    ,
    ListView
    ) if most children are off-screen. Always use
    .builder
    constructors for lazy loading.
  • DO NOT use
    saveLayer()
    unless absolutely necessary (e.g., dynamic overlapping shapes with transparency). Precalculate and cache static overlapping shapes.
  • 绝对不要在Debug模式下进行性能分析。始终在物理设备上使用
    --profile
    模式。
  • 绝对不要
    Widget
    对象上重写
    operator ==
    。这会导致O(N²)的性能表现,降低应用性能。应依赖
    const
    缓存。
  • 绝对不要将不依赖动画的子树放入
    AnimatedBuilder
    中。静态部分只需构建一次,并作为
    child
    参数传入。
  • 不要使用包含具体子Widget列表的构造函数(例如
    Column
    ListView
    ),如果大多数子Widget处于屏幕外。对于懒加载,始终使用
    .builder
    构造函数。
  • 不要使用
    saveLayer()
    ,除非绝对必要(例如,带有透明度的动态重叠形状)。提前计算并缓存静态重叠形状。