This is part two of my series, where we try to improve the user experience by better handling of API calls. It also simplifies the development process, introducing generic mechanisms.
If you haven't read my previous post, you can check it out here. In this article, we will be referring to it.
Start using the database
Currently, our app uses the database to save the data, nothing more. This is wasteful. Initially we can show users the data that was already saved, while at the same time load new data from api. If the user does not have an internet connection, we can present some data, instead of empty screen. It may be enough what they need, like list of favourite movies.
The first idea that we may have is to:
- Return current content of DB
- Load and save new data
- Return updated content from DB
It will work, but it is not the best solution. We would need to coordinate a few method calls to achieve this goal. The better one is to observe the database. Most of DB ORMs support Streams. When using streams our data in the UI will always be up to date. We can imagine a scenario where we want to add a movie to our favorite list. We press the ⭐️, and the list already shows the new state, while we only called method to update a single record. Nothing more. If we used the old technique we would also need to call a method that will update the list. For more operations we can picture how our code will become bloated with update list calls. Working with streams makes our code simpler and reactive.
How our dao will look depends on the library we use, so we’ll skip that. In the repository, we need to add new methods that will map database models into view ones.
Then we create use-cases and implement the connection to the UI which will listen to those streams.
Simple change, but such a great improvement.
Updating loading data in repositories
Until now, we loaded data from API, saved it in the database, and finally returned the list to the UI. This process took quite some time, and the user had to wait a moment to see anything on the screen. Now, we are observing the data, so the results on the screen are immediate (as long as some were loaded earlier). This means that our loading method in the repository does not have to return the list of data to be shown. In the last article, we focused on handling the API result, but we did not inform the user of any of it. Today we will harvest on the results of the hard work we put in last time. As you may remember we created an abstract class Response
that is returned from all of our API calls. When the call succeeds or fails we return the derived class from SuccessfulResponse
or ErrorResponse
respectively. Handling successful responses was already done in the previous article, we will focus on handling the errors this time.
All operations performed in the application, just like in math, end with a result. Let’s create the Result
base class, which will be equivalent to the Response
class used in API calls.
As we can deduce from the code snippet there are two types of Result
classes: successful, and error. We also created an additional DataResult
class if we would like to return some data that is not saved in the database or for another reason. Those two types are pretty simple to understand and use. When we are returning ErrorResult
, we also want to return the cause of this error, so that we can inform the user what went wrong. For that, we created a ErrorCause
class. It is created with the use of freezed so that we can use the when
method to handle all different types of errors.
Now, we are in a good position to update our loading methods from the repository. Currently, they return Future<List<ContentDetailData>>
, as we mentioned before we don't need to return the data to the UI, now we only care about errors, so we can return Future<Result>
.
As we can see, the change was a simple one, but this time we also improved the user experience by returning the cause of the error that we can display using snackbars.
Now, our code handling the result may look like this
As we can see, we can make simple changes to our code to improve the code quality and user experience significantly. There is one more thing that we can do to build better UX. About that we will talk in the next article.
Code with these changes can be seen on GitHub (branch: active_ui)