Flutter 新闻客户端 - 11 APP升级、android动态授权_第1张图片

B站视频

https://www.bilibili.com/vide...
https://www.bilibili.com/vide...

本节目标

  • app 升级策略
  • android 动态授权
  • android 设备目录
  • ios 支持 swift 语言
  • 快速提示框

正文

ios 支持 swift 语言

  • 出发点

社区第三方包都在用 swift 开发,打包的时候需要加入 swift 语言包。

  • 操作

创建一个支持 swift 的新项目,然后把 lib assets pubspec.yaml 覆盖即可。

app 升级策略

Flutter 新闻客户端 - 11 APP升级、android动态授权_第2张图片

代码实现

定义接口

  • post /app/update

Flutter 新闻客户端 - 11 APP升级、android动态授权_第3张图片

加入依赖包

  • pubspec.yaml
dependencies:  # 设备信息  device_info: ^0.4.2+3  # 包信息  package_info: ^0.4.0+18  # 路径查询  path_provider: ^1.6.8  # permission 权限  permission_handler: ^5.0.0+hotfix.6  # 安装  install_plugin: ^2.0.1  # 对话框  easy_dialog: ^1.0.5

升级工具类

  • lib/common/utils/update.dart
import 'dart:io';import 'package:dio/dio.dart';import 'package:easy_dialog/easy_dialog.dart';import 'package:flutter/material.dart';import 'package:flutter_ducafecat_news/common/apis/app.dart';import 'package:flutter_ducafecat_news/common/entitys/entitys.dart';import 'package:flutter_ducafecat_news/common/widgets/toast.dart';import 'package:flutter_ducafecat_news/global.dart';import 'package:install_plugin/install_plugin.dart';import 'package:path_provider/path_provider.dart';/// app 升级class AppUpdateUtil {  static AppUpdateUtil _instance = AppUpdateUtil._internal();  factory AppUpdateUtil() => _instance;  BuildContext _context;  AppUpdateResponseEntity _appUpdateInfo;  AppUpdateUtil._internal();  /// 获取更新信息  Future run(BuildContext context) async {    _context = context;    // 提交 设备类型、发行渠道、架构、机型    AppUpdateRequestEntity requestDeviceInfo = AppUpdateRequestEntity(      device: Global.isIOS == true ? "ios" : "android",      channel: Global.channel,      architecture: Global.isIOS == true          ? Global.iosDeviceInfo.utsname.machine          : Global.androidDeviceInfo.device,      model: Global.isIOS == true          ? Global.iosDeviceInfo.name          : Global.androidDeviceInfo.brand,    );    _appUpdateInfo =        await AppApi.update(context: context, params: requestDeviceInfo);    _runAppUpdate();  }  /// 检查是否有新版  Future _runAppUpdate() async {    // 比较版本    final isNewVersion =        (_appUpdateInfo.latestVersion.compareTo(Global.packageInfo.version) ==            1);    // 安装    if (isNewVersion == true) {      _appUpdateConformDialog(() {        Navigator.of(_context).pop();        if (Global.isIOS == true) {          // 去苹果店          InstallPlugin.gotoAppStore(_appUpdateInfo.shopUrl);        } else {          // apk 下载安装          toastInfo(msg: "开始下载升级包");          _downloadAPKAndSetup(_appUpdateInfo.fileUrl);        }      });    }  }  /// 下载文件 & 安装  Future _downloadAPKAndSetup(String fileUrl) async {    // 下载    Directory externalDir = await getExternalStorageDirectory();    String fullPath = externalDir.path + "/release.apk";    Dio dio = Dio(BaseOptions(        responseType: ResponseType.bytes,        followRedirects: false,        validateStatus: (status) {          return status < 500;        }));    Response response = await dio.get(      fileUrl,    );    File file = File(fullPath);    var raf = file.openSync(mode: FileMode.write);    raf.writeFromSync(response.data);    await raf.close();    // 安装    await InstallPlugin.installApk(fullPath, Global.packageInfo.packageName);  }  /// 升级确认对话框  void _appUpdateConformDialog(VoidCallback onPressed) {    EasyDialog(        title: Text(          "发现新版本 ${_appUpdateInfo.latestVersion}",          style: TextStyle(fontWeight: FontWeight.bold),          textScaleFactor: 1.2,        ),        description: Text(          _appUpdateInfo.latestDescription,          textScaleFactor: 1.1,          textAlign: TextAlign.center,        ),        height: 220,        contentList: [          Row(            mainAxisAlignment: MainAxisAlignment.end,            children: [              new FlatButton(                padding: const EdgeInsets.only(top: 8.0),                textColor: Colors.lightBlue,                onPressed: onPressed,                child: new Text(                  "同意",                  textScaleFactor: 1.2,                ),              ),              new FlatButton(                padding: const EdgeInsets.only(top: 8.0),                textColor: Colors.lightBlue,                onPressed: () {                  Navigator.of(_context).pop();                },                child: new Text(                  "取消",                  textScaleFactor: 1.2,                ),              ),            ],          )        ]).show(_context);  }}

读取设备信息

  • 插件

https://pub.flutter-io.cn/pac...

  • 全局信息

lib/global.dart

  /// 是否 ios  static bool isIOS = Platform.isIOS;  /// android 设备信息  static AndroidDeviceInfo androidDeviceInfo;  /// ios 设备信息  static IosDeviceInfo iosDeviceInfo;  /// 包信息  static PackageInfo packageInfo;  /// init  static Future init() async {    ...    // 读取设备信息    DeviceInfoPlugin deviceInfoPlugin = DeviceInfoPlugin();    if (Global.isIOS) {      Global.iosDeviceInfo = await deviceInfoPlugin.iosInfo;    } else {      Global.androidDeviceInfo = await deviceInfoPlugin.androidInfo;    }    // 包信息    Global.packageInfo = await PackageInfo.fromPlatform();    ...
  • 定义升级信息 entity

lib/common/entitys/app.dart

class AppUpdateRequestEntity {  String device;  String channel;  String architecture;  String model;  AppUpdateRequestEntity({    this.device,    this.channel,    this.architecture,    this.model,  });  factory AppUpdateRequestEntity.fromJson(Map json) =>      AppUpdateRequestEntity(        device: json["device"],        channel: json["channel"],        architecture: json["architecture"],        model: json["model"],      );  Map toJson() => {        "device": device,        "channel": channel,        "architecture": architecture,        "model": model,      };}class AppUpdateResponseEntity {  String shopUrl;  String fileUrl;  String latestVersion;  String latestDescription;  AppUpdateResponseEntity({    this.shopUrl,    this.fileUrl,    this.latestVersion,    this.latestDescription,  });  factory AppUpdateResponseEntity.fromJson(Map json) =>      AppUpdateResponseEntity(        shopUrl: json["shopUrl"],        fileUrl: json["fileUrl"],        latestVersion: json["latestVersion"],        latestDescription: json["latestDescription"],      );  Map toJson() => {        "shopUrl": shopUrl,        "fileUrl": fileUrl,        "latestVersion": latestVersion,        "latestDescription": latestDescription,      };}
  • api 请求

lib/common/apis/app.dart

/// 系统相关class AppApi {  /// 获取最新版本信息  static Future update({    @required BuildContext context,    AppUpdateRequestEntity params,  }) async {    var response = await HttpUtil().post(      '/app/update',      context: context,      params: params,    );    return AppUpdateResponseEntity.fromJson(response);  }}
  • 提交信息 获取版本

lib/common/utils/update.dart

  /// 获取更新信息  Future run(BuildContext context) async {    _context = context;    // 提交 设备类型、发行渠道、架构、机型    AppUpdateRequestEntity requestDeviceInfo = AppUpdateRequestEntity(      device: Global.isIOS == true ? "ios" : "android",      channel: Global.channel,      architecture: Global.isIOS == true          ? Global.iosDeviceInfo.utsname.machine          : Global.androidDeviceInfo.device,      model: Global.isIOS == true          ? Global.iosDeviceInfo.name          : Global.androidDeviceInfo.brand,    );    _appUpdateInfo =        await AppApi.update(context: context, params: requestDeviceInfo);    _runAppUpdate();  }  /// 检查是否有新版  Future _runAppUpdate() async {    // 比较版本    final isNewVersion =        (_appUpdateInfo.latestVersion.compareTo(Global.packageInfo.version) ==            1);    // 安装    if (isNewVersion == true) {      _appUpdateConformDialog(() {        Navigator.of(_context).pop();        if (Global.isIOS == true) {          // 去苹果店          InstallPlugin.gotoAppStore(_appUpdateInfo.shopUrl);        } else {          // apk 下载安装          toastInfo(msg: "开始下载升级包");          _downloadAPKAndSetup(_appUpdateInfo.fileUrl);        }      });    }  }

android 动态授权

  • 插件

https://pub.flutter-io.cn/pac...

  • 官方文章

https://developer.android.com...

https://developer.android.com...

  • AndroidManifest.xml 中加入权限

android/app/src/main/AndroidManifest.xml

            
  • flutter 启动页中执行授权

lib/pages/index/index.dart

在 initState 是执行

延迟 3 秒,用户体验好些

class _IndexPageState extends State {  @override  void initState() {    super.initState();    if (Global.isRelease == true) {      doAppUpdate();    }  }  Future doAppUpdate() async {    await Future.delayed(Duration(seconds: 3), () async {      if (Global.isIOS == false &&          await Permission.storage.isGranted == false) {        await [Permission.storage].request();      }      if (await Permission.storage.isGranted) {        AppUpdateUtil().run(context);      }    });  }

android 目录权限

  • 插件

https://pub.flutter-io.cn/pac...
https://pub.flutter-io.cn/pac...

  • 文章

https://developer.android.com...

  • lib/common/utils/update.dart
  /// 下载文件 & 安装  Future _downloadAPKAndSetup(String fileUrl) async {    // 下载    Directory externalDir = await getExternalStorageDirectory();    String fullPath = externalDir.path + "/release.apk";    Dio dio = Dio(BaseOptions(        responseType: ResponseType.bytes,        followRedirects: false,        validateStatus: (status) {          return status < 500;        }));    Response response = await dio.get(      fileUrl,    );    File file = File(fullPath);    var raf = file.openSync(mode: FileMode.write);    raf.writeFromSync(response.data);    await raf.close();    // 安装    await InstallPlugin.installApk(fullPath, Global.packageInfo.packageName);  }

EasyDialog 快速提示框

  • 插件

https://pub.flutter-io.cn/pac...

  • lib/common/utils/update.dart
  /// 升级确认对话框  void _appUpdateConformDialog(VoidCallback onPressed) {    EasyDialog(        title: Text(          "发现新版本 ${_appUpdateInfo.latestVersion}",          style: TextStyle(fontWeight: FontWeight.bold),          textScaleFactor: 1.2,        ),        description: Text(          _appUpdateInfo.latestDescription,          textScaleFactor: 1.1,          textAlign: TextAlign.center,        ),        height: 220,        contentList: [          Row(            mainAxisAlignment: MainAxisAlignment.end,            children: [              new FlatButton(                padding: const EdgeInsets.only(top: 8.0),                textColor: Colors.lightBlue,                onPressed: onPressed,                child: new Text(                  "同意",                  textScaleFactor: 1.2,                ),              ),              new FlatButton(                padding: const EdgeInsets.only(top: 8.0),                textColor: Colors.lightBlue,                onPressed: () {                  Navigator.of(_context).pop();                },                child: new Text(                  "取消",                  textScaleFactor: 1.2,                ),              ),            ],          )        ]).show(_context);  }

资源

视频

  • b 站
  • 油管镜像

蓝湖设计稿(加微信给授权 ducafecat)

https://lanhuapp.com/url/lYuz1
密码: gSKl

蓝湖现在收费了,所以查看标记还请自己上传 xd 设计稿
商业设计稿文件不好直接分享, 可以加微信联系 ducafecat

YAPI 接口管理

http://yapi.demo.qunar.com/

代码

https://github.com/ducafecat/...

参考

  • 文章

https://developer.android.com...
https://developer.android.com...
https://developer.android.com...

  • flutter 插件

https://pub.flutter-io.cn/pac...
https://pub.flutter-io.cn/pac...
https://pub.flutter-io.cn/pac...
https://pub.flutter-io.cn/pac...
https://pub.flutter-io.cn/pac...

VSCode 插件

  • Flutter、Dart
  • Flutter Widget Snippets
  • Awesome Flutter Snippets
  • Paste JSON as Code
  • bloc
  • Code Spell Checker

更多相关文章

  1. Android Log日志信息获取
  2. adb 发送文件到Android设备和从Android手机复制文件
  3. Android获得所有存储设备位置最佳方式
  4. android输入子设备类型
  5. 【Android Developers Training】 10. 序言:支持不同设备
  6. Android系统移植与调试之------->如何修改Android设备添加重启、
  7. 获取Android设备的唯一识别码|设备号|序号|UUID

随机推荐

  1. Android异步消息处理机制深度解析
  2. 【Android】实现XML解析的几种技术
  3. Support v4 v7 v13: Android Support Libr
  4. (转)Android中LocationManager的简单使用,
  5. 如何开发Android Maps
  6. 【移动安全高级篇】————7、APK 的自
  7. Android 开源绘画板项目 (ScaleSketchPadD
  8. Handler Looper源码解析(Android消息传递
  9. 更简单的学习Android事件分发
  10. Android的TextView/EditText使用Characte