ajwah_bloc 1.8.3 ajwah_bloc: ^1.8.3 copied to clipboard
Rx based state management library. Manage your application's states, effects, and actions easy way.
ajwah_bloc #
Rx based state management library. Manage your application's states, effects, and actions easy way.
States #
Every state class must derived from BaseState<T>
class. And it is mandatory to pass the
state name
and initialState
. The BaseState<T>
class has an abstract method Stream<T> mapActionToState(T state, Action action);
. This method should be invoked by sysytem passing current state and action. You should mutate the state based on action.
Example CounterState
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:ajwah_block_examples/actionTypes.dart';
class CounterModel {
int count;
bool isLoading;
CounterModel({this.count, this.isLoading});
copyWith({int count, bool isLoading}) {
return CounterModel(
count: count ?? this.count, isLoading: isLoading ?? this.isLoading);
}
CounterModel.init() : this(count: 10, isLoading: false);
}
class CounterState extends BaseState<CounterModel> {
CounterState() : super(name: 'counter', initialState: CounterModel.init());
Stream<CounterModel> mapActionToState(
CounterModel state, Action action) async* {
switch (action.type) {
case ActionTypes.Inc:
state.count++;
yield state.copyWith(isLoading: false);
break;
case ActionTypes.Dec:
state.count--;
yield state.copyWith(isLoading: false);
break;
case ActionTypes.AsyncInc:
yield state.copyWith(isLoading: true);
yield await getCount(state.count);
break;
default:
yield state;
}
}
Future<CounterModel> getCount(int count) {
return Future.delayed(Duration(milliseconds: 500),
() => CounterModel(count: count + 1, isLoading: false));
}
}
Example TodoState
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:ajwah_block_examples/actionTypes.dart';
import 'package:ajwah_block_examples/todoApi.dart';
class Todo {
final int id;
final String title;
bool completed;
Todo({this.id, this.title, this.completed});
factory Todo.fromJson(dynamic json) {
return Todo(
id: json['id'] as int,
title: json['title'] as String,
completed: json['completed'] as bool);
}
dynamic toJson() {
return {'id': id, 'title': title, 'completed': completed};
}
}
class TodoModel {
String message;
List<Todo> todoList = [];
TodoModel({this.message, this.todoList});
TodoModel copyWith({String message, List<Todo> todoList}) {
return TodoModel(
message: message ?? this.message, todoList: todoList ?? this.todoList);
}
}
class TodoState extends BaseState<TodoModel> {
TodoState()
: super(name: 'todo', initialState: TodoModel(message: '', todoList: []));
Stream<TodoModel> mapActionToState(TodoModel state, Action action) async* {
try {
switch (action.type) {
case ActionTypes.LoadingTodos:
yield state.copyWith(message: 'Loading todos.');
state.todoList = await TodoApi.getTodos();
yield state.copyWith(message: '');
break;
case ActionTypes.AddTodo:
yield state.copyWith(message: 'Adding todo.');
var todo = await TodoApi.addTodo(action.payload);
state.todoList = List.from(state.todoList)..insert(0, todo);
yield state.copyWith(message: '');
break;
case ActionTypes.UpdateTodo:
yield state.copyWith(message: 'Updating todo.');
await TodoApi.updateTodo(action.payload);
state.todoList = List<Todo>.from(state.todoList);
yield state.copyWith(message: '');
break;
case ActionTypes.RemoveTodo:
yield state.copyWith(message: 'Removing todo.');
var todo = await TodoApi.removeTodo(action.payload);
state.todoList =
state.todoList.where((it) => it.id != todo.id).toList();
yield state.copyWith(message: '');
break;
default:
yield state;
}
} catch (err) {
yield state.copyWith(message: err.toString());
}
}
}
Effects #
Every effect class must derived from BaseEffect
class. And it is optional to pass the
effectKey
. But it's mandatory if you want conditionally remove the effects by using
store.removeEffectsByKey('effectKey')
. The BaseEffect
class has one abstract method List<Stream<Action>> registerEffects(Actions action$, Store store$);
. This function should be invoked by system passing reference of Actions and Store classes. Please keep in mind that effects should not work until you register them.
Example
import 'package:ajwah_bloc/ajwah_bloc.dart';
import 'package:rxdart/rxdart.dart';
import '../../actionTypes.dart';
class CounterEffect extends BaseEffect {
Stream<Action> effectForAsyncInc(Actions action$, Store store$) {
return action$
.whereType(ActionTypes.AsyncInc)
.debounceTime(Duration(milliseconds: 500))
.mapTo(Action(type: ActionTypes.Inc));
}
List<Stream<Action>> registerEffects(Actions action$, Store store$) {
return [effectForAsyncInc(action$, store$)];
}
}
Using state in components #
Ajwah provides a comfortable way to use states in components and dispatching actions.
Just call the createStore(states:[], effects:[])
method from main()
function. Now store
instance should be available by the helper function store()
throughout the application.
Note: createStore(...)
method return store instance so that you can make a sate provider class(InheritedWidget) as your convenient.
We can use select
method to get state
data (passing state name): select('counter')
. or select2(...)
.
These methods return Stream<T>
. Now pass this Stream inside a StreamBuilder to make a reactive widget.
Example #
StreamBuilder<CounterModel>(
stream: select<CounterModel>('counter'),
builder:(BuildContext context, AsyncSnapshot<CounterModel> snapshot) {
if (snapshot.data.isLoading) {
return CircularProgressIndicator();
}
return Text(
snapshot.data.count.toString(),
style: Theme.of(context).textTheme.title,
);
},
)
And also for dispatching state's action - we can use dispatch(...)
or store().dispatch(Action(type:'any', payload:any))
method.