Open In App

EBook reader Application in Flutter

Last Updated : 03 May, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

EBook reader Application brings the library to your fingertips. This application will be the travel partner of every book lover who loves to read books of their choice. The app is developed using Flutter and provider state management. It uses the Google Books API to fetch the data of books. The app allows you to search for the book by its name. The API provides the details of all the books, you can read the description. The app has the functionality to preview the book or download it, as per availability.

Demo Video:

Concepts Covered in the app

  • Provider state management.
  • API management

Project Structure

Structure_gfg


Step-by-Step Implementation of EBook reader app

Step 1: Create a new Flutter Application

Create a new Flutter application using the command Prompt. To create a new app, write the following command and run it.

flutter create app_name

To know more about it refer this article: Creating a Simple Application in Flutter.

Step 2: Adding the Dependency

To add the dependency to the pubspec.yaml file,add  provider, url_launcher and http as a dependency in the dependencies part of the pubspec.yaml file, as shown below:

Dart
dependencies:
  flutter:
    sdk: flutter
  provider: ^6.1.4
  url_launcher: ^6.3.1
  http: ^1.3.0

Now run the below command in the terminal.

flutter pub get

Or

Run the below command in the terminal.

flutter pub add  provider url_launcher http


Step 3: Import dependencies

To use libraries, import all of them in the respective .dart file.

import 'package:provider/provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:http/http.dart' as http;


Step 4: Working with main.dart

Add the boilerplate code below in main.dart to initialize the ChangeNotifierProvider in the providers list inside the MultiProvider in the main function, and create a basic structure with an MaterialApp.

main.dart:

main.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/home_screen.dart';
import 'services/google_books_api.dart';

// Entry point of the Flutter application
void main() {
  runApp(const MyApp());
}

// Root widget of the application
class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MultiProvider(
      // Providing dependencies to the widget tree using MultiProvider
      providers: [
        // Registering GoogleBooksApi as a ChangeNotifier for state management
        ChangeNotifierProvider(create: (_) => GoogleBooksApi()),
      ],
      child: MaterialApp(
        debugShowCheckedModeBanner: false,
        title: 'EBook reader Application',
        // Sets the HomeScreen as the initial screen
        home: const HomeScreen(),
      ),
    );
  }
}


Step 5: Code for google_books_api.dart

An HTTP request is made to the API and the data of matching query is fetched. The UI is updated according to the status code.

google_books_api.dart:

Dart
import 'dart:convert';               
import 'package:flutter/material.dart'; 
import 'package:http/http.dart' as http; 

class GoogleBooksApi extends ChangeNotifier {
  // List to store fetched books data from the API
  List books = [];                    

  // Boolean to indicate whether data is currently being fetched
  bool isLoading = false;              

  // Function to search books based on user query
  Future<void> search(String query) async {
    // Construct the URL for the Google Books API with the query string
    final url =
        'https://www.googleapis.com/books/v1/volumes?q=$query';

    // Set loading state to true to indicate data fetching has started
    isLoading = true;
    // Notify listeners (e.g., UI) to update the loading state
    notifyListeners();   

    try {
      // Make an HTTP GET request to the Google Books API
      final response = await http.get(Uri.parse(url));

      // Log the response status code for debugging purposes
      print("response status code ${response.statusCode}");

      // Check if the request was successful (HTTP status code 200)
      if (response.statusCode == 200) {
        // Parse the JSON response body
        final data = json.decode(response.body);   
        // Extract the 'items' field from the response, or set to an empty list if null
        books = data['items'] ?? [];  
      } else {
        // If the request fails, clear the books list
        books = [];  
      }
    } catch (e) {
      // Handle any exceptions that occur during the HTTP request
      // Clear the books list in case of an error
      books = [];
    }

    // Set loading state to false to indicate data fetching is complete
    isLoading = false;
    // Notify listeners (e.g., UI) to update the loading state and display results
    notifyListeners();   
  }
}


Step 6: Code for screens/home_screen.dart

This has the UI of the home page. The user can enter the Title of the book in the textfield and click on search icon to fetch the result. ListView represents the scrollable list of available books. The navigation logic fetches the description of the book from the ListView.

home_screen.dart:

home_screen.dart
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import '../services/google_books_api.dart';
import './book_details.dart';

// HomeScreen is the main screen of the application where users can search for books
class HomeScreen extends StatelessWidget {
  const HomeScreen({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // Access the GoogleBooksApi provider
    final bookProvider = Provider.of<GoogleBooksApi>(context);
    // Controller for the search input field
    TextEditingController searchController = TextEditingController();

    return Scaffold(
      appBar: AppBar(
        centerTitle: true,
        title: const Text(
          'EBook reader Application',
          style: TextStyle(fontSize: 25),
        ),
        backgroundColor: Colors.green[700],
        foregroundColor: Colors.white,
        toolbarHeight: 70,
        elevation: 5,
        shadowColor: Colors.green[700],
        shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
      ),
      body: Column(
        children: [
          const SizedBox(height: 10), // Adds spacing at the top
          Padding(
            padding: const EdgeInsets.all(10.0),
            child: TextField(
              // Binds the controller to the TextField
              controller: searchController,
              // Triggers search on submission
              onSubmitted:
                  (value) => bookProvider.search(searchController.text),
              decoration: InputDecoration(
                border: OutlineInputBorder(),
                focusedBorder: OutlineInputBorder(
                  borderSide: const BorderSide(
                    color: Color.fromARGB(255, 3, 131, 29),
                  ),
                  borderRadius: BorderRadius.circular(10.0),
                ),
                // Placeholder text
                labelText: 'Search for books',
                labelStyle: TextStyle(color: Color.fromARGB(255, 3, 131, 29)),
                suffixIcon: IconButton(
                  icon: const Icon(
                    Icons.search,
                    size: 30,
                    color: Color.fromARGB(255, 3, 131, 29),
                  ),
                  onPressed: () {
                    // Triggers search on button press
                    bookProvider.search(searchController.text);
                  },
                ),
              ),
            ),
          ),
          Expanded(
            // Checks if data is still loading
            child:
                bookProvider.isLoading
                    ? const Center(
                      child: CircularProgressIndicator(
                        color: Color.fromARGB(255, 3, 131, 29),
                      ),
                    )
                    : ListView.builder(
                      // Number of books to display
                      itemCount: bookProvider.books.length,
                      itemBuilder: (context, index) {
                        // Current book data
                        final book = bookProvider.books[index];
                        return ListTile(
                          // Book title
                          title: Text(
                            book['volumeInfo']['title'] ?? 'No Title',
                          ),
                          // Authors list
                          subtitle: Text(
                            book['volumeInfo']['authors'] != null
                                ? book['volumeInfo']['authors'].join(', ')
                                : 'Unknown Author', // Fallback if no authors are available
                          ),
                          onTap: () {
                            // Navigate to the BookDetail screen when a book is tapped
                            Navigator.push(
                              context,
                              MaterialPageRoute(
                                builder: (context) => BookDetail(book: book),
                              ),
                            );
                          },
                        );
                      },
                    ),
          ),
        ],
      ),
    );
  }
}


Step 7: Code for screens/book_detail.dart

This page fetches the details of the book from the Google Books API. It fetches the preview link and download link of the book using the API. It has UI to present the cover of the book.

Note : Since we are using a free API, the cover image and download feature may not properly work. The code below handles the API failure.

book_details.dart:

book_detail.dart
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';

class BookDetail extends StatelessWidget {
  final Map book;

  const BookDetail({Key? key, required this.book}) : super(key: key);
  // Function to open URLs in the browser
  Future<void> _launchURL(String url) async {
    try {
      // Encode the URL to handle special characters
      final Uri uri = Uri.parse(url);
      if (!await launchUrl(uri)) {
        throw Exception('Could not launch $uri');
      }
    } catch (e) {
      // Show a snackbar with the error message
      debugPrint('Error launching URL: $e');
      // You might want to show this error to the user through a SnackBar
      // or other UI element
    }
  }

  @override
  Widget build(BuildContext context) {
    final volumeInfo = book['volumeInfo'];

    // Extract URLs
    final thumbnail =
        volumeInfo['imageLinks'] != null
            ? volumeInfo['imageLinks']['thumbnail']
            : null;

    final previewLink = volumeInfo['previewLink'];
    final downloadLink =
        book['accessInfo'] != null
            ? book['accessInfo']['pdf'] != null &&
                    book['accessInfo']['pdf']['isAvailable']
                ? book['accessInfo']['pdf']['acsTokenLink']
                : null
            : null;

    final buyLink = volumeInfo['infoLink'];

    return Scaffold(
      appBar: AppBar(title: Text(volumeInfo['title'] ?? 'No Title')),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(8.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Display the image with a placeholder and error handling
            thumbnail != null
                ? Center(
                  child: Image.network(
                    thumbnail,
                    fit: BoxFit.cover,
                    loadingBuilder: (context, child, loadingProgress) {
                      if (loadingProgress == null) {
                        return child;
                      }
                      return const Center(child: CircularProgressIndicator());
                    },
                    errorBuilder: (context, error, stackTrace) {
                      return const Icon(Icons.error, size: 100);
                    },
                  ),
                )
                : const Icon(Icons.book, size: 100),
            const SizedBox(height: 16),
            Text(
              volumeInfo['title'] ?? 'No Title',
              style: const TextStyle(fontSize: 15, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8),
            Text(
              'Author(s): ' +
                  (volumeInfo['authors'] != null
                      ? volumeInfo['authors'].join(', ')
                      : 'Unknown'),
            ),
            const SizedBox(height: 8),
            Text(volumeInfo['description'] ?? 'No Description'),
            const SizedBox(height: 16),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                // Preview Button
                if (previewLink != null)
                  ElevatedButton(
                    onPressed: () {
                      _launchURL(previewLink);
                    },
                    child: const Text(
                      'Preview',
                      style: TextStyle(
                        fontSize: 10,
                        letterSpacing: 1.0,
                        fontWeight: FontWeight.bold,
                      ),
                      textAlign: TextAlign.center,
                    ),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green[700],
                      foregroundColor: Colors.white,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                      padding: EdgeInsets.only(
                        right: 20,
                        left: 25,
                        top: 15,
                        bottom: 15,
                      ),
                    ),
                  ),
                const SizedBox(width: 10),
                // Download Button
                if (downloadLink != null)
                  ElevatedButton(
                    onPressed: () {
                      _launchURL(downloadLink);
                    },
                    child: const Text(
                      'Download',
                      style: TextStyle(
                        fontSize: 10,
                        letterSpacing: 1.0,
                        fontWeight: FontWeight.bold,
                      ),
                      textAlign: TextAlign.center,
                    ),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green[700],
                      foregroundColor: Colors.white,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                      padding: EdgeInsets.only(
                        right: 20,
                        left: 25,
                        top: 15,
                        bottom: 15,
                      ),
                    ),
                  )
                else
                  ElevatedButton(
                    onPressed: () {
                      _launchURL(buyLink);
                    },
                    child: const Text(
                      'More Info',
                      style: TextStyle(
                        fontSize: 10,
                        letterSpacing: 1.0,
                        fontWeight: FontWeight.bold,
                      ),
                      textAlign: TextAlign.center,
                    ),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.green[700],
                      foregroundColor: Colors.white,
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(10),
                      ),
                      padding: EdgeInsets.only(
                        right: 20,
                        left: 25,
                        top: 15,
                        bottom: 15,
                      ),
                    ),
                  ),
              ],
            ),
          ],
        ),
      ),
    );
  }
}


Step 8: Run the application

Save the project and enter below command to run the application.

flutter run

Click Here to get the Full Application Code Access

Output :


Next Article

Similar Reads