Разбираем BLoC во Flutter для начинающих

Разбираем BLoC во Flutter для начинающих

Пытаемся понять 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 можно почитать в других статьях на сайте.

При копировании материалов ссылка на https://terraideas.ru/ обязательна

Комментарии к статье: Разбираем BLoC во Flutter для начинающих

DartMitai 3 месяца назад

Комментариев в коде не хватает, где что происходит