DevGang
Авторизоваться

Hydrated Bloc: сохраняйте свое состояние приложения

«Hydrated Bloc» упрощает сохранение состояния во Flutter, упрощая сохранение и восстановление состояния вашего приложения. Попрощайтесь с сериализацией и десериализацией состояния вручную — это изящное расширение позаботится обо всем! С Hydrated Bloc вы можете сосредоточиться на разработке функций своего приложения и радовать своих пользователей, не беспокоясь о сложностях управления состоянием.

Что такое Hydrated Bloc и как он работает?

Hydrated Bloc — это мощное расширение, созданное поверх пакета блоков во Flutter. Он автоматизирует процесс сохранения и восстановления состояния, упрощая поддержание состояния вашего приложения в разных сеансах. Когда вы создаете BlocProvider, Hydrated Bloc автоматически использует метод fromJson для извлечения сохраненного состояния из локального хранилища.

Всякий раз, когда состояние блока изменяется, в игру вступает метод toJson. Он преобразует текущее состояние в формат Map<String, dynamic>, который затем сохраняется в локальном хранилище. Это гарантирует, что состояние вашего приложения всегда актуально и готово к восстановлению в случае необходимости, даже если приложение закрыто или пользователь перемещается между экранами.

Hydrated Bloc оптимизирует процесс сохранения состояния, избавляя от хлопот, связанных с ручной сериализацией и десериализацией. Автоматизируя управление состоянием, оно повышает надежность вашего приложения и устраняет риск потери состояния.

Преимущества Hydrated Bloc по сравнению с Traditional Bloc

Hydrated Bloc Bloc
Постоянство состояния Автоматизирует сохранение состояния и восстановление. Ручная сериализация и десериализация состояния.
Зависимость пакета Построен как расширение package:bloc. Напрямую использует package:bloc.
Сложность Упрощает управление состоянием с автоматическим сохранением. Требуется дополнительная обработка для сохранения состояния.
Инициализация Инициализируется из сохраненного состояния, если оно доступно. Запускается с начальным состоянием при каждом запуске.
Хранилище Использует локальное хранилище (улей [без SQL]) для сохранения состояний. Нет встроенного хранилища состояний, оставлено разработчикам.
Надежность приложения Повышает надежность приложения, предотвращая потерю состояния. Склонен к потере состояния при неправильном обращении.

Начало работы с Hydrated Bloc

Использование Hydrated Bloc очень просто! У вас есть два варианта для начала:

  • Путем расширения: вы можете использовать Hydrated Bloc, расширив существующий класс блоков.
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}

class CounterBloc extends HydratedBloc<CounterEvent, int> {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  }

  @override
  int fromJson(Map<String, dynamic> json) => json['value'] as int;

  @override
  Map<String, int> toJson(int state) => { 'value': state };
}
  • Использование HydratedMixin:
class CounterBloc extends Bloc<CounterEvent, int> with HydratedMixin {
  CounterBloc() : super(0) {
    on<CounterIncrementPressed>((event, emit) => emit(state + 1));
  }

  @override
  int fromJson(Map<String, dynamic> json) => json['value'] as int;

  @override
  Map<String, int> toJson(int state) => { 'value': state };
}

Чтобы получить доступ к объекту хранилища в Hydrated Bloc и управлять им, выполните следующие действия:

  • Создайте собственный класс хранения, расширив класс Storage.
class MyHydratedStorage implements Storage {
  @override
  dynamic read(String key) {
    // TODO: implement read
  }

  @override
  Future<void> write(String key, dynamic value) async {
    // TODO: implement write
  }

  @override
  Future<void> delete(String key) async {
    // TODO: implement delete
  }

  @override
  Future<void> clear() async {
    // TODO: implement clear
  }
}
  • Установите пользовательскую реализацию хранилища:
HydratedBloc.storage = MyHydratedStorage();

Внутри пользовательского класса хранилища вы можете реализовать методы для чтения, записи, удаления и очистки данных. Эти методы позволяют вам взаимодействовать с локальным хранилищем и управлять данными для Hydrated Bloc.

Давайте создадим простое приложение ToDo

  • Создайте проект Flutter и добавьте необходимые зависимости в файл pubspec.yaml.
dependencies:
  hydrated_bloc: ^9.1.2  //hydrated bloc
  path_provider: ^2.0.15  // for getTemporaryDirectory
  flutter_easyloading: ^3.0.5 // Helpful for showing loading without context
  flutter_bloc: ^8.1.3 //flutter bloc

для получения дополнительной информации hydrated bloc

  • Определите модель ToDo для сериализации JSON.
class TodoModel {
  final int id;
  final String title;
  final bool isCompleted;
  final String description;
  final String createdAt;
  final String updatedAt;

  TodoModel(
    this.id,
    this.title,
    this.isCompleted,
    this.description,
    this.createdAt,
    this.updatedAt,
  );

  // JSON serialization: Map<String, dynamic> -> TodoModel
  factory TodoModel.fromJson(Map<String, dynamic> json) {
    return TodoModel(
      json['id'],
      json['title'],
      json['is_completed'],
      json['description'],
      json['created_at'],
      json['updated_at'],
    );
  }

  // JSON deserialization: TodoModel -> Map<String, dynamic>
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'is_completed': isCompleted,
      'description': description,
      'created_at': createdAt,
      'updated_at': updatedAt,
    };
  }
  //Copy with function

  TodoModel copyWith({
    int? id,
    String? title,
    bool? isCompleted,
    String? description,
    String? createdAt,
    String? updatedAt,
  }) {
    return TodoModel(
      id ?? this.id,
      title ?? this.title,
      isCompleted ?? this.isCompleted,
      description ?? this.description,
      createdAt ?? this.createdAt,
      updatedAt ?? this.updatedAt,
    );
  }
}
  • Создайте репозиторий ToDo для управления задачами.
// ignore_for_file: depend_on_referenced_packages

import 'package:collection/collection.dart';
import 'package:hydated_bloc_example/Data/Model/todo_model.dart';

class TodoRepo {
  List<TodoModel> tasks = [];

  List<TodoModel> addTodo(TodoModel model) {
    tasks.add(model);
    return tasks;
  }

  List<TodoModel> removeTodo(int id) {
    tasks.removeWhere((element) => element.id == id);
    return tasks;
  }

  List<TodoModel> updateTask(int id, bool status) {
    TodoModel? taskModel =
        tasks.firstWhereOrNull((element) => element.id == id);
    if (taskModel != null) {
      tasks = tasks.map((task) {
        if (task.id == id) {
          return task.copyWith(isCompleted: status);
        }
        return task;
      }).toList();
    }
    return tasks;
  }
}
  • Определите возможные состояния и события для блока Todo.
//`todo_state.dart`:
sealed class TodoState {}

final class TodoInitial extends TodoState {}

final class TodoStateLoaded extends TodoState {
  final List<TodoModel> task;

  TodoStateLoaded(this.task);
}
import 'package:hydated_bloc_example/Data/Model/todo_model.dart';

sealed class TodoEvent {}

final class TodoFetch extends TodoEvent {}

final class TodoAdd extends TodoEvent {
  final TodoModel task;
  TodoAdd(this.task);
}

final class TodoRemove extends TodoEvent {
  final int id;

  TodoRemove(this.id);
}

final class TodoUpdate extends TodoEvent {
  final int id;
  final bool status;
  TodoUpdate(this.id, this.status);
}
  • Реализуйте блок ToDo с помощью Hydrated Bloc.
import 'package:hydated_bloc_example/Bloc/todo_event.dart';
import 'package:hydated_bloc_example/Bloc/todo_state.dart';
import 'package:hydated_bloc_example/Data/Model/todo_model.dart';
import 'package:hydated_bloc_example/Data/Repo/todo_repo.dart';
import 'package:hydated_bloc_example/UI/Widgets/fake_loading.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';

class TodoBloc extends HydratedBloc<TodoEvent, TodoState> {
  final TodoRepo todoRepo = TodoRepo();
  TodoBloc() : super(TodoInitial()) {
    on<TodoFetch>((event, emit) async {
      emit(TodoStateLoaded(todoRepo.tasks));
    });
    on<TodoAdd>((event, emit) async {
      await fakeLoading();
      emit(TodoStateLoaded(todoRepo.addTodo(event.task)));
    });
    on<TodoRemove>((event, emit) async {
      await fakeLoading();
      emit(TodoStateLoaded(todoRepo.removeTodo(event.id)));
    });
    on<TodoUpdate>((event, emit) async {
      await fakeLoading();
      emit(TodoStateLoaded(todoRepo.updateTask(event.id, event.status)));
    });
  }

  @override
  TodoState? fromJson(Map<String, dynamic> json) {
    if (json['data'] != null && (json['data'] as List<dynamic>).isNotEmpty) {
      return TodoStateLoaded((json['data'] as List<dynamic>)
          .map((e) => TodoModel.fromJson(e))
          .toList());
    }
    return TodoInitial();
  }

  @override
  Map<String, dynamic>? toJson(TodoState state) {
    if (state is TodoStateLoaded) {
      return {'data': state.task.map((e) => e.toJson()).toList()};
    }
    return {'data': []};
  }
}
  • Инициализируйте хранилище Hydrated Bloc в main.dart.
Future<void> main() async {
  WidgetsFlutterBinding.ensureInitialized();
  HydratedBloc.storage = await HydratedStorage.build(
    storageDirectory: kIsWeb
        ? HydratedStorage.webStorageDirectory
        : await getTemporaryDirectory(),
  );
  runApp(const MyApp());
}
  • Сейчас мы остаемся с пользовательским интерфейсом и виджетами, которые вы можете создать сами или обратиться к репозиторию github.

Различные функции для управления списком задач

  • Добавление нового ToDo:

 if (formKey.currentState!.validate()) {
BlocProvider.of<TodoBloc>(context).add(TodoAdd(TodoModel.fromJson({
                        'id': DateTime.now().millisecondsSinceEpoch+
                            Random().nextInt(9999999),
                        'title': titleText.text,
                        'description': description.text,
                        'is_completed': false,
                        'created_at': DateTime.now().toUtc().toIso8601String(),
                        'updated_at': DateTime.now().toUtc().toIso8601String()
                      })));
     Navigator.pop(context);
                    }
  • Обновить ToDo:
BlocProvider.of<TodoBloc>(context).add(TodoUpdate(task.id, value!));
  • Удалить ToDo
BlocProvider.of<TodoBloc>(context).add(TodoRemove(task.id));

Вывод

Исходный код:

Репозиторий гитхаб

#Flutter #Android
Комментарии
Чтобы оставить комментарий, необходимо авторизоваться

Присоединяйся в тусовку

В этом месте могла бы быть ваша реклама

Разместить рекламу