cubes 0.10.5
cubes: ^0.10.5 copied to clipboard

Flutter Android iOS web

Simple State Manager (Focusing on simplicity and rebuilding only the necessary)

pub package


Cubes #

Simple State Manager with dependency injection and no code generation required.

With Cubes, manage the state of the application in a simple and objective way and reconstructing in your widget tree only where necessary!

MVVM based architecture.

Install #

To use this plugin, add cubes as a dependency in your pubspec.yaml file.

Usage #

  • Creating Cube:

class CounterCube extends Cube {
  final count = ObservableValue<int>(value: 0); // To List use `ObservableList`.

    void onReady(Object arguments) {
      // do anything when view is ready

    void increment() {
      count.modify((value) => value + 1); // or count.update(newValue);

  • Registering Cubes and or dependencies:

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

void main() {
  // register cube
  Cubes.registerDependency((i) => CounterCube());

  // Example register singleton Cube
  // Cubes.registerDependency((i) => CounterCube(),isSingleton: true);

  // Example register repositories or anything
  // Cubes.registerDependency((i) => SingletonRepository(i.getDependency(),isSingleton: true);
  // Cubes.registerDependency((i) => FactoryRepository(i.getDependency());

      title: 'Flutter Demo',
      theme: ThemeData(
        visualDensity: VisualDensity.adaptivePlatformDensity,
      home: Home(),

  • Creating view

class Home extends CubeWidget<CounterCube> {

  Widget buildView(BuildContext context, CounterCube cube) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Home'),
      body: Center(
        child: Column(
          children: <Widget>[
              'You have pushed the button this many times:',
  <int>((value) {
              return Text(value.toString());
      floatingActionButton: FloatingActionButton(
        onPressed: cube.increment,
        tooltip: 'Increment',
        child: Icon(Icons.add),
      ), // This trailing comma makes auto-formatting nicer for build methods.


If you want to use cubes in a StatefulWidget you can use the mixin CubeStateMixin<StatefulWidget,Cube> in the state, see this example, or use CubeBuilder<Cube>.

OBS: Cube and its dependencies are injected into CubeWidget without the need for any extra configuration.

By doing this:<int>((value) {
    return Text(value.toString());

we register by listening to the Observable count, and every time this variable is changed, theView is notified by running the code block again:

  return Text(value.toString());

This guarantees that in the whole widget tree of your screen, only the necessary is rebuilt.

Listening observable variables #

You can listen to observables in two ways, using the extension build as in the example above or using the Observer widget:

Extension 'build' #<int>(
     (value) => Text(value.toString()),                              // Here you build the widget and it will be rebuilt every time the variable is modified and will leave the conditions of `when`.
     animate: true,                                                  // Setting to `true`, fadeIn animation will be performed between widget changes.
     when: (last, next) => last != next,                             // You can decide when rebuild widget using previous and next value. (For a good functioning of this feature use immutable variables)
     transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder,   // Here you can modify the default animation which is FadeIn.
     duration: Duration(milliseconds: 300),                          // Sets the duration of the animation.

Widget CObserver #

  return CObserver<int>(
      observable: cube.count,
      builder: (value)=> Text(value.toString()),
      when: (last, next) => true,
      transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder,
      duration: Duration(milliseconds: 300),

Provider #

To get the Cube by the children of CubeBuilder, CubeWidget you can use Cubes.of<MyCube>(context);

Methods inner Cube #

onAction #

onAction is used to send any type of action or message to a view. You simply create an 'action' extending from CubeAction.

  class NavigationAction extends CubeAction {
      final String route;

  // sending action
  onAction(NavigationAction(route: "/home"));

you will receive this action in the View through the method:

   void onAction(BuildContext context, MyCube cube, CubeAction action) {
     if(action is NavigationAction) Navigator.pushNamed(context, (action as NavigationAction).route);
     super.onAction(context, cube, data);

this will be useful for navigation, to start some more complex animation, among other needs that View has to perform.

runDebounce #

This method will help you to debounce the execution of something.

     'increment', // identify
     ()  => print(count.value),
     duration: Duration(seconds: 1),

listen #

Use to listen ObservableValue.

      // do anything

listenActions #

Use to listen to Action sent to view.

      // do anything

Useful Widgets #

CAnimatedList #

This is a version of AnimatedList that simplifies its use for the Cube context.

    observable: cube.todoList,
    itemBuilder: (context, item, animation, type) {
      return ScaleTransition(
        scale: animation,
        child: _buildItem(item),

Full usage example here.

CFeedBackManager #

Use this widget if you want to reactively control your Dialog, BottomSheet and SnackBar using an ObservableValue.

Creating observable to control:

final bottomSheetControl = ObservableValue<CFeedBackControl<String>(value: CFeedBackControl(data:'test'));
final dialogControl = ObservableValue<CFeedBackControl<String>>(value: CFeedBackControl(data:'test'));
final snackBarControl = ObservableValue<CFeedBackControl<String>>(value: CFeedBackControl());

Now just add the widget to your tree and settings:

   dialogControllers:[  // You can add as many different dialogs as you like
           observable: cube.dialogControl,
           builder: (data, context) {
               return Container(height: 200, child: Center(child: Text('Dialog: $data')));
   bottomSheetControllers: [  // You can add as many different BottomSheets as you like
           observable: cube.bottomSheetControl,
           builder: (data, context) {
               return Container(height: 200, child: Center(child: Text('BottomSheet: $data')));
   snackBarControllers: [
           observable: cube.snackBarControl,
           builder: (data, context) {
               return SnackBar(content: Text(data));
   child: ...

To show or hide:

bottomSheetControl.modify((value) => value.copyWith(show: true)); // or false to hide
dialogControl.modify((value) => value.copyWith(show: true)); // or false to hide
snackBarControl.modify((value) => value.copyWith(show: true, data: 'Success'));

Full usage example here.

CTextFormField #

Widget created to use TextFormField withObservableValue. With it you can work reactively with your TextFormField. Being able to modify and read its value, set error, enable and disable.

 /// code in Cube

 final ObservableValue<CTextFormFieldControl> textFieldControl = ObservableValue(value: CTextFormFieldControl());

 //  textFieldControl.value.text; // get text
 //  textFieldControl.modify((value) => value.copyWith(text: 'New text')); // change text
 //  textFieldControl.modify((value) => value.copyWith(error: 'error example')); // set error
 //  textFieldControl.modify((value) => value.copyWith(enable: true)); // enable or disable
 //  textFieldControl.modify((value) => value.copyWith(obscureText: true)); // enable or disable obscureText

 // code in Widget

   observable: cube.textFieldControl,
   obscureTextButtonConfiguration: CObscureTextButtonConfiguration(  // use to configure the hide and show content icon in case of obscureText = true.
     align: CObscureTextAlign.right,
     iconHide: Icon(Icons.visibility_off_outlined),
     iconShow: Icon(Icons.visibility_outlined),
   decoration: InputDecoration(hintText: 'Type something'),

It is exactly the same as the conventional TextFormField with two more fields, theobservable and obscureTextButtonConfiguration.

Full usage example here.

Internationalization support #

With Cubes you can configure internationalization in your application. in a simple way using .json files.

Using #

Create a folder named lang and put your files with name location. This way:

Add path in your pubspec.yaml:

  # To add assets to your application, add an assets section, like this:
   - lang/

In your MaterialApp you can configure the CubesLocalizationDelegate:

    final cubeLocation = CubesLocalizationDelegate(
        Locale('en', 'US'),
        Locale('pt', 'BR'),

    Widget build(BuildContext context) {
      return MaterialApp(
        title: 'My app',
        theme: ThemeData(
          visualDensity: VisualDensity.adaptivePlatformDensity,
        localizationsDelegates: cubeLocation.delegates, // see here
        supportedLocales: cubeLocation.supportedLocations, // see here
        home: Home(),

Ready!!! Your application already supports internationalization. Bas get the strings as follows:

  String text = Cubes.getString('welcome');

By default, we use get_it to manage dependencies. if you want to use another one you can overwrite the Injector:

  class MyInjector extends Injector {
    T getDependency<T>({String dependencyName}) {

    void registerDependency<T>(DependencyInjectorBuilder<T> builder, {String dependencyName, bool isSingleton = false}) {

    void reset() {


Useful extensions #

    // BuildContextExtensions


    context.mediaQuery; // MediaQuery.of(context);
    context.padding; // MediaQuery.of(context).padding;
    context.viewInsets; // MediaQuery.of(context).viewInsets;

    context.sizeScreen; // MediaQuery.of(context).size;
    context.widthScreen; // MediaQuery.of(context).size.width;
    context.heightScreen; // MediaQuery.of(context).size.height;


Testing #

import 'package:flutter_test/flutter_test.dart';

void main() {
  CounterCube cube;
  setUp(() {
    cube = CounterCube();

  tearDown(() {
  test('initial value', () {
    expect(cube.count.value, 0);

  test('increment value', () {
    expect(cube.count.value, 1);

  test('increment value 3 times', () {
    expect(cube.count.value, 3);

Example with asynchronous call here Example widget test here

Any questions see our example.

pub points

Simple State Manager (Focusing on simplicity and rebuilding only the necessary)

Repository (GitHub)
View/report issues


API reference





flutter, flutter_localizations, get_it


Packages that depend on cubes