change_emitter
ChangeEmitter is a highly composable, flexible alternative to ChangeNotifier from the Flutter framework that can be used to implement the Observable State Tree pattern which you can read about here. Instead of maintaining a list of callbacks, ChangeEmitters use a stream of Change objects which can contain specific information about changes and are easier to manipulate. Comes with a handful of basic ChangeEmitters that you can combine to create your own state abstractions and a minimalistic reimplementation of the Provider package.
Installation
To use ChangeEmitter, add it to your dependencies in your pubspec.yaml:
dependencies:
change_emitter: ^1.0.0-beta.1
Usage
To get started quickly, please check out the example.
And the API documentation
Built in ChangeEmitters
Comes with ChangeEmitters for basic primitives and a way to compose them.
ValueEmitter:
//ValueEmitters hold simple values
var toggle = ValueEmitter(true);
//All ChangeEmitters expose a stream of changes
var subscription = toggle.changes.listen((change)=>print('toggle switched'));
//set a new value
toggle.value = false; //prints 'toggle switched'
//read the current value
print('current value: ' + toggle.value.toString()); // prints 'current value: false'
//Cancel the stream subscription
subscription.cancel();
//ChangeEmitters need to be disposed when they're no longer needed.
toggle.dispose();
ListEmitter:
//Holds a list of elements
var intList = ListEmitter([1,3,5]);
intList.addAll([2,8,10]);
intList.retainWhere((element)=>element%2==0);
var subscription = intList.notifications.listen((change)=>print('changed'));
//ListEmitter will only emit changes when you call emit but will
//only do so if there has actually been a change.
//This allows you to perform multiple changes to the list before updating your UI
intList.emit(); //prints 'changed'
intList.emit(); //does nothing
subscription.cance();
intList.dispose();
MapEmitter:
//A map implementation
var colorMap = MapEmitter<String,Color>({});
colorMap['red']=Colors.red;
colorMap.emit();
colorMap.dispose();
EmitterContainer:
//EmitterContainer allows you to compose multiple ChangeEmitters into a single unit
//which is also a ChangeEmitter
class TextState extends EmitterContainer {
final text = ValueEmitter('Some text');
final bold = ValueEmitter(false);
final italic = ValueEmitter(false);
final color = ValueEmitter<Color>(Colors.red);
//Override this getter with list of all ChangeEmitters defined in this class.
//This has three functions. First, all of the elements will be disposed
//when this class is disposed (convenient!). Second, this class
//will also emit a change whenever a child changes. Third, children will
//have access to [this] as a parent and be able to use findAncestorOfExactType<T>()
@override
get children => [text, bold, italic, color];
//If you just want a subset of children to notify listeners you can optionally override this getter.
//@override
//get emittingChildren => [text, bold];
}
var myTextState = TextState();
var subscription = myTextState.changes.listen((notif)=>'changed!');
myTestState.italic.value = true; //prints 'changed!'
//We can notify listeners without changing any children:
myTextState.emit(); //prints 'changed!'
//We can also change one of the children's values without causing the container to emit a change.
//The child will still emit a change however:
myTextState.bold.quietSet(true); //nothing printed to the console but will cause listeners of 'bold' to fire
//You can do this for ListEmitter/Map/Containers as well
//someListEmitter.emit(quiet: true);
//dispose of resources.
subscription.cancel();
myTextState.dispose();
Providing and Consuming State
ChangeEmitter comes with a simple reimplementation of the Provider package.
//Povide your state to a part of the widget tree
//This will automatically dispose your state when removed from the widget tree
runApp(
Provider(
state:TextState(),
child: MyApp(),
)
);
class MyApp extends StatelessWidget{
@override
build(){
return Column(
children:[
//You can update your UI based on just a part of your state using a Reprovider
Reprovider<TextState,ValueEmitter<bool>>(
selector: (state) => state.bold
builder: (_, bold){
return Switch(
value: bold.value,
onChange: (newValue) => bold.value = newValue
)
}
),
//Use context extensions to update on any change to your state
Builder(
builder: (builderContext){
var state = builderContext.depend<TextState>()
return Text(
state.text.value,
style: TextStyle(
color: state.color.value,
fontWeight:
state.bold.value ? FontWeight.bold : FontWeight.normal,
fontStyle:
state.italic.value ? FontStyle.italic : FontStyle.normal),
);
}
)
]
);
}
}
Libraries
- change_emitter
- A flexible, composable alternative to ChangeNotifier for Flutter state management.