Flutter Minimalist: App Account Management Part-2(not Firebase)

Vaygeth (Abdulmohsen)
6 min readOct 24, 2021

Implementing and calling HTTP Requests

Intro

This is Part 2 of the tutorial for Flutter account management where we demonstrated in Part 1 how to set up our app to handle different user sign in states using a mocked API repository .

In this part, we will focus on replacing our mocked API repository with a real API repository that will make http requests with backend services.

What you going to learn in this tutorial

  1. Make use of http requests using dio package
  2. Serialize dart classes/models into json using json_serializable package

What not included in this tutorial

  • The backend/web-service code, the tutorial assumes you have a backend service that can meet our app requirements (sign-in, verifyToken, renewToken repositories)
  • If you want a tutorial on how to implement this in .NET (Core) contact me.

Let’s get going…

Dependencies

First add the following dependencies that we need to fully complete this part

Dio

  • The package that helps us make HTTP requests to our web-services

Json_serializable

  • The package that helps us convert our data models into json or vise versa by adding decorators/annotations into our model classes without doing this work manually

Build_runner (as dev_dependencies)

  • The package is a utility package that helps with many other packages to build its configuration, for example we will use it along with json_serializable to build the annotated data model classes and generate the files that has the code converting our model classes into json.

Generate Serialized Models

Since we intend in this part to replace the mocked api repository in Part 1, and create a repository that makes real HTTP requests, then we need our models that will be sent/requested to the web-services to serialize into JSON objects or data that are returned/responded from the web-services to be deserialized back to our model classes.

So let’s make use of the Json_serializable package to help us to do so by adding annotation into our models and the necessary imports

sign_in.dart

renew_access_token.dart

authentication_data.dart

business_error.dart

Then from the Terminal/CMD/Powershell run the following command to build and generate our auto created file for serialization

Flutter pub run build_runner build

Note if your classes contains errors (beside the toJson & fromJson we added) the above command will not work. Also if you try to rebuild your models in the future by adding new properties or changed files you may want to run the command with the option — delete-conlficting-ouputs to replace old classes like so

Flutter pub run build_runner build — delete-conlficting-ouputs

Result

Now our model classes are generated we can proceed with creating the account API repository that will make HTTP requests to our web-services.

API Repository

Create a file named account_api_repository.dart under lib/repositories/api/ folders (same level as account_repository.dart that was created in Part 1)

and define class named AccountApiRepository that implements AccountRepository abstract class and override the required methods, like so

Since we are going to make HTTP requests to our account web-services, we need to define our URL (Domain Name Server (DNS) or IP) of our web-service on the network, therefore base on where your web-services are you should input your URL and not my local IP since I’m testing this locally on my network (like using emulators)

So create a file named constants.dart under lib/constants/ and add our account service URL.

Note this assumes the account web-services have the same base URL root as I’m using like so

So if your web-services have different base routes (for instance sign-in webservice is https://mydns/api/v1/auth/sign-in while verifyToken web-service lives in another URL like https://mydns/api/v1/verify/verify-token you may want to adjust or add several URLs in the constants files).

Nevertheless here’s how I’m defining it like so

Code reusability

There’s an extra step (optional), to create an account api base class that can hold several properties that may be needed to complete for the most of the API classes (maybe you add new ones for different cases) that share common data, like HTTP headers, time-out durations and services base URLs.

This will help reduce our code redundancy and keep it DRY by our APIs repositories to extend it

So create an abstract class named ApiRepositoryBase in the same file as AccountApiRepository (or you can move it to a different file as you wish) that will contain

  • Base URL, since we are expecting any account related web services have the same base URL as described in constants.dart
  • Default Header about content we expecting which is JSON data
  • Dio object with the above data configured
  • Bearer header (this will be helpful when used to access certain resources that require authorization)
  • Timeout property that notify when our requests expires (currently set to 8 seconds)

AccountApiRepository Implementation

We will make our class to extend the ApiRepositoryBase like so and pass our accountAPIURL_v1 URL we defined in constants.dart as our base URL and change our methods into async methods like so

Methods implementations

signIn()

  • Add routePath into the method parameters if needed, mine is sign-in for the resource URL that expects an HTTP POSToperation for the sign-in web-service
  • POST SignIn data into the HTTP request using our super (ApiRepositoryBase dio object) and convert it into JSON
  • Set timeout for the HTTP request from the superclass timeout property (you can change this property to what duration you prefer)
  • Handle if the response was successful by checking the HTTP response status code (200)
  • Throw DioError exception if the HTTP response status code is not 200 passing it the result options that contains the error details
  • Catch the failed exception and throw the BusinessException by trying to parse the response data into the BusinessError class. Note if the Business Error parsing fails it will throw an exception which can be considered not a business error (i.e service is down, timeout…etc)

verifyToken()

  • Add routePath into the method parameters if needed, mine is verify-token for the resource URL that expects an HTTP GET operation for the verify token web-service
  • Call the web-service using super.dio.get(), with the queryParameter containing our accessToken as follows
  • Handle if the response was successful by checking the HTTP response status code (200) and return true
  • Throw DioError exception if the HTTP response status code is not 200 passing it the result options that contains the error details
  • Catch and throw exception if the HTTP response status code is above 400 (by convention HTTP 4XX error codes) and throw AccessTokenException to handle it from UserAuthenticationCubit notify that the access token was invalid or expired
  • Catch and throw the failed exception was not HTTP status code 4XX and throw the BusinessException by trying to parse the response data into the BusinessError class like we handled in the signIn()

refreshToken()

  • Add routePath into the method parameters if needed, mine is refreshToken for the resource URL that expects an HTTP GET operation for the verify token web-service
  • Call the web-service using super.dio.post(), with the refresh token data passed into the body and converted into JSON object
  • Handle if the response was successful by checking the HTTP response status code (200) and return the data from the web-service by deserializing it into AuthenticationData class
  • Throw DioError exception if the HTTP response status code is not 200 passing it the result options that contains the error details
  • Catch and throw exception if the HTTP response status code is above 400 (by convention HTTP 4XX error codes) and throw RefreshTokenException to handle it from UserAuthenticationCubit notify that the refresh token was invalid or expired
  • Catch and throw the failed exception was not HTTP status code 4XX and throw the BusinessException by trying to parse the response data into the BusinessError class like we handle it previously

Here’s the full code for the account_api_repository.dart with further comments to guide you further

main.dart

Now the final part is go into our main.dart file and replace our FakeApiRepository() that is being passed to our UserAuthenticationCubit() that we created in Part 1

This concludes Part 2 of the series, in the next parts of the series we will implement the user sign up feature like we did for the sign in part.

For the full source code click here

Make sure to follow and clap, much appreciated

--

--

Vaygeth (Abdulmohsen)

I’m a software developer & UI/UX designer who want to share my experience with fellow developers. abdulmohsen.co https://www.patreon.com/vaygeth