Skip to main content

magic-framework

Write correct, idiomatic code in a Flutter app that depends on the `magic` framework (Laravel-inspired: IoC container, 18 facades, Eloquent-style ORM, service providers, reactive controllers, GoRouter routing, validation, auth, broadcasting, MagicAction writes, Repository row caches, SessionScope tenant resets). Use whenever code imports `package:magic/magic.dart` or `package:magic/testing.dart`, or the work touches Magic.init, MagicApp, a facade (Auth/Http/Cache/DB/Echo/Event/Gate/Config/Lang/Launch/Log/Pick/MagicRoute/Schema/Session/Storage/Vault/Crypt), a Model, MagicController, a MagicView, MagicFormData, FormRequest, MagicAction, MagicFormObject, Repository, SessionScope, BroadcastListeners, a ServiceProvider, a migration, or the artisan make:* CLI. UI styling is Wind (separate wind-ui skill). Do NOT use for plain Flutter or Wind-only work with no magic import.

Source facts

Repository
fluttersdk/ai
Last source activity
October 28, 2026 at 16:54
Detected SKILL.md language
English
Stars
3
Forks
0

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

File Explorer
21 files

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
magic-framework
description
Write correct, idiomatic code in a Flutter app that depends on the `magic` framework (Laravel-inspired: IoC container, 18 facades, Eloquent-style ORM, service providers, reactive controllers, GoRouter routing, validation, auth, broadcasting, MagicAction writes, Repository row caches, SessionScope tenant resets). Use whenever code imports `package:magic/magic.dart` or `package:magic/testing.dart`, or the work touches Magic.init, MagicApp, a facade (Auth/Http/Cache/DB/Echo/Event/Gate/Config/Lang/Launch/Log/Pick/MagicRoute/Schema/Session/Storage/Vault/Crypt), a Model, MagicController, a MagicView, MagicFormData, FormRequest, MagicAction, MagicFormObject, Repository, SessionScope, BroadcastListeners, a ServiceProvider, a migration, or the artisan make:* CLI. UI styling is Wind (separate wind-ui skill). Do NOT use for plain Flutter or Wind-only work with no magic import.
when_to_use
Use proactively when editing or scaffolding a magic app: Magic.init / a facade / a Model / a MagicController or MagicView / a form (MagicFormData, FormRequest, Validator, MagicFormObject) / a write (MagicAction, RunsActions) / a resource cache (Repository, RepositoryQuery) / a tenant boundary (SessionScope, SessionScoped) / a shared realtime channel (BroadcastListeners, ListensToBroadcasts) / a ServiceProvider / a route or MagicMiddleware / a migration / MagicStateMixin + RxStatus + fetchList / Session flash + old() + trans() / testing with MagicTest + Http.fake/Auth.fake / the artisan make:* CLI / the magic_deeplink, magic_notifications, magic_social_auth, magic_starter, magic_payments, magic_devtools, or magic_sentry plugins. Trigger even when the user does not say the word 'magic'. Do NOT trigger for plain Flutter or Wind-only UI with no package:magic import.
version
0.1.72
<!-- magic 0.0.35 | Skill v0.1.72 (2026-10-28). API surface verified against lib/src. --> # Magic Framework Laravel-inspired Flutter framework: IoC container, 18 facades, Eloquent-style ORM, service providers, reactive controllers, and GoRouter-backed routing. This skill makes an agent write code that an experienced magic developer would write: facade-first, IoC-resolved, reactive, and verified against the real API in `lib/src`. All visual styling is handled by Wind (load the `wind-ui` skill for className work); this skill owns architecture, data, navigation, auth, and testing. The host app already depends on `package:magic/magic.dart`. The accuracy contract for this skill: every API you write must exist in `lib/src`. When unsure of a signature, open the source or the matching `doc/**` page rather than guessing; magic is pre-1.0 and the surface is exact, not approximate. ## 0. Before writing code in this project Three checks, each pays off across the whole session. 1. **Read `lib/main.dart` and `lib/config/app.dart`.** Note the `providers` list and its ORDER (AppServiceProvider must precede AuthServiceProvider so `setUserFactory` is set before auth restore runs), and whether `configFactories` or `configs` is used. 2. **Scan one existing controller + view pair in `lib/app/`** for the project's idioms: the singleton accessor shape, how views resolve controllers, how forms are wired. Match the surrounding code, do not invent a dialect. 3. **CLI invocation.** Magic ships an `artisan` executable, so every command runs as `dart run magic:artisan <cmd>` from any app that depends on magic (no package-name placeholder, no global activate). ## 1. Core Laws Hard constraints for every line of magic code. 1. **`await Magic.init()` first.** It must be awaited in `main()` before any facade call and before `runApp()`. Never `.then()`; providers are not booted until the future completes. 2. **Facade-first.** Reach for `Auth`, `Http`, `Config`, `Cache`, `DB`, `Schema`, `Log`, `Event`, `Echo`, `Lang`, `MagicRoute`, `Gate`, `Session`, `Vault`, `Storage`, `Pick`, `Crypt`, `Launch`. Resolve from the container manually (`Magic.make<T>('key')`) only when extending the framework. 3. **Controllers are singletons.** `static X get instance => Magic.findOrPut(X.new);` is the canonical accessor. Views resolve controllers via `Magic.find<T>()` (automatic in `MagicView`), never through constructors. 4. **IoC over `new` for services.** Bind in a provider's `register()`, resolve via the facade or `Magic.make<T>('key')`. Do not scatter `Service()` construction across the app. 5. **Provider discipline.** `register()` is synchronous and is where routes and bindings go. `boot()` is async and may resolve other services; set `Auth.manager.setUserFactory(...)` here. 6. **Reactive state, not setState.** Controllers extend `MagicController` (a `ChangeNotifier`); state flows through `MagicStateMixin` + `RxStatus`. Use `refreshUI()` (guarded `notifyListeners`, and the single seam every controller notification goes through, including validation), `setLoading/setSuccess/setError/setEmpty`, `MagicBuilder` for a section backed by a `ValueListenable`, and `MagicSelector` for a section backed by a plain controller field (it caches its child, so it survives the parent's `setState` and is the tool for a field that changes on every keystroke). `MagicPerfHooks.sink` is a null-by-default static tooling sets to observe those notifications (and repository, query, action, event, cast, timer and broadcast activity); there is no `onRefreshUI`. Local `setState` belongs only to genuine widget-local UI state inside a `MagicStatefulView`. 7. **Typed attribute access.** Models use `get<T>('key')` and `set('key', v)`, never raw `getAttribute`. Declare `fillable`; use `fill(validated, strict: true)` after validation so schema drift throws `MassAssignmentException`. 8. **Context-free navigation and feedback.** `MagicRoute.to/back/replace`, `Magic.snackbar/toast/dialog/confirm/loading`. Never depend on a `BuildContext` for navigation or feedback. Never navigate or fetch inside `build()`. 9. **Validate at the boundary.** `MagicFormData` for forms, `FormRequest` for complex payloads, `Validator` for ad hoc checks. Surface server errors with `handleApiError(response)` (from the `ValidatesRequests` mixin). 10. **Trailing commas, multi-line collections.** Always. Match the project's existing style. ## 2. Bootstrap ```dart import 'package:magic/magic.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await Magic.init( configFactories: [ () => appConfig, // factories: evaluated AFTER Env.load(), so env() works inside them () => authConfig, () => networkConfig, ], ); runApp(MagicApplication(title: 'My App')); } ``` Real lifecycle (from `lib/src/foundation/magic.dart`): `Env.load()` then `configFactories` evaluate, then `MagicApp.init` (config merge), then the web URL strategy is applied if `routing.url_strategy == 'path'`, then core bindings, then providers `register()` (sync), then `await boot()` (async), then the router pre-builds, then ready. Use `configFactories` (not `configs`) whenever a config value reads `Env.get()`: `configs` is evaluated before Env is loaded. `MagicApplication` accepts `title`, `titleSuffix`, `windTheme`, `themeMode`, `locale`, `localizationsDelegates`, `onThemeChanged`, `onInit`, `initialRoute`, and `builder`, which wraps the router like `MaterialApp.builder` so a layer above every page (a floating player) survives each navigation. ## 3. Mental model: Laravel to magic (and where it diverges) Magic mirrors Laravel's vocabulary; it diverges wherever Dart lacks PHP's runtime reflection or where the target is a Flutter client, not an HTTP server. Internalize the divergences; they fail silently (null, not an exception). | Laravel | magic | Note | |---|---|---| | `Container` autowiring, `__callStatic` facades | string-keyed factory closures + explicit static facade stubs | No reflection, no autowiring; an unregistered key throws at runtime | | `ServiceProvider::boot()` (sync) | `boot()` is `async` | await it; dropped futures leave a half-booted provider | | `Router::resource` returns Responses | routes resolve to WIDGETS; middleware runs on NAVIGATION | not an HTTP request cycle | | controllers = per-request handlers | controllers = reactive `ChangeNotifier` singletons | live for the session, drive UI via `RxStatus` | | Eloquent lazy load + `with()` eager load | relations cast from nested API Maps, cached on first access | NO lazy load, NO `with()`, NO query-builder relations: if the payload did not nest it, it is null | | `Gate`/`Policy` server-authoritative | `Gate`/`Policy` run CLIENT-side, advisory only | always re-authorize on the backend | | server sessions | tokens in `Vault` (secure storage), cache-first restore | `Auth.restore()` on cold start | | `Encrypter` AES + HMAC/AEAD JSON envelope | AES-256-CBC `iv:ciphertext` (base64), no MAC | not cross-decryptable with Laravel's `Crypt` | The five assumptions a Laravel developer gets wrong most: (1) the container autowires (it does not, register explicitly); (2) `user.posts` lazy-loads (it does not, embed in the payload); (3) `Gate.allows()` is real security (advisory only); (4) `with()` exists (it does not); (5) `boot()` is sync (it is async). Full mapping with Laravel source citations: `${CLAUDE_SKILL_DIR}/references/bootstrap-lifecycle.md`. ## 4. Facades and the container ### IoC container (the methods an app uses) | Call | Purpose | |---|---| | `Magic.bind('key', () => Svc(), {shared})` | factory binding (new instance per resolve; `shared: true` caches) | | `Magic.singleton('key', () => Svc())` | lazy shared singleton | | `Magic.make<T>('key')` | resolve a service (throws if unbound) | | `Magic.bound('key')` | is the key registered | | `Magic.put<T>(ctrl)` / `Magic.find<T>()` / `Magic.findOrPut<T>(T.new)` | controller register / resolve / get-or-create | | `Magic.delete<T>()` / `Magic.isRegistered<T>()` | controller remove / check | | `Magic.flush()` / `MagicApp.reset()` | clear controllers / full container reset (testing) | ### The 18 facades `Config` and `Gate` resolve through their managers (no plain IoC key); the rest bind to the key shown. | Facade | Key | Surface you reach for (all verified in `lib/src/facades/`) | |---|---|---| | `Auth` | `auth` | `login(data, user)`, `logout()`, `check()`, `guest` (getter), `user<T>()`, `id()`, `getToken()`, `refreshToken()`, `restore()`, `registerModel<T>(factory)`, `guard([name])`, `stateNotifier`, `manager`, `fake({user})` | | `Http` | `network` | `get/post/put/delete`, `upload`, RESTful `index/show/store/update/destroy`, `fake([stubs])`, `response([data, code])`, `unfake()`. NO `patch` | | `Config` | (manager) | `get<T>`, `getOrFail<T>`, `set`, `has`, `all`, `merge`, `prepend`, `push`, `forget`, `flush`, `repository` | | `Cache` | `cache` | `put(key, value, {ttl})`, `get`, `has`, `forget`, `flush`, `remember<T>(key, ttl, cb)`, `fake()` | | `DB` | (lazy) | `table(name)` (query builder), `select/statement/insert/update/delete` (raw SQL), `transaction(cb)`, `beginTransaction/commit/rollback` | | `Schema` | (manager) | `create(table, (b){})`, `table`, `drop`, `dropIfExists`, `hasTable`, `hasColumn`, `getColumns`, `rename` | | `Log` | `log` | `info/error/warning/debug/notice/critical/alert/emergency`, `log(level, msg)`, `channel(name)`, `fake()` | | `Event` | (dispatcher) | `dispatch(MagicEvent)`; `listen<T extends MagicEvent>(() => Listener())` (named `T`, register in `register()` not `boot()`); or `EventDispatcher.register(Type, [() => Listener()])` directly | | `Echo` | `broadcasting` | `channel/private/join`, `listen`, `leave`, `connect/disconnect`, `socketId`, `connectionState`, `onReconnect`, `addInterceptor`, `manager`, `fake()` | | `MagicRoute` | (router) | `page`, `group`, `layout`, `resource(name, ctrl, {only, except})`, `to`, `toNamed`, `push`, `back({fallback})`, `replace`, `setTitle`, `currentTitle`, `config` | | `Gate` | (manager) | `define`, `before`, `allows`, `denies`, `allowsAny(list)`, `allowsAll(list)`, `has`, `abilities`, `flush` | | `Session` | (store) | `flash(map)`, `flashErrors(map)`, `old(field, [fallback])`, `oldRaw`, `error(field)`, `errors(field)`, `hasError`, `hasFlash`, `tick()` | | `Lang` | (translator) | `get(key, [replace])`, `has`, `current`, `isLoaded`, `supportedLocales`, `setLocale`, `detectLocale`, `detectAndSetLocale`, `setSupportedLocales`, `addListener/removeListener`, `delegate` | | `Vault` | `vault` | `put(key, value)`, `get`, `delete`, `flush`, `fake([initial])` | | `Storage` | (manager) | `disk([name])`, `put`, `get`, `getFile`, `exists`, `delete`, `url`, `download`, `setManager`, `flush` | | `Pick` | (static) | `image`, `images`, `camera`, `media`, `video`, `recordVideo`, `file`, `files`, `directory`, `saveFile` | | `Crypt` | `encrypter` | `encrypt`, `decrypt`, `encryptWithDeviceKey`, `decryptWithDeviceKey`, `hasDeviceKey`, `generateDeviceKey`, `clearDeviceKey` | | `Launch` | `launch` | `url(u, {mode})`, `email`, `phone`, `sms`, `canLaunch` |
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub