0 purchases
view model x
View Model X #
An Android similar state management package which helps to implement MVVM pattern easily.
Note: In Android, ViewModel have an special functionality of keeping the state on configuration change.
The ViewModel of this package is not for that as Flutter Project doesn't need it. It is only for separating the View Logic from UI.
đ LIVE DEMO OF EXAMPLE PROJECT: https://shubham-gupta-16.github.io/view_model_x/
Features #
Simplified đ State Management
Similar code pattern with an Android project đ°
Easy for developers to migrate đŠī¸ from Android to Flutter
Allows developer to work with both Android to Flutter projects with ease đĨ
Easy to implement MVVM pattern đĒ
Package Components #
StateFlow (equivalent to LiveData) âĩ
SharedFlow đ
ViewModel (to separate the view logic from UI like Cubit)
ViewModelProvider
StateFlowBuilder
StateFlowConsumer
StateFlowListener
SharedFlowListener
MultiFlowListener
ChangeNotifierProvider
MultiProvider
Usage #
my_view_model.dart #
class CounterViewModel extends ViewModel {
// initialize StateFlow with initial value 1
final counterStateFlow = StateFlow<int>(1);
// you can also define more the one StateFlow or SharedFlow inside any ViewModel
void increment() {
// by changing the value, listeners notified
counterStateFlow.value = counterStateFlow.value + 1;
}
@override
void dispose() {
// must dispose all the StateFlows and SharedFlows
counterStateFlow.dispose();
}
}
copied to clipboard
main.dart #
void main() => runApp(CounterApp());
class CounterApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
home: ViewModelProvider(
create: (_) => CounterViewModel(),
child: CounterPage(),
),
);
}
}
copied to clipboard
counter_page.dart #
class CounterPage extends StatelessWidget {
const CounterPage({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ViewModel Counter Example')),
body: Center(
// implement StateFlowBuilder to rebuild Text on StateFlow value changed/updated
child: StateFlowBuilder(
// pass your StateFlow
stateFlow: context.vm<CounterViewModel>().counterStateFlow,
builder: (context, value) {
return Text("$value", style: const TextStyle(fontSize: 30));
},
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
// call the increment function which is inside MyViewModel
ViewModelProvider.of<CounterViewModel>(context).increment();
},
child: const Icon(Icons.add),
),
);
}
}
copied to clipboard
Package Components #
ViewModel (Create custom ViewModel class) #
Create your custom View-Model which must extends ViewModel. Declare all your Flows and View related logic inside of it.
Don't forget to dispose all flows inside dispose method of ViewModel.
class CustomViewModel extends ViewModel {
// initialize StateFlow
final myStateFlow = StateFlow<int>(1);
// view related logic here
@override
void dispose() {
// must dispose all flows
myStateFlow.dispose();
}
}
copied to clipboard
PostFrameCallback with ViewModel #
Using PostFrameCallback with ViewModel helps to get onPostFrameCallback event inside ViewModel easily.
class CustomViewModel extends ViewModel with PostFrameCallback {
//...
@override
void onPostFrameCallback(Duration timestamp) {
// do stuffs here
}
}
copied to clipboard
StateFlow #
It stores value and notify listeners whenever it changes. It can change/update the value.
final myStateFlow = StateFlow<int>(1, notifyOnSameValue: true);
copied to clipboard
Here, notifyOnSameValue is optional. If notifyOnSameValue is set to false, whenever you call stateFlow.value = newValue
where newValue is same as current value, it will not notify listeners. by default it is set to true.
To change the value
myStateFlow.value = 5; // listeners were automatically notified
copied to clipboard
To update the value
listStateFlow.update((value) {
value.add(obj);
}); // listeners were automatically notified
copied to clipboard
SharedFlow #
It is used to send data to the listeners. It can emit the value.
final mySharedFlow = SharedFlow<String>();
copied to clipboard
To emit the value
myStateFlow.emit("Hello from ViewModel!"); // listeners were automatically notified
copied to clipboard
Integrate ViewModel Into Flutter Widget #
ViewModelProvider #
ViewModelProvider is used to wrap the widget with your custom ViewModel.
This requires create which accepts custom ViewModel and child Widget.
ViewModelProvider(
create: (context) => counterViewModel, // provide your custom viewModel
child: ChildWidget(),
);
copied to clipboard
Get ViewModel instance inside Widget Tree #
ViewModelProvider.of<CustomViewModel>(context)
copied to clipboard
OR
context.vm<CustomViewModel>()
copied to clipboard
Builder, Listener, and Consumer Flutter Widgets #
StateFlowBuilder #
StateFlowBuilder is used to rebuild the widgets inside of it.
This requires stateFlow to listen on and builder to which rebuilds when the stateFlow's value changed/updated.
StateFlowBuilder(
stateFlow: context.vm<CustomViewModel>().myStateFlow, // pass StateFlow
builder: (context, value) {
return ChildWidget(value); // rebuild the widget with updated/changed value.
},
)
copied to clipboard
Experimental StateFlow.watch(BuildContext) #
We can use watch with any StateFlow which observe the value change and update the ui.
This is similar to Provider's context.watch<MyChangeNotifier>().
final value = context.vm<CustomViewModel>().myStateFlow.watch(context);
copied to clipboard
StateFlowConsumer #
StateFlowConsumer is used to rebuild the widgets inside of it and call the listener.
This requires stateFlow to listen on, builder and listener.
Whenever stateFlow's value changed/updated, builder will rebuild the widgets inside of it and listener will called.
StateFlowConsumer(
stateFlow: ViewModelProvider.of<CustomViewModel>(context).myStateFlow, // pass SharedFlow
listener: (context, value) {
// do stuff here based on value
},
builder: (context, value) {
return ChildWidget(value); // rebuild the widget with updated/changed value.
},
)
copied to clipboard
StateFlowListener #
StateFlowListener is used to catch the change/update value event of a stateFlow.
This requires stateFlow, listener and child.
Whenever stateFlow's value changed/updated, listener will called.
StateFlowListener(
stateFlow: ViewModelProvider.of<CustomViewModel>(context).myStateFlow, // pass StateFlow
listener: (context, value) {
// do stuff here based on value
},
child: ChildWidget(),
)
copied to clipboard
SharedFlowListener #
SharedFlowListener is used to catch the emitted value from sharedFlow.
This requires sharedFlow, listener and child.
Whenever sharedFlow emits a value, listener will called.
SharedFlowListener(
sharedFlow: ViewModelProvider.of<CustomViewModel>(context).mySharedFlow, // pass SharedFlow
listener: (context, value) {
// do stuff here based on value
},
child: ChildWidget(),
)
copied to clipboard
MultiFlowListener #
MultiFlowListener is a Flutter widget that merges multiple SharedFlowListener and StateFlowListener widgets into one.
MultiFlowListener improves the readability and eliminates the need to nest multiple listeners.
By using MultiFlowListener we can go from:
SharedFlowListener(
sharedFlow: context.vm<ViewModelA>().mySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
child: StateFlowListener(
stateFlow: context.vm<ViewModelA>().myStateFlow,
listener: (context, value) {
// do stuff here based on value
},
child: SharedFlowListener(
sharedFlow: context.vm<ViewModelB>().anySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
child: ChildA(),
)
)
)
copied to clipboard
to
MultiFlowListener(
providers: [
SharedFlowListener(
sharedFlow: context.vm<ViewModelA>().mySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
),
StateFlowListener(
stateFlow: context.vm<ViewModelA>().myStateFlow,
listener: (context, value) {
// do stuff here based on value
},
),
SharedFlowListener(
sharedFlow: context.vm<ViewModelB>().anySharedFlow,
listener: (context, value) {
// do stuff here based on value
},
),
],
child: ChildA(),
)
copied to clipboard
Extra Provider #
This package also includes some of Provider components with some modification. These are:
ChangeNotifierProvider #
This will allows to wrap a widget around ChangeNotifier.
ChangeNotifierProvider(
create: (context) => CustomChangeNotifier(),
child: WidgetA(),
)
copied to clipboard
To get the instance of ChangeNotifier or listen for notifyListeners():
ChangeNotifierProvider.of<CustomChangeNotifier>(context, listen: true)
copied to clipboard
If listen is true, the Widget will rebuild on notifyListeners(). This can also be written in simplified way.
If want to listen for notifyListeners(), use:
context.watch<CustomChangeNotifier>()
copied to clipboard
Or if want only instance, use:
context.read<CustomChangeNotifier>()
copied to clipboard
Note: Here context.watch and context.read is modified from provider library. Here, type is restricted to ChangeNotifier.
MultiProvider #
MultiProvider is a Widget that merges multiple ViewModelProvider and ChangeNotifierProvider widgets into one.
MultiProvider improves the readability and eliminates the need to nest multiple widgets.
By using MultiProvider we can go from:
ViewModelProvider(
create: (context) => ViewModelA(),
child: ViewModelProvider(
create: (context) => ViewModelB(),
child: ChangeNotifierProvider(
create: (context) => ChageNotifierA(),
child: ChildA(),
)
)
)
copied to clipboard
to
MultiProvider(
providers: [
ViewModelProvider(
create: (context) => ViewModelA(),
),
ViewModelProvider(
create: (context) => ViewModelB(),
),
ChageNotifierProvider(
create: (context) => ChageNotifierA(),
),
],
child: ChildA(),
)
copied to clipboard
Note: This MultiProvider is different from one in Provider package. This will only accepts ViewModelProvider and ChangeNotifierProvider.
Contributing #
Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.