应用更新
This commit is contained in:
@@ -12,6 +12,8 @@ PODS:
|
|||||||
- device_info_plus (0.0.1):
|
- device_info_plus (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- Flutter (1.0.0)
|
- Flutter (1.0.0)
|
||||||
|
- flutter_app_update (0.0.1):
|
||||||
|
- Flutter
|
||||||
- flutter_inappwebview_ios (0.0.1):
|
- flutter_inappwebview_ios (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
- flutter_inappwebview_ios/Core (= 0.0.1)
|
- flutter_inappwebview_ios/Core (= 0.0.1)
|
||||||
@@ -50,6 +52,7 @@ DEPENDENCIES:
|
|||||||
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
|
||||||
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
- device_info_plus (from `.symlinks/plugins/device_info_plus/ios`)
|
||||||
- Flutter (from `Flutter`)
|
- Flutter (from `Flutter`)
|
||||||
|
- flutter_app_update (from `.symlinks/plugins/flutter_app_update/ios`)
|
||||||
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
|
||||||
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
- flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`)
|
||||||
- flutter_pdfview (from `.symlinks/plugins/flutter_pdfview/ios`)
|
- flutter_pdfview (from `.symlinks/plugins/flutter_pdfview/ios`)
|
||||||
@@ -80,6 +83,8 @@ EXTERNAL SOURCES:
|
|||||||
:path: ".symlinks/plugins/device_info_plus/ios"
|
:path: ".symlinks/plugins/device_info_plus/ios"
|
||||||
Flutter:
|
Flutter:
|
||||||
:path: Flutter
|
:path: Flutter
|
||||||
|
flutter_app_update:
|
||||||
|
:path: ".symlinks/plugins/flutter_app_update/ios"
|
||||||
flutter_inappwebview_ios:
|
flutter_inappwebview_ios:
|
||||||
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
|
||||||
flutter_native_splash:
|
flutter_native_splash:
|
||||||
@@ -111,6 +116,7 @@ SPEC CHECKSUMS:
|
|||||||
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
|
||||||
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
device_info_plus: 71ffc6ab7634ade6267c7a93088ed7e4f74e5896
|
||||||
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
|
||||||
|
flutter_app_update: 816fdb2e30e4832a7c45e3f108d391c42ef040a9
|
||||||
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
|
||||||
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
flutter_native_splash: c32d145d68aeda5502d5f543ee38c192065986cf
|
||||||
flutter_pdfview: 32bf27bda6fd85b9dd2c09628a824df5081246cf
|
flutter_pdfview: 32bf27bda6fd85b9dd2c09628a824df5081246cf
|
||||||
|
|||||||
@@ -2,8 +2,11 @@ import 'dart:io';
|
|||||||
|
|
||||||
import 'package:aliyun_push_flutter/aliyun_push_flutter.dart';
|
import 'package:aliyun_push_flutter/aliyun_push_flutter.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_app_update/flutter_app_update.dart';
|
||||||
|
import 'package:flutter_app_update/result_model.dart';
|
||||||
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
import 'package:flutter_native_splash/flutter_native_splash.dart';
|
||||||
import 'package:getx_scaffold/getx_scaffold.dart';
|
import 'package:getx_scaffold/getx_scaffold.dart';
|
||||||
|
import 'package:ln_jq_app/common/model/base_model.dart';
|
||||||
import 'package:ln_jq_app/common/styles/theme.dart';
|
import 'package:ln_jq_app/common/styles/theme.dart';
|
||||||
import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/pages/b_page/base_widgets/view.dart';
|
||||||
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
import 'package:ln_jq_app/pages/c_page/base_widgets/view.dart';
|
||||||
@@ -20,107 +23,195 @@ class HomeController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
final _aliyunPush = AliyunPushFlutter();
|
final _aliyunPush = AliyunPushFlutter();
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool get listenLifecycleEvent => true;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void onInit() {
|
void onInit() {
|
||||||
super.onInit();
|
super.onInit();
|
||||||
initAliyunPush();
|
initAliyunPush();
|
||||||
addPushCallback();
|
addPushCallback();
|
||||||
FlutterNativeSplash.remove();
|
FlutterNativeSplash.remove();
|
||||||
|
log('page-init');
|
||||||
|
|
||||||
|
// 页面初始化后执行版本检查
|
||||||
|
checkVersionInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
String downloadUrl = "";
|
||||||
|
|
||||||
|
/// 检查 App 更新信息,增加版本号比对逻辑
|
||||||
|
void checkVersionInfo() async {
|
||||||
|
try {
|
||||||
|
final response = await HttpService.to.get('appointment/appConfig/get');
|
||||||
|
if (response != null) {
|
||||||
|
final result = BaseModel.fromJson(response.data);
|
||||||
|
if (result.code == 0 && result.data != null) {
|
||||||
|
final data = result.data as Map<String, dynamic>;
|
||||||
|
|
||||||
|
bool hasUpdate = data['hasUpdate']?.toString().toLowerCase() == "true";
|
||||||
|
bool isForce = data['isForce']?.toString().toLowerCase() == "true";
|
||||||
|
String versionName = data['versionName'] ?? "新版本";
|
||||||
|
String updateContent = data['updateContent'] ?? "优化系统性能,提升用户体验";
|
||||||
|
downloadUrl = data['downloadUrl'].toString();
|
||||||
|
|
||||||
|
// 获取服务器配置的目标构建号
|
||||||
|
int serverVersionCode =
|
||||||
|
int.tryParse(data['versionCode']?.toString() ?? "0") ?? 0;
|
||||||
|
int serverIosBuildId = int.tryParse(data['iosBuildId']?.toString() ?? "0") ?? 0;
|
||||||
|
|
||||||
|
// 获取本地当前的构建号
|
||||||
|
String currentBuildStr = await getBuildNumber();
|
||||||
|
int currentBuild = int.tryParse(currentBuildStr) ?? 0;
|
||||||
|
|
||||||
|
bool needUpdate = false;
|
||||||
|
if (GetPlatform.isAndroid) {
|
||||||
|
needUpdate = currentBuild < serverVersionCode;
|
||||||
|
} else if (GetPlatform.isIOS) {
|
||||||
|
needUpdate = currentBuild < serverIosBuildId;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果服务器标记有更新,且本地版本号确实较低,则弹出更新
|
||||||
|
if (hasUpdate && needUpdate) {
|
||||||
|
_showUpdateDialog("版本:$versionName\n\n更新内容:\n$updateContent", isForce);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
Logger.d("版本检查失败: $e");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 显示更新弹窗
|
||||||
|
void _showUpdateDialog(String content, bool isForce) {
|
||||||
|
DialogX.to.showConfirmDialog(
|
||||||
|
title: '升级提醒',
|
||||||
|
confirmText: '立即升级',
|
||||||
|
content: _buildDialogContent(content),
|
||||||
|
// 如果是强制更新,取消按钮显示为空,即隐藏
|
||||||
|
cancelText: isForce ? "" : '以后再说',
|
||||||
|
// 设置为 false,禁止点击背景和物理返回键关闭
|
||||||
|
barrierDismissible: false,
|
||||||
|
onConfirm: () {
|
||||||
|
jumpUpdateApp();
|
||||||
|
|
||||||
|
// ios如果是强制更新,点击后维持弹窗,防止用户进入 App
|
||||||
|
if (isForce && GetPlatform.isIOS) {
|
||||||
|
Future.delayed(const Duration(milliseconds: 500), () {
|
||||||
|
_showUpdateDialog(content, isForce);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDialogContent(String content) {
|
||||||
|
return PopScope(
|
||||||
|
canPop: false, // 关键:禁止 pop
|
||||||
|
child: TextX.bodyMedium(content).padding(bottom: 16.h),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void jumpUpdateApp() {
|
||||||
|
if (GetPlatform.isIOS) {
|
||||||
|
// 跳转到 iOS 应用商店网页
|
||||||
|
openWebPage("https://apps.apple.com/cn/app/羚牛氢能/6756245815");
|
||||||
|
} else if (GetPlatform.isAndroid) {
|
||||||
|
// Android 执行下载安装流程
|
||||||
|
showAndroidDownloadDialog();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void showAndroidDownloadDialog() {
|
||||||
|
AzhonAppUpdate.listener((ResultModel model) {
|
||||||
|
if (model.type == ResultType.start) {
|
||||||
|
DialogX.to.showConfirmDialog(
|
||||||
|
content: PopScope(
|
||||||
|
canPop: false,
|
||||||
|
child: Center(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
TextX.bodyMedium('升级中...').padding(bottom: 45.h),
|
||||||
|
CircularProgressIndicator(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
confirmText: '',
|
||||||
|
cancelText: "",
|
||||||
|
barrierDismissible: false,
|
||||||
|
);
|
||||||
|
} else if (model.type == ResultType.done) {
|
||||||
|
Get.back();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
UpdateModel model = UpdateModel(downloadUrl, "xll.apk", "logo", '正在下载最新版本...');
|
||||||
|
AzhonAppUpdate.update(model);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据登录状态和登录渠道返回不同的首页
|
// 根据登录状态和登录渠道返回不同的首页
|
||||||
Widget getHomePage() {
|
Widget getHomePage() {
|
||||||
requestPermission();
|
requestPermission();
|
||||||
//登录状态跳转
|
|
||||||
if (StorageService.to.isLoggedIn) {
|
if (StorageService.to.isLoggedIn) {
|
||||||
// 如果已登录,再判断是哪个渠道
|
|
||||||
if (StorageService.to.loginChannel == LoginChannel.station) {
|
if (StorageService.to.loginChannel == LoginChannel.station) {
|
||||||
return B_BaseWidgetsPage(); // 站点首页
|
return B_BaseWidgetsPage();
|
||||||
} else if (StorageService.to.loginChannel == LoginChannel.driver) {
|
} else if (StorageService.to.loginChannel == LoginChannel.driver) {
|
||||||
return BaseWidgetsPage(); // 司机首页
|
return BaseWidgetsPage();
|
||||||
} else {
|
} else {
|
||||||
return LoginPage();
|
return LoginPage();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// 未登录,直接去登录页
|
|
||||||
return LoginPage();
|
return LoginPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void requestPermission() async {
|
void requestPermission() async {
|
||||||
PermissionStatus status = await Permission.notification.status;
|
PermissionStatus status = await Permission.notification.status;
|
||||||
if (status.isGranted) {
|
if (status.isGranted) return;
|
||||||
Logger.d("通知权限已开启");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status.isDenied) {
|
if (status.isDenied) {
|
||||||
// 建议此处增加一个应用内的 Rationale (解释说明) 弹窗
|
|
||||||
status = await Permission.notification.request();
|
status = await Permission.notification.request();
|
||||||
}
|
}
|
||||||
if (status.isGranted) {
|
if (status.isGranted) {
|
||||||
// 授权成功
|
|
||||||
Logger.d('通知已开启');
|
Logger.d('通知已开启');
|
||||||
} else if (status.isPermanentlyDenied) {
|
} else if (status.isPermanentlyDenied) {
|
||||||
Logger.d('通知权限已被拒绝,请到系统设置中开启');
|
Logger.d('通知权限已被拒绝,请到系统设置中开启');
|
||||||
} else if (status.isDenied) {
|
|
||||||
Logger.d('请授予通知权限,以便接收加氢站通知');
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
///推送相关
|
///推送相关初始化 (保持原样)
|
||||||
Future<void> initAliyunPush() async {
|
Future<void> initAliyunPush() async {
|
||||||
// 1. 配置分离:建议将 Key 提取到外部或配置文件中
|
|
||||||
final String appKey = Platform.isIOS ? AppTheme.ios_key : AppTheme.android_key;
|
final String appKey = Platform.isIOS ? AppTheme.ios_key : AppTheme.android_key;
|
||||||
final String appSecret = Platform.isIOS
|
final String appSecret = Platform.isIOS
|
||||||
? AppTheme.ios_appsecret
|
? AppTheme.ios_appsecret
|
||||||
: AppTheme.android_appsecret;
|
: AppTheme.android_appsecret;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 初始化推送
|
|
||||||
final result = await _aliyunPush.initPush(appKey: appKey, appSecret: appSecret);
|
final result = await _aliyunPush.initPush(appKey: appKey, appSecret: appSecret);
|
||||||
|
if (result['code'] != kAliyunPushSuccessCode) return;
|
||||||
if (result['code'] != kAliyunPushSuccessCode) {
|
|
||||||
Logger.d('初始化推送失败: ${result['code']} - ${result['errorMsg']}');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Logger.d('阿里云推送初始化成功');
|
|
||||||
// 分平台配置
|
|
||||||
if (Platform.isIOS) {
|
if (Platform.isIOS) {
|
||||||
await _setupIOSConfig();
|
await _setupIOSConfig();
|
||||||
} else if (Platform.isAndroid) {
|
} else if (Platform.isAndroid) {
|
||||||
await _setupAndroidConfig();
|
await _setupAndroidConfig();
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
Logger.d('初始化过程中发生异常: $e');
|
Logger.d('初始化异常: $e');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// iOS 专属配置
|
|
||||||
Future<void> _setupIOSConfig() async {
|
Future<void> _setupIOSConfig() async {
|
||||||
final res = await _aliyunPush.showIOSNoticeWhenForeground(true);
|
await _aliyunPush.showIOSNoticeWhenForeground(true);
|
||||||
if (res['code'] == kAliyunPushSuccessCode) {
|
|
||||||
Logger.d('iOS 前台通知展示已开启');
|
|
||||||
} else {
|
|
||||||
Logger.d('iOS 前台通知开启失败: ${res['errorMsg']}');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Android 专属配置
|
|
||||||
Future<void> _setupAndroidConfig() async {
|
Future<void> _setupAndroidConfig() async {
|
||||||
await _aliyunPush.setNotificationInGroup(true);
|
await _aliyunPush.setNotificationInGroup(true);
|
||||||
final res = await _aliyunPush.createAndroidChannel(
|
await _aliyunPush.createAndroidChannel(
|
||||||
"xll_push_android",
|
"xll_push_android",
|
||||||
'新消息通知',
|
'新消息通知',
|
||||||
4,
|
4,
|
||||||
'用于接收加氢站实时状态提醒',
|
'用于接收加氢站实时状态提醒',
|
||||||
);
|
);
|
||||||
if (res['code'] == kAliyunPushSuccessCode) {
|
|
||||||
Logger.d('Android 通知通道创建成功');
|
|
||||||
} else {
|
|
||||||
Logger.d('Android 通道创建失败: ${res['code']} - ${res['errorMsg']}');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void addPushCallback() {
|
void addPushCallback() {
|
||||||
@@ -139,40 +230,23 @@ class HomeController extends GetxController with BaseControllerMixin {
|
|||||||
|
|
||||||
Future<void> _onAndroidNotificationClickedWithNoAction(
|
Future<void> _onAndroidNotificationClickedWithNoAction(
|
||||||
Map<dynamic, dynamic> message,
|
Map<dynamic, dynamic> message,
|
||||||
) async {
|
) async {}
|
||||||
Logger.d('onAndroidNotificationClickedWithNoAction ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onAndroidNotificationReceivedInApp(Map<dynamic, dynamic> message) async {
|
Future<void> _onAndroidNotificationReceivedInApp(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onAndroidNotificationReceivedInApp ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onMessage(Map<dynamic, dynamic> message) async {
|
Future<void> _onMessage(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onMessage ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onNotification(Map<dynamic, dynamic> message) async {
|
Future<void> _onNotification(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onNotification ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
|
Future<void> _onNotificationOpened(Map<dynamic, dynamic> message) async {
|
||||||
Logger.d('onNotificationOpened ====> $message');
|
|
||||||
await Get.to(() => const MessagePage());
|
await Get.to(() => const MessagePage());
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _onNotificationRemoved(Map<dynamic, dynamic> message) async {
|
Future<void> _onNotificationRemoved(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onNotificationRemoved ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onIOSChannelOpened(Map<dynamic, dynamic> message) async {
|
Future<void> _onIOSChannelOpened(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onIOSChannelOpened ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onIOSRegisterDeviceTokenSuccess(Map<dynamic, dynamic> message) async {
|
Future<void> _onIOSRegisterDeviceTokenSuccess(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onIOSRegisterDeviceTokenSuccess ====> $message');
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> _onIOSRegisterDeviceTokenFailed(Map<dynamic, dynamic> message) async {
|
Future<void> _onIOSRegisterDeviceTokenFailed(Map<dynamic, dynamic> message) async {}
|
||||||
Logger.d('onIOSRegisterDeviceTokenFailed====> $message');
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,13 @@ import 'package:ln_jq_app/pages/home/controller.dart';
|
|||||||
class HomePage extends GetView<HomeController> {
|
class HomePage extends GetView<HomeController> {
|
||||||
const HomePage({super.key});
|
const HomePage({super.key});
|
||||||
|
|
||||||
// 主视图
|
|
||||||
Widget _buildView() {
|
|
||||||
return <Widget>[Text('主页面')].toColumn(mainAxisSize: MainAxisSize.min).center();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return GetBuilder<HomeController>(
|
return GetBuilder<HomeController>(
|
||||||
init: HomeController(),
|
init: HomeController(),
|
||||||
id: 'home',
|
id: 'home',
|
||||||
builder: (_) {
|
builder: (_) {
|
||||||
return controller.getHomePage();
|
return Scaffold(body: controller.getHomePage());
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -204,9 +204,9 @@ class _LoginPageState extends State<LoginPage> with SingleTickerProviderStateMix
|
|||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
const Text(
|
const Text(
|
||||||
"欢迎使用 ",
|
"欢迎使用小羚羚 ",
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 22,
|
||||||
fontWeight: FontWeight.w500,
|
fontWeight: FontWeight.w500,
|
||||||
color: Color.fromRGBO(51, 51, 51, 1),
|
color: Color.fromRGBO(51, 51, 51, 1),
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -302,6 +302,14 @@ packages:
|
|||||||
description: flutter
|
description: flutter
|
||||||
source: sdk
|
source: sdk
|
||||||
version: "0.0.0"
|
version: "0.0.0"
|
||||||
|
flutter_app_update:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_app_update
|
||||||
|
sha256: "09290240949c4651581cd6fc535e52d019e189e694d6019c56b5a56c2e69ba65"
|
||||||
|
url: "https://pub.flutter-io.cn"
|
||||||
|
source: hosted
|
||||||
|
version: "3.2.2"
|
||||||
flutter_easyloading:
|
flutter_easyloading:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ dependencies:
|
|||||||
geolocator: ^14.0.2 # 获取精确定位
|
geolocator: ^14.0.2 # 获取精确定位
|
||||||
aliyun_push_flutter: ^1.3.6
|
aliyun_push_flutter: ^1.3.6
|
||||||
pull_to_refresh: ^2.0.0
|
pull_to_refresh: ^2.0.0
|
||||||
|
flutter_app_update: ^3.2.2
|
||||||
|
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user