- name
- magic-framework
- description
- Magic Framework: Flutter IoC + 18 Facades (Auth, Http, Cache, DB, Echo, Log, Event, Gate, Session, MagicRoute...), Eloquent ORM, FormRequest, Gate abilities, resource routing, async validation, Service Providers, testing, 4 plugins.
- version
- 1.0.0-alpha.13
- when_to_use
- TRIGGER when: code imports `package:magic/magic.dart` or `package:magic/testing.dart`, or user mentions Magic.init, MagicApp, MagicController, MagicView, MagicStatefulView, MagicStatefulViewState, MagicResponsiveView, MagicFormData, MagicForm, MagicBuilder, MagicRoute, MagicResponse, Model with HasTimestamps, InteractsWithPersistence, CastsAttributes, EnumCast, ListCast, MassAssignmentException, fill(strict:), FormRequest, AuthorizationException, ValidationException, AsyncRule, Unique rule, ResourceController, MagicRoute.resource, Gate.allowsAny, Gate.allowsAll, controller.authorize, Session facade, Session.flash, Session.old, old(), error() helper, ServiceProvider, MagicMiddleware, MagicStateMixin, ValidatesRequests, RxStatus, Auth/Http/Config/Cache/DB/Gate/Log/Event/Lang/Schema/Vault/Storage/Pick/Crypt/Launch/Echo/Session facade, MagicApplication, MagicTitle, TitleManager, MagicTest, fetchList, fetchOne, Http.fake, Auth.fake, Echo.fake, Magic.findOrPut, Magic.make, Magic.put, Magic.find, Magic.singleton, Magic.snackbar, Magic.toast, Magic.dialog, Magic.confirm, Carbon, trans(), env(), rules(), handleApiError, MagicStarter, magic_deeplink, magic_notifications, magic_social_auth, dart run magic:magic, make:model, make:controller, make:view, make:request. DO NOT TRIGGER when: code only uses Wind UI without Magic framework, or plain Flutter without package:magic import.
<!-- Magic v1.0.0-alpha.13 + [Unreleased] | magic_starter v0.0.1-alpha.14 | Skill updated: 2026-04-18 -->
# Magic Framework
Laravel-inspired Flutter framework. IoC Container + Facades + Eloquent ORM + GoRouter. All styling is handled by Wind UI (separate skill) -- this skill covers architecture, data, and navigation only. For UI styling, load the wind-ui skill.
## 1. Core Laws
1. **await Magic.init()**: Must be awaited in `main()` before ANY facade call. Never `.then()`.
2. **Facade-first**: Use `Auth`, `Http`, `Config`, `Cache`, `DB`, `Log`, `Event`, `Echo`, `Lang`, `MagicRoute`, `Gate`, `Session`, `Schema`, `Vault`, `Storage`, `Pick`, `Crypt`, `Launch` -- never resolve manually unless extending.
3. **Singleton controllers**: `static X get instance => Magic.findOrPut(X.new);` -- the canonical pattern.
4. **IoC over new**: Bind services in providers, resolve via `Magic.make<T>('key')`. Never scatter `new Service()` across code.
5. **Service Provider discipline**: `register()` = sync bindings only, routes go here. `boot()` = async, may resolve other services, set `Auth.manager.setUserFactory()` here.
6. **Controller-View binding**: Controllers extend `MagicController`, views resolve them via `Magic.find<T>()`. Never pass controllers through constructors.
7. **Eloquent conventions**: Models declare `table`, `resource`, `fillable`. Use typed `get<T>('key')` accessors -- never raw `getAttribute()`.
8. **Context-free UI**: Use `Magic.snackbar()`, `Magic.toast()`, `Magic.dialog()`, `MagicRoute.to()` -- never depend on `BuildContext` for feedback or navigation.
9. **Validation at boundaries**: Use `ValidatesRequests` mixin + `MagicFormData` for form validation. Server errors via `handleApiError(response)`.
10. **MagicFormData auto-inference**: String values become `TextEditingController`. Other types become `ValueNotifier<T>`.
11. **Trailing commas + multi-line**: Always. No exceptions.
## 2. Bootstrap
```dart
import 'package:magic/magic.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Magic.init(
configFactories: [
() => appConfig,
() => authConfig,
() => networkConfig,
],
);
runApp(MagicApplication(title: 'My App'));
}
```
**7-step lifecycle**: `Env.load()` -> `configFactories` evaluate -> `MagicApp.init` -> Core bindings (Log) -> Provider `register()` -> `await boot()` -> Router pre-build.
Use `configFactories` (not `configs`) when any value depends on `Env.get()`. The `configs` param evaluates before Env is loaded.
## 3. Quick Reference Tables
### IoC Container
| Method | Purpose |
|--------|---------|
| `Magic.app` | Access MagicApp container instance |
| `Magic.bind('key', () => Svc())` | New instance each resolve |
| `Magic.singleton('key', () => Svc())` | Lazy singleton (shared) |
| `app.setInstance('key', obj)` | Bind existing object directly |
| `Magic.make<T>('key')` | Resolve service from container |
| `Magic.bound('key')` | Check if service is registered |
| `Magic.register(provider)` | Register a ServiceProvider |
| `Magic.put<T>(ctrl)` | Register controller by type |
| `Magic.find<T>()` | Resolve controller by type |
| `Magic.findOrPut<T>(T.new)` | Find or create controller singleton |
| `Magic.delete<T>()` | Remove controller |
| `Magic.isRegistered<T>()` | Check if controller exists |
| `Magic.flush()` | Clear all controllers (testing) |
| `MagicApp.reset()` | Full container reset (testing) |
### Facade Summary (18 Facades)
| Facade | Purpose | Key Methods |
|--------|---------|-------------|
| `Auth` | Authentication | `check()`, `guest` (getter), `user<T>()`, `login(data, user)`, `logout()`, `restore()`, `manager` |
| `Http` | Network requests | `get()`, `post()`, `put()`, `delete()`, `upload()`, `index()`, `show()`, `store()`, `update()`, `destroy()` |
| `Config` | Configuration | `get('key', default)`, `set('key', value)`, `has('key')` |
| `Cache` | Caching | `get()`, `put()`, `forget()`, `flush()`, `has()` |
| `DB` | Database | `table('name')`, `raw()`, `transaction()` |
| `Schema` | Migrations | `create()`, `drop()`, `hasTable()` |
| `Log` | Logging | `info()`, `error()`, `warning()`, `debug()` |
| `Event` | Events | `dispatch(event)` |
| `Echo` | Broadcasting | `channel()`, `private()`, `join()`, `listen()`, `leave()`, `connect()`, `disconnect()`, `socketId`, `connectionState`, `onReconnect`, `fake()` |
| `MagicRoute` | Routing | `page()`, `group()`, `layout()`, `resource(name, ctrl, {only, except})`, `to()`, `back({fallback?})`, `replace()`, `push()`, `toNamed()`, `setTitle()`, `currentTitle` |
| `Gate` | Authorization | `allows()`, `denies()`, `allowsAny(list)`, `allowsAll(list)`, `define()`, `before()`, `policy()` |
| `Session` | Flash data | `flash(data)`, `flashErrors(errors)`, `old(field, [fallback])`, `error(field)`, `errors(field)`, `hasError(field)`, `hasFlash`, `tick()` |
| `Lang` | Localization | `get()`, `locale()` |
| `Vault` | Secure storage | `get()`, `put()`, `delete()`, `flush()` |
| `Storage` | File storage | `disk()`, `put()`, `get()`, `delete()`, `exists()` |
| `Pick` | File picker | `image()`, `file()`, `files()` |
| `Crypt` | Encryption | `encrypt()`, `decrypt()` |
| `Launch` | URL launcher | `url()`, `email()`, `phone()` |
### Controller Lifecycle
| Method | When | Use For |
|--------|------|---------|
| `onInit()` | Controller first created | Fetch initial data, set up streams |
| `onClose()` | Controller being disposed | Cancel streams, clean up resources |
| `refreshUI()` | Manually trigger rebuild | After state changes outside setState helpers |
### RxStatus (State Management)
| Constructor | Type | Convenience Getter |
|-------------|------|-------------------|
| `RxStatus.empty()` | `RxStatusType.empty` | `isEmpty` |
| `RxStatus.loading()` | `RxStatusType.loading` | `isLoading` |
| `RxStatus.success()` | `RxStatusType.success` | `isSuccess` |
| `RxStatus.error(msg)` | `RxStatusType.error` | `isError` |
**State helpers on MagicStateMixin**: `setLoading()`, `setSuccess(data)`, `setError(msg)`, `setEmpty()`, `setState(data, status: ...)`.
### View Types
| Type | Extends | Use When |
|------|---------|----------|
| `MagicView<T>` | `StatelessWidget` | Stateless display, auto-resolves controller |
| `MagicStatefulView<T>` + `MagicStatefulViewState<T, V>` | `StatefulWidget` | Local state needed (forms, TextEditingController, animations) |
| `MagicResponsiveView<T>` | `MagicView<T>` | Device-adaptive layouts with `phone()`, `tablet()`, `desktop()`, `watch()` |
| `MagicResponsiveViewExtended<T>` | `MagicView<T>` | All Wind breakpoints: `xs()`, `sm()`, `md()`, `lg()`, `xl()`, `xxl()` |
| `MagicBuilder<T>` | `StatelessWidget` | Reactive section wrapping a `ValueListenable<T>` |
### Context-Free UI Feedback
| Method | Purpose |
|--------|---------|
| `Magic.snackbar(title, msg, {type, duration})` | Standard snackbar |
| `Magic.success(title, msg)` | Green success snackbar |
| `Magic.error(title, msg)` | Red error snackbar |
| `Magic.toast(msg, {duration})` | Brief toast notification |
| `Magic.dialog<T>(widget, {barrierDismissible})` | Custom dialog, returns `Future<T?>` |
| `Magic.closeDialog()` | Dismiss current dialog |
| `Magic.confirm(title:, message:, {confirmText, cancelText, isDangerous})` | Confirmation dialog, returns `Future<bool>` |
| `Magic.loading({message})` | Persistent loading overlay |
| `Magic.closeLoading()` | Dismiss loading overlay |
| `Magic.isLoading` | Check if loading is shown (getter) |
## 4. Canonical Patterns
Read `references/templates.md` for full annotated Model, Controller, View, StatefulView, ResponsiveView, FormData, ServiceProvider, and Middleware templates.
### Model Skeleton
```dart
class User extends Model with HasTimestamps, InteractsWithPersistence {
@override String get table => 'users';
@override String get resource => 'users';
@override List<String> get fillable => ['name', 'email'];
@override Map<String, dynamic> get casts => {
'created_at': 'datetime',
'settings': 'json',
'status': EnumCast(UserStatus.values), // class-based cast
'tags': ListCast(EnumCast(UserTag.values)), // element-wise list cast
};
@override Map<String, Model Function()> get relations => {'company': Company.new};
int? get id => get<int>('id');
String? get name => get<String>('name');
View on GitHub