Flutter starter kit for the Magic Framework. Pre-built Auth, Profile, Teams & Notifications — 13 opt-in features, every screen overridable via Wind UI.
Version: 0.0.1-alpha.14 · Dart: >=3.6.0 · Flutter: >=3.27.0
| Command | Description |
|---|---|
flutter test --coverage |
Run all tests (~52 files, ~752 cases) with coverage |
flutter test test/http/controllers/ |
Run controller tests only |
flutter test --name "pattern" |
Run tests matching pattern |
flutter analyze --no-fatal-infos |
Static analysis (flutter_lints ^6.0) |
dart format . |
Format all code |
dart fix --apply |
Auto-fix lint issues |
dart run magic_starter:install |
Scaffold config + provider into consumer project |
dart run magic_starter:configure |
Interactive feature toggle configuration |
dart run magic_starter:doctor |
Diagnose project setup issues |
dart run magic_starter:publish |
Publish (copy) views/layouts for customization |
dart run magic_starter:uninstall |
Remove scaffolded files from consumer project |
Pattern: ServiceProvider + Singleton Manager + Feature Flags + View Registry + Controller/View separation
lib/
├── magic_starter.dart # Barrel export — 47 exports
├── config/
│ └── magic_starter.dart # Configuration template stub
└── src/
├── magic_starter_manager.dart # Singleton registry: user model, team resolver, nav, 7 sub-themes, views, slots
├── configuration/ # 13 feature toggles (all default false, opt-in)
├── providers/ # IoC registration, 9 Gate abilities, boot logic
├── facades/ # Static API: MagicStarter.useUserModel(), .useTheme(), .view.make(), .view.slot()
├── http/controllers/ # 7 controllers + NavigatesRoutes mixin
│ └── concerns/ # Shared mixins (navigates_routes.dart)
├── routes/ # Per-module route registration (feature-gated)
├── models/ # AuthUser, Team, NavItem
├── ui/
│ ├── layouts/ # AppLayout (authenticated), GuestLayout (auth pages)
│ ├── views/ # auth/, profile/, teams/, notifications/
│ └── widgets/ # 13 reusable Wind UI components
└── cli/ # install, configure, doctor, publish, uninstall
bin/
└── magic_starter.dart # CLI entry point — registers commands with Kernel
assets/stubs/ # Stub templates for code generation
Data flow: App boot → MagicStarterServiceProvider.register() → binds manager + 9 Gate abilities → boot() → resolves config, registers default views → Feature-gated routes registered → Controllers handle HTTP via Http.* → Views render via MagicStatefulView + Wind UI
Pure Dart — no android/, ios/, or native platform code.
Depends on: magic, magic_notifications (internal packages), path (cross-platform path handling in CLI).
After ANY source code change, sync before committing:
CHANGELOG.md— Add entry under[Unreleased]sectionREADME.md— Update if features, API, or usage changesdoc/— Update relevant documentation files
Every feature, fix, or refactor must go through the red-green-refactor cycle:
- Red — Write a failing test that describes the expected behavior
- Green — Write the minimum code to make the test pass
- Refactor — Clean up while keeping tests green
Rules:
- No production code without a failing test first
- Run
flutter testafter every change — all tests must stay green - Run
dart analyzeafter every change — zero warnings, zero errors - Run
dart format .before committing — zero formatting issues
Verification cycle: Edit → flutter test → dart analyze → repeat until green
- Mock via contract inheritance (no mockito):
class MockNetworkDriver implements NetworkDriver - MockNetworkDriver tracks:
lastMethod,lastUrl,lastData,lastHeaders - Reset state in setUp:
MagicApp.reset(),Magic.flush(), bind mocks, configure guards - Tests mirror
lib/src/structure intest/ - CLI tests in
test/cli/commands/ - Payload verification:
expect(mockDriver.lastData['field'], expectedValue) - State verification:
expect(controller.isSuccess, isTrue)
| Mistake | Fix |
|---|---|
Missing Auth.restore() after login |
Always call Auth.restore() after successful login — user model is incomplete without it |
| Single-level two-factor check | Check BOTH response['two_factor'] and response['data']['two_factor'] |
| Wrong identity mode precedence | Phone takes precedence when both enabled; use _applyIdentityToPayload() helper |
Direct context.go() in controllers |
Use navigateTo(path) from NavigatesRoutes mixin — navigator may not be mounted |
| Feature-gated route without check | Routes throw StateError if called when feature is disabled — always check config first |
Theme access in boot() |
Requires addPostFrameCallback — navigator context unavailable during boot phase |
Manual refreshNotifier poke |
MagicStarterAppLayout.refreshNotifier triggers layout rebuilds on auth change — don't poke manually |
| Missing ValueNotifier disposal | Controllers with ValueNotifier fields must have notifier.dispose() in tearDown |
| Unnormalized notification keys | NotificationController._normalizeMap() is critical — backend returns mixed-case keys |
MagicStarterCard title padding in noPadding mode |
When noPadding: true, title gets px-6 pt-6 pb-3 — do NOT add extra padding around it manually |
CardVariant import |
CardVariant is exported from the main barrel; import package:magic_starter/magic_starter.dart — no direct widget file import needed |
| Navigation theme not affecting UI | MagicStarter.useNavigationTheme() must be called before the app layout is first painted — ideally in AppServiceProvider.boot() |
brandBuilder + brandClassName both set |
brandBuilder wins — brandClassName is ignored when a builder is registered |
| Modal theme not affecting dialogs | MagicStarter.useModalTheme() must be called before any dialog is shown — ideally in AppServiceProvider.boot() |
| Hardcoding dialog classNames | All modal classNames must come from MagicStarter.manager.modalTheme — never hardcode in widget build methods |
| Theme sub-theme ordering | useTheme() sets all 7 sub-themes at once; individual useFormTheme() etc. can override after. Call unified first if using both |
| Slot not rendering | MagicStarter.view.slot(viewKey, slotName, builder) must be called before the view is built. Views call buildSlot() at build time |
| Published view not loading | dart run magic_starter:publish copies views to lib/resources/views/starter/. Auto-wire adds MagicStarter.view.register() to AppServiceProvider |
Icons.* in build() |
Extract as static const _iconName = Icons.xxx — required for Flutter web tree-shaking |
fluttersdk:magic-framework— Magic Framework patterns: facades, service providers, IoC, Eloquent ORM, controllers, routing. Use for ANY code touching Magic APIs.fluttersdk:magic-starter-widgets— Reusable standalone widgets exported frompackage:magic_starter/magic_starter.dart:MagicStarterCard(withCardVariantenum: surface/inset/elevated),MagicStarterPageHeader(title, subtitle, leading, actions, titleSuffix, inlineActions),MagicStarterConfirmDialog(withConfirmDialogVariantenum: primary/danger/warning),MagicStarterPasswordConfirmDialog,MagicStarterTwoFactorModal,MagicStarterDialogShell(sticky header/footer + scrollable body shell; acceptsfooterBuilder: Widget Function(BuildContext dialogContext)?so callers canNavigator.pop(dialogContext)safely),MagicStarterHideBottomNav(wrap route group to suppress mobile bottom nav bar). All accept plain callbacks — no internal controller coupling required. All modals readMagicStarterModalThemetokens at build time.
ci.yml: push/PR →flutter pub get→flutter analyze --no-fatal-infos→dart format --set-exit-if-changed→flutter test --coverage→ codecov upload