补充提交demo

This commit is contained in:
DESKTOP-I3JPKHK\wy 2025-11-28 14:49:54 +08:00
parent 4e64bc8196
commit 7bc09696af
4 changed files with 870 additions and 155 deletions

View File

@ -1,9 +1,9 @@
import 'dart:async';
import 'dart:io';
import 'package:yx_app_upgrade_flutter/app_upgrade_plugin.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:yx_app_upgrade_flutter/app_upgrade_plugin.dart';
void main() {
// Flutter绑定已初始化
@ -50,13 +50,9 @@ class HomePage extends StatefulWidget {
}
class _HomePageState extends State<HomePage> {
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((_) {
@ -70,46 +66,78 @@ class _HomePageState extends State<HomePage> {
await AppUpgradeSimple.instance.checkUpdate(
context: context,
future: () async {
//
// 使API AppUpgradeVersion
// final response = await myApi.checkVersion();
// return AppUpgradeVersion(...);
final updateAppEvent = await _getUpdateAppEvent();
print("获取最新版本: $updateAppEvent");
if (updateAppEvent == null) return null;
//
return AppUpgradeVersion(
versionName: '1.0.1',
versionBuildNumber: 11,
isForce: true,
updateContent: '修复了一些Bug\n优化了用户体验',
downloadUrl: 'https://example.com/app.apk',
supportedMethods: [AppUpgradeMethod.browser, AppUpgradeMethod.inApp, AppUpgradeMethod.market],
);
return _convertToAppUpgradeVersion(updateAppEvent);
},
showNoUpdateToast: false, // "已是最新版本"
autoInstall: true,
onComplete: (bool val) {
print("更新插件执行完成....: $val");
},
config: UpgradeConfig.development,
// showNoUpdateToast: false,
// autoDownload: false,
// autoInstall: true,
);
debugPrint('=== 网络功能测试完成 ===');
}
// Platform messages are asynchronous, so we initialize in an async method.
Future<void> 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.';
}
///
Future<Map<String, dynamic>?> _getUpdateAppEvent() async {
//
// String deviceInfo;
/// 1:,2:IOS
int? deviceType = Platform.isAndroid
? 1
: Platform.isIOS
? 2
: null;
if (deviceType == null) return null;
// 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;
///
return {
"id": 708608950206533,
"version": 307,
"versionName": "1.0.9",
"remark": """
****``\n
****__级可集中查看下级``__\n
****__在[]__\n""",
"imageBase": null,
"updatetype": 1,
"isActive": 1,
"fileid": 708608942190661,
"fileName": "app-release(62).apk",
"filePath": AppUpgradeVersion.getAppStoreByUrl('6747421483'),
// "filePath":
// "https://quanxue-oa.oss-cn-chengdu.aliyuncs.com/20251106/1762422545956.apk.1",
"fileSize": 144214,
"isforce": true
};
}
setState(() {
_platformVersion = platformVersion;
});
/// UpdateappResult AppUpgradeVersion
AppUpgradeVersion _convertToAppUpgradeVersion(Map<String, dynamic> model) {
// KB
final int? apkSizeBytes = model['fileSize'] != null ? model['fileSize'] * 1024 : null;
final filePath = model['filePath'];
final appUpgradeVersion = AppUpgradeVersion(
versionName: model['versionName'],
versionBuildNumber: model['version'],
isForce: model['isforce'],
updateContent: model['remark'],
downloadUrl: filePath,
appStoreUrl: filePath,
apkSize: apkSizeBytes,
apkMd5: null, // UpdateappResult MD5
// appMarkets: null, // UpdateappResult
supportedMethods: [AppUpgradeMethod.browser, AppUpgradeMethod.inApp, AppUpgradeMethod.market],
// appMarkets: [
// AppMarket.huawei,
// // AppMarket.xiaomi,
// ],
);
return appUpgradeVersion;
}
@override
@ -123,24 +151,30 @@ class _HomePageState extends State<HomePage> {
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('Running on: $_platformVersion\n'),
const SizedBox(height: 16),
ElevatedButton(
onPressed: () {
AppUpgradeSimple.instance.checkUpdate(
context: context,
onPressed: () async {
// 1. UI
final upgradeInfo = await AppUpgradeSimple.instance.silentCheckUpdate(
future: () async {
//
return AppUpgradeVersion(
versionName: '1.0.1',
versionBuildNumber: 11,
updateContent: '这是一个新版本',
downloadUrl: 'https://example.com/app.apk',
);
final updateAppEvent = await _getUpdateAppEvent();
print("获取最新版本: $updateAppEvent");
if (updateAppEvent == null) return null;
return _convertToAppUpgradeVersion(updateAppEvent);
},
showNoUpdateToast: false, // "已是最新版本"
autoInstall: false,
);
// 2.
if (upgradeInfo != null && upgradeInfo.hasUpdate) {
//
print('发现新版本: ${upgradeInfo.versionName}');
//
AppUpgradeSimple.instance.showPreparedUpgrade(
context: context,
info: upgradeInfo, // info
);
}
},
child: const Text('检查更新'),
),

557
example/pubspec.lock Normal file
View File

@ -0,0 +1,557 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
async:
dependency: transitive
description:
name: async
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.13.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"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.0.8"
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: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.3.3"
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_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"
fuchsia_remote_debug_protocol:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
http:
dependency: transitive
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.6.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: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.20.2"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.flutter-io.cn"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.0.2"
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"
package_info_plus:
dependency: transitive
description:
name: package_info_plus
sha256: f69da0d3189a4b4ceaeb1a3defb0f329b3b352517f52bed4290f83d4f06bc08d
url: "https://pub.flutter-io.cn"
source: hosted
version: "9.0.0"
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: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.22"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "6d13aece7b3f5c5a9731eaf553ff9dcbc2eff41087fd2df587fd0fed9a3eb0c4"
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.5.1"
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: transitive
description:
name: permission_handler
sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1
url: "https://pub.flutter-io.cn"
source: hosted
version: "12.0.1"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6"
url: "https://pub.flutter-io.cn"
source: hosted
version: "13.0.1"
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"
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: c6248e4526673988586e8c00bb22a49210c258dc91df5227d5da9748ecf79744
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.0.5"
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: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00"
url: "https://pub.flutter-io.cn"
source: hosted
version: "0.7.6"
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: "767344bf3063897b5cf0db830e94f904528e6dd50a6dfaf839f0abf509009611"
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.28"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: cfde38aa257dae62ffe79c87fab20165dfdf6988c1d31b58ebf59b9106062aad
url: "https://pub.flutter-io.cn"
source: hosted
version: "6.3.6"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.2"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.2.5"
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: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.5"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.flutter-io.cn"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
url: "https://pub.flutter-io.cn"
source: hosted
version: "15.0.2"
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: "2f3a14ca026957870cfd9c635b83507e0e51d8091568e90129fbf805aba7cade"
url: "https://pub.flutter-io.cn"
source: hosted
version: "3.1.0"
win32:
dependency: transitive
description:
name: win32
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
url: "https://pub.flutter-io.cn"
source: hosted
version: "5.15.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.flutter-io.cn"
source: hosted
version: "1.1.0"
yx_app_upgrade_flutter:
dependency: "direct main"
description:
path: ".."
relative: true
source: path
version: "1.0.0"
sdks:
dart: ">=3.9.0 <4.0.0"
flutter: ">=3.35.0"

View File

@ -32,7 +32,6 @@ dependencies:
# 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:

View File

@ -5,104 +5,97 @@
// 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:yx_app_upgrade_flutter/app_upgrade_plugin.dart';
import 'package:yx_app_upgrade_flutter/app_upgrade_plugin_enhanced.dart';
import 'package:app_upgrade_plugin_example/main.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:yx_app_upgrade_flutter/app_upgrade_plugin.dart';
import 'package:yx_app_upgrade_flutter/app_upgrade_plugin_platform_interface.dart';
class MockAppUpgradePluginEnhanced implements AppUpgradePluginEnhanced {
// mock AppUpgradePluginEnhanced
// ...
//
/// Mock implementation of AppUpgradePluginPlatform for testing
class MockAppUpgradePluginPlatform extends AppUpgradePluginPlatform {
MockAppUpgradePluginPlatform() : super();
@override
void addDownloadCallback(DownloadCallback callback) {}
Future<String?> getPlatformVersion() async => 'Mock Platform 1.0.0';
@override
void addErrorCallback(ErrorCallback callback) {}
Future<int?> getAndroidSdkVersion() async => 33;
@override
void addUpgradeCallback(UpgradeCallback callback) {}
Future<bool> openInstallPermissionSettings() async => true;
@override
Future<Map<String, dynamic>?> getDeviceInfo() async => {
'manufacturer': 'Mock Manufacturer',
'model': 'Mock Model',
'androidVersion': '13',
};
@override
Future<List<String>> getInstalledMarkets() async => ['huawei', 'xiaomi'];
@override
void configureHttp(HttpConfig config) {
// Mock implementation
}
@override
Future<Map<String, String>> getAppInfo() async => {
'appName': 'app_upgrade_plugin_example',
'packageName': 'com.example.app_upgrade_plugin_example',
'version': '1.0.0',
'buildNumber': '1',
};
@override
Future<UpgradeInfo?> checkUpdate(String url, {Map<String, dynamic>? params}) async {
// Return null to simulate no update available
return null;
}
@override
Future<String?> downloadApk(
String url, {
Function(DownloadProgress)? onProgress,
String? savePath,
}) async {
// Simulate download progress
if (onProgress != null) {
onProgress(DownloadProgress(received: 50, total: 100));
await Future.delayed(const Duration(milliseconds: 10));
onProgress(DownloadProgress(received: 100, total: 100));
}
return '/mock/path/to/downloaded.apk';
}
@override
Future<bool> installApk(String filePath) async => true;
@override
Future<bool> installApkWithSystemFlow(String filePath) async => true;
@override
Future<bool> installApkWithConfig(String filePath, InstallConfig config) async => true;
@override
Future<bool> goToAppStore(String url, {required BuildContext context}) async => true;
@override
Future<String?> getDownloadPath({bool checkPermission = true}) async => '/mock/download/path';
@override
Future<bool> checkApkExists(String version, String? md5) async => false;
@override
Future<UpgradeInfo?> checkUpdateSmart(String url,
{Map<String, dynamic>? params, bool forceRefresh = false, Duration? cacheDuration}) async =>
null;
@override
Future<void> 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<String, String>? customHeaders}) {}
@override
void dispose() {}
@override
Future<String?> downloadApkSmart(String url,
{String? versionName,
Function(DownloadProgress p1)? onProgress,
String? savePath,
String? md5,
String? sha256,
bool resumeIfExists = true}) async =>
null;
@override
Future<Map<String, String>> getAppInfo() async => {};
@override
Future<Map<String, dynamic>> getCacheStats() async => {};
@override
DownloadTask? getCurrentDownloadTask() => null;
@override
Future<String?> getPlatformVersion() async => 'mock';
@override
Future<bool> goToAppStore(String url) async => false;
@override
Future<bool> installApkSmart(String filePath) async => false;
@override
NetworkStatus? get networkStatus => NetworkStatus(
type: NetworkType.wifi,
quality: NetworkQuality.good,
isConnected: true,
isMetered: false,
);
@override
void pauseDownload() {}
@override
void removeDownloadCallback(DownloadCallback callback) {}
@override
void removeErrorCallback(ErrorCallback callback) {}
@override
void removeUpgradeCallback(UpgradeCallback callback) {}
@override
Future<bool> resumeDownload() async => false;
@override
Future<bool> retryDownload() async => false;
@override
Future<void> refreshNetworkStatus() async {}
@override
VersionComparator get versionComparator => VersionComparator();
@override
void cancelDownload() {}
}
void main() {
//
// Ensure test bindings are initialized
TestWidgetsFlutterBinding.ensureInitialized();
setUp(() {
// mock mock
// Set up mock method channel handlers for platform channels
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
const MethodChannel('plugins.flutter.io/package_info'),
const MethodChannel('plugins.flutter.io/package_info_plus'),
(MethodCall methodCall) async {
if (methodCall.method == 'getAll') {
return <String, dynamic>{
@ -116,45 +109,177 @@ void main() {
},
);
// Mock local notifications channel
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
const MethodChannel('dexterous.com/flutter/local_notifications'),
(MethodCall methodCall) async => null, // null
(MethodCall methodCall) async => null,
);
// Mock connectivity channel
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
const MethodChannel('dev.flutter.plugins/connectivity'),
(MethodCall methodCall) async {
if (methodCall.method == 'check') {
return 'wifi'; // WiFi
return 'wifi';
}
return null;
},
);
// Set mock platform implementation
AppUpgradePluginPlatform.instance = MockAppUpgradePluginPlatform();
});
testWidgets('Verify Platform version', (WidgetTester tester) async {
// 使 Mock enhanced plugin
AppUpgradeSimple.instance = AppUpgradeSimple.private(plugin: MockAppUpgradePluginEnhanced());
await tester.pumpWidget(const MyApp());
// UI内容
expect(find.byType(MyApp), findsOneWidget);
tearDown(() {
// Clean up method channel handlers
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
const MethodChannel('plugins.flutter.io/package_info_plus'),
null,
);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
const MethodChannel('dexterous.com/flutter/local_notifications'),
null,
);
TestDefaultBinaryMessengerBinding.instance.defaultBinaryMessenger.setMockMethodCallHandler(
const MethodChannel('dev.flutter.plugins/connectivity'),
null,
);
});
testWidgets('Verify Network Status Detection', (WidgetTester tester) async {
// 使 Mock enhanced plugin
final mockPlugin = MockAppUpgradePluginEnhanced();
AppUpgradeSimple.instance = AppUpgradeSimple.private(plugin: mockPlugin);
group('App Upgrade Plugin Widget Tests', () {
testWidgets('App should launch successfully', (WidgetTester tester) async {
// Build the app
await tester.pumpWidget(const MyApp());
await tester.pumpWidget(const MyApp());
// Verify that the app widget is present
expect(find.byType(MyApp), findsOneWidget);
expect(find.byType(HomePage), findsOneWidget);
//
final networkStatus = mockPlugin.networkStatus;
// Verify that the app bar is present
expect(find.text('App Upgrade Plugin 示例'), findsOneWidget);
expect(networkStatus, isNotNull);
expect(networkStatus!.isConnected, isTrue);
expect(networkStatus.type, equals(NetworkType.wifi));
expect(networkStatus.isMetered, isFalse);
// Verify that the check update button is present
expect(find.text('检查更新'), findsOneWidget);
});
testWidgets('App should display home page correctly', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
// Verify UI elements
expect(find.byType(Scaffold), findsOneWidget);
expect(find.byType(AppBar), findsOneWidget);
expect(find.byType(ElevatedButton), findsOneWidget);
});
testWidgets('Check update button should be tappable', (WidgetTester tester) async {
await tester.pumpWidget(const MyApp());
await tester.pumpAndSettle();
// Find and tap the check update button
final button = find.text('检查更新');
expect(button, findsOneWidget);
// Tap the button
await tester.tap(button);
await tester.pump();
// Button should still be present after tap
expect(button, findsOneWidget);
});
});
group('App Upgrade Plugin Platform Tests', () {
test('getPlatformVersion should return mock version', () async {
final platform = AppUpgradePluginPlatform.instance;
final version = await platform.getPlatformVersion();
expect(version, isNotNull);
expect(version, equals('Mock Platform 1.0.0'));
});
test('getAppInfo should return mock app info', () async {
final platform = AppUpgradePluginPlatform.instance;
final appInfo = await platform.getAppInfo();
expect(appInfo, isNotNull);
expect(appInfo['appName'], equals('app_upgrade_plugin_example'));
expect(appInfo['version'], equals('1.0.0'));
});
test('checkUpdate should return null when no update available', () async {
final platform = AppUpgradePluginPlatform.instance;
final upgradeInfo = await platform.checkUpdate('https://example.com/check');
expect(upgradeInfo, isNull);
});
test('getDownloadPath should return mock path', () async {
final platform = AppUpgradePluginPlatform.instance;
final path = await platform.getDownloadPath();
expect(path, isNotNull);
expect(path, equals('/mock/download/path'));
});
test('checkApkExists should return false for non-existent APK', () async {
final platform = AppUpgradePluginPlatform.instance;
final exists = await platform.checkApkExists('1.0.0', null);
expect(exists, isFalse);
});
});
group('AppUpgradeVersion Model Tests', () {
test('AppUpgradeVersion should be created correctly', () {
final version = AppUpgradeVersion(
versionName: '1.0.1',
versionBuildNumber: 2,
updateContent: 'Test update content',
downloadUrl: 'https://example.com/app.apk',
isForce: false,
);
expect(version.versionName, equals('1.0.1'));
expect(version.versionBuildNumber, equals(2));
expect(version.updateContent, equals('Test update content'));
expect(version.downloadUrl, equals('https://example.com/app.apk'));
expect(version.isForce, isFalse);
});
test('AppUpgradeVersion with force update should work', () {
final version = AppUpgradeVersion(
versionName: '1.0.1',
versionBuildNumber: 2,
updateContent: 'Force update',
isForce: true,
);
expect(version.isForce, isTrue);
});
test('AppUpgradeVersion.getAppStoreByUrl should generate correct URL', () {
final url = AppUpgradeVersion.getAppStoreByUrl('123456');
expect(url, equals('https://apps.apple.com/cn/app/123456'));
});
test('AppUpgradeVersion.getAppStoreByItunes should generate correct URL', () {
final url = AppUpgradeVersion.getAppStoreByItunes('123456');
expect(url, equals('itms-apps://itunes.apple.com/app/id123456'));
});
});
group('DownloadProgress Model Tests', () {
test('DownloadProgress should calculate progress correctly', () {
final progress = DownloadProgress(received: 50, total: 100);
expect(progress.progress, equals(0.5));
expect(progress.percentage, equals(50));
});
test('DownloadProgress with zero total should handle gracefully', () {
final progress = DownloadProgress(received: 0, total: 0);
expect(progress.progress, equals(0.0));
expect(progress.percentage, equals(0));
});
test('DownloadProgress should calculate percentage correctly', () {
final progress = DownloadProgress(received: 75, total: 100);
expect(progress.percentage, equals(75));
});
});
}