From 74aefd4b0553c7bbfad1404e5b12d9d74470e20d Mon Sep 17 00:00:00 2001 From: FaizHashmiCS22 Date: Tue, 3 Oct 2023 17:18:05 +0300 Subject: [PATCH] Added Weather --- assets/icons/cloudy.svg | 1 + assets/icons/cold.svg | 1 + assets/icons/hot.svg | 1 + assets/icons/mosque.png | Bin 0 -> 8924 bytes assets/icons/rainy.svg | 1 + assets/icons/weather.svg | 1 + assets/icons/windy.svg | 1 + lib/core/api.dart | 55 +++++- lib/core/config/config.dart | 12 +- .../response_models/prayers_widget_model.dart | 48 ++++++ .../weathers_widget_model.dart | 65 +++++++ .../response_models/widgets_config_model.dart | 53 ++++++ lib/header/app_header.dart | 49 +++++- lib/home/app_provider.dart | 163 ++++++++++++++++-- lib/home/home_screen.dart | 1 + lib/widget/data_display/app_texts_widget.dart | 3 +- macos/Flutter/GeneratedPluginRegistrant.swift | 2 + pubspec.lock | 64 +++++++ pubspec.yaml | 6 +- 19 files changed, 503 insertions(+), 24 deletions(-) create mode 100644 assets/icons/cloudy.svg create mode 100644 assets/icons/cold.svg create mode 100644 assets/icons/hot.svg create mode 100644 assets/icons/mosque.png create mode 100644 assets/icons/rainy.svg create mode 100644 assets/icons/weather.svg create mode 100644 assets/icons/windy.svg create mode 100644 lib/core/response_models/prayers_widget_model.dart create mode 100644 lib/core/response_models/weathers_widget_model.dart create mode 100644 lib/core/response_models/widgets_config_model.dart diff --git a/assets/icons/cloudy.svg b/assets/icons/cloudy.svg new file mode 100644 index 0000000..b1f6b7e --- /dev/null +++ b/assets/icons/cloudy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/cold.svg b/assets/icons/cold.svg new file mode 100644 index 0000000..06b4b0b --- /dev/null +++ b/assets/icons/cold.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/hot.svg b/assets/icons/hot.svg new file mode 100644 index 0000000..1fedcc1 --- /dev/null +++ b/assets/icons/hot.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/mosque.png b/assets/icons/mosque.png new file mode 100644 index 0000000000000000000000000000000000000000..6d736d7bee9b52e873fcace9b5a3209a4685ac10 GIT binary patch literal 8924 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 axyX$`Hy>u*mLc#a3PiB9wn literal 0 HcmV?d00001 diff --git a/assets/icons/rainy.svg b/assets/icons/rainy.svg new file mode 100644 index 0000000..c3ce867 --- /dev/null +++ b/assets/icons/rainy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/weather.svg b/assets/icons/weather.svg new file mode 100644 index 0000000..8c0ac78 --- /dev/null +++ b/assets/icons/weather.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/windy.svg b/assets/icons/windy.svg new file mode 100644 index 0000000..9e12431 --- /dev/null +++ b/assets/icons/windy.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/lib/core/api.dart b/lib/core/api.dart index c8cfa78..9e66b0b 100644 --- a/lib/core/api.dart +++ b/lib/core/api.dart @@ -5,9 +5,15 @@ import 'package:queuing_system/core/base/base_app_client.dart'; import 'package:queuing_system/core/config/config.dart'; import 'package:queuing_system/core/response_models/call_config_model.dart'; import 'package:queuing_system/core/response_models/patient_ticket_model.dart'; +import 'package:queuing_system/core/response_models/prayers_widget_model.dart'; +import 'package:queuing_system/core/response_models/weathers_widget_model.dart'; +import 'package:queuing_system/core/response_models/widgets_config_model.dart'; const _getCallRequestInfoByClinicInfo = "/GetCallRequestInfo_ByIP"; const _callUpdateNotIsQueueRecordByIDAsync = "/CallRequest_QueueUpdate"; +const _waitingAreaScreenConfigGet = "/WaitingAreaScreen_Config_Get"; +const _weatherForecastGetBy5Days = "/WeatherForecast_GetBy5Days"; +const _prayerTimeToday = "/PrayerTime_Today"; class MyHttpOverrides extends HttpOverrides { @override @@ -20,7 +26,7 @@ class API { static getCallRequestInfoByClinicInfo(String deviceIp, {required Function(List, List, CallConfig callConfig) onSuccess, required Function(dynamic) onFailure}) async { final body = {"ipAdress": deviceIp, "apiKey": apiKey}; - bool isDevMode = true; + bool isDevMode = false; if (isDevMode) { var callPatients = PatientTicketModel.testCallPatients; CallConfig callConfig = CallConfig.testCallConfig; @@ -80,4 +86,51 @@ class API { onFailure(false); } } + + static Future getWidgetConfigsFromServer(String deviceIp, {required Function(dynamic) onFailure}) async { + final body = {"ipAddress": deviceIp}; + + WidgetsConfigModel widgetsConfigModel = WidgetsConfigModel(); + await BaseAppClient.post(_waitingAreaScreenConfigGet, + body: body, + onSuccess: (response, status) { + if (status == 200 && response["data"] != null) { + widgetsConfigModel = (response["data"] as List).map((e) => WidgetsConfigModel.fromJson(e)).toList().first; + } + }, + onFailure: (error, status) => log("error: ${error.toString()}")); + return widgetsConfigModel; + } + + static Future getWeatherDetailsFromServer(String cityId, {required Function(dynamic) onFailure}) async { + final body = {"cityID": cityId}; + WeathersWidgetModel weathersWidgetModel = WeathersWidgetModel(); + await BaseAppClient.post(_weatherForecastGetBy5Days, + body: body, + onSuccess: (response, status) { + if (status == 200 && response["data"] != null) { + weathersWidgetModel = (response["data"] as List).map((e) => WeathersWidgetModel.fromJson(e)).toList().first; + } + }, + onFailure: (error, status) => log("error: ${error.toString()}")); + return weathersWidgetModel; + } + + static Future> getPrayerDetailsFromServer({ + required double latitude, + required double longitude, + required Function(dynamic) onFailure, + }) async { + final body = {"latitude": latitude, "longitude": longitude}; + await BaseAppClient.post(_prayerTimeToday, + body: body, + onSuccess: (response, status) { + if (status == 200 && response["data"] != null) { + List prayersWidgetModel = (response["data"] as List).map((e) => PrayersWidgetModel.fromJson(e)).toList(); + return prayersWidgetModel; + } + }, + onFailure: (error, status) => log("error: ${error.toString()}")); + return []; + } } diff --git a/lib/core/config/config.dart b/lib/core/config/config.dart index 1faa39c..6556230 100644 --- a/lib/core/config/config.dart +++ b/lib/core/config/config.dart @@ -24,15 +24,25 @@ class AppGlobal { static Color nebulizationColor = const Color(0xFF3C86D0); static Color vitalSignColor = const Color(0xFFD02127); static Color doctorColor = const Color(0xFF52964F); + // static Color procedureColor = const Color(0xFFC99609); static Color vaccinationColor = const Color(0xFFC99609); static Color procedureColor = const Color(0xFF460707); - //IconPaths static String vitalSignIcon = "assets/images/vitalsign_icon.svg"; static String nebulizationIcon = "assets/images/nebulization_icon.svg"; static String doctorIcon = "assets/images/doctor_icon.svg"; static String procedureIcon = "assets/images/procedure_icon.svg"; static String vaccinationIcon = "assets/images/vaccination_icon.svg"; + + //Widgets + static String mosqueIcon = "assets/icons/mosque.png"; + static String weatherIcon = "assets/icons/weather.svg"; + + static String cloudIcon = "assets/icons/cloudy.svg"; + static String coldIcon = "assets/icons/cold.svg"; + static String hotIcon = "assets/icons/hot.svg"; + static String rainIcon = "assets/icons/rainy.svg"; + static String windIcon = "assets/icons/windy.svg"; } diff --git a/lib/core/response_models/prayers_widget_model.dart b/lib/core/response_models/prayers_widget_model.dart new file mode 100644 index 0000000..c985644 --- /dev/null +++ b/lib/core/response_models/prayers_widget_model.dart @@ -0,0 +1,48 @@ +class PrayersWidgetModel { + String? fajr; + String? sunrise; + String? dhuhr; + String? asr; + String? sunset; + String? maghrib; + String? isha; + String? imsak; + String? midnight; + String? firstthird; + String? lastthird; + String? dateFor; + + PrayersWidgetModel({this.fajr, this.sunrise, this.dhuhr, this.asr, this.sunset, this.maghrib, this.isha, this.imsak, this.midnight, this.firstthird, this.lastthird, this.dateFor}); + + PrayersWidgetModel.fromJson(Map json) { + fajr = json['fajr']; + sunrise = json['sunrise']; + dhuhr = json['dhuhr']; + asr = json['asr']; + sunset = json['sunset']; + maghrib = json['maghrib']; + isha = json['isha']; + imsak = json['imsak']; + midnight = json['midnight']; + firstthird = json['firstthird']; + lastthird = json['lastthird']; + dateFor = json['dateFor']; + } + + Map toJson() { + final Map data = {}; + data['fajr'] = fajr; + data['sunrise'] = sunrise; + data['dhuhr'] = dhuhr; + data['asr'] = asr; + data['sunset'] = sunset; + data['maghrib'] = maghrib; + data['isha'] = isha; + data['imsak'] = imsak; + data['midnight'] = midnight; + data['firstthird'] = firstthird; + data['lastthird'] = lastthird; + data['dateFor'] = dateFor; + return data; + } +} diff --git a/lib/core/response_models/weathers_widget_model.dart b/lib/core/response_models/weathers_widget_model.dart new file mode 100644 index 0000000..65732eb --- /dev/null +++ b/lib/core/response_models/weathers_widget_model.dart @@ -0,0 +1,65 @@ +class WeathersWidgetModel { + int? id; + String? headline; + double? maxTemp; + double? minTemp; + String? iconPhrase; + String? forecastDate; + int? cityID; + String? forecastDay; + String? createDateTime; + double? windSpeed; + String? windDirection; + double? windDegrees; + + WeathersWidgetModel( + {this.id, + this.headline, + this.maxTemp, + this.minTemp, + this.iconPhrase, + this.forecastDate, + this.cityID, + this.forecastDay, + this.createDateTime, + this.windSpeed, + this.windDirection, + this.windDegrees}); + + WeathersWidgetModel.fromJson(Map json) { + id = json['id']; + headline = json['headline']; + maxTemp = json['maxTemp']; + minTemp = json['minTemp']; + iconPhrase = json['iconPhrase']; + forecastDate = json['forecastDate']; + cityID = json['cityID']; + forecastDay = json['forecastDay']; + createDateTime = json['createDateTime']; + windSpeed = json['windSpeed']; + windDirection = json['windDirection']; + windDegrees = json['windDegrees']; + } + + Map toJson() { + final Map data = {}; + data['id'] = id; + data['headline'] = headline; + data['maxTemp'] = maxTemp; + data['minTemp'] = minTemp; + data['iconPhrase'] = iconPhrase; + data['forecastDate'] = forecastDate; + data['cityID'] = cityID; + data['forecastDay'] = forecastDay; + data['createDateTime'] = createDateTime; + data['windSpeed'] = windSpeed; + data['windDirection'] = windDirection; + data['windDegrees'] = windDegrees; + return data; + } + + @override + String toString() { + return 'WeathersWidgetModel{id: $id, headline: $headline, maxTemp: $maxTemp, minTemp: $minTemp, iconPhrase: $iconPhrase, forecastDate: $forecastDate, cityID: $cityID, forecastDay: $forecastDay, createDateTime: $createDateTime, windSpeed: $windSpeed, windDirection: $windDirection, windDegrees: $windDegrees}'; + } +} diff --git a/lib/core/response_models/widgets_config_model.dart b/lib/core/response_models/widgets_config_model.dart new file mode 100644 index 0000000..cbc04b2 --- /dev/null +++ b/lib/core/response_models/widgets_config_model.dart @@ -0,0 +1,53 @@ +class WidgetsConfigModel { + int? waitingAreaID; + String? waitingAreaName; + bool? isWeatherReq; + bool? isPrayerTimeReq; + int? projectID; + double? projectLatitude; + double? projectLongitude; + int? cityKey; + + WidgetsConfigModel({ + this.waitingAreaID, + this.waitingAreaName, + this.isWeatherReq, + this.isPrayerTimeReq, + this.projectID, + this.projectLatitude, + this.projectLongitude, + this.cityKey, + }); + + WidgetsConfigModel.fromJson(Map json) { + print("jsonToConvert: $json"); + waitingAreaID = json['waitingAreaID']; + waitingAreaName = json['waitingAreaName']; + isWeatherReq = true; + isPrayerTimeReq = true; + // isWeatherReq = json['isWeatherReq']; + // isPrayerTimeReq = json['isPrayerTimeReq']; + projectID = json['projectID']; + projectLatitude = json['projectLatitude']; + projectLongitude = json['projectLongitude']; + cityKey = json['cityKey']; + } + + Map toJson() { + final Map data = {}; + data['waitingAreaID'] = waitingAreaID; + data['waitingAreaName'] = waitingAreaName; + data['isWeatherReq'] = isWeatherReq; + data['isPrayerTimeReq'] = isPrayerTimeReq; + data['projectID'] = projectID; + data['projectLatitude'] = projectLatitude; + data['projectLongitude'] = projectLongitude; + data['cityKey'] = cityKey; + return data; + } + + @override + String toString() { + return 'WidgetsConfigModel{waitingAreaID: $waitingAreaID, waitingAreaName: $waitingAreaName, isWeatherReq: $isWeatherReq, isPrayerTimeReq: $isPrayerTimeReq, projectLatitude: $projectLatitude,projectLongitude: $projectLongitude, cityKey: $cityKey}'; + } +} diff --git a/lib/header/app_header.dart b/lib/header/app_header.dart index 28a9f1a..0ba8bcd 100644 --- a/lib/header/app_header.dart +++ b/lib/header/app_header.dart @@ -2,6 +2,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_svg/flutter_svg.dart'; import 'package:provider/provider.dart'; import 'package:queuing_system/core/config/config.dart'; +import 'package:queuing_system/core/config/size_config.dart'; import 'package:queuing_system/home/app_provider.dart'; import 'package:queuing_system/utils/utils.dart'; import 'package:queuing_system/widget/data_display/app_texts_widget.dart'; @@ -9,6 +10,51 @@ import 'package:queuing_system/widget/data_display/app_texts_widget.dart'; class AppHeader extends StatelessWidget with PreferredSizeWidget { const AppHeader({Key? key}) : super(key: key); + Widget getWeatherWidget(AppProvider appProvider) { + if (appProvider.currentWeathersWidgetModel.maxTemp == null || appProvider.currentWeathersWidgetModel.minTemp == null || appProvider.currentWeathersWidgetModel.iconPhrase == null) { + return const SizedBox.shrink(); + } + return Row( + children: [ + SvgPicture.asset( + AppGlobal.weatherIcon, + height: SizeConfig.getHeightMultiplier() * 2.5, + color: Colors.white, + ), + const SizedBox(width: 10), + Padding( + padding: const EdgeInsets.only(top: 15), + child: AppText( + "Max: ${appProvider.currentWeathersWidgetModel.maxTemp}°C , Min: ${appProvider.currentWeathersWidgetModel.minTemp}°C", + color: Colors.white, + fontSize: SizeConfig.getHeightMultiplier() * 1.3, + ), + ), + ], + ); + } + + Widget getPrayerWidget(AppProvider appProvider) { + return Row( + children: [ + Image.asset( + AppGlobal.mosqueIcon, + height: SizeConfig.getHeightMultiplier() * 2.5, + color: Colors.white, + ), + const SizedBox(width: 10), + Padding( + padding: const EdgeInsets.only(top: 15), + child: AppText( + "Next Prayer: Dhuhr (11:49am)", + color: Colors.white, + fontSize: SizeConfig.getHeightMultiplier() * 1.3, + ), + ), + ], + ); + } + @override Widget build(BuildContext context) { AppProvider appProvider = context.read(); @@ -36,9 +82,10 @@ class AppHeader extends StatelessWidget with PreferredSizeWidget { child: AppText( appProvider.patientCallConfigurations.currentServeText, color: Colors.white, - fontFamily: 'Poppins-SemiBold.ttf', ), ), + getPrayerWidget(appProvider), + getWeatherWidget(appProvider), SvgPicture.asset( "assets/images/hmglogo.svg", height: Utils.getHeight() * 0.5, diff --git a/lib/home/app_provider.dart b/lib/home/app_provider.dart index bd9efa6..7606cf5 100644 --- a/lib/home/app_provider.dart +++ b/lib/home/app_provider.dart @@ -9,15 +9,26 @@ import 'package:just_audio/just_audio.dart'; import 'package:queuing_system/core/api.dart'; import 'package:queuing_system/core/response_models/call_config_model.dart'; import 'package:queuing_system/core/response_models/patient_ticket_model.dart'; +import 'package:queuing_system/core/response_models/prayers_widget_model.dart'; +import 'package:queuing_system/core/response_models/weathers_widget_model.dart'; +import 'package:queuing_system/core/response_models/widgets_config_model.dart'; import 'package:queuing_system/utils/call_by_voice.dart'; import 'package:queuing_system/utils/call_type.dart'; import 'package:queuing_system/utils/signalR_utils.dart'; +import 'package:shared_preferences/shared_preferences.dart'; class AppProvider extends ChangeNotifier { AppProvider() { - startSignalHubConnection(); + callInitializations(); + } + + Future callInitializations() async { + await startSignalHubConnection(); + await getInfoWidgetsDetailsFromServer(); + await getLastTimeUpdatedFromCache(); listenNetworkConnectivity(); listenAudioPlayerEvents(); + getTheWidgetsConfigurationsEveryMidnight(); } SignalRHelper signalRHelper = SignalRHelper(); @@ -57,6 +68,103 @@ class AppProvider extends ChangeNotifier { } } + WidgetsConfigModel? currentWidgetsConfigModel = WidgetsConfigModel(); + + Future getInfoWidgetsConfigurationsFromServer() async { + WidgetsConfigModel? widgetsConfigModel = await API.getWidgetConfigsFromServer(currentDeviceIp, onFailure: (error) { + log("Api call failed with this error: ${error.toString()}"); + }); + + if (widgetsConfigModel != null) { + currentWidgetsConfigModel = widgetsConfigModel; + log("I got this data: ${widgetsConfigModel.toString()}"); + notifyListeners(); + } + } + + WeathersWidgetModel currentWeathersWidgetModel = WeathersWidgetModel(); + + Future getWeatherDetailsFromServer() async { + log("I got this data from Weather: "); + + WeathersWidgetModel? weathersWidgetModel = await API.getWeatherDetailsFromServer( + (currentWidgetsConfigModel!.cityKey ?? "").toString(), + onFailure: (error) => log("Api call failed with this error: ${error.toString()}"), + ); + + if (weathersWidgetModel != null) { + currentWeathersWidgetModel = weathersWidgetModel; + log("I got this data from Weather: ${weathersWidgetModel.toString()}"); + notifyListeners(); + } + } + + PrayersWidgetModel nextPrayerToShow = PrayersWidgetModel(); + + Future getTheNextPrayerToShow() async { + + } + + List currentPrayersWidgetModel = []; + + Future getPrayerDetailsFromServer() async { + List prayersWidgetModel = await API.getPrayerDetailsFromServer( + latitude: currentWidgetsConfigModel!.projectLatitude ?? 0, + longitude: currentWidgetsConfigModel!.projectLongitude ?? 0, + onFailure: (error) => log("Api call failed with this error: ${error.toString()}")); + + if (prayersWidgetModel.isNotEmpty) { + currentPrayersWidgetModel = prayersWidgetModel; + log("I got this data: ${prayersWidgetModel.toString()}"); + notifyListeners(); + } + } + + Future getInfoWidgetsDetailsFromServer() async { + if (currentWidgetsConfigModel == null) return; + + await getInfoWidgetsConfigurationsFromServer().whenComplete(() async { + if (currentWidgetsConfigModel!.isWeatherReq!) { + await getWeatherDetailsFromServer(); + } + if (currentWidgetsConfigModel!.isPrayerTimeReq!) { + await getPrayerDetailsFromServer(); + } + }); + + int currentDate = DateTime.now().millisecondsSinceEpoch; + await setLastTimeUpdatedInCache(lasTimeUpdated: currentDate.toString()); + } + + Future getTheWidgetsConfigurationsEveryMidnight() async { + if (currentWidgetsConfigModel == null) return; + if (!currentWidgetsConfigModel!.isWeatherReq! && !currentWidgetsConfigModel!.isPrayerTimeReq!) { + return; + } + + DateTime current = DateTime.now(); + Stream timer = Stream.periodic(const Duration(hours: 1), (i) { + current = current.add(const Duration(hours: 1)); + return current; + }); + + timer.listen((data) async { + DateTime dateTime = DateTime.parse(data.toString()); + + if (currentWidgetsConfigModel!.isWeatherReq!) { + if (dateTime.day > currentLastTimeUpdated.day) { + await getWeatherDetailsFromServer(); + } + } + + if (currentWidgetsConfigModel!.isPrayerTimeReq!) { + if (dateTime.day > currentLastTimeUpdated.day) { + await getPrayerDetailsFromServer(); + } + } + }); + } + Future startSignalHubConnection() async { if (!signalRHelper.getConnectionState()) { await getCurrentIP().whenComplete(() => signalRHelper.startSignalRConnection( @@ -71,22 +179,22 @@ class AppProvider extends ChangeNotifier { Future callPatientsAPI() async { patientTickets.clear(); - API.getCallRequestInfoByClinicInfo(currentDeviceIp, onSuccess: (waitingCalls, isQueuePatientsCalls, callConfigs) async { - patientCallConfigurations = callConfigs; - if (waitingCalls.length > patientCallConfigurations.screenMaxDisplayPatients) { - patientTickets = waitingCalls.sublist(0, patientCallConfigurations.screenMaxDisplayPatients); - } else { - patientTickets = waitingCalls; - } - isQueuePatients = isQueuePatientsCalls; - notifyListeners(); - if (patientTickets.isNotEmpty) { - voiceCallPatientTicket(patientTickets.first); - updatePatientTicket(patientTickets.first); - } - }, onFailure: (error) { - log("Api call failed with this error: ${error.toString()}"); - }); + API.getCallRequestInfoByClinicInfo(currentDeviceIp, + onSuccess: (waitingCalls, isQueuePatientsCalls, callConfigs) async { + patientCallConfigurations = callConfigs; + if (waitingCalls.length > patientCallConfigurations.screenMaxDisplayPatients) { + patientTickets = waitingCalls.sublist(0, patientCallConfigurations.screenMaxDisplayPatients); + } else { + patientTickets = waitingCalls; + } + isQueuePatients = isQueuePatientsCalls; + notifyListeners(); + if (patientTickets.isNotEmpty) { + voiceCallPatientTicket(patientTickets.first); + updatePatientTicket(patientTickets.first); + } + }, + onFailure: (error) => log("Api call failed with this error: ${error.toString()}")); } onPingReceived(data) async { @@ -286,4 +394,25 @@ class AppProvider extends ChangeNotifier { } }); } + + //SHARED PREFERENCE HANDLING + + DateTime currentLastTimeUpdated = DateTime.now(); + + Future getLastTimeUpdatedFromCache() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + + if (prefs.containsKey("lastTimeUpdated")) { + String? lastTimeUpdated = prefs.getString("lastTimeUpdated"); + currentLastTimeUpdated = DateTime.fromMillisecondsSinceEpoch(int.parse(lastTimeUpdated!)); + return lastTimeUpdated; + } else { + return null; + } + } + + Future setLastTimeUpdatedInCache({required String lasTimeUpdated}) async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + await prefs.setString("lastTimeUpdated", lasTimeUpdated); + } } diff --git a/lib/home/home_screen.dart b/lib/home/home_screen.dart index 61bab0f..b39fb28 100644 --- a/lib/home/home_screen.dart +++ b/lib/home/home_screen.dart @@ -1,3 +1,4 @@ + import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import 'package:queuing_system/core/base/app_scaffold_widget.dart'; diff --git a/lib/widget/data_display/app_texts_widget.dart b/lib/widget/data_display/app_texts_widget.dart index e4c9767..b566592 100644 --- a/lib/widget/data_display/app_texts_widget.dart +++ b/lib/widget/data_display/app_texts_widget.dart @@ -112,6 +112,7 @@ class _AppTextState extends State { ? _getFontStyle()?.copyWith( fontStyle: widget.italic ? FontStyle.italic : null, color: widget.color, + fontFamily: 'Poppins', fontWeight: widget.fontWeight ?? _getFontWeight(), height: widget.fontHeight, ) @@ -121,7 +122,7 @@ class _AppTextState extends State { fontSize: widget.fontSize ?? _getFontSize(), letterSpacing: widget.letterSpacing ?? (widget.variant == "overline" ? 1.5 : null), fontWeight: widget.fontWeight ?? _getFontWeight(), - fontFamily: widget.fontFamily ?? 'Poppins', + fontFamily: 'Poppins', decoration: widget.textDecoration, height: widget.fontHeight), ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index caf5f62..fda5d83 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -10,6 +10,7 @@ import connectivity_macos import flutter_tts import just_audio import path_provider_foundation +import shared_preferences_foundation import wakelock_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { @@ -18,5 +19,6 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { FlutterTtsPlugin.register(with: registry.registrar(forPlugin: "FlutterTtsPlugin")) JustAudioPlugin.register(with: registry.registrar(forPlugin: "JustAudioPlugin")) PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) WakelockMacosPlugin.register(with: registry.registrar(forPlugin: "WakelockMacosPlugin")) } diff --git a/pubspec.lock b/pubspec.lock index 05279b0..f396328 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -121,6 +121,14 @@ packages: url: "https://pub.dev" source: hosted version: "2.0.2" + file: + dependency: transitive + description: + name: file + sha256: "1b92bec4fc2a72f59a8e15af5f52cd441e4a7860b49499d69dfa817af20e925d" + url: "https://pub.dev" + source: hosted + version: "6.1.4" flutter: dependency: "direct main" description: flutter @@ -384,6 +392,62 @@ packages: url: "https://pub.dev" source: hosted version: "0.27.7" + shared_preferences: + dependency: "direct main" + description: + name: shared_preferences + sha256: b7f41bad7e521d205998772545de63ff4e6c97714775902c199353f8bf1511ac + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8568a389334b6e83415b6aae55378e158fbc2314e074983362d20c562780fb06" + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "7bf53a9f2d007329ee6f3df7268fd498f8373602f943c975598bbb34649b62a7" + url: "https://pub.dev" + source: hosted + version: "2.3.4" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: c2eb5bf57a2fe9ad6988121609e47d3e07bb3bdca5b6f8444e4cf302428a128a + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: d4ec5fc9ebb2f2e056c617112aa75dcf92fc2e4faaf2ae999caa297473f75d8a + url: "https://pub.dev" + source: hosted + version: "2.3.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: d762709c2bbe80626ecc819143013cc820fa49ca5e363620ee20a8b15a3e3daf + url: "https://pub.dev" + source: hosted + version: "2.2.1" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: f763a101313bd3be87edffe0560037500967de9c394a714cd598d945517f694f + url: "https://pub.dev" + source: hosted + version: "2.3.1" signalr_core: dependency: "direct main" description: diff --git a/pubspec.yaml b/pubspec.yaml index 73fc1be..3d20df2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,7 +31,7 @@ dependencies: sdk: flutter - # Base packages + # Base packages provider: ^6.0.1 get_it: ^7.1.3 connectivity: ^3.0.6 @@ -42,13 +42,12 @@ dependencies: just_audio: ^0.9.31 flutter_tts: ^3.6.3 wakelock: ^0.6.2 + shared_preferences: ^2.2.1 #signalr core signalr_core: ^1.1.1 - - dev_dependencies: flutter_test: sdk: flutter @@ -75,6 +74,7 @@ flutter: assets: - assets/images/ - assets/tones/ + - assets/icons/ fonts: