Session & Flash Data
The Session facade provides flash data that survives exactly one navigation hop, letting you pass form input and validation errors across a back-navigation without temporary controller state.
On this page
Introduction#
The Session facade provides Laravel-style flash data that survives exactly one navigation hop. Use it to repopulate a form after a failed submit and a back navigation without wiring temporary state through controllers, the router, or global singletons.
The store has two buckets: the current bucket (readable by the view being built right now) and the next bucket (being collected by the currently-active handler). A call to Session.tick() promotes next into current, so flashed data is visible on exactly one frame.
Flashing Input#
Flash a map of values before navigating away:
Session.flash({
'name': 'John',
'email': '[email protected]',
});
MagicRoute.back();
Flashing Errors#
Flash per-field error messages:
Session.flashErrors({
'email': ['The email has already been taken.'],
});
Reading Old Input#
In the form view, repopulate via the old() helper:
WFormInput(
initialValue: old('email') ?? '',
);
if (Session.hasError('email'))
WText(Session.error('email')!, className: 'text-red-500');
Session.oldRaw(field) returns the original typed value (booleans, numbers, custom objects) instead of stringifying it.
Automatic Flash in MagicFormData#
MagicFormData.validate() automatically flashes the current form data when validation fails, so you never need to wire it manually:
void _submit() {
if (!form.validate()) return; // form data is auto-flashed on failure
controller.register(form.data);
}
After a back navigation and a Session.tick(), old('email') returns the last-submitted value.
Advancing the Flash Bucket#
Flash data survives exactly one navigation. Advance the bucket only when the router location actually changes. Do not wire Session.tick directly to the router delegate listener: the delegate can notify for more than real navigation (redirect re-evaluation, notifier rebuilds) and would expire flash data prematurely.
var lastLocation = MagicRouter.instance.currentLocation;
MagicRouter.instance.routerConfig.routerDelegate.addListener(() {
final currentLocation = MagicRouter.instance.currentLocation;
if (currentLocation == lastLocation) return;
lastLocation = currentLocation;
Session.tick();
});
Place this in a ServiceProvider.boot() after Magic.init() completes.
Testing#
In tests, always reset the store in setUp():
setUp(() {
MagicApp.reset();
Magic.flush();
Session.reset();
});
Swap the backing store with a custom SessionStore via Session.setStore(store) if you need isolated buckets per test group.