Flutter自动更新插件flutter_autoupdate的使用

Flutter自动更新插件flutter_autoupdate的使用

本库允许你在Android、iOS和Windows的Flutter应用中轻松添加自动更新功能。

我们的使用场景是在iOS上启动App Store,在Android或Windows上安装/执行APK或Windows可执行文件;因此,此包应运而生。要使用此包,你应该遵循以下版本模板格式。

特性

  • 从iTunes App Store/远程URL获取更新
  • 启动App Store
  • 带有进度的下载功能
  • 下载时进行SHA512哈希校验

安装

在你的Android应用中添加以下内容:

AndroidManifest.xml

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

android/app/src/main/res/xml/file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths>
    <external-path path="Android/data/<package name>/" name="files_root" />
    <external-path path="." name="external_storage_root" />
</paths>

<package name>替换为你的应用包名。

示例

import 'dart:async';
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:flutter_autoupdate/flutter_autoupdate.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:version/version.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  @override
  void initState() {
    super.initState();
    initPlatformState();
  }

  UpdateResult? _result;
  DownloadProgress? _download;
  var _startTime = DateTime.now().millisecondsSinceEpoch;
  var _bytesPerSec = 0;

  // 平台消息是异步的,所以我们通过异步方法初始化。
  Future<void> initPlatformState() async {
    UpdateResult? result;

    // 如果小部件在异步平台消息传输期间被树移除,则我们希望丢弃回复而不是调用setState来更新我们的不存在的外观。
    if (!mounted) return;

    if (Platform.isAndroid || Platform.isIOS) {
      var status = await Permission.storage.status;
      if (status.isDenied) {
        await Permission.storage.request();
      }
    }

    String versionUrl;
    if (Platform.isAndroid) {
      versionUrl =
          'https://storage.googleapis.com/download-dev.feedmepos.com/version_android_sample.json';
    } else if (Platform.isWindows) {
      versionUrl =
          'https://storage.googleapis.com/download-dev.feedmepos.com/version_windows_sample.json';
    }

    /// Android/Windows
    var manager = UpdateManager(versionUrl: versionUrl);
    /// iOS
    // var manager = UpdateManager(appId: 1500009417, countryCode: 'my');
    try {
      result = await manager.fetchUpdates();
      setState(() {
        _result = result;
      });
      if (Version.parse('1.0.0') < result?.latestVersion) {
        var controller = await result?.initializeUpdate();
        controller?.stream.listen((event) async {
          setState(() {
            if (DateTime.now().millisecondsSinceEpoch - _startTime >= 1000) {
              _startTime = DateTime.now().millisecondsSinceEpoch;
              _bytesPerSec = event.receivedBytes - _bytesPerSec;
            }
            _download = event;
          });
          if (event.completed) {
            print("Downloaded completed");
            await controller.close();
            await result?.runUpdate(event.path, autoExit: true);
          }
        });
      }
    } on Exception catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('插件示例应用'),
        ),
        body: Center(
          child: _download != null
              ? Text('最新版本: ${_result!.latestVersion}\n'
                  'Url: ${_result!.downloadUrl}\n'
                  '发布说明: ${_result!.releaseNotes}\n'
                  '发布时间: ${_result!.releaseDate}\n\n'
                  '文件: ${_download!.toPrettyMB(_download!.receivedBytes)}/'
                  '${_download!.toPrettyMB(_download!.totalBytes)} '
                  '(${_download!.progress.toInt()}%)\n'
                  '速度: ${_download!.toPrettyMB(_bytesPerSec)}/s\n'
                  '目标路径: ${_download!.path}')
              : null,
        ),
      ),
    );
  }
}

更多详细信息,请参阅示例

版本模板

默认情况下,它会选取第一个索引作为最新版本。更多信息,可以参考sample.json。

Android

[
  {
    "version": "3.0.0",
    "url": "https://storage.googleapis.com/download-dev.feedmepos.com/android/feedme-pos-3.0.0-beta.5.apk",
    "releaseNotes": "新更新 3.0.0!",
    "releaseDate": "2021-07-28T11:58:25Z",
    "sha512": "2e0349c1e729eac0f4cb9f831fa1130241743c8db9e115013091dbeec6b8b86dc18c62bcbfab516033869c8ec8c8967615c622303d4bee62640c3b507051aca2"
  },
  {
    "version": "1.4.5",
    "url": "https://storage.googleapis.com/download-dev.feedmepos.com/android/feedme-pos-1.4.5.apk",
    "releaseNotes": "新更新 1.4.5!",
    "releaseDate": "2021-06-16T11:58:25Z",
    "sha512": "f028475ad562f9f9566213774b02e0bd3ac2198899222687613937791307ec5d326cfb79ee2882273bffd6777f9c76371d6b2eed7d46e326fd687bc95e2edb2a"
  }
]

Windows

[
  {
    "version": "2.5.0",
    "url": "https://storage.googleapis.com/download-dev.feedmepos.com/feedme_sample.exe",
    "releaseNotes": "新更新 2.5.0!",
    "releaseDate": "2021-07-28T11:58:25Z",
    "sha512": "53d4cc95ad07470b53b3f9bc010ab8c6776f2bc2f9f3115b0807ecebcc34175f530d02e549c260112ad08c2c86a8b92d7e7f11308df0406422be8ceea76a9190"
  }
]

更多关于Flutter自动更新插件flutter_autoupdate的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter自动更新插件flutter_autoupdate的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,关于如何在Flutter项目中使用flutter_autoupdate插件来实现应用的自动更新功能,以下是一个简要的代码示例和配置步骤。请注意,实际使用中可能需要根据具体需求进行调整。

1. 添加依赖

首先,在你的pubspec.yaml文件中添加flutter_autoupdate依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_autoupdate: ^x.y.z  # 替换为最新版本号

然后运行flutter pub get来安装依赖。

2. 配置权限(Android)

如果你的应用需要访问网络来检查更新,你需要在AndroidManifest.xml中添加必要的权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.INTERNET"/>

    <!-- 其他配置 -->

</manifest>

3. 初始化并使用插件

在你的Flutter项目的main.dart或其他合适的文件中,初始化并使用flutter_autoupdate插件。以下是一个基本的示例:

import 'package:flutter/material.dart';
import 'package:flutter_autoupdate/flutter_autoupdate.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Auto Update Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  @override
  void initState() {
    super.initState();
    _checkForUpdates();
  }

  Future<void> _checkForUpdates() async {
    try {
      // 假设你的更新信息是从一个JSON API获取的
      String updateUrl = "https://yourserver.com/update.json";
      
      // 使用dio或其他HTTP客户端来获取更新信息
      var response = await Dio().get(updateUrl);
      var updateData = response.data;

      // 解析更新信息(这里假设更新信息包含版本号和新APK的下载URL)
      String latestVersion = updateData['latest_version'];
      String apkUrl = updateData['apk_url'];

      // 获取当前应用的版本号
      PackageInfo packageInfo = await PackageInfo.fromPlatform();
      String currentVersion = packageInfo.version;

      // 比较版本号,如果需要更新则显示更新对话框
      if (compareVersion(currentVersion, latestVersion) < 0) {
        showUpdateDialog(context, latestVersion, apkUrl);
      }
    } catch (e) {
      print("Error checking for updates: $e");
    }
  }

  // 比较版本号的辅助函数
  int compareVersion(String current, String latest) {
    List<String> currentParts = current.split('.');
    List<String> latestParts = latest.split('.');
    int length = math.max(currentParts.length, latestParts.length);

    for (int i = 0; i < length; i++) {
      int currentPart = i < currentParts.length ? int.parse(currentParts[i]) : 0;
      int latestPart = i < latestParts.length ? int.parse(latestParts[i]) : 0;

      if (currentPart > latestPart) return 1;
      if (currentPart < latestPart) return -1;
    }
    return 0;
  }

  // 显示更新对话框
  Future<void> showUpdateDialog(BuildContext context, String latestVersion, String apkUrl) async {
    return showDialog<void>(
      context: context,
      barrierDismissible: false, // 用户不能通过点击背景关闭对话框
      builder: (BuildContext context) {
        return AlertDialog(
          title: Text('New Update Available'),
          content: SingleChildScrollView(
            child: ListBody(
              children: <Widget>[
                Text('A new version of the app is available: $latestVersion'),
                SizedBox(height: 16.0),
                ElevatedButton(
                  onPressed: () async {
                    // 下载并安装APK(这里需要额外的处理,因为Flutter本身不支持直接安装APK)
                    // 可以引导用户到浏览器下载或者使用其他插件如`open_file`来处理APK安装
                    await launchUrl(Uri.parse(apkUrl));
                    Navigator.of(context).pop();
                  },
                  child: Text('Download Update'),
                ),
              ],
            ),
          ),
        );
      },
    );
  }
}

注意

  1. 上述代码中的Dio是一个HTTP客户端库,用于从服务器获取更新信息。你需要先添加dio依赖:dio: ^x.y.z
  2. 由于Flutter本身不支持直接安装APK,所以下载后需要引导用户手动安装。你可以使用url_launcher插件来打开下载链接。
  3. 版本号比较的逻辑可能需要根据实际情况进行调整。
  4. 在生产环境中,请确保更新信息的来源是安全可靠的,以避免潜在的安全风险。

这个示例提供了一个基本的框架,你可以根据自己的需求进一步扩展和完善。

回到顶部