用flutter開發了一個簡單的跨平臺app,在網上找了很多 app內升級的博客,大部分都是複製的,講的不全,不過有一篇好文章推薦給大家,重點是引用插件的版本儘量和博客中的一致,否則編譯時各種報錯,Flutter 項目 app迭代更新,我開始引用各個插件最新版本,結果一直編譯失敗
下面將分詳細說說,爲那些初學者提供思路,並把demo分享出來
1.有兩種方法實現app升級
- 1.直接使用url_launcher插件跳轉本地瀏覽器下載,版本號沒有要求
- 2.使用多個flutter插件,一起達到下載升級效果
2.大概思路
- 1.進入頁面,請求服務端接口,對比服務端的版本與當前手機版本是否一致
- 2.不一致則提示用戶可以升級
- 3.如果是android:則下載apk文件,存入手機某個位置,完成後打開該apk進行安裝
- 4.如果是ios:則通過url_launcher插件,打開 app_store中你們應用的鏈接地址,用戶自己選擇是否升級
下面看看手機上的效果
- 使用 url_launcher插件方式,會提示使用本地瀏覽器下載
最後都能進入安裝界面
3.代碼實現
- 1.引用插件,千萬要注意版本,我最開始使用的最新版,結果一直編譯失敗
dependencies:
flutter:
sdk: flutter
dio: ^3.0.8
package_info: ^0.4.0+13
permission_handler: 3.2.0
flutter_downloader: 1.1.7
path_provider: 1.5.1
open_file: ^1.3.0
url_launcher: 5.1.2
progress_dialog: 1.2.0
- 2.在android/app/src/main/AndroidManifest.xml中添加配置
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application .....>
.....其他已有內容省略了
<provider
android:name="vn.hunghd.flutterdownloader.DownloadedFileProvider"
android:authorities="${applicationId}.flutter_downloader.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
<provider
android:name="androidx.work.impl.WorkManagerInitializer"
android:authorities="${applicationId}.workmanager-init"
android:enabled="false"
android:exported="false" />
<provider
android:name="vn.hunghd.flutterdownloader.FlutterDownloaderInitializer"
android:authorities="${applicationId}.flutter-downloader-init"
android:exported="false">
<meta-data
android:name="vn.hunghd.flutterdownloader.MAX_CONCURRENT_TASKS"
android:value="5" />
</provider>
</application>
- 3.服務端返回的數據集如下:
{
"android_version" : "1.0.2",
"android_msg" : "對系統進行了優化,修復了多個bug",
"android_url" : "http://www.xxx.com/flutterApp.apk"
}
- 4.代碼實現,代碼比較長,都很簡單且有註釋,請細心閱讀一下
import 'dart:convert';
import 'dart:io';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_downloader/flutter_downloader.dart';
import 'package:open_file/open_file.dart';
import 'package:package_info/package_info.dart';
import 'package:path_provider/path_provider.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:permission_handler/permission_handler.dart';
import 'Http.dart';
import 'package:progress_dialog/progress_dialog.dart';
//定義apk的名稱,與下載進度dialog
String apkName ='flutterApp.apk';
ProgressDialog pr;
class Upgrade extends StatefulWidget {
Upgrade({Key key}) : super(key: key);
@override
_UpgradeState createState() => _UpgradeState();
}
class _UpgradeState extends State<Upgrade> {
@override
void initState() {
super.initState();
//檢查是否有更新
checkUpdate(context);
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('app upgrade'),
),
body: Container(
alignment: Alignment.center,
child: Text('Checking...' , style: TextStyle(
fontSize: 16,
),),
),
);
}
///檢查是否有更新
Future<void> checkUpdate(BuildContext context) async{
//Android , 需要下載apk包
if(Platform.isAndroid){
print('is android');
PackageInfo packageInfo = await PackageInfo.fromPlatform();
String localVersion = packageInfo.version;
//此處使用了dio,封裝了httpGet方法,獲取服務端中最新的app版本信息
String versionInfo = await httpGet('/appversion.json');
if(versionInfo != ""){
Map<String, dynamic> map = json.decode(versionInfo);
String serverAndroidVersion = map['android_version'].toString();
String serverMsg = map['android_msg'].toString();
String url = map['android_url'].toString();
print(url);
print('本地版本: ' + localVersion + ',最新版本: ' + serverAndroidVersion );
int c = serverAndroidVersion.compareTo(localVersion);
//如果服務端版本大於本地版本則提示更新
if(c == 1){
showUpdate(context, serverAndroidVersion, serverMsg, url);
}
}
}
//Ios , 只能跳轉到 AppStore , 直接採用url_launcher就可以了
//android也可以採用此方法,會跳轉到手機瀏覽器中下載
if(Platform.isIOS){
print('is ios');
final url = "https://itunes.apple.com/cn/app/id1380512641"; // id 後面的數字換成自己的應用 id 就行了
if (await canLaunch(url)) {
await launch(url, forceSafariVC: false);
} else {
throw 'Could not launch $url';
}
}
}
///2.顯示更新內容
Future<void> showUpdate(BuildContext context , String version, String data, String url) async {
return showDialog<void>(
context: context,
barrierDismissible: true,
builder: (BuildContext context) {
return CupertinoAlertDialog(
title: Text('檢測到新版本 v$version'),
content : Text('是否要更新到最新版本?') ,
actions: <Widget>[
FlatButton(
child: Text('下次在說'),
onPressed: () {
Navigator.of(context).pop();
},
),
FlatButton(
child: Text('立即更新'),
onPressed: ()=>doUpdate(context,version,url)
,
),
],
);
},
);
}
///3.執行更新操作
doUpdate(BuildContext context , String version,String url) async {
//關閉更新內容提示框
Navigator.pop(context);
//獲取權限
var per = await checkPermission();
if(per != null && !per){
return null;
}
//開始下載apk
executeDownload(context , url);
}
///4.檢查是否有權限
Future<bool> checkPermission() async {
//檢查是否已有讀寫內存權限
PermissionStatus status = await PermissionHandler().checkPermissionStatus(PermissionGroup.storage);
print(status);
//判斷如果還沒擁有讀寫權限就申請獲取權限
if(status != PermissionStatus.granted){
var map = await PermissionHandler().requestPermissions([PermissionGroup.storage]);
if(map[PermissionGroup.storage] != PermissionStatus.granted){
return false;
}
}
return true;
}
///5.下載apk
Future<void> executeDownload(BuildContext context ,String url) async {
//下載時顯示下載進度dialog
pr = new ProgressDialog(context,type: ProgressDialogType.Download, isDismissible: true, showLogs: true);
if (!pr.isShowing()) {
pr.show();
}
//apk存放路徑
final path = await _apkLocalPath;
File file = File(path + '/' + apkName);
if (await file.exists()) await file.delete();
//下載
final taskId = await FlutterDownloader.enqueue(
url: url,//下載最新apk的網絡地址
savedDir: path,
fileName: apkName,
showNotification: true,
openFileFromNotification: true);
FlutterDownloader.registerCallback((id, status, progress) {
if (status == DownloadTaskStatus.running) {
pr.update(progress: progress.toDouble(), message: "下載中,請稍後…");
}
if (status == DownloadTaskStatus.failed) {
if (pr.isShowing()) {
pr.hide();
}
}
if (taskId == id && status == DownloadTaskStatus.complete) {
if (pr.isShowing()) {
pr.hide();
}
_installApk();
}
});
}
//6.安裝app
Future<Null> _installApk() async {
String path = await _apkLocalPath;
await OpenFile.open(path + '/' + apkName);
}
// 獲取apk存放地址(外部路徑)
Future<String> get _apkLocalPath async {
final directory = await getExternalStorageDirectory();
return directory.path;
}
}
謝謝