Building an end-to-end system in dart using grpc* & flutter(Part 2) using the riverpod library.

bett dougie
6 min readAug 26, 2021

--

First of all, my apologies for delaying to write part 2. I wanted to continue writing here, but couldn’t write another article without completing part 2 as I promised.

Should I blame my failure to write more on the impostor syndrome, laziness, motivation, fear,etc? …I’m not really sure which but a combination of those.

The other reason I couldn’t write was my indecision on which state management solution library to use in the demo that would make it pretty easy for a beginner to understand and make the article short enough, till I played around with riverpod for a while.

In part 1 of this tutorial series, we created our hospital service in gRPC. We saw how to implement a gRPC server in dart. In this part, we’ll create a mobile app to interact with the hospitals server with flutter using riverpod as our state manager. If you haven’t looked at it, I highly recommend looking into it here. It’s really easy and straight foward.

Introducing riverpod.

Riverpod is a rewrite* of the famous provider package which aims to bring type-safety when accessing your providers, while adding syntactic sugar, fun and ease to state management of the status of your app. I highly recommend looking into the documentation to learn more.

In riverpod, we define our providers globally and call them elsewhere in our programs. This in turn makes accessing our providers state safe at runtime.

Riverpod provides a couple of default providers to handle various scenarios such as futures(FutureProvider), streams(StreamProvider), ChangeNotifiers(ChangeNotifierProvider), StateNotifiers(StateNotifierProvider) etc.

In this article, we are going to aim at displaying all the hospitals and implementing an auto-complete suggestions as the user is typing. We’ll use FutureProvider to fetch and display all hospitals and StreamProvider to implement auto-complete search functionality.

All Hospitals Page

Handling Futures using riverpod.

In flutter, the default way of doing handling a future in flutter is through using a FutureBuilder. But if you’ve used it for a while, you’ll know it’s not really the best way to handle futures. Implementing retries is difficult, you’ll need many if’s which clutter up your code plus many more.

Dart futures have three states. [Loading, Loaded with value or it completed with an error]. When we read a defined FutureProvider, it enables us to render or do something based on the state of the future functionally. Riverpod comes with a provider called FutureProvider. This allows us to watch the state of a future and handles the state of the future for us. When we make a network call, read data from a database or do any IO, we’ll normally need to wait for some future to complete using await, then figure out what to do with the data once the future has completed. In our case, we’ll be getting the data from our API endpoint which returns a list of hospitals.

Code to fetch list of all hospitals from the hospitals endpoint.

To get allHospitals, we use the generated HospitalStub class to talk to our api by calling the getHospitals function which is a future. We’ll need to show a loading widget when loading, a button with retry functionality when an error occurs and render the list of hospitals when the call to the server completes successfully.

Creating a FutureProvider is as simple as shown below.

This is how we create a FutureProvider

In order to listen to the current state our future, we’ll use the consumer widget which gives us access to a provider watcher called ScopedReader with which we can use to access the current state of our provider.

The result of watching a FutureProvider is an AsyncValue which is a union type which gives us a way to functionally access the different states of the Future as shown below using when. So when it’s loading, there’s nothing to be accessed, when it’s completed well, we get access to the data function which gives us access to the data which the future completed with, and when the future completes with an error, it gives us access to the error(err) and stackTrace behind the error. I don’t know about you, but this is 100x better than flutter’s default FutureBuilder and simple enough for anyone to read.

It also looks neat, doesn’t it?

How to map the state of the widget to a widget. Looks neat isn’t it?

AsyncValue exposes functional ways to access the future’s states which you can then map over/transform to widgets and depending on the state of the future. I prefer this to using if statements. It also saves you many errors which you could have made. These come from the fantastic freezed library. Check out freezed library to see how you can create your own union types.

In our case, we just need to show a progress loading indicator when loading, render hospitals in a list view after we have the hospitals data and show text error in case of an error.

In the end we have an app that looks like the one below.

Hospitals Real-time Search Feature

For this part, let’s aim is to implement a realtime search where every name you search for, you get back realtime results. We’ll use streams to implement this feature.

We’re going to use a StreamController for this purpose to transform search inputs to List of Hospitals which are returned by the search endpoint we have. We are going to use the Stream’s asyncMap function which transforms a stream type into a future which on completion, yields the value of the Future or Error if an error happened. The asyncMap method’s syntax looks like below.

What it simply does is convert our stream of type T to the return type of the Function which is E in that case. In our case we’ll convert the strings being typed by the user to List<Hospitals> which are filtered by our Hospital Server.

Remember code to search for hospitals from our endpoint looks like this

In our case, we’re going to transform the value input in the TextField and convert it using asyncMap using the _searchHospital query.

To begin, we’ll create a StreamController of type string which we’ll use to add the values typed by the user i.e the hospitals he wants to search. The StreamController gives us access to a stream value which we can use to transform to a Stream of Hospitals.

Realtime search logic

What we’ll end up doing is creating a TextField and listening to onChanged and adding the values to our SearchAhead class using add(String). Every time a value is added to our StreamController, the asyncMap is called, a response or error is added to the stream which we can listen to using freezed’s StreamProvider.

To complete our search functionality, we’ll need to provide the SearchAhead class using Provider class. Think of this as Dependency Injection. Then we’ll watch the SearchAhead provider and use StreamProvider to listen to the changes on exposed hospitalStream in the SearchAhead class.

Provide the SearchAhead class. Read the provided SearchAhead and access the hospitalsStream.

In our UI, we’ll use the ConsumerWidget from flutter_riverpod to access the searchAheadProvider so that we can add values being typed from the TextField by listening to the onChanged closure to our streamController.

We’ll also use the watch method to listen to the resulting hospitalStream and updating our UI when there’s new data/error/loading as we did above.

And behold the widget that handles our UI. Looks neat isn’t it?

Since using riverpod, I think the watch method is a killer tool. It’s the Swiss army knife of providers. Thanks to Remi for creating this awesome package.

The code for the server and the app is found in the repo https://github.com/bettdouglas/hospitals.git

--

--

bett dougie
bett dougie

Written by bett dougie

Software Developer in code wanderlust. Interested in Geospatial Systems and Mobile Development

No responses yet