返回首页

Flutter 优化:避免重建

本文分析了 8 个常见的 Flutter 应用优化错误:多余重建、缺少 const、build() 中的逻辑、不正确的列表。提供了修复代码示例和保持稳定 60 FPS 的关键实践。

无卡顿的 Flutter:8 个关键优化
Advertisement 728x90

Flutter应用性能优化实战技巧

Flutter应用常因不必要的重建、UI线程上的操作和低效列表处理导致卡顿。本文剖析关键错误及修复方法:从局部更新到构建器和动画的正确使用。这些技巧有助于在低端设备上保持60 FPS。

最小化不必要的UI重建

不必要的重建是性能下降的主要原因。setState()会重建StatefulWidget的整个子树,包括重型列表和图像,导致布局重新计算和FPS下降。

问题代码:

Google AdInline article slot
class MyScreen extends StatefulWidget {
  @override
  State<MyScreen> createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
  int counter = 0;
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Text('计数器: $counter'),
        ElevatedButton(
          onPressed: () {
            setState(() {
              counter++; // 重建整个树
            });
          },
          child: Text('增加'),
        ),
      ],
    );
  }
}

解决方案: ValueListenableBuilder仅更新UI的依赖部分。

class MyScreen extends StatelessWidget {
  final ValueNotifier<int> counter = ValueNotifier(0);
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ValueListenableBuilder<int>(
          valueListenable: counter,
          builder: (_, value, __) {
            return Text('计数器: $value');
          },
        ),
        ElevatedButton(
          onPressed: () => counter.value++,
          child: const Text('增加'),
        ),
      ],
    );
  }
}

类似地,StreamBuilder、provider的Selector或BlocBuilder也适用。重绘区域最小化,减少CPU负载。

使用const构造函数

不使用const时,每次重建都会创建新的widget对象,增加GC和CPU负担。在包含数百个图标、文本和内边距的列表中,这至关重要。

Google AdInline article slot

最佳实践:

Widget build(BuildContext context) {
  return const Column(
    children: [
      Text('你好'),
      Icon(Icons.home),
      Padding(
        padding: EdgeInsets.all(8),
        child: Text('世界'),
      ),
    ],
  );
}

Flutter重用const对象,减少内存分配。在包含数千个小部件的复杂UI中,效果显著。

将逻辑移出build()

在build()中进行排序、过滤或解析会在每次调用时运行——每秒高达60次。为保持60 FPS,每帧仅16毫秒可用;重型操作会导致卡顿。

Google AdInline article slot

正确方法:

late List<int> filtered;
@override
void initState() {
  super.initState();
  final sorted = [...items]..sort((a, b) => a.compareTo(b));
  filtered = sorted.where((e) => e > 10).toList();
}
@override
Widget build(BuildContext context) {
  return ListView.builder(
    itemCount: filtered.length,
    itemBuilder: (_, i) => Text('${filtered[i]}'),
  );
}

逻辑在initState或业务层中运行一次。build()保持声明式。

使用ListView.builder实现懒加载列表

ListView使用children: [...]会一次性创建所有项,包括不可见项。对于100+项,内存和初始渲染会受影响。

推荐:

Widget build(BuildContext context) {
  return ListView.builder(
    itemCount: items.length,
    itemBuilder: (context, index) {
      final item = items[index];
      return ListTile(
        title: Text(item.title),
        subtitle: Text(item.subtitle),
      );
    },
  );
}

项在滚动时生成,内存优化,回收机制高效工作。

使用Key确保列表稳定性

没有key时,Flutter无法在列表更新时匹配元素,导致不必要的重建和视觉异常。

关键用于:

  • 重新排序元素
  • 删除/插入
  • 过渡动画
ListView.builder(
  itemCount: items.length,
  itemBuilder: (_, index) {
    final item = items[index];
    return ListTile(
      key: ValueKey(item.id),
      title: Text(item.title),
    );
  },
);

ValueKey(item.id)确保正确更新。

无需setState的动画

Timer.periodic配合setState每16–100毫秒调用会过载UI线程。

标准工具:

late AnimationController controller;
@override
void initState() {
  super.initState();
  controller = AnimationController(
    vsync: this,
    duration: Duration(seconds: 2),
  )..repeat();
}
Widget build(BuildContext context) {
  return AnimatedBuilder(
    animation: controller,
    builder: (_, __) {
      return LinearProgressIndicator(value: controller.value);
    },
  );
}

AnimationController与设备FPS同步,更新最小UI。

拆分为小组件

一个庞大的StatefulWidget在任何变化时都会完全重建。

组合的优势:

  • 局部重建
  • 代码复用
  • 更好的可读性和测试性
Widget build(BuildContext context) {
  return Column(
    children: const [
      Header(),
      Expanded(child: ItemsList()),
      Footer(),
    ],
  );
}
class Header extends StatelessWidget {
  const Header();
  @override
  Widget build(BuildContext context) {
    return Text('头部');
  }
}

将Future移出build()

FutureBuilder在build()中使用future: fetchData()会在每次重建时发起请求。

late Future dataFuture;
@override
void initState() {
  super.initState();
  dataFuture = fetchData();
}
Widget build(BuildContext context) {
  return FutureBuilder(
    future: dataFuture,
    builder: (_, snapshot) {
      if (!snapshot.hasData) return CircularProgressIndicator();
      return Text(snapshot.data.toString());
    },
  );
}

Future创建一次;UI响应变化。

关键要点

  • 局部更新: 使用ValueListenableBuilder、AnimatedBuilder替代全局setState。
  • 处处const: 在复杂UI中减少50–70%的内存分配。
  • 逻辑放initState: build()仅用于声明式描述。
  • ListView.builder + keys: 用于稳定的动态列表。
  • 组件组合: 小型StatelessWidget优于单体结构。

— Editorial Team

Advertisement 728x90

继续阅读