mvvm_framework 1.0.0 copy "mvvm_framework: ^1.0.0" to clipboard
mvvm_framework: ^1.0.0 copied to clipboard

A Flutter package for implementing the MVVM architectural pattern with ease, built as an extension of Flutter Bloc.

Change Log #

All notable changes to this project will be documented in this file. See Conventional Commits for commit guidelines.

1.0.0 - 2026-04-13 #

Major architectural simplification: introduces the ViewListener pattern, type-based error dispatch via GenericErrorManager, and numerous correctness and DX improvements.

Breaking Changes & Migration #

1. Model<T> — self-referential generic

copyWithViewStatus now returns T directly, eliminating as T casts. Add the type argument to every Model implementation.

// Before
class MyModel implements Model { ... }

// After
class MyModel implements Model<MyModel> { ... }

For Freezed models only the implements clause changes — no re-generation needed:

// Before
@freezed
abstract class CounterModel with _$CounterModel implements Model { ... }

// After
@freezed
abstract class CounterModel with _$CounterModel implements Model<CounterModel> { ... }

2. MVVM widget — builder pattern

view/viewModel params replaced by vmBuilder/viewBuilder; model renamed to externalModel; new optional listenWhen/buildWhen params added.

// Before
CounterPage({required CounterModel model})
  : super(model: model, vmBuilder: ...);

// After
CounterPage({required CounterModel model})
  : super(externalModel: model, vmBuilder: ...);

3. ViewListener<M, VM> — new side-effect pattern

listener() removed from ViewModel. Side effects now live in a ViewListener subclass passed as a factory to MVVM; the framework calls .execute() automatically. Override typed hooks instead of a manual switch over ViewStatus:

Hook Triggered on
onSuccess() ViewStatusSuccess
onFailure(ViewStatusError) ViewStatusFailure (after error dispatch)
onInitial() ViewStatusInitial
onLoading() ViewStatusLoading
// Before
listener: (ctx, vm, state) {
  CounterListener(viewContext: ViewContext(ctx), viewModel: vm, state: state).execute();
},

// After
listener: (ctx, vm, state) => CounterListener(
  viewContext: ViewContext(ctx),
  viewModel: vm,
  state: state,
),
// Before
@override
void execute() {
  if (state.viewStatus.isSuccess) {
    viewContext.navigator.pop();
  }
}

// After
@override
Future<void> onSuccess() async {
  await viewContext.navigator.maybePop();
}

4. ViewStatus — no more Freezed

Plain abstract class with four concrete subclasses. ViewStatusError.message replaced by ViewStatusError.cause (type Object). The viewStatusFailure constant is removed.

// Before
expect(failure.error.message, 'something');

// After
expect(failure.error.cause, isA<NetworkException>());

5. ViewContext — simplified construction

ViewContext(context) now takes only a BuildContext; others is optional (defaults to ViewEmptyAdditionalContext()). showDialog and showModalBottomSheet are regular instance methods.

6. ViewModel — API changes

  • viewContext property removed; use ViewListener for all UI interactions.
  • errorHandlers is now @visibleForTesting; use dispatchErrorHandler(Type) from ViewListener instead.
  • didUpdateWidget return type: voidFuture<void>.
  • onInit and didUpdateWidget are now wrapped in safeExecute by the framework automatically.
  • safeExecute onError callback: void Function(ViewStatusError)Future<void> Function(ViewStatusError).
// Before
onError: (_) { doSomething(); }

// After
onError: (_) async { doSomething(); }

7. ErrorPresenter<T> — added StackTrace? parameter

Add StackTrace? stackTrace as the second parameter to every GenericErrorManager.register<T>() callback, errorFallback, and setFallback lambda.

// New signature
Future<void> Function(T cause, StackTrace? stackTrace, ViewContext viewContext)

8. ConfigMVVM.initialize()onGenericError is now async

Signature changed from void Function(...) to Future<void> Function(...); the previous signature silently discarded async work.


New Features #

  • safeExecute(withLoading: true) — auto-emits viewStatusLoading before the action; eliminates manual emit(state.copyWithViewStatus(viewStatusLoading)) calls.
// Before
Future<void> load() async {
  emit(state.copyWithViewStatus(viewStatusLoading));
  await safeExecute(_fetch);
}

// After
Future<void> load() => safeExecute(_fetch, withLoading: true);
  • subscribeStream<T>(stream, onData:, onError:) — managed stream subscription; auto-cancelled in close(), no StreamSubscription field needed.
// Before
late StreamSubscription<Location> _sub;

@override
Future<void> onInit() async {
  _sub = locationStream.listen(_onLocation);
}

@override
Future<void> close() async {
  await _sub.cancel();
  await super.close();
}

// After
@override
Future<void> onInit() async {
  subscribeStream(locationStream, onData: _onLocation);
}
  • ViewModel.onDispose() — lifecycle hook called just before close; override to release controllers, timers, or other resources.

  • GenericErrorManager — centralized, type-based error dispatch exported from mvvm_framework.dart.

    • register<T>(handler) — per-exception-type handler.
    • setFallback(handler) — catch-all override (default: basic AlertDialog).
    • reset() — test helper; clears all handlers and restores defaults.

    Dispatch priority (lowest → highest):

    1. Default AlertDialog fallback
    2. GenericErrorManager.setFallback(...) / ConfigMVVM.initialize(errorFallback: ...)
    3. GenericErrorManager.register<T>(...)
    4. ViewModel.registerErrorHandler<T>(...) (highest)
GenericErrorManager.register<NetworkException>(
  (e, stackTrace, ctx) => ctx.scaffoldMessenger.showSnackBar(MySnackBar(e.message)).closed,
);
ConfigMVVM.initialize(
  errorFallback: (cause, stackTrace, ctx) => MyErrorDialog(ctx.context, cause.toString()).show(),
);
  • ConfigMVVM improvements: errorFallback parameter, isInitialized getter, double-init assert guard, and reset() now also resets GenericErrorManager.

  • ViewStatusError value equality: ==/hashCode on both ViewStatusError and ViewStatusFailure; toString() includes the captured stack trace for easier debugging.


Bug Fixes #

  • MVVM.didUpdateWidget now calls ViewModel.didUpdateWidget only when externalModel is non-null and has actually changed (previously fired on every parent rebuild).
  • ViewContext is no longer constructed eagerly on every state change when a custom listener factory is provided.
  • Removed spurious unused dart:ui import from view_model.dart.

0.1.0 - 2025-06-24 #

  • FEAT: add showModalBottomSheet support to ViewContext (6f5e2a7)
  • CHORE: update dependencies (517eb64)

0.0.1 - 2025-04-22 #

  • FEAT: initial release version 0.0.1 (893a029)
0
likes
160
points
102
downloads

Documentation

API reference

Publisher

verified publisherdraxent.dk

Weekly Downloads

A Flutter package for implementing the MVVM architectural pattern with ease, built as an extension of Flutter Bloc.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter, flutter_bloc

More

Packages that depend on mvvm_framework