Riverpod Samples — I

ryandam
3 min readJan 6, 2022

#1 Overview

This post is about using Riverpod for state management in a simple web application created using Flutter. The application has a Page ‘ARTICLES’ that renders a grid of tiles. Each tile corresponds to an article. Each article has metadata such as Title, Description, tags, like/dislike counts. This metadata is provided by a remote API. Whenever the like/dislike button is pressed, the count is updated at the remote database and latest counts will be fetched (Note that, the articles can be liked / disliked by other users at the same time).

In terms of UI, these are the requirements to be met:

  • When the Articles page is opened, fetch the counts from remote API and use them while rendering the tiles.
  • When a like/dislike button is pressed, rebuild only the article tile that was clicked on. Do not rebuild all the tiles.

#2 Data model

The Data model of each article is shown below.

#3 Service — To fetch metadata from remote API, Update counts, and convert from JSON to Classes

#4 State management

Here, the state being managed is a List of ArticleMetadata. The state is changed in two cases:

  • To fetch all articles, and up to date counts from the api
  • Update the counts for a single article at the remote api, and then fetch the latest counts from it. When it is done, existing State is updated to reflect the latest data.

Note that, the same article might be liked / disliked by multiple users at the same time. So, we should always get the counts from the API. Otherwise, local state does not reflect up-to-date counts.

#5 Which provider should be used?

Using StateNotifierProvider seems to be the right fit for the job in this case.

#6 UI Changes

Requirement #1

go_router is used for navigation.

Which widget to use to get the latest counts whenever articles page is opened?

  • Initially, I was planning to use StatelessWidget. I thought of using Consumer in the build() method. So that, I will have to access the provider via ref and to the state. I could get the notifier using ref.watch(articlesProvider.notifier).getArticles(), so that I can invoke the API. But, the problem is build() is invoked a number of times during UI rendering. Invoking the API in build() is not recommended.
  • My next option was using a StatefulWidget. Again, the API should not be invoked in the build(). But, State object has other methods init(), didChangeDependencies(). so I thought this should work. But, next challenge was How could I watch the provider, so that when the state changes widget can be rebuilt? In other words, How could I have ref inside a stateful widget?
  • After going through the documentation, I read that ConsumerStatefulWidget and ConsumerState are the equivalent of a StatefulWidget with its State, with the difference that the state as a “ref” object. This time, the “ref” isn’t passed as parameter of the build method, but is rather a property of the ConsumerState object.
  • The API call ref.watch(articlesProvider.notifier).getArticles(); is placed in the didChangeDependencies() method. It is invoked when the ArticlesGrid is built, and it invokes the API, fetches the latest counts, and updates the local state.
  • In the build() method, the state is accessed using List<ArticleMetadata> articles = ref.watch(articlesProvider); inside a Consumer widget's builder().

Requirement #2

When like/dislike button is clicked on any article, only that tile should be updated, entire ListView should not be rebuilt. This is achieved using another provider currentArticle and overriding its value with the actual Article when the ListView is built. It is based on the pattern used in this example - todo.

#7 More Articles

--

--