Navigator class Null safety

Navigator widget.

Rad comes with a powerful Navigator widget that carries out three big tasks for you:

  • Routing
  • Deep linking
  • Single page experience (no page reloads when user hit forward/back buttons)

Deep linking and Single page experience in action

And most importantly, all three tasks are carried out without any special configuration or management from developer side. That is, Framework will automatically deep link your Navigators, and route requests to the correct ones no matter how deeply nested your Navigators are.

Let's talk about Navigator's syntax:

Navigator(

    // required

    routes: [
        ...
    ],


    // both are optional

    onInit: (NavigatorState state) {

    }

    onRouteChange: (String name) {

    }
)

routes:[]

This property takes list of Routes. What is a Route? in simplified view, a Route consists of two things,

  1. Name of the route e.g 'home', 'settings'

  2. Contents to show on route e.g some widget

To declare a route, there's actually a Route widget which simply wraps both parts of route into a single widget that Navigator can manage.

routes: [

    Route(name: "home", page: HomePage()),

    Route(name: "edit", page: SomeOtherWidget())

    ...
]

Above, we've defined two routes, home and edit.

Navigator basic Understanding

Since Navigator's route is basically a widget that have a name attached to it, those routes will be treated as child widgets of Navigator just like Span can have its childs. Difference being, Navigator's childs(Route widgets) are built in a lazy fashion(only when requested). This also means that Navigator do not stack duplicate pages. All Route widgets are built only once. That is when you open a route, if route widget doesn't exists, it'll create it else it'll use the Route widget that's already built.

NavigatorState

Navigator widget creates a state object. State object provides methods which you can use to jump between routes, open routes and things like that. To access a Navigator's state object, there are two methods:

  1. If widget from where you accessing NavigatorState is in child tree of Navigator then use Navigator.of(context). This method will return NavigatorState of the nearest ancestor Navigator from the given BuildContext.

  2. For accessing state in parent widget of Navigator, use onInit hook of Navigator:

     class SomeWidget extends StatelessWidget
     {
         @override
         build(context)
         {
             return Navigator(
                 onInit: _onInit,
                 ...
             )
         }
    
         _onInit(NavigatorState state)
         {
             // do something with state
         }
     }
    

n. Also, here's how you can look for a specific Navigator instance:

```dart
// 1. Give Navigator instance a global/local key

var key = GlobalKey('my-navigator');

Navigator(key: key)

// 2. Use of(context, key) anywhere in the subtree of that Navigator,

Navigator.of(context, key);

// or

Navigator.of(context, GlobalKey('my-navigator'));

```

onRouteChange hook:

This hooks gets called when Navigator opens a route. This allows Navigator's parent to do something when Navigator that it's enclosing has changed. for example, you could've a header and you can change active tab when Navigator's route has changed.

Navigator(
    onRouteChange: (name) => print("changed to $name");
    ...
);

Jumping to a Route

To go to a route, use open method of Navigator state. We could've named it push but open conveys what exactly Navigator do when you jump to a route. When you call open, Navigator will build route widget if it's not already created. Once ready, it'll bring it to the top simply by hiding all other Route widgets that this Navigator is managing.

Navigator.of(context).open(name: "home");

Going back

Going back means, opening the Route that was closed previously.


Navigator.of(context).open(name: "home")
Navigator.of(context).open(name: "profile")
Navigator.of(context).open(name: "home")

Navigator.of(context).back() // ->  open profile
Navigator.of(context).back() // ->  open home
Navigator.of(context).back() // ->  error, no previous route!

// helper method to prevent above case:

Navigator.of(context).canGoBack() // ->  false, since no previous route

Passing values between routes

Values can be passed to a route while opening that route:

Navigator.of(context).open(name: "home", values: {"id": "123"});

Then on homepage, value can be accessed using getValue:

var id = Navigator.of(context).getValue("id");
// "123"

Passing multiple values:

Navigator.of(context).open(
  name: "home", values: {"id": "123", "username" : "adamback"}
);

On homepage,

var id = Navigator.of(context).getValue("id"); // -> "123"
var username = Navigator.of(context).getValue("username"); // -> "adamback"

Cool thing about Navigator is that values passed to a route will presist during browser reloads. If you've pushed some values while opening a route, those will presist in browser history too.

Mangaging state in Routes:

Since Navigator do not duplicate pages, you don't have to parameterize your page content, instead pass values on open:

// rather than doing this
Route(name: "profile", page: Profile(id: 123));

// do this
Route(name: "profile", page: Profile());

// and when opening profile route
Navigator.of(context).open(name: "profile", values: {"id": "123"});

// on profile page
var id = Navigator.of(context).getValue("id");

But remember since routes are built only once, you've to re-initialize state in your ProfilePage widget when id changes. One way is to make your ProfilePage a StatefulWidget and re-initialize state in State.didChangeDependencies when you see that id has changed.

Here's an example:

class ProfilePageState ...
{
  var _userId = '0';

  @override
  void initState() {
    // intialize here, all things that don't depend on Navigator
  }

  @override
  void didChangeDependencies()
  {
    // initialize here, all things that depends on Navigator

    // this method gets called when your app do Navigator.open(name: 'profile')
    // you can re-initialize page state here if page is opened with different values
    // remember, you don't have to call setState inside this method as framework always
    // call build method on this widget after calling this method.

    var newValue = Navigator.of(context).getValue('id');

    // if user id has changed(means your app has opened profile page with
    // a different id value)
    if(newValue != _userId) {
      // change widget state
      _userId = newValue;
      // fetch user from server, or other things that depends
      // on user id.
    }
  }
}

See also:

Inheritance

Constructors

const

Properties

correspondingTag DomTagType
Corresponding HTML tag to use to render this widget
@nonVirtual, read-only, override
hashCode int
The hash code for this object.
read-only, inherited
initialKey Key
final, inherited
onInit NavigatorStateCallback?
Called when Navigator state is created.
final
onRouteChange NavigatorRouteChangeCallback?
Called when Navigator's route changes.
final
routes List<Route>
Routes that this Navigator instance handles.
final
runtimeType Type
A representation of the runtime type of the object.
read-only, inherited
widgetCaptureEventListeners Map<DomEventType, EventCallback?>
Events that this widget is listening to in capturing phase.
read-only, inherited
widgetChildren List<Widget>
Child widgets if any.
read-only, inherited
widgetEventListeners Map<DomEventType, EventCallback?>
Events that this widget is listening to.
read-only, inherited
widgetType String
Type of widget.
@nonVirtual, read-only, override

Methods

createConfiguration() WidgetConfiguration
Create widget's configuration.
inherited
createRenderObject(BuildContext context) RenderObject
Called when framework needs a RenderObject for current widget.
override
isConfigurationChanged(WidgetConfiguration oldConfiguration) bool
Whether configuration has changed.
@nonVirtual, override
noSuchMethod(Invocation invocation) → dynamic
Invoked when a non-existent method or property is accessed.
inherited
toString() String
A string representation of this object.
inherited

Operators

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

Static Methods

of(BuildContext context, [Key? navigatorKey]) NavigatorState
Navigator's state from the closest instance of this class that encloses the given context.