API Caching Using Hive and Bloc.
In this article, we will see how we can cache API responses using Hive and Bloc.
![API Caching Using Hive and Bloc](https://mobikul.com/wp-content/uploads/2023/06/Green-Feminine-and-Beauty-Boutique-Banner-Store-1024x512.png)
Hive is the lightweight,key-value database that is used to store the data locally in Flutter applications.
There are numerous ways to store data locally. Hive can be used to store data for both mobile and web applications.
For API caching hive is best suitable to use.
Bloc is a state management system for Flutter Apps recommended by Google developers. It helps in managing the state and making access to data from a central place in your project.
Read more about our Flutter app development services.
API Caching is used to make our mobile app response faster.
In this, article we are going to cache(save Api response locally)response using hive.
We will use the Bloc pattern for state management.
Dependencies
Let’s start by adding dependencies.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
dependencies: flutter: sdk: flutter hive: ^2.2.3 path_provider: ^2.0.11 hive_flutter: ^1.1.0 hive_generator: ^1.1.3 flutter_bloc: ^7.0.0 equatable: ^2.0.0 dev_dependencies: flutter_test: sdk: flutter build_runner: ^2.2.0 |
Create a Hive Model
Firstly, we will create a model class.
For example-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
import 'package:hive/hive.dart'; part 'student.g.dart'; @HiveType(typeId: 0) class Student extends HiveObject { @HiveField(0) @JsonKey(name:"etag") String? eTag = ""; @HiveField(1) String name; @HiveField(2) int age; } |
Create a Hive adapter:
Run the following command in your project directory to generate the adapter:
1 2 |
flutter packages pub run build_runner build |
Initialize Hive and Register Hive Adapter
Inside main. dart file initialize hive and register adapter.
For example-
1 2 3 4 5 6 7 8 9 |
void main() async { await Hive.initFlutter(); Hive.registerAdapter(StudentAdapter()); runApp(MyApp()); } |
Hive Service class
Let,s make another class in which we define all methods related to the hive.
For example-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
class HiveService { static HiveService? <em>hive</em> = null; static HiveService <em>getHive</em>() { if (<em>hive </em>== null) { <em>hive </em>= HiveService(); } return <em>hive</em>!; } /* function to check box is already created or not*/ Future<bool> isExists({required String boxName}) async { final openBox = await Hive.openBox(boxName); int length = openBox.length; return length != 0; } /* function to add box */ addBoxes<T>(dynamic response, String boxName) async { print("adding boxes "+ boxName); final openBox = await Hive.openBox(boxName); try{ openBox.deleteAt(0); }catch(exception){ print("adding boxes exception ${exception}"); } finally{ openBox.add(response); } } /* function to get box */ Future<dynamic> getBoxes(String boxName) async { print("getting boxes-> "+boxName); final openBox = await Hive.openBox(boxName); return openBox.getAt(0) ; } } |
Basically, the addBoxes() function saves dynamic responses coming in API response.
So that we can use the same function at every place to add a box.
Ok. We have done with the hive part. Let’s move to another part of this article i.e. Bloc.
State Class
Firstly, we create a class for bloc States.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
abstract class StudentState { const StudentState(); } class StudentStateInitial extends StudentState {} class StudentStateSuccess extends StudentState { final Student model; const StudentStateSuccess(this.model); } class StudentStateError extends StudentState { StudentStateError(this._message); String? _message; } |
Event Class
1 2 3 4 5 6 7 |
abstract class StudentEvent { const StudentEvent(); } class StudentDetailsFetchEvent extends StudentEvent { const StudentDetailsFetchEvent(); } |
Repository Class
Create a class which in which we will define functions to get data from Db or Server.
For example-
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
abstract class StudentRepository { Future<Student> getStudentDetailsFromServer(String etag); Future<Student?> getStudentDetailsFromDb(); } class StudentRepositoryImp extends StudentRepository { Future<Student?> getStudentDetailsFromDb() async { Student? student; String boxname = "studentDetails";//your box name await HiveService.getHive().isExists(boxName: boxname).then((value) async { if (value) { await HiveService.getHive().getBoxes(boxname).then((value) { student = value as Student; }); } }); return student; } @override Future<Student> getStudentDetailsFromServer(String etag) async { Student? student =await ApiClient().getStudentDetailsFromServer(etag); await HiveService.getHive().addBoxes(getAddress, "studentDetails");//save student details data in hive db return student; } } |
Bloc Class
Finally, we will create a Bloc class. Which is the main class of bloc pattern.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
class StudentScreenBloc extends Bloc<StudentEvent, StudentState> { StudentRepository? repository; StudentScreenBloc({this.repository}) : super(StudentStateInitial()) { on<StudentEvent>(mapEventToState); } void mapEventToState( StudentEvent event, Emitter<StudentState> emit) async { switch (event.runtimeType) { case StudentDetailsFetchEvent: try { var model = await repository?.getStudentDetailsFromDb(); if (model != null) { emit(StudentStateSuccess(model)); } model = await repository?.getStudentDetailsFromServer(model?.eTag??""); if (model != null) { emit(StudentStateSuccess(model)); } else { emit(StudentStateError('error ')); } } catch (error, _) { emit(StudentStateError(error.toString())); } break; } } } |
Now you can call the event from your widget class.
When an event will be triggered it will check the data in DB.
If data is found then it will display on the screen and in the background API will hit to get an updated response from the server.
If there is any updated response comes then it will update the DB and screen as well.
Etag– We are using etag variable to sync App-end db data with server-end data.
Conclusion :
This is how we can manage or enhance app performance and provide a seamless user experience.
Thanks for reading this article.