Skip to content

Configuration

View raw .md
On this page

Introduction#

The magic_starter plugin is configured via a single Dart file at lib/config/magic_starter.dart in your consumer project. This file is generated automatically when you run the install command, and contains all feature toggles, route prefixes, auth identity settings, and locale defaults your app needs.

bash
dart run magic_starter:install

The install command scaffolds lib/config/magic_starter.dart with all features defaulting to false. Enable only the features your app requires — each toggle controls route registration, UI visibility, and controller behaviour.

The Config File#

The generated lib/config/magic_starter.dart exports a single top-level getter that returns a nested Map<String, dynamic>:

dart
Map<String, dynamic> get magicStarterConfig => {
  'magic_starter': {
    'features': {
      'teams': false,
      'registration': false,
      'extended_profile': false,
      'profile_photos': false,
      'social_login': false,
      'two_factor': false,
      'sessions': false,
      'phone_otp': false,
      'newsletter': false,
      'notifications': false,
      'email_verification': false,
      'guest_auth': false,
      'timezones': false,
      'billing': false,
      'support': false,
      'releases': false,
      'app_update': false,
      'handoff': false,
    },
    'auth': {
      'email': true,
      'phone': false,
    },
    'defaults': {
      'locale': 'en',
      'timezone': 'UTC',
    },
    'localization': {
      'apply_user_locale': true,
    },
    'supported_locales': [
      'en',
      'tr',
    ],
    'routes': {
      'home': '/',
      'login': '/auth/login',
      'auth_prefix': '/auth',
      'teams_prefix': '/teams',
      'profile_prefix': '/settings',
      'notifications_prefix': '/notifications',
      'billing': '/teams/billing',
    },
    'billing': {
      'web_origin': null,
    },
    'notifications': {
      'external_id_prefix': 'user_',
    },
    'handoff': {
      'confirm_without_session': true,
      'signed_out_route': null,
    },
    'legal': {
      'terms_url': null,
      'privacy_url': null,
    },
  },
};

Register it with Magic.init() alongside your other config factories:

dart
await Magic.init(
  configFactories: [
    () => appConfig,
    () => magicStarterConfig,
  ],
);

Feature Toggles#

Every feature defaults to false and must be explicitly enabled. Each toggle controls whether routes are registered, UI sections are rendered, and controller logic is activated.

Key Default Description
teams false Team creation, management, member invitations, and team switching.
registration false User registration screen and route.
extended_profile false Additional profile fields: phone, timezone, and language preferences.
profile_photos false Profile photo upload and removal in the profile settings.
social_login false Social login: provider buttons on the login and register screens, the Connected accounts settings page, and the provider option of the identity-confirmation dialog. Needs a bridge registered with MagicStarter.useSocialAuth(...) (see Social Login); the flag alone renders nothing.
two_factor false Two-factor authentication setup, challenge screen, and recovery codes.
sessions false Browser sessions list with logout/revoke capability.
phone_otp false Phone-based OTP authentication flow (send code, verify).
newsletter false Newsletter subscribe/unsubscribe toggle in the profile.
notifications false In-app notification list, unread badge, and preferences screen.
email_verification false Email verification banner and resend verification flow.
guest_auth false The login screen's "Continue as guest" button, plus the listeners that record a guest sign-in and claim that guest's data when an account signs in. It adds no routes or screens, and MagicStarterGuestAuthController works without it.
timezones false Timezone selection with debounced async API search.
support false Support desk screens (/support, /support/new, /support/:id), the Support profile-menu item and the Contact sales conversation. The host registers the routes with registerMagicStarterSupportRoutes(); see Support.
releases false The "What's new" changelog page (/settings/changelog), its settings hub row and the MagicStarterAppVersion caption, read from the backend's GET releases. Needs Features::releases() on magic-starter-laravel; see Releases.
app_update false MagicStarterAppUpdateBanner in the app layout: Reload when a new web deploy appears in version.json, Update when the store has a newer build than a store-installed release build. The store path reads GET releases and needs Features::releases() on the backend; see Releases.
handoff false The sign-in handoff page /auth/handoff?code=&next=: trades a one-time code for a session, after asking "Sign in as ...?", and opens next. handoff.confirm_without_session (default true) keeps that question when nobody is signed in, which is what stops a link carrying an attacker's own code from signing a visitor in to the attacker's account; false only saves the tap. handoff.signed_out_route (default null, meaning routes.login) is where a signed-out visit without a code goes, carrying ?next=. See Sign-in handoff.

To enable features, set their values to true in lib/config/magic_starter.dart:

dart
'features': {
  'teams': true,
  'registration': true,
  'notifications': true,
  // ... remaining features stay false
},

Route Customization#

The routes block controls all URL paths registered by Magic Starter. Change these values to match your app's URL structure:

dart
'routes': {
  'home': '/',
  'login': '/auth/login',
  'auth_prefix': '/auth',
  'teams_prefix': '/teams',
  'profile_prefix': '/settings',
  'notifications_prefix': '/notifications',
},
Key Default Description
home '/' Redirect target after successful login.
login '/auth/login' Login screen path; used by RedirectIfAuthenticated middleware.
auth_prefix '/auth' Prefix for all auth routes (login, register, forgot, reset, two-factor challenge).
teams_prefix '/teams' Prefix for team routes (create, settings).
profile_prefix '/settings' Prefix for profile routes (profile, notifications preferences).
notifications_prefix '/notifications' Prefix for the notifications list page.

The config class also provides computed route paths built from these prefixes:

dart
MagicStarterConfig.teamCreateRoute();          // '/teams/create'
MagicStarterConfig.teamSettingsRoute();         // '/teams/settings'
MagicStarterConfig.profileRoute();              // '/settings/profile'
MagicStarterConfig.notificationsRoute();        // '/notifications'
MagicStarterConfig.notificationPreferencesRoute(); // '/settings/notifications'
MagicStarterConfig.twoFactorChallengeRoute();   // '/auth/two-factor-challenge'

Billing#

The billing feature gates the teams.billing view, which sells subscriptions through magic_payments. It is off by default and it is gated on its OWN toggle rather than on teams: a subscription is bought by whoever holds the account, so an app with no team features can still sell one.

dart
'features': {
  'billing': true,
},
'routes': {
  'billing': '/teams/billing',
},
'billing': {
  'web_origin': 'https://app.example.com',
},

The origin is the host app's own web origin, with no trailing slash. It is only read on the web rail; a store purchase on iOS or Android never touches it.


The push external id#

With the notifications feature on, this package declares who the device is subscribed as every time a session begins, and releases it on every sign-out, including the ones no call site performs (account deletion, and a session that simply expires). You wire nothing.

What it declares is the prefix below followed by the authenticated user's id:

dart
'notifications': {
  'external_id_prefix': 'user_',
},

Change it when one OneSignal app serves more than one kind of subject, and change the backend with it.

dart run magic:dowel starter:doctor prints the prefix this config resolves to, so the two sides can be read against each other:

text
Push external id prefix: user_ (default, key not set)
Push external id prefix: user_ (blank, using default)
Push external id prefix: operator-

The first two resolve to the same prefix and are not the same situation: the second means the value you wrote is being discarded, which "key not set" would hide.

It is printed rather than checked, and never fails: any prefix is valid as long as the backend uses the same one, and nothing inside this repository can see the other side to compare. --verbose adds the key name and the file to compare it against. The value comes from this config FILE, so an app that writes the key at runtime through Config.set is not covered; the command boots nothing and has no container to ask. Whole-line // comments are skipped, so a commented-out old key above the live one does not win; a block comment around it, or a key written outside the notifications block, is not covered.


Auth Identity Modes#

The auth block controls which identity fields are shown on login and registration forms:

dart
'auth': {
  'email': true,
  'phone': false,
},

There are three supported modes:

Email only (default) — users authenticate with email and password:

dart
'auth': {
  'email': true,
  'phone': false,
},

Phone only — users authenticate with phone number:

dart
'auth': {
  'email': false,
  'phone': true,
},

Both — both fields are shown, but phone takes precedence in the login payload when both are provided:

dart
'auth': {
  'email': true,
  'phone': true,
},

Access identity mode at runtime:

dart
final emailEnabled = MagicStarterConfig.emailIdentity(); // true
final phoneEnabled = MagicStarterConfig.phoneIdentity(); // false

Locale & Timezone Defaults#

The defaults and supported_locales sections configure localisation behaviour:

dart
'defaults': {
  'locale': 'en',
  'timezone': 'UTC',
},
'supported_locales': [
  'en',
  'tr',
],
Key Default Description
defaults.locale 'en' Default locale for new users.
defaults.timezone 'UTC' Default timezone for new users.
localization.apply_user_locale true Whether a signed-in user's saved locale attribute is applied to the running app automatically, on sign-in and on restore. Set false to own locale switching yourself. See Locale Application.
supported_locales ['en', 'tr'] Locales available in the language selector (when extended_profile is enabled).

Access these values at runtime:

dart
final locale = MagicStarterConfig.defaultLocale();       // 'en'
final timezone = MagicStarterConfig.defaultTimezone();    // 'UTC'
final locales = MagicStarterConfig.supportedLocales();    // ['en', 'tr']
final applies = MagicStarterConfig.applyUserLocale();     // true

The legal block provides optional links displayed on auth screens:

dart
'legal': {
  'terms_url': 'https://example.com/terms',
  'privacy_url': 'https://example.com/privacy',
},

Accessing Values#

At runtime, all values are accessible in two ways.

Via MagicStarterConfig static methods — the recommended approach with type safety and defaults:

dart
// Feature checks
final teamsEnabled = MagicStarterConfig.hasTeamFeatures();
final registrationEnabled = MagicStarterConfig.hasRegistrationFeatures();
final twoFactorEnabled = MagicStarterConfig.hasTwoFactorFeatures();

// Route paths
final loginPath = MagicStarterConfig.loginRoute();
final profilePath = MagicStarterConfig.profileRoute();

// Identity
final emailAuth = MagicStarterConfig.emailIdentity();
final phoneAuth = MagicStarterConfig.phoneIdentity();

// Defaults
final locale = MagicStarterConfig.defaultLocale();
final timezone = MagicStarterConfig.defaultTimezone();

// Legal
final termsUrl = MagicStarterConfig.termsUrl();
final hasLegal = MagicStarterConfig.hasLegalLinks();

Via Config.get() dot notation — for dynamic access or when you need the raw value:

dart
final teamsEnabled = Config.get<bool>('magic_starter.features.teams', false);
final authPrefix = Config.get<String>('magic_starter.routes.auth_prefix', '/auth');
final termsUrl = Config.get<String?>('magic_starter.legal.terms_url', null);
esc

Search index did not load.

No match for "".

Try a class like rounded-lg or a widget like WButton.