更新记录
1.0.4(2021-09-26)
- 升级Flutter 2.5.1
1.0.3(2021-06-30)
- 升级Flutter SDK 2.2.2
- 支持集成Flutter插件
1.0.2(2021-04-12)
- 修复iOS nvue组件进入后台,返回后无响应问题
- 升级Flutter SDK 2.0.4
平台兼容性
Android | Android CPU类型 | iOS |
---|---|---|
适用版本区间:5.0 - 11.0 | armeabi-v7a:未测试,arm64-v8a:未测试,x86:未测试 | 适用版本区间:9 - 15 |
原生插件通用使用流程:
- 购买插件,选择该插件绑定的项目。
- 在HBuilderX里找到项目,在manifest的app原生插件配置中勾选模块,如需要填写参数则参考插件作者的文档添加。
- 根据插件作者的提供的文档开发代码,在代码中引用插件,调用插件功能。
- 打包自定义基座,选择插件,得到自定义基座,然后运行时选择自定义基座,进行log输出测试。
- 开发完毕后正式云打包
付费原生插件目前不支持离线打包。
Android 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/android
iOS 离线打包原生插件另见文档 https://nativesupport.dcloud.net.cn/NativePlugin/offline_package/ios
注意事项:使用HBuilderX2.7.14以下版本,如果同一插件且同一appid下购买并绑定了多个包名,提交云打包界面提示包名绑定不一致时,需要在HBuilderX项目中manifest.json->“App原生插件配置”->”云端插件“列表中删除该插件重新选择
使用文档 (更多插件及问题,+微信:zhuzheVIP1)
重要提示:
该插件已经支持集成任何Flutter插件,包括webf(北海Kraken)。
详细集成,如有不懂,请联系个人微信:zhuzheVIP1
该插件不再更新,推荐使用离线打包方式:
插件详细文档
由于篇幅有限,详情文档请看:
插件说明
此插件提供了 sn-flutter 模块和 sn-flutter-view 组件(仅 nvue 使用)
插件示例
- uniapp: https://github.com/snice/uniapp-flutter
- flutter: https://github.com/snice/uniapp-flutter-module
温馨提示
- flutter 开发与 uniapp 开发都是独立的,插件只是为了把 2 者揉在一起
- 为了 iOS 能够云打包(不超过 40M),ios 插件仅支持 arm64;如有需要支持 armv7,请加群获取
Flutter 开发说明
Flutter 插件是基于多个页面或视图特性
-
创建 flutter module
-
开发
需要使用提供的模板
channel/uniapp.dart
class UniappMethodChannel { MethodChannel _channel; BuildContext _context; Map<String, Function(Map<String, dynamic> map)> _methodHandlers = new Map(); bool isInit = false; double _sh = 0; double get statusBarHeight { var su = ScreenUtil(); if (su != null && su.statusBarHeight > 0) { return su.statusBarHeight; } else { if (_sh > 0) return _sh; return 30; } } void initChannel() { if (isInit) return; isInit = true; _channel = MethodChannel('com.itfenbao.uniapp'); _channel.setMethodCallHandler((MethodCall call) async { if (call.method == "canPop") { _channel.invokeMethod("canPop", Navigator.canPop(_context)); } else { if (_methodHandlers.containsKey(call.method)) { if (call.arguments != null) { Function.apply(_methodHandlers[call.method], [new Map<String, dynamic>.from(call.arguments)]); } else { Function.apply(_methodHandlers[call.method], []); } } else { throw Exception('not implemented ${call.method}'); } } }); } void setContext(BuildContext context) { _context = context; _sh = MediaQuery.of(context).padding.top; } BuildContext getContext() { return _context; } void push(Route route) { Navigator.push(_context, route); this.fireEvent("canPop", true); } void pushNamed(String routeName) { Navigator.pushNamed(_context, routeName); } bool canPop() { return Navigator.canPop(_context); } void pop() { if (canPop()) { Navigator.maybePop(_context); Future.delayed(const Duration(milliseconds: 10), () { this.fireEvent("canPop", Navigator.canPop(_context)); }); } else { fireEvent("pop"); } } /// /// 监听uniapp事件 /// void $on(String method, Function(Map<String, dynamic> map) handler) { _methodHandlers[method] = handler; } /// /// 取消uniapp事件监听 /// void $off(String method) { _methodHandlers.remove(method); } /// /// 给uniapp发送事件 /// void $emit(String eventName, [dynamic arguments]) { fireEvent(eventName, arguments); } /// /// 给uniapp发送事件(同步) /// Future $emitSync(String eventName, [dynamic arguments]) { String alphabet = 'qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'; int strlenght = 25; /// 生成的字符串固定长度 String left = ""; for (var i = 0; i < strlenght; i++) { left = left + alphabet[Random().nextInt(alphabet.length)]; } var params = new Map<String, dynamic>.from(arguments); params["callbackId"] = eventName + "&" + left; return fireEvent(eventName, params); } /// /// 调用uniapp回调 /// void callback(String callbackId, [dynamic arguments]) { var params = new Map<String, dynamic>.from(arguments); params["callbackId"] = callbackId; fireEvent('_uni_callback', params); } /// /// 调用uniapp回调(持久回调) /// void callbackKeepAlive(String callbackId, [dynamic arguments]) { var params = new Map<String, dynamic>.from(arguments); params["callbackId"] = callbackId; params["keepAlive"] = true; fireEvent('_uni_callback', params); } Future fireEvent(String eventName, [dynamic arguments]) { if (isInit) { return _channel.invokeMethod(eventName, arguments); } return Future.value(""); } }
-
调试
使用 flutter 调试即可
-
打包
- android
flutter build aar
拷贝 build/host/outputs/repo/xx/flutter_release/1.0/flutter_release-1.0.aar 到 插件 android 目录下
- ios
flutter build ios-framework --xcframework --no-universal --output output/ios
拷贝 App.framework 到 插件 ios 目录下
-
工具
- ios framework 检测工具
检测 framework 文件是否动态库、包含的架构 下载后,放到*.framewrok 文件同级目录运行即可
manifest.json 配置注意
-
minSdkVersion
21
sn-flutter 模块
方法
-
cachePages
缓存页面,可以提前执行 flutter,提升页面打开速度
const flutter = uni.requireNativePlugin("sn-flutter"); flutter.cachePages({ pages: ["main", "topMain", "bottomMain"], });
main, topMain,bottomMain 都是入口函数
void main() => runApp(MyApp(Colors.blue)); @pragma('vm:entry-point') void topMain() => runApp(MyApp(Colors.green)); @pragma('vm:entry-point') void bottomMain() => runApp(MyApp(Colors.purple));
-
openPage
打开 flutter 页面。仅支持 uniapp page -> flutter page -> flutter page。
flutter.openPage({ id: "main", });
sn-flutter-view 组件
sn-flutter-view 是一个 nvue 组件,仅用于 nvue 页面
属性
属性 | 类型 | 默认值 | 说明 |
---|---|---|---|
entryPoint | string | main | 入口函数 |
destroyAfterBack | boolean | true | 是否返回页面销毁 flutter |
事件
-
pop
pop 调用
-
popChange
pop 变化
popChange({ detail }) { // 是否可以pop,默认是不可以pop this.canPop = detail.pop; }
方法
-
pop
pop 路由
使用
<template>
<sn-flutter-view ref="flutter" :instanceId="instanceId" :entryPoint="entryPoint" :params="params" destroyAfterBack="true" @pop="onPop" @popChange="popChange" style="flex:1;" />
</template>
<script>
import { MethodChannel } from '@/js/flutter/flutter.js';
export default {
data() {
return {
canPop: false,
instanceId: 'topMain',
entryPoint: 'main',
params: {
a: 1
}
};
},
onBackPress() {
if (this.canPop) {
this.$refs.flutter.pop();
return true;
}
},
onLoad() {
this.methodChannel = new MethodChannel(this.instanceId);
this.methodChannel.$on('test', e => {
uni.showToast({
title: JSON.stringify(e),
icon: 'none'
});
if (e.callbackId) {
this.methodChannel.callback(e.callbackId, { result: 3 });
}
});
},
onUnload() {
this.methodChannel.$off('test');
},
methods: {
onPop() {
uni.navigateBack();
},
popChange({ detail }) {
this.canPop = detail.pop;
}
}
};
</script>
<style></style>