MVC pattern for flutter. Works as state management, dependency injection and service locator.
Here's a diagram describing the flow between the state (model
), widget (view
) and the logic (controller
):
Both MomentumController
and MomentumModel
are abstract classes that needs to be implemented. A pair of model and controller is called a component. MomentumBuilder
is simply a widget. This is used to listen to controllers for rebuilds and accessing models to display their values.
If you want to see a full code example that runs. Visit the example tab for more details or you can visit the official webpage. Otherwise, if you only want to see a glimpse of how momentum works, read the Overview and FAQs below.
Advance Example: Listify (clone the repo and run the app, requires Flutter 2.0.0)
MomentumModel - the data or state. Must be Immutable.
class ProfileModel extends MomentumModel<ProfileController> {
// ...
final int userId;
final String username;
// ...
}
MomentumBuilder - the view or widget to display the state.
MomentumBuilder(
controllers: [ProfileController], /// injects both `ProfileController` and `ProfileModel`.
builder: (context, snapshot) {
var profileState = snapshot<ProfileModel>(); /// grab the `ProfileModel` using snapshot.
var username = profileState.username;
return // some widgets here ...
}
)
MomentumController - the logic to manipulate the model or state.
class ProfileController extends MomentumController<ProfileModel> {
// ...
Future<void> loadProfile() async {
var profile = await http.get(...);
// update the model's properties.
model.update(
userId: profile.userId,
username: profile.username,
);
}
// ...
}
Calling model.update(...)
from inside the controller rebuilds all the MomentumBuilder
s that are listening to it.
It is automatically provided by MomentumController
for you to use. Inside a controller class, you can access it directly. It's never null.
By implementing the T init()
method which is required by MomentumController. Like this:
class ShopController extends MomentumController<ShopModel> {
@override
ShopModel init() {
return ShopModel(
this, // required
shopList: [],
productList: [],
);
}
}
Of course. The model object is already provided by MomentumController meaning you can also directly access its properties like this:
class ShopController extends MomentumController<ShopModel> {
bool hasProducts() {
return model.productList.isNotEmpty;
}
}
Yes, definitely. This is the required setup for Momentum in a flutter app:
void main() {
runApp(momentum());
}
Momentum momentum() {
return Momentum(
child: MyApp(),
controllers: [
ProfileController(),
ShopController(),
],
// and more optional parameters here.
);
}
Momentum is highly testable. This is how a basic widget testing for momentum would look like:
void main() {
testWidgets('should display username', (tester) async {
var profileCtrl = ProfileController();
await tester.pumpWidget(
Momentum(
child: MyApp(),
controllers: [profileCtrl],
),
);
await tester.pumpAndSettle();
profileCtrl.updateUsername("johndoe");
await tester.pumpAndSettle(); // ensure rebuilds
expect(profileCtrl.model.username, "johndoe"); // unit check
expect(find.text("johndoe"), findsOneWidget); // widget check
});
}
Or you might not be a fan of widget testing and only want to test your components:
void main() {
test('should display username', () async {
var profileCtrl = ProfileController();
var tester = MomentumTester(
Momentum(
controllers: [profileCtrl],
),
);
await tester.init();
profileCtrl.updateUsername("johndoe");
expect(profileCtrl.model.username, "johndoe"); // unit check
});
}
- Routing - Navigation system that supports persistence. The app will open the page where the user left off.
- Event System - For showing dialogs, prompts, navigation, alerts.
- Persistence State - Restore state when the app opens again.
- Testing - Tests your widgets and logic. Built-in helper class for unit testing.
Momentum leverages the power of setState(..)
and StatefulWidget behind the scenes. The feature Event System
uses Stream.
- The router doesn't support named routes yet.
- The parameter handling for router is slightly verbose. And might be complicated for some. But it works magically.
- Needs to explicitly implement
RouterPage
widget in order to handle the system's back button. - (FIXED ✅) The router breaks after hot reload. Only a problem during development but it should work in normal execution.
Visit the official webpage of momentum to browse the full api reference, guides, and examples.
Thanks for checking out momentum. I hope you try it soon and don't hesitate to file on issue on github. I always check them everyday.