Welcome to part 4 of my series on Flutter Architecture:

While the last two posts weren’t specifically aimed at RxVMS, they are a necessary requirement to understand the RxVMS architecture. Today, we turn to the important packages you will need to use RxVMS in your own App.

GetIt: The Fast ServiceLocator

When you recall the diagram picturing the different elements of an RxVMS app…

…you might have wondered how the various views, managers, and services are able to know of each other. More importantly, you may be wondering how one element is able to access another one’s functions.

While there are many different ways to achieve this (e.g. Inherited Widgets, IoC containers, dependency injections…), I personally prefer the use of a service locator. I wrote a whole blog post on the use of my Service Locator GetIt, but I’ll give a short introduction here as well. Basically, you register types or objects in the ServiceLocator (SL) once, and then you can access these objects from anywhere in your app. This is similar to how you’d use a Singleton…but with more flexibility.

Usage

GetIt’s usage is pretty straightforward. At the start of your app, you register the Services or Managers that you will want to access later in other parts of your app. After that, you call the SL to access instances of the registered types.

The nice thing is that you can register an interface or abstract class together with a concrete implementation. When accessing the instance, you always ask for the interface/abstract class type. This makes it easy to switch the implementation by just switching the concrete type at registration time. This allows you to easily switch a real Service for a MockService.

Getting practical

I typically initialize my SL in a file called service_locator.dart , which also contains the global (ambient) variable for the SL. This will be the only global variable in your app. (As GetIt is also a singleton itself you don’t have to use a global variable. To keep the code concise when using GetIt I personally always use a global variable.)

// This is an example of a current project GetIt sl = GetIt.instance; void setUpServiceLocator(ErrorReporter reporter) { // Services // [registerSingleton] registers an instance of an object with the type or a derived-type // of the one you pass as a generic Parameter // sl.get<ErrorReporter>.get() will always return that instance sl.registerSingleton<ErrorReporter>(reporter); // [registerLazySingleton] gets passed a factory function that returns the type or a derived-type // of the one you pass as a generic Parameter // sl.get<ImageService>.get() will call the registered factory function on the first call and store // the returned instance. On any subsequent calls, the stored instance is returned sl.registerLazySingleton<ImageService>(() => ImageServiceImplementation()); sl.registerLazySingleton<MapService>(() => MapServiceImplementation()); // Managers sl.registerSingleton<UserManager>(UserManagerImplementation()); sl.registerLazySingleton<EventManager>(() => EvenManagerImplementation()); sl.registerLazySingleton<ImageManager>(() => ImageManagerImplementation()); sl.registerLazySingleton<AppManager>(() => AppManagerImplementation()); }

Whenever you want to access one of these objects, simply call

RegistrationType object = sl.get<RegistrationType>(); //because GetIt is a callable class, you can write this even shorter: RegistrationType object2 = sl<RegistrationType>();

If this was a bit too quick, please read my blog post for more details.

RxCommand

Now that we are using GetIt to access our Objects from anywhere (including the UI), I want to look at how we can implement handler functions for UI events. The simplest approach would be to add functions to our Manager objects and use them in our Widgets:

class SearchManager { void lookUpZip(String zip); }

Then in the UI:

TextField(onChanged: sl.get<SearchManager>().lookUpZip,)

This would call lookUpZip on every change in the TextField. But how do we display the result? As we try to be reactive, we could add a StreamController to the SearchManager

abstract class SearchManager { Stream<String> get nameOfCity; void lookUpZip(String zip); } class SearchManagerImplementation implements SearchManager{ @override Stream<String> get nameOfCity => cityController.stream; StreamController<String> cityController = new StreamController(); @override Future<void> lookUpZip(String zip) async { var cityName = await sl.get<ZipApiService>().lookUpZip(zip); cityController.add(cityName); } }

Then in the UI we can use:

StreamBuilder<String>( initialData:'' stream: sl.get<SearchManager>().nameOfCity, builder: (context, snapshot) => Text(snapShot.data);

While this approach does work, it isn’t optimal. Issues include:

boilerplate – We must always create a function, a StreamController , and a getter for the Stream if we don’t want to expose the whole StreamController .

– We must always create a function, a , and a getter for the Stream if we don’t want to expose the whole . busy states – While the function is running, what if we want our UI to show a Spinner?

– While the function is running, what if we want our UI to show a Spinner? error handling – What happens if lookUpZip throws an exception?

Sure, we could add more StreamControllers to handle busy states and errors…but this gets tedious if we have to do that manually for every event handler. That’s where the rx_command package comes in handy.

RxCommand solves all of the above issues and more. An RxCommand encapsulates a function (sync or async) and automatically publishes its results on a Stream.

With RxCommand , we could rewrite our manager to:

abstract class SearchManager { RxCommand<String,String> lookUpZipCommand; } class SearchManagerImplementation implements SearchManager{ @override RxCommand<String,String> lookUpZipCommand; SearchManagerImplementation() { lookUpZipCommand = RxCommand.createAsync((zip) => sl.get<ZipApiService>().lookUpZip(zip)); } }

In the UI:

TextField(onChanged: sl.get<SearchManager>().lookUpZipCommand,) ... StreamBuilder<String>( initialData:'' stream: sl.get<SearchManager>().lookUpZipCommand, builder: (context, snapshot) => Text(snapShot.data);

Which is much more concise and readable.

RxCommand in Detail

RxCommand has one input and five output Observables :

canExecuteInput is an optional Observable<bool> which you can pass to the factory function when creating an RxCommand . It signals the RxCommand if it can be executed, depending on the last value it received over this Observable.

is an optional which you can pass to the factory function when creating an . It signals the if it can be executed, depending on the last value it received over this Observable. isExecuting is an Observable<bool> that signals if the command is currently executing its wrapped function. When a command is busy, it cannot be triggered a second time. If you want to display a Spinner while the wrapped function is running, listen to isExecuting .

is an that signals if the command is currently executing its wrapped function. When a command is busy, it cannot be triggered a second time. If you want to display a Spinner while the wrapped function is running, listen to . canExecute is an Observable<bool> that signals if the command can be executed. This synergizes well with a StreamBuilder to change the appearance of a button between enabled/disabled. Its value is actually:

is an that signals if the command can be executed. This synergizes well with a StreamBuilder to change the appearance of a button between enabled/disabled.

Observable<bool> canExecute = Observable.combineLatest2<bool,bool>(canExecuteInput,isExecuting) => canExecuteInput && !isExecuting).distinct.

Which means: It will emit false if isExecuting emits true It will emit true only if isExecuting emits false AND canExecuteInput hasn’t emitted false .

thrownExceptions is an Observable<Exeption> . All thrown exceptions that the wrapped function may throw will be caught and emitted on this Observable. It is ideal to listen to it and display a dialog if an error occurs.

is an . All thrown exceptions that the wrapped function may throw will be caught and emitted on this Observable. It is ideal to listen to it and display a dialog if an error occurs. (the command itself) is actually an Observable. Values returned by the wrapped function will be emitted on that channel, so you can directly pass an RxCommand to a StreamBuilder as a stream parameter.

results contain all command states in one Observable<CommandResult> , where CommandResult is defined as

/// Combined execution state of an `RxCommand` /// Will be issued for any state change of any of the fields /// During normal command execution, you will get this item's listening at the command's [.results] observable. /// 1. If the command was just newly created, you will get `null, false, false` (data, error, isExecuting) /// 2. When calling execute: `null, false, true` /// 3. When exceution finishes: `result, false, false` class CommandResult<T> { final T data; final Exception error; final bool isExecuting; const CommandResult(this.data, this.error, this.isExecuting); bool get hasData => data != null; bool get hasError => error != null; @override bool operator ==(Object other) => other is CommandResult<T> && other.data == data && other.error == error && other.isExecuting == isExecuting; @override int get hashCode => hash3(data.hashCode, error.hashCode, isExecuting.hashCode); @override String toString() { return 'Data: $data - HasError: $hasError - IsExecuting: $isExecuting'; } }

The .results Observable is especially useful if you want to feed the result of a Command directly to a StreamBuilder . This will display different contents depending on the state of the command’s execution, and it works very well with RxLoader from the rx_widgets package. Here is an example of an RxLoader widget that uses the .results Observable:

Expanded( /// RxLoader executes different builders based on the /// state of the Stream<CommandResult> child: RxLoader<List<WeatherEntry>>( spinnerKey: AppKeys.loadingSpinner, radius: 25.0, commandResults: sl.get<AppManager>().updateWeatherCommand.results, /// executed if .hasData == true dataBuilder: (context, data) => WeatherListView(data, key: AppKeys.weatherList), /// executed if .isExceuting == true placeHolderBuilder: (context) => Center( key: AppKeys.loaderPlaceHolder, child: Text("No Data")), /// executed if .hasError == true errorBuilder: (context, ex) => Center( key: AppKeys.loaderError, child: Text("Error: ${ex.toString()}")), ), ),

Creating RxCommands

RxCommands can wrap synchronous and asynchronous functions. Functions that:

Take no parameter and don’t return any value

Take a Parameter and don’t return a result value

Take no Parameter but return a value

Take a parameter and return a value

For the different variations of possible handler functions RxCommand offers several factory methods for synchronous and asynchronous handlers. They look like this.

/// Creates a RxCommand for a synchronous handler function with no parameter and no return type /// `action`: handler function /// `canExecute` : observable that can bve used to enable/diable the command based on some other state change /// if omitted the command can be executed always except it's already executing static RxCommand<void, void> createSyncNoParamNoResult(Action action,[Observable<bool> canExecute])

There are these variants:

static RxCommand<TParam, TResult> createSync<TParam, TResult>(Func1<TParam, TResult> func,... static RxCommand<void, TResult> createSyncNoParam<TResult>(Func<TResult> func,... static RxCommand<TParam, void> createSyncNoResult<TParam>(Action1<TParam> action,... static RxCommand<void, void> createSyncNoParamNoResult(Action action,... static RxCommand<TParam, TResult> createAsync<TParam, TResult>(AsyncFunc1<TParam, TResult> func,... static RxCommand<void, TResult> createAsyncNoParam<TResult>(AsyncFunc<TResult> func,... static RxCommand<TParam, void> createAsyncNoResult<TParam>(AsyncAction1<TParam> action,... static RxCommand<void, void> createAsyncNoParamNoResult(AsyncAction action,...

Even if your wrapped function does not return a value, RxCommand will emit a void value when the function has executed. So you can listen to such a command to react when the function has finished

Accessing the last result

RxCommand.lastResult gives you access to the last successful result value of the commands execution. Which can be uses as initialData of a StreamBuilder.

If you want to get the last result included in the CommandResult events while executing or in case of an error you can pass emitInitialCommandResult=true when creating the command.

If you want to assign an initialValue to .lastResult e.g. if you use it with a StreamBuilder's initialData you can pass it with the initialLastResult parameter when creating the command.

Example – Making Flutter Reactive

The latest version of the https://github.com/escamoteur/making_flutter_reactive- repository was refactored to RxVMS, so now you should have a good example on how to use it.

Because it’s a very simple app, we only need one Manager:

class AppManager { RxCommand<String, List<WeatherEntry>> updateWeatherCommand; RxCommand<bool, bool> switchChangedCommand; RxCommand<String, String> textChangedCommand; AppManager() { // This Command expects a bool value when executed and passes it through on it's // result Observable (stream) switchChangedCommand = RxCommand.createSync<bool, bool>((b) => b); // We pass the result of switchChangedCommand as canExecute Observable to // the updateWeatherCommand updateWeatherCommand = RxCommand.createAsync<String, List<WeatherEntry>>( sl.get<WeatherService>().getWeatherEntriesForCity, canExecute: switchChangedCommand, ); // Will be called on every change of the search field textChangedCommand = RxCommand.createSync<String, String>((s) => s); // When the user starts typing textChangedCommand // Wait for the user to stop typing for 500ms .debounce(new Duration(milliseconds: 500)) // Then call the updateWeatherCommand .listen(updateWeatherCommand); // Update data on startup updateWeatherCommand(''); } }

You can combine different RxCommands together. Notice how switchedChangedCommand is actually the canExecute Observable for updateWeatherCommand .

Now observe how the Manager is used in the UI:

return Scaffold( appBar: AppBar(title: Text("WeatherDemo")), resizeToAvoidBottomPadding: false, body: Column( children: <Widget>[ Padding( padding: const EdgeInsets.all(16.0), child: TextField( key: AppKeys.textField, autocorrect: false, controller: _controller, decoration: InputDecoration( hintText: "Filter cities", ), style: TextStyle( fontSize: 20.0, ), // Here we use the textChangedCommand! onChanged: sl.get<AppManager>().textChangedCommand, ), ), Expanded( /// RxLoader executes different builders based on the /// state of the Stream<CommandResult> child: RxLoader<List<WeatherEntry>>( spinnerKey: AppKeys.loadingSpinner, radius: 25.0, commandResults: sl.get<AppManager>().updateWeatherCommand.results, dataBuilder: (context, data) => WeatherListView(data, key: AppKeys.weatherList), placeHolderBuilder: (context) => Center(key: AppKeys.loaderPlaceHolder, child: Text("No Data")), errorBuilder: (context, ex) => Center(key: AppKeys.loaderError, child: Text("Error: ${ex.toString()}")), ), ), Padding( padding: const EdgeInsets.all(8.0), child: Row( children: <Widget>[ /// Building the Updatebutton depending on updateWeatherCommand.canExecute Expanded( // This might be solved with a Streambuilder too but it should show `WidgetSelector` child: WidgetSelector( buildEvents: sl .get<AppManager>() .updateWeatherCommand .canExecute, //We access our ViewModel through the inherited Widget onTrue: RaisedButton( key: AppKeys.updateButtonEnabled, child: Text("Update"), onPressed: () { _controller.clear(); sl.get<AppManager>().updateWeatherCommand(); }, ), onFalse: RaisedButton( key: AppKeys.updateButtonDisabled, child: Text("Please Wait"), onPressed: null, ), ), ), // This Switch toggles the canExecuteInput of the updadateWeatherCommand StateFullSwitch( state: true, onChanged: sl.get<AppManager>().switchChangedCommand, ), ], ), ), ], ), );

Typical usage patterns

We have already seen one way to react to the different states of a command using CommandResults . In cases where we want to display if the command has succeeded (but not display the result), a common pattern is to listen to the command’s Observables in the initState function of a StatefulWidget . Here is an example of a real project.

createEventCommand is defined as:

RxCommand<Event, void> createEventCommand;

It will create an Event object in the database and return no real value. But as we have learned before, even an RxCommand with return type void will emit one data item on completion. So we can use this behaviour to trigger an action in our App as soon as the command completes:

@override void initState() { // this subscription just waits for the completion of the command and then pops the page and shows a Toast _eventCommandSubscription = _createCommand.listen((_) async { Navigator.pop(context); await showToast('Event saved'); }); // react on any error while creating an event _errorSubscription = _createEventCommand.thrownExceptions.listen((ex) async { await sl.get<ErrorReporter>().logException(ex); await showMessageDialog(context, 'There was a problem saving event', ex.toString()); }); }

Important: we have to cancel this subscriptions when we don’t need them anymore:

@override void dispose() { _eventCommandSubscription?.cancel(); _errorSubscription?.cancel(); super.dispose(); }

Also, if you want to use an overlay to display a busy spinner, you could:

listen to the isExceuting Observable of a command in the initState function;

Observable of a command in the function; show/hide the spinner in the subscription; and

use the Command itself as the data source for a StreamBuilder

Making life easier with RxCommandListeners

If you want to react to more than one Observable, you may have to manage multiple subscriptions. Directly managing the listening and freeing of multiple subscriptions can be challenging, makes the code less readable, and puts you at risk for making mistakes (e.g. forgetting to cancel during dispose ).

The latest version of rx_command added a helper class called RxCommandListener , which is designed to makes this handling easier. Its constructor takes a command and direct handler functions for the different state changes:

class RxCommandListener<TParam, TResult> { final RxCommand<TParam, TResult> command; // Is called on every emitted value of the command final void Function(TResult value) onValue; // Is called when isExceuting changes final void Function(bool isBusy) onIsBusyChange; // Is called on exceptions in the wrapped command function final void Function(Exception ex) onError; // Is called when canExecute changes final void Function(bool state) onCanExecuteChange; // is called with the vealue of the .results Observable of the command final void Function(CommandResult<TResult> result) onResult; // to make the handling of busy states even easier these are called on their respective states final void Function() onIsBusy; final void Function() onNotBusy; // optional you can directly pass in a debounce duration for the values of the command final Duration debounceDuration; RxCommandListener(this.command,{ this.onValue, this.onIsBusyChange, this.onIsBusy, this.onNotBusy, this.onError, this.onCanExecuteChange, this.onResult, this.debounceDuration,} ) void dispose();

You don’t have to pass all of the handler functions. They all are optional, so you can just pass the ones you need. You only have to dispose the RxCommandListener in your dispose function, and it will cancel all of the internally-used subscriptions.

Let’s compare the same code with and without RxCommandListener in another real app example. The selectAndUploadImageCommand here is used in a chat screen where the user can upload images to the chat. When the command is called:

An ImagePicker dialog is shown.

dialog is shown. After an image has been selected, the image is uploaded.

Once upload is complete, the command returns the storage location of the image so that a new image chat entry can be created.

Without RxCommandListener :

_selectImageCommandSubscription = sl .get<ImageManager>() .selectAndUploadImageCommand .listen((imageLocation) async { if (imageLocation == null) return; // this calls the execute method of the command sl.get<EventManager>().createChatEntryCommand(new ChatEntry( event: widget.event, isImage: true, content: imageLocation.downloadUrl, )); }); _selectImageIsExecutingSubscription = sl .get<ImageManager>() .selectAndUploadImageCommand .isExecuting .listen((busy) { if (busy) { MySpinner.show(context); } else { MySpinner.hide(); } }); _selectImageErrorSubscription = sl .get<ImageManager>() .selectAndUploadImageCommand .thrownExceptions .listen((ex) => showMessageDialog(context, 'Upload problem', "We cannot upload your selected image at the moment. Please check your internet connection"));

With RxCommandListener :

selectImageListener = RxCommandListener( command: sl.get<ImageManager>().selectAndUploadImageCommand, onValue: (imageLocation) async { if (imageLocation == null) return; sl.get<EventManager>().createChatEntryCommand(new ChatEntry( event: widget.event, isImage: true, content: imageLocation.downloadUrl, )); }, onIsBusy: () => MySpinner.show(context), onNotBusy: MySpinner.hide, onError: (ex) => showMessageDialog(context, 'Upload problem', "We cannot upload your selected image at the moment. Please check your internet connection"));

As a rule of thumb, I would only use an RxCommandListener if I wanted to listen to more than one observable.

Give RxCommands a try and see how it can make your live easier. You don’t need to use RxVMS to benefit from the power of RxCommands.

For additional information about RxCommand read the readme of the rx_command package.

Unfortunately I’m pretty busy in a client project at the moment so that I can’t say when the next post will be ready, so please have a bit patience.

Contact me: