Mztj67_#M}W?l>kYSliK<%xAp;0j{!}J0!o7b
zE>q9${Lb$D&h7k=+4=!ek^n+`0zq>LL1O?lVyea53S5x`Nqqo2YyeuIrQrJj9XjOp
z{;T5qbj3}&1vg1VK~#9!?b~^C5-}JC@Pyrv-6dSEqJqT}#j9#dJ@GzT@B8}x
zU&J@bBI>f6w6en+CeI)3^kC*U?}X%OD8$Fd$H&LV$H&LV$H&LV#|K5~mLYf|VqzOc
zkc7qL~0sOYuM{tG`rYEDV{DWY`Z8&)kW*hc2VkBuY+^Yx&92j&StN}Wp=LD
zxoGxXw6f&8sB^u})h@b@z0RBeD`K7RMR9deyL(ZJu#39Z>rT)^>v}Khq8U-IbIvT>
z?4pV9qGj=2)TNH3d)=De<+^w;>S7m_eFKTvzeaBeir45xY!^m!FmxnljbSS_3o=g(
z->^wC9%qkR{kbGnW8MfFew_o9h3(r55Is`L$8KI@d+*%{=Nx+FXJ98L0PjFIu;rGnnfY
zn1R5Qnp<{Jq0M1vX=X&F8gtLmcWv$1*M@4ZfF^9``()#hGTeKeP`1!iED
ztNE(TN}M5}3Bbc*d=FIv`DNv&@|C6yYj{sSqUj5oo$#*0$7pu|Dd2TLI>t5%I
zIa4Dvr(iayb+5x=j*Vum9&irk)xV1`t509lnPO0%skL8_1c#Xbamh(2@f?4yUI
zhhuT5<#8RJhGz4%b$`PJwKPAudsm|at?u;*hGgnA
zU1;9gnxVBC)wA(BsB`AW54N{|qmikJR*%x0c`{LGsSfa|NK61pYH(r-UQ4_JXd!Rsz)=k
zL{GMc5{h138)fF5CzHEDM>+FqY)$pdN3}Ml+riTgJOLN0F*Vh?{9ESR{SVVg>*>=#
zix;VJHPtvFFCRY$Ks*F;VX~%*r9F)W`PmPE9F!(&s#x07n2<}?S{(ygpXgX-&B&OM
zONY&BRQ(#%0%jeQs?oJ4P!p*R98>qCy5p8w>_gpuh39NcOlp)(wOoz0sY-Qz55eB~
z7OC-fKBaD1sE3$l-6QgBJO!n?QOTza`!S_YK
z_v-lm^7{VO^8Q@M_^8F)09Ki6%=s?2_5eupee(w1FB%aqSweusQ-T+CH0Xt{`
zFjMvW{@C&TB)k25()nh~_yJ9coBRL(0oO@HK~z}7?bm5j;y@69;bvlHb2tf!$ReA~x{22wTq550
z?f?Hnw(;m3ip30;QzdV~7pi!wyMYhDtXW#cO7T>|f=bdFhu+F!zMZ2UFj;GUKX7tI
z;hv3{q~!*pMj75WP_c}>6)IWvg5_yyg<9Op()eD1hWC19M@?_9_MHec{Z8n3FaF{8
z;u`Mw0ly(uE>*CgQYv{be6ab2LWhlaH1^iLIM{olnag$78^Fd}%dR7;JECQ+hmk|o
z!u2&!3MqPfP5ChDSkFSH8F2WVOEf0(E_M(JL17G}Y+fg0_IuW%WQ
zG(mG&u?|->YSdk0;8rc{yw2@2Z&GA}z{Wb91Ooz9VhA{b2DYE7RmG
zjL}?eq#iX%3#k;JWMx_{^2nNax`xPhByFiDX+a7uTGU|otOvIAUy|dEKkXOm-`aWS
z27pUzD{a)Ct<6p{{3)+lq@i`t@%>-wT4r?*S}k)58e09WZYP0{{R3FC5Sl00039P)t-s|Ns9~
z#rP?<_5oL$Q^olD{r_0T`27C={r>*`|Nj71npVa5OTzc(_WfbW_({R{p56NV{r*M2
z_xt?)2V0#0NsfV0u>{42ctGP(8vQj-Btk1n|O0ZD=YLwd&R{Ko41Gr9H=
zY@z@@bOAMB5Ltl$E>bJJ{>JP30ZxkmI%?eW{k`b?Wy<&gOo;dS`~CR$Vwb@XWtR|N
zi~t=w02?-0&j0TD{>bb6sNwsK*!p?V`RMQUl(*DVjk-9Cx+-z1KXab|Ka2oXhX5f%
z`$|e!000AhNklrxs)5QTeTVRiEmz~MKK1WAjCw(c-JK6eox;2O)?`?
zTG`AHia671e^vgmp!llKp|=5sVHk#C7=~epA~VAf-~%aPC=%Qw01h8mnSZ|p?hz91
z7p83F3%LVu9;S$tSI$C^%^yud1dfTM_6p2|+5Ejp$bd`GDvbR|xit>i!ZD&F>@CJrPmu*UjD&?DfZs=$@e3FQA(vNiU+$A*%a}
z?`XcG2jDxJ_ZQ#Md`H{4Lpf6QBDp81_KWZ6Tk#yCy1)32zO#3<7>b`eT7UyYH1eGz
z;O(rH$=QR*L%%ZcBpc=eGua?N55nD^K(8<#gl2+pN_j~b2MHs4#mcLmv%DkspS-3<
zpI1F=^9siI0s-;IN_IrA;5xm~3?3!StX}pUv0vkxMaqm+zxrg7X7(I&*N~&dEd0kD
z-FRV|g=|QuUsuh>-xCI}vD2imzYIOIdcCVV=$Bz@*u0+Bs<|L^)32nN*=wu3n%Ynw
z@1|eLG>!8ruU1pFXUfb`j>(=Gy~?Rn4QJ-c3%3T|(Frd!bI`9u&zAnyFYTqlG#&J7
zAkD(jpw|oZLNiA>;>hgp1KX7-wxC~31II47gc
zHcehD6Uxlf%+M^^uN5Wc*G%^;>D5qT{>=uxUhX%WJu^Z*(_Wq9y}npFO{Hhb>s6<9
zNi0pHXWFaVZnb)1+RS&F)xOv6&aeILcI)`k#0YE+?e)5r7J#c`3Z7x!LpTc01dx
zrdC3{Z;joZ^KN&))zB_i)I9fWedoN>Zl-6_Iz+^G&*ak2jpF07*qoM6N<$f;w%0(f|Me
literal 0
HcmV?d00001
diff --git a/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..0467bf12aa4d28f374bb26596605a46dcbb3e7c8
GIT binary patch
literal 1418
zcmV;51$Fv~P)q
zKfU)WzW*n(@|xWGCA9ScMt*e9`2kdxPQ&&>|-UCa7_51w+
zLUsW@ZzZSW0y$)Hp~e9%PvP|a03ks1`~K?q{u;6NC8*{AOqIUq{CL&;p56Lf$oQGq
z^={4hPQv)y=I|4n+?>7Fim=dxt1
z2H+Dm+1+fh+IF>G0SjJMkQQre1x4|G*Z==(Ot&kCnUrL4I(rf(ucITwmuHf^hXiJT
zkdTm&kdTm&kdTm&kdP`esgWG0BcWCVkVZ&2dUwN`cgM8QJb`Z7Z~e<&Yj2(}>Tmf`
zm1{eLgw!b{bXkjWbF%dTkTZEJWyWOb##Lfw4EK2}<0d6%>AGS{po>WCOy&f$Tay_>
z?NBlkpo@s-O;0V%Y_Xa-G#_O08q5LR*~F%&)}{}r&L%Sbs8AS4t7Y0NEx*{soY=0MZExqA5XHQkqi#4gW3
zqODM^iyZl;dvf)-bOXtOru(s)Uc7~BFx{w-FK;2{`VA?(g&@3z&bfLFyctOH!cVsF
z7IL=fo-qBndRUm;kAdXR4e6>k-z|21AaN%ubeVrHl*<|s&Ax@W-t?LR(P-24A5=>a
z*R9#QvjzF8n%@1Nw@?CG@6(%>+-0ASK~jEmCV|&a*7-GKT72W<(TbSjf)&Eme6nGE
z>Gkj4Sq&2e+-G%|+NM8OOm5zVl9{Z8Dd8A5z3y8mZ=4Bv4%>as_{9cN#bm~;h>62(
zdqY93Zy}v&c4n($Vv!UybR8ocs7#zbfX1IY-*w~)p}XyZ-SFC~4w>BvMVr`dFbelV{lLL0bx7@*ZZdebr3`sP;?
zVImji)kG)(6Juv0lz@q`F!k1FE;CQ(D0iG$wchPbKZQELlsZ#~rt8#90Y_Xh&3U-<
z{s<&cCV_1`^TD^ia9!*mQDq&
zn2{r`j};V|uV%_wsP!zB?m%;FeaRe+X47K0e+KE!8C{gAWF8)lCd1u1%~|M!XNRvw
zvtqy3iz0WSpWdhn6$hP8PaRBmp)q`#PCA`Vd#Tc$@f1tAcM>f_I@bC)hkI9|o(Iqv
zo}Piadq!j76}004RBio<`)70k^`K1NK)q>w?p^C6J2ZC!+UppiK6&y3Kmbv&O!oYF
z34$0Z;QO!JOY#!`qyGH<3Pd}Pt@q*A0V=3SVtWKRR8d8Z&@)3qLPA19LPA19LPEUC
YUoZo%k(ykuW&i*H07*qoM6N<$f+CH{y8r+H
literal 0
HcmV?d00001
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000..0bedcf2
--- /dev/null
+++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838
GIT binary patch
literal 68
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J
Q1PU{Fy85}Sb4q9e0B4a5jsO4v
literal 0
HcmV?d00001
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838
GIT binary patch
literal 68
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J
Q1PU{Fy85}Sb4q9e0B4a5jsO4v
literal 0
HcmV?d00001
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000000000000000000000000000000000000..9da19eacad3b03bb08bbddbbf4ac48dd78b3d838
GIT binary patch
literal 68
zcmeAS@N?(olHy`uVBq!ia0vp^j3CUx0wlM}@Gt=>Zci7-kcv6Uzs@r-FtIZ-&5|)J
Q1PU{Fy85}Sb4q9e0B4a5jsO4v
literal 0
HcmV?d00001
diff --git a/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000..89c2725
--- /dev/null
+++ b/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000..f2e259c
--- /dev/null
+++ b/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/ios/Runner/Base.lproj/Main.storyboard b/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000..f3c2851
--- /dev/null
+++ b/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist
new file mode 100644
index 0000000..56edd98
--- /dev/null
+++ b/example/ios/Runner/Info.plist
@@ -0,0 +1,49 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleDisplayName
+ App Upgrade Plugin
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ app_upgrade_plugin_example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ CADisableMinimumFrameDurationOnPhone
+
+ UIApplicationSupportsIndirectInputEvents
+
+
+
diff --git a/example/ios/Runner/Runner-Bridging-Header.h b/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000..308a2a5
--- /dev/null
+++ b/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/example/ios/RunnerTests/RunnerTests.swift b/example/ios/RunnerTests/RunnerTests.swift
new file mode 100644
index 0000000..bfacd0c
--- /dev/null
+++ b/example/ios/RunnerTests/RunnerTests.swift
@@ -0,0 +1,27 @@
+import Flutter
+import UIKit
+import XCTest
+
+
+@testable import app_upgrade_plugin
+
+// This demonstrates a simple unit test of the Swift portion of this plugin's implementation.
+//
+// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
+
+class RunnerTests: XCTestCase {
+
+ func testGetPlatformVersion() {
+ let plugin = AppUpgradePlugin()
+
+ let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: [])
+
+ let resultExpectation = expectation(description: "result block must be called.")
+ plugin.handle(call) { result in
+ XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion)
+ resultExpectation.fulfill()
+ }
+ waitForExpectations(timeout: 1)
+ }
+
+}
diff --git a/example/lib/main.dart b/example/lib/main.dart
new file mode 100644
index 0000000..e364f8c
--- /dev/null
+++ b/example/lib/main.dart
@@ -0,0 +1,147 @@
+import 'dart:async';
+
+import 'package:app_upgrade_plugin/app_upgrade_plugin.dart';
+import 'package:app_upgrade_plugin/app_upgrade_plugin_enhanced.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
+
+void main() {
+ // 确保Flutter绑定已初始化
+ WidgetsFlutterBinding.ensureInitialized();
+
+ // 插件现在会自动配置证书验证:
+ // - Debug模式:自动绕过证书验证
+ // - Release模式:严格证书验证
+
+ // 如需强制绕过证书(不推荐),可以使用:
+ // AppUpgradePlugin.ignoreCertificate = true;
+
+ // 或手动配置HTTP设置:
+ // AppUpgradePlugin().configureHttp(HttpConfig.unsafe);
+
+ // 延迟插件配置到Flutter完全初始化后
+ Future.delayed(Duration.zero, () {
+ AppUpgradePluginEnhanced.instance.configure(
+ debugMode: true,
+ wifiOnly: false,
+ autoCheck: true,
+ );
+ });
+
+ runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ // 这里是国际化支持,确保添加flutter_localizations依赖
+ supportedLocales: const [Locale('zh', 'CN')],
+ localizationsDelegates: const [
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ ],
+ home: const HomePage(),
+ );
+ }
+}
+
+class HomePage extends StatefulWidget {
+ const HomePage({super.key});
+
+ @override
+ State createState() => _HomePageState();
+}
+
+class _HomePageState extends State {
+ String _platformVersion = 'Unknown';
+ final _appUpgradePlugin = AppUpgradePlugin();
+
+ @override
+ void initState() {
+ super.initState();
+ initPlatformState();
+ // Use addPostFrameCallback to ensure the context is valid and mounted
+ // after the first frame has been rendered.
+ WidgetsBinding.instance.addPostFrameCallback((_) {
+ if (mounted) {
+ _testNetworkFunctionality();
+ }
+ });
+ }
+
+ Future _testNetworkFunctionality() async {
+ await AppUpgradeSimple.instance.checkUpdate(
+ context: context,
+ url: 'https://dpc-teacher-api.23544.com/api/infra/AppVersion/Get',
+ params: {
+ 'appName': 'making_school_asignment_app',
+ 'ftuType': 1,
+ },
+ showNoUpdateToast: true,
+ autoDownload: false,
+ autoInstall: true,
+ );
+ debugPrint('=== 网络功能测试完成 ===');
+ }
+
+ // Platform messages are asynchronous, so we initialize in an async method.
+ Future initPlatformState() async {
+ String platformVersion;
+ // Platform messages may fail, so we use a try/catch PlatformException.
+ // We also handle the message potentially returning null.
+ try {
+ platformVersion = await _appUpgradePlugin.getPlatformVersion() ?? 'Unknown platform version';
+ } on PlatformException {
+ platformVersion = 'Failed to get platform version.';
+ }
+
+ // If the widget was removed from the tree while the asynchronous platform
+ // message was in flight, we want to discard the reply rather than calling
+ // setState to update our non-existent appearance.
+ if (!mounted) return;
+
+ setState(() {
+ _platformVersion = platformVersion;
+ });
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('App Upgrade Plugin 示例'),
+ ),
+ body: Padding(
+ padding: const EdgeInsets.all(16.0),
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text('Running on: $_platformVersion\n'),
+ const SizedBox(height: 16),
+ ElevatedButton(
+ onPressed: () {
+ AppUpgradeSimple.instance.checkUpdate(
+ context: context,
+ url: 'https://dpc-teacher-api.23544.com/api/infra/AppVersion/Get',
+ params: {
+ 'appName': 'making_school_asignment_app',
+ 'ftuType': 1,
+ },
+ showNoUpdateToast: true,
+ autoDownload: false,
+ autoInstall: false,
+ );
+ },
+ child: const Text('检查更新'),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
diff --git a/example/lib/main_enhanced.dart b/example/lib/main_enhanced.dart
new file mode 100644
index 0000000..e1108cb
--- /dev/null
+++ b/example/lib/main_enhanced.dart
@@ -0,0 +1,738 @@
+import 'dart:async';
+
+import 'package:app_upgrade_plugin/app_upgrade_plugin_enhanced.dart';
+import 'package:flutter/material.dart';
+import 'package:permission_handler/permission_handler.dart';
+
+void main() {
+ runApp(const MyEnhancedApp());
+}
+
+class MyEnhancedApp extends StatelessWidget {
+ const MyEnhancedApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: '增强版App升级插件示例',
+ theme: ThemeData(
+ colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
+ useMaterial3: true,
+ ),
+ home: const EnhancedUpgradePage(),
+ );
+ }
+}
+
+class EnhancedUpgradePage extends StatefulWidget {
+ const EnhancedUpgradePage({super.key});
+
+ @override
+ State createState() => _EnhancedUpgradePageState();
+}
+
+class _EnhancedUpgradePageState extends State with SingleTickerProviderStateMixin {
+ final _plugin = AppUpgradePluginEnhanced.instance;
+
+ late TabController _tabController;
+
+ // 状态变量
+ Map? _appInfo;
+ NetworkStatus? _networkStatus;
+ DownloadTask? _currentDownload;
+ UpgradeInfo? _upgradeInfo;
+ Map? _cacheStats;
+
+ // 配置选项
+ bool _wifiOnly = true;
+ bool _autoCheck = true;
+ bool _supportBreakpoint = true;
+ bool _verifyIntegrity = true;
+ VersionCompareStrategy _versionStrategy = VersionCompareStrategy.semantic;
+
+ // 测试数据
+ final _testVersions = ['1.0.0', '1.1.0', '1.2.0', '2.0.0-beta.1', '2.0.0'];
+ String _currentTestVersion = '1.0.0';
+ String _remoteTestVersion = '2.0.0';
+
+ @override
+ void initState() {
+ super.initState();
+ _tabController = TabController(length: 4, vsync: this);
+ _initPlugin();
+ _loadData();
+ }
+
+ @override
+ void dispose() {
+ _tabController.dispose();
+ _plugin.removeUpgradeCallback(_onUpgradeInfo);
+ _plugin.removeDownloadCallback(_onDownloadProgress);
+ _plugin.removeErrorCallback(_onError);
+ super.dispose();
+ }
+
+ void _initPlugin() {
+ // 配置插件
+ _plugin.configure(
+ debugMode: true,
+ autoCheck: _autoCheck,
+ wifiOnly: _wifiOnly,
+ supportBreakpoint: _supportBreakpoint,
+ verifyIntegrity: _verifyIntegrity,
+ versionStrategy: _versionStrategy,
+ );
+
+ // 添加回调
+ _plugin.addUpgradeCallback(_onUpgradeInfo);
+ _plugin.addDownloadCallback(_onDownloadProgress);
+ _plugin.addErrorCallback(_onError);
+
+ // 监听网络状态
+ NetworkMonitor.instance.statusStream.listen((status) {
+ setState(() {
+ _networkStatus = status;
+ });
+ });
+ }
+
+ Future _loadData() async {
+ // 请求权限
+ await _requestPermissions();
+
+ // 获取App信息
+ _appInfo = await _plugin.getAppInfo();
+
+ // 获取网络状态
+ _networkStatus = _plugin.networkStatus;
+
+ // 获取缓存统计
+ _cacheStats = await _plugin.getCacheStats();
+
+ setState(() {});
+ }
+
+ Future _requestPermissions() async {
+ if (Theme.of(context).platform == TargetPlatform.android) {
+ await [
+ Permission.storage,
+ Permission.requestInstallPackages,
+ Permission.notification,
+ ].request();
+ }
+ }
+
+ void _onUpgradeInfo(UpgradeInfo info) {
+ setState(() {
+ _upgradeInfo = info;
+ });
+
+ // 显示升级对话框
+ UpgradeDialog.show(
+ context,
+ upgradeInfo: info,
+ primaryColor: Theme.of(context).colorScheme.primary,
+ );
+ }
+
+ void _onDownloadProgress(DownloadTask task) {
+ setState(() {
+ _currentDownload = task;
+ });
+ }
+
+ void _onError(String error) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ SnackBar(
+ content: Text(error),
+ backgroundColor: Colors.red,
+ ),
+ );
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('增强版App升级插件'),
+ backgroundColor: Theme.of(context).colorScheme.inversePrimary,
+ bottom: TabBar(
+ controller: _tabController,
+ tabs: const [
+ Tab(text: '基础功能'),
+ Tab(text: '高级配置'),
+ Tab(text: '网络监测'),
+ Tab(text: '版本管理'),
+ ],
+ ),
+ ),
+ body: TabBarView(
+ controller: _tabController,
+ children: [
+ _buildBasicTab(),
+ _buildConfigTab(),
+ _buildNetworkTab(),
+ _buildVersionTab(),
+ ],
+ ),
+ );
+ }
+
+ // 基础功能标签页
+ Widget _buildBasicTab() {
+ return SingleChildScrollView(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ // App信息卡片
+ if (_appInfo != null)
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ 'App信息',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 12),
+ Text('应用名称: ${_appInfo!['appName']}'),
+ Text('包名: ${_appInfo!['packageName']}'),
+ Text('版本: ${_appInfo!['version']}'),
+ Text('构建号: ${_appInfo!['buildNumber']}'),
+ ],
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ // 升级信息卡片
+ if (_upgradeInfo != null)
+ Card(
+ color: Colors.amber.shade50,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ const Icon(Icons.system_update, color: Colors.amber),
+ const SizedBox(width: 8),
+ const Text(
+ '发现新版本',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ Text('版本: ${_upgradeInfo!.versionName}'),
+ Text('强制更新: ${_upgradeInfo!.isForceUpdate ? "是" : "否"}'),
+ if (_upgradeInfo!.apkSize != null) Text('大小: ${_formatBytes(_upgradeInfo!.apkSize!)}'),
+ ],
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ // 下载进度卡片
+ if (_currentDownload != null)
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '下载进度',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 12),
+ LinearProgressIndicator(value: _currentDownload!.progress),
+ const SizedBox(height: 8),
+ Text('状态: ${_getDownloadStatusText(_currentDownload!.status)}'),
+ Text('进度: ${(_currentDownload!.progress * 100).toStringAsFixed(1)}%'),
+ if (_currentDownload!.totalSize != null)
+ Text(
+ '${_formatBytes(_currentDownload!.downloadedSize)} / ${_formatBytes(_currentDownload!.totalSize!)}'),
+ if (_currentDownload!.errorMessage != null)
+ Text('错误: ${_currentDownload!.errorMessage}', style: const TextStyle(color: Colors.red)),
+ ],
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ // 操作按钮
+ Wrap(
+ spacing: 8,
+ runSpacing: 8,
+ children: [
+ ElevatedButton.icon(
+ onPressed: () => _checkUpdate(),
+ icon: const Icon(Icons.refresh),
+ label: const Text('检查更新'),
+ ),
+ if (_currentDownload != null) ...[
+ if (_currentDownload!.status == DownloadStatus.downloading)
+ ElevatedButton.icon(
+ onPressed: () => _plugin.pauseDownload(),
+ icon: const Icon(Icons.pause),
+ label: const Text('暂停'),
+ ),
+ if (_currentDownload!.status == DownloadStatus.paused)
+ ElevatedButton.icon(
+ onPressed: () => _plugin.resumeDownload(),
+ icon: const Icon(Icons.play_arrow),
+ label: const Text('继续'),
+ ),
+ if (_currentDownload!.status == DownloadStatus.failed)
+ ElevatedButton.icon(
+ onPressed: () => _plugin.retryDownload(),
+ icon: const Icon(Icons.replay),
+ label: const Text('重试'),
+ ),
+ OutlinedButton.icon(
+ onPressed: () => _plugin.cancelDownload(),
+ icon: const Icon(Icons.cancel),
+ label: const Text('取消'),
+ ),
+ ],
+ ],
+ ),
+ ],
+ ),
+ );
+ }
+
+ // 高级配置标签页
+ Widget _buildConfigTab() {
+ return SingleChildScrollView(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '升级配置',
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 16),
+
+ SwitchListTile(
+ title: const Text('仅WiFi下载'),
+ subtitle: const Text('只在WiFi环境下自动下载更新'),
+ value: _wifiOnly,
+ onChanged: (value) {
+ setState(() {
+ _wifiOnly = value;
+ });
+ _plugin.configure(wifiOnly: value);
+ },
+ ),
+
+ SwitchListTile(
+ title: const Text('自动检查更新'),
+ subtitle: const Text('定期自动检查是否有新版本'),
+ value: _autoCheck,
+ onChanged: (value) {
+ setState(() {
+ _autoCheck = value;
+ });
+ _plugin.configure(autoCheck: value);
+ },
+ ),
+
+ SwitchListTile(
+ title: const Text('断点续传'),
+ subtitle: const Text('支持暂停后继续下载'),
+ value: _supportBreakpoint,
+ onChanged: (value) {
+ setState(() {
+ _supportBreakpoint = value;
+ });
+ _plugin.configure(supportBreakpoint: value);
+ },
+ ),
+
+ SwitchListTile(
+ title: const Text('文件校验'),
+ subtitle: const Text('下载完成后校验文件完整性'),
+ value: _verifyIntegrity,
+ onChanged: (value) {
+ setState(() {
+ _verifyIntegrity = value;
+ });
+ _plugin.configure(verifyIntegrity: value);
+ },
+ ),
+
+ const Divider(),
+
+ ListTile(
+ title: const Text('版本比较策略'),
+ subtitle: Text(_getVersionStrategyText(_versionStrategy)),
+ trailing: DropdownButton(
+ value: _versionStrategy,
+ onChanged: (value) {
+ if (value != null) {
+ setState(() {
+ _versionStrategy = value;
+ });
+ _plugin.configure(versionStrategy: value);
+ }
+ },
+ items: VersionCompareStrategy.values.map((strategy) {
+ return DropdownMenuItem(
+ value: strategy,
+ child: Text(_getVersionStrategyText(strategy)),
+ );
+ }).toList(),
+ ),
+ ),
+
+ const Divider(),
+
+ // 缓存管理
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '缓存管理',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 12),
+ if (_cacheStats != null) ...[
+ Text(
+ '内存缓存: ${_cacheStats!['memoryCache']['sizeFormatted']} (${_cacheStats!['memoryCache']['count']}项)'),
+ Text(
+ '磁盘缓存: ${_cacheStats!['diskCache']['sizeFormatted']} (${_cacheStats!['diskCache']['count']}项)'),
+ Text('总计: ${_cacheStats!['total']['sizeFormatted']} (${_cacheStats!['total']['count']}项)'),
+ ],
+ const SizedBox(height: 12),
+ Row(
+ children: [
+ ElevatedButton(
+ onPressed: () async {
+ _cacheStats = await _plugin.getCacheStats();
+ setState(() {});
+ },
+ child: const Text('刷新'),
+ ),
+ const SizedBox(width: 8),
+ OutlinedButton(
+ onPressed: () async {
+ await _plugin.clearCache();
+ _cacheStats = await _plugin.getCacheStats();
+ if (mounted) {
+ setState(() {});
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('缓存已清空')),
+ );
+ }
+ },
+ child: const Text('清空缓存'),
+ ),
+ ],
+ ),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ // 网络监测标签页
+ Widget _buildNetworkTab() {
+ return SingleChildScrollView(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '网络状态',
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 16),
+ if (_networkStatus != null) ...[
+ Card(
+ color: _networkStatus!.isConnected ? Colors.green.shade50 : Colors.red.shade50,
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Row(
+ children: [
+ Icon(
+ _networkStatus!.isConnected ? Icons.wifi : Icons.wifi_off,
+ color: _networkStatus!.isConnected ? Colors.green : Colors.red,
+ ),
+ const SizedBox(width: 8),
+ Text(
+ _networkStatus!.isConnected ? '已连接' : '未连接',
+ style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ ],
+ ),
+ const SizedBox(height: 12),
+ Text('网络类型: ${_getNetworkTypeText(_networkStatus!.type)}'),
+ Text('网络质量: ${_getNetworkQualityText(_networkStatus!.quality)}'),
+ Text('计费网络: ${_networkStatus!.isMetered ? "是" : "否"}'),
+ if (_networkStatus!.downloadSpeed != null)
+ Text('下载速度: ${_formatBytes(_networkStatus!.downloadSpeed!.toInt())}/s'),
+ if (_networkStatus!.ping != null) Text('延迟: ${_networkStatus!.ping} ms'),
+ const SizedBox(height: 12),
+ Text(
+ '适合大文件下载: ${_networkStatus!.isSuitableForLargeDownload ? "是" : "否"}',
+ style: TextStyle(
+ color: _networkStatus!.isSuitableForLargeDownload ? Colors.green : Colors.orange,
+ fontWeight: FontWeight.bold,
+ ),
+ ),
+ ],
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ // 下载策略建议
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '下载策略建议',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 12),
+ ...NetworkMonitor.instance.getSuggestedDownloadStrategy().entries.map((e) {
+ return Text('${e.key}: ${e.value}');
+ }),
+ ],
+ ),
+ ),
+ ),
+ ] else
+ const Center(child: CircularProgressIndicator()),
+ ],
+ ),
+ );
+ }
+
+ // 版本管理标签页
+ Widget _buildVersionTab() {
+ final comparator = _plugin.versionComparator;
+
+ return SingleChildScrollView(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '版本比较测试',
+ style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 16),
+
+ // 版本选择
+ Row(
+ children: [
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text('当前版本:'),
+ DropdownButton(
+ value: _currentTestVersion,
+ isExpanded: true,
+ onChanged: (value) {
+ setState(() {
+ _currentTestVersion = value!;
+ });
+ },
+ items: _testVersions.map((v) {
+ return DropdownMenuItem(value: v, child: Text(v));
+ }).toList(),
+ ),
+ ],
+ ),
+ ),
+ const SizedBox(width: 16),
+ Expanded(
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text('远程版本:'),
+ DropdownButton(
+ value: _remoteTestVersion,
+ isExpanded: true,
+ onChanged: (value) {
+ setState(() {
+ _remoteTestVersion = value!;
+ });
+ },
+ items: _testVersions.map((v) {
+ return DropdownMenuItem(value: v, child: Text(v));
+ }).toList(),
+ ),
+ ],
+ ),
+ ),
+ ],
+ ),
+
+ const SizedBox(height: 16),
+
+ // 比较结果
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '比较结果',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 12),
+ Text('需要更新: ${comparator.isUpdateAvailable(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'),
+ Text('更新类型: ${comparator.getVersionDifference(_currentTestVersion, _remoteTestVersion)}'),
+ Text('主要版本更新: ${comparator.isMajorUpdate(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'),
+ Text('次要版本更新: ${comparator.isMinorUpdate(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'),
+ Text('修订版本更新: ${comparator.isPatchUpdate(_currentTestVersion, _remoteTestVersion) ? "是" : "否"}'),
+ ],
+ ),
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ // 版本列表排序
+ Card(
+ child: Padding(
+ padding: const EdgeInsets.all(16),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ const Text(
+ '版本排序',
+ style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 12),
+ Text('升序: ${comparator.sortVersions(_testVersions).join(', ')}'),
+ Text('降序: ${comparator.sortVersions(_testVersions, descending: true).join(', ')}'),
+ Text('最新版本: ${comparator.getLatestVersion(_testVersions)}'),
+ ],
+ ),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+
+ // 检查更新
+ Future _checkUpdate() async {
+ // 模拟服务器地址
+ const url = 'https://api.example.com/check-update';
+
+ final info = await _plugin.checkUpdateSmart(
+ url,
+ forceRefresh: true,
+ cacheDuration: const Duration(hours: 1),
+ );
+
+ if (info == null) {
+ ScaffoldMessenger.of(context).showSnackBar(
+ const SnackBar(content: Text('已是最新版本')),
+ );
+ }
+ }
+
+ // 工具方法
+ String _formatBytes(int bytes) {
+ if (bytes < 1024) return '$bytes B';
+ if (bytes < 1024 * 1024) return '${(bytes / 1024).toStringAsFixed(2)} KB';
+ if (bytes < 1024 * 1024 * 1024) return '${(bytes / (1024 * 1024)).toStringAsFixed(2)} MB';
+ return '${(bytes / (1024 * 1024 * 1024)).toStringAsFixed(2)} GB';
+ }
+
+ String _getDownloadStatusText(DownloadStatus status) {
+ switch (status) {
+ case DownloadStatus.pending:
+ return '等待中';
+ case DownloadStatus.downloading:
+ return '下载中';
+ case DownloadStatus.paused:
+ return '已暂停';
+ case DownloadStatus.completed:
+ return '已完成';
+ case DownloadStatus.failed:
+ return '失败';
+ case DownloadStatus.cancelled:
+ return '已取消';
+ }
+ }
+
+ String _getNetworkTypeText(NetworkType type) {
+ switch (type) {
+ case NetworkType.none:
+ return '无网络';
+ case NetworkType.mobile:
+ return '移动网络';
+ case NetworkType.wifi:
+ return 'WiFi';
+ case NetworkType.ethernet:
+ return '以太网';
+ case NetworkType.bluetooth:
+ return '蓝牙';
+ case NetworkType.vpn:
+ return 'VPN';
+ case NetworkType.other:
+ return '其他';
+ }
+ }
+
+ String _getNetworkQualityText(NetworkQuality quality) {
+ switch (quality) {
+ case NetworkQuality.unknown:
+ return '未知';
+ case NetworkQuality.poor:
+ return '差';
+ case NetworkQuality.moderate:
+ return '中等';
+ case NetworkQuality.good:
+ return '良好';
+ case NetworkQuality.excellent:
+ return '优秀';
+ }
+ }
+
+ String _getVersionStrategyText(VersionCompareStrategy strategy) {
+ switch (strategy) {
+ case VersionCompareStrategy.numeric:
+ return '数字比较';
+ case VersionCompareStrategy.semantic:
+ return '语义化版本';
+ case VersionCompareStrategy.timestamp:
+ return '时间戳';
+ case VersionCompareStrategy.buildNumber:
+ return '构建号';
+ case VersionCompareStrategy.custom:
+ return '自定义';
+ }
+ }
+}
diff --git a/example/lib/main_simple.dart b/example/lib/main_simple.dart
new file mode 100644
index 0000000..c14639f
--- /dev/null
+++ b/example/lib/main_simple.dart
@@ -0,0 +1,165 @@
+import 'package:app_upgrade_plugin/app_upgrade_simple.dart';
+import 'package:flutter/material.dart';
+
+/// 最简单的使用示例
+/// 展示如何用最少的代码实现App升级功能
+void main() {
+ runApp(const MyApp());
+}
+
+class MyApp extends StatelessWidget {
+ const MyApp({super.key});
+
+ @override
+ Widget build(BuildContext context) {
+ return MaterialApp(
+ title: 'App Upgrade Simple Example',
+ theme: ThemeData(
+ primarySwatch: Colors.blue,
+ ),
+ home: const MyHomePage(),
+ );
+ }
+}
+
+class MyHomePage extends StatefulWidget {
+ const MyHomePage({super.key});
+
+ @override
+ State createState() => _MyHomePageState();
+}
+
+class _MyHomePageState extends State {
+ // 模拟的更新检查URL(实际使用时替换为真实的API地址)
+ static const String checkUpdateUrl = 'https://api.example.com/check-update';
+
+ @override
+ void initState() {
+ super.initState();
+
+ // 可选:启动时自动检查更新(静默检查)
+ _checkUpdateOnStart();
+ }
+
+ /// 启动时静默检查更新
+ void _checkUpdateOnStart() async {
+ // 延迟2秒,避免启动时界面还未完全加载
+ await Future.delayed(const Duration(seconds: 2));
+
+ if (mounted) {
+ // 静默检查,有更新才显示对话框
+ final info = await AppUpgradeSimple.instance.checkUpdateSilent(
+ url: checkUpdateUrl,
+ );
+
+ if (info != null && mounted) {
+ // 有更新时显示对话框
+ AppUpgradeSimple.instance.checkUpdate(
+ context: context,
+ url: checkUpdateUrl,
+ showNoUpdateToast: false, // 不显示"已是最新版本"提示
+ autoDownload: false, // 不自动下载,让用户选择
+ );
+ }
+ }
+ }
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ appBar: AppBar(
+ title: const Text('App升级 - 极简示例'),
+ backgroundColor: Theme.of(context).colorScheme.inversePrimary,
+ ),
+ body: Center(
+ child: Column(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ const Icon(
+ Icons.rocket_launch,
+ size: 80,
+ color: Colors.blue,
+ ),
+ const SizedBox(height: 24),
+ const Text(
+ '最简单的App升级实现',
+ style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
+ ),
+ const SizedBox(height: 8),
+ const Text(
+ '一行代码搞定升级功能',
+ style: TextStyle(fontSize: 16, color: Colors.grey),
+ ),
+ const SizedBox(height: 48),
+
+ // 示例1:最简单的使用方式(一行代码)
+ ElevatedButton.icon(
+ onPressed: () {
+ // 🚀 一行代码检查更新!
+ AppUpgradeSimple.instance.checkUpdate(
+ context: context,
+ url: checkUpdateUrl,
+ );
+ },
+ icon: const Icon(Icons.flash_on),
+ label: const Text('一键检查更新(最简单)'),
+ style: ElevatedButton.styleFrom(
+ padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
+ ),
+ ),
+
+ const SizedBox(height: 16),
+
+ const SizedBox(height: 16),
+
+ const SizedBox(height: 48),
+
+ // 使用说明
+ Container(
+ margin: const EdgeInsets.symmetric(horizontal: 32),
+ padding: const EdgeInsets.all(16),
+ decoration: BoxDecoration(
+ color: Colors.blue.shade50,
+ borderRadius: BorderRadius.circular(12),
+ border: Border.all(color: Colors.blue.shade200),
+ ),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: const [
+ Text(
+ '💡 使用提示',
+ style: TextStyle(
+ fontSize: 16,
+ fontWeight: FontWeight.bold,
+ color: Colors.blue,
+ ),
+ ),
+ SizedBox(height: 8),
+ Text('1. 替换 checkUpdateUrl 为您的API地址'),
+ Text('2. API返回格式请参考文档'),
+ Text('3. Android需要配置权限和FileProvider'),
+ Text('4. iOS需要配置App Store地址'),
+ ],
+ ),
+ ),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+/// API返回格式示例:
+/// ```json
+/// {
+/// "hasUpdate": true,
+/// "isForceUpdate": false,
+/// "versionCode": "2",
+/// "versionName": "1.1.0",
+/// "updateContent": "1. 修复已知问题\n2. 优化用户体验",
+/// "downloadUrl": "https://example.com/app-v1.1.0.apk", // Android
+/// "appStoreUrl": "https://apps.apple.com/app/id123456", // iOS
+/// "apkSize": 26214400,
+/// "apkMd5": "abc123def456"
+/// }
+/// ```
diff --git a/example/pubspec.lock b/example/pubspec.lock
new file mode 100644
index 0000000..fabc538
--- /dev/null
+++ b/example/pubspec.lock
@@ -0,0 +1,741 @@
+# Generated by pub
+# See https://dart.dev/tools/pub/glossary#lockfile
+packages:
+ app_upgrade_plugin:
+ dependency: "direct main"
+ description:
+ path: ".."
+ relative: true
+ source: path
+ version: "1.0.0"
+ args:
+ dependency: transitive
+ description:
+ name: args
+ sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.7.0"
+ async:
+ dependency: transitive
+ description:
+ name: async
+ sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.12.0"
+ boolean_selector:
+ dependency: transitive
+ description:
+ name: boolean_selector
+ sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.2"
+ characters:
+ dependency: transitive
+ description:
+ name: characters
+ sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.4.0"
+ clock:
+ dependency: transitive
+ description:
+ name: clock
+ sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.2"
+ collection:
+ dependency: transitive
+ description:
+ name: collection
+ sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.19.1"
+ connectivity_plus:
+ dependency: transitive
+ description:
+ name: connectivity_plus
+ sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.1.5"
+ connectivity_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: connectivity_plus_platform_interface
+ sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.0.1"
+ crypto:
+ dependency: transitive
+ description:
+ name: crypto
+ sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.0.6"
+ cupertino_icons:
+ dependency: "direct main"
+ description:
+ name: cupertino_icons
+ sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.0.8"
+ dbus:
+ dependency: transitive
+ description:
+ name: dbus
+ sha256: "79e0c23480ff85dc68de79e2cd6334add97e48f7f4865d17686dd6ea81a47e8c"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.7.11"
+ device_info_plus:
+ dependency: transitive
+ description:
+ name: device_info_plus
+ sha256: "98f28b42168cc509abc92f88518882fd58061ea372d7999aecc424345c7bff6a"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "11.5.0"
+ device_info_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: device_info_plus_platform_interface
+ sha256: e1ea89119e34903dca74b883d0dd78eb762814f97fb6c76f35e9ff74d261a18f
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "7.0.3"
+ dio:
+ dependency: transitive
+ description:
+ name: dio
+ sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.9.0"
+ dio_web_adapter:
+ dependency: transitive
+ description:
+ name: dio_web_adapter
+ sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.1"
+ fake_async:
+ dependency: transitive
+ description:
+ name: fake_async
+ sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.3.2"
+ ffi:
+ dependency: transitive
+ description:
+ name: ffi
+ sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.4"
+ file:
+ dependency: transitive
+ description:
+ name: file
+ sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "7.0.1"
+ flutter:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_driver:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_lints:
+ dependency: "direct dev"
+ description:
+ name: flutter_lints
+ sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.0.0"
+ flutter_local_notifications:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications
+ sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "18.0.1"
+ flutter_local_notifications_linux:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_linux
+ sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.0.0"
+ flutter_local_notifications_platform_interface:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_platform_interface
+ sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "8.0.0"
+ flutter_localizations:
+ dependency: "direct main"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ flutter_web_plugins:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ fluttertoast:
+ dependency: transitive
+ description:
+ name: fluttertoast
+ sha256: "25e51620424d92d3db3832464774a6143b5053f15e382d8ffbfd40b6e795dcf1"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "8.2.12"
+ fuchsia_remote_debug_protocol:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ http:
+ dependency: transitive
+ description:
+ name: http
+ sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.5.0"
+ http_parser:
+ dependency: transitive
+ description:
+ name: http_parser
+ sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "4.1.2"
+ integration_test:
+ dependency: "direct dev"
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ intl:
+ dependency: "direct main"
+ description:
+ name: intl
+ sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.19.0"
+ leak_tracker:
+ dependency: transitive
+ description:
+ name: leak_tracker
+ sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "10.0.8"
+ leak_tracker_flutter_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_flutter_testing
+ sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.0.9"
+ leak_tracker_testing:
+ dependency: transitive
+ description:
+ name: leak_tracker_testing
+ sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.0.1"
+ lints:
+ dependency: transitive
+ description:
+ name: lints
+ sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.1.1"
+ matcher:
+ dependency: transitive
+ description:
+ name: matcher
+ sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.12.17"
+ material_color_utilities:
+ dependency: transitive
+ description:
+ name: material_color_utilities
+ sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.11.1"
+ meta:
+ dependency: transitive
+ description:
+ name: meta
+ sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.16.0"
+ mime:
+ dependency: transitive
+ description:
+ name: mime
+ sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.0.0"
+ nm:
+ dependency: transitive
+ description:
+ name: nm
+ sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.5.0"
+ package_info_plus:
+ dependency: transitive
+ description:
+ name: package_info_plus
+ sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "8.3.1"
+ package_info_plus_platform_interface:
+ dependency: transitive
+ description:
+ name: package_info_plus_platform_interface
+ sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.2.1"
+ path:
+ dependency: transitive
+ description:
+ name: path
+ sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.9.1"
+ path_provider:
+ dependency: transitive
+ description:
+ name: path_provider
+ sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.5"
+ path_provider_android:
+ dependency: transitive
+ description:
+ name: path_provider_android
+ sha256: "993381400e94d18469750e5b9dcb8206f15bc09f9da86b9e44a9b0092a0066db"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.2.18"
+ path_provider_foundation:
+ dependency: transitive
+ description:
+ name: path_provider_foundation
+ sha256: "16eef174aacb07e09c351502740fa6254c165757638eba1e9116b0a781201bbd"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.2"
+ path_provider_linux:
+ dependency: transitive
+ description:
+ name: path_provider_linux
+ sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.2.1"
+ path_provider_platform_interface:
+ dependency: transitive
+ description:
+ name: path_provider_platform_interface
+ sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.2"
+ path_provider_windows:
+ dependency: transitive
+ description:
+ name: path_provider_windows
+ sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.3.0"
+ permission_handler:
+ dependency: "direct main"
+ description:
+ name: permission_handler
+ sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "11.4.0"
+ permission_handler_android:
+ dependency: transitive
+ description:
+ name: permission_handler_android
+ sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "12.1.0"
+ permission_handler_apple:
+ dependency: transitive
+ description:
+ name: permission_handler_apple
+ sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "9.4.7"
+ permission_handler_html:
+ dependency: transitive
+ description:
+ name: permission_handler_html
+ sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.1.3+5"
+ permission_handler_platform_interface:
+ dependency: transitive
+ description:
+ name: permission_handler_platform_interface
+ sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "4.3.0"
+ permission_handler_windows:
+ dependency: transitive
+ description:
+ name: permission_handler_windows
+ sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.2.1"
+ petitparser:
+ dependency: transitive
+ description:
+ name: petitparser
+ sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.1.0"
+ platform:
+ dependency: transitive
+ description:
+ name: platform
+ sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.1.6"
+ plugin_platform_interface:
+ dependency: transitive
+ description:
+ name: plugin_platform_interface
+ sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.8"
+ process:
+ dependency: transitive
+ description:
+ name: process
+ sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.0.3"
+ shared_preferences:
+ dependency: transitive
+ description:
+ name: shared_preferences
+ sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.5.3"
+ shared_preferences_android:
+ dependency: transitive
+ description:
+ name: shared_preferences_android
+ sha256: a2608114b1ffdcbc9c120eb71a0e207c71da56202852d4aab8a5e30a82269e74
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.12"
+ shared_preferences_foundation:
+ dependency: transitive
+ description:
+ name: shared_preferences_foundation
+ sha256: "6a52cfcdaeac77cad8c97b539ff688ccfc458c007b4db12be584fbe5c0e49e03"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.5.4"
+ shared_preferences_linux:
+ dependency: transitive
+ description:
+ name: shared_preferences_linux
+ sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_platform_interface:
+ dependency: transitive
+ description:
+ name: shared_preferences_platform_interface
+ sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.1"
+ shared_preferences_web:
+ dependency: transitive
+ description:
+ name: shared_preferences_web
+ sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.3"
+ shared_preferences_windows:
+ dependency: transitive
+ description:
+ name: shared_preferences_windows
+ sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.1"
+ sky_engine:
+ dependency: transitive
+ description: flutter
+ source: sdk
+ version: "0.0.0"
+ source_span:
+ dependency: transitive
+ description:
+ name: source_span
+ sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.10.1"
+ stack_trace:
+ dependency: transitive
+ description:
+ name: stack_trace
+ sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.12.1"
+ stream_channel:
+ dependency: transitive
+ description:
+ name: stream_channel
+ sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.4"
+ string_scanner:
+ dependency: transitive
+ description:
+ name: string_scanner
+ sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.4.1"
+ sync_http:
+ dependency: transitive
+ description:
+ name: sync_http
+ sha256: "7f0cd72eca000d2e026bcd6f990b81d0ca06022ef4e32fb257b30d3d1014a961"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.3.1"
+ term_glyph:
+ dependency: transitive
+ description:
+ name: term_glyph
+ sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.2.2"
+ test_api:
+ dependency: transitive
+ description:
+ name: test_api
+ sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.7.4"
+ timezone:
+ dependency: transitive
+ description:
+ name: timezone
+ sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "0.10.1"
+ typed_data:
+ dependency: transitive
+ description:
+ name: typed_data
+ sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.4.0"
+ url_launcher:
+ dependency: transitive
+ description:
+ name: url_launcher
+ sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.3.2"
+ url_launcher_android:
+ dependency: transitive
+ description:
+ name: url_launcher_android
+ sha256: "81777b08c498a292d93ff2feead633174c386291e35612f8da438d6e92c4447e"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.3.20"
+ url_launcher_ios:
+ dependency: transitive
+ description:
+ name: url_launcher_ios
+ sha256: d80b3f567a617cb923546034cc94bfe44eb15f989fe670b37f26abdb9d939cb7
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.3.4"
+ url_launcher_linux:
+ dependency: transitive
+ description:
+ name: url_launcher_linux
+ sha256: "4e9ba368772369e3e08f231d2301b4ef72b9ff87c31192ef471b380ef29a4935"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.2.1"
+ url_launcher_macos:
+ dependency: transitive
+ description:
+ name: url_launcher_macos
+ sha256: c043a77d6600ac9c38300567f33ef12b0ef4f4783a2c1f00231d2b1941fea13f
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.2.3"
+ url_launcher_platform_interface:
+ dependency: transitive
+ description:
+ name: url_launcher_platform_interface
+ sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.3.2"
+ url_launcher_web:
+ dependency: transitive
+ description:
+ name: url_launcher_web
+ sha256: "4bd2b7b4dc4d4d0b94e5babfffbca8eac1a126c7f3d6ecbc1a11013faa3abba2"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.4.1"
+ url_launcher_windows:
+ dependency: transitive
+ description:
+ name: url_launcher_windows
+ sha256: "3284b6d2ac454cf34f114e1d3319866fdd1e19cdc329999057e44ffe936cfa77"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.1.4"
+ vector_math:
+ dependency: transitive
+ description:
+ name: vector_math
+ sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.4"
+ vm_service:
+ dependency: transitive
+ description:
+ name: vm_service
+ sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "14.3.1"
+ web:
+ dependency: transitive
+ description:
+ name: web
+ sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.1"
+ webdriver:
+ dependency: transitive
+ description:
+ name: webdriver
+ sha256: "3d773670966f02a646319410766d3b5e1037efb7f07cc68f844d5e06cd4d61c8"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "3.0.4"
+ win32:
+ dependency: transitive
+ description:
+ name: win32
+ sha256: "329edf97fdd893e0f1e3b9e88d6a0e627128cc17cc316a8d67fda8f1451178ba"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "5.13.0"
+ win32_registry:
+ dependency: transitive
+ description:
+ name: win32_registry
+ sha256: "6f1b564492d0147b330dd794fee8f512cec4977957f310f9951b5f9d83618dae"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "2.1.0"
+ xdg_directories:
+ dependency: transitive
+ description:
+ name: xdg_directories
+ sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "1.1.0"
+ xml:
+ dependency: transitive
+ description:
+ name: xml
+ sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
+ url: "https://pub.flutter-io.cn"
+ source: hosted
+ version: "6.5.0"
+sdks:
+ dart: ">=3.7.0 <4.0.0"
+ flutter: ">=3.29.0"
diff --git a/example/pubspec.yaml b/example/pubspec.yaml
new file mode 100644
index 0000000..69ac2e0
--- /dev/null
+++ b/example/pubspec.yaml
@@ -0,0 +1,91 @@
+name: app_upgrade_plugin_example
+description: "Demonstrates how to use the app_upgrade_plugin plugin."
+# The following line prevents the package from being accidentally published to
+# pub.dev using `flutter pub publish`. This is preferred for private packages.
+publish_to: 'none' # Remove this line if you wish to publish to pub.dev
+
+environment:
+ sdk: '>=3.0.0 <4.0.0'
+
+# Dependencies specify other packages that your package needs in order to work.
+# To automatically upgrade your package dependencies to the latest versions
+# consider running `flutter pub upgrade --major-versions`. Alternatively,
+# dependencies can be manually updated by changing the version numbers below to
+# the latest version available on pub.dev. To see which dependencies have newer
+# versions available, run `flutter pub outdated`.
+dependencies:
+ flutter:
+ sdk: flutter
+ flutter_localizations:
+ sdk: flutter
+
+ intl: ^0.19.0
+
+
+ app_upgrade_plugin:
+ # When depending on this package from a real application you should use:
+ # app_upgrade_plugin: ^x.y.z
+ # See https://dart.dev/tools/pub/dependencies#version-constraints
+ # The example app is bundled with the plugin so we use a path dependency on
+ # the parent directory to use the current plugin's version.
+ path: ../
+
+ # The following adds the Cupertino Icons font to your application.
+ # Use with the CupertinoIcons class for iOS style icons.
+ cupertino_icons: ^1.0.8
+ permission_handler: ^11.3.1
+
+dev_dependencies:
+ integration_test:
+ sdk: flutter
+ flutter_test:
+ sdk: flutter
+
+ # The "flutter_lints" package below contains a set of recommended lints to
+ # encourage good coding practices. The lint set provided by the package is
+ # activated in the `analysis_options.yaml` file located at the root of your
+ # package. See that file for information about deactivating specific lint
+ # rules and activating additional ones.
+ flutter_lints: ^5.0.0
+
+# For information on the generic Dart part of this file, see the
+# following page: https://dart.dev/tools/pub/pubspec
+
+# The following section is specific to Flutter packages.
+flutter:
+
+ # The following line ensures that the Material Icons font is
+ # included with your application, so that you can use the icons in
+ # the material Icons class.
+ uses-material-design: true
+
+ # To add assets to your application, add an assets section, like this:
+ # assets:
+ # - images/a_dot_burr.jpeg
+ # - images/a_dot_ham.jpeg
+
+ # An image asset can refer to one or more resolution-specific "variants", see
+ # https://flutter.dev/to/resolution-aware-images
+
+ # For details regarding adding assets from package dependencies, see
+ # https://flutter.dev/to/asset-from-package
+
+ # To add custom fonts to your application, add a fonts section here,
+ # in this "flutter" section. Each entry in this list should have a
+ # "family" key with the font family name, and a "fonts" key with a
+ # list giving the asset and other descriptors for the font. For
+ # example:
+ # fonts:
+ # - family: Schyler
+ # fonts:
+ # - asset: fonts/Schyler-Regular.ttf
+ # - asset: fonts/Schyler-Italic.ttf
+ # style: italic
+ # - family: Trajan Pro
+ # fonts:
+ # - asset: fonts/TrajanPro.ttf
+ # - asset: fonts/TrajanPro_Bold.ttf
+ # weight: 700
+ #
+ # For details regarding fonts from package dependencies,
+ # see https://flutter.dev/to/font-from-package
diff --git a/example/test/widget_test.dart b/example/test/widget_test.dart
new file mode 100644
index 0000000..0fcabc0
--- /dev/null
+++ b/example/test/widget_test.dart
@@ -0,0 +1,160 @@
+// This is a basic Flutter widget test.
+//
+// To perform an interaction with a widget in your test, use the WidgetTester
+// utility in the flutter_test package. For example, you can send tap and scroll
+// gestures. You can also use WidgetTester to find child widgets in the widget
+// tree, read text, and verify that the values of widget properties are correct.
+
+import 'package:app_upgrade_plugin/app_upgrade_plugin.dart';
+import 'package:app_upgrade_plugin/app_upgrade_plugin_enhanced.dart';
+import 'package:app_upgrade_plugin_example/main.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+class MockAppUpgradePluginEnhanced implements AppUpgradePluginEnhanced {
+ // 在这里 mock AppUpgradePluginEnhanced 的所有方法和属性
+ // ...
+ // 你可以根据测试需要返回特定的值
+
+ @override
+ void addDownloadCallback(DownloadCallback callback) {}
+ @override
+ void addErrorCallback(ErrorCallback callback) {}
+ @override
+ void addUpgradeCallback(UpgradeCallback callback) {}
+ @override
+ Future checkApkExists(String version, String? md5) async => false;
+ @override
+ Future checkUpdateSmart(String url,
+ {Map? params, bool forceRefresh = false, Duration? cacheDuration}) async =>
+ null;
+ @override
+ Future clearCache() async {}
+ @override
+ UpgradeConfig get config => UpgradeConfig.instance;
+ @override
+ void configure(
+ {bool? debugMode,
+ int? checkIntervalHours,
+ bool? autoCheck,
+ bool? wifiOnly,
+ int? downloadTimeout,
+ int? connectTimeout,
+ int? maxRetryCount,
+ bool? supportBreakpoint,
+ bool? verifyIntegrity,
+ VersionCompareStrategy? versionStrategy,
+ Map? customHeaders}) {}
+ @override
+ void dispose() {}
+ @override
+ Future downloadApkSmart(String url,
+ {String? versionName,
+ Function(DownloadProgress p1)? onProgress,
+ String? savePath,
+ String? md5,
+ String? sha256,
+ bool resumeIfExists = true}) async =>
+ null;
+ @override
+ Future