用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;
}
}
谢谢