flutter_dva 1.0.7
flutter_dva: ^1.0.7 copied to clipboard
A Flutter state management library inspired by dva, built on Redux. Supports models, reducers, async effects, loading plugin, and state persistence.
flutter_dva #
A Flutter state management library inspired by dva, built on top of Redux architecture. It provides a structured and scalable way to manage application state with models, reducers, and effects.
Features #
- Model-based state management — Encapsulate state, reducers, and effects into one unit
- Async effects — Handle side effects like API calls with ease
- Plugin system — Extend functionality with hooks (
onError,onAction,onStateChange, etc.) - Built-in loading plugin — Automatically track loading states of async effects
- State persistence — Built-in Redux persist support using
SharedPreferences - Single store — Global singleton store with namespace-partitioned state
- Type-safe — Full Dart type support for state and actions
Installation #
Add this to your pubspec.yaml:
dependencies:
flutter_dva: ^1.0.4
Quick Start #
1. Define a Model #
A model encapsulates state, reducers (synchronous state updates), and effects (asynchronous operations).
import 'package:flutter_dva/flutter_dva.dart';
class CountState extends DvaState {
CountState({this.count = 0});
num? count;
@override
void fromJson(Map<String, dynamic> json) {
count = json['count'];
}
@override
Map<String, dynamic> toJson() => <String, dynamic>{
'count': count,
};
}
class CounterModel implements Model<CountState> {
@override
String namespace = 'counter';
@override
StoreOfState<CountState> state = StoreOfState<CountState>(state: CountState());
@override
Map<String, ReducerFuction> reducers = {
'add': (StoreOfState<dynamic> lastState, DvaAction action) {
CountState s = lastState.getState() as CountState;
s.count = (s.count ?? 0) + 1;
lastState.setState(s);
return lastState;
},
};
@override
Map<String, EffectFunction> effects = {
'asyncAdd': (DvaAction action, ModelEffects effects) async {
await Future.delayed(const Duration(seconds: 1));
effects.dispatch(DvaAction('add', null));
},
};
}
2. Create the Dva Instance #
import 'package:flutter_dva/flutter_dva.dart';
Dva dva = Dva(DvaOpts(
models: <Model<dynamic>>[
CounterModel(),
],
));
3. Boot the App #
void main() async {
WidgetsFlutterBinding.ensureInitialized();
WidgetCreatorFunction app = await dva.start(() => MyApp(), () async {
// Optional initialization (e.g., state persistence)
});
return runApp(app());
}
4. Connect Widget to State #
Use Connect (an abstract State) to subscribe to state changes:
import 'package:flutter_dva/flutter_dva.dart';
class MyHomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => MyHomePageState();
}
class MyHomePageState extends Connect<MyHomePage> {
void increment() {
props?.dispatch('counter/add');
}
@override
Widget build(BuildContext context) {
// Access state from the global store
num? count = state?.getState('counter')?.count;
return Scaffold(
body: Center(child: Text('$count')),
floatingActionButton: FloatingActionButton(
onPressed: increment,
child: Icon(Icons.add),
),
);
}
}
Core Concepts #
Model #
Each model has:
namespace— Unique key to identify the model's state slicestate— Initial state (StoreOfState<T>)reducers— Synchronous functions that update stateeffects— Async functions for side effects, dispatch actions to reducers
Reducer #
Reducers are pure functions that take the current state and an action, and return a new state:
(StoreOfState<dynamic> lastState, DvaAction action) {
lastState.setState(newValue);
return lastState;
}
Effect #
Effects handle asynchronous operations like HTTP requests. They can dispatch actions to reducers:
(DvaAction action, ModelEffects effects) async {
final data = await fetchData();
effects.dispatch(DvaAction('save', data));
}
Connect #
Connect<T extends StatefulWidget> is a base class that automatically subscribes to the Redux store. State changes trigger setState().
Provider #
A singleton that provides access to the Store instance from anywhere in the app:
Store store = Provider().store!;
await store.dispatch(DvaAction('counter/add', null));
Plugins #
dva-loading #
Automatically tracks loading states for async effects:
Dva dva = Dva(DvaOpts(
models: [...],
));
dva.use(createLoading());
You can then access loading states from the loading namespace:
// Access in your code
bool isGlobalLoading = Provider().store!.getStateByKey<bool>('loading')?.global;
redux-persist #
Persist state using SharedPreferences:
void main() async {
WidgetsFlutterBinding.ensureInitialized();
WidgetCreatorFunction app = await dva.start(() => MyApp(), () async {
final ReduxPersistor persistor = ReduxPersistor(
store: dva.store!,
heartBeat: 1500,
);
await persistor.persist(); // Restore persisted state
}, waitCallabck: true);
return runApp(app());
}
Note: Your state classes should implement
fromJson()andtoJson()for persistence to work.
Hooks #
Customize behavior with lifecycle hooks:
DvaOpts(
models: [...],
onError: (dynamic error, Map<String, dynamic> context) {
print('Error: $error');
},
onAction: (DvaAction action) {
print('Action: ${action.type}');
},
onStateChange: (StoreOfState state, DvaAction action) {
print('State changed: ${state.namespace}');
},
)
API Reference #
| API | Description |
|---|---|
Dva |
Main class, initializes models and creates the Redux store |
DvaOpts |
Configuration options for Dva (models, hooks, initialState) |
Model<T> |
Interface for defining a state model |
Connect<T> |
Base State class that subscribes to store changes |
Provider |
Singleton providing access to the Store |
StoreOfState<T> |
Wrapper around state with update timestamp |
DvaAction |
Action object with type and payload |
DvaReducer<T> |
Reducer implementation specific to a model |
DvaLoadingState |
State class for the loading plugin |
ReduxPersistor |
State persistence manager using SharedPreferences |
createLoading() |
Creates a loading plugin hook |
dvaDispatch(type, payload) |
Global dispatch helper function |
dvaGetStateByKey<T>(key) |
Global state access helper function |
Examples #
Check out the example project for a complete Flutter counter application that demonstrates:
- Model definition with reducers and effects
- State persistence with SharedPreferences
- Widget-to-store connection using
Connect
License #
MIT