Manage State with Bloc Pattern - Rest API Parse JSON as Model class

Last updated Sep 24, 2021

State management in flutter application is crucial point while developing large scale application. Flutter has different state management techniques but in this post we are covering Bloc pattern to state management while calling the rest api.

Business Logic Component is google provided state management system in flutter abbreviated as Bloc. This bloc pattern encapsulate business logic from UI. Every Bloc component will have three blocs

  • Bloc
  • Event
  • State
Download Source code

 

Flutter REST API with Bloc state management

 

Advantages of Bloc

  • Easy to separate Business Logic from UI
  • Easy to Test application with bloc_test
  • Clear Documentation of Bloc pattern

 

Disadvantages

  • To use bloc we need to write so many dart files and increase boilerplate codes
  • For small application bloc pattern will not suitable

 

In this flutter application we will rest api with bloc state management. Here we are calling the Movie Albums Json API and fetch albums details and render on the UI. While Fetching the data from the network we will manage the state by loading indicator, once we fetched complete data from the API we will show albums data on the list by passing the data by bloc state class.

 

 

Let's get started

Step 1: Create Flutter application

Step 2: Add required dependencies to pubspec.yaml file, here we have used below dependencies

dependencies:
  flutter:
    sdk: flutter
  http: ^0.13.3
  equatable: ^2.0.3
  flutter_bloc: ^7.0.1

 

http: For Call API requests

equatable : To compare objects while using bloc

flutter_bloc: To Manage state with bloc

 

Step 3: Here we are getting the albums so create a data model class with Response data from the api. For this creating the model class we will copy json response and paste in quicktype tool. this will give us model class which will contains the logic to convert json to model object.

our model class will be like this

 

We can parse json data with Model classes. We will pass the json string to json.decode() method then from the model class we will parse it.

import 'dart:convert';

List<Albums> albumsFromJson(String str) => List<Albums>.from(json.decode(str).map((x) => Albums.fromJson(x)));

String albumsToJson(List<Albums> data) => json.encode(List<dynamic>.from(data.map((x) => x.toJson())));

class Albums {
  Albums({
    required this.userId,
    required this.id,
    required this.title,
  });

  late int userId;
  late int id;
  late  String title;

  factory Albums.fromJson(Map<String, dynamic> json) => Albums(
    userId: json["userId"],
    id: json["id"],
    title: json["title"],
  );

  Map<String, dynamic> toJson() => {
    "userId": userId,
    "id": id,
    "title": title,
  };
}

 

Step 4: Create Service Layer to connect server and fetch data from server

Here created two files one is API Service class and other is repository class

service_api.dart file will be like this. We are calling the api with http get method and fetch http response and pass it to model class to create list of albums data

import 'package:flutter_api_with_bloc/model/album_model.dart';
import 'package:http/http.dart' as http;

abstract class ServiceApi{
  Future<List<Albums>>getAlbums();

}

class AlbumService extends ServiceApi{
String BASE_URL="https://jsonplaceholder.typicode.com";
String ALBUMS="/albums";
  @override
  Future<List<Albums>> getAlbums() async{

    try {
      var uri = Uri.parse("https://jsonplaceholder.typicode.com/albums");
      var response = await http.get(
          uri, headers: {"ContentType": "application/json"});
      var albumslist = albumsFromJson(response.body);

      return albumslist;
    } catch(e){

      return List<Albums>.empty();
    }
  }

}

 

 

Step 5: Create bloc

We know that every bloc will contains three class bloc, state and event. let's create these class like below

albums_block.dart

import 'dart:io';

import 'album_state.dart';
import 'package:flutter_api_with_bloc/bloc/album/albums_events.dart';
import 'package:flutter_api_with_bloc/model/album_model.dart';
import 'package:flutter_api_with_bloc/repositories/Albumsrepository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';

class AlbumsBloc extends Bloc<AlbumEvents,AlbumsState>
{

  final Albumsrepository albumsrepository;
   late List<Albums> listAlbums;
  AlbumsBloc({required this.albumsrepository}) : super(AlbumInitialState());

  @override
  Stream<AlbumsState> mapEventToState(AlbumEvents event) async* {

    switch(event)
    {
      case AlbumEvents.fetchAlbums:

        yield  AlbumLoadingState();

        try {
          listAlbums = await albumsrepository.getAlbumsList();

          yield AlbumLoadedState(albums: listAlbums);

        }on SocketException {
          yield AlbumListErrorstate(
            error: ('No Internet'),
          );
        } on HttpException {
          yield AlbumListErrorstate(
            error: ('No Service'),
          );
        } on FormatException {
          yield AlbumListErrorstate(
            error: ('No Format Exception'),
          );
        } catch (e) {
          print(e.toString());
          yield AlbumListErrorstate(
            error: ('Un Known Error ${e.toString()}'),
          );
        }
        break;
    }

  }

}

 

albums_state.dart

here to fetch api data and display we will create below states

AlbumInitialState : Before calling the API

AlbumLoadingState : While calling the API

AlbumLoadedState : After fetch the data from API

AlbumListErrorstate : If we receive error on Data

 

import 'package:equatable/equatable.dart';
import 'package:flutter_api_with_bloc/model/album_model.dart';

abstract class AlbumsState extends Equatable{
  @override
  // TODO: implement props
  List<Object?> get props => [];
}

class AlbumLoadingState extends AlbumsState{ }
class AlbumInitialState extends AlbumsState{ }

class AlbumLoadedState extends AlbumsState{
final List<Albums> albums;
AlbumLoadedState({required this.albums});
}
class AlbumListErrorstate extends AlbumsState{

final error;
AlbumListErrorstate({this.error});
}

 

albums_event.dart

we have only one event, that is fetch albums from server. so we created one event like below

enum AlbumEvents{
  fetchAlbums
}

 

Step 6: We are ready with Bloc and Service layer, now its time to handle UI. To Communicate UI with Service layer we will use BlocProvider This BlockProvider will contains two properties one for create bloc and other is child to pass your widgets

BlocProvider(create: (context)=>AlbumsBloc(albumsrepository: Albumsrepository()),
    child:AlbumClass())

 

Step 7: Create Widget to display albums list

albums.dart

import 'package:flutter/material.dart';
import 'package:flutter_api_with_bloc/bloc/theme/themek_bloc.dart';
import 'package:flutter_api_with_bloc/themes/app_theme.dart';
import '../bloc/album/album_state.dart';
import '../bloc/album/albums_block.dart';
import 'package:flutter_api_with_bloc/bloc/album/albums_events.dart';
import 'package:flutter_api_with_bloc/model/album_model.dart';
import 'package:flutter_api_with_bloc/repositories/Albumsrepository.dart';
import 'package:flutter_bloc/flutter_bloc.dart';


class AlbumClass extends StatefulWidget{
  @override
  State<StatefulWidget> createState() {
    // TODO: implement createState
    return AlbumClassState();
  }

}

class AlbumClassState extends State<AlbumClass>{
bool isdark=true;
  @override
  void initState() {
    // TODO: implement initState
    super.initState();
    loadAlbums();
  }

  loadAlbums() async
  {
    context.read<AlbumsBloc>().add(AlbumEvents.fetchAlbums);
  }
  @override
  Widget build(BuildContext context) {

    return Scaffold(
      appBar: AppBar(actions: [
        Switch(
          value:  isdark,
          onChanged: (val) async {

            _setTheme(val);
            isdark=!isdark;
          },
        )
      ],),
      body: BlocBuilder<AlbumsBloc,AlbumsState>(builder: (BuildContext contex,AlbumsState state){

        if (state is AlbumListErrorstate) {
          final error = state.error;
          String message = '${error.message}\nTap to Retry.';
          return Text(
            message,

          );
        }
        if (state is AlbumLoadedState) {
          List<Albums> albums = state.albums;
          return _list(albums);
        }
        return Center(child: CircularProgressIndicator(),);



      }),
    );
  }
  Widget _list(List<Albums> albums) {
    return ListView.builder(
      itemCount: albums.length,
      itemBuilder: (_, index) {
        Albums album = albums[index];
        return Container(
          padding: EdgeInsets.all(8.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
            Container(
            child: Text(
            album.title,
            style: TextStyle(
              fontSize: 20,
              color: Theme.of(context).textTheme.bodyText1!.color,
            ),
          ),
        ),
              Divider(color: Theme.of(context).textTheme.bodyText1!.color,),
            ],
          ),
        );;
      },
    );

}
  _setTheme(bool darkTheme) async {
    AppTheme selectedTheme =
    darkTheme ? AppTheme.lightTheme : AppTheme.darkTheme;
    print(darkTheme);
    context.read<ThemekBloc>().add(ThemekEvent(appTheme: selectedTheme));
    //Preferences.saveTheme(selectedTheme);
  }

}

 

Step 8: Run application

 

Flutter Bloc Pattern to call rest api

 

 

Conclusion: In this flutter example we covered how to create bloc and fetch data from rest API and parse json data to display it on the UI with manage the state.

 

 

Download Source code

 

Article Contributed By :
https://www.rrtutors.com/site_assets/profile/assets/img/avataaars.svg

7443 Views