Building a Movie Database App in Flutter
Last Updated :
04 Apr, 2025
In this tutorial, we'll create a Flutter app that fetches movie data from an API, displays details such as ratings and reviews, and allows users to save their favorite movies locally using SQLite. This Application will demonstrate API integration, state management, and local data persistence.
Application Features
- Search for movies by name or genre.
- View detailed movie information (cast, plot, rating, etc.).
- Save and retrieve favorite movies locally.
What API is used in the Application
For this app, we’ll use the OMDb API to fetch movie data. OMDb is a free API that provides movie information like titles, posters, IMDb ratings, etc. You can get an API key from OMDb after signing up.
Setting Up the Project
Creating the Flutter Project
Create a new Flutter application using the command Prompt. To create a new app, write the below command and run it.
flutter create app_name
To know more about it refer this article: Creating a Simple Application in Flutter
Installing Dependencies
For this project, you’ll need these dependencies:
- http: To make network requests to the OMDb API.
- sqflite and path_provider : To save and manage favorite movies in a local SQLite database.
Add these dependencies in your pubspec.yaml file:
Dart
dependencies:
flutter:
sdk: flutter
http:
sqflite:
path_provider:
Run the following command to install the dependencies:
flutter pub get
Folder Structure
Create the following folder structure for better organization of your files:
This structure keeps database logic, models, services, and UI separated, making the code modular and easy to maintain.
Build the Application
The Application is divided into the following packages:
- Models: Contains Objects that encapsulate the data and behavior of the application domain.
- UI elements: These Contain Code that defines the UI of the Application.
- API Services: It contains the code able to maintain the API Services
- Database Helper: This part helps us to manage data in our local storage(SQLite).
Step 1 : Models
Let's start by creating a model for our movie data in movie.dart.
movie.dart
movie.dart
class Movie {
// Movie properties
final String imdbID;
final String title;
final String year;
final String poster;
final String type;
// Constructor to initialize the Movie object.
Movie({
required this.imdbID,
required this.title,
required this.year,
required this.poster,
required this.type,
});
// Creates a Movie object from a JSON map.
factory Movie.fromJson(Map<String, dynamic> json) {
return Movie(
imdbID: json['imdbID'],
title: json['Title'],
year: json['Year'],
poster: json['Poster'],
type: json['Type'],
);
}
// Converts the Movie object into a map.
Map<String, dynamic> toMap() {
return {
'imdbID': imdbID,
'title': title,
'year': year,
'poster': poster,
'type': type,
};
}
}
Now create another Model movie_details.dart; .this will store the movie details.
movie_details.dart
movie_details.dart
class MovieDetails {
// MovieDetails properties
final String title;
final String year;
final String rated;
final String released;
final String genre;
final String director;
final String actors;
final String plot;
final String poster;
final String imdbRating;
// Constructor to initialize the MovieDetails object.
MovieDetails({
required this.title,
required this.year,
required this.rated,
required this.released,
required this.genre,
required this.director,
required this.actors,
required this.plot,
required this.poster,
required this.imdbRating,
});
// Creates a MovieDetails object from a JSON map.
factory MovieDetails.fromJson(Map<String, dynamic> json) {
return MovieDetails(
title: json['Title'],
year: json['Year'],
rated: json['Rated'],
released: json['Released'],
genre: json['Genre'],
director: json['Director'],
actors: json['Actors'],
plot: json['Plot'],
poster: json['Poster'],
imdbRating: json['imdbRating'],
);
}
}
Step 2 : API Service
Create the movie_service.dart file to handle the API calls using the http package.
movie_service.dart
movie_service.dart
import 'dart:convert';
import 'dart:developer';
import 'package:http/http.dart' as http;
import 'package:movies/models/movie.dart';
import 'package:movies/models/movie_details.dart';
class MovieService {
// Replace with your OMDb API key.
final String apiKey = "API_KEY";
// Fetches a list of movies based on the search query.
Future<List<Movie>> fetchMovies(String searchQuery) async {
final response = await http.get(
Uri.parse('http://www.omdbapi.com/?s=$searchQuery&apikey=$apiKey'),
);
if (response.statusCode == 200) {
// Decodes the JSON response and extracts
// the 'Search' list.
List<dynamic> moviesJson = jsonDecode(response.body)['Search'];
// Logs the list of movies.
log(moviesJson.toString());
// Maps each JSON object to a Movie
// instance and returns the list.
return moviesJson.map((json) => Movie.fromJson(json)).toList();
}
else {
// Throws an exception if the request fails.
throw Exception('Failed to load movies');
}
}
// Fetches detailed information for a specific
// movie by its IMDb ID.
Future<MovieDetails> fetchMovieDetails(String imdbID) async {
final response = await http.get(
Uri.parse('http://www.omdbapi.com/?i=$imdbID&apikey=$apiKey'),
);
if (response.statusCode == 200) {
// Decodes the JSON response into a MovieDetails object.
return MovieDetails.fromJson(jsonDecode(response.body));
}
else {
// Throws an exception if the request fails.
throw Exception('Failed to load movie details');
}
}
}
Step 3. Database Helper
Next, we’ll create the db_helper.dart file to manage saving favorites using SQLite to our local storage.
db_helper.dart
db_helper.dart
import 'package:geeks_for_geeks/models/movie.dart';
import 'package:path/path.dart';
import 'package:sqflite/sqflite.dart';
class DBHelper {
// Singleton pattern to ensure only one
// instance of DBHelper is created.
static final DBHelper _instance = DBHelper._internal();
factory DBHelper() => _instance;
// Private constructor for singleton implementation.
DBHelper._internal();
// Holds the database instance.
Database? _database;
// Returns the database instance, initializes
// it if not already created.
Future<Database> get database async {
if (_database != null) return _database!;
_database = await _initDB();
return _database!;
}
// Initializes the database and returns
// the database object.
Future<Database> _initDB() async {
// Constructs the path to the 'movies.db' database file.
String path = join(await getDatabasesPath(), 'movies.db');
// Opens the database, creating it if it doesn't exist.
return await openDatabase(
path,
version: 1, // Database version.
onCreate: _onCreate, // Executes _onCreate if the database is new.
);
}
// Called when the database is created; creates the 'favorites' table.
Future<void> _onCreate(Database db, int version) async {
await db.execute('''
CREATE TABLE favorites (
id INTEGER PRIMARY KEY AUTOINCREMENT,
imdbID TEXT,
title TEXT,
year TEXT,
poster TEXT,
type TEXT
)
'''); // Creates the 'favorites' table with relevant columns.
}
// Inserts a movie into the 'favorites' table.
Future<void> insertFavorite(Movie movie) async {
final db = await database; // Gets the database instance.
await db.insert(
'favorites', // Target table.
movie.toMap(), // Converts the movie to a map.
conflictAlgorithm: ConflictAlgorithm.replace, // Replaces if conflict.
);
}
// Retrieves all movies from the 'favorites' table.
Future<List<Movie>> getFavorites() async {
final db = await database; // Gets the database instance.
final List<Map<String, dynamic>> maps = await db.query('favorites');
// Converts the list of maps into a list of Movie objects.
return List.generate(maps.length, (i) {
return Movie(
imdbID: maps[i]['imdbID'],
title: maps[i]['title'],
year: maps[i]['year'],
poster: maps[i]['poster'],
type: maps[i]['type'],
);
});
}
// Deletes a movie from the 'favorites' table based on its IMDb ID.
Future<bool> deleteFavorite(String imdbID) async {
// Gets the database instance.
final db = await database;
final int count = await db.delete(
'favorites',
where: 'imdbID = ?',
whereArgs: [imdbID], // Filters by the provided IMDb ID.
);
// Returns true if a row was deleted.
return count > 0;
}
Future<bool> isFavorite(String imdbID) async {
final db = await database; // Gets the database instance.
final List<Map<String, dynamic>> maps = await db.query(
'favorites',
where: 'imdbID = ?', // Filters by the provided IMDb ID.
whereArgs: [imdbID],
);
// Returns true if a movie with the given IMDb ID exists in favorites.
return maps.isNotEmpty;
}
}
Step 4. UI Components
Our Application contains three main UI Components Screens, as mentioned below:
- Home Screen
- Movie Details Screen
- Favorites Screen
Now let us Observe all them.
- Home Screen (Search Movies)
The home_screen.dart is where users can search for movies:
home_screen.dart
home_screen.dart
import 'package:flutter/material.dart';
import 'package:geeks_for_geeks/models/movie.dart';
import 'package:geeks_for_geeks/screens/favorites_screen.dart';
import 'package:geeks_for_geeks/services/movie_services.dart';
import 'movie_details_screen.dart';
class HomeScreen extends StatefulWidget {
// Constructor with optional key for widget.
const HomeScreen({super.key});
// Creates the state object.
@override
_HomeScreenState createState() => _HomeScreenState();
}
class _HomeScreenState extends State<HomeScreen> {
// Controller for the search input.
final TextEditingController _controller = TextEditingController();
// Instance of MovieService to fetch movies.
final MovieService _movieService = MovieService();
// List to store fetched movies.
List<Movie> _movies = [];
// Fetches movies based on the search query
// and updates the UI.
void _searchMovies() async {
// Fetches movies.
final movies = await _movieService.fetchMovies(_controller.text);
setState(() {
// Updates the movie list.
_movies = movies;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('GFG Movies App'), // App bar title.
backgroundColor: Colors.green, // App bar background color.
foregroundColor: Colors.white, // App bar text color.
actions: [
// Favorite button navigates to the FavoritesScreen.
IconButton(
icon: const Icon(Icons.favorite),
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (context) => FavoritesScreen()),
);
},
),
],
),
body: Column(
children: [
// Input field for entering the search query.
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller:
_controller, // Binds the controller to the input field.
decoration: const InputDecoration(
labelText: 'Search for movies', // Input field hint text.
),
),
),
// Button to trigger the search function.
ElevatedButton(
style: ElevatedButton.styleFrom(
backgroundColor: Colors.green, // Button background color.
foregroundColor: Colors.white, // Button text color.
),
onPressed: _searchMovies, // Calls _searchMovies on press.
child: const Text(
"Search", // Button text.
)),
// Displays search results or a message if no movies are found.
Expanded(
child: _movies.isEmpty
? const Center(
child: Text('No movies found')) // No results message.
: ListView.builder(
itemCount: _movies.length, // Number of movies to display.
itemBuilder: (context, index) {
final movie = _movies[
index]; // Fetches the movie at the current index.
return Card(
margin: const EdgeInsets.all(8.0), // Card margin.
elevation: 5, // Card elevation effect.
child: ListTile(
leading: Image.network(
movie.poster), // Displays the movie poster.
title: Text(movie.title), // Displays the movie title.
subtitle:
Text(movie.year), // Displays the movie year.
trailing: const Icon(Icons
.arrow_forward), // Arrow icon for navigation.
onTap: () {
// Navigates to MovieDetailsScreen with the selected movie's IMDb ID.
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
MovieDetailsScreen(imdbID: movie.imdbID),
),
);
},
),
);
},
),
),
],
),
);
}
}
Screenshot of Home Page:
- Movie Details Screen
The movie_details_screen.dart is where users can see the movie details :
movie_details_screen.dart
movie_details_screen.dart
import 'package:flutter/material.dart';
import 'package:geeks_for_geeks/helpers/db_helper.dart';
import 'package:geeks_for_geeks/models/movie.dart';
import 'package:geeks_for_geeks/models/movie_details.dart';
import 'package:geeks_for_geeks/services/movie_services.dart';
class MovieDetailsScreen extends StatefulWidget {
// IMDb ID of the selected movie.
final String imdbID;
// Constructor with required IMDb ID.
const MovieDetailsScreen({super.key, required this.imdbID});
// Creates the state for this widget.
@override
_MovieDetailsScreenState createState() => _MovieDetailsScreenState();
}
class _MovieDetailsScreenState extends State<MovieDetailsScreen> {
// Service to fetch movie data from API.
final MovieService _movieService = MovieService();
// Database helper instance to manage favorites.
final DBHelper _dbHelper = DBHelper();
// Holds the detailed movie information.
MovieDetails? _movieDetails;
// Tracks whether the movie is marked as favorite.
bool isFavorite = false;
@override
void initState() {
super.initState();
// Fetch movie details when the screen initializes.
_loadMovieDetails();
// Check if the movie is already marked as favorite.
_checkFavoriteStatus();
}
// Fetches movie details from the API using the IMDb ID.
void _loadMovieDetails() async {
// API call.
final movieDetails = await _movieService.fetchMovieDetails(widget.imdbID);
setState(() {
// Updates the state with the fetched movie details.
_movieDetails = movieDetails;
});
}
// Checks if the movie is already marked as a favorite.
void _checkFavoriteStatus() async {
// Checks in the database.
final status = await _dbHelper.isFavorite(widget.imdbID);
setState(() {
// Updates the favorite status.
isFavorite = status;
});
}
// Toggles the favorite status of the movie.
void _toggleFavorite() async {
if (isFavorite) {
// Removes the movie from favorites.
await _dbHelper.deleteFavorite(widget.imdbID);
} else {
// Adds the movie to favorites.
await _dbHelper.insertFavorite(Movie(
imdbID: widget.imdbID,
title: _movieDetails!.title,
year: _movieDetails!.year,
type: 'movie', // Assuming it is a movie.
poster: _movieDetails!.poster,
));
}
// Re-checks and updates the favorite status.
_checkFavoriteStatus();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Movie Details'), // App bar title.
backgroundColor: Colors.green, // App bar background color.
foregroundColor: Colors.white, // App bar text color.
actions: [
// Favorite button to toggle the favorite status.
IconButton(
icon: Icon(isFavorite
? Icons.favorite
: Icons
.favorite_border), // Icon changes based on favorite status.
onPressed:
_toggleFavorite, // Toggles the favorite status when pressed.
),
],
),
// Displays a loading indicator if movie details are not loaded yet.
body: _movieDetails == null
? const Center(
child: CircularProgressIndicator()) // Shows a loading spinner.
: Padding(
padding:
const EdgeInsets.all(8.0), // Adds padding around the content.
child: Column(
children: [
Image.network(
_movieDetails!.poster), // Displays the movie poster.
const SizedBox(height: 10), // Adds spacing.
// Displays the movie title with bold styling.
Text(
_movieDetails!.title,
style: const TextStyle(
fontSize: 24, fontWeight: FontWeight.bold),
),
const SizedBox(height: 10), // Adds spacing.
// Displays the IMDb rating of the movie.
Text('Rating: ${_movieDetails!.imdbRating}'),
const SizedBox(height: 10), // Adds spacing.
// Displays the plot/description of the movie.
Text(_movieDetails!.plot),
],
),
),
);
}
}
Screenshot of the Movie Details Screen:
- Favorites Screen
The favorites_screen.dart displays the movies added to favorites and stored in the local database :
favorites_screen.dart
favorites_screen.dart
import 'package:flutter/material.dart';
import 'package:geeks_for_geeks/helpers/db_helper.dart';
import 'package:geeks_for_geeks/models/movie.dart';
import 'movie_details_screen.dart';
class FavoritesScreen extends StatelessWidget {
// DBHelper instance to manage favorites.
final DBHelper _dbHelper = DBHelper();
// Constructor with optional key for widget.
FavoritesScreen({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Favorite Movies'), // App bar title.
backgroundColor: Colors.green, // App bar background color.
foregroundColor: Colors.white, // App bar text color.
),
// Builds the body of the screen using FutureBuilder
// to load favorite movies asynchronously.
body: FutureBuilder<List<Movie>>(
future: _dbHelper
.getFavorites(), // Fetches favorite movies from the database.
builder: (context, snapshot) {
// If data is not yet available, show a loading spinner.
if (!snapshot.hasData) {
return const Center(child: CircularProgressIndicator());
}
// If there are no favorite movies, display a message.
if (snapshot.data!.isEmpty) {
return const Center(child: Text('No favorite movies'));
}
// Displays the list of favorite movies.
return ListView.builder(
itemCount: snapshot.data!.length, // Number of favorite movies.
itemBuilder: (context, index) {
final movie = snapshot
.data![index]; // Fetches the movie at the current index.
// Displays each movie as a ListTile with an image, title, and year.
return ListTile(
leading:
Image.network(movie.poster), // Displays the movie poster.
title: Text(movie.title), // Displays the movie title.
subtitle: Text(movie.year), // Displays the movie release year.
// Navigates to the MovieDetailsScreen when tapped.
onTap: () {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) => MovieDetailsScreen(
imdbID: movie.imdbID), // Passes the IMDb ID.
),
);
},
);
},
);
},
),
);
}
}
Screenshot of the Favorites Screen:
Main Application File Code
After successfully creating Models, Service, and Helper files, we can finally utilize them to create a fully functional Application.
Finally, let's Call Our HomeScreen from the main.dart file.
main.dart
main.dart
import 'package:flutter/material.dart';
import 'package:movies/screens/home_screen.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.green),
useMaterial3: true,
),
home: const HomeScreen());
}
}
Complete code for the Application Here : Movie_Database_Application
Output :
Similar Reads
How to Build a Video Calling App in Flutter?
Video Calling has become a necessary feature in most real-time communication applications used around the world. Its primary application is to connect people from all around the world along with other obvious applications. Chat applications like Facebook, WhatsApp, and Instagram all have video calli
4 min read
Flutter - Building News Reader App
In today's fast-paced world, staying informed is essential, and mobile applications have become a popular medium for accessing news. In this article, we'll guide you through building a News Reader App step by step using Flutter, a powerful and versatile framework for creating cross-platform applicat
5 min read
Flutter - Build an Image Compressor App
Many applications accept images of small size, to reduce image size we use different online applications to compress images while maintaining their quality. In this article, we will create an image compressor app in Flutter that compresses images with the help of available libraries in Flutter. Step
9 min read
Listview.builder in Flutter
ListView is a very important widget in a flutter. It is used to create the list of children But when we want to create a list recursively without writing code again and again then ListView.builder is used instead of ListView. ListView.builder creates a scrollable, linear array of widgets. ListView.b
3 min read
Movie Database Android Application
Movies are an integral part of our entertainment lives. As a developer, making an application that displays information about movies can be a rewarding project idea! In this article we will learn about making an application that displays basic information about movies like Posters, Title, Runtime, T
15+ min read
Flutter - Store Data in Hive Local Database
Hive is a data storage system on our phone where we can store data in boxes. We can store an integer, string, list of strings, Boolean, double, models, integers, etc., in Hive. Now, let us discuss where we can implement these. The first way we can use this is to save the user information user is log
12 min read
Basic Quiz App In Flutter
Flutter SDK is an open-source software development kit for building beautiful UI that is natively compiled. Currently, it is available as a stable version for iOS and Android OS. In this app, we are going to have the features or modules mentioned below: Five multiple-choice questions ( more question
8 min read
Creating a Simple Application in Flutter
Flutter is an open-source cross-platform mobile application development SDK created by Google. It is highly user-friendly and builds high-quality mobile applications. The intention behind this article is to guide readers through the process of building an application through Flutter by creating a si
5 min read
Flutter - Build a Inventory Management App
Inventory management is a crucial aspect of any business. With the power of Flutter, we can create a simple yet effective inventory management app. In this tutorial, we'll build a basic inventory manager using Flutter and local storage. A sample video is given below to get an idea about what we are
6 min read
Remote Config in Flutter and Firebase
In Current app development, it is essential to offer smooth updates without asking users to download a new version from the Play Store to improve user experience. Remote Config in Flutter with the power of Firebase allows developers to update app content and features dynamically without releasing a
9 min read