Introduction
State management is a crucial aspect of Flutter development, especially when dealing with applications that need to maintain state across app restarts. Hydrated Bloc, an extension of the Bloc library, enables effortless state persistence by automatically storing and restoring state using local storage.
In this article, we will cover:
- What Hydrated Bloc is and why it’s useful.
- How to integrate Hydrated Bloc into a Flutter project.
- A step-by-step implementation guide with examples.
What is Hydrated Bloc?
Hydrated Bloc is a state management solution built on top of Bloc, which persists state across app restarts using local storage such as SharedPreferences or Hive.
Why Use Hydrated Bloc?
- Automatic State Persistence: Restores state automatically after app restarts.
- Minimal Boilerplate: No need to manually save and retrieve state.
- Efficient Storage: Uses binary storage for optimal performance.
- Works with Bloc: Seamlessly integrates with the Bloc pattern.
Installing Hydrated Bloc in a Flutter Project
Add the following dependencies to your pubspec.yaml
:
dependencies: flutter_bloc: ^8.0.0 hydrated_bloc: ^9.1.0 path_provider: ^2.0.11 equatable: ^2.0.3
Run:
flutter pub get
Setting Up Hydrated Bloc
1. Initializing Hydrated Storage
Before using Hydrated Bloc, we need to initialize storage in the main.dart
file:
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'; import 'counter_bloc.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); final storage = await HydratedStorage.build(storageDirectory: await getApplicationDocumentsDirectory()); HydratedBloc.storage = storage; runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return BlocProvider( create: (_) => CounterBloc(), child: MaterialApp( home: CounterScreen(), ), ); } }
2. Creating a Hydrated Bloc
Hydrated Bloc works like a regular Bloc but requires toJson
and fromJson
methods to serialize and deserialize state.
import 'package:hydrated_bloc/hydrated_bloc.dart'; import 'package:equatable/equatable.dart'; // Counter State class CounterState extends Equatable { final int counter; const CounterState(this.counter); @override List<Object> get props => [counter]; } // Counter Bloc class CounterBloc extends HydratedBloc<String, CounterState> { CounterBloc() : super(const CounterState(0)); void increment() => emit(CounterState(state.counter + 1)); void decrement() => emit(CounterState(state.counter - 1)); @override CounterState? fromJson(Map<String, dynamic> json) { return CounterState(json['counter'] as int); } @override Map<String, dynamic>? toJson(CounterState state) { return {'counter': state.counter}; } }
3. Creating the UI
import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'counter_bloc.dart'; class CounterScreen extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text("Hydrated Bloc Counter")), body: Center( child: BlocBuilder<CounterBloc, CounterState>( builder: (context, state) { return Text( 'Counter: ${state.counter}', style: TextStyle(fontSize: 24), ); }, ), ), floatingActionButton: Row( mainAxisAlignment: MainAxisAlignment.end, children: [ FloatingActionButton( onPressed: () => context.read<CounterBloc>().increment(), child: Icon(Icons.add), ), SizedBox(width: 10), FloatingActionButton( onPressed: () => context.read<CounterBloc>().decrement(), child: Icon(Icons.remove), ), ], ), ); } }
Handling Edge Cases
1. Clearing Persisted State
If needed, you can manually clear the persisted state using:
context.read<CounterBloc>().clear();
2. Handling Corrupt Storage
To handle corrupt storage scenarios, update the initialization in main.dart
:
void main() async { WidgetsFlutterBinding.ensureInitialized(); final storage = await HydratedStorage.build( storageDirectory: await getApplicationDocumentsDirectory(), ); HydratedBloc.storage = storage; runApp(MyApp()); }
Conclusion
Hydrated Bloc is a powerful extension to Bloc that enables persistent state management with minimal effort. It is particularly useful for applications that require state retention across app restarts.
Key Takeaways:
- Hydrated Bloc persists state across app restarts without manual intervention.
- Requires
toJson
andfromJson
to serialize and deserialize state. - Easy integration with existing Bloc-based architectures.
Call to Action
Try implementing Hydrated Bloc in your next Flutter project and experience seamless state persistence. Follow our blog for more Flutter development insights!