阅读 2688

Flutter下载更新 App

最后更新时间 2020-05-12

1. 说明

  1. iOS 和Android 更新是完全不一样的。

  2. iOS 只能跳转到 AppStore,比较好实现

  3. Android则需要下载apk包,由于Android机型较多,这里我们用 dart 连接第三方的原生 Android 下载库。

  4. 更新界面和下载更新分开处理的。

  5. iOS 没得下载进度这一说,Android 则有。

screen

2. 代码

2.1 iOS 直接采用url_launcher打开 AppStore就可以了

if (Platform.isIOS) {
  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.1 Android实现

2.1.1 在 android/app/build.gradle 文件添加下载库
dependencies {
    // 只复制这一行
    implementation 'com.king.app:app-updater:1.0.8-androidx'
}
复制代码
2.1.2 在 AndroidManifest.xml添加存储权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
复制代码
2.1.3 在 Android 项目中编写插件(UpdateVersionPlugin.java)
package com.iwubida.wbd_b2b_app.plugins;

import android.content.Context;
import android.util.Log;

import com.king.app.updater.AppUpdater;
import com.king.app.updater.callback.UpdateCallback;


import java.io.File;
import java.util.HashMap;
import java.util.Map;

import io.flutter.embedding.engine.plugins.FlutterPlugin;
import io.flutter.plugin.common.BinaryMessenger;
import io.flutter.plugin.common.EventChannel;

import io.flutter.plugin.common.MethodChannel;
import io.flutter.plugin.common.PluginRegistry.Registrar;
import io.flutter.plugins.packageinfo.PackageInfoPlugin;
import io.flutter.plugins.pathprovider.PathProviderPlugin;

/** UpdateVersionPlugin */
@SuppressWarnings("unchecked")
public class UpdateVersionPlugin implements EventChannel.StreamHandler, FlutterPlugin {

  private static String TAG = "MY_UPDATE";

  private static String channelName = "plugins.iwubida.com/update_version";

  private Context context;

  private EventChannel eventChannel;

  private AppUpdater update;


    /** Plugin registration. */
    public static void registerWith(Registrar registrar) {
      final UpdateVersionPlugin instance = new UpdateVersionPlugin();
      instance.onAttachedToEngine(registrar.context(), registrar.messenger());
    }

    @Override
    public void onAttachedToEngine(FlutterPlugin.FlutterPluginBinding binding) {
        onAttachedToEngine(binding.getApplicationContext(), binding.getBinaryMessenger());
    }

    private void onAttachedToEngine(Context applicationContext, BinaryMessenger messenger) {
        this.context = applicationContext;
        this.eventChannel = new EventChannel(messenger, channelName);
        this.eventChannel.setStreamHandler(this);
    }

    @Override
    public void onDetachedFromEngine(FlutterPlugin.FlutterPluginBinding binding) {
        context = null;
        eventChannel.setStreamHandler(null);
        eventChannel = null;
    }



    @Override
    public void onListen(Object o, EventChannel.EventSink eventSink) {
        if (o.toString().length() < 5) {
            eventSink.error(TAG, "URL错误", o);
            return;
        }
        if (!o.toString().startsWith("http")){
            eventSink.error(TAG, "URL错误", o);
            return;
        }

        update = new AppUpdater(context,o.toString()).setUpdateCallback(new UpdateCallback() {

        Map data = new HashMap<String, Object>();

        // 发送数据到 Flutter
        private  void sendData() {
            eventSink.success(data);
        }

        @Override
        public void onDownloading(boolean isDownloading) {

        }

        @Override
        public void onStart(String url) {
            data.put("start", true);
            data.put("cancel", false);
            data.put("done",false );
            data.put("error", false);
            data.put("percent", 1);
            sendData();
        }

        @Override
        public void onProgress(long progress, long total, boolean isChange) {
            int percent = (int)(progress * 1.0 / total * 100);
            if (isChange && percent > 0) {
                data.put("percent", percent);
                sendData();
            }
        }


        @Override
        public void onFinish(File file) {
            data.put("done", true);
            sendData();
        }

        @Override
        public void onError(Exception e) {
            data.put("error", e.toString());
            sendData();
        }

        @Override
        public void onCancel() {
            data.put("cancel", true);
            sendData();
        }
    });
    update.start();
  }

  @Override
  public void onCancel(Object o) {
    Log.i(TAG, "取消下载");
    if(update != null) {
        update.stop();
    }
  }
}


复制代码
2.1.4 在 MainActivity 中注册插件
//  注册更新组件 在onCreate方法中(旧版本)
//  *不建议
UpdateVersionPlugin.registerWith(registrarFor("iwubida.com/update_version"));
复制代码
//  新版本(Flutter v1.12.13)(在 Flutter V1.17.0中有问题)
// *不建议
@Override
  public void configureFlutterEngine(@NonNull FlutterEngine flutterEngine) {
    // 注册更新组件
    ShimPluginRegistry shimPluginRegistry = new ShimPluginRegistry(flutterEngine);
    UpdateVersionPlugin.registerWith(shimPluginRegistry.registrarFor("iwubida.com/update_version"));
  }
复制代码
// kotlin写法 - Flutter V1.17.0 - 最新
// 由于改了插件代码,上面两种可能有问题。建议用这种。
// 下面是 MainActivity.kt 代码。java 版本的自行写。
 override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
    GeneratedPluginRegistrant.registerWith(flutterEngine)
    // 注册更新组件
    flutterEngine.plugins.add(UpdateVersionPlugin())
}
复制代码

我们需要获取到下载进度,所以我们采用EventChannel来持续单向通讯。

2.3 dart端实现

static const channelName = 'plugins.iwubida.com/update_version';
  static const stream = const EventChannel(channelName);
  // 进度订阅
  StreamSubscription downloadSubscription;
  int percent = 0;
  
   // 开始下载
  void _startDownload() {
    if (downloadSubscription == null) {
      downloadSubscription = stream
          .receiveBroadcastStream(widget.data.apkUrl)
          .listen(_updateDownload);
    }
  }

  // 停止监听进度
  void _stopDownload() {
    if (downloadSubscription != null) {
      downloadSubscription.cancel();
      downloadSubscription = null;
      percent = 0;
    }
  }

  // 进度下载
  void _updateDownload(data) {
    int progress = data["percent"];
    if (progress != null) {
      setState(() {
        percent = progress;
      });
    }
  }
  
复制代码

2.4 其它

另外 Android 还有权限申请的问题。可以参考下面项目中的代码。

项目源码

位置: lib/widget/update_version.dart