DEV Community

loading...
Cover image for Flutter: Getting Started with the BLoC Pattern, Streams & HTTP Request\Response

Flutter: Getting Started with the BLoC Pattern, Streams & HTTP Request\Response

Offline Programmer
Full Stack Architect | Problem Solver | Lifelong Learner
・Updated on ・3 min read

I mentioned in my previous post that I am learning Flutter to implement & publish my App KidzTokenz on Apple App Store & so far, I am enjoying the experience, and I signed up for the #30DaysOfFlutter starting on Feb 1

In this post, we are going to follow the BLoC pattern to integrate with the users API on {JSON} Placeholder and build an app to do the following:

  • Use the http package for making the HTTP requests.
  • Wrap the API call with the BLoC pattern.
  • Use Streams to display the users' list & the users' count.

Let's start by creating an App using the command below using Terminal.


flutter create http_for_flutter

Enter fullscreen mode Exit fullscreen mode

Add the http package to the pubspec.yaml file


dependencies:
  flutter:
    sdk: flutter
  http: ^0.12.2

Enter fullscreen mode Exit fullscreen mode

Create the user.dart file below in the (lib\models) folder, we will create the User from the API's json response.


class User {
  final String name;
  final String email;
  final String symbol;
  final String phone;

  User.fromJson(Map<String, dynamic> json)
      : name = json['name'],
        email = json['email'],
        symbol = json['username'].toString().substring(0, 1),
        phone = json['phone'];
}


Enter fullscreen mode Exit fullscreen mode

Create the users_service.dart file below in the (lib\services) folder. We will convert the API's response to a list of users.


import 'package:http/http.dart' as http;
import 'dart:convert' as convert;

import 'package:http_for_flutter/models/user.dart';

class UserService {
  static String _url = 'https://jsonplaceholder.typicode.com/users';
  static Future browse() async {
    List collection;
    List<User> _contacts;
    var response = await http.get(_url);
    if (response.statusCode == 200) {
      collection = convert.jsonDecode(response.body);
      _contacts = collection.map((json) => User.fromJson(json)).toList();
    } else {
      print('Request failed with status: ${response.statusCode}.');
    }

    return _contacts;
  }
}


Enter fullscreen mode Exit fullscreen mode

Create the user_bloc.dart file below in the (lib\bloc) folder. UserBLoC will be the middleman between the source of data (UserService) and the widget.
Note how we are listening to the usersList stream to update the userCounter stream


import 'dart:async';

import 'package:http_for_flutter/models/user.dart';
import 'package:http_for_flutter/services/users_service.dart';

class UserBLoC {
  Stream<List<User>> get usersList async* {
    yield await UserService.browse();
  }

  final StreamController<int> _userCounter = StreamController<int>();

  Stream<int> get userCounter => _userCounter.stream;

  UserBLoC() {
    usersList.listen((list) => _userCounter.add(list.length));
  }
}

Enter fullscreen mode Exit fullscreen mode

We need to update the main.dart to use a StreamBuilder in the AppBar to display the number of users using the (userBLoC.userCounter) stream, and we will use another one in the body to display the list of users using (userBLoC.usersList) stream.


import 'package:flutter/material.dart';
import 'package:http_for_flutter/bloc/user_bloc.dart';
import 'package:http_for_flutter/models/user.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Users List',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        visualDensity: VisualDensity.adaptivePlatformDensity,
      ),
      home: MyHomePage(title: 'Users List Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);
  final String title;
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  UserBLoC userBLoC = new UserBLoC();
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Users'),
        actions: [
          Chip(
            label: StreamBuilder<int>(
                stream: userBLoC.userCounter,
                builder: (context, snapshot) {
                  return Text(
                    (snapshot.data ?? 0).toString(),
                    style: TextStyle(
                        color: Colors.white, fontWeight: FontWeight.bold),
                  );
                }),
            backgroundColor: Colors.red,
          ),
          Padding(
            padding: EdgeInsets.only(right: 16),
          )
        ],
      ),
      body: StreamBuilder(
          stream: userBLoC.usersList,
          builder: (context, snapshot) {
            switch (snapshot.connectionState) {
              case ConnectionState.none:
              case ConnectionState.waiting:
              case ConnectionState.active:
                return Center(child: CircularProgressIndicator());
              case ConnectionState.done:
                if (snapshot.hasError)
                  return Text('There was an error : ${snapshot.error}');
                List<User> users = snapshot.data;

                return ListView.separated(
                  itemCount: users?.length ?? 0,
                  itemBuilder: (BuildContext context, int index) {
                    User _user = users[index];
                    return ListTile(
                      title: Text(_user.name),
                      subtitle: Text(_user.email),
                      leading: CircleAvatar(
                        child: Text(_user.symbol),
                      ),
                    );
                  },
                  separatorBuilder: (context, index) => Divider(),
                );
            }
          }),
    );
  }
}


Enter fullscreen mode Exit fullscreen mode

Run the App and you will get the screen below

Simulator Screen Shot - iPhone 12 Pro Max - 2021-01-27 at 13.40.56

Check the code here

Follow me on Twitter for more tips about #coding, #learning, #technology...etc.

Check my Apps on Google Play

Cover image Mitchell Kmetz on Unsplash

Discussion (0)