Hive Database in Flutter: Building a Simple Movie App
Introduction
In this article, we will explore Hive, a lightweight and blazing fast key-value database for Flutter and Dart applications. We’ll walk through the process of building a simple Movie App, where users can add their favorite movies along with the director’s name and poster image. We’ll use Hive to store and manage the movie data locally and demonstrate how to create, read, update, and delete movie entries.
Prerequisites
Before we begin, make sure you have the following dependencies added to your pubspec.yaml
file:
dependencies:
flutter:
sdk: flutter
hive: ^1.4.1+1
hive_flutter: ^0.3.0+2
image_picker: ^0.8.4+3
Login Screen
Let’s start by creating a login screen where users can log in to the Movie App. The login screen will be a simple page with fields to input a username and password. If the user logs in successfully, they will be redirected to the Movie List Screen.
import 'package:flutter/material.dart';
class LoginScreen extends StatelessWidget {
const LoginScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
body: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
const Text(
"Movie App",
style: TextStyle(fontSize: 22, fontWeight: FontWeight.w500),
),
const SizedBox(
height: 50,
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10.0),
child: Column(
children: [
const TextField(
decoration: InputDecoration(
hintText: "input name",
label: Text("Username"),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(),
),
border: OutlineInputBorder(borderSide: BorderSide())),
),
const SizedBox(
height: 20,
),
const TextField(
decoration: InputDecoration(
hintText: "input password",
label: Text("Password"),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(),
),
border: OutlineInputBorder(borderSide: BorderSide()),
suffixIcon: Icon(Icons.remove_red_eye)),
obscureText: true,
),
const SizedBox(
height: 15,
),
ElevatedButton(
onPressed: () {
Navigator.pushNamed(context, "movieList");
},
child: const Text("Login"),
),
],
),
),
],
),
),
);
}
}
Movie Model
Before we proceed, we need to define a model for the Movie data. The model will represent each movie entry with properties like name, director, and imagePath (for the poster image).
import 'package:hive/hive.dart';
part 'movie.g.dart';
@HiveType(typeId: 0)
class Movie extends HiveObject {
@HiveField(0)
late String name;
@HiveField(1)
late String director;
@HiveField(2)
late String imagePath;
}
Hive Database Setup
Now, let’s set up the Hive database in our app. We’ll need to initialize Hive and register the Movie model using a TypeAdapter.
Now, We will run a code generator by typing the following command in the terminal which will automatically generate database code for us.
flutter packages pub run build_runner build
Note: ‘movie.dart’ is the name of the file. A line section called “movie.g.dart” will be added, where the word “generated” is used. hence, movie.g.dart would be the new file created.
// GENERATED CODE - DO NOT MODIFY BY HAND
part of 'movie.dart';
// **************************************************************************
// TypeAdapterGenerator
// **************************************************************************
class MovieAdapter extends TypeAdapter<Movie> {
@override
final int typeId = 0;
@override
Movie read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
};
return Movie()
..name = fields[0] as String
..director = fields[1] as String
..imagePath = fields[2] as String;
}
@override
void write(BinaryWriter writer, Movie obj) {
writer
..writeByte(3)
..writeByte(0)
..write(obj.name)
..writeByte(1)
..write(obj.director)
..writeByte(2)
..write(obj.imagePath);
}
@override
int get hashCode => typeId.hashCode;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is MovieAdapter &&
runtimeType == other.runtimeType &&
typeId == other.typeId;
}
Movie List Screen
Next, we’ll create a Movie List Screen that displays the list of added movies. Users can see the movie name, director, and poster image in a ListView. We’ll also add the functionality to delete movies from the list.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:flutter_staggered_animations/flutter_staggered_animations.dart';
import 'package:hive_flutter/adapters.dart';
import 'package:movie_app/screens/add_movie_screen.dart';
import '../models/movie.dart';
class MovieListScreen extends StatelessWidget {
MovieListScreen({super.key});
final Box<Movie> movieBox = Hive.box<Movie>("movies");
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
title: const Text(
"Movie List",
style: TextStyle(fontSize: 24, fontWeight: FontWeight.w500),
),
backgroundColor: Colors.brown[600],
),
body: ValueListenableBuilder(
valueListenable: movieBox.listenable(),
builder: (BuildContext context, Box<Movie> box, _) {
return AnimationLimiter(
child: ListView.builder(
itemCount: box.length,
itemBuilder: (context, index) {
final movie = box.getAt(index);
return AnimationConfiguration.staggeredList(
position: index,
duration: const Duration(milliseconds: 500),
child: SlideAnimation(
verticalOffset: 50.0,
child: FadeInAnimation(
child: ListTile(
leading: Image.file(File(movie!.imagePath),
width: 100,
),
title: Text(movie.name),
subtitle: Text(movie.director),
trailing: IconButton(
onPressed: () {
//delete from the box
box.deleteAt(index);
},
icon: const Icon(Icons.delete)),
),
),
),
);
},
),
);
},
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// navigate to the add movie screen
Navigator.push(context,
MaterialPageRoute(builder: (_) => const AddMovieScreen()));
},
child: const Icon(Icons.add),
),
);
}
}
Add Movie Screen
In the Add Movie Screen, users can input the movie name, director’s name, and select a poster image from their device’s gallery. After filling in the details, they can save the movie entry, which will be added to the local Hive database.
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:hive/hive.dart';
import 'package:image_picker/image_picker.dart';
import 'package:movie_app/models/movie.dart';
class AddMovieScreen extends StatefulWidget {
const AddMovieScreen({super.key});
@override
State<AddMovieScreen> createState() => _AddMovieScreenState();
}
class _AddMovieScreenState extends State<AddMovieScreen> {
final TextEditingController _nameController = TextEditingController();
final TextEditingController _directorController = TextEditingController();
File? posterImage;
Future<void> _selectPosterImage() async {
final picker = ImagePicker();
final pickedFile = await picker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
setState(() {
posterImage = File(pickedFile.path);
});
}
}
Future<void> _saveMovie() async {
final movieBox = Hive.box<Movie>("movies");
final newMovie = Movie()
..name = _nameController.text
..director = _directorController.text
..imagePath = posterImage!.path;
movieBox.add(newMovie);
Navigator.pop(context);
}
@override
void dispose() {
// TODO: implement dispose
_nameController.dispose();
_directorController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.amber[100],
appBar: AppBar(
title: const Text("Add Movie"),
),
body: Padding(
padding: const EdgeInsets.all(16.0),
child: SingleChildScrollView(
child:
Column(crossAxisAlignment: CrossAxisAlignment.start, children: [
TextField(
controller: _nameController,
decoration: const InputDecoration(
labelText: 'Name',
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(),
),
border: OutlineInputBorder(borderSide: BorderSide()),
),
),
const SizedBox(
height: 16.0,
),
TextField(
controller: _directorController,
decoration: const InputDecoration(
labelText: 'Director',
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(),
),
border: OutlineInputBorder(borderSide: BorderSide()),
),
),
const SizedBox(
height: 16.0,
),
ElevatedButton(
onPressed: _selectPosterImage,
child: const Text("Select Poster Image")),
const SizedBox(
height: 16.0,
),
if (posterImage != null)
Image.file(
posterImage!,
width: 200,
height: 140,
fit: BoxFit.cover,
),
const SizedBox(
height: 16.0,
),
ElevatedButton(
onPressed: _saveMovie, child: const Text("Save Movie"))
]),
),
),
);
}
}
Check out the output below
Final Thoughts
In this article, we learned how to use Hive, a fast and lightweight key-value database, to build a simple Movie App in Flutter. We covered setting up the Hive database, creating model classes, and implementing screens for login, displaying the movie list, and adding new movies. Hive allows us to store and manage data efficiently, making it a great choice for local data storage in Flutter applications.
Conclusion
Hive is an excellent choice for handling local data storage in Flutter applications due to its speed and simplicity. By building a simple Movie App, we explored the basics of using Hive for database management, which can be extended to more complex applications with ease.
You can refer to the official documentation with Hive:
I hope this article helps you understand the power of Hive in Flutter and encourages you to explore more possibilities with local data storage in your future projects. Happy coding!