flutter开发实战-go_router使用

在这里插入图片描述

一、go_router介绍与特性

go_router是一个Flutter的第三方声明式路由插件,使用路由器API提供一个方便的、基于url的API,用于在不同屏幕之间导航。可以定义URL模式、使用URL导航、处理深度链接以及许多其他与导航相关的场景。

GoRouter具有许多功能,使导航变得简单明了:

  • 使用模板语法解析路由路径和路由查询(query)参数;
  • 支持单个目标路由展示多个页面(子路由);
  • 重定向:可以基于应用状态跳转到不同的URL,比如用户没有登录时跳转到登录页;
  • 使用 StatefulShellRoute 可以支持嵌套的 Tab 导航;
  • 同时支持 Material 风格和 Cupertino 风格应用;
  • 兼容 Navigator API 。

二、引入go_router

根据自己需要的版本引入对应的版本,在pubspec.yaml加入

  go_router: ^8.2.0
    

稍后执行flutter pub get命令

三、go_router路由配置

引入插件后,我们需要配置MaterialApp.router的routerConfig

/// The route configuration.
final GoRouter _router = GoRouter(
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (BuildContext context, GoRouterState state) {
        return const HomeScreen();
      },
      routes: <RouteBase>[
        GoRoute(
          path: 'details',
          builder: (BuildContext context, GoRouterState state) {
            return const DetailsScreen();
          },
        ),
      ],
    ),
  ],
);
    

配置MaterialApp.router

/// The main app.
class MyApp extends StatelessWidget {
  /// Constructs a [MyApp]
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}
    

四、go_router路由跳转

如果跳转页面,可以使用content.go

context.go('/details')

4.1、路径参数

GoRouter 的每一个路由都通过 GoRoute对象来配置,可以通过路径参数进行传递相关参数到目标页面。例如路径comment/参数id

GoRoute(
          path: 'comment/:id',
          builder: (BuildContext context, GoRouterState state) {
            String? id = state.pathParameters['id'];
            print("id:${id}");// Get "id" param from URL
            return CommentScreen(id: id);
          },
        ),

那么可以通过context.go(’/comment/55555’)传参数

ElevatedButton(
          onPressed: () => context.go('/comment/55555'),
          child: const Text('Go to the comment screen'),
        )

4.2、路径查询参数

可以获取路径的查询参数 URL 路径中的查询(query)参数,例如从/search?keyword=myname中获取search参数。

GoRoute(
          path: 'search',
          builder: (BuildContext context, GoRouterState state) {
            String? keyword = state.queryParameters['keyword'];
            print("keyword:${keyword}"); // Get "id" param from URL
            return SearchScreen(keyword: keyword);
          },
        ),

传递参数context.go(’/search?keyword=myName’)目标页面可以得到参数myName

ElevatedButton(
              onPressed: () => context.go('/search?keyword=myName'),
              child: const Text('Go to the Search screen'),
            ),

五、添加子路由

子路由,路由匹配支持多个页面,当一个新的页面在旧的页面之上展示时

GoRoute(
          path: 'details',
          builder: (BuildContext context, GoRouterState state) {
            return const DetailsScreen();
          },
          routes: <RouteBase>[
            // Add child routes
            GoRoute(
              path: 'sub-details',
              // NOTE: Don't need to specify "/" character for router’s parents
              builder: (context, state) {
                return SubDetailsScreen();
              },
            ),
          ],
        ),

可以通过context.go(’/details/sub-details’)来进行调用

ElevatedButton(
              onPressed: () => context.go('/details/sub-details'),
              child: const Text('Go to the SubDetails screen'),
            ),

六、页面导航

go_router提供多种方式进行跳转

context.goNamed方式

ElevatedButton(
              onPressed: () => context.goNamed('/details'),
              child: const Text('Go to the Details screen'),
            ),

七、路由重定向

go_router提供全局重定向,比如在没有登录的用户,需要跳转到登录页面.在 GoRouter 中,可以通过redirect 参数配置重定向.

redirect: (BuildContext context, GoRouterState state) {
    final isLogin = false;// your logic to check if user is authenticated
    if (!isLogin) {
      return '/login';
    } else {
      return null; // return "null" to display the intended route without redirecting
    }
  }

八、错误处理(404页面)

go_router为MaterialApp 和CupertinoApp定义了默认的错误页面,也可以通过 errorBuilder 参数自定义错误页面。

errorBuilder: (
    BuildContext context,
    GoRouterState state,
  ) {
    // ErrorPage
    return ErrorScreen();
  }

九、路由跳转监测

在flutter自带的会有NavigatorObserver,go_router页提供路由跳转监测功能

class MyNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    print('did push route');
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    print('did pop route');
  }
}

在observers中配置

GoRouter(
  observers: [MyNavigatorObserver()],
...
)

这里只是代码尝试了一下常用功能。还有一些转场动画、嵌套导航等等特性需要去尝试。
可以查看https://juejin.cn/post/7270343009790853172

注意:不同版本的代码有所不同。

测试的完整代码如下

import 'package:flutter/material.dart';
import 'package:go_router/go_router.dart';

void main() {
  runApp(const MyApp());
}

class MyNavigatorObserver extends NavigatorObserver {
  @override
  void didPush(Route<dynamic> route, Route<dynamic>? previousRoute) {
    print('did push route');
  }

  @override
  void didPop(Route<dynamic> route, Route<dynamic>? previousRoute) {
    print('did pop route');
  }
}

/// The route configuration.
final GoRouter _router = GoRouter(
  observers: [MyNavigatorObserver()],
  redirect: (BuildContext context, GoRouterState state) {
    final isLogin = false; // your logic to check if user is authenticated
    if (!isLogin) {
      return '/login';
    } else {
      return null; // return "null" to display the intended route without redirecting
    }
  },
  errorBuilder: (
    BuildContext context,
    GoRouterState state,
  ) {
    // ErrorPage
    return ErrorScreen();
  },
  routes: <RouteBase>[
    GoRoute(
      path: '/',
      builder: (BuildContext context, GoRouterState state) {
        return const HomeScreen();
      },
      routes: <RouteBase>[
        GoRoute(
          path: 'details',
          builder: (BuildContext context, GoRouterState state) {
            return const DetailsScreen();
          },
          routes: <RouteBase>[
            // Add child routes
            GoRoute(
              path: 'sub-details',
              // NOTE: Don't need to specify "/" character for router’s parents
              builder: (context, state) {
                return SubDetailsScreen();
              },
            ),
          ],
        ),
        GoRoute(
          path: 'comment/:id',
          builder: (BuildContext context, GoRouterState state) {
            String? id = state.pathParameters['id'];
            print("id:${id}"); // Get "id" param from URL
            return CommentScreen(id: id);
          },
        ),
        GoRoute(
          path: 'search',
          builder: (BuildContext context, GoRouterState state) {
            String? keyword = state.queryParameters['keyword'];
            print("keyword:${keyword}"); // Get "id" param from URL
            return SearchScreen(keyword: keyword);
          },
        ),
      ],
    ),
  ],
);

/// The main app.
class MyApp extends StatelessWidget {
  /// Constructs a [MyApp]
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp.router(
      routerConfig: _router,
    );
  }
}

/// The home screen
class HomeScreen extends StatelessWidget {
  /// Constructs a [HomeScreen]
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home Screen')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => context.goNamed('/details'),
              child: const Text('Go to the Details screen'),
            ),
            SizedBox(
              height: 30,
            ),
            ElevatedButton(
              onPressed: () => context.go('/details/sub-details'),
              child: const Text('Go to the SubDetails screen'),
            ),
          ],
        ),
      ),
    );
  }
}

/// The details screen
class DetailsScreen extends StatelessWidget {
  /// Constructs a [DetailsScreen]
  const DetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Details Screen')),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: () => context.go('/comment/55555'),
              child: const Text('Go to the comment screen'),
            ),
            SizedBox(
              height: 30,
            ),
            ElevatedButton(
              onPressed: () => context.go('/search?keyword=myName'),
              child: const Text('Go to the Search screen'),
            ),
          ],
        ),
      ),
    );
  }
}

/// The details screen
class SubDetailsScreen extends StatelessWidget {
  /// Constructs a [SubDetailsScreen]
  const SubDetailsScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('SubDetailsScreen Screen')),
      body: Center(
        child: ElevatedButton(
          onPressed: () => context.go('/details'),
          child: const Text('Go back to the Details screen'),
        ),
      ),
    );
  }
}

/// The details screen
class CommentScreen extends StatelessWidget {
  /// Constructs a [DetailsScreen]
  const CommentScreen({super.key, this.id});

  final String? id;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('CommentScreen Screen')),
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text("param id:${id}"),
          SizedBox(
            height: 30,
          ),
          ElevatedButton(
            onPressed: () => context.go('/'),
            child: const Text('Go back to the Details screen'),
          ),
        ],
      )),
    );
  }
}

/// The Search screen
class SearchScreen extends StatelessWidget {
  /// Constructs a [DetailsScreen]
  const SearchScreen({super.key, this.keyword});

  final String? keyword;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('SearchScreen Screen')),
      body: Center(
          child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Text("param keyword:${keyword}"),
          SizedBox(
            height: 30,
          ),
          ElevatedButton(
            onPressed: () => context.go('/'),
            child: const Text('Go back to the details screen'),
          ),
        ],
      )),
    );
  }
}

/// The Search screen
class ErrorScreen extends StatelessWidget {
  /// Constructs a [DetailsScreen]
  const ErrorScreen();

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('ErrorScreen Screen')),
      body: Center(
        child: Container(
          child: const Text('Error info'),
        ),
      ),
    );
  }
}

十、小结

flutter开发实战-go_router使用

学习记录,每天不停进步。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐