在 Dart 2 / Flutter 中使用 `const` 使用 "instantiating" 对象时有任何性能提升吗?

Any performance gain when using "instantiating" objects using `const` in Dart 2 / Flutter?

在 Dart 2 中,不再需要关键字 newconst 来实例化对象。例如,new ListView(...) 现在等同于 ListView(...).

但是,一些 class 构造函数被声明为 const,包括大多数 Flutter 小部件,例如 Text:

const Text(
    String this.data, {
    Key? key,
    this.style,
    // ...
    this.textHeightBehavior,
})

然后,要实例化 Text,我们可以键入 Text('hi!')const Text('hi!')

我的问题:两者在性能或编译代码方面有什么不同吗?

注:

调用处const的用法在某些地方比较突出,比如在AndroidStudio中,如果我们选择“Wrap with Padding”,EdgeInsets.all构造函数调用使用 const:

 Padding(
     padding: const EdgeInsets.all(8.0),
     child: Text('hi!'),
 ),

您应该尽可能尝试使用 const,尤其是在您创建 Flutter 应用程序时。常量构造函数非常重要,因为它们用于“缓存”小部件,这样它们就不会被不必要地重建。


假设您的 Flutter 应用程序中有 10k 个项目的列表:

class Example extends StatelessWidget {
  const Example();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('My items:'),
        
        ListView.builder(
          shrinkWrap: true,
          itemCount: 10000,
          itemBuilder: (_, i) => SomeComplexWidget(
            value: i,
          ),
        ),
      ],
    );
  }
}

有很多因素可以触发小部件重建,但我们假设您只是旋转了几次设备屏幕。使用此代码...

SizedBox(
  width: 100,
  height: 400,
  child: Example(),
),

...您没有使用 const 构造函数,因此您在 每个 重建时重建 Example (=您也不必要重建 ColumnListView,并重新渲染一些 SomeComplexWidget)。 ListView.builder 是一个惰性初始化程序,因此它会为您节省一点,但您仍然不希望它总是被重建。

如果您为您的列表使用 Column() 或普通的 ListView() 构造函数,它会对性能产生更大的影响(更多的卡顿和更多的跳帧)。如果您改为这样做...

SizedBox(
  width: 100,
  height: 400,
  child: const Example(),
),

...您将“缓存”Example 小部件(这意味着它将仅构建一次)。您可以猜到,这是您可以执行的非常简单但重要的性能优化。使用常量构造函数不仅是我的话,而且是文档官方推荐的:

请注意,如果您的小部件包含依赖于 InheritedWidget 的内容,则 const 关键字将不起作用(否则,小部件无法“监听”继承小部件的更改".


奖金

在 Flutter 中,您还可以“手动”缓存 widget。当不能使用const时,可以在state内部使用late(或late final)来缓存widget。看这个例子:

class OtherExample extends StatelessWidget {
  final List<Interests> interests;
  const OtherExample({
    required this.interests,
  });

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('My list'),
   
        MyInterestsList(
          data: interests,
        ),
      ]
    );
  }
}

您希望 MyInterestsList 被缓存,因为它永远不会改变,但 const 在这里不起作用(因为 interests 而不是 编译时)常量。当你不能使用 const 但你仍然想缓存一个小部件时,手动执行:

class OtherExample extends StatefulWidget {
  final List<Interests> interests;
  const OtherExample({
    required this.interests,
  });

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

class _OtherExampleState extends State<OtherExample> {
  late Widget myInterests = MyInterestsList(
     data: widget.interests,
  );

  @override
  void didUpdateWidget(OtherExample oldWidget) { 
    super.didUpdateWidget(oldWidget);

    // This makes sure that the interests list is updated ONLY when
    // it actually changes
    if (widget.interests != oldWidget.interests) {
      myInterests = MyInterestsList(
        data: widget.interests,
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const Text('My list'),
   
        myInterests,
      ]
    );
  }
}

感谢 late,您将小部件缓存在状态 class 中。由于 didUpdateWidget 覆盖,MyInterestsList 小部件将 只有 在列表更改时重建。如果列表永远不会改变,MyInterestsList 将永远不会重建。