- name
- magic-framework
- description
- Magic Framework -- Laravel-inspired Flutter framework with IoC Container, Facades, Eloquent ORM, Service Providers, and GoRouter wrapper. ALWAYS activate for: Magic.init, MagicApp, MagicController, MagicView, MagicStatefulView, MagicStatefulViewState, MagicResponsiveView, MagicFormData, MagicForm, MagicBuilder, MagicRoute, MagicResponse, Eloquent Model, InteractsWithPersistence, HasTimestamps, ServiceProvider, MagicMiddleware, MagicStateMixin, ValidatesRequests, RxStatus, SimpleMagicController, Auth facade, Http facade, Config facade, Cache facade, DB facade, Gate facade, Log facade, Event facade, Lang facade, Schema facade, Vault facade, Storage facade, Pick facade, Crypt facade, Launch facade, Route facade, MagicCan, MagicCannot, MagicApplication, MagicAppWidget, MagicRouterOutlet, RouteServiceProvider, Kernel, Magic.findOrPut, Magic.make, Magic.bind, Magic.singleton, Magic.put, Magic.find, Magic.delete, Magic.snackbar, Magic.success, Magic.error, Magic.toast, Magic.confirm, Magic.dialog, Magic.loading, Magic.closeLoading, Magic.reload, Magic.seed, Magic.flush, Magic.view, Carbon, trans(), env(), rules(), handleApiError, setErrorsFromResponse, MagicViewRegistry, MagicFeedback, dart run magic:magic, magic install, make:model, make:controller, make:view, make:migration, make:enum, make:event, make:listener, make:middleware, make:factory, make:seeder, make:provider, make:policy, make:request, make:lang, key:generate, magic_deeplink, DeeplinkManager, DeeplinkHandler, DeeplinkDriver, RouteDeeplinkHandler, magic_notifications, Notify, NotificationManager, DatabaseNotification, Notifiable, PushDriver, NotificationChannel, magic_social_auth, SocialAuth, SocialAuthManager, SocialDriver, SocialAuthButtons, SocialToken, magic_starter, MagicStarter, MagicStarterServiceProvider, MagicStarterViewRegistry. Use for ANY Flutter project built on the Magic framework.
# 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`, `Lang`, `MagicRoute`, `Gate`, `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 (17 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()`, `to()`, `back({fallback?})`, `replace()`, `push()`, `toNamed()` |
| `Gate` | Authorization | `allows()`, `denies()`, `define()`, `policy()` |
| `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. Templates
### Model
```dart
import 'package:magic/magic.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',
'avatar_url',
];
@override
Map<String, String> get casts => {
'settings': 'json',
'is_active': 'bool',
'created_at': 'datetime',
'updated_at': 'datetime',
};
@override
Map<String, Model Function()> get relations => {
'company': Company.new,
'posts': Post.new,
};
// Typed getters/setters
int? get id => get<int>('id');
String? get name => get<String>('name');
set name(String? v) => set('name', v);
String? get email => get<String>('email');
set email(String? v) => set('email', v);
String? get avatarUrl => get<String>('avatar_url');
Carbon? get createdAt => get<Carbon>('created_at');
// Relations
Company? get company => getRelation<Company>('company');
List<Post> get posts => getRelations<Post>('posts');
// Static query methods
static User fromMap(Map<String, dynamic> map) {
Ver no GitHub