前言

近期做flutter APP框架的搭建封装,在APP自动更新这块,参考了很多网址,但都不全面;故自己动手封装了一套,主要采用flutter_downloader及progress_dialog等;在APP启动成功后,若有最新版本,便会自动弹框提示是否更新,若更新,将会下载最新APP,并显示下载进度,下载完成后,将自动提示是否安装最新包。
此功能主要针对android APP自动更新

在pubspec.yaml中安装依赖

     permission_handler: 5.0.0+hotfix.4
     package_info: 0.4.1
     path_provider: 1.6.11
     open_file: 3.0.1
     flutter_downloader: 1.5.0
     progress_dialog: 1.2.0

在控制台输入 flutter packages get 命令,下载依赖包

在main.dart文件中,初始化FlutterDownLoader

  ...
  import 'package:flutter_downloader/flutter_downloader.dart';
  
  ...

  
  void main() async {
    WidgetsFlutterBinding.ensureInitialized();
    await FlutterDownloader.initialize(
      debug: true
    );
    runApp(MyApp());
  }

  ....

配置网络

在android/app/src/main/res目录下,新建文件夹xml,在xml文件夹下,新增network_security_config.xml
在network_security_config.xml中写入以下代码

 <?xml version="1.0" encoding="utf-8"?>
    <network-security-config>
        <base-config cleartextTrafficPermitted="true">
            <trust-anchors>
                <certificates src="system" />
            </trust-anchors>
        </base-config>
    </network-security-config>

如下图:
在这里插入图片描述

在AndroidManifest.xml新增如下配置

  1. 新增权限
  <uses-permission android:name="android.permission.INTERNET"/>
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
  <uses-permission android:name="android.permission.RECORD_AUDIO" />
  1. 在<application …>中新增network_security_config配置
  <application
        android:name="io.flutter.app.FlutterApplication"
        android:label="flutter助手"
        android:icon="@mipmap/ic_launcher"
        android:networkSecurityConfig="@xml/network_security_config"
    >

如下图:
在这里插入图片描述
3. 在<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">
            <!-- changes this number to configure the maximum number of concurrent tasks -->
            <meta-data
                android:name="vn.hunghd.flutterdownloader.MAX_CONCURRENT_TASKS"
                android:value="5" />
        </provider>


        <provider
                android:name="androidx.core.content.FileProvider"
                android:authorities="${applicationId}.fileProvider"
                android:exported="false"
                android:grantUriPermissions="true"
                tools:replace="android:authorities">
            <meta-data
                    android:name="android.support.FILE_PROVIDER_PATHS"
                    android:resource="@xml/filepaths"
                    tools:replace="android:resource" />
        </provider>

如下图:
在这里插入图片描述

在项目入口dart文件中,新增自动更新逻辑代码

  1. 导入相关包
  import 'dart:isolate';
  import 'dart:ui';
  import 'dart:async';
  import 'dart:io';

  import 'package:package_info/package_info.dart';
  import 'package:path_provider/path_provider.dart';
  import 'package:open_file/open_file.dart';
  import 'package:flutter_downloader/flutter_downloader.dart';
  import 'package:progress_dialog/progress_dialog.dart';

  1. 声明变量
 String serviceVersionCode = '';
  String appId = '';
  ProgressDialog pr;
  String apkName ='app-release.apk';
  String appPath = '';
  ReceivePort _port = ReceivePort();
  1. 在initState中初始化

     IsolateNameServer.registerPortWithName(_port.sendPort, 'downloader_send_port');
     _port.listen(_updateDownLoadInfo);
     FlutterDownloader.registerCallback(_downLoadCallback);
    
  2. 判断,自动更新

 @override
  void afterFirstLayout(BuildContext context) {
    // 如果是android,则执行热更新
    if(Platform.isAndroid){
       _getNewVersionAPP(context);
    }
   
  }
  1. 自动更新代码
 /// 执行版本更新的网络请求
  _getNewVersionAPP(context) async {
     HttpUtils.send(
      context,
      'http://update.rwworks.com:8088/appManager/monitor/app/version/check/flutterTempldate',
    ).then((res) {
       serviceVersionCode = res.data["versionNo"];
       appId = res.data['id'];
      _checkVersionCode();
    });
  }
  
  /// 检查当前版本是否为最新,若不是,则更新
  void _checkVersionCode() {
    PackageInfo.fromPlatform().then((PackageInfo packageInfo) {
      var currentVersionCode = packageInfo.version;
      if (double.parse(serviceVersionCode.substring(0,3))> double.parse(currentVersionCode.substring(0,3))) {
        _showNewVersionAppDialog();
      }
   });
  }
  
  /// 版本更新提示对话框
  Future<void> _showNewVersionAppDialog() async {
    return showDialog<void>(
        context: context,
        barrierDismissible: false,
        builder: (BuildContext context) {
          return AlertDialog(
            title: new Row(
              children: <Widget>[
                new Padding(
                    padding: const EdgeInsets.fromLTRB(30.0, 0.0, 10.0, 0.0),
                    child: new Text("发现新版本"))
              ],
            ),
            content: new Text(
                serviceVersionCode
            ),
            actions: <Widget>[
              new FlatButton(
                child: new Text('下次再说'),
                onPressed: () {
                  Navigator.of(context).pop();              
                },
              ),
              new FlatButton(
                child: new Text('立即更新'),
                onPressed: () {
                  _doUpdate(context);           
                },
              )
            ],
          );
        });

  }


 /// 执行更新操作
  _doUpdate(BuildContext context) async {
    Navigator.pop(context);
     _executeDownload(context);
  }
  
  /// 下载最新apk包
  Future<void> _executeDownload(BuildContext context) async {
       pr = new ProgressDialog(
        context,
        type: ProgressDialogType.Download, 
        isDismissible: true, 
        showLogs: true,
      );
      pr.style(message: '准备下载...');
      if (!pr.isShowing()) {
        pr.show();
      }

      final path = await _apkLocalPath;
      await FlutterDownloader.enqueue(
        url: 'http://update.rwworks.com:8088/appManager/monitor/app/appload/' + appId + '',
        savedDir: path,
        fileName: apkName,
        showNotification: true,
        openFileFromNotification: true
      );
    }
    
   /// 下载进度回调函数
    static void _downLoadCallback(String id, DownloadTaskStatus status, int progress) {
      final SendPort send = IsolateNameServer.lookupPortByName('downloader_send_port');
      send.send([id, status, progress]);
    }
    
    /// 更新下载进度框
    _updateDownLoadInfo(dynamic data) {
      DownloadTaskStatus status = data[1];
      int progress = data[2];
      if (status == DownloadTaskStatus.running) {
        pr.update(progress: double.parse(progress.toString()), message: "下载中,请稍后…");
      }
      if (status == DownloadTaskStatus.failed) {
        if (pr.isShowing()) {
          pr.hide();
        }
      }

      if (status == DownloadTaskStatus.complete) {
        if (pr.isShowing()) {
          pr.hide();
        }
        _installApk();
      }
    }
   
    /// 安装apk
    Future<Null> _installApk() async {
       await OpenFile.open(appPath + '/' + apkName);
    }
    
    /// 获取apk存储位置
    Future<String> get _apkLocalPath async {
      final directory = await getExternalStorageDirectory();
      String path = directory.path  + Platform.pathSeparator + 'Download';;
      final savedDir = Directory(path);
      bool hasExisted = await savedDir.exists();
      if (!hasExisted) {
        await savedDir.create();
      }
      this.setState((){
          appPath = path;
      });
      return path;
    }

效果图

在这里插入图片描述

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐