Найти в Дзене

Flutter управление состоянием (ReactiveWidget)

Современные мобильные приложения редко ограничиваются статичными экранами. Почти всегда приходится работать с динамическими данными: пользователь вводит текст, нажимает кнопки, получает данные из сети. Всё это требует чёткого и удобного способа управлять состоянием приложения. Без правильного подхода код быстро превращается в хаос, теряется читаемость и усложняется поддержка. В этой статье мы рассмотрим одну из множества библиотек управления состоянием во Flutter. На данный момент эта библиотека не размещена на pub.dev, поэтому добавляется он через ссылку на Github следующим образом:
Также есть вариант скачать архив, распаковать и добавить в свой проект как пакет следующим образом, но при таком добавлении при обновлении пакета на Github вы не получите обновлений После добавления пакета в pubspec.yaml, необходимо выполнить команду flutter pub get На этом установка завершена, можем приступать к использованию. При создание этой библиотеки основной задачей ставилось упрощение разделени
Оглавление

Управление состоянием (State Management) в Flutter: зачем оно нужно и как выбрать подходящий инструмент

Современные мобильные приложения редко ограничиваются статичными экранами. Почти всегда приходится работать с динамическими данными: пользователь вводит текст, нажимает кнопки, получает данные из сети. Всё это требует чёткого и удобного способа управлять состоянием приложения. Без правильного подхода код быстро превращается в хаос, теряется читаемость и усложняется поддержка. В этой статье мы рассмотрим одну из множества библиотек управления состоянием во Flutter.

Установка

На данный момент эта библиотека не размещена на pub.dev, поэтому добавляется он через ссылку на Github следующим образом:

sbeu_reactive_pattern: 
    git: https://github.com/SilentSt/reactive_widget
sbeu_reactive_pattern: git: https://github.com/SilentSt/reactive_widget

Также есть вариант скачать архив, распаковать и добавить в свой проект как пакет следующим образом, но при таком добавлении при обновлении пакета на Github вы не получите обновлений

-2

sbeu_reactive_pattern: 
    path: packages/reactive_widget
sbeu_reactive_pattern: path: packages/reactive_widget

После добавления пакета в pubspec.yaml, необходимо выполнить команду

flutter pub get

На этом установка завершена, можем приступать к использованию.

Основная концепция

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

Основные объекты

  • ReactiveWidget - используйте как View, не сильно отличается от обычного виджета, но позволяет избавиться от необходимости написания своей собственной системы связи с ViewModel
  • ReactiveWidgetModel - используйте как ViewModel, используется для написания логики и хранении данных для вашего ReactiveWidget
  • ReactiveContainer - используйте для хранения объектов на основе данных которых будет отрисовываться пользовательский интерфейс. Содерижт 2 способа обновления данных: update (синхронно обновляет данные) и updateAsync (асинхронно обновляет данные, принимает Future, и сам дожидается ее завершения)
  • ReactiveBuiderWidget - используется для отрисовки пользовательского интерфейса, который будет автоматически изменяться при изменении значения у ReactiveContainer

Пример использования

Для того чтобы рассмотреть функционал данной библиотеки напишем небольшое приложение Counter

Для начала создадим в папке lib папку для нашего экрана, назовем ее counter, в ней создадим 2 файла: counter_widget.dart, который будет отвечать за пользовательский интерфейс, и counter_wm.dart, который будет отвечать за логику для этого экрана.

-4

В файле counter_wm.dart создадим класс CounterWm, унаследуем его от ReactiveWidgetModel.

В этом классе создадим объект типа ReactiveContainer<int> counter и инициализируем его со значением по умолчанию 0.

Также переопределим метод dispose, в котором будет вызывать метод dispose у нашего контейнера со значением.

Создадим метод increment, который будет увеличивать значение в контейнере.

import 'package:sbeu_reactive_pattern/reactive/reactive_widget_model.dart';

class CounterWm extends ReactiveWidgetModel {
  final ReactiveContainer<int> counter = ReactiveContainer(initialValue: 0);

  void increment() {
    counter.update(counter.value ?? 0 + 1);
  }

  @override
  void dispose() {
    counter.dispose();
    super.dispose();
  }
}
import 'package:sbeu_reactive_pattern/reactive/reactive_widget_model.dart'; class CounterWm extends ReactiveWidgetModel { final ReactiveContainer<int> counter = ReactiveContainer(initialValue: 0); void increment() { counter.update(counter.value ?? 0 + 1); } @override void dispose() { counter.dispose(); super.dispose(); } }

В файле counter_widget.dart создадим класс CounterWidget и унаследуем его от ReactiveWidget<CounterWm>. Создадим 2 метода который необходимо переопределить.

Первый - это метод buidView, он отвечает за отрисовку интерфейса пользователя, а также в его параметрах содержится экземпляр класса CounterWm, что позволит взаимодействовать с ним.

Второй - это метод widgetModelBuilder, он отвечает за создание экземпляра класса ConterWm. Это позволяет нам при необходимости передать параметры в конструктор это класса.

import 'package:counter/counter/counter_wm.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:sbeu_reactive_pattern/sbeu_reactive_pattern.dart';

class CounterWidget extends ReactiveWidget<CounterWm> {
  const CounterWidget({super.key});

  @override
  Widget buildView(BuildContext context, CounterWm wm) {
    return Scaffold(
      body: Center(
        child: ReactiveBuilderWidget<int>(
          container: wm.counter,
          builder: (context, counterConatiner) {
            return Text(
              '${counterConatiner.value ?? 0}',
              style: TextStyle(color: Colors.black, fontSize: 16),
            );
          },
        ),
      ),
      floatingActionButton: CupertinoButton(
        onPressed: wm.increment,
        padding: EdgeInsets.all(16),
        color: Colors.blue,
        child: Text('+'),
      ),
    );
  }

  @override
  CounterWm widgetModelBuilder(BuildContext context) {
    return CounterWm();
  }
}
import 'package:counter/counter/counter_wm.dart'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:sbeu_reactive_pattern/sbeu_reactive_pattern.dart'; class CounterWidget extends ReactiveWidget<CounterWm> { const CounterWidget({super.key}); @override Widget buildView(BuildContext context, CounterWm wm) { return Scaffold( body: Center( child: ReactiveBuilderWidget<int>( container: wm.counter, builder: (context, counterConatiner) { return Text( '${counterConatiner.value ?? 0}', style: TextStyle(color: Colors.black, fontSize: 16), ); }, ), ), floatingActionButton: CupertinoButton( onPressed: wm.increment, padding: EdgeInsets.all(16), color: Colors.blue, child: Text('+'), ), ); } @override CounterWm widgetModelBuilder(BuildContext context) { return CounterWm(); } }

Запустим наше приложение и увидим следующий экран

-7

При нажатии кнопки "+" значение будет увеличиваться

-8

Заключение

Ознакомиться с исходным кодом данной библиотеки можно по следующей ссылке. Спасибо за внимание!