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

bwntbbo3  于 12个月前  发布在  Flutter
关注(0)|答案(3)|浏览(159)

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

// 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);
  }
}

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

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


我在想:这对性能来说真的很糟糕吗?每次消费者的状态更新时,所有的树都必须重建。然后我意识到到处都是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的回答,另一种防止重建的方法是进行以下修改:

//       onPressed: Provider.of<Counter>(context).increment,  // This listens
      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()?

相关问题