PWI Software Documentation Help

Flutter Master AI Agent Prompt

MVVM+ (mvvm_plus) + Bilocator — Repo‑Enforceable Architecture (for editing an existing Flutter codebase)

Executive Summary

  • MVVM+: Tiny API layered on Flutter: get, listenTo, buildView; ViewWidget<T> for views; ViewModel for state/logic; Model for shared logic. Use createProperty<T>() for view‑bound state. (Dart packages)

  • Bilocator DI: Register single services (registry) and/or inherited models (widget tree). Access with Bilocator.get<T>(), or get<T>()/listenTo<T>() in MVVM+ classes. (Dart packages)

  • Registry vs Tree: Location.registry (global access) vs Location.tree (descendant scope). Pick the narrowest scope that fits. (Dart packages)

  • Hot‑reload safety: Wrap multiple registrations in Bilocators and assign a stable key to avoid re‑registration errors on hot‑reload. (Dart packages)

  • View rules: Prefer ViewWidget<T>, keep widgets stateless; no StatefulWidget unless a package forces it. No ListenableBuilder. Logic & side‑effects live in ViewModels/services. (Dart packages)

  • Streams & disposal: Mirror repository/stream changes into MVVM+ Properties via createProperty/createStreamProperty; cancel subscriptions in dispose(). (Dart packages)

  • Navigation boundary: Prefer go_router behind a RouterService; Views call ViewModel → service. Keep routing APIs out of Views. (Dart packages)

  • Models: Use json_serializable for (de)serialization; keep models immutable where practical. (Dart packages)

  • Style: Follow Effective Dart + PWI standards; match existing project patterns. (Dart, PWI Works Help)

Architecture & Patterns (mvvm_plus + bilocator)

Core APIs & Lifecycle

  • View (ViewWidget<T extends ViewModel>): a widget whose viewModel is provided by a builder; MVVM+ adds get/listenTo helpers. Use it instead of hand‑rolled StatefulWidget. (Dart packages)

  • ViewModel: extends Model; owns UI state and orchestrates services; triggers view rebuilds via buildView(). Lifecycle includes initState/dispose. (Dart packages)

  • Property: MVVM+ typedef over ValueNotifier<T>; create with createProperty<T>() (auto‑wires buildView). Prefer this over raw ValueNotifier. (Dart packages)

  • Streams: Use createStreamProperty<T>(stream); dispose in dispose(). (Dart packages)

DI & Service Location (bilocator)

  • Register

    • Single service (registry): Bilocator<MyService>(builder: () => MyService(), child: ...).

    • Multiple: Bilocators(delegates: [BilocatorDelegate<T>(builder: ...), ...]).

    • Inherited model (tree): set location: Location.tree. (Dart packages)

  • Access

    • Anywhere: Bilocator.get<T>().

    • Inside MVVM+ classes: get<T>()/listenTo<T>(); prefer listenTo to avoid .of ‑style rebuild coupling. (Dart packages)

  • Hot‑reload: Assign a repeatable key to Bilocators to prevent delegate re‑registration on hot reload. (Dart packages)

Widget‑Tree Patterns

  • Register cross‑cutting services at the app root (main.dart) with Bilocators (stable key). Register feature‑scoped models closer to their Views using location: Location.tree. (Dart packages)

  • Use go_router with MaterialApp.router(routerConfig: ...); expose only a RouterService to ViewModels; Views invoke ViewModel methods. (Dart packages)

Style & Docs

  • Conform to Effective Dart + PWI standards; keep comments purposeful and at the API/decision points. (Dart, PWI Works Help)

Repo Editing Guide & Scaffold (match current patterns)

Directory Conventions (adapt, don’t rename without approval)

  • Typical PWI structure: lib/system, lib/data/{models,repositories,services}, lib/ui/<page>/{view.dart,view_model.dart}, lib/utils, plus app entry (main.dart). See PWI standards for an example tree. (PWI Works Help)

Canonical Bilocators Initialization (use this; replace other init examples)

// lib/main.dart import 'package:flutter/material.dart'; import 'package:bilocator/bilocator.dart'; // Minimal service to demonstrate DI registration. // AGENTS: Replace with the real service from the existing codebase. class UserRepository {} class App extends StatelessWidget { const App({super.key}); @override Widget build(BuildContext context) => const MaterialApp(home: SizedBox()); } void main() { // AGENTS: Bilocators is the DI root. Keep a stable key for hot reload. runApp( Bilocators( key: const ValueKey('Bilocators'), delegates: [ // Register services via BilocatorDelegate<T>. // Use builder closures; do NOT create globals outside DI. BilocatorDelegate<UserRepository>(builder: () => UserRepository()), ], child: const App(), ), ); }

(Why: Bilocators manages multiple registrations; the stable key prevents hot‑reload re‑registration issues.) (Dart packages)

Canonical ViewModel (mirror repo changes from a repository into a Property)

// ViewModel example for agents — follow this structure. // DO: use Bilocator for DI, createProperty for state, and dispose subscriptions. // DO NOT: use ListenableBuilder or raw ValueNotifier. import 'dart:async'; import 'package:mvvm_plus/mvvm_plus.dart'; import 'package:bilocator/bilocator.dart'; // Example domain types (replace with existing codebase types) class User {} abstract class UserRepository { // Prefer a stream or equivalent change feed exposed by the existing repo. Stream<User?> get dataStream; } class UserViewModel extends ViewModel { // Local MVVM+ state that Views read via ViewWidget<T>. late final user = createProperty<User?>(null); // <- MVVM+ Property late final UserRepository _repo; // <- fetched via Bilocator StreamSubscription<User?>? _sub; UserViewModel() { // AGENTS: Always resolve via DI; do not instantiate globals here. _repo = Bilocator.get<UserRepository>(); // Reflect repository changes into MVVM+ Property (triggers View rebuilds). _sub = _repo.dataStream.listen((u) { user.value = u; }); } @override void dispose() { _sub?.cancel(); // AGENTS: Always clean up subscriptions. super.dispose(); } }

Notes: createProperty<T> auto‑adds a buildView listener; streams use createStreamProperty when appropriate. (Dart packages)

Minimal View (stateless UI; reads Property via viewModel)

import 'package:flutter/material.dart'; import 'package:mvvm_plus/mvvm_plus.dart'; import 'user_view_model.dart'; class UserView extends ViewWidget<UserViewModel> { const UserView({super.key}) : super(builder: () => UserViewModel()); @override Widget build(BuildContext context) { final u = viewModel.user.value; // property → triggers rebuilds via buildView return Scaffold(appBar: AppBar(title: const Text('User')), body: Center(child: Text(u == null ? 'No user' : 'Hello, user')), ); } }

Why this shape: stateless View; no StatefulWidget; all state in the ViewModel; rebuilds are driven by MVVM+ Properties. (Dart packages)

Minimal model with json_serializable (adapt names/fields)

import 'package:json_annotation/json_annotation.dart'; part 'user.g.dart'; @JsonSerializable() class User { final String id; final String name; const User({required this.id, required this.name}); factory User.fromJson(Map<String, dynamic> json) => _$UserFromJson(json); Map<String, dynamic> toJson() => _$UserToJson(this); }

README “Agent Contract” (copy‑ready for the repo)

Do

  • Views: Use ViewWidget<T>; keep widgets stateless; no StatefulWidget unless a package forces it. (Dart packages)

  • State & Logic: In ViewModels via createProperty<T>()/createStreamProperty<T>(); call buildView() to redraw. (Dart packages)

  • DI: Register via Bilocators/BilocatorDelegate at the app root (stable key); access via Bilocator.get<T>() or MVVM+ get<T>()/listenTo<T>(). Prefer Location.tree for feature‑scoped models. (Dart packages)

  • Navigation: Use go_router behind a RouterService; Views don’t call router APIs directly. (Dart packages)

  • Models: Use json_serializable; keep conversions out of Views. (Dart packages)

  • Style/Docs: Follow Effective Dart + PWI + existing repo patterns. (Dart, PWI Works Help)

Do Not

  • ❌ No ListenableBuilder or raw ValueNotifier in examples/components; use MVVM+ Property/createProperty. (Dart packages)

  • ❌ No globals/singletons outside bilocator. Use DI. (Dart packages)

  • ❌ No direct routing calls from Views. Call ViewModel → RouterService. (Dart packages)

Directory & File Rules

  • Keep lib/ui/<page>/{view.dart,view_model.dart}; services/repos/models under lib/data; DI in lib/system/. Align with the repo’s current structure and PWI guidance. (PWI Works Help)

PR & Review Checklist

  • [ ] View is ViewWidget<T>; no stray state in widgets. (Dart packages)

  • [ ] ViewModel holds all state via Properties; no raw ValueNotifier. (Dart packages)

  • [ ] Services via bilocator; no globals. Bilocators has a stable key. (Dart packages)

  • [ ] Navigation only through RouterService; no view‑router coupling. (Dart packages)

  • [ ] Effective Dart + PWI style. (Dart, PWI Works Help)

Agent Prompting Toolkit (repo‑aware; reference the README)

1) Add a screen (View + ViewModel) “Edit the existing codebase to add Foo. Create lib/ui/foo/foo_view.dart and foo_view_model.dart using ViewWidget<FooViewModel>. Keep the View stateless; use createProperty in the ViewModel. Wire UI to the Property. Follow the README contract (DI via Bilocator, style via Effective Dart/PWI).”

2) Add a service + DI “Add UserRepository and register it in Bilocators (stable key) in main.dart. Expose a Stream<User?> dataStream. Update UserViewModel to resolve via Bilocator.get<UserRepository>() and mirror changes into a Property<User?>. Follow the README.”

3) Refactor to MVVM+ “Refactor OldWidget (was StatefulWidget) into ViewWidget<OldViewModel>. Move all state to the ViewModel using createProperty. Replace any raw ValueNotifier with Property. Access services via get<T>().”

4) Navigation change “Add a RouterService wrapping go_router calls (go, push). Inject via bilocator; Views call ViewModel → RouterService. No direct router calls in Views.”

5) Repo rules sync “Ensure .github/copilot-instructions.md mirrors the README ‘Agent Contract’ sections (Do/Do Not, DI rules, directory rules).” (GitHub Docs)

Migration & Pitfalls (when touching existing features)

  • From Provider/Riverpod/get_it/stacked: Replace provider scopes/singletons with bilocator registrations; replace notifyListeners ‑heavy flows with buildView() + Properties in ViewModels; move view logic into ViewModels. (MVVM+ docs show buildView/createProperty patterns.) (Dart packages)

  • Hot‑reload: Bilocators needs a stable key or you may get duplicate registration errors. (Dart packages)

  • Streams: Prefer createStreamProperty or mirror repository streams into a Property; cancel subscriptions in dispose(). (Dart packages)

  • Navigation: Keep go_router behind a service; configure MaterialApp.router(routerConfig: ...). Avoid coupling Views to router APIs. (Dart packages)

  • Avoid .of where possible: prefer listenTo/get to reduce unwanted rebuild dependencies. (Dart packages)

Examples (minimal, aligned with docs)

Register multiple services at root (hot‑reload safe):

Bilocators( key: const ValueKey('services'), delegates: [ BilocatorDelegate<ApiService>(builder: () => ApiService()), BilocatorDelegate<AuthService>(builder: () => AuthService()), ], child: const App(), );

(Bilocators + stable key; lifecycle bound to the widget.) (Dart packages)

ViewModel with MVVM+ Properties (no raw ValueNotifier):

class CounterViewModel extends ViewModel { late final counter = createProperty<int>(0); void inc() => counter.value++; }

(Property triggers view rebuild via buildView listener.) (Dart packages)

Access another ViewModel/service:

final auth = get<AuthService>(); // bilocator registry final header = listenTo<HeaderViewModel>(); // listens for changes

(Dart packages)

Version Awareness (re‑check before upgrades)

  • mvvm_plus: 1.5.4, published \~22 months ago on pub.dev; docs include ViewWidget, buildView, createProperty, createStreamProperty, Location.registry/tree. (Dart packages)

  • bilocator: 0.6.1, published \~23 months ago; supports registry/tree, Bilocators, BilocatorDelegate, and hot‑reload stable key guidance. (Dart packages)

  • PWI Standards: Flutter & coding standards (structure, naming, package prefs). Last modified Aug 21, 2025. (PWI Works Help)

  • MVVM+ official docs & API: patterns, ViewWidget, lifecycle, Properties, registry/tree. (Dart packages)

  • Bilocator official docs & API: single services vs inherited models, Bilocators, get/listenTo, hot‑reload key. (Dart packages)

  • Effective Dart: style/docs/design. (Dart)

  • json_serializable: usage + API. (Dart packages)

  • go_router: router config & features (for RouterService). (Dart packages)

Deviation Notes (if conflicts arise)

  • If this page conflicts with mvvm_plus or bilocator docs, add a short Deviation Note in your PR description with a link to the exact API doc section and the smallest possible change to align the code. (Dart packages)

05 December 2025