Exploring Flutter Command with RVMS

Exploring Flutter Command with RVMS

Photo by Hannah Joshua on Unsplash

Flutter state management and Architecture is one of the hottest topics in the flutter community, while there is a multitude of options, let’s explore one of the more fairly recent package that takes a different approach than most popular packages.

flutter_command by Thomas Burkhart is a reiteration and a simplified version of the previous package which aimed at the same architecture, rx_command.

Why RVMS?

The most popular architectures for flutter are usually BLoC, MVC, or MVVM. But on second thought, as described by Thomas, these almost feel unintuitive to the way flutter is built. Although it is practically possible to implement them, flutter follows a reactive approach where the UI reacts to the data flow.

BLoC accomplishes this but at the expense of boilerplate code and making the file structure more complex, while MVVM on the other hand is much less intuitive for the reactive nature of flutter as it is more suitable to Native android or Xamarian where the UI elements are in XML and need have a ViewModel Associated with, to represent and update the UI accordingly.

But Flutter widgets are self-responsible and can handle state without the need for any ViewModel, and they do not communicate via bindings like native android does to update its views, flutter widgets always rebuild instead.

The Basics

RVMS (Reactive Views, Managers, Services) is again, a reiteration of the previous architecture, RxVMS, RVMS simplifies the process by removing the Reactive part, streams with ValueNotifiers.

Services

Handle, incoming and outgoing requests with external services like a database, authentication, or geolocation service. They can be classes or interfaces that define the different types of requests needed by the app to fulfill its data requirements. They are not visible to the view and do not change any state.

Managers

Managers are responsible for managing the business logic of the app and communicating incoming data to the Views via state updates. It groups together use cases or logic that multiple connected Views might use. Basically, managers act as the middle man between the Services and Views by sending data and transforming it, if required.

Views

Views are what a user sees on the screen, it describes how UI on the screen should look like, and what layout it should follow. Views consume the incoming data from the managers and send back any interaction that a user makes. In flutter, Views are described using StatefulWidget or a StatelessWidget. Flutter’s declarative UI makes this simple.

RVMS by Thomas BurkhartRVMS by Thomas Burkhart

RVMS with Flutter Command

Let’s see how we can implement RVMS using flutter_command which was specifically built to facilitate this architecture.

We’ll build a classic hacker news app with a couple of simple API calls and views and explore how we can achieve the above architecture.

.
├── home
│   ├── manager
│   │   └── home.manager.dart
│   ├── service
│   │   ├── locator.dart
│   │   └── repo.dart
│   └── views
│       └── home.dart
├── main.dart
└── models
    └── common.dart

The most recommended way to structure your project would be to separate folders by features so only the most relevant code to a particular feature is located in its folder which makes it easy to find and debug code when working on one particular feature.

The Service file has the necessary API calls required to get the hacker news feed, you can take a look at how this is structured here.

Let’s create the Manager which will interact with the service and enable the View to consume its data.

class HomeManager {

Command<void, List<Story>> storiesCommand;
Command<Story, List<Comment>> commentsCommand;
...

Something you might notice here is Command, commands are the building blocks of flutter_command, they are ValueNotifiers that wrap the function which performs some task, and then update the UI depending on the execution state of the function.

HomeManager() {
    storiesCommand = Command.createAsyncNoParam<List<Story>>(topStories, []);
    storiesCommand();

    commentsCommand = Command.createAsync<Story, List<Comment>>(getComments, []);
   ...

We can register our functions in a Command by specifying the type, return type, and initial values. It offers different static factory functions for the different function types you want to wrap

/// if your method is async and has no parameters.
Command.createAsyncNoParam<T>();
/// if your method is sync and has no parameters.
Command.createSyncNoParam<T>();
/// if your method is async with parameters.
Command.createAsync<T>();
/// if your method is async and has parameters.
Command.createSync<T>();

We can execute a command by either calling execute() on it or by calling the command itself as it’s a callable class.

HomeManager() {
...
storiesCommand();
...
}
// or
void fetchComments(Story story) {
  commentsCommand.execute(story);
}

We can now register our Manager and Service as a singleton using GetIt so we can easily access them in our View.

GetIt getIt = GetIt.instance;

void setUp() {
   getIt.registerLazySingleton<Service>(() => Service());
   getIt.registerSingleton<HomeManager>(HomeManager());
}

We can call setup() in our main() a method so the singletons are registered. An advantage of using GetIt over Provider is the global access without needing BuildContext .

Let’s consume the NewsFeed from HackerNews API in our View.

CommandBuilder(
  command: getIt.get<HomeManager>().storiesCommand,
  whileExecuting: (context, comments, _) => Center(
    child: CircularProgressIndicator(),
    ),
   ),
   onError: (context, error, category, _) => Column(
    children: [
    Text('An Error occurred!'),
    Text(error.toString()),
     ],
   ),
  onnData: (context, stories, _) => ListView.builder(
    itemCount: stories.length,
    itemBuilder: (_, index) {
    return ListTile(
      title: Text(stories[index].title),
   ....

We can use the CommandBuilder to build our widget according to the state change during a method call. It takes in the command we want to listen to and provides three attributes to consume, namely :

  • whileExecutingwhich is when the method is awaiting a Future or just execution.

  • onError , which gives us an easy way to display errors that were caught.

  • onData , from which we can obtain the data and display it in our widget, which in this case is the HackerNews Feed.

This same task of consuming the command state could also be accomplished by GetItMixin.

flutter_command along with RVMS gives a new perspective on handling state and architecting apps in flutter. The concept of Command is easier to grasp without overcomplicating that solution, and CommandBuilder provides an easy way to listen to a Command and React according to its updates.

Make sure to give a few claps they're free and try out flutter_command.

If you want the full code, grab it here, and Thank you for reading all the way through!

Check out some of my previous work