dart flutter:const widgets什么时候重建?

bwntbbo3  于 2024-01-04  发布在  Flutter
关注(0)|答案(3)|浏览(203)

我目前正在阅读provider包的示例代码:

  1. // ignore_for_file: public_member_api_docs
  2. import 'package:flutter/foundation.dart';
  3. import 'package:flutter/material.dart';
  4. import 'package:provider/provider.dart';
  5. void main() => runApp(MyApp());
  6. class Counter with ChangeNotifier {
  7. int _count = 0;
  8. int get count => _count;
  9. void increment() {
  10. _count++;
  11. notifyListeners();
  12. }
  13. }
  14. class MyApp extends StatelessWidget {
  15. @override
  16. Widget build(BuildContext context) {
  17. return MultiProvider(
  18. providers: [
  19. ChangeNotifierProvider(builder: (_) => Counter()),
  20. ],
  21. child: Consumer<Counter>(
  22. builder: (context, counter, _) {
  23. return MaterialApp(
  24. supportedLocales: const [Locale('en')],
  25. localizationsDelegates: [
  26. DefaultMaterialLocalizations.delegate,
  27. DefaultWidgetsLocalizations.delegate,
  28. _ExampleLocalizationsDelegate(counter.count),
  29. ],
  30. home: const MyHomePage(),
  31. );
  32. },
  33. ),
  34. );
  35. }
  36. }
  37. class ExampleLocalizations {
  38. static ExampleLocalizations of(BuildContext context) =>
  39. Localizations.of<ExampleLocalizations>(context, ExampleLocalizations);
  40. const ExampleLocalizations(this._count);
  41. final int _count;
  42. String get title => 'Tapped $_count times';
  43. }
  44. class _ExampleLocalizationsDelegate
  45. extends LocalizationsDelegate<ExampleLocalizations> {
  46. const _ExampleLocalizationsDelegate(this.count);
  47. final int count;
  48. @override
  49. bool isSupported(Locale locale) => locale.languageCode == 'en';
  50. @override
  51. Future<ExampleLocalizations> load(Locale locale) =>
  52. SynchronousFuture(ExampleLocalizations(count));
  53. @override
  54. bool shouldReload(_ExampleLocalizationsDelegate old) => old.count != count;
  55. }
  56. class MyHomePage extends StatelessWidget {
  57. const MyHomePage({Key key}) : super(key: key);
  58. @override
  59. Widget build(BuildContext context) {
  60. return Scaffold(
  61. appBar: AppBar(title: const Title()),
  62. body: const Center(child: CounterLabel()),
  63. floatingActionButton: const IncrementCounterButton(),
  64. );
  65. }
  66. }
  67. class IncrementCounterButton extends StatelessWidget {
  68. const IncrementCounterButton({Key key}) : super(key: key);
  69. @override
  70. Widget build(BuildContext context) {
  71. return FloatingActionButton(
  72. onPressed: Provider.of<Counter>(context).increment,
  73. tooltip: 'Increment',
  74. child: const Icon(Icons.add),
  75. );
  76. }
  77. }
  78. class CounterLabel extends StatelessWidget {
  79. const CounterLabel({Key key}) : super(key: key);
  80. @override
  81. Widget build(BuildContext context) {
  82. final counter = Provider.of<Counter>(context);
  83. return Column(
  84. mainAxisSize: MainAxisSize.min,
  85. mainAxisAlignment: MainAxisAlignment.center,
  86. children: <Widget>[
  87. const Text(
  88. 'You have pushed the button this many times:',
  89. ),
  90. Text(
  91. '${counter.count}',
  92. style: Theme.of(context).textTheme.display1,
  93. ),
  94. ],
  95. );
  96. }
  97. }
  98. class Title extends StatelessWidget {
  99. const Title({Key key}) : super(key: key);
  100. @override
  101. Widget build(BuildContext context) {
  102. return Text(ExampleLocalizations.of(context).title);
  103. }
  104. }

字符串
一开始我对下面的代码感到困惑。它是一个MultiProvider,紧接着是一个Consumer,位于Widget树的顶部:

  1. return MultiProvider(
  2. providers: [
  3. ChangeNotifierProvider(builder: (_)=>Counter()),
  4. ],
  5. child: Consumer<Counter>(
  6. builder: (context, counter, _){
  7. return MaterialApp(
  8. home: const MyHomePage()
  9. );
  10. },
  11. ),
  12. );


我在想:这对性能来说真的很糟糕吗?每次消费者的状态更新时,所有的树都必须重建。然后我意识到到处都是const限定符。这看起来是一个非常整洁的设置。我决定调试它,看看何时何地重建小部件。
当应用程序第一次启动时,flutter会沿着树一个接一个地构建小部件,这是有道理的。
当单击按钮并递增Counter时,builder在树的最顶部的Consumer上被调用。之后,buildCounterLabelIncrementCounterButton上被调用。
CounterLabel是有意义的。这不是const,实际上会改变它的内容。但是IncrementCounterButton被标记为const。为什么它会重建?
我不太清楚为什么有些const小部件被重建了,而另一些却没有。这背后的系统是什么?

huus2vyu

huus2vyu1#

小部件重建的最常见原因是:

  • 这是父重建(无论原因是什么)
  • 手动调用了元素.markNeedsBuild(通常使用setState)
  • 它所依赖的继承小部件已更新

小部件的Const示例不受第一个原因的影响,但它们仍然受到其他两个原因的影响。
这意味着一个StatelessWidget的const示例将 * 仅 * 在它使用更新的继承小部件之一时重建。

qyswt5oh

qyswt5oh2#

Provider是InheritedWidget的一个方便的 Package 器,为您做了很多好的事情。
因为IncrementCounterButton访问Provider(以及底层的InheritedWidget),所以每当数据发生更改时,它都会侦听并重新构建。
要防止按钮或其他不需要在数据更改时重建的小部件,请将listen设置为false
Provider.of(context,listen:false).increment
需要注意的是,如果根部件重新生成,标记为listen: false的部件仍将重新生成。Understand how listen: false works when used with Provider.of(context, listen: false)
希望这有帮助!

dffbzjpn

dffbzjpn3#

基于@RayLi和@Remi的回答,另一种防止重建的方法是进行以下修改:

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

字符串
context.read()不会更新,但在这种情况下,这是你想要的。onPressed将Map到.increment的同一个示例在整个浮动按钮的存在。
context.read<Counter>()Provider.of<Counter>(context, listen: false)具有相同的行为。参见Is Provider.of(context, listen: false) equivalent to context.read()?

相关问题