Merge remote-tracking branch 'origin/master' into ai-analysis

# Conflicts:
#	pubspec.lock
ai-analysis
taha 1 week ago
commit d2e0eaf0ef

@ -1,12 +1 @@
{ []
"key": "none",
"enabled": false,
"start_date": null,
"end_date": null,
"primary_color": null,
"secondary_color": null,
"background_opacity_image": null,
"background_image_opacity": 0.2,
"header_image": null
}

@ -317,10 +317,8 @@ class ApiConsts {
static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations"; static String googleCloudStorageENTranslationFileBaseURL = "https://storage.googleapis.com/hmg-patientapp-translations";
// ************ static values for Api **************** // ************ static values for Api ****************
// static final double appVersionID = 21.9; static final double appVersionID = 21.6;
static final double appVersionID = 21.5;
// static final double appVersionID = 50.7;
static final int appChannelId = 3; static final int appChannelId = 3;
static final String appIpAddress = "10.20.10.20"; static final String appIpAddress = "10.20.10.20";
static final String appGeneralId = "Cs2020@2016\$2958"; static final String appGeneralId = "Cs2020@2016\$2958";

@ -608,7 +608,6 @@ class PushNotificationHandler {
// Permission.audio, // Permission.audio,
// Permission.microphone, // Permission.microphone,
].request(); ].request();
log("=-=-=-=-=-=-=-=-=-=-");
log(statuses[Permission.notification].toString()); log(statuses[Permission.notification].toString());
} }
} catch (_) { } catch (_) {

@ -47,7 +47,7 @@ class FamilyFileRequest {
"SharedPatientIdentificationID": sharedPatientIdentificationId, "SharedPatientIdentificationID": sharedPatientIdentificationId,
"SearchType": searchType, "SearchType": searchType,
"SharedPatientMobileNumber": sharedPatientMobileNumber, "SharedPatientMobileNumber": sharedPatientMobileNumber,
"zipCode": zipCode, "ZipCode": zipCode,
"isRegister": isRegister, "isRegister": isRegister,
"PatientStatus": patientStatus, "PatientStatus": patientStatus,
"isDentalAllowedBackend": isDentalAllowedBackend, "isDentalAllowedBackend": isDentalAllowedBackend,

@ -17,9 +17,12 @@ class OccasionConfigModel {
/// default/current theme, regardless of any other field below. /// default/current theme, regardless of any other field below.
final bool enabled; final bool enabled;
/// Optional activation window. When both are present, the theme is only /// Activation window. Both must be present and `DateTime.now()` must fall
/// considered active if `DateTime.now()` falls within [startDate, endDate]. /// within [startDate, endDate] for this occasion to be eligible — a
/// When null, the [enabled] flag alone controls activation. /// missing date on either side makes the occasion inactive (dates are
/// mandatory, not an optional override of [enabled]). This lets several
/// occasions be scheduled in advance in the same list (see
/// [OccasionConfigModel.resolveActive]) without colliding.
final DateTime? startDate; final DateTime? startDate;
final DateTime? endDate; final DateTime? endDate;
@ -66,13 +69,9 @@ class OccasionConfigModel {
return OccasionConfigModel( return OccasionConfigModel(
key: (json['key'] as String?) ?? 'none', key: (json['key'] as String?) ?? 'none',
enabled: (json['enabled'] as bool?) ?? false, enabled: (json['enabled'] as bool?) ?? false,
// enabled: true,
startDate: _parseDate(json['start_date']), startDate: _parseDate(json['start_date']),
endDate: _parseDate(json['end_date']), endDate: _parseDate(json['end_date']),
// enabled: true,
// startDate: _parseDate("2026-09-30T00:00:00Z"),
// endDate: _parseDate("2026-10-07T00:00:00Z"),
primaryColorHex: json['primary_color'] as String?, primaryColorHex: json['primary_color'] as String?,
backgroundOpacityImage: json['background_opacity_image'] as String?, backgroundOpacityImage: json['background_opacity_image'] as String?,
backgroundImageOpacity: (json['background_image_opacity'] as num?)?.toDouble() ?? 1.0, backgroundImageOpacity: (json['background_image_opacity'] as num?)?.toDouble() ?? 1.0,
@ -80,6 +79,25 @@ class OccasionConfigModel {
); );
} }
/// Parses the remote-config value as a JSON array of occasions — lets
/// several occasions be scheduled in advance (e.g. National Day, Founding
/// Day, Eid), each with its own non-overlapping [startDate]/[endDate]
/// window, all under the same `occasion_config` remote-config key.
static List<OccasionConfigModel> listFromJson(List<dynamic> jsonList) {
return jsonList.map((e) => OccasionConfigModel.fromJson(e as Map<String, dynamic>)).toList();
}
/// Picks whichever scheduled occasion is enabled and whose
/// [startDate]/[endDate] window contains `now` — first match in list
/// order wins, so entries are expected not to overlap. Returns
/// [OccasionConfigModel.disabled] when nothing matches.
static OccasionConfigModel resolveActive(List<OccasionConfigModel> configs, [DateTime? now]) {
for (final config in configs) {
if (config.enabled && config.isWithinActiveWindow(now)) return config;
}
return OccasionConfigModel.disabled();
}
static DateTime? _parseDate(dynamic value) { static DateTime? _parseDate(dynamic value) {
if (value == null) return null; if (value == null) return null;
if (value is String && value.isNotEmpty) { if (value is String && value.isNotEmpty) {
@ -89,11 +107,13 @@ class OccasionConfigModel {
} }
/// Whether "now" falls inside the configured activation window. /// Whether "now" falls inside the configured activation window.
/// If no window is configured, the [enabled] flag alone decides. /// Both [startDate] and [endDate] are required — if either is missing,
/// this occasion is never considered active (see field doc above).
bool isWithinActiveWindow([DateTime? now]) { bool isWithinActiveWindow([DateTime? now]) {
if (startDate == null || endDate == null) return false;
final current = now ?? DateTime.now(); final current = now ?? DateTime.now();
if (startDate != null && current.isBefore(startDate!)) return false; if (current.isBefore(startDate!)) return false;
if (endDate != null && current.isAfter(endDate!)) return false; if (current.isAfter(endDate!)) return false;
return true; return true;
} }
@ -125,4 +145,3 @@ class OccasionConfigModel {
'header_image': headerImage, 'header_image': headerImage,
}; };
} }

@ -35,8 +35,17 @@ class OccasionThemeViewModel extends ChangeNotifier {
OccasionConfigModel _config = OccasionConfigModel.disabled(); OccasionConfigModel _config = OccasionConfigModel.disabled();
OccasionConfigModel get config => _config; OccasionConfigModel get config => _config;
/// Testing-only override for "now" when resolving which scheduled
/// occasion is active — set this instead of changing the device clock to
/// preview each occasion in the list. Only ever takes effect in debug
/// builds (see [_now]); always ignored in profile/release, so it can
/// never leak into production even if accidentally left set.
static DateTime? debugNowOverride;
DateTime? get _now => kDebugMode ? debugNowOverride : null;
/// True only when enabled AND (if a window is configured) within it. /// True only when enabled AND (if a window is configured) within it.
bool get isOccasionActive => _config.enabled && _config.isWithinActiveWindow(); bool get isOccasionActive => _config.enabled && _config.isWithinActiveWindow(_now);
String? get backgroundOpacityImage => isOccasionActive ? _config.backgroundOpacityImage : null; String? get backgroundOpacityImage => isOccasionActive ? _config.backgroundOpacityImage : null;
@ -66,14 +75,20 @@ class OccasionThemeViewModel extends ChangeNotifier {
} }
} }
/// Fetches the latest occasion config from Firebase Remote Config and /// Fetches the latest occasion schedule from Firebase Remote Config and
/// applies it. Safe to call again later (e.g. on app resume) to pick up /// applies whichever entry is currently active. Safe to call again later
/// changes without requiring an app update. /// (e.g. on app resume) to pick up changes without requiring an app update.
Future<void> refresh() async { Future<void> refresh() async {
try { try {
await _remoteConfigService.fetchAndActivate(); await _remoteConfigService.fetchAndActivate();
final rawJson = _remoteConfigService.occasionConfigRawJson; final rawJson = _remoteConfigService.occasionConfigRawJson;
_config = OccasionConfigModel.fromJson(json.decode(rawJson) as Map<String, dynamic>); final configs = OccasionConfigModel.listFromJson(json.decode(rawJson) as List<dynamic>);
_config = OccasionConfigModel.resolveActive(configs, _now);
_loggerService.logInfo('OccasionThemeViewModel: chosen theme = "${_config.key}" (enabled=${_config.enabled}, active=$isOccasionActive)');
_loggerService.logInfo('OccasionThemeViewModel: chosen theme object = ${_config.toJson()}');
// Cache the full schedule (not just the resolved entry) so offline
// restore at next startup re-resolves against that day's date instead
// of replaying whichever occasion happened to be active last time.
await _cacheService.saveString(key: _cachedConfigKey, value: rawJson); await _cacheService.saveString(key: _cachedConfigKey, value: rawJson);
_applyToAppColors(); _applyToAppColors();
notifyListeners(); notifyListeners();
@ -91,7 +106,8 @@ class OccasionThemeViewModel extends ChangeNotifier {
_config = OccasionConfigModel.disabled(); _config = OccasionConfigModel.disabled();
return; return;
} }
_config = OccasionConfigModel.fromJson(json.decode(cachedJson) as Map<String, dynamic>); final configs = OccasionConfigModel.listFromJson(json.decode(cachedJson) as List<dynamic>);
_config = OccasionConfigModel.resolveActive(configs, _now);
} catch (ex) { } catch (ex) {
_loggerService.logError('OccasionThemeViewModel._restoreCachedConfig error: $ex'); _loggerService.logError('OccasionThemeViewModel._restoreCachedConfig error: $ex');
_config = OccasionConfigModel.disabled(); _config = OccasionConfigModel.disabled();

@ -73,7 +73,6 @@ import 'core/utils/size_utils.dart';
import 'features/monthly_reports/terms_conditions_view_model.dart'; import 'features/monthly_reports/terms_conditions_view_model.dart';
import 'firebase_options.dart'; import 'firebase_options.dart';
@pragma('vm:entry-point') @pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async { Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
@ -144,7 +143,11 @@ Future<void> callInitializations() async {
// Initialize Security Service early to catch threats before app logic runs // Initialize Security Service early to catch threats before app logic runs
if (kReleaseMode) { if (kReleaseMode) {
await getIt.get<SecurityService>().initialize(); try {
await getIt.get<SecurityService>().initialize();
} catch (e) {
getIt.get<LoggerService>().logError('SecurityService.initialize failed, continuing app startup: $e');
}
} }
// Set up critical threat callback to navigate to unsafe device page // Set up critical threat callback to navigate to unsafe device page
@ -177,6 +180,8 @@ Future<void> callInitializations() async {
// disabled by default — the app looks exactly as it does today until // disabled by default — the app looks exactly as it does today until
// Remote Config enables an occasion (e.g. National Day) on the server side. // Remote Config enables an occasion (e.g. National Day) on the server side.
final occasionThemeViewModel = getIt.get<OccasionThemeViewModel>(); final occasionThemeViewModel = getIt.get<OccasionThemeViewModel>();
OccasionThemeViewModel.debugNowOverride = DateTime.now();
// OccasionThemeViewModel.debugNowOverride = DateTime.parse('2027-03-09T00:00:00Z');
unawaited(occasionThemeViewModel.initialize()); unawaited(occasionThemeViewModel.initialize());
} }

@ -858,7 +858,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
print("onBrowserLoadStart"); print("onBrowserLoadStart");
print(url); print(url);
if (selectedPaymentMethod == "tamara") { if (selectedPaymentMethod.toLowerCase() == "tamara") {
if (Platform.isAndroid) { if (Platform.isAndroid) {
Uri uri = new Uri.dataFromString(url); Uri uri = new Uri.dataFromString(url);
tamaraPaymentStatus = uri.queryParameters['status']!; tamaraPaymentStatus = uri.queryParameters['status']!;
@ -897,7 +897,7 @@ class _AppointmentPaymentPageState extends State<AppointmentPaymentPage> {
void checkPaymentStatus() async { void checkPaymentStatus() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context)); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr(context: context));
if (selectedPaymentMethod == "TAMARA") { if (selectedPaymentMethod.toLowerCase() == "tamara") {
await payfortViewModel.checkTamaraPaymentStatus( await payfortViewModel.checkTamaraPaymentStatus(
transactionID: transID, transactionID: transID,
onSuccess: (apiResponse) async { onSuccess: (apiResponse) async {

@ -781,7 +781,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
print("onBrowserLoadStart"); print("onBrowserLoadStart");
print(url); print(url);
if (selectedPaymentMethod == "TAMARA") { if (selectedPaymentMethod.toLowerCase() == "tamara") {
if (Platform.isAndroid) { if (Platform.isAndroid) {
Uri uri = new Uri.dataFromString(url); Uri uri = new Uri.dataFromString(url);
tamaraPaymentStatus = uri.queryParameters['status'] ?? ""; tamaraPaymentStatus = uri.queryParameters['status'] ?? "";
@ -821,7 +821,7 @@ class _ImmediateLiveCarePaymentPageState extends State<ImmediateLiveCarePaymentP
void checkPaymentStatus() async { void checkPaymentStatus() async {
LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr()); LoaderBottomSheet.showLoader(loadingText: LocaleKeys.checkingPaymentStatusPleaseWait.tr());
if (selectedPaymentMethod == "TAMARA") { if (selectedPaymentMethod.toLowerCase() == "tamara") {
await payfortViewModel.checkTamaraPaymentStatus( await payfortViewModel.checkTamaraPaymentStatus(
transactionID: transID, transactionID: transID,
onSuccess: (apiResponse) async { onSuccess: (apiResponse) async {

@ -234,6 +234,7 @@ class _LandingPageState extends State<LandingPage> {
// inline with the rest of the content. // inline with the rest of the content.
final hasStickyBanner = context.watch<OccasionThemeViewModel>().headerImage?.isNotEmpty ?? false; final hasStickyBanner = context.watch<OccasionThemeViewModel>().headerImage?.isNotEmpty ?? false;
log("hasStickyBanner: $hasStickyBanner");
return Stack( return Stack(
children: [ children: [
// Renders nothing unless a remote-config occasion theme (e.g. // Renders nothing unless a remote-config occasion theme (e.g.

@ -1,3 +1,5 @@
import 'dart:developer';
import 'package:logger/logger.dart'; import 'package:logger/logger.dart';
abstract class LoggerService { abstract class LoggerService {
@ -18,6 +20,6 @@ class LoggerServiceImp implements LoggerService {
@override @override
void logInfo(String message) { void logInfo(String message) {
logger.d(message); log(message);
} }
} }

@ -16,8 +16,9 @@ abstract class RemoteConfigService {
/// minimum fetch interval) and activates them. /// minimum fetch interval) and activates them.
Future<bool> fetchAndActivate(); Future<bool> fetchAndActivate();
/// Raw JSON string currently held for the `occasion_config` key /// Raw JSON array string currently held for the `occasion_config` key —
/// (bundled default until the first successful fetch/activate). /// a list of scheduled occasions (bundled default `[]` until the first
/// successful fetch/activate).
String get occasionConfigRawJson; String get occasionConfigRawJson;
} }
@ -41,7 +42,9 @@ class RemoteConfigServiceImp implements RemoteConfigService {
fetchTimeout: const Duration(seconds: 10), fetchTimeout: const Duration(seconds: 10),
// In debug builds, always fetch fresh values so you can test // In debug builds, always fetch fresh values so you can test
// Remote Config changes instantly without waiting/throttling. // Remote Config changes instantly without waiting/throttling.
// Release builds keep a sane 1-hour minimum interval. // Release/profile builds keep a 1-hour minimum interval —
// balances timely occasion-theme rollout against not hammering
// Remote Config across the whole user base.
minimumFetchInterval: kDebugMode ? Duration.zero : const Duration(hours: 1), minimumFetchInterval: kDebugMode ? Duration.zero : const Duration(hours: 1),
), ),
); );
@ -79,5 +82,7 @@ class RemoteConfigServiceImp implements RemoteConfigService {
} }
} }
static const String _fallbackDisabledJson = '{"key":"none","enabled":false}'; /// Empty schedule — [OccasionConfigModel.resolveActive] treats this the
/// same as "no occasion currently active".
static const String _fallbackDisabledJson = '[]';
} }

@ -314,6 +314,8 @@ class MyInAppBrowser extends InAppBrowser {
if (isLiveCareAppo) { if (isLiveCareAppo) {
form = form.replaceFirst('IS_SCHEDULE_VALUE', "true"); form = form.replaceFirst('IS_SCHEDULE_VALUE', "true");
} else {
form = form.replaceFirst('IS_SCHEDULE_VALUE', "false");
} }
var bytes = utf8.encode(form); var bytes = utf8.encode(form);

@ -133,10 +133,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: clarity_flutter name: clarity_flutter
sha256: b3a114fec6e873caf03cd59a32953f8cb4b1693bd3b4f749ef3030fea027ebce sha256: "4d46afd76c31d3876f23384d44d6f5728a556daff6cec8b2c98d251dac14899e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.9.0" version: "1.10.0"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -165,18 +165,18 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: connectivity_plus name: connectivity_plus
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec sha256: "762c99f890ca8bf87f7337236f99edd42793843bc6c3631da294a76653a54bd0"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "6.1.5" version: "7.3.1"
connectivity_plus_platform_interface: connectivity_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: connectivity_plus_platform_interface name: connectivity_plus_platform_interface
sha256: "42657c1715d48b167930d5f34d00222ac100475f73d10162ddf43e714932f204" sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.0.1" version: "2.1.0"
convert: convert:
dependency: transitive dependency: transitive
description: description:
@ -534,10 +534,9 @@ packages:
flutter_amazonpaymentservices: flutter_amazonpaymentservices:
dependency: "direct main" dependency: "direct main"
description: description:
name: flutter_amazonpaymentservices path: "package/flutter_amazonpaymentservices"
sha256: "23f45fd3b1e12a0933da3704d66cebd19c4c41e28a50d4fd458d3e3e6311f332" relative: true
url: "https://pub.dev" source: path
source: hosted
version: "0.0.13" version: "0.0.13"
flutter_cache_manager: flutter_cache_manager:
dependency: transitive dependency: transitive
@ -1213,10 +1212,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: intl name: intl
sha256: "1ca20c894b1717686a2319b8548763d812bc0aabdac580420a44c5178c57a867" sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.20.3" version: "0.20.2"
jiffy: jiffy:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1301,26 +1300,26 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: local_auth name: local_auth
sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b" sha256: ecf24edf2283c509ecd217e3595f6f71034b68888d28ad1dae6bfa0857b816ac
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.0" version: "3.0.2"
local_auth_android: local_auth_android:
dependency: transitive dependency: transitive
description: description:
name: local_auth_android name: local_auth_android
sha256: a0bdfcc0607050a26ef5b31d6b4b254581c3d3ce3c1816ab4d4f4a9173e84467 sha256: "76c389998fe95cff4352644776ab5775f24ddd58f179dbf3df1b9c268cbbadc4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.56" version: "2.2.0"
local_auth_darwin: local_auth_darwin:
dependency: transitive dependency: transitive
description: description:
name: local_auth_darwin name: local_auth_darwin
sha256: "699873970067a40ef2f2c09b4c72eb1cfef64224ef041b3df9fdc5c4c1f91f49" sha256: b1b2d938cc3511512e552d92e782e055b8dc29c83e21c59e6ecfc6bd79ab0744
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.6.1" version: "2.0.4"
local_auth_platform_interface: local_auth_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -1333,10 +1332,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: local_auth_windows name: local_auth_windows
sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5 sha256: d72b8a441e5e6a679902f87c51529fcce65f442d77598484cf35454c3d733eda
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.0.11" version: "2.0.2"
location: location:
dependency: "direct main" dependency: "direct main"
description: description:
@ -1405,10 +1404,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: matcher name: matcher
sha256: "31bd099b47c10cd1aeb55146a2d46ce0277630ecef3f7dae54ad7873f36696cd" sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.12.20" version: "0.12.17"
material_color_utilities: material_color_utilities:
dependency: transitive dependency: transitive
description: description:
@ -1421,10 +1420,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: meta name: meta
sha256: "307249ce4ff29d58a18e97f6345f539382eb9c9c29ecda628900f31de0443dd9" sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "1.19.0" version: "1.18.0"
mime: mime:
dependency: transitive dependency: transitive
description: description:
@ -1797,10 +1796,10 @@ packages:
dependency: "direct main" dependency: "direct main"
description: description:
name: shared_preferences name: shared_preferences
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" sha256: "1e85e0dccf5b4a016077fea7dfbb4a7cb39c759ac1596fcf60c06deadb7da91f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.5.4" version: "2.5.6"
shared_preferences_android: shared_preferences_android:
dependency: transitive dependency: transitive
description: description:
@ -2018,10 +2017,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: test_api name: test_api
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11" sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.7.12" version: "0.7.11"
time: time:
dependency: transitive dependency: transitive
description: description:
@ -2154,10 +2153,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: vector_math name: vector_math
sha256: "92b9910f66ed1057fd4da7b040ae7c74cafacf885bdc81be496928d5049b032d" sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.4.3" version: "2.2.0"
video_player: video_player:
dependency: transitive dependency: transitive
description: description:
@ -2303,5 +2302,5 @@ packages:
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
sdks: sdks:
dart: ">=3.11.0-0 <4.0.0" dart: ">=3.12.0 <4.0.0"
flutter: ">=3.35.0" flutter: ">=3.44.0"

Loading…
Cancel
Save