maac_mvvm 0.2.0+1 maac_mvvm: ^0.2.0+1 copied to clipboard
maac_mvvm is a package supporting easy MVVM pattern implementation without any dependency injection wrapping, allowing freedom of choice.
maac_mvvm is a package that supports simple implementation of the MVVM pattern. The package doesn't wrap any dependency injection inside. With this, you can choose any framework dependency injection you want. It simply has three components: ViewModel, StreamData and ViewModelWidget.
It's simple, clean, and very easy to implement.
Features #
ViewModelWidget #
A place to build UI widgets that the ViewModel is binding to.
ViewModel #
Automatic handling of ViewModel initialization, resumption, pausing, and disposal.
StreamData #
A wrapper of Stream useful to update UI and automatically cancel in the dispose of the ViewModel lifecycle.
Getting Started #
- Install from pub:
flutter pub add maac_mvvm
Usage #
1 - Install package #
flutter pub add maac_mvvm
2 - Create your ViewModel #
The below ViewModel is a simple ViewModel that hold logic increase counter from widget
class ExamplePageViewModel extends ViewModel {
ExamplePageViewModel();
late final _uiState = 0.mutableData(this);
late final uiState = _uiState.streamData;
void increaseCounter() {
_uiState.postValue(_uiState.data + 1);
}
}
3 - Create your Widget bind's with ViewModel #
The ViewModelWidget will only contain two methods: createViewModel and build.
- The build method is where you build the interface
- The createViewModel method is where you initialize the corresponding ViewModel.
We don't need to worry about the other lifecycles of the widget because they will be called automatically corresponding to the ViewModel.
class ExamplePage extends ViewModelWidget<ExamplePageViewModel> {
const ExamplePage({Key? key}) : super(key: key);
@override
ExamplePageViewModel createViewModel(BuildContext context) => ExamplePageViewModel();
@override
Widget build(BuildContext context, ExamplePageViewModel viewModel) {
return BuildYourUiWidget();
}
}
In case the widget has properties passed in and we need to pass them to the ViewModel, we can override awake.
The awake method will be called immediately after the createViewModel method of ViewModelWidget and before the onInitState method of the ViewModel. This will be helpful for setting up the necessary data.
class ExamplePage extends ViewModelWidget<ExamplePageViewModel> {
final int initValue;
const ExamplePage({super.key, required this.initValue});
@override
ExamplePage createViewModel(BuildContext context) => ExamplePageViewModel();
@override
void awake(WrapperContext wrapperContext, ExamplePageViewModel viewModel) => viewModel.setup(initValue);
@override
Widget build(BuildContext context, ExamplePageViewModel viewModel) {
return BuildYourUiWidget();;
}
}
4 - Listen to data changes from ViewModel #
Listen to data changes from ViewModel and update UI with StreamDataConsumer.
A StreamDataConsumer is a widget that listens to changes in a data stream and updates its UI accordingly. It can be used to display data from a ViewModel and update the UI whenever the data changes.
To use a StreamDataConsumer, you first need to create a data stream in your ViewModel. This can be done using a StreamDataViewModel .
Once you have created the data stream, you can pass it to a StreamDataConsumer widget as a parameter. The StreamDataConsumer will then listen to changes in the data stream and update its UI accordingly.
For example, if you have a counter variable in your ViewModel that you want to display in your UI, you can create a StreamDataViewModel for it and pass it to a StreamDataConsumer widget. Whenever the counter changes, the StreamDataConsumer will update its UI to display the new value.
Widget _buildCounterDisplay(ExamplePageViewModel viewModel) {
return Center(
child: StreamDataConsumer<int>(
streamData: viewModel.uiState,
builder: (context, data) {
return Text("You have pressed the button $data times.");
},
),
);
}
Full Example #
class ExamplePage extends ViewModelWidget<ExamplePageViewModel> {
const ExamplePage({Key? key}) : super(key: key);
@override
ExamplePageViewModel createViewModel(BuildContext context) => ExamplePageViewModel();
@override
Widget build(BuildContext context, ExamplePageViewModel viewModel) {
return Scaffold(
body: Column(
children: [
_buildCounterDisplay(viewModel),
_buildButtonPlus(viewModel),
],
),
);
}
Widget _buildButtonPlus(ExamplePageViewModel viewModel) {
return Padding(
padding: const EdgeInsets.all(16),
child: ElevatedButton(
onPressed: () {
viewModel.increaseCounter();
},
child: const Text("+"),
),
);
}
Expanded _buildCounterDisplay(ExamplePageViewModel viewModel) {
return Expanded(
child: Center(
child: StreamDataConsumer<int>(
streamData: viewModel.uiState,
builder: (context, data) {
return Text("You have pressed the button $data times.");
},
),
),
);
}
}
class ExamplePageViewModel extends ViewModel {
ExamplePageViewModel();
late final _uiState = 0.mutableData(this);
late final uiState = _uiState.streamData;
void increaseCounter() {
_uiState.postValue(_uiState.data + 1);
}
}
API: #
ViewModelWidget
awake
createViewModel
build
ViewModel
onInitState
onResume
onPause
onDispose
onApplicationResumed
onApplicationInactive
onApplicationPaused
onApplicationDetached
StreamData
data
asStream
StreamDataViewModel
postValue
setValue
asStream
close
Additional Information #
Any PR is a great help. Thanks