banner
jzman

jzman

Coding、思考、自觉。
github

Using the Navigator Component in the Flutter Series

PS: It's easy to want to do something, but it's hard 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, the term "navigation" usually refers to Activity transitions, also known as page transitions. In Flutter, it refers to Route transitions. Pages in Android correspond to Routes in Flutter. The Navigator is responsible for managing the stack of Route objects and provides methods for managing 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 that 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, when triggered, will call 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 of a Route, allowing navigation to the corresponding page via 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 of 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, as shown below:

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

As shown, 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 of a named Route, allowing parameters to be passed via the optional arguments parameter, as shown below:

/// 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),
        ),
      ),
    );
  }
}

You can then use Navigator.pushNamed to navigate to the NavigatorPushNamedPage, 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);
}

This is the most basic way of page navigation in Flutter. Both Navigator.push and Navigator.pushNamed are implemented based on the push method in NavigatorState. The NavigatorState is obtained through the specific BuildContext via 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 from a page.

Route Parameter Passing#

The parameter passing process during page navigation includes carrying parameters when navigating to a page and carrying parameters when returning from a page.

Navigator.push with Parameters#

Use Navigator.push to carry parameters during page navigation. The parameters are received through the constructor of the corresponding page, 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 to be carried when returning from a page. Navigator.push will return a Future, and the result of the page return can be handled 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 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));
  }

  /// Return from the page and set the return parameter, 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 and 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.