一、为什么需要?
在我们日常使用App中经常会看到有版本需要更新。这有有两个用处:
1.通知用户有新版本,并且有什么更新内容;
2.在一些bug性要求强制更新的情况可以强制必须更新。
二、更新样式是什么?
三、需要的插件
插件有下面四个。我使用的是当前版本,读者根据最新版本使用
#dio
dio: ^3.0.8
#更新插件
ota_update: ^2.2.1
#获取app信息
package_info: ^0.4.0+14
#跳转第三方
url_launcher: ^5.4.2
把上面代码写在pubspec.yaml文件的以下位置
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
#这里写插件
执行package get
四、主要代码是什么?
-首先定义一个版本模型,下面的BaseModel只是定义了一个抽象类,这里你们可以去掉
class VersionModel extends BaseModel{
String version;
bool mandatory;
List<String> updateContent;
String iosAddress;
String androidAddress;
VersionModel.fromJson(Map<String, dynamic> json) : super.fromJson(json){
version=json['version'];
mandatory=json['mandatory'];
updateContent=List.from(json['updateContent']);
iosAddress=json['iosAddress'];
androidAddress=json['androidAddress'];
}
@override
Map<String, dynamic> toJson() {
return {
'version':version,
'mandatory':mandatory,
'updateContent':updateContent,
'iosAddress':iosAddress,
'androidAddress':androidAddress,
};
}
}
先是检查版本的方法
///检测当前app版本
_getCurrentVersion()async{
PackageInfo packageInfo=await PackageInfo.fromPlatform();
var currentVersion=packageInfo.version;
return currentVersion;
}
///版本校验
_checkVersion()async {
/// 获得服务器版本
//这写上获取json的url,json格式按照定义的versionModel
String url='http://xxxxxxxxxxxxxxxxxxx/version.json'
var json = await Dio().get(url);
if(json==null){
//获取版本失败 网络或者其他原因 退出App
//ToastUtil.showToast('获取版本失败');
await SystemChannels.platform.invokeMethod('SystemNavigator.pop');
}
serviceVersion=VersionModel.fromJson(json);
var currentVersion = await _getCurrentVersion();
print(currentVersion);
if(currentVersion==serviceVersion.version){
//验证通过
_goNextPage();
}else{
//版本不符弹出对话框
_showUpdateDialog();
}
}
_showUpdateDialog(){
showDialog(
context: context,
barrierDismissible: false,
builder: (_){
List<Widget> contentList=[];
var style=TextStyle(fontSize: 15,);
contentList.addAll(serviceVersion.updateContent.map((item){
return Text(item,style: style,);
}).toList());
contentList.insert(0, Container(height: 0.4,color: Colors.black,margin: EdgeInsets.only(top: 5,bottom: 5),));
return CupertinoAlertDialog(
title:Text('版本更新',style:TextStyle(fontSize: 18,fontWeight: FontWeight.w600)),
content: Container(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: contentList
),
),
actions: <Widget>[
FlatButton(
child: Text('取消'),
textColor: Colors.grey,
onPressed: serviceVersion.mandatory?null:(){
_goNextPage();
},
),
FlatButton(
child: Text('更新'),
textColor: Colors.blue,
onPressed: ()async{
if(Platform.isAndroid){
//安卓应用内下载
Navigator.pop(context);
tryOtaUpdate();
}else{
//ios 跳转商店
if(await canLaunch(serviceVersion.iosAddress)){
await launch(serviceVersion.iosAddress);
}else{
throw 'Could not launch ';
}
}
},
)
],
);
}
);
}
下面是安卓后台下载更新方法
Future<void> tryOtaUpdate() async {
try {
OtaUpdate()
.execute(serviceVersion.androidAddress, destinationFilename: 'task_app.apk')
.listen(
(OtaEvent event) {
setState(() => currentEvent = event);
},
);
} catch (e) {
print('Failed to make OTA update. Details: $e');
}
}
页面可以如下展示。也可根据自己实际情况更换
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: currentEvent != null && serviceVersion != null ? AppBar(
title: const Text('更新应用'),
) : null,
body: Center(
child: Container(
padding: EdgeInsets.only(left: 20, right: 20),
child: Column(
mainAxisAlignment: MainAxisAlignment.end,
children: <Widget>[
Image.asset(
'assets/images/check_logo.png', height: 100, width: 100,),
Container(
height: 100,
//如果currentEvent不为空则展示加载条
child: currentEvent != null ? Column(
children: <Widget>[
double.tryParse(currentEvent.value) is double
? LinearProgressIndicator(
semanticsLabel: currentEvent.value,
value: double.tryParse(currentEvent.value) / 100,
semanticsValue: currentEvent.value,
)
: Container(),
Container(
child: Text(
'${currentEvent.status == OtaStatus.DOWNLOADING
? '下载中'
: (currentEvent.status == OtaStatus.INSTALLING
? '安装中...'
: '')} ${(currentEvent.status == OtaStatus.DOWNLOADING
? ':'
: '')} ${currentEvent.value}${currentEvent.status ==
OtaStatus.DOWNLOADING ? '%' : ''} \n'),
margin: EdgeInsets.only(bottom: 50),
)
],
) : Container()
)
],
),
),
)
);
}
下面是我给的示例验证version.json
{
"version": "1.0.3",
"mandatory": false,
"updateContent": ["1、修复了一些显示问题", "2、新增搜索页面", "3、项目管理可切换成员搜索", "4、项目过期显示橙色"],
"iosAddress": "https://apps.apple.com/cn/app/tasktodo/id1498326734",
"androidAddress": "http://lc-sys.oss-cn-shanghai.aliyuncs.com/apk/task_s103.apk"
}
五、安卓和ios设置
ios因为是跳转到appstore去更新没什么需要设置的
安卓使用Ota_update插件还需要一些其他设置
-首先在项目android/app/src/main/AndroidManifest.xml文件中添加如下代码
...以上省略,这段代码主要是用于ota_update插件的
<provider
android:name="sk.fourq.otaupdate.OtaUpdateFileProvider"
android:authorities="${applicationId}.ota_update_provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
-然后在android/app/src/main/res文件夹内新建文件夹xml再往里面添加一个filepaths.xml文件,内容是
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-path name="external_download" path="Download"/>
</paths>
到此有很多小伙伴就成功了。。但是这里面还有几个大坑
六、还有哪些坑?
1、如果你安卓下载地址是http的那么恭喜你成功入坑。
安卓从sdk 27开始http访问很多情况下会被禁止。如何做呢?
在上面的xml文件夹内再新建一个文件network_security_config.xml
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<base-config cleartextTrafficPermitted="true" />
</network-security-config>
然后在AndroidManifest.xml的
<application
android:name="io.flutter.app.FlutterApplication"
android:label="task todo"
android:networkSecurityConfig="@xml/network_security_config"
...以下省略。添加上面一句用来执行刚才添加的network_security_config.xml
然后大功告成。
2、有很多朋友这里会发现下载成功了。安装却失败了,那么你又进坑了。
我测试很多次发现这是模拟器的问题。模拟器无法直接安装apk。你不用管。直接安装到手机上去就行了。手机上可以安装成功。