view_model 0.4.0-dev.2
view_model: ^0.4.0-dev.2 copied to clipboard
view model for flutter
view_model #
中文文档
I would like to express my sincere gratitude to Miolin for entrusting me with the permissions of the view_model package and transferring its ownership. This support is invaluable, and I'm truly thrilled to drive its continuous development forward. Thank you!
Features #
- Simplicity and Lightweight Design: Boasts a streamlined architecture with minimal resource overhead, ensuring efficient performance.
- Transparent Implementation: Built on
StreamController
andsetState
, its internal logic is straightforward and easily comprehensible, eliminating any hidden complexities. - Automatic Resource Disposal: Resources are automatically released in tandem with the
State
of aStatefulWidget
, simplifying memory management. - Cross - Widget Sharing: Can be shared across multiple
StatefulWidget
s, promoting code reusability and modularity.
Important Note:
ViewModel
is designed to be bound exclusively to theState
of aStatefulWidget
. SinceStatelessWidget
s do not maintain state, they are not compatible with this binding mechanism.
Core Concepts #
- ViewModel: Serves as the central repository for state management. It holds the application state and notifies registered listeners whenever the state undergoes a change.
- ViewModelFactory: Defines the instantiation logic for
ViewModel
s, specifying how they are created and configured. - getViewModel: A utility function used to either create a new
ViewModel
instance or retrieve an existing one, facilitating easy access to view models within the application.
Stateful and Stateless ViewModels #
By default, ViewModel
operates in a stateless mode.
Stateless ViewModel #
- Simplified Approach: Offers a more lightweight alternative without maintaining an internal
state
. - Change Notification: Data changes are communicated to listeners by invoking the
notifyListeners()
method.
Stateful ViewModel #
- State - Centric: Mandatorily holds an internal
state
object. - Immutability Principle: The
state
is designed to be immutable, ensuring data integrity and predictability. - State Updates: State modifications are achieved through the
setState()
method, which triggers a rebuild of the associated widgets.
Step - by - Step Guide to Using ViewModel #
Using the view_model
package is a straightforward process. Follow these four steps:
Add Dependency:
dependencies:
view_model: ^0.4.0
1. Define a State Class (for Stateful ViewModel) #
For stateful view models, start by creating an immutable state class:
class MyState {
final String name;
const MyState({required this.name});
MyState copyWith({String? name}) =>
MyState(
name: name ?? this.name,
);
@override
bool operator ==(Object other) =>
identical(this, other) ||
(other is MyState && runtimeType == other.runtimeType && name == other.name);
@override
int get hashCode => name.hashCode;
@override
String toString() => 'MyState{name: $name}';
}
Pro Tip: If your use case doesn't require managing complex state, you can skip this step and opt for a Stateless ViewModel instead (refer to Step 2).
2. Create a ViewModel #
Extend either ViewModel<T>
for stateless scenarios or StateViewModel
for stateful management.
Example: Stateless ViewModel
import 'package:view_model/view_model.dart';
class MyViewModel extends ViewModel {
String name = "Initial Name";
void updateName(String newName) {
name = newName;
notifyListeners();
}
}
Example: Stateful ViewModel
import 'package:view_model/view_model.dart';
class MyViewModel extends StateViewModel<MyState> {
MyViewModel({required super.state});
void updateName(String newName) {
setState(state.copyWith(name: newName));
}
@override
void dispose() async {
super.dispose();
debugPrint('Disposed MyViewModel: $state');
}
}
3. Implement a ViewModelFactory #
Use a ViewModelFactory
to specify how your ViewModel
should be instantiated:
class MyViewModelFactory with ViewModelFactory<MyViewModel> {
final String initialName;
MyViewModelFactory({this.initialName = ""});
@override
MyViewModel build() => MyViewModel(state: MyState(name: initialName));
// Optional: Enable singleton sharing. Only applicable when key() returns null.
@override
bool singleton() => true;
// Optional: Share ViewModel based on a custom key.
@override
String? key() => initialName;
}
4. Integrate ViewModel into Your Widget #
In a StatefulWidget
, utilize getViewModel
to access the view model:
import 'package:view_model/view_model.dart';
class _MyPageState extends State<MyPage> with ViewModelStateMixin<MyPage> {
MyViewModel get viewModel =>
getViewModel<MyViewModel>(factory: MyViewModelFactory(initialName: "Hello"));
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Text(viewModel.state.name),
),
floatingActionButton: FloatingActionButton(
onPressed: () => viewModel.updateName("New Name"),
child: Icon(Icons.refresh),
),
);
}
}
Note: Additional functionality such as change listening, view model refresh, and cross - page sharing are also supported. Refer to the sections below for more details.
Advanced APIs #
Listening for State Changes #
@override
void initState() {
super.initState();
viewModel.listen(onChanged: (prev, next) {
print('State changed: $prev -> $next');
});
}
Retrieving Existing ViewModels #
Option 1: Use getViewModel
to fetch an existing view model (creates a new one if not found):
MyViewModel get viewModel =>
getViewModel<MyViewModel>(factory: MyViewModelFactory(
key: "my-key",
));
Option 2: Use requireExistingViewModel
to retrieve only existing view models (throws an
exception if not found):
// Find newly created <MyViewModel> instance
MyViewModel get viewModel => requireExistingViewModel<MyViewModel>();
// Find <MyViewModel> instance by key
MyViewModel get viewModel => requireExistingViewModel<MyViewModel>(key: "my-key");
Refreshing ViewModel #
Create a new instance of the view model:
void refresh() {
refreshViewModel(viewModel);
// This will obtain a new instance
viewModel = getViewModel<MyViewModel>(
factory: MyViewModelFactory(
key: "my-key",
),
);
}