#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:
- Является ли это правильным подходом к поставщику блоков и поставщику изменений для взаимодействия друг с другом? Через прокси-провайдера?
- Почему я получаю это исключение? Связано ли это с тем, что среда выполнения обеспокоена тем, что, когда я вношу изменения в BloCSM, эти изменения не будут обновлять зависимых лиц при изменении BloCSM? Должен ли я изменить BlocSM на экземпляр с помощью ChangeNotifierProxyProvider?
- Есть ли более простой способ сделать это?.. я уверен, что я не первый человек, который совмещает провайдера с Блоком..