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

一、为什么需要?

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容