
Пытаемся понять BLoC во Flutter на примере стрима.
Многие пишут, что BLoC во Flutter сложный. Решил набросать простой пример, который надеюсь упростит понимание.
Можно представить BLoC как стрим.
Пример https://dartpad.dev/?id=3453cea2b54d21d0d83bcee8e83b0abf
import 'dart:async'; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return const MaterialApp(home: PageView()); } } class PageView extends StatefulWidget { const PageView({Key? key}) : super(key: key); @override State<PageView> createState() => _PageViewState(); } class _PageViewState extends State<PageView> { final CounterBloc _bloc = CounterBloc(); late final StreamSubscription _sub; int _counter = 0; @override void initState() { super.initState(); _sub = _bloc.stream.listen((state) { setState(() { _counter = state; }); }); } @override void dispose() { _sub.cancel(); _bloc.close(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( floatingActionButton: FloatingActionButton( onPressed: () { _bloc.add(1); }, child: const Icon(Icons.add), ), body: Center( child: Text('$_counter'), ), ); } } class CounterBloc { final StreamController<int> _controller = StreamController<int>(); int _state = 0; Stream<int> get stream => _controller.stream; void add(int event) { _state += event; _controller.sink.add(_state); } close() { _controller.close(); } }
Это очень упрощенный пример, без создания типов для состояний и событий.
Перепишем его же с классами для событий и состояний.
import 'dart:async'; import 'package:flutter/material.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp(home: PageView()); } } class PageView extends StatefulWidget { const PageView({Key? key}) : super(key: key); @override State<PageView> createState() => _PageViewState(); } class _PageViewState extends State<PageView> { final CounterBloc _bloc = CounterBloc(); @override void dispose() { _bloc.close(); super.dispose(); } @override Widget build(BuildContext context) { return Scaffold( body: StreamBuilder<Object>( stream: _bloc.stream, builder: (context, snapshot) { return Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(snapshot.data is CounterState ? '${(snapshot.data as CounterState).count}' : '0'), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ OutlinedButton( onPressed: () { _bloc.add(CounterEventDec(1)); }, child: Text('-'), ), OutlinedButton( onPressed: () { _bloc.add(CounterEventInc(1)); }, child: Text('+'), ), ], ), ], ); }), ); } } abstract class CounterEvent {} class CounterEventInc extends CounterEvent { CounterEventInc(this.value); final int value; } class CounterEventDec extends CounterEvent { CounterEventDec(this.value); final int value; } class CounterState { CounterState(this.count); final int count; } class CounterBloc { final StreamController<CounterState> _controller = StreamController<CounterState>(); CounterState _state = CounterState(0); Stream<CounterState> get stream => _controller.stream; void add(CounterEvent event) { if (event is CounterEventInc) { _state = CounterState(_state.count + event.value); _controller.sink.add(_state); } else if (event is CounterEventDec) { _state = CounterState(_state.count - event.value); _controller.sink.add(_state); } } close() { _controller.close(); } }
https://dartpad.dev/?id=17bd98070cfd027c3b86668b095f1bc2
По сути то же самое, только теперь у нас state и even наш собственный класс.
При этом событий сделали два. Абстрактный класс CounterEvent нужен для передачи различных событий в метод add нашего блока.
Внутри метода проверяем какое событие пришло и на основании этого выполняем какое либо действие.
Так же вместо setState я использовал StreamBuilder для аналогии с BlocBuilder.
Это очень упрощенная схема BLoC на Flutter. Но главное понять суть, что ничего страшного в блоке нет :)
Более подробно о BLoC и Cubit можно почитать в других статьях на сайте.
Комментарии к статье: Разбираем BLoC во Flutter для начинающих
Комментариев в коде не хватает, где что происходит