Last updated:
0 purchases
jetpack
Jetpack for Flutter #
A set of abstractions, utilities inspired from Android Jetpack 🚀 to help manage state in flutter applications.
Features #
LiveData #
State holder and change notifier, that also allows to read current value.
If you are fully onto Streams and reactive programming, you might not need this. But if you want to write imperative code to update state, this should help.
EventQueue #
For pushing ephemeral state to the UI and clearing off after being handled. Useful for triggering toasts / popups from within ViewModel
ViewModel #
Business logic container that exposes state, event methods to the UI and communicates with the rest of the application
Installation #
$ flutter pub add jetpack
Usage #
Create your ViewModel and expose state using LiveData
import 'package:jetpack/jetpack.dart';
class CounterViewModel extends ViewModel {
final MutableLiveData<int> _counter = MutableLiveData(0);
LiveData<int> get counter => _counter;
void increment() {
_counter.value++;
}
}
copied to clipboard
You can access your CounterViewModel anywhere using BuildContext as described below
@override
Widget build(BuildContext context) {
final CounterViewModel viewModel = context.viewModel();
}
copied to clipboard
And you can consume LiveData using LiveDataBuilder
LiveDataBuilder<int>(
liveData: viewModel.counter,
builder: (BuildContext buildContext, int count) =>
Text('$count'),
)
)
copied to clipboard
And you can pass UI events to ViewModel by just invoking the method on it
FloatingActionButton(
onPressed: viewModel.increment,
//...
)
copied to clipboard
Setup (One time in a flutter project) #
Create a ViewModelFactory for your app
class MyAppViewModelFactory extends ViewModelFactory {
const MyAppViewModelFactory();
@override
T create<T extends ViewModel>() {
if (T == HomeViewModel) {
return HomeViewModel() as T;
}
throw Exception("Unknown ViewModel type");
}
}
copied to clipboard
If you use dependency injection frameworks like get_it, your factory could like this
class MyAppViewModelFactory extends ViewModelFactory {
const MyAppViewModelFactory();
@override
T create<T extends ViewModel>() {
return GetIt.I.get<T>();
}
}
copied to clipboard
Provide your ViewModelFactory at the root of your App
void main() {
const MyAppViewModelFactory viewModelFactory = MyAppViewModelFactory();
runApp(const MyApp(viewModelFactory: viewModelFactory));
}
class MyApp extends StatelessWidget {
final ViewModelFactory viewModelFactory;
const MyApp({super.key, required this.viewModelFactory});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return ViewModelFactoryProvider(
viewModelFactory: viewModelFactory,
child: MaterialApp(
title: 'Flutter App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const HomePage(title: 'Home Page'),
),
);
}
}
copied to clipboard
Create a base widget Page to wrap all page contents with a `ViewModelScope`
abstract class Page extends StatelessWidget {
const Page({super.key});
Widget buildContent(BuildContext context);
@override
Widget build(BuildContext context) {
return ViewModelScope(builder: buildContent);
}
}
copied to clipboard
If you have a base class already for all pages, then wrap the content using ViewModelScope as above
Why another State Management Library? #
These are proven patterns in Android Ecosystem for more than 5 years. They are still intact even after the adoption of a completely new UI framework - Jetpack Compose. These abstractions have been resilient to change because of low coupling and flexibility.
Existing solutions in flutter like bloc, provider etc. limit the logic holders to only emit one stream of state by default, and require extra boiler plate to "select" the pieces of states that the UI would want to react to.
class MyLogicHolder: LogicHolder<StateModel>
copied to clipboard
Sometimes, we want to expose multiple different state streams that are related but change/emit at a different frequency. Exposing them right from the ViewModel without any boilerplate overhead of writing Selectors etc. is very convenient without any cost.
class MyViewModel: ViewModel {
final MutableLiveData<int> _counter = MutableLiveData(0);
final MutableLiveData<boolean> _isModified = MutableLiveData(false);
LiveData<int> get counter => _counter;
LiveData<int> get isModified => _isModified;
void increment() {
_counter.value++;
_isModified.value = true;
}
}
copied to clipboard
This allows us to organize and propagate state the way it is consumed in the UI and minimize unnecessary rebuilding of widgets
You can expose state to the UI using Futures and Streams as well. Your choice.
class ProductViewModel: ViewModel {
//
Future<ProductDetails> productDetails;
Stream<bool> isAddedToCart;
ValueNotifier<bool> isLoading;
}
copied to clipboard
And use FutureBuilder, StreamBuilder and ValueListenableBuilder to listen and update the UI.
And there is no need of creating extra models for communicating UI events to ViewModel. Just call the methods directly.
ElevatedButton(
onPressed: viewModel.increment
//...
)
copied to clipboard
For personal and professional use. You cannot resell or redistribute these repositories in their original state.
There are no reviews.