banner
jzman

jzman

Coding、思考、自觉。
github

Using the Navigator Component in the Flutter Series

PS: It is easy to want to do something, but it is difficult to actually do it.

The Navigator uses a stack rule to manage Widgets, recording the page access history, and can be used to perform navigation operations between pages.

In Android development, what we usually refer to as navigation is the transition between Activities, also known as page navigation. In Flutter, it refers to the transition between Routes. Pages in Android correspond to Routes in Flutter. The Navigator is responsible for managing the stack of Route objects and provides methods to manage the stack, such as Navigator.push and Navigator.pop.

As mentioned above, Flutter provides methods for pushing and popping Routes. Some Android devices have a back button, which is compatible with Flutter's Navigator.push and Navigator.pop methods. If certain devices do not have a corresponding back button, a back button can be added to the AppBar. The Scaffold already includes a back button that triggers the Navigator.pop operation. The content of this article is as follows:

  1. Basic route navigation
  2. Route parameter passing
  3. Other route navigation

Basic Route Navigation#

As mentioned earlier, Flutter uses a stack to manage Widgets, with the methods for pushing and popping being Navigator.push and Navigator.pop, respectively. These two methods are used to complete navigation and back operations between pages.

Navigator.push#

Navigator.push is used to perform the push operation for a Route, allowing navigation to the corresponding page through the specified Route. The method is as follows:

/// Parameters: (context, specific route)
static Future<T> push<T extends Object>(BuildContext context, Route<T> route) {
    return Navigator.of(context).push(route);
}

Use MaterialPageRoute to build the corresponding page's route, as shown below:

// Navigate to NavigatorPushPopPage
_navigateToPage(context, NavigatorPushPopPage());

/// Navigator.push
/// Page navigation
_navigateToPage(BuildContext context, Widget widget) {
  Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
    return widget;
  }));
}

Navigator.pop#

Navigator.pop is used to perform the pop operation for a Route, which means going back to the previous page. An optional parameter result can be added to carry parameters when returning from the page. The method is as follows:

/// Parameters: (context, parameters to carry back (optional))
static bool pop<T extends Object>(BuildContext context, [ T result ]) {
    return Navigator.of(context).pop<T>(result);
}

For example, clicking the IconButton exits the current page:

IconButton(
    icon: Icon(Icons.arrow_back),
    onPressed: () => Navigator.pop(context)),

Navigator.pushNamed#

Navigator.pushNamed is used to perform the push operation for a named Route, allowing parameters to be passed through the optional arguments parameter. The method is as follows:

/// Parameters: (context, route name, parameters to carry (optional))
static Future<T> pushNamed<T extends Object>(
    BuildContext context,
    String routeName, {
    Object arguments,
   }) {
    return Navigator.of(context).pushNamed<T>(routeName, arguments: arguments);
}

When using it, first add the corresponding route name to the route table routes in MaterialApp, as shown below:

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
      routes: <String, WidgetBuilder>{
        // Corresponding route/NavigatorPushNamedPage
        NavigatorPushNamedPage.routeName: (BuildContext context) =>
            NavigatorPushNamedPage(),
      },
    );
  }
}

/// page
/// Navigator.pushNamed
/// Using named routes
class NavigatorPushNamedPage extends StatelessWidget {
  static final routeName = '/NavigatorPushNamedPage';

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Navigator.pushNamed"),
        centerTitle: true,
      ),
      body: Center(
        child: Text(
          "Navigator.pushNamed",
          style: TextStyle(fontSize: 18),
        ),
      ),
    );
  }
}

Then you can use Navigator.pushNamed to navigate to the NavigatorPushNamedPage page, as shown below:

// Navigate to NavigatorPushNamedPage
_navigatePushNamedPage(context, NavigatorPushNamedPage.routeName);

/// Navigator.pushNamed
/// Page navigation
_navigatePushNamedPage(BuildContext context, String routeName,
    [Object arguments]) {
  Navigator.pushNamed(context, routeName, arguments: arguments);
}

The above is the most basic page navigation method in Flutter. Whether it is Navigator.push or Navigator.pushNamed, both are implemented based on the push method in NavigatorState. The NavigatorState is obtained through the specific BuildContext by Navigator.of(context). The push method of NavigatorState is as follows:

/// NavigatorState.push
Future<T> push<T extends Object>(Route<T> route) {
    assert(!_debugLocked);
    assert(() { _debugLocked = true; return true; }());
    assert(route != null);
    assert(route._navigator == null);
    final Route<dynamic> oldRoute = _history.isNotEmpty ? _history.last : null;
    route._navigator = this;
    route.install(_currentOverlayEntry);
    _history.add(route);
    route.didPush();
    route.didChangeNext(null);
    if (oldRoute != null) {
      oldRoute.didChangeNext(route);
      route.didChangePrevious(oldRoute);
    }
    for (NavigatorObserver observer in widget.observers)
      observer.didPush(route, oldRoute);
    RouteNotificationMessages.maybeNotifyRouteChange(_routePushedMethod, route, oldRoute);
    assert(() { _debugLocked = false; return true; }());
    _afterNavigation(route);
    return route.popped;
}

Next, let's introduce parameter passing in Flutter, including passing parameters during page navigation and passing parameters when returning to the previous page.

Route Parameter Passing#

The parameter passing during the page navigation process includes parameters carried during page navigation and parameters carried during page return.

Navigator.push with Parameters#

Use Navigator.push to carry parameters during page navigation. The parameters are received through the corresponding page's constructor, as shown below:

/// Navigate to NavigatorPushWithParamPage
_navigateToPage(context,
    NavigatorPushWithParamPage(
      param: "this info from last page!",
    ));

/// Navigator.push/pop
/// Page navigation
_navigateToPage(BuildContext context, Widget widget) {
  Navigator.push(context, MaterialPageRoute(builder: (BuildContext context) {
    return widget;
  }));
}

/// page
/// Navigator.push with parameters
class NavigatorPushWithParamPage extends StatelessWidget {
  // Parameter
  final String param;

  NavigatorPushWithParamPage({
    this.param,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text("Navigator.push with parameters"),
        centerTitle: true,
      ),
      body: Center(
        child: Text(
          "arguments:${this.param}",
          style: TextStyle(fontSize: 18),
        ),
      ),
    );
  }
}

Navigator.pushNamed with Parameters#

As mentioned above, the optional parameter arguments in the Navigator.pushNamed(context, routeName, {arguments}) method is the parameter to be carried during navigation. First, receive the parameters passed through arguments in the onGenerateRoute of MaterialApp, as shown below:

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HomePage(),
      // Parameter reception
      onGenerateRoute: (RouteSettings settings) {
        if (settings.name == NavigatorPushNamedWithParamPage.routeName) {
          return MaterialPageRoute<String>(builder: (BuildContext context) {
            return NavigatorPushNamedWithParamPage(settings.arguments);
          });
        } else {
          return null;
        }
      },
    );
  }
}

Then use Navigator.pushNamed for page navigation, with parameters still received by the corresponding page's constructor, as shown below:

/// Navigate to NavigatorPushNamedWithParamPage
_navigatePushNamedPage(
    context,
    NavigatorPushNamedWithParamPage.routeName,
    "this info from last page!");
    
/// Navigator.pushNamed with parameters
_navigatePushNamedPage(BuildContext context, String routeName,
    [Object arguments]) {
  Navigator.pushNamed(context, routeName, arguments: arguments);
}

/// page
/// Navigator.pushNamed with parameters
/// Using named routes
class NavigatorPushNamedWithParamPage extends StatelessWidget {
  static final String routeName = '/NavigatorPushNamedWithParamPage';
  final String info;

  NavigatorPushNamedWithParamPage(this.info);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text("Navigator.pushNamed with parameters"),
          centerTitle: true,
        ),
        body: Center(
          child: Text(
            "arguments:${this.info}",
            style: TextStyle(fontSize: 18),
          ),
        ),
      ),
    );
  }
}

Carrying Parameters When Returning from a Page#

As mentioned above, the optional parameter result in the Navigator.pop(context, [result]) method is the parameter carried when returning from the page. Navigator.push will return a Future, and the result of the page return can be processed in the then statement, as shown below:

/// Navigate to NavigatorPopWithParamPage
_navigatePopWithParamPage(context, NavigatorPopWithParamPage());

/// Navigator.pop returns with parameters
_navigatePopWithParamPage(BuildContext context, Widget widget) {
  Navigator.push<String>(context,
      MaterialPageRoute(builder: (BuildContext context) {
    return widget;
  })).then((result) {
    // Handle the parameters carried back
    Toast.show("Navigator.pop returns with parameters: " + result, context);
  });
}

/// page
/// Navigator.pop returns with parameters
class NavigatorPopWithParamPage extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return WillPopScope(
        child: Scaffold(
          appBar: AppBar(
            title: Text("Navigator.pop returns with parameters"),
            centerTitle: true,
          ),
          body: Center(
            child: Text(
              "Navigator.pop returns with parameters",
              style: TextStyle(fontSize: 18),
            ),
          ),
        ),
        onWillPop: () => _popBack(context));
  }

  /// Set return parameters when going back, similar to Android's setResult method
  _setResult(BuildContext context) {
    Navigator.pop(context, "this message from NavigatorPopWithParamPage!");
  }

  /// Unified handling of the back button
  Future<bool> _popBack(BuildContext context) {
    _setResult(context);
    return Future.value(false);
  }
}

Other Route Navigation#

Other commonly used route navigation methods are as follows:

// Remove the current route from Navigator and then navigate to a new route, equivalent to finish then startActivity
Navigator.popAndPushNamed
// Directly return based on the specified Route, clearing the previous routes
Navigator.popUntil
// Navigate to a new Route and clear the routes before the specified Route, similar to pushNamedAndRemoveUntil
Navigator.pushAndRemoveUntil
// Page replacement, similar to pushReplacementNamed
Navigator.pushReplacement

Other route-related operation methods will not be listed one by one; you can refer to the relevant API.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.