浏览代码

增加版本更新功能

maqiang 1 年之前
父节点
当前提交
0db3606d2f

+ 3 - 0
android/app/src/main/AndroidManifest.xml

@@ -1,5 +1,8 @@
1 1
 <manifest xmlns:android="http://schemas.android.com/apk/res/android">
2 2
     <uses-permission android:name="android.permission.INTERNET"/>
3
+    <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
4
+    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
5
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
3 6
     <application
4 7
         android:label="粮食质量管理"
5 8
         android:name="${applicationName}"

+ 5 - 6
lib/base/base_lifecycle_state.dart

@@ -56,7 +56,6 @@ abstract class BaseLifecycleState<T extends StatefulWidget> extends BaseState<T>
56 56
   /// app在后台
57 57
   bool appPause = false;
58 58
 
59
-
60 59
   /// ************** 生命周期 **************
61 60
   /// 初始化
62 61
   void onInit() {}
@@ -87,7 +86,9 @@ abstract class BaseLifecycleState<T extends StatefulWidget> extends BaseState<T>
87 86
     // 绘制完成监听
88 87
     ServicesBinding.instance.addPostFrameCallback((timeStamp) {
89 88
       // 路由监听
90
-      MyNavigator.routeObs.subscribe(this, ModalRoute.of(context)!);
89
+      if (ModalRoute.of(context) != null) {
90
+        MyNavigator.routeObs.subscribe(this, ModalRoute.of(context)!);
91
+      }
91 92
       debugPrint('onFistShow: $runtimeType');
92 93
       onFirstShow(timeStamp);
93 94
       isShow = true;
@@ -141,7 +142,6 @@ abstract class BaseLifecycleState<T extends StatefulWidget> extends BaseState<T>
141 142
     super.didPushNext();
142 143
   }
143 144
 
144
-
145 145
   /// 已回到当前页(注意:不建议直接使用此函数),可使用 [onShow] 方法
146 146
   @override
147 147
   void didPopNext() {
@@ -150,14 +150,13 @@ abstract class BaseLifecycleState<T extends StatefulWidget> extends BaseState<T>
150 150
     onShow();
151 151
   }
152 152
 
153
-
154 153
   /// App 生命周期监听(注意:不建议直接使用此函数)
155 154
   ///
156 155
   /// 请使用 onAppBackstage/onAppBackToFront 等生命周期 API
157 156
   @override
158 157
   void didChangeAppLifecycleState(AppLifecycleState state) {
159 158
     switch (state) {
160
-      case AppLifecycleState.resumed:   // App 可见
159
+      case AppLifecycleState.resumed: // App 可见
161 160
         if (appPause) {
162 161
           debugPrint('onAppBackToFront: $runtimeType');
163 162
           appPause = false;
@@ -168,7 +167,7 @@ abstract class BaseLifecycleState<T extends StatefulWidget> extends BaseState<T>
168 167
         break;
169 168
       case AppLifecycleState.hidden: // App 不可见
170 169
         break;
171
-      case AppLifecycleState.paused:   // App 在后台暂停运行
170
+      case AppLifecycleState.paused: // App 在后台暂停运行
172 171
         debugPrint('onBackstage: $runtimeType');
173 172
         appPause = true;
174 173
         onAppBackstage();

+ 4 - 0
lib/network/api.dart

@@ -32,4 +32,8 @@ abstract class Api {
32 32
   /// 扦样任务单列表
33 33
   @GET('/admin-api/zj/code-sampling-task-details-sgjc/sampling_task_detail_page')
34 34
   Future<ApiRsp<SampleTaskListRsp>> sampleTaskList(@Queries() Map<String, dynamic> map);
35
+
36
+  /// 获取app下载地址
37
+  @GET('/admin-api/zj/base-file/getAppDownLoadUrl')
38
+  Future<ApiRsp<String?>> getAppDownloadUrl();
35 39
 }

+ 4 - 3
lib/network/base_dio.dart

@@ -1,6 +1,6 @@
1 1
 import 'package:connectivity_plus/connectivity_plus.dart';
2 2
 import 'package:dio/dio.dart';
3
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
3
+import 'package:lszlgl/base/base_lifecycle_state.dart';
4 4
 import 'package:lszlgl/main.dart';
5 5
 import 'package:lszlgl/service/user_service.dart';
6 6
 
@@ -131,12 +131,13 @@ class MyInterceptor extends Interceptor {
131 131
     // 响应
132 132
     Map<String, Object?> map = {};
133 133
     map['url'] = err.requestOptions.uri;
134
+    map['errorType'] = err.type;
134 135
     map['statusCode'] = err.response?.statusCode;
135 136
     map['data'] = err.response?.data;
136 137
     map['msg'] = appException.message;
137 138
     if (err.response?.extra.isNotEmpty ?? false) map['extra'] = err.response?.extra;
138 139
     logger.e('ApiError: $map');
139
-    SmartDialog.showToast(appException.message ?? '');
140
+    MyNavigator.showToast(appException.message ?? '');
140 141
     handler.next(err);
141 142
   }
142 143
 }
@@ -167,7 +168,7 @@ class AppException implements Exception {
167 168
       case DioExceptionType.badCertificate:
168 169
         return AppException(-1, '证书错误');
169 170
       case DioExceptionType.badResponse:
170
-        var code = error.response?.data['code'];
171
+        var code = error.response?.data['code'] ?? -1;
171 172
         if (code == 401) UserService.get().logout();
172 173
         return AppException(code, error.response?.data['msg'] ?? '');
173 174
       case DioExceptionType.cancel:

+ 8 - 9
lib/page/login/login_page.dart

@@ -1,16 +1,14 @@
1 1
 import 'package:dio/dio.dart';
2 2
 import 'package:flutter/material.dart';
3 3
 import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
4
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
5 4
 import 'package:lszlgl/base/base_lifecycle_state.dart';
6 5
 import 'package:lszlgl/model/req/login_req.dart';
7 6
 import 'package:lszlgl/network/api.dart';
8 7
 import 'package:lszlgl/service/dict_service.dart';
8
+import 'package:lszlgl/service/upgrade_service.dart';
9 9
 import 'package:lszlgl/service/user_service.dart';
10 10
 import 'package:lszlgl/widget/button.dart';
11 11
 
12
-import '../../base/base_state.dart';
13
-
14 12
 /// 登录页面
15 13
 class LoginPage extends StatefulWidget {
16 14
   const LoginPage({Key? key}) : super(key: key);
@@ -27,23 +25,23 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
27 25
     var account = accountCtrl.text;
28 26
     var pwd = pwdCtrl.text;
29 27
     if (account.isEmpty || pwd.isEmpty) {
30
-      SmartDialog.showToast('请输入账号和密码');
28
+      MyNavigator.showToast('请输入账号和密码');
31 29
       return;
32 30
     }
33
-    SmartDialog.showLoading(clickMaskDismiss: false, backDismiss: false);
31
+    MyNavigator.showLoading(clickDismiss: false);
34 32
     try {
35 33
       // 登录
36 34
       var login = await Api().login(LoginReq(username: account, password: pwd));
37 35
       await UserService.get().saveLogin(login.data);
38 36
       getSystemData();
39 37
     } on DioException catch (_) {
40
-      SmartDialog.dismiss(status: SmartStatus.loading);
38
+      MyNavigator.dismissLoading();
41 39
     }
42 40
   }
43 41
 
44 42
   /// 获取基础数据
45 43
   void getSystemData() async {
46
-    SmartDialog.showLoading(clickMaskDismiss: false, backDismiss: false);
44
+    MyNavigator.showLoading(clickDismiss: false);
47 45
     try {
48 46
       // 获取用户数据
49 47
       var user = await Api().userProfile();
@@ -51,12 +49,13 @@ class _LoginPageState extends BaseLifecycleState<LoginPage> {
51 49
       // 获取字典
52 50
       var dictData = await Api().getAllDict();
53 51
       DictService.saveDictList(dictData.data);
52
+      UpgradeService.checkUpgrade(false);
54 53
       // 进入主页
55
-      SmartDialog.dismiss(status: SmartStatus.loading);
54
+      MyNavigator.dismissLoading();
56 55
       startHome();
57 56
     } on DioException catch (_) {
58 57
       // 获取数据失败
59
-      SmartDialog.dismiss(status: SmartStatus.loading);
58
+      MyNavigator.dismissLoading();
60 59
     }
61 60
   }
62 61
 

+ 1 - 2
lib/page/main_tab_page.dart

@@ -1,7 +1,6 @@
1 1
 import 'dart:async';
2 2
 
3 3
 import 'package:flutter/material.dart';
4
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
5 4
 import 'package:lszlgl/base/base_state.dart';
6 5
 import 'package:lszlgl/page/home/home_page.dart';
7 6
 import 'package:lszlgl/page/user_center/user_center_page.dart';
@@ -66,7 +65,7 @@ class _MainTabPageState extends State<MainTabPage> {
66 65
           child: child,
67 66
           onPopInvoked: (didPop) {
68 67
             if (didPop) return;
69
-            SmartDialog.showToast('再试一次退出');
68
+            MyNavigator.showToast('再试一次退出');
70 69
             popState.value = true;
71 70
             // 2秒后不可退出
72 71
             Timer(

+ 1 - 2
lib/page/sample_task/reap_sample_task/reap_sample_task_page.dart

@@ -1,6 +1,5 @@
1 1
 import 'package:flutter/material.dart';
2 2
 import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
3
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
4 3
 import 'package:lszlgl/base/base_state.dart';
5 4
 import 'package:lszlgl/config/colors.dart';
6 5
 import 'package:lszlgl/model/rsp/sample_task_rsp.dart';
@@ -68,7 +67,7 @@ class _ReapSampleTaskPageState extends BaseState<ReapSampleTaskPage> with Ticker
68 67
 
69 68
   void submit() {
70 69
     MyNavigator.pop();
71
-    SmartDialog.showToast('提交成功');
70
+    MyNavigator.showToast('提交成功');
72 71
   }
73 72
 
74 73
   @override

+ 1 - 2
lib/page/user_center/change_pwd_page.dart

@@ -3,7 +3,6 @@ import 'dart:async';
3 3
 import 'package:flutter/material.dart';
4 4
 import 'package:flutter/services.dart';
5 5
 import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart';
6
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
7 6
 import 'package:lszlgl/base/base_state.dart';
8 7
 import 'package:lszlgl/widget/button.dart';
9 8
 import 'package:lszlgl/widget/card_item.dart';
@@ -31,7 +30,7 @@ class _ChangePwdPageState extends BaseState<ChangePwdPage> {
31 30
   }
32 31
 
33 32
   void onChange() {
34
-    SmartDialog.showToast('修改成功');
33
+    MyNavigator.showToast('修改成功');
35 34
     MyNavigator.pop();
36 35
   }
37 36
 

+ 7 - 3
lib/page/user_center/setting_page.dart

@@ -1,6 +1,6 @@
1 1
 import 'package:flutter/material.dart';
2
-import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
3 2
 import 'package:lszlgl/base/base_state.dart';
3
+import 'package:lszlgl/service/upgrade_service.dart';
4 4
 import 'package:lszlgl/widget/button.dart';
5 5
 import 'package:lszlgl/widget/card_item.dart';
6 6
 import 'package:package_info_plus/package_info_plus.dart';
@@ -19,10 +19,14 @@ class _SettingPageState extends BaseState<SettingPage> {
19 19
   String version = '';
20 20
 
21 21
   void onSave() {
22
-    SmartDialog.showToast('保存成功');
22
+    MyNavigator.showToast('保存成功');
23 23
     MyNavigator.pop();
24 24
   }
25 25
 
26
+  void onVersionTap() async {
27
+    UpgradeService.checkUpgrade(true);
28
+  }
29
+
26 30
   @override
27 31
   void initState() {
28 32
     super.initState();
@@ -75,7 +79,7 @@ class _SettingPageState extends BaseState<SettingPage> {
75 79
             rightChild: buildSwitch(shake, (value) => setState(() => shake = value)),
76 80
             bottomLine: true,
77 81
           ),
78
-          CardItemWidget('版本信息', rightText: 'V$version'),
82
+          CardItemWidget('版本信息', rightText: 'V$version', onTap: onVersionTap),
79 83
         ],
80 84
       ),
81 85
     );

+ 100 - 18
lib/router/my_navigator.dart

@@ -3,7 +3,6 @@ import 'package:flutter/material.dart';
3 3
 import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
4 4
 import 'package:lszlgl/base/base_state.dart';
5 5
 import 'package:lszlgl/page/unknow_page.dart';
6
-import 'package:flutter_smart_dialog/src/data/animation_param.dart' as dialog;
7 6
 
8 7
 /// 路由监听
9 8
 class NavigatorPageObserver extends NavigatorObserver {
@@ -295,25 +294,18 @@ class MyNavigator {
295 294
   }
296 295
 
297 296
   /// 显示Dialog
298
-  static Future<T?> pushDialog<T>({
297
+  static Future<T?> showDialog<T>({
299 298
     required WidgetBuilder builder,
300 299
     SmartDialogController? controller,
301 300
     Alignment? alignment,
302
-    bool? clickMaskDismiss,
301
+    bool? clickDismiss,
303 302
     bool? usePenetrate,
304
-    bool? useAnimation,
305
-    SmartAnimationType? animationType,
306
-    List<SmartNonAnimationType>? nonAnimationTypes,
307
-    dialog.AnimationBuilder? animationBuilder,
308
-    Duration? animationTime,
309 303
     Color? maskColor,
310 304
     Widget? maskWidget,
311
-    bool? debounce,
312 305
     VoidCallback? onDismiss,
313 306
     VoidCallback? onMask,
314 307
     Duration? displayTime,
315 308
     String? tag,
316
-    bool? backDismiss,
317 309
     bool? keepSingle,
318 310
     bool? permanent,
319 311
     bool? useSystem,
@@ -325,21 +317,20 @@ class MyNavigator {
325 317
       builder: builder,
326 318
       controller: controller,
327 319
       alignment: alignment,
328
-      clickMaskDismiss: clickMaskDismiss,
320
+      clickMaskDismiss: clickDismiss,
329 321
       usePenetrate: usePenetrate,
330
-      useAnimation: useAnimation,
331
-      animationType: animationType,
332
-      nonAnimationTypes: nonAnimationTypes,
333
-      animationBuilder: animationBuilder,
334
-      animationTime: animationTime,
322
+      useAnimation: null,
323
+      animationType: null,
324
+      nonAnimationTypes: null,
325
+      animationBuilder: null,
326
+      animationTime: null,
335 327
       maskColor: maskColor,
336 328
       maskWidget: maskWidget,
337
-      debounce: debounce,
338 329
       onDismiss: onDismiss,
339 330
       onMask: onMask,
340 331
       displayTime: displayTime,
341 332
       tag: tag,
342
-      backDismiss: backDismiss,
333
+      backDismiss: clickDismiss,
343 334
       keepSingle: keepSingle,
344 335
       permanent: permanent,
345 336
       useSystem: useSystem,
@@ -348,6 +339,97 @@ class MyNavigator {
348 339
       ignoreArea: ignoreArea,
349 340
     );
350 341
   }
342
+
343
+  static Future<void> showToast(
344
+    String msg, {
345
+    SmartDialogController? controller,
346
+    Duration? displayTime,
347
+    Alignment? alignment,
348
+    bool? clickMaskDismiss,
349
+    Color? maskColor,
350
+    Widget? maskWidget,
351
+    VoidCallback? onDismiss,
352
+    SmartToastType? displayType,
353
+  }) =>
354
+      SmartDialog.showToast(
355
+        msg,
356
+        controller: controller,
357
+        displayTime: displayTime,
358
+        alignment: alignment,
359
+        clickMaskDismiss: clickMaskDismiss,
360
+        animationType: null,
361
+        nonAnimationTypes: null,
362
+        animationBuilder: null,
363
+        usePenetrate: null,
364
+        useAnimation: null,
365
+        animationTime: null,
366
+        maskColor: maskColor,
367
+        maskWidget: maskWidget,
368
+        onDismiss: onDismiss,
369
+        onMask: null,
370
+        consumeEvent: null,
371
+        debounce: null,
372
+        displayType: displayType,
373
+        builder: null,
374
+      );
375
+
376
+  static Future<T?> showLoading<T>({
377
+    String msg = '加载中',
378
+    bool? clickDismiss,
379
+    SmartDialogController? controller,
380
+    Alignment? alignment,
381
+    bool? usePenetrate,
382
+    Color? maskColor,
383
+    Widget? maskWidget,
384
+    VoidCallback? onDismiss,
385
+    VoidCallback? onMask,
386
+    Duration? displayTime,
387
+  }) {
388
+    return SmartDialog.showLoading(
389
+      msg: msg,
390
+      controller: controller,
391
+      alignment: alignment,
392
+      clickMaskDismiss: clickDismiss,
393
+      animationType: null,
394
+      nonAnimationTypes: null,
395
+      animationBuilder: null,
396
+      usePenetrate: usePenetrate,
397
+      useAnimation: null,
398
+      animationTime: null,
399
+      maskColor: maskColor,
400
+      maskWidget: maskWidget,
401
+      onDismiss: onDismiss,
402
+      onMask: onMask,
403
+      displayTime: displayTime,
404
+      backDismiss: clickDismiss,
405
+      builder: null,
406
+    );
407
+  }
408
+
409
+  static Future<void> dismiss<T>({
410
+    SmartStatus status = SmartStatus.smart,
411
+    String? tag,
412
+    T? result,
413
+    bool force = false,
414
+  }) =>
415
+      SmartDialog.dismiss(
416
+        status: status,
417
+        tag: tag,
418
+        result: result,
419
+        force: force,
420
+      );
421
+
422
+  static Future<void> dismissLoading<T>({
423
+    String? tag,
424
+    T? result,
425
+    bool force = false,
426
+  }) =>
427
+      SmartDialog.dismiss(
428
+        status: SmartStatus.loading,
429
+        tag: tag,
430
+        result: result,
431
+        force: force,
432
+      );
351 433
 }
352 434
 
353 435
 /// 页面设置信息

+ 87 - 0
lib/service/upgrade_service.dart

@@ -0,0 +1,87 @@
1
+import 'dart:io';
2
+
3
+import 'package:dio/dio.dart';
4
+import 'package:install_plugin/install_plugin.dart';
5
+import 'package:package_info_plus/package_info_plus.dart';
6
+import 'package:path_provider/path_provider.dart';
7
+import 'package:permission_handler/permission_handler.dart';
8
+
9
+import '../main.dart';
10
+import '../network/api.dart';
11
+import '../router/my_navigator.dart';
12
+import '../widget/upgrade_dialog.dart';
13
+
14
+class UpgradeService {
15
+  UpgradeService._();
16
+
17
+  static Future<Response> download(String apkUrl, String apkName, ProgressCallback progress) async {
18
+    String savePath = '${await getApkDirectoryPath()}/$apkName';
19
+    var rsp = await Dio().download(
20
+      apkUrl,
21
+      savePath,
22
+      onReceiveProgress: (count, total) {
23
+        if (total < 0) return;
24
+        progress.call(count, total);
25
+      },
26
+    );
27
+    return rsp;
28
+  }
29
+
30
+  /// 获取apk文件夹路径
31
+  static Future<String> getApkDirectoryPath() async {
32
+    return '${(await getApplicationSupportDirectory()).path}/apk';
33
+  }
34
+
35
+  /// 获取apk文件
36
+  static Future<File> getApkFile(String apkName) async {
37
+    return File('${await getApkDirectoryPath()}/$apkName');
38
+  }
39
+
40
+  /// 安装APK
41
+  static Future<bool> installApk(String path) async {
42
+    var status = await Permission.requestInstallPackages.status;
43
+    if (!status.isGranted) {
44
+      Permission.requestInstallPackages.request();
45
+      return false;
46
+    }
47
+    await InstallPlugin.install(path);
48
+    return true;
49
+  }
50
+
51
+  /// 从路径中获取文件名
52
+  static String getName(String path) {
53
+    var pathSplit = path.split('/');
54
+    return pathSplit[pathSplit.length - 1];
55
+  }
56
+
57
+  static bool checking = false;
58
+
59
+  /// 检查版本更新
60
+  static Future<void> checkUpgrade(bool showLoading) async {
61
+    if (!Platform.isAndroid) return;
62
+    if (checking) return;
63
+    checking = true;
64
+    if (showLoading) MyNavigator.showLoading();
65
+    try {
66
+      // 获取apk下载地址
67
+      var path = (await Api().getAppDownloadUrl()).data;
68
+      if (showLoading) MyNavigator.dismissLoading();
69
+      if (path != null) {
70
+        // 获取线上版本
71
+        List<String> nameSplit = UpgradeService.getName(path).split('_');
72
+        int onlineCode = int.parse(nameSplit[nameSplit.length - 1].split('.').first);
73
+        int localCode = int.parse((await PackageInfo.fromPlatform()).buildNumber);
74
+        logger.d('localCode:$localCode onlineCode:$onlineCode');
75
+        if (localCode >= onlineCode) {
76
+          if (showLoading) MyNavigator.showToast('已是最新版本');
77
+        } else {
78
+          // 版本更新
79
+          UpgradeDialog.showDialog(path);
80
+        }
81
+      }
82
+    } catch (e) {
83
+      if (showLoading) MyNavigator.dismissLoading();
84
+    }
85
+    checking = false;
86
+  }
87
+}

+ 130 - 0
lib/widget/upgrade_dialog.dart

@@ -0,0 +1,130 @@
1
+import 'package:flutter/material.dart';
2
+import 'package:lszlgl/base/base_lifecycle_state.dart';
3
+import 'package:lszlgl/main.dart';
4
+import 'package:lszlgl/service/upgrade_service.dart';
5
+import 'package:lszlgl/widget/button.dart';
6
+
7
+class UpgradeDialog extends StatefulWidget {
8
+  final String path;
9
+
10
+  const UpgradeDialog(this.path, {super.key});
11
+
12
+  static void showDialog(String path) {
13
+    MyNavigator.showDialog(
14
+      builder: (_) => UpgradeDialog(path),
15
+      tag: 'upgrade',
16
+      clickDismiss: false,
17
+    );
18
+  }
19
+
20
+  @override
21
+  State<UpgradeDialog> createState() => _UpgradeDialogState();
22
+}
23
+
24
+class _UpgradeDialogState extends BaseLifecycleState<UpgradeDialog> {
25
+  double progress = -1;
26
+
27
+  void dismiss() {
28
+    MyNavigator.dismiss(tag: 'upgrade');
29
+  }
30
+
31
+  void onDownload() async {
32
+    String urlPath = widget.path;
33
+
34
+    UpgradeService.getName(urlPath);
35
+    // 拆分文件名
36
+    var pathSplit = urlPath.split('/');
37
+    String apkName = pathSplit[pathSplit.length - 1];
38
+    logger.d('apkName:$apkName');
39
+
40
+    var file = await UpgradeService.getApkFile(apkName);
41
+    // 已有文件 直接安装
42
+    if (await file.exists()) {
43
+      installApk(file.path);
44
+      return;
45
+    }
46
+    // 无文件 去下载
47
+    try {
48
+      await UpgradeService.download(
49
+        urlPath,
50
+        apkName,
51
+        (count, total) {
52
+          setState(() {
53
+            progress = count / total;
54
+          });
55
+        },
56
+      );
57
+      installApk(file.path);
58
+    } catch (_) {
59
+      MyNavigator.showToast('下载失败,请重试');
60
+      setState(() {
61
+        progress = -1;
62
+      });
63
+    }
64
+  }
65
+
66
+  /// 安装apk
67
+  void installApk(String path) async {
68
+    bool success = await UpgradeService.installApk(path);
69
+    if (success) {
70
+      dismiss();
71
+    } else {
72
+      setState(() {
73
+        progress = -1;
74
+      });
75
+    }
76
+  }
77
+
78
+  @override
79
+  Widget build(BuildContext context) {
80
+    return Container(
81
+      margin: const EdgeInsets.symmetric(horizontal: 24),
82
+      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
83
+      decoration: const BoxDecoration(
84
+        color: Colors.white,
85
+        borderRadius: BorderRadius.all(Radius.circular(8)),
86
+      ),
87
+      child: Column(
88
+        mainAxisSize: MainAxisSize.min,
89
+        children: [
90
+          Row(
91
+            children: [
92
+              const Text(
93
+                '发现新版本',
94
+                style: TextStyle(color: Color(0xFF333333), fontSize: 16, fontWeight: FontWeight.w500),
95
+              ),
96
+              const Spacer(),
97
+              progress == -1
98
+                  ? GestureDetector(
99
+                      onTap: dismiss,
100
+                      child: const Text(
101
+                        '取消',
102
+                        style: TextStyle(color: Color(0xFF333333), fontSize: 14),
103
+                      ),
104
+                    )
105
+                  : const SizedBox.shrink(),
106
+            ],
107
+          ),
108
+          const SizedBox(height: 24),
109
+          progress == -1 ? MyButton('立即下载安装', onTap: onDownload, alignment: null) : buildProgress(),
110
+        ],
111
+      ),
112
+    );
113
+  }
114
+
115
+  Widget buildProgress() {
116
+    int pre = (double.parse(progress.toStringAsFixed(2)) * 100).toInt();
117
+    return Row(
118
+      children: [
119
+        Expanded(flex: 6, child: LinearProgressIndicator(value: progress, borderRadius: BorderRadius.circular(8), minHeight: 8)),
120
+        Expanded(
121
+          child: Text(
122
+            '$pre%',
123
+            textAlign: TextAlign.right,
124
+            style: const TextStyle(color: Color(0xFF333333), fontSize: 14),
125
+          ),
126
+        )
127
+      ],
128
+    );
129
+  }
130
+}

文件差异内容过多而无法显示
+ 195 - 131
pubspec.lock


+ 9 - 1
pubspec.yaml

@@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
16 16
 # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
17 17
 # In Windows, build-name is used as the major, minor, and patch parts
18 18
 # of the product and file versions while build-number is used as the build suffix.
19
-version: 0.0.2+2
19
+version: 0.0.3+3
20 20
 
21 21
 environment:
22 22
   sdk: '>=3.1.5 <4.0.0'
@@ -70,6 +70,14 @@ dependencies:
70 70
   easy_refresh: ^3.3.4
71 71
   # 依赖注入
72 72
   get_it: ^7.1.3
73
+  # 文件路径
74
+  path_provider: ^2.1.2
75
+  # intent
76
+  android_intent_plus: ^4.0.3
77
+  # 安装apk
78
+  install_plugin: ^2.1.0
79
+  # 权限管理
80
+  permission_handler: ^11.3.0
73 81
 
74 82
 dev_dependencies:
75 83
   flutter_test:

+ 3 - 0
windows/flutter/generated_plugin_registrant.cc

@@ -7,8 +7,11 @@
7 7
 #include "generated_plugin_registrant.h"
8 8
 
9 9
 #include <connectivity_plus/connectivity_plus_windows_plugin.h>
10
+#include <permission_handler_windows/permission_handler_windows_plugin.h>
10 11
 
11 12
 void RegisterPlugins(flutter::PluginRegistry* registry) {
12 13
   ConnectivityPlusWindowsPluginRegisterWithRegistrar(
13 14
       registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin"));
15
+  PermissionHandlerWindowsPluginRegisterWithRegistrar(
16
+      registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin"));
14 17
 }

+ 1 - 0
windows/flutter/generated_plugins.cmake

@@ -4,6 +4,7 @@
4 4
 
5 5
 list(APPEND FLUTTER_PLUGIN_LIST
6 6
   connectivity_plus
7
+  permission_handler_windows
7 8
 )
8 9
 
9 10
 list(APPEND FLUTTER_FFI_PLUGIN_LIST