最近在使用flutter开发APP,flutter实现了一套代码同时生成Android和iOS两个平台的APP,可以实现零基础快速上手APP开发,缩短开发周期。但flutter仍处于较快增长期,版本迭代速度快,文档资料相对较少,这里将开发中遇到的一些问题整理下来,备忘+分享。
flutter版本:1.12.13+hotfix.5
微信分享
这里使用fluwx插件实现微信中分享,版本号1.2.1+1。
配置依赖
在pubspec.yaml中增加依赖
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
#other dependencies
fluwx: ^1.2.1+1
Android配置
如果需要微信回调,需要配置WXEntryActivity和WXPayEntryActivity,如果不使用回调,则不需要特殊配置。详见官方doc
IOS配置
如官方文档所示,需要配置ios的URLSchema, LSApplicationQueriesSchemes or universal link
ios/Runner/Info.plist
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>your url name</string>
<key>CFBundleURLSchemes</key>
<array>
<string>your scheme</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>weixin</string>
<key>CFBundleURLSchemes</key>
<array>
<string>you wechat appid</string>
</array>
</dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>wechat</string>
<string>weixin</string>
<string>weixinULAPI</string>
</array>
其中CFBundleURLTypes的第一项是为了能够从url跳回APP所设置
ios/Runner/Runner.entitlements
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<!-- ... other keys -->
<key>com.apple.developer.associated-domains</key>
<array>
<string>webcredentials:your universal link</string>
</array>
<!-- ... other keys -->
</dict>
</plist>
这里的universal link要与微信开放平台上的设置保持一致。
Flutter调用
注册微信sdk,如果用于ios,必须指定universalLink
import 'package:fluwx/fluwx.dart' as fluwx;
@override
void initState() {
fluwx.registerWxApi(
appId: your wechat appid,
doOnAndroid: true,
doOnIOS: true,
universalLink: your universal link
);
}
分享,以图文链接到好友为例
fluwx.shareToWeChat(
fluwx.WeChatShareWebPageModel(
title: your title,
description: your description(can split by \n),
webPage: 'https://example.com/app/jump.html?pageName=xx&id=xx',
thumbnail: 'https://example.com/images/share_weixin.png',
scene: fluwx.WeChatScene.SESSION
)
).then((data) {
print (data);
});
至此,可实现从APP分享到微信,下一步将从微信点击分享后的链接,唤起APP并直接打开指定页面。
打开APP指定页面
从APP分享出去只是产品宣传的第一步,下一步需要接收到的用户通过点击链接卡片可以打开我们的APP的某个指定页面。当然,用户点击分享链接后,可以打开产品的小程序or网页版,以更好的提升体验,这里仅以跳转APP为例。
很多文档提到改写Activity类来实现跳转链接参数的解析,从而进一步跳转到指定页面,但随着flutter的升级,kotlin的Activity相关API有明显变化,由于笔者并未做过Android开发,所以放弃了硬写的方案,这里使用uni_links来实现参数的解析。
配置依赖
uni_links: ^0.2.0
Android配置
android/app/src/main/AndroidManifest.xml
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data
android:scheme="yourScheme"
android:host="yourHost"
/>
</intent-filter>
</activity>
IOS配置
在上面微信配置中已加入相关配置(这里没有使用HTTPS scheme,如果使用的话,还需要一些额外配置,详见uni_link的文档)
Flutter调用
uni_link的官方example已经非常详尽,照着来就可以实现,如下是我的实现
在main.dart中实现
class MyAppState extends State<MyApp> with WidgetsBindingObserver {
StreamSubscription _sub;
String _latestLink = 'Unknown';
Uri _latestUri;
// This widget is the root of your application.
@override
void initState() {
super.initState();
initPlatformState();
}
@override
void dispose() {
if (_sub != null) _sub.cancel();
super.dispose();
}
Future<void> initPlatformState() async {
_sub = getLinksStream().listen((String link) {
if (!mounted) return;
setState(() {
_latestLink = link ?? "Unknown";
_latestUri = null;
try {
if (link != null) _latestUri = Uri.parse(link);
} on FormatException {}
});
}, onError: (err) {
if (!mounted) return;
setState(() {
_latestLink = 'Failed to get latest link: $err.';
_latestUri = null;
});
});
getLinksStream().listen((String link) {
print('got link: $link');
}, onError: (err) {
print('got err: $err');
});
String initialLink;
Uri initialUri;
try {
initialLink = await getInitialLink();
print('initial link: $initialLink');
if (initialLink != null) initialUri = Uri.parse(initialLink);
} on PlatformException {
initialLink = 'Failed to get initial link.';
initialUri = null;
} on FormatException {
initialLink = 'Failed to parse the initial link as Uri.';
initialUri = null;
}
if (!mounted) return;
setState(() {
_latestLink = initialLink;
_latestUri = initialUri;
});
}
@override
Widget build(BuildContext context) {
final queryParams = _latestUri?.queryParametersAll?.entries?.toList();
// print (queryParams);
// print (queryParams?.length);
Widget homeWidget = new SplashPage();
String pageType;
String taskID;
if (queryParams != null) {
for (var param in queryParams) {
if (param.key == 'pageName') {
pageType = param.value.length > 0 ? param.value[0] : null;
}
if (param.key == 'taskid') {
taskID = param.value.length > 0 ? param.value[0] : null;
}
}
}
print (pageType);
if (pageType == 'report') {
homeWidget = new ReportPage(taskID: taskID,);
}
return MaterialApp(
routes: {
Config.routeMain: (context) => MyHomePage()
},
debugShowCheckedModeBanner: false,
title: 'your title',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: homeWidget
);
}
}
中转页面
这里写了一个简单的中转页面
<!Doctype html>
<html xmlns=http://www.w3.org/1999/xhtml>
<head>
<meta http-equiv=Content-Type content="text/html;charset=utf-8">
<title>Your Title</title>
</head>
<body>
<style>
#mobliestart{font-size:40px;}
</style>
<a href='yourScheme://yourHost?page=report&taskid=1' id="mobliestart">点击打开APP</a>
<script type="text/javascript">
function applink(){
window.location = 'yourScheme://yourHost?page=report&taskid=1';
}
applink();
</script>
</body>
</html>
中转页面设置了自动跳转和点击跳转,由于微信浏览器对scheme link的限制,ios和Android都需要点击右上角的‘浏览器中打开’。ios(iPhone xr,13.3)在Safari中打开后会自动弹出对话框,Android(华为)在系统浏览器和chrome会自动弹出对话框,qq浏览器需要点击页面超链接才会弹出对话框。
注:目前仍存在一个问题,如果APP处于后台打开状态,从链接打开后,只是唤起APP,并不会跳转到指定页面,通过打印日志,build函数中解析链接参数的逻辑解析正确,目前这个问题还未进一步解决。