Использование блока и Provder вместе или

#flutter #bloc #flutter-provider

Вопрос:

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

Я выбрал блок для управления состоянием моего графического интерфейса, потому что он, похоже, подходит для традиционной машины состояний (переход событий / состояний), и я выбрал changeNotifierProvider, поскольку он кажется естественным местом для структуры данных моих приложений (дерево), и он не ожидает, что я буду рассматривать его как FSM с перечисляемыми состояниями.

Так что теперь вот где я как бы теряюсь. Как мне заставить BLoC и ChangeNotifierProvider хорошо взаимодействовать друг с другом?

Мне нужно, чтобы мой класс BLoC вызывал методы, определенные в моем классе ChangeNotifierProvider.

Поскольку мы используем BlockProvider для предоставления класса блока приложению, я рассудил, что должен относиться к своему классу блока так же, как к другому поставщику..

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

Так вот что я сделал.

Ниже приведен минимальный воспроизводимый пример того, что я пытаюсь сделать:

Мой основной мультипровер с ProxyProvider

 void main() {
  runApp(MultiProvider(providers: [
    ChangeNotifierProvider(create: (context) => DataStore(0)),
    ProxyProvider<DataStore, BlocSM>(
      update: (_, dataStore, __) =>
          BlocSM(dataStore.sumData, dataStore.prodData),
    ),
  ], child: MyApp()));
}
 

Мой класс хранилища данных «Определитель изменений»

 class DataStore extends ChangeNotifier {
  List<int> _data = List<int>();

  DataStore(int dataInit) {
    _data.add(dataInit);
  }

  void addData(int data) {
    _data.add(data);
  }

  int sumData() {
    int sum;
    for (var x in _data) {
      sum  = x;
    }
    return sum;
  }

  int prodData() {
    int prod;
    for (var x in _data) {
      prod *= x;
    }
    return prod;
  }
}
 

My BLoC class

 enum States { sumState, prodState }
enum Events { sumSelected, prodSelected, calculateEvent }

class BlocSM extends Bloc<Events, States> {
  Function() sumData;
  Function() prodData;
  BlocSM(Function() sumData, Function() prodData) : super(States.sumState);
  Stream<States> mapEventToState(Events event) async* {
    switch (state) {
      case States.sumState:
        if (event == Events.prodSelected) {
          yield States.prodState;
        } else if (event == Events.calculateEvent) {
          sumData();
        }
        break;
      case States.prodState:
        if (event == Events.sumSelected) {
          yield States.sumState;
        } else if (event == Events.calculateEvent) {
          prodData();
        }
        break;
    }
  }
}
 
 class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Provider BLoC Talk',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Provider BLoC Talk Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

enum MathOperation { summation, product }

class _MyHomePageState extends State<MyHomePage> {
  MathOperation _sumProduct = MathOperation.summation;
  final myController = TextEditingController();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ListTile(
              title: const Text('Summation'),
              leading: Radio(
                value: MathOperation.summation,
                groupValue: _sumProduct,
                onChanged: (MathOperation value) {
                  setState(() {
                    _sumProduct = value;
                    BlocProvider.of<BlocSM>(context, listen: false)
                        .add(Events.sumSelected);
                  });
                },
              ),
            ),
            ListTile(
              title: const Text('Product'),
              leading: Radio(
                value: MathOperation.product,
                groupValue: _sumProduct,
                onChanged: (MathOperation value) {
                  setState(() {
                    _sumProduct = value;
                    BlocProvider.of<BlocSM>(context, listen: false)
                        .add(Events.prodSelected);
                  });
                },
              ),
            ),
            TextField(
              decoration: InputDecoration(hintText: 'enter integers'),
              controller: myController,
            ),
            RaisedButton(
              child: Text("Add"),
              onPressed: () {
                Provider.of<DataStore>(context, listen: false)
                    .addData(int.parse(myController.text));
                myController.clear();
              },
            )
          ],
        ),
      ),
    );
  }
}
 

This compiles, but when i switch state i get an exception:

 ════════ Exception caught by gesture ═══════════════════════════════════════════
The following assertion was thrown while handling a gesture:
Tried to use Provider with a subtype of Listenable/Stream (BlocSM).


This is likely a mistake, as Provider will not automatically update dependents
when BlocSM is updated. Instead, consider changing Provider for more specific
implementation that handles the update mechanism, such as:

- ListenableProvider
- ChangeNotifierProvider
- ValueListenableProvider
- StreamProvider

Alternatively, if you are making your own provider, consider using InheritedProvider.

If you think that this is not an error, you can disable this check by setting
Provider.debugCheckInvalidValueType to `null` in your main file:

So my questions are these:
 
  1. Является ли это правильным подходом к поставщику блоков и поставщику изменений для взаимодействия друг с другом? Через прокси-провайдера?
  2. Почему я получаю это исключение? Связано ли это с тем, что среда выполнения обеспокоена тем, что, когда я вношу изменения в BloCSM, эти изменения не будут обновлять зависимых лиц при изменении BloCSM? Должен ли я изменить BlocSM на экземпляр с помощью ChangeNotifierProxyProvider?
  3. Есть ли более простой способ сделать это?.. я уверен, что я не первый человек, который совмещает провайдера с Блоком..