Flutter状态持久化插件hydrated_bloc的使用
Flutter状态持久化插件hydrated_bloc的使用
介绍
hydrated_bloc
是 package:bloc
的一个扩展,它能够自动地持久化和恢复 Bloc 和 Cubit 的状态。它基于 hive 实现了一个跨平台、高性能的存储层。下面将详细介绍如何使用这个插件。
安装与配置
首先,在 pubspec.yaml
文件中添加依赖:
dependencies:
flutter:
sdk: flutter
hydrated_bloc: ^8.0.0
path_provider: ^2.0.11
然后在 main.dart
中设置 HydratedStorage
:
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: kIsWeb
? HydratedStorageDirectory.web
: HydratedStorageDirectory((await getTemporaryDirectory()).path),
);
runApp(const App());
}
创建持久化的Cubit或Bloc
创建持久化的Cubit
class CounterCubit extends HydratedCubit<int> {
CounterCubit() : super(0);
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
@override
int fromJson(Map<String, dynamic> json) => json['value'] as int;
@override
Map<String, int> toJson(int state) => {'value': state};
}
创建持久化的Bloc
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}
final class CounterDecrementPressed extends CounterEvent {}
class CounterBloc extends HydratedBloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) => emit(state + 1));
on<CounterDecrementPressed>((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创建Cubit
class CounterCubit extends Cubit<int> with HydratedMixin {
CounterCubit() : super(0) {
hydrate(); // 必须调用hydrate方法
}
void increment() => emit(state + 1);
void decrement() => emit(state - 1);
@override
int fromJson(Map<String, dynamic> json) => json['value'] as int;
@override
Map<String, int> toJson(int state) => {'value': state};
}
自定义存储目录
你可以通过自定义 storageDirectory
来指定不同的存储位置:
final storage = await HydratedStorage.build(
storageDirectory: await getApplicationDocumentsDirectory(),
);
测试
为了测试使用了 hydrated_bloc
的代码,建议使用 mocktail
包来模拟 Storage
接口的行为:
import 'package:flutter_test/flutter_test.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:mocktail/mocktail.dart';
class MockStorage extends Mock implements Storage {}
void main() {
late Storage storage;
setUp(() {
storage = MockStorage();
when(() => storage.write(any(), any<dynamic>())).thenAnswer((_) async {});
HydratedBloc.storage = storage;
});
testWidgets('...', (tester) async {
when<dynamic>(() => storage.read('$MyBloc')).thenReturn(MyState().toJson());
// ...
});
}
完整示例
以下是一个完整的例子,展示了如何在一个计数器应用中使用 hydrated_bloc
:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:path_provider/path_provider.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
HydratedBloc.storage = await HydratedStorage.build(
storageDirectory: kIsWeb
? HydratedStorageDirectory.web
: HydratedStorageDirectory((await getTemporaryDirectory()).path),
);
runApp(const App());
}
class App extends StatelessWidget {
const App({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider(
create: (_) => BrightnessCubit(),
child: const AppView(),
);
}
}
class AppView extends StatelessWidget {
const AppView({super.key});
@override
Widget build(BuildContext context) {
return BlocBuilder<BrightnessCubit, Brightness>(
builder: (context, brightness) {
return MaterialApp(
theme: ThemeData(brightness: brightness),
home: const CounterPage(),
);
},
);
}
}
class CounterPage extends StatelessWidget {
const CounterPage({super.key});
@override
Widget build(BuildContext context) {
return BlocProvider<CounterBloc>(
create: (_) => CounterBloc(),
child: const CounterView(),
);
}
}
class CounterView extends StatelessWidget {
const CounterView({super.key});
@override
Widget build(BuildContext context) {
final textTheme = Theme.of(context).textTheme;
return Scaffold(
appBar: AppBar(title: const Text('Counter')),
body: Center(
child: BlocBuilder<CounterBloc, int>(
builder: (context, state) {
return Text('$state', style: textTheme.displayMedium);
},
),
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
FloatingActionButton(
child: const Icon(Icons.brightness_6),
onPressed: () => context.read<BrightnessCubit>().toggleBrightness(),
),
const SizedBox(height: 4),
FloatingActionButton(
child: const Icon(Icons.add),
onPressed: () {
context.read<CounterBloc>().add(CounterIncrementPressed());
},
),
const SizedBox(height: 4),
FloatingActionButton(
child: const Icon(Icons.remove),
onPressed: () {
context.read<CounterBloc>().add(CounterDecrementPressed());
},
),
const SizedBox(height: 4),
FloatingActionButton(
child: const Icon(Icons.delete_forever),
onPressed: () => HydratedBloc.storage.clear(),
),
],
),
);
}
}
sealed class CounterEvent {}
final class CounterIncrementPressed extends CounterEvent {}
final class CounterDecrementPressed extends CounterEvent {}
class CounterBloc extends HydratedBloc<CounterEvent, int> {
CounterBloc() : super(0) {
on<CounterIncrementPressed>((event, emit) => emit(state + 1));
on<CounterDecrementPressed>((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};
}
class BrightnessCubit extends HydratedCubit<Brightness> {
BrightnessCubit() : super(Brightness.light);
void toggleBrightness() {
emit(state == Brightness.light ? Brightness.dark : Brightness.light);
}
@override
Brightness fromJson(Map<String, dynamic> json) {
return Brightness.values[json['brightness'] as int];
}
@override
Map<String, dynamic> toJson(Brightness state) {
return <String, int>{'brightness': state.index};
}
}
这个例子展示了如何使用 hydrated_bloc
来创建一个简单的计数器应用,并且可以持久化计数器的状态。即使应用程序关闭后重新启动,计数器的状态仍然会被保留。
更多关于Flutter状态持久化插件hydrated_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter状态持久化插件hydrated_bloc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,下面是一个关于如何在Flutter中使用hydrated_bloc
进行状态持久化的代码案例。hydrated_bloc
是一个与bloc
状态管理库集成的插件,用于简化状态的持久化操作。
步骤 1: 添加依赖
首先,你需要在pubspec.yaml
文件中添加bloc
和hydrated_bloc
的依赖:
dependencies:
flutter:
sdk: flutter
bloc: ^8.0.0 # 确保使用最新版本
hydrated_bloc: ^8.0.0 # 确保使用最新版本
# 其他依赖...
步骤 2: 配置HydratedBloc
在你的应用程序入口文件(通常是main.dart
)中,配置HydratedBloc
:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:hydrated_bloc/hydrated_bloc.dart';
import 'package:your_app/bloc/your_bloc.dart'; // 导入你的Bloc
import 'package:your_app/repository/your_repository.dart'; // 导入你的Repository
void main() async {
// 初始化HydratedBloc的存储
final hydratedStorage = await HydratedStorage.build(
storageDirectory: await getApplicationDocumentsDirectory(),
);
runApp(
HydratedBlocProvider(
storage: hydratedStorage,
child: MultiBlocProvider(
providers: [
// 提供你的Bloc
BlocProvider<YourBloc>(
create: (context) => YourBloc(repository: YourRepository()),
),
// 其他Bloc...
],
child: MyApp(),
),
),
);
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: YourHomePage(),
);
}
}
步骤 3: 创建Bloc和State
创建一个简单的Bloc和State类。假设我们有一个计数器Bloc:
import 'package:bloc/bloc.dart';
import 'package:meta/meta.dart';
part 'counter_event.dart';
part 'counter_state.dart';
class CounterBloc extends Bloc<CounterEvent, CounterState> {
CounterBloc() : super(CounterInitial()) {
on<CounterIncremented>(_ => add(CounterIncremented()));
}
@override
Stream<CounterState> mapEventToState(CounterEvent event) async* {
if (event is CounterIncremented) {
yield* _mapToIncrementedState(currentState);
}
}
Stream<CounterState> _mapToIncrementedState(CounterState currentState) async* {
yield currentState.copy(count: currentState.count + 1);
}
}
步骤 4: 定义Event和State
定义你的Event和State类:
// counter_event.dart
part of 'counter_bloc.dart';
abstract class CounterEvent {}
class CounterIncremented extends CounterEvent {}
// counter_state.dart
part of 'counter_bloc.dart';
class CounterState {
final int count;
CounterState({required this.count});
CounterState copyWith({int? count}) {
return CounterState(
count: count ?? this.count,
);
}
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is CounterState &&
runtimeType == other.runtimeType &&
count == other.count;
@override
int get hashCode => count.hashCode;
}
class CounterInitial extends CounterState {
CounterInitial() : super(count: 0);
}
步骤 5: 使用Bloc在UI中
在你的UI组件中使用BlocBuilder或BlocConsumer来监听Bloc的状态变化:
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:your_app/bloc/counter_bloc.dart';
class YourHomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('Hydrated Bloc Example'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pushed the button this many times:'),
BlocBuilder<CounterBloc, CounterState>(
builder: (context, state) {
return Text(
'${state.count}',
style: Theme.of(context).textTheme.headline4,
);
},
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: () => context.read<CounterBloc>().add(CounterIncremented()),
tooltip: 'Increment',
child: Icon(Icons.add),
),
);
}
}
这样,你就完成了一个简单的Flutter应用,其中使用了hydrated_bloc
进行状态持久化。每次应用程序重新启动时,之前的状态都会被恢复。