cubes 0.4.0 cubes: ^0.4.0 copied to clipboard
Simple State Manager (Focusing on rebuilding only the necessary)
Cubes #
Simple State Manager with dependency injection and no code generation required.
With Cubes, rebuilding only takes place where it is needed!
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`.
// To others objects not primitive remember call `prop.notify();` after modification to notify listeners.
@override
void ready() {
// do anything when view is ready
super.ready();
}
void increment() {
count.value++;
if (count.value == 5) {
onAction({'key': 'param'}); // Method to send anything to view
}
if (count.value == 10) {
onSuccess('Value iguals 10'); // Method to send the success message
}
if (count.value == 50) {
onError('You are clicking too much o.O'); // Method to send the failure message
}
// example apply debounce
runDebounce(
'increment', // identify
() => print(count.value),
duration: Duration(seconds: 1),
);
}
}
- 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());
runApp(MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: Home(),
));
}
- Creating view with
CubeBuilder
:
class Home extends StatelessWidget {
@override
Widget build(BuildContext context) {
return CubeBuilder<CounterCube>(
onSuccess: (cube, text) {
print('onSuccess: $text');
},
onError: (cube, text) {
print('onError: $text');
},
onAction: (cube, data) {
print('onAction: $data');
},
builder: (context, cube) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
cube.count.build<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.
);
},
);
}
}
or use CubeWidget
class Home extends CubeWidget<CounterCube> {
@override
Widget buildView(BuildContext context, CounterCube cube) {
return Scaffold(
appBar: AppBar(
title: Text('Home'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(
'You have pushed the button this many times:',
),
cube.count.build<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.
);
}
@override
void onError(BuildContext context, PokemonCube cube, String text) {
// TODO: implement onError
super.onError(context, cube, text);
}
@override
void onSuccess(BuildContext context, PokemonCube cube, String text) {
// TODO: implement onSuccess
super.onSuccess(context, cube, text);
}
@override
void onAction(BuildContext context, PokemonCube cube, data) {
// TODO: implement onAction
super.onAction(context, cube, data);
}
}
Cube and its dependencies are injected into CubeBuilder and CubeWidget without the need for any extra configuration.
By doing this:
cube.count.build<int>((value) {
return Text(value.toString());
}),
we register by listening to the Observer count
, and every time this variable is changed, the View
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.
Testing #
import 'package:flutter_test/flutter_test.dart';
void main() {
CounterCube cube;
setUp(() {
cube = CounterCube();
});
tearDown(() {
cube?.dispose();
});
test('initial value', () {
expect(cube.count.value, 0);
});
test('increment value', () {
cube.increment();
expect(cube.count.value, 1);
});
test('increment value 3 times', () {
cube.increment();
cube.increment();
cube.increment();
expect(cube.count.value, 3);
});
}
Example with asynchronous call here Example widget test here
Useful extensions #
// BuildContextExtensions
context.goTo(Widget());
context.goToReplacement(Widget());
context.goToAndRemoveUntil(Widget(),RoutePredicate);
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;
context.theme;
context.scaffold;
Useful Widgets #
AnimatedListCube #
This is a version of AnimatedList that simplifies its use for the Cube context.
AnimatedListCube<String>(
itemList: cube.todoList,
itemBuilder: (context, item, animation, type) {
return ScaleTransition(
scale: animation,
child: _buildItem(item),
);
},
)
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:
assets:
- lang/
In your MaterialApp
you can configure the CubesLocalizationDelegate
:
final cubeLocation = CubesLocalizationDelegate(
[
Locale('en', 'US'),
Locale('pt', 'BR'),
],
);
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'My app',
theme: ThemeData(
primarySwatch: Colors.blue,
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 {
@override
T getDependency<T>({String dependencyName}) {
}
@override
void registerDependency<T>(DependencyInjectorBuilder<T> builder, {String dependencyName, bool isSingleton = false}) {
}
@override
void reset() {
}
}
Cubes.instance.customInjector(MyInjector());
Any questions see our example.