DEV Community

Cover image for Building a Generic and Performant Networking Layer in Flutter
dklimkin
dklimkin

Posted on

Building a Generic and Performant Networking Layer in Flutter

Implement a high-performant and scalable networking layer

These days it’s hard to find an app that doesn’t perform any API calls. So might be yours. Luckily, Flutter provides packages out of the box to make this process a bit simpler. Examples of those could be easily found on pub.dev: HTTP and DIO.

Essentially, both packages allow you to assemble a network request and then perform it. Leaving to you error handling, JSON parsing, and all other aspects of mobile application networking.

For example, your API call looks like this:

This code is totally fine for a small pet project. But you start noticing inconveniences when the project starts growing at the scale stage.

Specifically:

❌ Duplicate code
❌ Blocking main thread and dropping FPS
❌ Error-prone codebase
❌ More and more time on adding new services and API calls
❌ Unreliable error handling

Image description

Smells bad🤮. Right?

Here we’re going to tackle all these issues and implement high-performant and scalable networking layer. Let’s start from the very top. Ideally, our API call should look very simple:

Image description

Quite nice?

Now let’s start crafting our Networking Layer step by step with a help of one amazing package: Freezed (yet another code generator for unions/pattern-matching and copy)!

Step 0: Design our data model. Let it be something easy to grasp

Step 1: Create a flexible request body

Request body encapsulated data to send along with your API call and could contain JSON object, binary data, text, etc.

Step 2: Create a request

Our request will contain common parameters you might need to serve an API call: type (Get, Post, etc), API path, data to pass as a body, optionally query parameters and headers to override globally specified headers if needed:

Step 3. Create a response

Our response object will contain only raw data received from your API:

Now we are ready to implement the most intriguing part: network service/layer itself!

Step 4. Let’s prototype our service with what we already have

Our service needs a base URL and optionally DIO instance and HTTP headers. It will create a default DIO instance if not provided:

You can add other body types and conversions here so I’m leaving it to you.

Everything is ready to execute our request and handle exceptions:

We can also add a method(s) to simplify authenticated API calls:

Let’s summarise what we’ve got here:

  • API call is prepared with all headers combined and parameters incapsulated
  • API call is executed by DIO package
  • Data returned from DIO is converted to your Model type provided
  • Exceptions are handled and wrapped in a corresponding API response

But all this stuff still happens on main thread. And this is something we are going to fix now by utilizing a powerful yet simple Isolates mechanism in Dart.

Image description

Step 5. Make Networking Layer to be performant!

The way Dart work is organized to pass data between Isolates we need to copy all parameters and then call a globally defined function. We will encapsulate all data in a private PreparedNetworkRequest class:

And now all we need to do is to move request execution call along with exception handling into Isolate function:

And call it in our Network Service:

Image description

Huh! We’re done coding☺️.

Finally, our NetworkService looks like this:

Recap of what we’ve achieved today

✅ No duplicate code
✅ Not blocking main thread and dropping FPS
✅ Error-proof codebase
✅ Minimised time on adding new services and API calls
✅ Structured and strongly-typed error handling

Resources

Concurrency in Dart
Freezed Dart package
Dio Dart package
RealHTTP Modern Networking Layers in iOS Using Async/Await
Full source code available here

To get hand-picked the latest tech stories subscribe on my Telegram Channel where I post daily.

Happy coding!😉

Top comments (0)