Как правильно повторно использовать провайдера во Flutter

Итак, у меня этот ChangeNotifierProvider находится высоко в моем дереве виджетов, поскольку я вижу много дочерних виджетов, которые слушают его значение.

В настоящее время я передаю объект Provider.of (context) из родительского виджета в его дочерние элементы через конструкторы всякий раз, когда мне нужно повторно использовать некоторые значения / функции в моих дочерних виджетах. Например, каждый раз, когда я создаю объект Provider.of (context) для своих дочерних виджетов, кажется, что он не переносит обновленные значения, которые у меня есть в родительском поставщике, а скорее у этого есть мое значение по умолчанию. null / 0 / «пустые», как будто они только что были созданы. Это побудило меня передать начальный объект Provider.of (context) каждому дочернему элементу, который будет использовать обновленные значения и функции ChangeNotifier.

Эта настройка работает для меня, однако, когда мое дерево виджетов стало сложным, я постоянно передаю значения через каждый виджет и некоторым из них, которые даже не используют его вообще, только для того, чтобы его дочерние элементы слушали основного поставщика.

Я думаю, что то, что я сейчас делаю, является антипаттерном архитектуры провайдера, я надеюсь, что вы, ребята, поможете мне найти более оптимизированный и эффективный способ сделать это.

Большое спасибо!

P.S. В документации есть некоторые вещи, которые я еще не совсем понял.

Ниже приведены правки, включающие образец кода и визуализацию:

provider_type.dart

class ProviderType extends ChangeNotifier{
    String valueA = '';
    String valueB = '';
}

home.dart

import ..provider_type.dart
...
    Widget build(BuildContext context){
        return ChangeNotifierProvider<ProviderType>(
            create: (context) => ProviderType(),
            child: ScreenColumn();
        );
    }
...

screen_column.dart

import ..screen_a.dart
import ..screen_b.dart
class ScreenColumn extends StatelessWidget{
    Widget build(BuildContext context){
        var providerType = Provider.of<ProviderType>(context);

        return Column(
            children: <Widget>[
                ScreenA(providerType: providerType),
                ScreenB(providerType: providerType),
            ],
        );
    }
}

screen_a.dart

class ScreenA extends StatelessWidget{
    final ProviderType providerType;

    ScreenA({this.providerType});

    Widget build(BuildContext context){
        return Text(
            '${providerType.valueA}'
        );
    }
}

screen_b.dart

import ..screen_c.dart
class ScreenB extends StatelessWidget{
    final ProviderType providerType;

    ScreenB({this.providerType});

    Widget build(BuildContext context){
        return ScreenC(providerType: providerType);
    }
}

screen_c.dart

class ScreenC extends StatelessWidget{
    final ProviderType providerType;

    ScreenB({this.providerType});

    Widget build(BuildContext context){
        return Column(
        children: <Widget>[
            Text(
                '${providerType.valueA}'
            )
            Text(
                '${providerType.valueB}'
            )
            Text(
                '${providerType.valueC}'
            )
        ]
        );
    }
}

Визуализация

введите здесь описание изображения

В настоящее время я передаю объект providerType из ScreenColumn на экраны A, B и C, чтобы каждый из них имел один и тот же «Источник значений». Потому что, когда я пытаюсь создать разные объекты Provider.of и использовать их, они не используют одни и те же обновленные значения, когда я выполняю некоторые вычисления.

Могу ли я что-то сделать, чтобы сделать это более эффективным, или есть способ лучше?


person AverageCoder    schedule 19.02.2020    source источник
comment
Я не совсем понимаю вашу проблему. Не могли бы вы поделиться небольшим фрагментом кода, чтобы помочь визуализировать проблему?   -  person Rémi Rousselet    schedule 19.02.2020
comment
О боже, это ты! Я просто сделаю несколько быстрых образцов   -  person AverageCoder    schedule 19.02.2020
comment
Привет, @ RémiRousselet, я добавил в свой вопрос образцы, которые вы задали. Надеюсь, это поможет   -  person AverageCoder    schedule 19.02.2020
comment
Что вы имеете в виду под тем, что они не используют одни и те же обновленные значения, когда я делаю некоторые вычисления.   -  person Rémi Rousselet    schedule 19.02.2020
comment
Эта фраза применяется, когда я не наследую дочерним элементам тот же объект Provider.of родительского объекта. Например, в моем родительском объекте Provider.of я обновил значение x до 1, это же значение переносится на потомков, когда я передаю его. Но когда я создаю другой объект Provider.of, когда я снова пытаюсь вызвать значение x, он скажет мне, что значение x было моим начальным значением 0, а не таким же, как у родительского объекта Provider.of .   -  person AverageCoder    schedule 19.02.2020
comment
Я не говорю, что это ошибка, а, я просто объясняю, что я делаю прямо сейчас, чтобы получить доступ к тем же значениям объекта Provider.of, и, возможно, есть лучший способ улучшить то, что я сейчас делаю, поскольку будет тонна дочерних виджетов, которым придется унаследовать один и тот же объект Provider.of.   -  person AverageCoder    schedule 19.02.2020
comment
Извините, я все еще не понимаю. Не могли бы вы это продемонстрировать?   -  person Rémi Rousselet    schedule 19.02.2020
comment
Привет, @ RémiRousselet, мне очень жаль, братан. После некоторых проб и ошибок я понял, что на самом деле ошибаюсь в своем понимании и реализации, и просто упустил некоторые основные концепции. Похоже, что я могу просто создать новые объекты Provider.of для каждого дочернего виджета, и они будут просто слушать моего родительского провайдера как для значений, так и для функций. Мои эксперименты подтвердили это. -_- Я не знаю, что сказать, извините за проблемы и спасибо, что нашли время, чтобы помочь мне. Виноват.   -  person AverageCoder    schedule 19.02.2020
comment
Итак, проблема, хорошего дня!   -  person Rémi Rousselet    schedule 19.02.2020


Ответы (1)


Тем, кто может интересоваться или искать ответы на тот же вопрос, посмотрите мой пример кода ниже, который показывает, как вы можете повторно использовать / делиться своими значениями и функциями поставщика в любой точке вашего дерева виджетов, если они находятся под вашим родительским элементом. Провайдер.

И да, вы можете просто создать Provider.of объектов в любом месте вашего дерева, не передавая начальный объект Provider.of, который вы создали.

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

class ProviderType extends ChangeNotifier {
  String value = DateTime.now().toString();

  changeValue() {
    value = DateTime.now().toString();
    notifyListeners();
  }
}

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider<ProviderType>(
      create: (context) => ProviderType(),
      child: MaterialApp(
        home: Home(),
      ),
    );
  }
}

class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var providerType = Provider.of<ProviderType>(context);

    return Scaffold(
      appBar: AppBar(
        title: Text('Sample App'),
      ),
      body: ScreenColumn(),
      floatingActionButton: FloatingActionButton.extended(
        onPressed: () => providerType.changeValue(),
        label: Text('ChangeValue'),
      ),
    );
  }
}

class ScreenColumn extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return SingleChildScrollView(
        child: Column(
      crossAxisAlignment: CrossAxisAlignment.stretch,
      children: <Widget>[
        ScreenA(),
        ScreenB(),
        ScreenC(),
        ScreenC(),
      ],
    ));
  }
}

class ScreenA extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var providerType = Provider.of<ProviderType>(context);
    return Card(
      color: Colors.red,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Text(providerType.value),
      ),
    );
  }
}

class ScreenB extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var providerType = Provider.of<ProviderType>(context);
    return Card(
      color: Colors.blue,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text(providerType.value),
            ScreenC(),
            ScreenC(),
          ],
        ),
      ),
    );
  }
}

class ScreenC extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // var providerType = Provider.of<ProviderType>(context);

    return Card(
      color: Colors.green,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text('This is Screen B with no Provider.of Object'),
            ScreenD(),
            ScreenD(),
            ScreenD(),
          ],
        ),
      ),
    );
  }
}

class ScreenD extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    var providerType = Provider.of<ProviderType>(context);
    return Card(
      color: Colors.yellow,
      elevation: 8.0,
      child: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: <Widget>[
            Text(
                'This is Screen D. A Provider.of object was created here without inheriting the Parent\'s Provider.of object.'),
            Text(providerType.value),
          ],
        ),
      ),
    );
  }
}
person AverageCoder    schedule 20.02.2020
comment
привет @AverageCoder, применимо ли это также к buildMethods во Flutter, или лучше передать здесь 1 Provider в качестве аргумента? - person Nuqo; 16.05.2021
comment
Привет @Nuqo, вы имеете в виду передачу экземпляра провайдера каждому виджету в качестве параметра? Насколько я понимаю, это было бы нелогично делать это, поскольку это было бы проблематично для виджетов, расположенных ниже в дереве виджетов - вы могли бы делать ненужные проходы для виджетов, которые его не используют. Замечательная вещь с поставщиком - вы можете вызвать экземпляр объекта поставщика верхнего уровня в любом месте вашего дерева виджетов с помощью provider.of или других помощников, поэтому вам не придется передавать их каждому виджету. В моем случае у меня есть один главный провайдер для всего дерева виджетов. - person AverageCoder; 17.05.2021
comment
Отличный ответ! - person apxcode; 29.05.2021