Flutter插件faraday的特性与使用

Flutter插件faraday的特性与使用

faraday_cli(以下简称cli)是配合g_faraday进行模块化混合应用开发的命令行程序,帮助我们减少部分重复性工作让开发更高效。

特性

  • ✅ 生成公共接口和入口接口
  • ✅ 打包iOS和Android框架/aar

为什么使用faraday

进行混合应用开发通常会有以下需求。有一个功能在iOS/Android两端已经实现,在Flutter层需要调用,这时候就需要在Flutter层来定义接口,然后在两端分别实现。下面我们来看一段代码示例。

class EncryptUtils {

    // 加密
    static Future<String> encrypt(String content, Map<String, dynamic> options) {
        return FaradayCommon.invokeMethod('SomeFeature#encrypt', {'content': content, 'options': options});
    }

    // 解密
    static Future<String> decrypt(String encryptedContent, Map<String, dynamic> options) {
        return FaradayCommon.invokeMethod('SomeFeature#decrypt', {'encryptedContent': encryptedContent, 'options': options});
    }
}

以上,我们在Flutter中定义了加密和解密两个方法,接下来我们需要分别在iOS/Android的FaradayCommon中来实现代码如下(仅Swift,Kotlin类似):

func handle(_ name: String, _ arguments: Any?, _ completion: @escaping (_ result: Any?) -> Void) -> Void {
    if (name == "SomeFeature#encrypt") {
        guard let content = arguments?["content"] as? String else {
            fatalError("Invalid argument: content")
        }
        let options = arguments?["options"] as? [String: Any]

        // 使用以上参数来调用原生加密方法
    } else if (name == "SomeFeature#decrypt") {
        guard let encryptedContent = arguments?["encryptedContent"] as? String else {
            fatalError("Invalid argument: encryptedContent")
        }
        let options = arguments?["options"] as? [String: Any]

        // 使用以上参数来调用原生解密方法
    }
    ...
}

仔细看上述代码实现就会发现有很多重复代码片段,因此cli提供了对应的生成工具来实现代码生。(具体命令请见下述示例)

路由注册

Flutter中一个典型的路由注册如下:

// flutter
Map<String, RouteFactory> routes = {
    'first_page': (RouteSettings settings) => CupertinoPageRoute(
        builder: (context) => FirstPage(0), settings: settings),
    'home': (RouteSettings settings) => CupertinoPageRoute(
        builder: (context) => HomePage(settings.arguments), settings: settings),
    'flutter_tab_2': (RouteSettings settings) => CupertinoPageRoute(
        builder: (context) => HomePage(settings.arguments), settings: settings),
    'flutter_tab_1': (RouteSettings settings) => CupertinoPageRoute(
        builder: (context) => EmbeddingPage(0), settings: settings),
};
// swift

enum FaradayRoute {
    // ---》 enum
    // ---》 enum MainTabPage
    case first_page
    case home(Int id)
    case flutter_tab_2(String name)
    case flutter_tab_1
}

上面的例子可以看出我们在3端需要定义很多常量字符串, 如果任由开发者手动输入,IDE没有任何提醒,那会非常容易出错。(具体命令下述会有相关示例)

打包集成

iOS

官方集成方案一共有3种,其中2种需要iOS开发者本地由Flutter开发环境,还有一种需要获取别人打包好的framework。这种开发体验对一个稍具规模的iOS开发团队来说并不是特别友好。我们将采用 pod 私有库的方式在团队成员之间共享打包产物。同时对CI/CD流程提供支持。

Android

Android官方方案对比iOS来说,使用aar方式集成非常友好,只需要将编译产物放到大家都可以访问到的地址即可。

如何使用

前置准备条件

  1. 安装faraday cli

    pub global activate faraday
    
    # 打印版本
    faraday version
    
  2. 准备文件服务器 为了无感集成Flutter模块至现有的iOS和Android工程,需要一个所有团队成员均可访问的文件服务器(支持文件上传下载)来存储编译产物(iOS为cocoapods framework,Android为maven仓库)。

    如果暂时没有文件服务器可以下载 simple-http-server.py 启动一个用于测试的文件服务器。需要安装python3

  3. Cocoapods私有spec iOS选择使用cocoapods来集成,为了让native开的同学无感我们需要创建私有repo。 具体步骤参见 Private Pods

配置文件

  1. iOS native 项目中添加2个空文件 新建2个空文件用来生成桥接方法和路由的模版代码,建议文件名 FaradayCommon.swiftFaradayRoute.swift

  2. Android native 项目中添加2个空文件 建议文件名 FaradayCommon.ktFaradayRoute.kt

  3. Flutter module 项目根目录下创建.faraday.json文件 cli主要作用就是为iOS和Android生成模版代码,所以在一切工作开始之前我们需先告诉cli相应文件的具体位置。

    {
      "ios-common": "/path/to/FaradayCommon.swift", // 上面2步新建的文件路径
      "ios-route": "/path/to/FaradayRoute.swift",
      "android-common": "/path/to/FaradayCommon.kt",
      "android-route": "/path/to/FaradayRoute.kt"
    }
    

    版本控制工具请忽略此文件 每个开发者的 .faraday.json 文件都需要根据自己的需要配置添加

  4. 修改pubspec.yaml文件, 添加以下配置

    ...
    
    version: 0.2.1-dev.0
    
    ####################################################
    faraday:
      static_file_server_address: "http://localhost:8000" # 前置条件中的静态文件服务器
      pod_repo_name: "faraday" # 前置条件中的 private pod spec name
    ####################################################
    
    environment:
      sdk: ">=2.9.0 <3.0.0"
    
    ...
    

初始化

cd [your_flutter_module]

faraday init

初始化完成以后查看lib/src/debug目录

debug_folder

同时在lib/src/目录下会生成routes.dart文件,用来定义Flutter侧的路由定义。如果一切正常说明初始化正常

集成

初始化完成以后,我们分别在3端来集成faraday

Flutter
import 'src/debug/debug.dart';
import 'src/routes.dart';

....

// debugMessage 每次打包都会更新称最新版本号,可以添加一个banner显示
debugPrint(debugMessage);

// 可以使用 CupertinoApp、MaterialApp、WidgetsApp 或者 Navigator 均可
CupertinoApp(onGenerateRoute: (_) => faraday.wrapper(routeFactory));

iOS端产物说明

  • protocol FaradayCommonHandler { } 自动根据Flutter端的common方法定义来生成接口定义,我们需要实现他,一般可以直接用 AppDelegate 实现

    extension AppDelegate: FaradayCommonHandler {
    ...
    }
    
  • enum FaradayRoute { } 根据Flutter端的路由信息来生成对应的枚举 使用方式 大概如下

    let vc = FaradayRoute.demoPage.viewController()
    navigationController?.pushViewController(vc, animated: true)
    ...
    

Android和iOS类似这里就不在赘述

添加一个桥接方法

g_faraday提供了2个注解@common@ignore。当一个类被@common注解时,这个类中所包含的所有符合条件的方法都会在native侧生成相应的接口类

何为符合条件的方法 如果一个方法为 静态方法而且参数和返回值都可以序列化成json,那么就是一个符合条件的方法 如果一个符合条件的方法,不需要生成native接口,那么只需要用@ignore注解此方法即可

执行下面的命令,应该可以看到对应的文件生成了相应的方法

cd [your_flutter_module]

faraday generate

添加一个路由

当一个Flutter页面需要从native打开是需要以下两步来操作

  1. @entry注解你当前页面的class
  2. 添加faraday静态方法
// 声明路由

@entry
class DemoPage extends StatefulWidget {

    static Route faraday(int id) => CupertinoPageRoute(
        builder: (ctx) => DemoPage(status),
      );

    ....
}

执行

faraday generate

在对应的route文件和routes.dart文件中,会根据faraday方法的签名,自动生成对应路由

如果有路由只需要生成flutter侧,不需要从native打开,那么可以考虑使用@flutterEntry进行注解

打包发布

faraday对flutter build命令进行封装,可以用以下命令快速打包发布

faraday tag --no-release

更多关于Flutter插件faraday的特性与使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter插件faraday的特性与使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


Flutter 是一个由 Google 开发的开源 UI 软件开发工具包,用于构建跨平台的移动应用程序。Faraday 是一个在 Flutter 中用于处理 HTTP 请求的插件,它基于 Dart 的 http 包,并提供了更高级的功能和更简洁的 API。

如果你在 Flutter 项目中使用 Faraday 插件时遇到“未定义功能”的问题,可能是由于以下原因之一:

1. 插件未正确安装

确保你已经在 pubspec.yaml 文件中正确添加了 Faraday 插件的依赖项。你可以在 pubspec.yaml 文件中添加以下内容:

dependencies:
  faraday: ^0.1.0  # 请使用最新版本

然后运行 flutter pub get 来获取依赖项。

2. 插件未导入

在你的 Dart 文件中,确保你已经导入了 Faraday 插件:

import 'package:faraday/faraday.dart';

3. 插件版本不兼容

如果你使用的是较新版本的 Flutter,可能会遇到 Faraday 插件与当前 Flutter 版本不兼容的问题。你可以尝试更新 Faraday 插件到最新版本,或者查看插件的文档和发布说明,看看是否有已知的兼容性问题。

4. 插件功能未正确使用

确保你正确地使用了 Faraday 插件的功能。以下是一个简单的示例,展示了如何使用 Faraday 发送 HTTP GET 请求:

import 'package:faraday/faraday.dart';

void fetchData() async {
  var client = Faraday();
  var response = await client.get('https://jsonplaceholder.typicode.com/posts/1');
  
  if (response.statusCode == 200) {
    print('Response data: ${response.body}');
  } else {
    print('Request failed with status: ${response.statusCode}');
  }
}
回到顶部