Flutter Route (路由) - Fluro

3,713 阅读3分钟

前言: Flutter 路由跳转有二种方法:一种是用系统原生的跳转方法。第二种是,用第三方框架, 比如 Fluro...

Flutter 引入 fluro 库

官方简介: The brightest, hippest, coolest router for Flutter.

打开 pub.dev/packages/fl… 找到最新版本。

添加依赖

dependencies:
  fluro: ^1.6.3

示例项目(官方原文)

在 “example” 文件夹中有一个非常可爱的示例项目。检查出来。否则,继续阅读,直到跑起来。

设置

首先,你应该通过初始化来定义一个新的 Router 对象:

final router = Router();

对于您来说,全局/静态地存储路由器是很方便的,这样您就可以在应用程序的其他区域访问路由器。

在实例化路由器之后,你需要定义你的 routes 和你的 route handlers:

var usersHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
  return UsersScreen(params["id"][0]);
});

void defineRoutes(Router router) {
  router.define("/users/:id", handler: usersHandler);

  // it is also possible to define the route transition to use
  // router.define("users/:id", handler: usersHandler, transitionType: TransitionType.inFromLeft);
}

在上面的例子中,路由器会拦截诸如 /users/1234 这样的路由,并将应用程序路由到 UsersScreen ,将值 1234 作为参数传递给那个页面。

导航

你可以在 MaterialApp.onGenerateRoute 上使用 Router。参数通过 Router.generator 函数。为此,将函数引用传递给 onGenerate 参数,类似于 onGenerateRoute: router.generator

然后你可以使用 Navigator.push。然后 flutter 路由机制会为你匹配路由路径。

您也可以自己手动推送到一个路径。像这样做:

router.navigateTo(context, "/users/1234", transition: TransitionType.fadeIn);

实际操作

呃,官方文档可能有点云里雾里的感觉,我们来个实操吧~

新建三个文件

  • \routes\Routes.dart 路由文件
  • \routes\Routes_handler.dart 路由 handler
  • \routes\fluro_convert_utils.dart 参数编码转换工具, fluro 不支持中文传参

设置路由 (Routes)

在 main 函数里设备路由

import 'package:fluro/fluro.dart';
import 'package:smarthome/routes/routes.dart';

void main() {
  // 设置路由
  final router = Router();
  Routes.configureRoutes(router);
  Routes.router = router;
  
  ...
}

路由入口与配置 (\routes\Routes.dart)

这里要做二步

  • 定义地址变量, 比如: static String root = '/';
  • 定义路由 handler, 比如: router.define(root, handler: appHandler); // 主页
///
/// 路由入口
/// 配置路径 Route
///
import 'package:fluro/fluro.dart';
import 'package:flutter/cupertino.dart';
import 'package:smarthome/routes/routes_handler.dart';
import '../main.dart';

class Routes {
  /// 路由
  static Router router;
  /// 主页
  static String root = '/';
  /// 欢迎页
  static String welcome = '/welcome';
  /// 登录页
  static String login = '/login';
  /// 注册页
  static String register = '/register';

  // 配置route
  static void configureRoutes(Router router) {
    // 未发现对应route
    router.notFoundHandler = Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
      print('route not found!');
      return;
    });

    router.define(root, handler: appHandler); // 主页
    router.define(welcome, handler: welcomeHandler); // 欢迎
    router.define(login, handler: loginHandler); // 登录
    router.define(register, handler: registerHandler); // 注册
  }

  /// 跳转路由
  /// path: 路由地址
  /// params: 参数
  /// transition: 转场动画
  /// replace: 代换
  static Future push(BuildContext context, String path, {Map<String, dynamic> params, TransitionType transition = TransitionType.cupertino, bool replace = false, bool clearStack = false}) {
    // 对参数进行 encode,解决参数中有特殊字符,影响 fluro 路由匹配
    String query =  "";
    if (params != null) {
      int index = 0;
      for (var key in params.keys) {
        var value = Uri.encodeComponent(params[key]);
        if (index == 0) {
          query = "?";
        } else {
          query = query + "\&";
        }
        query += "$key=$value";
        index++;
      }
    }

    // print('navigateTo传递的参数:$query');

    path = path + query;
    return router.navigateTo(context, path, transition:transition, replace: replace, clearStack: clearStack);
  }

  /// 返回上一个页面
  static pop() {
    // 全局 context
    BuildContext context = navigatorKey.currentState.overlay.context;
    router.pop(context);
  }
}

路由 handler (\routes\Routes_handler.dart)

handler 有二种情况

  • 不带参数 直接返回 跳转页面
  • 带参数 有中文 做转码跳转。
import 'package:fluro/fluro.dart';
import 'package:flutter/cupertino.dart';
import 'package:smarthome/routes/fluro_convert_utils.dart';
import 'package:smarthome/views/root.dart';
import 'package:smarthome/views/home/device.dart';
import 'package:smarthome/views/welcome/login.dart';
import 'package:smarthome/views/welcome/register.dart';
import 'package:smarthome/views/welcome/welcome.dart';

/// 主页
var appHandler =
    Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
  // 不带参数 直接返回 跳转页面
  return Root();
});

/// 设备
var deviceHandler =
    Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
  // 参数没有中文
  String device = params['device']?.first;
  return Device(device: device);
});

/// 选择动作
var selectDoHandler =
    Handler(handlerFunc: (BuildContext context, Map<String, dynamic> params) {
  String gatewayIdString = params['gatewayId']?.first;
  String controlString = params['control']?.first;
  int gatewayId = int.parse(gatewayIdString);
  if (controlString != null) {
    /// 参数有中文, 做转换后 跳转页面
    Map<String, dynamic> control = FluroConvertUtils.string2map(controlString);
    SelectDo(gatewayId: gatewayId, haveControl: control);
  }
  return SelectDo(gatewayId: gatewayId);
});

fluro 参数编码转换工具 (\routes\fluro_convert_utils.dart)

import 'dart:convert';

/// fluro 参数编码转换工具类
class FluroConvertUtils {
  /// fluro 传递中文参数前,先转换,fluro 不支持中文传递
  static String fluroCnParamsEncode(String originalCn) {
    return jsonEncode(Utf8Encoder().convert(originalCn));
  }

  /// fluro 传递后取出参数,解析
  static String fluroCnParamsDecode(String encodeCn) {
    var list = List<int>();

    ///字符串解码
    jsonDecode(encodeCn).forEach(list.add);
    String value = Utf8Decoder().convert(list);
    return value;
  }

  /// string 转为 int
  static int string2int(String str) {
    return int.parse(str);
  }

  /// string 转为 double
  static double string2double(String str) {
    return double.parse(str);
  }

  /// string 转为 bool
  static bool string2bool(String str) {
    if (str == 'true') {
      return true;
    } else {
      return false;
    }
  }

  /// object 转为 string json
  static String object2string<T>(T t) {
    return fluroCnParamsEncode(jsonEncode(t));
  }

  /// string json 转为 map
  static Map<String, dynamic> string2map(String str) {
    return json.decode(fluroCnParamsDecode(str));
  }
}

页面跳转 Routes.push()

Routes.push(context, Routes.addDevice, params: {'gatewayId': gatewayId});

这个是路由封装的方法,直接调用 push 就行。

	/// 跳转路由
  /// path: 路由地址
  /// params: 参数
  /// transition: 转场动画
  /// replace: 代换
  static Future push(BuildContext context, String path, {Map<String, dynamic> params, TransitionType transition = TransitionType.cupertino, bool replace = false, bool clearStack = false}) {
    // 对参数进行 encode,解决参数中有特殊字符,影响 fluro 路由匹配
    String query =  "";
    if (params != null) {
      int index = 0;
      for (var key in params.keys) {
        var value = Uri.encodeComponent(params[key]);
        if (index == 0) {
          query = "?";
        } else {
          query = query + "\&";
        }
        query += "$key=$value";
        index++;
      }
    }

    // print('navigateTo传递的参数:$query');

    path = path + query;
    return router.navigateTo(context, path, transition:transition, replace: replace, clearStack: clearStack);
  }

回到上一页面 Routes.pop()

Routes.pop()

pop 方法封装

  /// 返回上一个页面
  static pop() {
    // 全局 context
    BuildContext context = navigatorKey.currentState.overlay.context;
    router.pop(context);
  }

这里设置了一个全局 context,如果没有设置,直接把 context 传入就行了。

比如:

 static pop(BuildContext context) {
   router.pop(context);
 }

取得全局 Context

有时,我们要在网络工具类做弹框处理或页面跳转,比如没有登录时,要跳转到登录页面,但是网络工具类不是 Widget,没有 Context, 所以做不了跳转,这时,我们就要一个全局 Context..

main 文件配置 Context

/// 定义全局 navigatorKey
/// 全局 context
/// BuildContext context = navigatorKey.currentState.overlay.context;
/// 路由跳转
/// navigatorKey.currentState.pushNamed('/');
final GlobalKey<NavigatorState> navigatorKey = new GlobalKey<NavigatorState>();

class App extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ATU 智能家居',
      debugShowCheckedModeBanner: Global.isDebug,
      // 配置路由
      onGenerateRoute: Routes.router.generator,
      // 全局配置
      navigatorKey: navigatorKey,
      theme: ThemeData(
        // This is the theme of your application.
        //
        // Try running your application with "flutter run". You'll see the
        // application has a blue toolbar. Then, without quitting the app, try
        // changing the primarySwatch below to Colors.green and then invoke
        // "hot reload" (press "r" in the console where you ran "flutter run",
        // or simply save your changes to "hot reload" in a Flutter IDE).
        // Notice that the counter didn't reset back to zero; the application
        // is not restarted.
        primarySwatch: Global.themeColor,
      ),
      darkTheme: ThemeData.dark(),
      home: Root(),
    );
  }
}

全局 Context 调用

/// 全局 context
BuildContext context = navigatorKey.currentState.overlay.context;

参考资料

Fluro (pub.dev/packages/fl… )

Flutter - 路由管理 - 02 - Fluro (juejin.im/post/684490…)