如何防止 Flutter 中不必要的渲染?

Ben*_*Lee 12 flutter

下面是一个最小的应用程序,展示了我的担忧。如果运行它,您将看到每个可见项的构建方法都会运行,即使只有一项的数据发生了更改。

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyList extends StatelessWidget {
  final List<int> items;
  MyList(this.items);
  @override
  Widget build(BuildContext context) {
    return ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          print("item $index built");
          return ListTile(
            title: Text('${items[index]}'),
          );
        });
  }
}

class MyApp extends StatefulWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
   List<int> items = List<int>.generate(10000, (i) => i);

  void _incrementItem2() {
    setState(() {
      this.items[1]++;
    });
  }

  @override
  Widget build(BuildContext context) {
    final title = 'Long List';

    return MaterialApp(
      title: title,
      home: Scaffold(
        appBar: AppBar(
          title: Text(title),
        ),
        body: Column(
          children: <Widget>[
            MyButton(_incrementItem2),
            Expanded(child: MyList(this.items))
          ],
        ),
      ),
    );
  }
}

class MyButton extends StatelessWidget {
  final Function incrementItem2;
  void handlePress() {
    incrementItem2();
  }

  MyButton(this.incrementItem2);
  @override
  Widget build(BuildContext context) {
    return TextButton(
      style: ButtonStyle(
        foregroundColor: MaterialStateProperty.all<Color>(Colors.blue),
      ),
      onPressed: handlePress,
      child: Text('increment item 2'),
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

在 React / React Native 中,我会使用 shouldComponentUpdate 或 UseMemo 来防止不必要的渲染。我想在 Flutter 中做同样的事情。

以下是 Flutter github 上同一主题的已关闭问题的链接:(我将在此问题的链接中发表评论) https://github.com/flutter/flutter/issues/19421

避免无用的“重新渲染”或调用 Flutter 中的构建方法的最佳实践是什么,或者考虑到 Flutter 和 React 之间的结构差异,这些额外的构建不是一个严重的问题吗?

这或多或少类似于几年前在堆栈溢出上提出的这个问题:How to Prevent rerender of a widget based on custom logic in flutter?

给出的赞成答案是使用 ListView.builder。虽然这可以阻止一些(如果不是大多数)无用的渲染,但它并不能阻止所有无用的渲染,如我的示例所示。

预先感谢您的知识和见解。

Chr*_*ian 5

Flutter 的关键特性之一是每个 Widget 都是自己的单元,并且可以在需要时决定重建。

Bloc 库可以很好地控制重建的内容。如果 ListTile 中的文本发生变化,您将有一个 MyListTile 类:

class MyListTile extends StatelessWidget {
  final int index;
  MyListTile(this.index):super(key:ValueKey(index));

  @override
  Widget build(BuildContext context) {
    return BlocBuilder<ItemBloc, ItemState>(
      buildWhen: (previous, current) {
        return previous.items[index] != current.items[index];
      },
      builder: (context, state) {
        return ListTile(title: Text('${state.items[index]}'));
      },
    );
  }
}
Run Code Online (Sandbox Code Playgroud)

BlocBuilder 的 buildWhen 就像您习惯的 shouldComponentUpdate 一样,它为您提供了很多控制权。