flutter app内更新升级

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

谢谢

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章