简体   繁体   English

颤振:什么时候重建 const 小部件?

[英]flutter: when are const widgets rebuilt?

I'm currently reading the example code of the provider package:我目前正在阅读提供程序包的示例代码:

// ignore_for_file: public_member_api_docs
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';

void main() => runApp(MyApp());

class Counter with ChangeNotifier {
  int _count = 0;
  int get count => _count;

  void increment() {
    _count++;
    notifyListeners();
  }
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      providers: [
        ChangeNotifierProvider(builder: (_) => Counter()),
      ],
      child: Consumer<Counter>(
        builder: (context, counter, _) {
          return MaterialApp(
            supportedLocales: const [Locale('en')],
            localizationsDelegates: [
              DefaultMaterialLocalizations.delegate,
              DefaultWidgetsLocalizations.delegate,
              _ExampleLocalizationsDelegate(counter.count),
            ],
            home: const MyHomePage(),
          );
        },
      ),
    );
  }
}

class ExampleLocalizations {
  static ExampleLocalizations of(BuildContext context) =>
      Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);

  const ExampleLocalizations(this._count);

  final int _count;

  String get title => 'Tapped $_count times';
}

class _ExampleLocalizationsDelegate
    extends LocalizationsDelegate<ExampleLocalizations> {
  const _ExampleLocalizationsDelegate(this.count);

  final int count;

  @override
  bool isSupported(Locale locale) => locale.languageCode == 'en';

  @override
  Future<ExampleLocalizations> load(Locale locale) =>
      SynchronousFuture(ExampleLocalizations(count));

  @override
  bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Title()),
      body: const Center(child: CounterLabel()),
      floatingActionButton: const IncrementCounterButton(),
    );
  }
}

class IncrementCounterButton extends StatelessWidget {
  const IncrementCounterButton({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return FloatingActionButton(
      onPressed: Provider.of<Counter>(context).increment,
      tooltip: 'Increment',
      child: const Icon(Icons.add),
    );
  }
}

class CounterLabel extends StatelessWidget {
  const CounterLabel({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final counter = Provider.of<Counter>(context);
    return Column(
      mainAxisSize: MainAxisSize.min,
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        const Text(
          'You have pushed the button this many times:',
        ),
        Text(
          '${counter.count}',
          style: Theme.of(context).textTheme.display1,
        ),
      ],
    );
  }
}

class Title extends StatelessWidget {
  const Title({Key key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Text(ExampleLocalizations.of(context).title);
  }
}

At first I was confused to see the following code.起初我看到下面的代码感到很困惑。 It is a MultiProvider, immediately followed by a Consumer, at the top of the Widget tree:它是一个 MultiProvider,紧接着是一个 Consumer,位于 Widget 树的顶部:

return MultiProvider(
  providers: [
    ChangeNotifierProvider(builder: (_)=>Counter()),
  ],
  child: Consumer<Counter>(
    builder: (context, counter, _){
      return MaterialApp(
        home: const MyHomePage()
      );
    },
  ),
);

I was wondering: Isn't this really bad for performance?我想知道:这对性能真的不好吗? Everytime the state of the consumer is updated, all the tree has to be rebuilt.每次更新消费者的状态时,都必须重建所有树。 Then I realized the const qualifiers everywhere.然后我意识到无处不在的const限定符。 This seems like a very neat setup.这似乎是一个非常简洁的设置。 I decided to debug through it and see when and where widgets are rebuilt.我决定通过它进行调试,看看何时何地重新构建小部件。

When the app is first started, flutter goes down the tree and builds the widgets one by one.当应用程序第一次启动时,flutter 会沿着树向下并一个一个地构建小部件。 This makes sense.这是有道理的。

When the button is clicked and the Counter is incremented, builder is called on the Consumer at the very top of the tree.当单击按钮并且Counter增加时,在树的最顶部的消费者上调用builder After that, build is called on CounterLabel and IncrementCounterButton .在此之后, build被称为上CounterLabelIncrementCounterButton

CounterLabel makes sense. CounterLabel是有道理的。 This is not const and will actually change its content.这不是const ,实际上会改变它的内容。 But IncrementCounterButton is marked as const .但是IncrementCounterButton被标记为const Why does it rebuild?为什么要重建?

It is not clear to me why some const widgets are rebuilt while others aren't.我不清楚为什么一些const小部件会被重建,而另一些则不会。 What is the system behind this?这背后的系统是什么?

The most common reasons for a widget to rebuild are:小部件重建的最常见原因是:

  • its parent rebuilt (whatever the reason is)它的父级重建(无论是什么原因)
  • Element.markNeedsBuild have been called manually (typically using setState) Element.markNeedsBuild 已被手动调用(通常使用 setState)
  • an inherited widget it depends on updated它依赖的继承小部件更新

Const instance of widgets are immune to the first reason, but they are still affected by the two others.小部件的 const 实例不受第一个原因的影响,但它们仍受其他两个原因的影响。

This means that a const instance of a StatelessWidget will rebuild only if one of the inherited widget it uses update.这意味着 StatelessWidget 的 const 实例只有在它使用的继承小部件之一更新时才会重建。

Provider is a convenient wrapper for InheritedWidget with a lot of nice things done for you. Provider 是 InheritedWidget 的一个方便的包装器,它为你做了很多好事。

Because IncrementCounterButton accesses Provider (and InheritedWidget under the hood), it listens and rebuilds whenever the data changes.因为IncrementCounterButton访问 Provider(以及引擎盖下的 InheritedWidget),它会在数据发生变化时监听并重建。

To prevent buttons or other widgets that do not need to be rebuild on data change, set listen to false .为了防止不需要在数据更改时重建的按钮或其他小部件,请将listen设置为false

Provider.of(context, listen: false).increment Provider.of(context, listen: false).increment

The caveat is that if the root widget rebuilds, widgets marked with listen: false will still rebuild.需要注意的是,如果根小部件重建,标记为listen: false小部件仍将重建。 Understand how listen: false works when used with Provider<SomeType>.of(context, listen: false) 了解与 Provider<SomeType>.of(context, listen: false) 一起使用时 listen: false 的工作原理

Hope this helps!希望这可以帮助!

Building on @RayLi and @Remi's answers, another way to prevent a rebuild is to make this modification:基于@RayLi 和@Remi 的回答,另一种防止重建的方法是进行以下修改:

//       onPressed: Provider.of<Counter>(context).increment,  // This listens
      onPressed: context.read<Counter>().increment,     // this doesn't listen

context.read() won't update, but in this case this is what you want. context.read()不会更新,但在这种情况下,这就是您想要的。 onPressed will be mapped to the same instance of .increment throughout the FloatingActionButton's existence. onPressed将被映射到相同的实例.increment整个FloatingActionButton的存在。

context.read<Counter>() has the same behavior as Provider.of<Counter>(context, listen: false) . context.read<Counter>()Provider.of<Counter>(context, listen: false)具有相同的行为。 See Is Provider.of(context, listen: false) equivalent to context.read()?请参阅Provider.of(context, listen: false) 是否等同于 context.read()?

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Flutter:StreamProvider 的奇怪行为,使用不完整数据重建的小部件 - Flutter: Strange behavour of StreamProvider, widgets rebuilt with incomplete data Flutter:使用带有 AnimatedSwitcher 的 setstate 时未重建小部件 - Flutter: Widget not rebuilt when using setstate with AnimatedSwitcher 对于哪些颤振小部件,我们需要使用 const? - For which flutter widgets we need to use const? 在使用 Flutter 中的 ChangeNotifierProvider 时,消费者之外的小部件也会被重建 - Widgets present outside the Consumer are also getting rebuilt while using ChangeNotifierProvider in Flutter Flutter - 我的小部件每次都不断重建并发出 http 请求 - Flutter - My widgets keep getting rebuilt and making http request every time Flutter 页面重建时重建底部导航栏 - Flutter rebuild bottom navigation bar when page is rebuilt 有状态与无状态的区别如何影响何时重建小部件? - How does the stateful vs. stateless distinction affect when widgets will be rebuilt? 有没有办法重建整个项目,包括flutter项目中的const小部件? - Is there a way to rebuild the whole app include const widgets in flutter project? flutter 小部件填充时的问题 - issue when flutter widgets padding flutter riverpod ConsumerWidget 未重建 - flutter riverpod ConsumerWidget not being rebuilt
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM