乐趣区

关于前端:Flutter-生成小程序的混合-App-实践

一、背景

微信小程序倒退的越来越快,目前小程序甚至取代了大部分 App 的生态位,公司的坑位不增反降,只能让原生利用开发兼顾或换岗进行小程序的开发。

以我的理论状况来讲,公司利用采纳的 Flutter 框架,同样的性能不可避免的就会存在 Flutter 利用开发和微信小程序开发兼顾的状况,这种反复造轮子的工作十分低效。

为什么会呈现这种状况呢?随着 2019 年 5 月 Google I/O 上 Flutter 1.5.4 的公布,宣示着 Flutter 真正开始进入全终端时代,意味着只须要写一份代码,不须要任何额定的修改改,就能够运行在 iOS、Android、Web、PC 上。Flutter 正在革命性的扭转挪动开发的生态系统,从面向各个终端的开发,转向面向框架开发,不仅会扭转开发者的开发方式,也有越来越多的公司开始关注应用 Flutter。

Flutter 作为一个跨平台的框架,其开发技术栈交融了 Native 和前端的技术,不仅波及到了 Native(Android、iOS)的开发常识,又汲取了很多前端(例如 React)的技术理念和框架,并且在此基础上又有晋升,造成 Flutter 本人独特的技术思维。

但目前来讲,Flutter 并不反对小程序,Flutter for Web 尽管最初也会生成 JS 代码,然而 Flutter 生成的 JS 和 CSS 都是不能批改的。而在 Flutter 中也没方法通过 Dart 间接调用小程序的接口,所以现阶段用 Flutter 开发小程序不是太好的抉择。

二、一些解决思路

事实上,随着小程序这种轻量级终端的呈现,公司和业务也不得不向着互联网巨头的流量抬头,同时小程序的逐步风靡,也使得用户下载 App 的习惯产生了变动。不论购物、订餐还是办事,人们都会首先查找“关上即用,即用即走”的小程序,省去了下载 App 的繁琐流程。

当然也晓得很多开发者对于小程序是有十分多意见的,App 也不会说死就死,毕竟 App 绝对于小程序来讲,还是有很多劣势。所以 App 和小程序开发都共存的状况下,如何解决效率问题?是否让过往开发的小程序间接运行在 Flutter 开发的利用中呢?同样一个性能业务仅需一次小程序开发,即可实现在除了微信端的其它 App 中也运行起来。

在 Google 找相干的解决方案和材料的时候,发现国外简直没有这种计划,国内倒是有厂商在做这块,想想也的确合乎情理。基于公司 Flutter 框架的根底现实情况下,名为 FinClip 小程序容器技术的产品是可能反对除原生 iOS、Android 之外的 Flutter 和 React Native,于是大略测试了下这个产品。

三、FinClip 实操

原理其实挺简略的,FinClip 提供了小程序 SDK 给 Flutter 利用进行集成,这样以来 App 即领有了一套可运行小程序业务代码的宿主环境,原理示意图如下。

3.1 获取凭据

集成 SDK 须要在 FinClip 平台中创立利用并绑定小程序,取得每个利用专属的 SDK KEY 及 SDK SECRET,随后能够在集成 SDK 时填写对应的参数。关上小程序时 SDK 会主动初始化,并校验 SDK KEY、SDK SECRET 与 BundleID (Application ID) 是否正确。

3.2 集成插件

首先,关上 Flutter 我的项目,而后在我的项目的 pubspec.yaml 文件中增加依赖。

mop: latest.version

依赖阐明参考:https://pub-web.flutter-io.cn/packages/mop

如果电脑是 mac M1 芯片,还须要在 iOS 文件夹的 Podfile 文件减少以下 3 行代码:

config.build settings ['ENABLE BITCODE'] = 'NO'
config.build settings ['IPHONEOS DEPLOYMENT TARGET'] = 19.0'config.build settings ['EXCLUDED ARCHS (sdk=iphonesimulator*]'] ='arm64 1386'

依赖示例如下:

3.3 Flutter API

在集成 mop 插件后,应用 SDK 提供的 API 之前必须要初始化 SDK。上面我列举官网的一些必要的 API,更具体的也能够查阅官网文档。

1,初始化 sdk 接口

在应用 sdk 提供的 api 之前必须要初始化 sdk,初始化 sdk 的接口为:

  ///
  ///
  /// initialize mop miniprogram engine.
  /// 初始化小程序
  /// [sdkkey] is required. it can be getted from api.finclip.com
  /// [secret] is required. it can be getted from api.finclip.com
  /// [apiServer] is optional. the mop server address. default is https://mp.finogeek.com
  /// [apiPrefix] is optional. the mop server prefix. default is /api/v1/mop
  /// [cryptType] is optional. cryptType, should be MD5/SM
  /// [disablePermission] is optional.
  /// [encryptServerData] 是否对服务器数据进行加密,须要服务器反对
  /// [userId] 用户 id
  /// [finStoreConfigs] 多服务配置
  /// [uiConfig] UI 配置
  /// [debug] 设置 debug 模式,影响调试和日志
  /// [customWebViewUserAgent] 设置自定义 webview ua
  /// [appletIntervalUpdateLimit] 设置小程序批量更新周期
  /// [maxRunningApplet] 设置最大同时运行小程序个数
  ///
  Future<Map> initialize(
    String sdkkey,
    String secret, {
    String? apiServer,
    String? apiPrefix,
    String? cryptType,
    bool encryptServerData = false,
    bool disablePermission = false,
    String? userId,
    bool debug = false,
    bool bindAppletWithMainProcess = false,
    List<FinStoreConfig>? finStoreConfigs,
    UIConfig? uiConfig,
    String? customWebViewUserAgent,
    int? appletIntervalUpdateLimit,
    int? maxRunningApplet,
  }) 

应用示例:

final res = await Mop.instance.initialize(
    '22LyZEib0gLTQdU3MUauARlLry7JL/2fRpscC9kpGZQA', '1c11d7252c53e0b6',
    apiServer: 'https://api.finclip.com', apiPrefix: '/api/v1/mop');

 

2,关上小程序

  /// open the miniprogram [appId] from the  mop server.
  /// 关上小程序
  /// [appId] is required.
  /// [path] is miniprogram open path. example /pages/index/index
  /// [query] is miniprogram query parameters. example key1=value1&key2=value2
  /// [sequence] is miniprogram sequence. example 0,1.2.3,4,5...
  /// [apiServer] is optional. the mop server address. default is https://mp.finogeek.com
  /// [apiPrefix] is optional. the mop server prefix. default is /api/v1/mop
  /// [fingerprint] is optional. the mop sdk fingerprint. is nullable
  /// [cryptType] is optional. cryptType, should be MD5/SM
  Future<Map> openApplet(
    final String appId, {
    final String? path,
    final String? query,
    final int? sequence,
    final String? apiServer,
    final String? scene,
  }) 

3,获取小程序信息

以后小程序信息,包含的字段有 appId、name、icon、description、version、thumbnail 等。

  ///
  ///  get current using applet
  ///  获取以后正在应用的小程序信息
  ///  {appId,name,icon,description,version,thumbnail}
  ///
  ///
  Future<Map<String, dynamic>> currentApplet()

4,敞开小程序

  ///
  /// close all running applets
  /// 敞开以后关上的所有小程序
  ///
  Future closeAllApplets()

5,清理小程序

革除缓存的小程序,当再次关上时,会从新下载小程序。

  ///
  /// clear applets cache
  /// 革除缓存的小程序
  ///
  Future clearApplets() 

3.4 官网示例

上面是官网提供的一段示例代码。

import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:io';
import 'package:mop/mop.dart';


void main() => runApp(MyApp());


class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}


class _MyAppState extends State<MyApp> {
  @override
  void initState() {super.initState();
    init();}


  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> init() async {if (Platform.isiOS) {
      //com.finogeeks.mopExample
      final res = await Mop.instance.initialize(
          '22LyZEib0gLTQdU3MUauARlLry7JL/2fRpscC9kpGZQA', '1c11d7252c53e0b6',
          apiServer: 'https://api.finclip.com', apiPrefix: '/api/v1/mop');
      print(res);
    } else if (Platform.isAndroid) {
      //com.finogeeks.mopexample
      final res = await Mop.instance.initialize(
          '22LyZEib0gLTQdU3MUauARjmmp6QmYgjGb3uHueys1oA', '98c49f97a031b555',
          apiServer: 'https://api.finclip.com', apiPrefix: '/api/v1/mop');
      print(res);
    }
    if (!mounted) return;
  }


  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('凡泰极客小程序 Flutter 插件'),
        ),
        body: Center(
          child: Container(
            padding: EdgeInsets.only(top: 20,),
            child: Column(
              children: <Widget>[
                Container(
                  decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5)),
                    gradient: LinearGradient(colors: const [Color(0xFF12767e), Color(0xFF0dabb8)],
                      stops: const [0.0, 1.0],
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                    ),
                  ),
                  child: FlatButton(onPressed: () {Mop.instance.openApplet('5e3c147a188211000141e9b1');
                    },
                    child: Text(
                      '关上示例小程序',
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
                SizedBox(height: 30),
                Container(
                  decoration: BoxDecoration(borderRadius: BorderRadius.all(Radius.circular(5)),
                    gradient: LinearGradient(colors: const [Color(0xFF12767e), Color(0xFF0dabb8)],
                      stops: const [0.0, 1.0],
                      begin: Alignment.topCenter,
                      end: Alignment.bottomCenter,
                    ),
                  ),
                  child: FlatButton(onPressed: () {Mop.instance.openApplet('5e4d123647edd60001055df1',sequence: 1);
                    },
                    child: Text(
                      '关上官网小程序',
                      style: TextStyle(color: Colors.white),
                    ),
                  ),
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

而后,我装置到真机上测试了一下,体验还不错。

参考文档:https://pub-web.flutter-io.cn/packages/mop

退出移动版