NavigatorResizable class

A thin wrapper around Navigator that visually resizes the child navigator to match the size of the content displayed in the current route.

This widget is functionally similar to combining OverflowBox and ClipRect, but it is specifically designed for this use case. It adjusts its size, hit test area, and painting area to align with the size of the widget displayed by the child navigator's current route. The navigator itself can overflow this widget, maintaining its size as determined by the parent constraints unless those constraints change. This helps minimize unnecessary layout operations for the navigator and its routes.

Routes and Pages

The NavigatorResizable can respect the content size of a route only if the route mix-ins the ObservableRouteMixin and its content is wrapped in a ResizableNavigatorRouteContentBoundary. This is especially important during route transitions, as the NavigatorResizable can animate its size in sync with the transition animation only when both the current route and the next route satisfy these requirements. Otherwise, the size remains unchanged before and after the transition.

For convenience, the following built-in route and page classes are provided, all of which satisfy the requirements of NavigatorResizable:

Note that the child navigator and its routes are constrained by the constraints imposed by the parent widget of the NavigatorResizable. To ensure that the route content fills the entire available space, the easiest way is to set the content widget's width or height to double.infinity.

ResizableMaterialPageRoute(
  builder: (context) {
    return Container(
      color: Colors.while,
      width: double.infinity,
      height: double.infinity,
    );
  },
);

For more advanced use cases, you can create a custom route compatible with NavigatorResizable by mixing in the ObservableRouteMixin and returning a ResizableNavigatorRouteContentBoundary in ModalRoute.buildPage.

class CustomResizableRoute<T> extends ModalRoute<T>
  with ObservableRouteMixin<T>{
  CustomResizableRoute({
    required super.builder,
    ...
  });

  @override
  Widget buildContent(BuildContext context) {
    return ResizableNavigatorRouteContentBoundary(
      child: builder(context),
    );
  }
}

Caveats

  • Avoid wrapping the navigator in widgets that add additional space (e.g., Padding). Zero-size widgets, such as GestureDetector or InheritedWidget, are acceptable.
  • Do not place NavigatorResizable inside a widget with a tight constraint, as this forces NavigatorResizable to ignore the size of the current route's content and adopt the size dictated by the constraints. In such cases, an assertion error will be thrown. Typically, Center and Align are good choices for the parent widget.
  • The initial route of the child navigator must satisfy the requirements of NavigatorResizable. Otherwise, NavigatorResizable will be unable to determine the initial size and will throw an assertion error.

Example

The following example demonstrates a resizable window centered within a Scaffold that can display multiple pages:

Navigator nestedNavigator;
return Scaffold(
  body: Center(
    child: Material(
      color: Colors.white,
      child: NavigatorResizable(
        child: nestedNavigator,
      ),
    ),
  ),
);

You can use any standard navigation methods, such as Navigator.push, Navigator.pop, named routes, and the Pages API, with NavigatorResizable as you would with a regular Navigator:

Navigator.push(
  context,
  ResizableMaterialPageRoute(
    builder: (context) {
      return Container(
        color: Colors.red,
        width: 300,
        height: 300,
      );
    },
  ),
);

For more practical examples, refer to the /example directory.

Inheritance

Constructors

Creates a thin wrapper around Navigator that visually resizes the child navigator to match the size of the content displayed in the current route.
const

Properties

child Widget
The Navigator for which the visual resizing should be applied.
final
hashCode int
The hash code for this object.
no setterinherited
interpolationCurve Curve
The Curve used for interpolating the size of this widget during a route transition animation.
final
key Key?
Controls how one widget replaces another widget in the tree.
finalinherited
runtimeType Type
A representation of the runtime type of the object.
no setterinherited

Methods

createElement() StatefulElement
Creates a StatefulElement to manage this widget's location in the tree.
inherited
createState() State<NavigatorResizable>
Creates the mutable state for this widget at a given location in the tree.
override
debugDescribeChildren() List<DiagnosticsNode>
Returns a list of DiagnosticsNode objects describing this node's children.
inherited
debugFillProperties(DiagnosticPropertiesBuilder properties) → void
Add additional properties associated with the node.
inherited
noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
toDiagnosticsNode({String? name, DiagnosticsTreeStyle? style}) DiagnosticsNode
Returns a debug representation of the object that is used by debugging tools and by DiagnosticsNode.toStringDeep.
inherited
toString({DiagnosticLevel minLevel = DiagnosticLevel.info}) String
A string representation of this object.
inherited
toStringDeep({String prefixLineOne = '', String? prefixOtherLines, DiagnosticLevel minLevel = DiagnosticLevel.debug, int wrapWidth = 65}) String
Returns a string representation of this node and its descendants.
inherited
toStringShallow({String joiner = ', ', DiagnosticLevel minLevel = DiagnosticLevel.debug}) String
Returns a one-line detailed description of the object.
inherited
toStringShort() String
A short, textual description of this widget.
inherited

Operators

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