Review past

The Journey of Flutter from scratch: StatelessWidget

The Journey of Flutter from Scratch: StatefulWidget

A Journey to Flutter from scratch: InheritedWidget

The Journey to Flutter from scratch: Provider

In this article, the fifth installment of the Starting From Scratch series, we covered widgets and Provider handling combined with data sharing.

This time we will continue to look at the routing navigation Navigator information.

Route management in Flutter is similar to native development in that it maintains a routing stack, pushes in to open a new page, and pops out to close the old page.

The sample

Let’s go straight to Flutter_github for a simple instance.

  void _goToLogin() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String authorization = prefs.getString(SP_AUTHORIZATION);
    String token = prefs.getString(SP_ACCESS_TOKEN);
    if((authorization ! = null && authorization.isNotEmpty) || (token ! = null && token.isNotEmpty)) { Navigator.of(context).push(MaterialPageRoute(builder: (context) {return HomePage();
      }));
    } else {
      Navigator.of(context).push(MaterialPageRoute(builder: (context) {
        returnLoginPage(); })); }}Copy the code

The above method is to determine whether you have logged in. If you have logged in to the HomePage, go to the Navigator page; otherwise, go to the LoginPage page.

It’s easy to pass a Route through a push. This corresponds to the MaterialPageRoute. It provides a Builder method where we can simply return the instance of the page we want to jump to.

It inherits from PageRoute, an abstract class that provides transition animations and interfaces for route switching. Through these interfaces, the MaterialPageRoute realizes the animation effect of route switching of corresponding styles on different platforms. Such as:

  1. On Android, a push page slides from the bottom of the screen to the top to enter, and a POP page slides from the top to the bottom of the screen to exit.
  2. On Ios, the page slides from the right side of the screen to the left side of the screen for push to enter, and slides from the left side to the right side of the screen for pop to exit.

If you want to customize the switch animation, you can copy the MaterialPageRoute and inherit from PageRoute.

Navigator

Note that the push operation returns a Future that will receive the data returned when the new route is closed. The equivalent in Android is the startActivityForResult() and onActivityResult() apis.

@optionalTypeArgs Future<T> push<T extends Object>(Route<T> route) { assert(! _debugLocked); assert(() { _debugLocked =true;
      return true; } ()); . . }Copy the code

The other is the pop operation, which can be pushed out of the stack to pass data to the previous page. In Android, the equivalent is the setResult() Api

@optionalTypeArgs bool pop<T extends Object>([ T result ]) { assert(! _debugLocked); assert(() { _debugLocked =true;
      return true; } ()); final Route<dynamic> route = _history.last; assert(route._navigator == this); bool debugPredictedWouldPop; . . }Copy the code

In addition to the two common ones above, there are several special operations below

  1. PushReplacement: Indicates that the current route page is replaced with a new one. The original route becomes invalid.
  2. PushAndRemoveUntil: adds a new route and receives a judgment condition that will remove all previous routes if it meets the condition.

These are scenario-specific, such as the login judgment example at the beginning of this article. This code is actually in the boot page when the App starts, so no matter which page is redirected to, the boot page will eventually disappear from the route. Therefore, pushReplacement can be used to open the new route page.

     Navigator.of(context).pushReplacement(MaterialPageRoute(builder: (context){
        return HomePage();
      }));
Copy the code

The ginseng

Route jump page naturally not without parameter transfer, through the above way to route jump, parameter transfer is also very simple, you can directly through the instance class parameter transfer.

I use WebViePage in Flutter_github as an example.

class WebViewPage extends BasePage<_WebViewState> {
  final String url;
  final String requestUrl;
  final String title;
 
  WebViewPage({@required this.title, this.url = ' ', this.requestUrl = ' '});
 
  @override
  _WebViewState createBaseState() => _WebViewState(title, url, requestUrl);
}
Copy the code

Above is the receipt of WebViewPage parameters, which are passed directly through the instantiation

  contentTap(int index, BuildContext context) {
    NotificationModel item = _notifications[index];
    if(item.unread) _markThreadRead(index, context);
    Navigator.push(context, MaterialPageRoute(builder: (_) {
      returnWebViewPage( title: item.subject? .title ??' ', requestUrl: item.subject? .url ??' ',); })); }Copy the code

Click on the text to jump to the WebViewPage page, use push to navigate to the WebViewPage page, and pass the parameters when instantiating.

So that’s a relatively primitive way of passing parameters, but there’s another way

Any Android friend knows that an Activity can be passed parameters to an Intent when it jumps to the page, and that the receiving page can also retrieve parameters from the Intent.

There is a similar way of transferring parameters in Flutter. We can build a Arguments object from the Settings in the MaterialPageRoute and pass it to the jump page.

Modify the code above

  contentTap(int index, BuildContext context) {
    NotificationModel item = _notifications[index];
    if (item.unread) _markThreadRead(index, context);
    Navigator.push(
        context,
        MaterialPageRoute(
            builder: (_) {
              returnWebViewPage(); }, settings: RouteSettings( arguments: {WebViewPage.ARGS_TITLE: item.subject? .title ??' ', WebViewPage.ARGS_REQUEST_URL: item.subject? .url ??' '})));
  }
Copy the code

This is parameter passing, and here is how parameters are received in WebViewPage

    Map<String, String> arguments = ModalRoute.of(context).settings.arguments;
    _title = arguments[WebViewPage.ARGS_TITLE];
    _url = arguments[WebViewPage.ARGS_URL];
    vm.requestUrl = arguments[WebViewPage.ARGS_REQUEST_URL];
Copy the code

Arguments are retrieved via ModalRoute on the receiving page, and arguments are retrieved as the parameter map data passed above.

ModalRoute. Internal use of () is the context. DependOnInheritedWidgetOfExactType ()

Does that look familiar? Recommend revisiting the Flutter journey from scratch if you can’t remember: InheritedWidget

All the above are non-named routes. Let’s learn about the use and parameter methods of named routes.

After routing

Named routes, as the name implies, redirect to the corresponding page through a pre-registered name.

On the home page, we need to register a routing table and agree on a one-to-one correspondence between the name and the page.

Routing tables can be defined using routes

  final Map<String, WidgetBuilder> routes;
Copy the code

It should make sense by definition. It is a map, with keys representing route names and values representing specific page instances.

Take GithubApp in Flutter_github as an example.

class _GithubAppState extends State<GithubApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Github', theme: ThemeData.light(), initialRoute: welcomeRoute.routeName, routes: { welcomeRoute.routeName: (BuildContext context) => WelcomePage(), loginRoute.routeName: (BuildContext context) => LoginPage(), homeRoute.routeName: (BuildContext context) => HomePage(), repositoryRoute.routeName: (BuildContext context) => RepositoryPage(), followersRoute.routeName: (BuildContext context) => FollowersPage(followersRoute.pageType), followingRoute.routeName: (BuildContext context) => FollowersPage(followingRoute.pageType), webViewRoute.routeName: (BuildContext context) => WebViewPage(), }, ); }}Copy the code

There are two things to note

  1. InitialRoute is used to initialize the routing page, and it receives the registered name of the corresponding routing page
  2. Routes is the registered routing table. You only need to register the corresponding routing page using the key and value methods.

To facilitate the management of route hops, AppRoutes is used to uniformly manage route names

class AppRoutes {
  final String routeName;
  final String pageTitle;
  final String pageType;
 
  const AppRoutes(this.routeName, {this.pageTitle, this.pageType});
}
 
class PageType {
  static const String followers = 'followers';
  static const String following = 'following';
}
 
const AppRoutes welcomeRoute = AppRoutes('/');
 
const AppRoutes loginRoute = AppRoutes('/login');
 
const AppRoutes homeRoute = AppRoutes('/home');
 
const AppRoutes repositoryRoute =
    AppRoutes('/repository', pageTitle: 'repository');
 
const AppRoutes followersRoute = AppRoutes('/followers',
    pageTitle: 'followers', pageType: PageType.followers);
const AppRoutes followingRoute = AppRoutes('/following',
    pageTitle: 'following', pageType: PageType.following);
 
const AppRoutes webViewRoute = AppRoutes('/webview', pageTitle: 'WebView');

Copy the code

Now that we have registered the route to the page we want to jump to, we will use the named route to replace the route described earlier.

  void _goToLogin() async {
    SharedPreferences prefs = await SharedPreferences.getInstance();
    String authorization = prefs.getString(SP_AUTHORIZATION);
    String token = prefs.getString(SP_ACCESS_TOKEN);
    if((authorization ! = null && authorization.isNotEmpty) || (token ! = null && token.isNotEmpty)) { Navigator.pushReplacementNamed(context, homeRoute.routeName); }else{ Navigator.pushReplacementNamed(context, loginRoute.routeName); }}Copy the code

PushReplacementNamed () can be used to jump to the corresponding page during login status detection. The difference is that we only need to pass the route name corresponding to the jump page. Because you already have the routing registry, it transforms itself into the corresponding page.

The corresponding methods are pushNamed() and pushNamedAndRemoveUntil()

Parameter passing for named routes is similar to that described last, for example

Navigator.of(context).pushNamed(webViewRoute.routeName, arguments: {WebViewPage.ARGS_TITLE: item.subject? .title ??' ', WebViewPage.ARGS_REQUEST_URL: item.subject? .url ??' '});
Copy the code

Basically, the same is true for passing arguments, and the corresponding page receives arguments in the same way.

onGenerateRoute

Another thing to note in named routes is onGenerateRoute

class _GithubAppState extends State<GithubApp> {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
    ...
      onGenerateRoute: (RouteSettings setting) {
        returnMaterialPageRoute(builder: (context) { String routeName = setting.name; // todo navigator }); }); }}Copy the code

The callback condition is: the page to which the jump has not been registered in routes

Through this callback method, we can intercept the route here, and then do some unified page jump logic processing.

This is the knowledge of Navigator. If there are any deficiencies in the article, please point them out. Or if you have any questions, please leave a message with me, AND I will answer them as best as I can.

Recommended project

The following is a complete description of the Flutter project, which is a good introduction to the Flutter for beginners.

Flutter_github is a Github client based on Flutter that supports both Android and IOS, passwords and authentication login. Dart language was used for development, and the project architecture was MSVM based on Model/State/ViewModel. Use Navigator to jump to the page; The network framework uses DIO. The project is being updated continuously, those who are interested can follow it.

Of course, if you want to learn about Android native, flutter_Github’s pure Android version AwesomeGithub is a good choice.

If you like my article mode, or are interested in my next article, I suggest you follow my wechat official account: [Android supply station]

Or scan the qr code below to establish effective communication with me and receive my update push faster and more accurately.