Cubit во Flutter на примере Connectivity.

Cubit во Flutter на примере Connectivity.

Использование Cubit во флаттер на примере отслеживания состояния соединения с интернет.

Приложения со временем растут и постоянно меняются. Для удобства поддержки и масштабирования приложения есть различные подходы. Рассмотрим архитектуру BLoC на примере популярной библиотеки bloc.

Данная библиотека дает нам два основных инструмента: Cubit и BLoC.

Flutter Cubit.


Cubit стримит изменение состояний (State) в UI. Для передачи нового состояния нужно вызвать emit у Cubit. Обычно изменение состояния вызывается со слоя данных, через другой Stream. Но вы можете объявить и функции, вызов которых будет влиять на состояния приложения, например запрашивать какие либо данные. Хотя для этих целей рекомендуется использовать Bloc.

Cubit и Bloc являются слоем бизнес-логики.

Рассмотрим пример отслеживания состояния соединения с интернет:

class ConnectionState {
  final bool connected;
  ConnectionState(this.connected): assert(connected != null);
}

class ConnectionCubit extends Cubit<ConnectionState> {
  StreamSubscription<ConnectivityResult> _subscription;
  ConnectionCubit() : super(ConnectionState(false)) {
    _subscription = Connectivity().onConnectivityChanged
        .listen((ConnectivityResult result) {
      emit(ConnectionState(result != ConnectivityResult.none)));
    });
  }

  @override
  Future<void> close() {
    _subscription.cancel();
    return super.close();
  }
}

Нас интересует только наличие подключения или его отсутствие, и не важно Wi-Fi это или нет.

В конструкторе Cubit подпишемся на изменение состояния. Интернет есть при состоянии отличном от ConnectivityResult.none. Когда Cubit будет закрыт, нужно отменить подписку.

Чтобы посмотреть в работе, воспользуемся еще одной библиотекой flutter_bloc. Эта библиотека предоставляет виджеты для работы с Cubit/Bloc в UI.

Виджеты, принимающие параметр Cubit принимают так же и Bloc, так как Bloc наследует Cubit.

Виджет BlocBuilder вызывает функцию builder(BuildContext, State) каждый раз, когда State (состояние) изменяется. Вы можете указать когда именно нужно вызывать данную функцию для перерисовки интерфейса с помощью buildWhen: (previousState, state) или переопределив метод сравнения у класса State.

Созданный нами Cubit можно использовать например так:

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        title: 'Flutter App',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: HomePage()
    );
  }
}

class HomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  ConnectionCubit _cubit;

  @override
  void initState() {
    super.initState();
    _cubit = ConnectionCubit();
  }

  @override
  void dispose() {
    _cubit?.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: Text("Приложение"),
    ),
    body: Center(
      child: BlocBuilder<ConnectionCubit, ConnectionState>(
        cubit: _cubit,
        builder: (context, state) => state.connected
          ? Text('Соединение есть')
          : Text("Нет соединения с интернет!",
              style: TextStyle(color: Colors.red),)
      ),
    ),
  );
}

Отключив передачу данных и Wi-Fi мы увидем красную надпись, о том что нет интернета.

Вы могли заметить, что на мгновение, при отключении Wi-Fi, появилась надпись о отсутствии интернета, это связанно с тем, что на телефоне может отключаться мобильная передача данных пока подключена сеть WI-Fi. В. результате на промежуток времени, требуемый для подключения мобильных данных возвращается стейт ConnectivityResult.none.

Можно доработать Cubit, добавив небольшую задержку на изменения стейта на отключения интернета.

class ConnectionCubit extends Cubit<ConnectionState> {
  StreamSubscription<ConnectivityResult> _subscription;
  Timer _timer;
  ConnectionCubit() : super(ConnectionState(false)) {
    _subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
      _subscription = Connectivity().onConnectivityChanged.listen((ConnectivityResult result) {
      if (_timer != null && _timer.isActive) {
        _timer.cancel();
      }
      if (result != ConnectivityResult.none) {
        emit(ConnectionState(true));
      } else {
        _timer = Timer(Duration(seconds: 1), () =>
            emit(ConnectionState(false)));
      }
    });
  }

  @override
  Future<void> close() {
    _subscription.cancel();
    return super.close();
  }
}

При изменении состояния на none, запустим таймер на 1 секунду. Если за секунду состояние изменится, то таймер будет остановлен. Если же состояние не изменится, то будет вызван emit.

При появлении интернета вызываем emit незамедлительно.

В библиотеку flutter_bloc есть еще один полезный виджет, это BlocProvider. Данный виджет имеет метод create, который будет вызван при первом обращении к провайдеру в поисках данного Bloc/Cubit. Это поведение унаследовано от библиотеки Provider и его можно изменить при необходимости указав lazy: false. Тогда Bloc/Cubit будет создан сразу.

С помощью BlocProvider можно удобно использовать наш Cubit в StatelessWidget. Также можно разместить провайдер выше навигатора и получить доступ во всех экранах.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return BlocProvider<ConnectionCubit>(
      create: (context) => ConnectionCubit(),
      child: MaterialApp(
          title: 'Flutter App',
          theme: ThemeData(
            primarySwatch: Colors.blue,
            visualDensity: VisualDensity.adaptivePlatformDensity,
          ),
          home: HomePage()
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  @override
  Widget build(BuildContext context) => Scaffold(
    appBar: AppBar(
      title: Text("Приложение"),
    ),
    body: Center(
      child: BlocBuilder<ConnectionCubit, ConnectionState>(
        builder: (context, state) => state.connected
          ? Text('Соединение есть')
          : Text("Нет соединения с интернет!",
              style: TextStyle(color: Colors.red),)
      ),
    ),
  );
}


Читайте о  Bloc в статье https://terraideas.ru/article/bloc-vo-flutter-otlichie-ot-cubit-i-primer-ispolzovaniya-36.

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

Комментарии к статье: Cubit во Flutter на примере Connectivity.

Нет ни одного комментария. Будьте первым!