RouteRestorable<T extends RouteTarget> mixin

A mixin that enables custom state restoration for routes that cannot be fully represented by a URI.

What This Mixin Does

RouteRestorable extends RouteTarget to provide explicit control over how a route is serialized to and deserialized from restoration data. While RouteUnique routes can only be saved as URI strings, RouteRestorable routes can preserve arbitrary complex state through custom serialization logic. This is essential for routes that carry rich data objects, computed state, or information that doesn't belong in a URL.

Where to Use This Mixin

Apply this mixin to any route class that needs to preserve state beyond what can be encoded in a URI. This is particularly common for:

  • Detail pages with complex loaded data (avoiding refetching on restore)
  • Routes with form state or user input that's not yet submitted
  • Routes with computed or derived state that's expensive to recreate
  • Routes with objects from your domain model (products, users, documents, etc.)

When Restoration Happens

Serialization (serialize is called): Whenever the coordinator's state changes and Flutter needs to save navigation state to the restoration bucket. This happens automatically in the background, typically when the app goes to the background, the user switches to another app, or the system needs to free memory.

Deserialization (deserialize is called): During app launch when Flutter detects existing restoration data. This happens when the system restores your app after terminating it, when the user force-quits and relaunches the app (on some platforms), or during development when hot restart occurs with restoration enabled.

How to Implement Custom Restoration

Complete implementation example combining all required pieces:

// 1. Define your route with RouteRestorable
class BookDetailRoute extends AppRoute with RouteRestorable<BookDetailRoute> {
  BookDetailRoute({required this.book});

  final Book book;  // Complex object that can't be in URL

  @override
  RestorationStrategy get strategy => RestorationStrategy.converter;

  @override
  RestorableConverter<BookDetailRoute> get converter => const BookDetailConverter();

  @override
  String get restorationId => 'book_${book.id}';

  @override
  Uri toUri() => Uri.parse('/books/${book.id}');
}

// 2. Implement the converter
class BookDetailConverter extends RestorableConverter<BookDetailRoute> {
  const BookDetailConverter();

  @override
  String get key => 'book_detail';

  @override
  Map<String, dynamic> serialize(BookDetailRoute route) => {
    'id': route.book.id,
    'title': route.book.title,
    'author': route.book.author,
  };

  @override
  BookDetailRoute deserialize(Map<String, dynamic> data) => BookDetailRoute(
    book: Book(
      id: data['id'],
      title: data['title'],
      author: data['author'],
    ),
  );
}

// 3. Register in your coordinator
class AppCoordinator extends Coordinator<AppRoute> {
  @override
  void init() {
    super.init();
    defineRestorableConverter('book_detail', BookDetailConverter.new);
  }
}

Important Considerations

Converter keys must be globally unique and stable: Never change a converter's key in production as it will break restoration for existing users. Prefix keys with your app or package name to avoid collisions.

Serialize efficiently: Only serialize the minimum data needed to reconstruct the route. Large serialized data slows down app startup during restoration.

Handle missing data gracefully: Your deserialize method should handle cases where data might be missing or invalid from older app versions. Provide sensible defaults or fail gracefully.

See also:

Superclass constraints

Properties

converter RestorableConverter<T>
The converter to use when restorationStrategy is RestorationStrategy.converter.
no setter
hashCode int
The hash code for this object.
no setterinherited
isPopByPath bool
Whether the pop was initiated by the path mechanism.
getter/setter pairinherited
onResult Completer<Object?>
no setterinherited
props List<Object?>
Properties used for equality comparison.
no setterinherited
restorationId String
The unique identifier for this route in the restoration system.
no setter
restorationStrategy RestorationStrategy
The restoration strategy to use for this route.
no setter
resultValue Object?
The result value passed when this route was popped.
no setterinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited
stackPath StackPath<RouteTarget>?
The StackPath that contains this route.
no setterinherited

Methods

bindResultValue(Object? value) → void
inherited
bindStackPath(StackPath<RouteTarget> path) → void
Binds the route to a path.
inherited
clearStackPath() → void
Clears the path binding.
inherited
compareWith(Object other) bool
Compares this object with another for equality.
inherited
completeOnResult(Object? result, covariant CoordinatorCore<RouteUri>? coordinator, [bool failSilent = false]) → void
Completes the route's result future.
inherited
deepEquals(RouteTarget other) bool
Whether other is the same lifecycle entry as this route.
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
onDidPop(Object? result, covariant CoordinatorCore<RouteUri>? coordinator) → void
inherited
onDiscard() → void
Called when the route is discarded without being displayed.
inherited
onUpdate(covariant RouteTarget newRoute) → void
Called when this route is updated with state from a new route instance.
inherited
serialize() Map<String, dynamic>
Serializes a RouteRestorable route into a map that can be persisted.
toString() String
A string representation of this object.
inherited

Operators

operator ==(Object other) bool
The equality operator.
inherited

Static Methods

deserialize<T extends RouteTarget>(Map<String, dynamic> data, {required RouteUriParserSync<T>? parseRouteFromUri, required RestorableConverterLookupFunction getRestorableConverter}) → T
Deserializes restoration data back into a RouteTarget instance.