Android

Flutter app应用内更新 版本检查(教你避坑)

2020-03-04  本文已影响0人  吻_风

一、为什么需要?

在我们日常使用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。你不用管。直接安装到手机上去就行了。手机上可以安装成功。

上一篇下一篇

猜你喜欢

热点阅读