Fetching Data from an API in a Flutter Splash Screen and Inserting it into a Database

Fetching Data from an API in a Flutter Splash Screen and Inserting it into a Database

Flutter is a popular framework for developing cross-platform mobile applications. In the following article, we'll look at a simple Flutter app featuring a splash screen. We'll read via the given code to understand its structure and purpose.

  1. Imports: The code begins by importing two packages: flutter/material.dart and a custom splash_screen.dart file. These imports are essential for building the user interface and managing navigation within the app.

  2. main() Function: The main() function is the entry point of a Flutter app. Here, it ensures that Flutter is initialized and then runs the MyApp widget.

  3. MyApp Widget: MyApp is a custom Flutter widget that extends StatelessWidget. In Flutter, everything is a widget, and widgets are the building blocks of your app's UI.

  4. build() Method: The build() method is where you define the structure of your app. Here's what's happening within this method:

    • MaterialApp: It's a Flutter widget that provides a Material Design-themed app. The debugShowCheckedModeBanner property is set to false to hide the debug banner when running the app in debug mode.

    • title: This sets the title of the app to 'REST API'.

    • home: The home property specifies the initial screen of the app, and it's set to SplashScreenPage(). This suggests that your app starts with a splash screen, and SplashScreenPage likely resides in the splash_screen.dart file you've imported.

import 'package:flutter/material.dart';
import 'package:rest_api/splash_screen.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return  const MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'REST API',
      home: SplashScreenPage(),
    );
  }
}

Splash Screen and Beyond

In this code, you've set up the foundation for your Flutter app. It begins with a splash screen, and from there, you can expand and create additional screens and functionality.

  • To implement the splash screen, you can customize the SplashScreenPage in the splash_screen.dart file. A splash screen is often used to display a logo or branding while the app loads resources.

  • As your app evolves, you can navigate to other screens, fetch data from REST APIs, and provide a rich user experience. Flutter offers various packages and tools for making HTTP requests, handling responses, and managing state.

Creating a Flutter Model Class: PhotoModel

In Flutter, model classes are crucial for representing data structures that your application uses. In this article, we'll explore the PhotoModel class you've provided. This class is designed to represent photo data fetched from an API and serves as a blueprint for how this data is structured in this Flutter app.

Understanding the PhotoModel Class

class PhotoModel {
  int? albumId;
  int? id;
  String? title;
  String? url;
  String? thumbnailUrl;

  PhotoModel({this.albumId, this.id, this.title, this.url, this.thumbnailUrl});

  PhotoModel.fromJson(Map<String, dynamic> json) {
    albumId = json['albumId'];
    id = json['id'];
    title = json['title'];
    url = json['url'];
    thumbnailUrl = json['thumbnailUrl'];
  }

  Map<String, dynamic> toJson() {
    final Map<String, dynamic> data = <String, dynamic>{};
    data['albumId'] = albumId;
    data['id'] = id;
    data['title'] = title;
    data['url'] = url;
    data['thumbnailUrl'] = thumbnailUrl;
    return data;
  }
}

Properties: The PhotoModel class defines several properties that represent attributes of a photo object:

  • albumId: An integer representing the album to which the photo belongs.

  • id: An integer uniquely identifying the photo.

  • title: A string describing the title or caption of the photo.

  • url: A string containing the URL where the full-sized photo can be accessed.

  • thumbnailUrl: A string with the URL of a thumbnail version of the photo.

Constructor: This is a constructor method for the PhotoModel class. It allows you to create instances of PhotoModel with optional named parameters. This means you can create a PhotoModel object with any combination of properties.

fromJson Factory Method: This method is used to create a PhotoModel object from a JSON map (a key-value pair structure commonly used for data interchange). It takes a Map<String, dynamic> as input, extracts the values for each property from the map, and assigns them to the corresponding class properties.

toJson Method: This method converts a PhotoModel object back into a JSON map. It creates a new map, assigns each property's value to the corresponding key, and returns the map as a JSON representation of the object.

Managing Local Database in a Flutter App: DatabaseHelper Class

In this page, we'll explore the DatabaseHelper class you've provided. This class is a critical component of your Flutter app as it handles the initialization and management of a local SQLite database. This database is used to store and retrieve instances of the PhotoModel class, which represents photo data fetched from a remote API.

import 'package:rest_api/model/photomodel.dart';
import 'package:sqflite/sqflite.dart';
import 'package:path/path.dart';

class DatabaseHelper {

  Future<Database> initializedDB() async {
    String path = await getDatabasesPath();
    return openDatabase(
      join(path,'photos.db'),
      version:  1,
      onCreate: (Database db, int version) async{
        await db.execute(
            "CREATE TABLE photos("
                "albumId INTEGER,"
                " id INTEGER PRIMARY KEY,"
                " title TEXT NOT NULL,"
                " url TEXT NOT NULL,"
                " thumbnailUrl TEXT NOT NULL)"
        );
      }
    );

  }

  Future<int> insertData (PhotoModel data) async {
     final db = await initializedDB();
     return await db.insert('photos', data.toJson(),
     conflictAlgorithm: ConflictAlgorithm.replace);  }

  Future<List<PhotoModel>> getAllData() async{

    final db = await initializedDB();
    final List<Map<String,dynamic>> maps = await db.query('photos');
    return List.generate(maps.length, (i){
      return PhotoModel.fromJson(maps[i]);
    });
  }

}

Imports: The class imports necessary packages like photomodel.dart for working with PhotoModel objects, sqflite for SQLite database operations, and path for managing file paths.

initializedDB Method: This method is responsible for initializing the SQLite database. It returns a Future<Database>, which represents the database instance. Here's what this method does:

  • It determines the path where the database file should be stored using getDatabasesPath().

  • It opens or creates a new SQLite database named photos.db at the specified path.

  • If the database doesn't exist, the onCreate callback is executed. In this callback, it defines the structure of the photos table with columns for albumId, id, title, url, and thumbnailUrl.

insertData Method: This method inserts a PhotoModel object (data) into the photos table. It returns the ID of the newly inserted row as a Future<int>. Key points:

  • It retrieves a reference to the initialized database.

  • It uses the insert method to add the data to the photos table. The conflictAlgorithm: ConflictAlgorithm.replace parameter specifies that if there is a conflict (i.e., a row with the same primary key exists), it should replace the existing row.

getAllData Method: This method retrieves all data from the photos table and returns it as a list of PhotoModel objects. Here's how it works:

  • It retrieves a reference to the initialized database.

  • It uses the query method to fetch all rows from the photos table, resulting in a list of Map<String, dynamic> objects.

  • It uses List.generate to convert each map into a PhotoModel object by calling the PhotoModel.fromJson constructor.

Creating a Splash Screen in Flutter with Data Fetching and Local Database

In this page, we'll explore a Flutter SplashScreenPage class that serves as an introductory screen for this app. This splash screen is designed to fetch data from a remote API, store it in a local database, and then navigate to another page while displaying an image. Let's dive into the code :

import 'dart:async';
import 'package:flutter/material.dart';
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:rest_api/photo.dart';
import 'datahelper.dart';
import 'model/photomodel.dart';


class SplashScreenPage extends StatefulWidget {
  const SplashScreenPage({super.key});

  @override
  State<SplashScreenPage> createState() => _SplashScreenPageState();

}

class _SplashScreenPageState extends State<SplashScreenPage> {
  final dbHelper = DatabaseHelper();
  late final List<PhotoModel> data = [];

  @override
  void initState() {
    databaseInitialized();
    fetchAndSavePhotos();
    super.initState();
  }

  Future<void> databaseInitialized() async {
    await dbHelper.initializedDB();
  }

  Future fetchAndSavePhotos() async {
    final response = await http
        .get(Uri.parse('https://jsonplaceholder.typicode.com/albums/1/photos'));

    if (response.statusCode == 200) {
      var jsonData = json.decode(response.body);
      final dbHelper = DatabaseHelper();

      for (final item in jsonData) {
        PhotoModel yourData = PhotoModel.fromJson(item);
        await dbHelper.insertData(yourData);
      }

      data.clear();
      data.addAll(await dbHelper.getAllData());

      Navigator.pushReplacement(
        context,
        MaterialPageRoute(builder: (context) => MyHomePage(data : data )),
      );
    } else {
      throw Exception('Failed to local data from the API');
    }

    return data;
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      body: SafeArea(
        top: true,
        child: Container(
          width: MediaQuery.of(context).size.width,
          height: MediaQuery.of(context).size.height * 1,
          color: Colors.black,
          child: Center(
            child: ClipRRect(
              borderRadius: BorderRadius.circular(8),
              child: Image.network(
                'https://wallpapercave.com/wp/wp5443115.jpg',
                fit: BoxFit.scaleDown,
              ),
            ),
          ),
        ),
      ),
    );
  }
}

Imports: The code imports essential packages, including flutter/material.dart for Flutter UI, http for making HTTP requests, and custom files like datahelper.dart and model/photomodel.dart, which contain database and data model logic.

State Class: _SplashScreenPageState is the state class for your SplashScreenPage. It contains the logic for fetching and managing data, as well as a list to store photo data.

initState Method: In the initState method, you perform the following actions:

  • Call databaseInitialized to initialize the local database using DatabaseHelper.

  • Call fetchAndSavePhotos to fetch data from a remote API and save it to the local database.

  • databaseInitialized Method: This method initializes the local database using the DatabaseHelper class. It awaits the completion of the database initialization.

  • fetchAndSavePhotos Method: This asynchronous method fetches data from a remote API and saves it to the local database:

    • It makes an HTTP GET request to the API using the http package.

    • If the response status code is 200 (indicating success), it decodes the JSON response and saves each item to the database using dbHelper.insertData.

    • After saving the data, it clears and populates the data list with the data from the database.

    • Finally, it navigates to another page (MyHomePage) while passing the fetched data.

  • build Method: The build method defines the UI structure of your splash screen. In this case, it displays a black background with a centered image fetched from a URL. This screen will be shown while data is fetched and processed.

Building a Flutter Photo List Page

In this page, we'll explore a Flutter MyHomePage class, which represents a photo list page that displays a list of photos fetched from a local database. Users can tap on a photo to view its details. Let's dissect the code :

import 'package:flutter/material.dart';
import 'package:rest_api/datahelper.dart';
import 'package:rest_api/details.dart';
import 'package:rest_api/model/photomodel.dart';

class MyHomePage extends StatefulWidget {
  final List<PhotoModel> data;
  const MyHomePage({Key? key, required this.data}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final dbHelper = DatabaseHelper();
  bool isLoading = true;


  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('REST API'),
      ),
      body:  ListView.builder(
                itemCount: widget.data.length,
                itemBuilder: (context, index) {
                  return SizedBox(
                    height: 100,
                    child: GestureDetector(
                      onTap: () {
                        Navigator.push(
                          context,
                          MaterialPageRoute(
                            builder: (context) =>
                                DetailsPage( photoModel: widget.data[index],),
                          ),
                        );
                      },
                      child: Card(
                        elevation: 4,
                        child: Row(
                          children: [
                            Image.network(widget.data[index].url.toString(),
                                width: 80, fit: BoxFit.cover),
                            Expanded(
                                child: Container(
                              padding: const EdgeInsets.all(10),
                              child: Column(
                                mainAxisAlignment: MainAxisAlignment.start,
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    widget.data[index].id.toString(),
                                    style: const TextStyle(
                                        fontSize: 15,
                                        fontWeight: FontWeight.bold),
                                  ),
                                  Text(
                                    widget.data[index].title.toString(),
                                    maxLines: 2,
                                    style: const TextStyle(
                                        fontSize: 13,
                                        fontWeight: FontWeight.bold),
                                  ),
                                  Text(
                                    widget.data[index].thumbnailUrl.toString(),
                                    style: const TextStyle(
                                        fontSize: 11,
                                        fontWeight: FontWeight.w400),
                                  ),
                                ],
                              ),
                            ))
                          ],
                        ),
                      ),
                    ),
                  );
                },
              ),
    );
  }
}

Imports: The code includes necessary imports for Flutter (flutter/material.dart) and custom classes like datahelper.dart, details.dart, and model/photomodel.dart. MyHomePage receives a list of PhotoModel objects as its data source.

State Class: _MyHomePageState is the state class for your MyHomePage. It manages the state of the widget.

build Method: The build method defines the UI structure of your MyHomePage widget. Here's how it works:

  • It creates a Scaffold with an AppBar at the top, displaying the title "REST API."

  • The body of the Scaffold is a ListView.builder widget that generates a list of photo items using the data received from the constructor.

  • For each photo item, it creates a Card with an Image and additional details.

  • The GestureDetector allows users to tap on a photo, triggering navigation to the DetailsPage while passing the selected PhotoModel.

Creating a Flutter Details Page for Photo Display

In this page, we'll explore a Flutter DetailsPage class that is responsible for displaying detailed information about a specific photo. This page will show the photo, its ID, title, and thumbnail URL. Let's break down the code :


import 'package:flutter/material.dart';
import 'package:rest_api/model/photomodel.dart';

class DetailsPage extends StatelessWidget {
  final PhotoModel photoModel;

  const DetailsPage({Key? key, required this.photoModel}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Details'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(8.0),
        child: Card(
          elevation: 10,
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              Center(
                child: Card(
                  margin: EdgeInsets.only(top: 30),
                  elevation: 10,
                  child: Image.network(
                    photoModel.url.toString(),
                    width: 300,
                    height: 200,
                    fit: BoxFit.cover,
                  ),
                ),
              ),
              SizedBox(height: 16),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                 photoModel.id.toString(),
                  style: TextStyle(fontSize: 21, fontWeight: FontWeight.bold),
                ),
              ),
              SizedBox(height: 10),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                  photoModel.title.toString(),
                  style: TextStyle(fontSize: 18,fontWeight: FontWeight.bold),
                ),
              ),
              SizedBox(height: 10),
              Padding(
                padding: const EdgeInsets.all(8.0),
                child: Text(
                 photoModel.thumbnailUrl.toString(),
                  style: TextStyle(fontSize: 16,fontWeight: FontWeight.w400),
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

Here's a breakdown of the DetailsPage code:

  • Imports: The code includes necessary imports for Flutter (flutter/material.dart) and PhotoModel for displaying the details of the selected photo.

  • Constructor: The DetailsPage class has a constructor that takes a PhotoModel object as a parameter. This allows you to pass the specific photo's data to the details page when navigating to it.

  • build Method: This method defines the UI structure of the DetailsPage widget. Here's how it works:

    • It creates a Scaffold with an AppBar that displays the title "Details."

    • Inside the Scaffold, there's a Card widget with elevated content for displaying the photo details.

    • The photo is displayed in the center using an Image.network widget. The image URL is obtained from the photoModel.

    • Below the photo, there are three pieces of information displayed: ID, title, and thumbnail URL. Each piece of information is placed in a Text widget with different text styles.

    • The SizedBox widgets add vertical spacing between the elements for better readability.

  • Setting up a Flutter Project with REST API Integration

  • In this article, we will guide you through setting up a Flutter project named "rest_api" for integrating REST API data fetching and displaying it in your app. We'll also include dependencies for HTTP requests, local database storage, and splash screen functionality.

    Here's your pubspec.yaml file with the necessary configurations and dependencies:

  •   name: rest_api
      description: A new Flutter project.
      publish_to: 'none'
    
      version: 1.0.0+1
    
      environment:
        sdk: '>=3.0.5 <4.0.0'
    
      dependencies:
        flutter:
          sdk: flutter
    
        cupertino_icons: ^1.0.2
        http: ^1.1.0       # For making HTTP requests to your API
        sqflite: ^2.3.0    # For local database storage
        path: ^1.8.3       # For handling file paths
        splash_screen_view: ^3.0.0   # For implementing a splash screen
    
      dev_dependencies:
        flutter_test:
          sdk: flutter
    
        flutter_lints: ^2.0.0   # Optional linting rules for your Flutter project
    
      flutter:
        uses-material-design: true