A Flutter library built to expose widgets that integrate with state notifier. Built to work with the state_notifier state management packages.
It's very similar to widgets the bloc.
Already exists a package to integrate with the status notifier, called flutter_state_notifier, but flutter_lid covers different cases.
Remember this package is destined to be used together with state_notifier
Let's take a look at how to use LidBuilder to hook up a CounterPage widget to a CounterState.
Add it in your pubspec.yaml:
dependencies:
flutter_lid:
state_notifier:
class CounterState extends StateNotifier<int> {
CounterState() : super(0);
void increment() => state += 1;
void decrement() => state -= 1;
}
void main() => runApp(const LidCounter());
class LidCounter extends StatelessWidget {
const LidCounter({Key key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
class CounterPage extends StatelessWidget {
CounterPage({Key key}) : super(key: key);
final _counter = CounterState();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Lid Counter')),
body: LidBuilder<int>(
stateNotifier: _counter,
builder: (_, count) {
return Center(
child: Text(
'$count',
style: Theme.of(context).textTheme.headline1,
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: FloatingActionButton(
onPressed: _counter.increment,
child: const Icon(Icons.add),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: FloatingActionButton(
onPressed: _counter.decrement,
child: const Icon(Icons.remove),
),
),
],
),
);
}
}
LidBuilder is a Flutter widget which requires a stateNotifier and a builder function. LidBuilder handles building the widget in response to new states. LidBuilder is very similar to StreamBuilder but has a more simple API to reduce the amount of boilerplate code needed. The builder function will potentially be called many times and should be a pure function that returns a widget in response to the state.
See LidListener if you want to "do" anything in response to state changes such as navigation, showing a dialog, etc...
LidBuilder<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
builder: (context, state) {
// return widget here based on State Notifier's state
}
)
For fine-grained control over when the builder function is called buildWhen that can be provided that omitted, it will default true if previous stateis different currentstate, otherwise isfalse.buildWhentakes the previous state and current state and returns a boolean. IfbuildWhenreturns true,builderwill be called withstateand the widget will rebuild. IfbuildWhenreturns false,builderwill not be called withstate` and no rebuild will occur.
buildWhen is only called once for each state change (NOT including initialState).
LidBuilder<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
buildWhen: (previousState, state) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on State Notifier's state
}
)
There is the possibility to animate between state changes.
LidBuilder<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
animate: true, // Setting to `true`, fadeIn animation will be performed between widget changes.
transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder, // Here you can modify the default animation which is FadeIn.
duration: Duration(milliseconds: 300), // Sets the duration of the animation.
builder: (context, state) {
// return widget here based on State Notifier's state
}
)
LidListener is a Flutter widget which takes a LidWidgetListener and requires a stateNotifier and invokes the listener in response to state changes in the state notifier. It should be used for functionality that needs to occur once per state change such as navigation, showing a SnackBar, showing a Dialog, etc...
listener is only called once for each state change (NOT including initialState) unlike builder in LidBuilder and is a void function.
LidListener<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
listener: (context, state) {
// do stuff here based on State Notifier's state
},
child: const SizedBox(),
)
For fine-grained control over when the listener function is called an optional listenWhen can be provided. listenWhen takes the previous state and current state and returns a boolean. If listenWhen returns true, listener will be called with state. If listenWhen returns false, listener will not be called with state.
LidListener<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
listenWhen: (previousState, state) {
// return true/false to determine whether or not
// to call listener with state
},
listener: (context, state) {
// do stuff here based on State Notifier's state
},
child: const SizedBox(),
)
MultiLidListener is a Flutter widget that merges multiple LidListener widgets into one. MultiLidListener improves the readability and eliminates the need to nest multiple LidListeners. By using MultiLidListener we can go from:
LidListener<StateType>(
stateNotifier: stateNotifierA, // provide the state notifier instance
listener: (context, state) {},
child: LidListener<StateType>(
stateNotifier: stateNotifierB, // provide the state notifier instance
listener: (context, state) {},
child: LidListener<StateType>(
stateNotifier: stateNotifierC,// provide the state notifier instance
listener: (context, state) {},
child: ChildA(),
),
),
)
to:
MultiLidListener(
listeners: [
LidListener<StateType>(
stateNotifier: stateNotifierA, // provide the state notifier instance
listener: (context, state) {},
),
LidListener<StateType>(
stateNotifier: stateNotifierB, // provide the state notifier instance
listener: (context, state) {},
),
LidListener<StateType>(
stateNotifier: stateNotifierC, // provide the state notifier instance
listener: (context, state) {},
),
],
child: ChildA(),
)
LidConsumer exposes a builder and listener in order react to new states. LidConsumer is analogous to a nested LidListener and LidBuilder but reduces the amount of boilerplate needed. LidConsumer should only be used when it is necessary to both rebuild UI and execute other reactions to state changes in the state notifier. LidConsumer takes a required LidWidgetBuilder and LidWidgetListener and StateNotifier, an optional LidBuilderCondition, and LidListenerCondition.
LidConsumer<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
listener: (context, state) {
// do stuff here based on State Notifier's state
},
builder: (context, state) {
// return widget here based on State Notifier's state
}
)
An optional listenWhen and buildWhen can be implemented for more granular control over when listener and builder are called. The listenWhen and buildWhen will be invoked on each state change. They each take the previous state and current state and must return a bool which determines whether or not the builder and/or listener function will be invoked. The previous state will be initialized to the state of the state_notifier when the LidConsumer is initialized. listenWhen and buildWhen are optional and if they aren't implemented, they will default to true.
If buildWhen é omitted then, it will default true if previous state is different current state, otherwise is false.
LidConsumer<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
listenWhen: (previous, current) {
// return true/false to determine whether or not
// to invoke listener with state
},
listener: (context, state) {
// do stuff here based on State Notifier's state
},
buildWhen: (previous, current) {
// return true/false to determine whether or not
// to rebuild the widget with state
},
builder: (context, state) {
// return widget here based on State Notifier's state
}
)
There is the possibility to animate between state changes.
LidConsumer<StateType>(
stateNotifier: stateNotifier, // provide the state notifier instance
animate: true, // Setting to `true`, fadeIn animation will be performed between widget changes.
transitionBuilder: AnimatedSwitcher.defaultTransitionBuilder, // Here you can modify the default animation which is FadeIn.
duration: Duration(milliseconds: 300), // Sets the duration of the animation.
listener: (context, state) {
// do stuff here based on State Notifier's state
},
builder: (context, state) {
// return widget here based on State Notifier's state
}
)
LidSelector is a Flutter widget which is analogous to LidBuilder but allows developers to filter updates by selecting a new value based on the current stateNotifier. Unnecessary builds are prevented if the selected value does not change. The selected value must be immutable in order for LidSelector to accurately determine whether builder should be called again.
LidSelector<StateType, SelectedState>(
selector: (state) {
// return selected state based on the provided state.
},
builder: (context, state) {
// return widget here based on the selected state.
},
)
There is the possibility to animate between state changes as LidBuilder.
There are 3 extensions for -> LidBuilder, LidListener and LidConsumer.
It's super simple to use:
// Same as LidBuilder
stateNotifier.toLidBuilder(
buildWhen: (previousState, state) {},
builder: (context, state) {},
);
// Same as LidListener
stateNotifier.toLidListener(
listenWhen: (previousState, state) {},
listener: (context, state) {},
child: const SizedBox(),
);
// Same as LidConsumer
stateNotifier.toLidConsumer(
listenWhen: (previous, current) {},
listener: (context, state) {},
buildWhen: (previous, current) {},
builder: (context, state) {}
);
// Same as LidSelector
stateNotifier.toLidSelector<bool>(
selector: (state) {},
builder: (context, state) {},
),
You liked this package? then give it a star. If you want to help then:
Run this command:
With Flutter:
$ flutter pub add flutter_lid
This will add a line like this to your package's pubspec.yaml (and run an implicit dart pub get):
dependencies:
flutter_lid: ^1.2.0
Alternatively, your editor might support flutter pub get. Check the docs for your editor to learn more.
Now in your Dart code, you can use:
import 'package:flutter_lid/flutter_lid.dart';
import 'package:flutter/material.dart';
import 'package:flutter_lid/flutter_lid.dart';
import 'package:state_notifier/state_notifier.dart';
void main() => runApp(const LidCounter());
/// A [StatelessWidget] which uses:
/// * [state_notifier](https://pub.dev/packages/state_notifier)
/// * [lid](https://pub.dev/packages/lid)
/// to manage the state of a counter.
class LidCounter extends StatelessWidget {
const LidCounter({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return MaterialApp(
home: CounterPage(),
);
}
}
/// A [StatelessWidget] which demonstrates
/// how to consume and interact with a [CounterState].
class CounterPage extends StatelessWidget {
CounterPage({Key? key}) : super(key: key);
final _counter = CounterState();
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Lid Counter')),
body: LidBuilder<int>(
stateNotifier: _counter,
builder: (_, count) {
return Center(
child: Text(
'$count',
style: Theme.of(context).textTheme.headline1,
),
);
},
),
floatingActionButton: Column(
crossAxisAlignment: CrossAxisAlignment.end,
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: FloatingActionButton(
onPressed: () => _counter.increment(),
child: const Icon(Icons.add),
),
),
Padding(
padding: const EdgeInsets.symmetric(vertical: 4.0),
child: FloatingActionButton(
onPressed: () => _counter.decrement(),
child: const Icon(Icons.remove),
),
),
],
),
);
}
}
/// {@template counter_lid}
/// A simple [State Notifier] which manages an `int` as its state
/// and exposes two public methods to [increment] and [decrement]
/// the value of the state.
/// {@endtemplate}
class CounterState extends StateNotifier<int> {
/// {@macro counter_state}
CounterState() : super(0);
/// Increments the state by 1.
void increment() => state += 1;
/// Decrements the state by 1.
void decrement() => state -= 1;
}