Skip to main content

Dice

In this example, we will build a fun dice app. You can make the dice roll at the tap of a button and a random count is generated with every roll of dice.

info

See the complete code here.

Store setup​

The DiceCounter store is quite simple. It keeps a track of dice values and the total count. Below is how it looks:

  • Generate dice_counter.g.dart file using build_runner and this must be imported into the store. Read more about build_runner in the MobX Code Generation library, pub.
import 'dart:math';
import 'package:mobx/mobx.dart';

part 'dice_counter.g.dart';

class DiceCounter = _DiceCounter with _$DiceCounter;

abstract class _DiceCounter with Store {

int left = Random().nextInt(6) + 1;


int right = Random().nextInt(6) + 1;


int total;


void roll() {
left = Random().nextInt(6) + 1;
right = Random().nextInt(6) + 1;
total = left + right;
}
}

  • In the above store left and right are the dice counts defined with @observable annotations and are initialised with a random number ranging from 1 - 6.
  • total is also an observable which keeps track of the total count of the dice.
  • The action-method roll() defined with @action annotation is used to update the dice counts every time user taps on the dice.

With the above implementation I was able to keep a track of the left, right and total counts with every user interaction as expected or so I thought 🤔.

Discovering @computed​

I ran into an issue... The total count was null for the very first time. It gets updated only when the action-method roll() is called.

In search of the solution I went through the documentation and few examples and realised there is an insanely easy way to get around this issue. @computed BOOM! my issue is solved.

I have modified the above DiceCounter implementation using @computed.

import 'dart:math';
import 'package:mobx/mobx.dart';

part 'dice_counter.g.dart';

class DiceCounter = _DiceCounter with _$DiceCounter;

abstract class _DiceCounter with Store {

int left = Random().nextInt(6) + 1;


int right = Random().nextInt(6) + 1;


int get total => left + right;


void roll() {
left = Random().nextInt(6) + 1;
right = Random().nextInt(6) + 1;
}
}

  • Now total is a computed observable annotated with @computed. Computed observables are in-sync every time left or right count is updated.
  • The value of total is automatically updated when the instance of the store is created so I no longer have the null value when I load the app.
  • @computed does more than that. Now, I do not have to write any additional code like the above in the action-method to update it.

It exactly does what the author of mobx quoted... "What can be derived, should be derived. Automatically".

Integrating the Store with the View​

Now that the DiceCounter store is ready, it's time to add it to the Widget to see the magic happen. Let's create an instance of our store:

import 'package:flutter/material.dart';
import 'package:flutter_mobx/flutter_mobx.dart';

import 'dice_counter.dart';

final diceCounter = DiceCounter();

The observables and actions from the store can be accessed via the newly created instance diceCounter along with the Observer widget as shown below:

class DiceView extends StatelessWidget {

Widget build(BuildContext context) {
final diceCounter = Provider.of<DiceCounter>(context);

return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: TextButton(
child: Observer(
builder: (_) =>
Image.asset('images/dice${diceCounter.left}.png'),
),
onPressed: diceCounter.roll,
),
),
Expanded(
child: TextButton(
child: Observer(
builder: (_) =>
Image.asset('images/dice${diceCounter.right}.png'),
),
onPressed: diceCounter.roll,
),
),
],
),
Padding(
padding: const EdgeInsets.all(16),
child: Observer(
builder: (_) => Text(
'Total ${diceCounter.total}',
style: const TextStyle(
fontWeight: FontWeight.bold,
color: Colors.black87,
fontSize: 16,
fontFamily: 'Verdana'),
),
),
),
],
),
);
}
}

Summary​

The working example will be as seen in the figure below:

info

See the complete code here.