Installation
Add the magic package, scaffold the app with dart run magic:dowel magic:install, and call Magic.init() to get the IoC container, facades, Eloquent-style models, routing and auth.
On this page
Meet Magic#
Magic is the full-stack framework for Flutter: Eloquent-style models, auth, routing, realtime and payments, wired and ready on every device. The core package provides the container, facades, models, auth, routing, validation, forms, events, cache, localisation and test fakes. Realtime, payments, SQLite, notifications, deep links, social sign-in, encryption, file picking and the starter kit are official plugin packages, so flutter pub add magic alone does not include them: see Installing Magic for how to add one. Core provides:
- Eloquent-style models: active-record models over your REST API (and over local storage with a plugin).
- Expressive routing: the app's navigation defined with fluent route definitions.
- Service container: dependency injection and service management.
- Facades: static-style access to core services.
- CLI: scaffolding, a
dowelplugin, for controllers, models, views, actions and more. - Wind UI: Tailwind-style utility classes for Flutter widgets.
Why Magic?#
Magic is progressive: start with the container, routing and a controller, and adopt the rest as the app grows.
Familiar to Laravel Developers#
Magic's structure and vocabulary follow Laravel, so the names carry over:
// Routing
MagicRoute.page('/users', () => UserController.instance.index());
MagicRoute.page('/users/:id', (String id) => UserController.instance.show(id));
// Eloquent
final users = await User.all();
final user = await User.find(1);
if (user != null) await user.delete();
// HTTP
final response = await Http.get('/api/users');
if (response.successful) {
// Handle data
}
No BuildContext Needed#
Dialogs and navigation work from controllers, services and plain Dart classes:
// Show dialogs from anywhere
Magic.success('Done!', 'Profile updated successfully');
Magic.dialog(ConfirmationDialog());
Magic.loading();
// Navigate without context
MagicRoute.to('/dashboard');
MagicRoute.back();
Wind UI#
Build UIs with utility-first classes instead of nested widgets:
WDiv(
className: 'flex flex-col p-4 bg-white shadow-lg rounded-xl',
children: [
WText('Hello World', className: 'text-xl font-bold text-primary'),
WButton(
onTap: () => MagicRoute.to('/next'),
className: 'bg-blue-500 hover:bg-blue-600 px-4 py-2 rounded-lg',
child: WText('Get Started', className: 'text-white'),
),
],
)
Requirements#
Magic requires:
- Dart SDK: 3.13.0 or higher
- Flutter: 3.47.0 or higher
Installing Magic#
1. Add the Package#
Add magic to your Flutter project:
flutter pub add magic
This pulls in Magic and its dependencies, including Wind UI (wind_kit) and the CLI. Layers beyond the core are separate plugin packages. Add one with flutter pub add <package> then dart run magic:dowel plugin:install <package>:
| Package | Provides |
|---|---|
magic_local_storage |
SQLite, query builder, migrations, seeders, factories, sync |
magic_encryption |
AES encryption (Crypt) |
magic_picker |
File, image and video picking, share sheet |
magic_socket |
Real-time broadcasting (Echo) |
2. Scaffold Your Application#
The Magic CLI ships as a dowel executable bundled with the magic package (declared in its pubspec.yaml executables:). Run it from any project that depends on magic:
dart run magic:dowel magic:install
No global activation or package-name substitution is needed: dart run magic:dowel resolves magic's bundled executable directly.
The installer adds wind_kit, material_ui and dowel to your pubspec.yaml and scaffolds bin/dispatcher.dart (plus the fast wrapper bin/dowel) through dowel's install. This command creates everything you need:
lib/config/: Configuration files (app, auth, cache, network, logging, routing, view)lib/app/:providers/app_service_provider.dart,providers/route_service_provider.dartandkernel.dart(controllers, models and the rest come from themake:*generators)lib/routes/: Route definitionslib/resources/views/: UI view classeslib/main.dart: Bootstrapped entry point withMagic.init().env/.env.example: Environment configuration
You can exclude features you don't need:
dart run magic:dowel magic:install --without-auth --without-cache
Available flags: --without-auth, --without-network, --without-cache, --without-localization, --without-logging, --preserve (inject the Magic bootstrap into an existing lib/main.dart instead of overwriting it; it rejects a synchronous main(), so make the entry point async first) and --with-devtools (wire magic_devtools, tapir and crumbs into lib/main.dart under !kReleaseMode).
Bootstrapping Your Application#
If you used dart run magic:dowel magic:install, your application is already bootstrapped. The install command generates a ready-to-run main.dart and all configuration files. Here's what was created:
Generated Entry Point#
The generated lib/main.dart initialises Magic and runs your app:
import 'package:material_ui/material_ui.dart';
import 'package:magic/magic.dart';
import 'config/app.dart';
import 'config/view.dart';
import 'config/auth.dart';
import 'config/network.dart';
import 'config/cache.dart';
import 'config/logging.dart';
import 'config/routing.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Magic.init(
configFactories: [
() => appConfig,
() => viewConfig,
() => authConfig,
() => networkConfig,
() => cacheConfig,
() => loggingConfig,
() => routingConfig,
],
);
runApp(
MagicApplication(title: 'My App'),
);
}
The Magic.init() method accepts:
| Parameter | Type | Description |
|---|---|---|
envFileName |
String |
Environment file name (default: .env) |
configFactories |
List<Function> |
Configuration factory functions |
configs |
List<Map> |
Direct configuration maps |
providers |
List<ServiceProvider> |
Additional service providers |
Manual Setup (Without CLI)#
If you prefer to set up manually without the CLI, create a lib/config/app.dart:
import 'package:magic/magic.dart';
final appConfig = {
'app': {
'name': Env.get('APP_NAME', 'Magic App'),
'debug': Env.get('APP_DEBUG', true),
'url': Env.get('APP_URL', 'http://localhost'),
'providers': [
(app) => RouteServiceProvider(app),
(app) => AppServiceProvider(app),
],
}
};
Then initialise in lib/main.dart:
import 'package:material_ui/material_ui.dart';
import 'package:magic/magic.dart';
import 'config/app.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Magic.init(
configFactories: [() => appConfig],
);
runApp(MagicApplication(title: 'My App'));
}
The Application Widget#
MagicApplication is your root widget. It handles routing, themes, localisation, and overlays automatically:
runApp(
MagicApplication(
title: 'My App',
debugShowCheckedModeBanner: false,
),
);
To keep a widget on screen across every page, such as a floating player, pass a builder; see A Layer Above Every Page.
Wind UI Plugin#
Magic includes Wind UI (wind_kit), a utility-first styling engine with Tailwind-style syntax. Instead of nesting widgets, you compose UIs with className strings like flex flex-col p-4 bg-white rounded-xl shadow-md.
Upgrading from 0.0.x#
The CLI, the styling package, the SDK floor and four layers changed in 1.0.0. See the changelog for the full list; the steps an app needs are:
- Raise the SDK floor to Dart 3.13 and Flutter 3.47, then swap the packages:
dart pub remove fluttersdk_artisan fluttersdk_windanddart pub add dowel wind_kit material_ui. Runflutter pub add magicso themagicconstraint moves to the 1.x line (a^0.0.xconstraint does not admit it). fluttersdk_windis nowwind_kit: changepackage:fluttersdk_wind/fluttersdk_wind.darttopackage:wind_kit/wind_kit.dart.MaterialApp,ThemeDataandColorscome frompackage:material_ui/material_ui.dart, because wind_kit builds on it and the two sets of types do not mix: rundart fix --apply --code=deprecated_member_useto swap the imports.- Move the project state from
.artisan/to.dowel/, so the install records and their file hashes survive. Delete.dowel/plugins.jsonand the records under.dowel/installed/that belong to renamed packages (fluttersdk_*). Delete the generatedbin/dispatcher.dart,bin/fsa, a leftoverbin/artisan.dart,lib/app/_plugins.g.dartandlib/app/commands/_index.g.dart. In your own commands and providers replacepackage:fluttersdk_artisan/artisan.dartwithpackage:dowel/dowel.dartandArtisan*classes withDowel*. - Run
dart run dowel install, which scaffoldsbin/dispatcher.dartandbin/dowel, thendart run dowel commands:refresh. Register magic and each plugin again so they land in.dowel/plugins.json:dart run dowel plugin:install magic, thendart run dowel plugin:install <package>. A file a renamed plugin published earlier is reported as anunmanaged-fileconflict: review it, then re-run with--force. - From then on run the CLI as
dart run magic:dowel <command>. It hands over to yourbin/dispatcher.dart, so plugin commands resolve. Ifdart run dowel installran beforemagicwas in the project, the dispatcher has no magic provider: rundart run dowel plugin:install magic. - Add the plugin for each layer your app used that left core, with
flutter pub add <package>thendart run magic:dowel plugin:install <package>:Cryptis inmagic_encryption;Pickand theMagicFile.toXFile()andXFile.toMagicFile()conversions are inmagic_picker;Echo, the broadcasting manager andListensToBroadcastsare inmagic_socket;DB,Schema,Model.query(), migrations, seeders, factories,SyncFeedandSyncLedgerare inmagic_local_storage. An app installed on 0.0.41 haslib/config/database.dartandlib/config/broadcasting.dart: back up any customised settings in them and move those settings into the matching plugin configuration, then delete them beforeplugin:install magic_local_storageorplugin:install magic_socket, because the plugins exportdatabaseConfigandbroadcastingConfigand the import becomes ambiguous otherwise. - Replace what no longer exists in core:
Magic.seedbecomesSeeding.run(List<Seeder>)frommagic_local_storage;make:controller --broadcastsis gone, so mixListensToBroadcastsinto the controller by hand;--without-databaseand--without-broadcastingare gone frommagic:install. A model withuseLocal => truethrows aStateErrornamingmagic_local_storageuntil that plugin is installed, andStorage.downloadon IO throwsUnsupportedErroruntilmagic_pickeris installed.
The dowel changelog for 0.0.21 carries the full recipe, including the .mcp.json cleanup.
Related Documentation#
- Configuration: how Magic's configuration system works.
- Directory Structure: the recommended project layout.
- Service Providers: registering and booting services.
- Routing: defining your application's navigation.
- Controllers: handling user interactions and business logic.
- Eloquent ORM: working with your models.
- Magic CLI:
magic:installand themake:*generators.