Разница Consumer / Provider.of в ChangeNotifierProvider

Я действительно не понимаю разницы между Provider.of () и Consumer. Я прочитал здесь, что Потребитель подобен Поставщику. из с прослушиванием: правда.

Однако в приведенном ниже примере я не получаю сообщение об ошибке, когда использую Consumer, но я получаю его при использовании Provider.of. Я вынужден использовать listen: false. Приведенный ниже пример - это приложение флаттера по умолчанию с реализованным ChangeNotifierProvider.

Я просто изменю код в FloatingActionButton в main.dart, чтобы увидеть различия между Consumer, Provider.of listen: true и Provider.of listen: false.

Код counter.dart

import 'package:flutter/material.dart';

class Counter extends ChangeNotifier {
  int value = 0;

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

  void decrement() {
    value--;
    notifyListeners();
  }
}

Полный код main.dart, с floatActionButton и Consumer. Это работает

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:unit_test/counter.dart';

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

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: ChangeNotifierProvider<Counter>(
        create: (context) => Counter(),
        child: MyHomePage(title: 'Flutter Demo Home Page'),
      ),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              'You have pushed the button this many times:',
            ),
            Consumer<Counter>(builder: (context, counter, child) {
              return Text(
                counter.value.toString(),
                style: Theme.of(context).textTheme.headline4,
              );
            }),
          ],
        ),
      ),
      floatingActionButton:
          Consumer<Counter>(builder: (context, counter, child) {
        return FloatingActionButton(
          onPressed: () {
            counter.increment();
          },
          tooltip: 'Increment',
          child: Icon(Icons.add),
        );
      }),
    );
  }
}

Код FloatingActionButton с использованием Provider.of, прослушивание: true, не работает

floatingActionButton: FloatingActionButton(
        onPressed: () {
          Provider.of<Counter>(context, listen: true).increment();
        },
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ),

Ошибка: попытался прослушать значение, предоставленное поставщиком, за пределами дерева виджетов ...

Код floatActionButton с использованием Provider.of, прослушивание: false, работает

floatingActionButton: FloatingActionButton(
    onPressed: () {
      Provider.of<Counter>(context, listen: false).increment();
    },
    tooltip: 'Increment',
    child: Icon(Icons.add),
  ),

Я не понимаю. Я поставил listen: false, но он все еще слушает и перестраивает виджет

Спасибо за помощь


person Dimitri Leurs    schedule 12.10.2020    source источник


Ответы (1)


listen:true необходимо поместить в дерево виджетов сборки. Его цель - перестроить весь виджет, если он вызывается (notifyListener).

@override
  Widget build(BuildContext context) {
    Counter counter = Provider.of<Counter>(context, listen: true);
...
    counter.increment(); // Will rebuild entire widget.
...

Consumer - это виджет, который вызывает listen:true внутри себя, но только перестраивает его дочерний виджет.

Consumer<Counter>(builder: (_, counter, __) {
  return Column(
    children:[
      TextButton(
        onPressed: () => counter.increment(), // Will only rebuild this Column
        child: Icon(Icons.add),),
      Text(counter.value.toString());
}),

listen:false получит доступ к провайдеру без перестроения виджета, если он будет вызван.

TextButton(
  onPressed: () { 
    Counter counter = Provider.of<Counter>(context, listen: false);
    counter.increment(); 
    // Will change Provider value, but won't rebuild. It will make widget that has 
    // Consumer as parent to rebuild (or with listen:true to rebuild). 
  }
  child: Icon(Icons.add),
),
person Ryde    schedule 11.05.2021
comment
Хорошо объяснено. Вдобавок, возможно, стоит взглянуть на исходный код Consumer в consumer.dart, чтобы узнать, что все, что он делает, это вызывает построитель с Provider.of<T>. Таким образом, использование Consumer - это просто удобство использования Provider.of с Builder. - person Theo Tiger; 19.05.2021