关注公众号: 微信搜索 前端工具人
; 收货更多的干货
一、思路步骤:
- 获取后台接口返回的最新版本的
vsersion
值! (这需要每次APP发版提审后,同时更新后台数据库Appversion
); - 使用
package_info
插件获取项目当前APP运行的 的版本号version
; - 后台返回的版本
version
大于package_info
获取的version
!则弹出提示更新; ios
跳转AppStore
进行更新;android
则App内下载更新;
二、说明:
android
版本的 apk 上传到公司 oss, 生成的链接就是下载链接- 可能存在的坑有, 本地开发模式下
android
下载好的apk安装不了, 提示应用签名不一致! (原因 本地开发模式并没有走 keystore签名;但是apk有; 用户从应用商店下载不会出现这情况; 或者你通过apk安装应用,在走流程测试就不会了
) - 使用的第三方插件有
package_info
用于获取当前版本号;flutter_xupdate
用于android
App内下载更新; - 效果图见尾部
三、具体代码:
注: android 实现 App 内下载更新,我选用的是 flutter_xupdate
插件并且使用的是自定义JSON的格式(灵活性较好); 插件使用方式请点击 flutter_xupdate
3.1、 homePage.dart 文件
import './CheckUpdate.dart';
CheckUpdate checkUpdate = CheckUpdate();
// 获取版本
Future<dynamic> initPackageInfo () async {
PackageInfo packageInfo = await PackageInfo.fromPlatform();
version = packageInfo.version;
}
// 检查是否需要版本更新
void _checkUpdateVersion () async {
await initPackageInfo();
try {
var response = await HttpUtil().post("app/version/detail?", 'mobile', 'json', false);
if (response["code"] != 0) {
setState(() {
versionData = response["data"];
});
// 后台返回的版本号是带小数点的(2.8.1)所以去除小数点用于做对比
var targetVersion = response["data"]["versionCode"].replaceAll('.', '');
// 当前App运行版本
var currentVersion = version.replaceAll('.', '');
if (int.parse(targetVersion) > int.parse(currentVersion)) {
if (Platform.isAndroid) { // 安卓弹窗提示本地下载, 交由flutter_xupdate 处理,不用我们干嘛。
await checkUpdate.initXUpdate();
checkUpdate.checkUpdateByUpdateEntity(versionData); // flutter_xupdate 自定义JSON 方式,
} else if (Platform.isIOS) { // IOS 跳转 AppStore
showIOSDialog(); // 弹出ios提示更新框
}
}
}
} catch (e) {
print(e);
}
}
3.2、CheckUpdate.dart 文件
import 'dart:convert';
import 'package:flutter_xupdate/flutter_xupdate.dart';
class CheckUpdate{
// 将自定义的json内容解析为UpdateEntity实体类
UpdateEntity customParseJson(String json) {
AppInfo appInfo = AppInfo.fromJson(json);
return UpdateEntity(
isForce: appInfo.isForce, // 是否强制更新
hasUpdate: appInfo.hasUpdate, // 是否需要更新 默认true, 手动自行判断
isIgnorable: appInfo.isIgnorable, // 是否显示 “忽略该版本”
versionCode: appInfo.versionCode, // 新版本号
versionName: appInfo.versionName, // 新版名称
updateContent: appInfo.updateLog, // 新版更新日志
downloadUrl: appInfo.apkUrl, // 新版本下载链接
apkSize: appInfo.apkSize); // 新版本大小
}
// 自定义JSON更新
checkUpdateByUpdateEntity(Map jsonData) async {
var versionCode = jsonData["versionCode"].replaceAll('.', '');
var updateText = jsonData["updateLog"].split('。');
var updateLog = '';
updateText.forEach((t) {
updateLog += '\r\n$t';
});
var rusultJson = {
"isForce": jsonData["isForce"] == 1,
"hasUpdate": true,
"isIgnorable": false,
"versionCode": int.parse(versionCode),
"versionName": jsonData["versionName"],
"updateLog": updateLog,
"apkUrl": jsonData["apkUrl"],
"apkSize":jsonData["apkSize"]
};
FlutterXUpdate.updateByInfo(updateEntity: customParseJson(json.encode(rusultJson)));
}
// 初始化插件
Future<dynamic> initXUpdate () async {
FlutterXUpdate.init(
//是否输出日志
debug: true,
//是否使用post请求
isPost: false,
//post请求是否是上传json
isPostJson: false,
//是否开启自动模式
isWifiOnly: false,
///是否开启自动模式
isAutoMode: false,
//需要设置的公共参数
supportSilentInstall: false,
//在下载过程中,如果点击了取消的话,是否弹出切换下载方式的重试提示弹窗
enableRetry: false)
.then((value) {
print("初始化成功: $value");
}).catchError((error) {
print(error);
});
FlutterXUpdate.setUpdateHandler(
onUpdateError: (Map<String, dynamic> message) async {
print("初始化成功: $message");
}, onUpdateParse: (String json) async {
//这里是自定义json解析
return customParseJson(json);
});
}
}
// 使用Dart Data Class Generator插件进行创建 使用命令: Generate from JSON
class AppInfo {
final bool isForce;
final bool hasUpdate;
final bool isIgnorable;
final int versionCode;
final String versionName;
final String updateLog;
final String apkUrl;
final int apkSize;
AppInfo({
this.isForce,
this.hasUpdate,
this.isIgnorable,
this.versionCode,
this.versionName,
this.updateLog,
this.apkUrl,
this.apkSize,
});
Map<String, dynamic> toMap() {
return {
'isForce': isForce,
'hasUpdate': hasUpdate,
'isIgnorable': isIgnorable,
'versionCode': versionCode,
'versionName': versionName,
'updateLog': updateLog,
'apkUrl': apkUrl,
'apkSize': apkSize,
};
}
static AppInfo fromMap(Map<String, dynamic> map) {
if (map == null) return null;
return AppInfo(
isForce: map['isForce'],
hasUpdate: map['hasUpdate'],
isIgnorable: map['isIgnorable'],
versionCode: map['versionCode']?.toInt(),
versionName: map['versionName'],
updateLog: map['updateLog'],
apkUrl: map['apkUrl'],
apkSize: map['apkSize']?.toInt(),
);
}
String toJson() => json.encode(toMap());
static AppInfo fromJson(String source) => fromMap(json.decode(source));
@override
String toString() {
return 'AppInfo isForce: $isForce, hasUpdate: $hasUpdate, isIgnorable: $isIgnorable, versionCode: $versionCode, versionName: $versionName, updateLog: $updateLog, apkUrl: $apkUrl, apkSize: $apkSize';
}
}
3.3 IOS showIOSDialog 方法
// 跳转 AppStore 更新 iOSUrl APP 在 AppStore 的链接
Future<void> showIOSDialog() async {
showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (context) {
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Container(
padding: const EdgeInsets.fromLTRB(40.0, 0, 40.0, 0),
child: Image(
width: MediaQuery.of(context).size.width,
image: AssetImage("images/bg_update_top.png"),
fit: BoxFit.fill,
)),
Container(
width: MediaQuery.of(context).size.width,
margin: const EdgeInsets.fromLTRB(40.0, 0, 40.0, 0),
decoration: new BoxDecoration(
color: Color(0xffffffff),
borderRadius: BorderRadius.only(
bottomLeft: Radius.circular(10),
bottomRight: Radius.circular(10),
)),
child: Container(
padding: const EdgeInsets.fromLTRB(20.0, 0, 20.0, 0.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Padding(
padding:
const EdgeInsets.only(bottom: 8.0, top: 10.0),
child: Text(
'是否升级到${versionData["versionName"]}版本',
style: TextStyle(
fontSize: 14.0,
color: Color(0xff555555),
decoration: TextDecoration.none,
),
textAlign: TextAlign.left,
),
),
Padding(
padding: const EdgeInsets.only(bottom: 15.0),
child: Text(
'新版本大小: ${versionData["apkSize"]}MB',
style: TextStyle(
fontSize: 13.0,
color: Color(0xff777777),
decoration: TextDecoration.none,
),
textAlign: TextAlign.left,
),
),
Container(
height: 110.0,
child: ListView(
itemExtent: null,
shrinkWrap: true,
children: _getData())),
Container(
margin: const EdgeInsets.only(bottom: 6.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
FlatButton(
child: Text(
"下次再说",
style: TextStyle(
color: Color(0xffffbb5b), fontSize: 18.0),
),
onPressed: () =>
Navigator.of(context).pop(), //关闭对话框
),
FlatButton(
child: Text(
"立即前往",
style: TextStyle(
color: Color(0xffffbb5b), fontSize: 18.0),
),
onPressed: () async {
if (await canLaunch(iOSUrl)) {
await launch(iOSUrl);
}
}, //关闭对话框
),
],
),
)
],
))),
],
);
},
);
}
四、 效果图
android
:
ios
: