From 9bff11a0b1459db197d64fc14285d96829bcaf4a Mon Sep 17 00:00:00 2001 From: Tran Anh Tuan Date: Thu, 23 Oct 2025 11:08:03 +0700 Subject: [PATCH] chore(group): fix delay show group when switch and convert to relative import --- analysis_options.yaml | 4 + lib/bloc/device_detail_bloc.dart | 2 - lib/bloc/device_logs_bloc.dart | 1 - .../device_notification_settings_bloc.dart | 1 - lib/bloc/device_update_bloc.dart | 1 - lib/bloc/group_detail_bloc.dart | 2 +- lib/bloc/home_bloc.dart | 4 +- lib/bloc/inter_family_bloc.dart | 1 - lib/feature/bell/bell_screen.dart | 1 - .../device_log/device_logs_screen.dart | 2 +- lib/feature/devices/delete_device_widget.dart | 1 + .../device_detail/device_detail_screen.dart | 6 +- .../device_update/device_update_screen.dart | 1 - .../devices/device_update/map_dialog.dart | 26 +- .../devices/devices_manager_screen.dart | 2 +- lib/feature/error/not_found_screen.dart | 5 +- lib/feature/home/shared/alert_card.dart | 2 +- lib/feature/home/shared/overview_card.dart | 1 + lib/feature/home/shared/status_card.dart | 1 + .../add_device_to_group_dialog.dart | 2 +- .../group_detail/group_detail_screen.dart | 3 +- .../inter_family/groups/groups_screen.dart | 250 +++++++----------- .../inter_family/inter_family_screen.dart | 231 +++++++++------- lib/feature/main/main_screen.dart | 2 +- .../map/widget/on_tap_marker_widget.dart | 2 +- .../map/widget/show_direction_widget.dart | 3 +- .../map/widget/show_nearest_place.dart | 2 +- .../settings/sim_data/sim_data_screen.dart | 2 +- lib/product/constant/app/app_constants.dart | 2 +- lib/product/extension/context_extension.dart | 5 +- lib/product/lang/l10n/app_localizations.dart | 23 +- lib/product/lang/language_model.dart | 4 +- .../permission/location_permission.dart | 4 +- lib/product/services/map_services.dart | 1 - .../services/notification_services.dart | 102 +++---- lib/product/shared/shared_background.dart | 3 +- .../shared/shared_language_switch.dart | 3 +- lib/product/shared/shared_snack_bar.dart | 3 +- lib/product/theme/app_theme_dark.dart | 2 +- lib/product/utils/device_utils.dart | 11 +- lib/product/utils/permission_handler.dart | 10 +- lib/product/utils/qr_utils.dart | 1 + lib/product/utils/response_status_utils.dart | 1 + lib/product/utils/responsive_text_utils.dart | 1 + 44 files changed, 357 insertions(+), 380 deletions(-) diff --git a/analysis_options.yaml b/analysis_options.yaml index 12c7678..8cd9160 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml @@ -7,6 +7,10 @@ # The following line activates a set of recommended lints for Flutter apps, # packages, and plugins designed to encourage good coding practices. +analyzer: + errors: + use_build_context_synchronously: ignore + avoid_print: ignore include: package:flutter_lints/flutter.yaml linter: diff --git a/lib/bloc/device_detail_bloc.dart b/lib/bloc/device_detail_bloc.dart index 5460a51..9055c79 100644 --- a/lib/bloc/device_detail_bloc.dart +++ b/lib/bloc/device_detail_bloc.dart @@ -1,5 +1,4 @@ // ignore_for_file: use_build_context_synchronously - import 'dart:async'; import 'dart:developer'; import 'package:flutter/material.dart'; @@ -9,7 +8,6 @@ import '../product/services/api_services.dart'; import '../product/utils/date_time_utils.dart'; import '../feature/device_log/device_logs_model.dart'; import '../feature/devices/device_model.dart'; - import '../product/base/bloc/base_bloc.dart'; import '../product/utils/device_utils.dart'; diff --git a/lib/bloc/device_logs_bloc.dart b/lib/bloc/device_logs_bloc.dart index 1141905..fb9c132 100644 --- a/lib/bloc/device_logs_bloc.dart +++ b/lib/bloc/device_logs_bloc.dart @@ -6,7 +6,6 @@ import '../product/base/bloc/base_bloc.dart'; import '../product/constant/app/app_constants.dart'; import '../product/services/api_services.dart'; import '../product/utils/date_time_utils.dart'; - import '../product/utils/device_utils.dart'; import '../feature/device_log/device_logs_model.dart'; diff --git a/lib/bloc/device_notification_settings_bloc.dart b/lib/bloc/device_notification_settings_bloc.dart index bbed7af..7923b81 100644 --- a/lib/bloc/device_notification_settings_bloc.dart +++ b/lib/bloc/device_notification_settings_bloc.dart @@ -1,5 +1,4 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import '../feature/settings/device_notification_settings/device_notification_settings_model.dart'; diff --git a/lib/bloc/device_update_bloc.dart b/lib/bloc/device_update_bloc.dart index f639c6d..500d694 100644 --- a/lib/bloc/device_update_bloc.dart +++ b/lib/bloc/device_update_bloc.dart @@ -2,7 +2,6 @@ import 'dart:async'; import 'dart:developer'; - import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:intl/intl.dart'; diff --git a/lib/bloc/group_detail_bloc.dart b/lib/bloc/group_detail_bloc.dart index 1fe0659..340833b 100644 --- a/lib/bloc/group_detail_bloc.dart +++ b/lib/bloc/group_detail_bloc.dart @@ -1,8 +1,8 @@ // ignore_for_file: use_build_context_synchronously import 'dart:async'; - import 'package:flutter/widgets.dart'; + import '../feature/devices/device_model.dart'; import '../product/base/bloc/base_bloc.dart'; import '../product/services/api_services.dart'; diff --git a/lib/bloc/home_bloc.dart b/lib/bloc/home_bloc.dart index f32e710..d170c23 100644 --- a/lib/bloc/home_bloc.dart +++ b/lib/bloc/home_bloc.dart @@ -1,8 +1,7 @@ import 'dart:async'; - import 'package:flutter/material.dart'; -import 'package:sfm_app/product/extension/context_extension.dart'; +import '../product/extension/context_extension.dart'; import '../product/services/api_services.dart'; import '../feature/home/device_alias_model.dart'; import '../product/base/bloc/base_bloc.dart'; @@ -65,7 +64,6 @@ class HomeBloc extends BlocBase { void getOwnerDeviceState(BuildContext context,List allDevices) async { // int notificationCount = 0; Map> ownerDevicesStatus = {}; - List ownerDevicesState = []; if (!context.mounted) return; sinkOwnerDevicesStatus.add(ownerDevicesStatus); diff --git a/lib/bloc/inter_family_bloc.dart b/lib/bloc/inter_family_bloc.dart index 571f31e..7738ef2 100644 --- a/lib/bloc/inter_family_bloc.dart +++ b/lib/bloc/inter_family_bloc.dart @@ -1,7 +1,6 @@ // ignore_for_file: use_build_context_synchronously import 'dart:async'; - import 'package:flutter/material.dart'; import '../product/constant/app/app_constants.dart'; diff --git a/lib/feature/bell/bell_screen.dart b/lib/feature/bell/bell_screen.dart index af699b7..2d4a3dd 100644 --- a/lib/feature/bell/bell_screen.dart +++ b/lib/feature/bell/bell_screen.dart @@ -1,5 +1,4 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import '../../product/services/language_services.dart'; diff --git a/lib/feature/device_log/device_logs_screen.dart b/lib/feature/device_log/device_logs_screen.dart index 3b09211..5dff8b8 100644 --- a/lib/feature/device_log/device_logs_screen.dart +++ b/lib/feature/device_log/device_logs_screen.dart @@ -1,6 +1,7 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; + import 'package:intl/intl.dart'; import '../../product/shared/shared_component_loading_animation.dart'; import '../../product/shared/shared_loading_animation.dart'; @@ -14,7 +15,6 @@ import '../../product/services/language_services.dart'; import '../../product/shared/shared_snack_bar.dart'; import '../../product/utils/date_time_utils.dart'; import '../../product/utils/device_utils.dart'; - import '../../product/base/bloc/base_bloc.dart'; import 'device_logs_model.dart'; diff --git a/lib/feature/devices/delete_device_widget.dart b/lib/feature/devices/delete_device_widget.dart index 863e34b..e4cec69 100644 --- a/lib/feature/devices/delete_device_widget.dart +++ b/lib/feature/devices/delete_device_widget.dart @@ -1,6 +1,7 @@ // ignore_for_file: use_build_context_synchronously import 'package:flutter/material.dart'; + import '../../bloc/devices_manager_bloc.dart'; import '../../product/constant/enums/role_enums.dart'; import '../../product/services/api_services.dart'; diff --git a/lib/feature/devices/device_detail/device_detail_screen.dart b/lib/feature/devices/device_detail/device_detail_screen.dart index 338d084..69b4645 100644 --- a/lib/feature/devices/device_detail/device_detail_screen.dart +++ b/lib/feature/devices/device_detail/device_detail_screen.dart @@ -1,10 +1,9 @@ import 'dart:async'; - import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart'; -import 'package:sfm_app/product/shared/shared_component_loading_animation.dart'; import 'package:simple_ripple_animation/simple_ripple_animation.dart'; +import 'package:google_maps_flutter/google_maps_flutter.dart'; +import '../../../product/shared/shared_component_loading_animation.dart'; import '../../../product/constant/image/image_constants.dart'; import '../../../product/shared/shared_line_chart.dart'; import '../../../product/shared/shared_curve.dart'; @@ -15,7 +14,6 @@ import '../../../product/base/bloc/base_bloc.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/language_services.dart'; import '../../../product/utils/device_utils.dart'; - import '../../../product/constant/icon/icon_constants.dart'; import '../../../bloc/device_detail_bloc.dart'; diff --git a/lib/feature/devices/device_update/device_update_screen.dart b/lib/feature/devices/device_update/device_update_screen.dart index e8042c0..a77f0f3 100644 --- a/lib/feature/devices/device_update/device_update_screen.dart +++ b/lib/feature/devices/device_update/device_update_screen.dart @@ -9,7 +9,6 @@ import '../../../product/base/bloc/base_bloc.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/api_services.dart'; import '../../../product/services/language_services.dart'; - import '../../../product/shared/model/district_model.dart'; import '../../../product/shared/model/province_model.dart'; import '../../../product/shared/model/ward_model.dart'; diff --git a/lib/feature/devices/device_update/map_dialog.dart b/lib/feature/devices/device_update/map_dialog.dart index e7d48c8..a70c7c6 100644 --- a/lib/feature/devices/device_update/map_dialog.dart +++ b/lib/feature/devices/device_update/map_dialog.dart @@ -2,15 +2,14 @@ import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'package:http/http.dart' as http; - import 'package:flutter/material.dart'; -import 'package:google_maps_flutter/google_maps_flutter.dart' ; +import 'package:google_maps_flutter/google_maps_flutter.dart'; + import '../../../bloc/device_update_bloc.dart'; import '../../../product/constant/app/app_constants.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/language_services.dart'; import '../../../product/shared/find_location_maps/shared_map_search_location.dart'; - import '../../../product/shared/find_location_maps/model/prediction_model.dart'; import '../../../product/shared/shared_transition.dart'; import 'geocode_model.dart'; @@ -64,7 +63,8 @@ showMapDialog( String latitude = mapDialogLatitudeController.text; String longitude = mapDialogLongitudeController.text; log("Finish -- Latitude: $latitude, longitude: $longitude --"); - getDataFromApi(context,latitude, longitude, deviceUpdateBloc); + getDataFromApi( + context, latitude, longitude, deviceUpdateBloc); latitudeController.text = mapDialogLatitudeController.text; longitudeController.text = @@ -184,7 +184,7 @@ addMarker( updateCameraPosition(position, 14, mapController); } -void getDataFromApi(BuildContext context,String latitude, String longitude, +void getDataFromApi(BuildContext context, String latitude, String longitude, DeviceUpdateBloc deviceUpdateBloc) async { String path = "maps/api/geocode/json?latlng=$latitude,$longitude&language=vi&result_type=political&key=${ApplicationConstants.MAP_KEY}"; @@ -215,7 +215,7 @@ void getDataFromApi(BuildContext context,String latitude, String longitude, log("$key: $value"); }); - await _processLocations(context,locations, deviceUpdateBloc); + await _processLocations(context, locations, deviceUpdateBloc); } Map _extractLocationComponents( @@ -248,24 +248,24 @@ Future _processLocations(BuildContext context, String wardNameFromAPI = locations['wardkey'] ?? ""; final province = - await deviceUpdateBloc.getProvinceByName(context,provinceNameFromAPI); + await deviceUpdateBloc.getProvinceByName(context, provinceNameFromAPI); if (province.name != "null") { log("Province: ${province.fullName}, ProvinceCode: ${province.code}"); deviceUpdateBloc.sinkProvinceData .add({"code": province.code!, "name": province.fullName!}); deviceUpdateBloc.getAllProvinces(context); - final district = await deviceUpdateBloc.getDistrictByName(context, - districtNameFromAPI, province.code!); + final district = await deviceUpdateBloc.getDistrictByName( + context, districtNameFromAPI, province.code!); log("Districtname: ${district.fullName}, districtCode: ${district.code}"); - deviceUpdateBloc.getAllDistricts(context,province.code!); + deviceUpdateBloc.getAllDistricts(context, province.code!); if (district.name != "null") { deviceUpdateBloc.sinkDistrictData .add({"code": district.code!, "name": district.fullName!}); - final ward = - await deviceUpdateBloc.getWardByName(context,wardNameFromAPI, district.code!); + final ward = await deviceUpdateBloc.getWardByName( + context, wardNameFromAPI, district.code!); log("Wardname: ${ward.fullName}, WardCode: ${ward.code}"); - deviceUpdateBloc.getAllWards(context,district.code!); + deviceUpdateBloc.getAllWards(context, district.code!); if (ward.name != "null") { log("Xac dinh duoc het thong tin tu toa do"); deviceUpdateBloc.sinkWardData diff --git a/lib/feature/devices/devices_manager_screen.dart b/lib/feature/devices/devices_manager_screen.dart index 5bf8866..3369d59 100644 --- a/lib/feature/devices/devices_manager_screen.dart +++ b/lib/feature/devices/devices_manager_screen.dart @@ -1,9 +1,9 @@ import 'dart:async'; - import 'package:data_table_2/data_table_2.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; + import '../../product/shared/shared_component_loading_animation.dart'; import '../../product/shared/shared_loading_animation.dart'; import 'add_new_device_widget.dart'; diff --git a/lib/feature/error/not_found_screen.dart b/lib/feature/error/not_found_screen.dart index e62ee83..886def9 100644 --- a/lib/feature/error/not_found_screen.dart +++ b/lib/feature/error/not_found_screen.dart @@ -1,7 +1,8 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; -import 'package:sfm_app/product/constant/enums/app_route_enums.dart'; -import 'package:sfm_app/product/constant/image/image_constants.dart'; + +import '../../product/constant/enums/app_route_enums.dart'; +import '../../product/constant/image/image_constants.dart'; class NotFoundScreen extends StatelessWidget { const NotFoundScreen({super.key}); diff --git a/lib/feature/home/shared/alert_card.dart b/lib/feature/home/shared/alert_card.dart index cb5486b..58d0ba0 100644 --- a/lib/feature/home/shared/alert_card.dart +++ b/lib/feature/home/shared/alert_card.dart @@ -3,13 +3,13 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; + import '../../../product/shared/shared_rocket_container.dart'; import '../../../product/constant/enums/app_route_enums.dart'; import '../../../product/constant/image/image_constants.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/language_services.dart'; import '../../../product/utils/device_utils.dart'; - import '../../../product/constant/icon/icon_constants.dart'; import '../device_alias_model.dart'; diff --git a/lib/feature/home/shared/overview_card.dart b/lib/feature/home/shared/overview_card.dart index 9b95680..e4173df 100644 --- a/lib/feature/home/shared/overview_card.dart +++ b/lib/feature/home/shared/overview_card.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import 'status_card.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/language_services.dart'; diff --git a/lib/feature/home/shared/status_card.dart b/lib/feature/home/shared/status_card.dart index 08dc479..4cb48e9 100644 --- a/lib/feature/home/shared/status_card.dart +++ b/lib/feature/home/shared/status_card.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import '../../../product/extension/context_extension.dart'; class StatusCard extends StatelessWidget { diff --git a/lib/feature/inter_family/group_detail/add_device_to_group_dialog.dart b/lib/feature/inter_family/group_detail/add_device_to_group_dialog.dart index 73b7666..cf548c9 100644 --- a/lib/feature/inter_family/group_detail/add_device_to_group_dialog.dart +++ b/lib/feature/inter_family/group_detail/add_device_to_group_dialog.dart @@ -2,11 +2,11 @@ import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; + import '../../../product/extension/context_extension.dart'; import '../../../bloc/group_detail_bloc.dart'; import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/services/language_services.dart'; - import '../../devices/device_model.dart'; import 'group_detail_model.dart'; diff --git a/lib/feature/inter_family/group_detail/group_detail_screen.dart b/lib/feature/inter_family/group_detail/group_detail_screen.dart index 8454bf4..7e9e4c0 100644 --- a/lib/feature/inter_family/group_detail/group_detail_screen.dart +++ b/lib/feature/inter_family/group_detail/group_detail_screen.dart @@ -1,8 +1,8 @@ // ignore_for_file: use_build_context_synchronously import 'dart:async'; - import 'package:flutter/material.dart'; + import '../../../bloc/group_detail_bloc.dart'; import '../../../product/shared/shared_loading_animation.dart'; import 'group_detail_model.dart'; @@ -14,7 +14,6 @@ import '../../../product/services/api_services.dart'; import '../../../product/services/language_services.dart'; import '../../../product/utils/device_utils.dart'; import '../../../product/utils/response_status_utils.dart'; - import 'add_device_to_group_dialog.dart'; class DetailGroupScreen extends StatefulWidget { diff --git a/lib/feature/inter_family/groups/groups_screen.dart b/lib/feature/inter_family/groups/groups_screen.dart index 79ae184..36566f0 100644 --- a/lib/feature/inter_family/groups/groups_screen.dart +++ b/lib/feature/inter_family/groups/groups_screen.dart @@ -1,187 +1,125 @@ // ignore_for_file: use_build_context_synchronously -import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import '../../../product/constant/enums/app_route_enums.dart'; -import '../../../product/shared/shared_component_loading_animation.dart'; -import '../../../product/shared/shared_loading_animation.dart'; import 'groups_model.dart'; +import 'groups_widget.dart'; import '../../../bloc/inter_family_bloc.dart'; import '../inter_family_widget.dart'; -import '../../../product/base/bloc/base_bloc.dart'; import '../../../product/constant/app/app_constants.dart'; import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/language_services.dart'; -import 'groups_widget.dart'; - -class GroupsScreen extends StatefulWidget { - const GroupsScreen({super.key, required this.role}); +/// Stateless widget that renders a provided list of groups. The parent +/// screen owns fetching/updating the list; this widget only displays it and +/// forwards actions to the provided [InterFamilyBloc]. +class GroupsScreen extends StatelessWidget { + const GroupsScreen( + {super.key, + required this.role, + required this.groups, + required this.interFamilyBloc}); final String role; - - @override - State createState() => _GroupsScreenState(); -} - -class _GroupsScreenState extends State { - late InterFamilyBloc interFamilyBloc; - Timer? getAllGroupsTimer; - - @override - void initState() { - super.initState(); - interFamilyBloc = BlocProvider.of(context); - const duration = Duration(seconds: 5); - getAllGroupsTimer = Timer.periodic( - duration, - (Timer t) => interFamilyBloc.getAllGroup(context, widget.role), - ); - } - - @override - void dispose() { - getAllGroupsTimer?.cancel(); - super.dispose(); - } + final List groups; + final InterFamilyBloc interFamilyBloc; @override Widget build(BuildContext context) { - if (widget.role == ApplicationConstants.OWNER_GROUP || - widget.role == ApplicationConstants.PARTICIPANT_GROUP) { - return StreamBuilder>( - stream: interFamilyBloc.streamCurrentGroups, - builder: (context, groupsSnapshot) { - if (groupsSnapshot.data == null) { - interFamilyBloc.getAllGroup(context,widget.role); - return const SharedLoadingAnimation(); - } else if (groupsSnapshot.data!.isEmpty) { - return Center( - child: Text(appLocalization(context).dont_have_group), - ); - } else { - return Scaffold( - body: groupsSnapshot.data?.isEmpty ?? true - ? const SharedComponentLoadingAnimation() - : ListView.builder( - itemCount: groupsSnapshot.data!.length, - itemBuilder: (context, index) { - return ListTile( - onTap: () { - context.pushNamed(AppRoutes.GROUP_DETAIL.name, - pathParameters: { - "groupId": groupsSnapshot.data![index].id! - }, - extra: widget.role); - }, - leading: IconConstants.instance - .getMaterialIcon(Icons.diversity_2), - title: Text( - groupsSnapshot.data![index].name ?? '', - style: const TextStyle(fontWeight: FontWeight.bold), - ), - subtitle: Text( - groupsSnapshot.data![index].description ?? ""), - trailing: - widget.role == ApplicationConstants.OWNER_GROUP - ? PopupMenuButton( - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(8.0), - bottomRight: Radius.circular(8.0), - topLeft: Radius.circular(8.0), - topRight: Radius.circular(8.0), - ), - ), - itemBuilder: (ctx) => [ - _buildPopupMenuItem( - groupsSnapshot.data![index], - context, - appLocalization(context) - .share_group_title, - Icons.share, - 4), - _buildPopupMenuItem( - groupsSnapshot.data![index], - context, - appLocalization(context) - .change_group_infomation_title, - Icons.settings_backup_restore, - 2), - _buildPopupMenuItem( - groupsSnapshot.data![index], - context, - appLocalization(context) - .delete_group_title, - Icons.delete_forever_rounded, - 3), - ], - icon: const Icon(Icons.more_horiz), - ) - : const SizedBox.shrink(), - ); - }, - ), - ); - } - }, - ); - } else { + if (role != ApplicationConstants.OWNER_GROUP && + role != ApplicationConstants.PARTICIPANT_GROUP) { return const SizedBox.shrink(); } + + if (groups.isEmpty) { + return Center(child: Text(appLocalization(context).dont_have_group)); + } + + return ListView.builder( + itemCount: groups.length, + itemBuilder: (context, index) { + final group = groups[index]; + return ListTile( + onTap: () => context.pushNamed(AppRoutes.GROUP_DETAIL.name, + pathParameters: {"groupId": group.id!}, extra: role), + leading: IconConstants.instance.getMaterialIcon(Icons.diversity_2), + title: Text(group.name ?? '', + style: const TextStyle(fontWeight: FontWeight.bold)), + subtitle: Text(group.description ?? ''), + trailing: role == ApplicationConstants.OWNER_GROUP + ? _ownerPopupMenu(group, context) + : null, + ); + }, + ); } - PopupMenuItem _buildPopupMenuItem(Group group, BuildContext context, - String title, IconData iconData, int position) { - return PopupMenuItem( + Widget _ownerPopupMenu(Group group, BuildContext context) { + return PopupMenuButton( + shape: const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8.0))), + itemBuilder: (ctx) => [ + _buildPopupMenuItem(group, context, + appLocalization(context).share_group_title, Icons.share, 4), + _buildPopupMenuItem( + group, + context, + appLocalization(context).change_group_infomation_title, + Icons.settings_backup_restore, + 2), + _buildPopupMenuItem( + group, + context, + appLocalization(context).delete_group_title, + Icons.delete_forever_rounded, + 3), + ], + icon: const Icon(Icons.more_horiz), + ); + } + + PopupMenuItem _buildPopupMenuItem(Group group, BuildContext context, + String title, IconData iconData, int value) { + return PopupMenuItem( + value: value, + child: Row(children: [ + Icon(iconData, color: Colors.black), + const SizedBox(width: 10), + Text(title) + ]), onTap: () { - if (title == appLocalization(context).share_group_title) { - Future.delayed(context.lowDuration, () { + Future.delayed(context.lowDuration, () { + if (title == appLocalization(context).share_group_title) { shareGroup(context, group); - }); - } else if (title == - appLocalization(context).change_group_infomation_title) { - Future.delayed(context.lowDuration, () { + } else if (title == + appLocalization(context).change_group_infomation_title) { createOrJoinGroupDialog( - context, - interFamilyBloc, - widget.role, - appLocalization(context).change_group_infomation_content, - appLocalization(context).group_name_title, - group.name!, - false, - group.id!, - appLocalization(context).description_group, - group.description ?? ""); - }); - } else if (title == appLocalization(context).delete_group_title) { - Future.delayed(context.lowDuration, () { + context, + interFamilyBloc, + role, + appLocalization(context).change_group_infomation_content, + appLocalization(context).group_name_title, + group.name ?? '', + false, + group.id ?? '', + appLocalization(context).description_group, + group.description ?? '', + ); + } else if (title == appLocalization(context).delete_group_title) { showActionDialog( - context, - widget.role, - interFamilyBloc, - appLocalization(context).delete_group_title, - appLocalization(context).delete_group_content, - group); - }); - } else {} + context, + role, + interFamilyBloc, + appLocalization(context).delete_group_title, + appLocalization(context).delete_group_content, + group, + ); + } + }); }, - value: position, - child: Row( - // mainAxisAlignment: MainAxisAlignment.spaceBetween, - children: [ - Icon( - iconData, - color: Colors.black, - ), - const SizedBox(width: 10), - Text(title), - ], - ), ); } } diff --git a/lib/feature/inter_family/inter_family_screen.dart b/lib/feature/inter_family/inter_family_screen.dart index 733314a..a171358 100644 --- a/lib/feature/inter_family/inter_family_screen.dart +++ b/lib/feature/inter_family/inter_family_screen.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'groups/groups_screen.dart'; +import 'groups/groups_model.dart'; import '../../bloc/inter_family_bloc.dart'; import 'inter_family_widget.dart'; import '../../product/base/bloc/base_bloc.dart'; @@ -25,22 +26,31 @@ class _InterFamilyScreenState extends State { void initState() { super.initState(); interFamilyBloc = BlocProvider.of(context); + // fetch initial groups for the default selected tab + WidgetsBinding.instance.addPostFrameCallback((_) { + interFamilyBloc.getAllGroup( + context, + _selectedIndex == 0 + ? ApplicationConstants.OWNER_GROUP + : ApplicationConstants.PARTICIPANT_GROUP); + }); } - final _widgetOptions = [ - BlocProvider( - blocBuilder: () => InterFamilyBloc(), - child: const GroupsScreen( - role: ApplicationConstants.OWNER_GROUP, - ), - ), - BlocProvider( - blocBuilder: () => InterFamilyBloc(), - child: const GroupsScreen( - role: ApplicationConstants.PARTICIPANT_GROUP, - ), - ), - ]; + List ownerGroups = []; + List participantGroups = []; + + List get _widgetOptions => [ + GroupsScreen( + role: ApplicationConstants.OWNER_GROUP, + groups: ownerGroups, + interFamilyBloc: interFamilyBloc, + ), + GroupsScreen( + role: ApplicationConstants.PARTICIPANT_GROUP, + groups: participantGroups, + interFamilyBloc: interFamilyBloc, + ), + ]; @override Widget build(BuildContext context) { @@ -49,96 +59,109 @@ class _InterFamilyScreenState extends State { stream: interFamilyBloc.streamSelectedScreen, initialData: _selectedIndex, builder: (context, selectSnapshot) { - return Scaffold( - appBar: AppBar( - actions: [ - ElevatedButton( - onPressed: () { - if (selectSnapshot.data == 0) { - createOrJoinGroupDialog( - context, - interFamilyBloc, - selectSnapshot.data! == 0 - ? ApplicationConstants.OWNER_GROUP - : ApplicationConstants.PARTICIPANT_GROUP, - appLocalization(context).add_new_group, - appLocalization(context).group_name_title, - "", - false, - "", - "", - ""); - } else { - createOrJoinGroupDialog( - context, - interFamilyBloc, - selectSnapshot.data! == 0 - ? ApplicationConstants.OWNER_GROUP - : ApplicationConstants.PARTICIPANT_GROUP, - appLocalization(context).join_group, - appLocalization(context).group_id_title, - '', - true, - "", - appLocalization(context).group_name_title, - ""); - } - }, - style: ElevatedButton.styleFrom( - shape: const CircleBorder(), + // subscribe to groups stream and update local lists so child widgets render instantly + return StreamBuilder>( + stream: interFamilyBloc.streamCurrentGroups, + builder: (context, groupsSnapshot) { + if (groupsSnapshot.hasData) { + final all = groupsSnapshot.data!; + ownerGroups = all + .where((g) => g.isOwner == true && g.visibility == 'PUBLIC') + .toList(); + participantGroups = all + .where((g) => g.isOwner == null && g.visibility == 'PUBLIC') + .toList(); + } + // build UI below + return Scaffold( + appBar: AppBar( + actions: [ + ElevatedButton( + onPressed: () { + if (selectSnapshot.data == 0) { + createOrJoinGroupDialog( + context, + interFamilyBloc, + ApplicationConstants.OWNER_GROUP, + appLocalization(context).add_new_group, + appLocalization(context).group_name_title, + "", + false, + "", + "", + "", + ); + } else { + createOrJoinGroupDialog( + context, + interFamilyBloc, + ApplicationConstants.PARTICIPANT_GROUP, + appLocalization(context).join_group, + appLocalization(context).group_id_title, + '', + true, + "", + appLocalization(context).group_name_title, + "", + ); + } + }, + style: + ElevatedButton.styleFrom(shape: const CircleBorder()), + child: IconConstants.instance.getMaterialIcon(Icons.add), + ), + ], + leading: Builder( + builder: (context) { + return IconButton( + onPressed: () { + Scaffold.of(context).openDrawer(); + }, + icon: const Icon(Icons.menu), + ); + }, + ), + title: StreamBuilder( + stream: interFamilyBloc.streamTitleScreen, + initialData: title, + builder: (context, titleSnapshot) { + return Center( + child: Text(titleSnapshot.data ?? title), + ); + }, ), - child: IconConstants.instance.getMaterialIcon(Icons.add), ), - ], - leading: Builder( - builder: (context) { - return IconButton( - onPressed: () { - Scaffold.of(context).openDrawer(); - }, - icon: const Icon(Icons.menu), - ); - }, - ), - title: StreamBuilder( - stream: interFamilyBloc.streamTitleScreen, - initialData: title, - builder: (context, titleSnapshot) { - return Center( - child: Text(titleSnapshot.data ?? title), - ); - }, - ), - ), - drawer: Drawer( - width: context.dynamicWidth(0.6), - child: ListView( - padding: EdgeInsets.zero, - children: [ - ListTile( - title: Text(appLocalization(context).my_group_title), - selected: _selectedIndex == 0, - onTap: () { - _onItemTapped(0); - title = appLocalization(context).my_group_title; - interFamilyBloc.sinkTitleScreen.add(title); - Navigator.pop(context); - }, + drawer: Drawer( + width: context.dynamicWidth(0.6), + child: ListView( + padding: EdgeInsets.zero, + children: [ + ListTile( + title: Text(appLocalization(context).my_group_title), + selected: _selectedIndex == 0, + onTap: () { + _onItemTapped(0); + title = appLocalization(context).my_group_title; + interFamilyBloc.sinkTitleScreen.add(title); + Navigator.pop(context); + }, + ), + ListTile( + title: Text(appLocalization(context).invite_group), + selected: _selectedIndex == 1, + onTap: () { + _onItemTapped(1); + title = appLocalization(context).invite_group; + interFamilyBloc.sinkTitleScreen.add(title); + Navigator.pop(context); + }, + ), + ], ), - ListTile( - title: Text(appLocalization(context).invite_group), - selected: _selectedIndex == 1, - onTap: () { - _onItemTapped(1); - title = appLocalization(context).invite_group; - interFamilyBloc.sinkTitleScreen.add(title); - Navigator.pop(context); - }, - ), - ], - ), - ), - body: _widgetOptions[selectSnapshot.data ?? _selectedIndex], + ), + body: _widgetOptions[selectSnapshot.data ?? _selectedIndex], + ); + }, ); }, ); @@ -159,5 +182,11 @@ class _InterFamilyScreenState extends State { interFamilyBloc.sinkSelectedScreen.add(_selectedIndex); isLoading = false; interFamilyBloc.sinkIsLoading.add(isLoading); + // fetch groups for the selected tab immediately + interFamilyBloc.getAllGroup( + context, + _selectedIndex == 0 + ? ApplicationConstants.OWNER_GROUP + : ApplicationConstants.PARTICIPANT_GROUP); } } diff --git a/lib/feature/main/main_screen.dart b/lib/feature/main/main_screen.dart index e79ca33..ab28826 100644 --- a/lib/feature/main/main_screen.dart +++ b/lib/feature/main/main_screen.dart @@ -7,8 +7,8 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:badges/badges.dart' as badges; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import 'package:sfm_app/product/shared/shared_snack_bar.dart'; +import '../../product/shared/shared_snack_bar.dart'; import '../../product/services/notification_services.dart'; import '../../product/utils/permission_handler.dart'; import '../../product/permission/notification_permission.dart'; diff --git a/lib/feature/map/widget/on_tap_marker_widget.dart b/lib/feature/map/widget/on_tap_marker_widget.dart index 1532c34..72a05f0 100644 --- a/lib/feature/map/widget/on_tap_marker_widget.dart +++ b/lib/feature/map/widget/on_tap_marker_widget.dart @@ -3,6 +3,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; + import 'show_direction_widget.dart'; import 'show_nearest_place.dart'; import '../../../product/constant/icon/icon_constants.dart'; @@ -11,7 +12,6 @@ import '../../../bloc/map_bloc.dart'; import '../../../product/services/api_services.dart'; import '../../../product/services/language_services.dart'; import '../../../product/utils/device_utils.dart'; - import '../../devices/device_model.dart'; onTapMarker( diff --git a/lib/feature/map/widget/show_direction_widget.dart b/lib/feature/map/widget/show_direction_widget.dart index 1a5f027..ea2a253 100644 --- a/lib/feature/map/widget/show_direction_widget.dart +++ b/lib/feature/map/widget/show_direction_widget.dart @@ -1,12 +1,11 @@ import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; import 'package:maps_launcher/maps_launcher.dart'; + import '../../../product/constant/icon/icon_constants.dart'; import '../../../product/extension/context_extension.dart'; import '../../../product/services/language_services.dart'; - import '../../../bloc/map_bloc.dart'; showDirections( diff --git a/lib/feature/map/widget/show_nearest_place.dart b/lib/feature/map/widget/show_nearest_place.dart index 2e1889a..e6ca573 100644 --- a/lib/feature/map/widget/show_nearest_place.dart +++ b/lib/feature/map/widget/show_nearest_place.dart @@ -1,9 +1,9 @@ // ignore_for_file: use_build_context_synchronously import 'dart:async'; - import 'package:flutter/material.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; + import '../../../bloc/map_bloc.dart'; import 'show_direction_widget.dart'; import '../../../product/constant/icon/icon_constants.dart'; diff --git a/lib/feature/settings/sim_data/sim_data_screen.dart b/lib/feature/settings/sim_data/sim_data_screen.dart index ad334f5..e135544 100644 --- a/lib/feature/settings/sim_data/sim_data_screen.dart +++ b/lib/feature/settings/sim_data/sim_data_screen.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:sfm_app/feature/settings/sim_data/shared_sim_component.dart'; +import 'shared_sim_component.dart'; import '../../../product/services/language_services.dart'; import '../../../product/shared/shared_loading_animation.dart'; import '../../../bloc/sim_data_bloc.dart'; diff --git a/lib/product/constant/app/app_constants.dart b/lib/product/constant/app/app_constants.dart index e2784ab..8528b9f 100644 --- a/lib/product/constant/app/app_constants.dart +++ b/lib/product/constant/app/app_constants.dart @@ -1,4 +1,4 @@ -// ignore_for_file: constant_identifier_names +// ignore_for_file: constant_identifier_names, non_constant_identifier_names class ApplicationConstants { static const APP_NAME = "Smatec SFM"; diff --git a/lib/product/extension/context_extension.dart b/lib/product/extension/context_extension.dart index cab7a38..524d6e6 100644 --- a/lib/product/extension/context_extension.dart +++ b/lib/product/extension/context_extension.dart @@ -1,9 +1,8 @@ import 'dart:math'; - import 'package:flutter/material.dart'; -import 'package:sfm_app/product/utils/app_logger_utils.dart'; -import 'package:sfm_app/product/utils/responsive_text_utils.dart'; +import '../utils/app_logger_utils.dart'; +import '../utils/responsive_text_utils.dart'; import '../theme/app_theme_light.dart'; // MEDIA diff --git a/lib/product/lang/l10n/app_localizations.dart b/lib/product/lang/l10n/app_localizations.dart index ba561dc..b93f20c 100644 --- a/lib/product/lang/l10n/app_localizations.dart +++ b/lib/product/lang/l10n/app_localizations.dart @@ -63,7 +63,7 @@ import 'app_localizations_vi.dart'; /// property. abstract class AppLocalizations { AppLocalizations(String locale) - : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -86,16 +86,16 @@ abstract class AppLocalizations { /// of delegates is preferred or required. static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ Locale('en'), - Locale('vi'), + Locale('vi') ]; /// No description provided for @description_NOTUSE. @@ -1620,9 +1620,8 @@ AppLocalizations lookupAppLocalizations(Locale locale) { } throw FlutterError( - 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' - 'an issue with the localizations generation tool. Please file an issue ' - 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.', - ); + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.'); } diff --git a/lib/product/lang/language_model.dart b/lib/product/lang/language_model.dart index ec5a7be..5e11f45 100644 --- a/lib/product/lang/language_model.dart +++ b/lib/product/lang/language_model.dart @@ -1,5 +1,5 @@ -import 'package:sfm_app/product/constant/icon/icon_constants.dart'; -import 'package:sfm_app/product/constant/lang/language_constants.dart'; +import '../constant/icon/icon_constants.dart'; +import '../constant/lang/language_constants.dart'; class Language { final int id; diff --git a/lib/product/permission/location_permission.dart b/lib/product/permission/location_permission.dart index 315cc40..d4aa0f3 100644 --- a/lib/product/permission/location_permission.dart +++ b/lib/product/permission/location_permission.dart @@ -1,10 +1,10 @@ import 'dart:developer'; import 'dart:io'; - import 'package:app_settings/app_settings.dart'; import 'package:flutter/material.dart'; import 'package:permission_handler/permission_handler.dart'; -import 'package:sfm_app/product/base/widget/dialog/request_permission_dialog.dart'; + +import '../base/widget/dialog/request_permission_dialog.dart'; class LocationPermissionRequest { LocationPermissionRequest._init(); diff --git a/lib/product/services/map_services.dart b/lib/product/services/map_services.dart index 726ad22..fe17b75 100644 --- a/lib/product/services/map_services.dart +++ b/lib/product/services/map_services.dart @@ -59,6 +59,5 @@ class MapServices { log("Lỗi khi tìm đường"); return []; } - return []; } } diff --git a/lib/product/services/notification_services.dart b/lib/product/services/notification_services.dart index f8ba1b3..3797c2e 100644 --- a/lib/product/services/notification_services.dart +++ b/lib/product/services/notification_services.dart @@ -3,18 +3,20 @@ import 'dart:io'; import 'dart:math' as math; import 'package:alarm/alarm.dart'; import 'package:firebase_core/firebase_core.dart'; -import 'package:firebase_messaging/firebase_messaging.dart' hide NotificationSettings; +import 'package:firebase_messaging/firebase_messaging.dart' + hide NotificationSettings; import 'package:flutter/material.dart'; -import 'package:flutter_local_notifications/flutter_local_notifications.dart'; import 'package:persistent_bottom_nav_bar_v2/persistent_bottom_nav_bar_v2.dart'; -import 'package:sfm_app/product/utils/app_logger_utils.dart'; +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import '../utils/app_logger_utils.dart'; import '../../firebase_options.dart'; import '../../main.dart'; import 'alarm_services.dart'; class NotificationServices { - static final FlutterLocalNotificationsPlugin _notificationsPlugin = FlutterLocalNotificationsPlugin(); + static final FlutterLocalNotificationsPlugin _notificationsPlugin = + FlutterLocalNotificationsPlugin(); final FirebaseMessaging _messaging = FirebaseMessaging.instance; AlarmServices alarmServices = AlarmServices(); @@ -24,25 +26,29 @@ class NotificationServices { } Future initializeLocalNotifications() async { - try{ - const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); - const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings(); - const InitializationSettings initializationSettings = InitializationSettings( - android: androidInitializationSettings, - iOS: iosInitializationSettings, - ); + try { + const AndroidInitializationSettings androidInitializationSettings = + AndroidInitializationSettings('@mipmap/ic_launcher'); + const DarwinInitializationSettings iosInitializationSettings = + DarwinInitializationSettings(); + const InitializationSettings initializationSettings = + InitializationSettings( + android: androidInitializationSettings, + iOS: iosInitializationSettings, + ); - await _notificationsPlugin.initialize( - initializationSettings, - onDidReceiveNotificationResponse: (NotificationResponse response) { - dev.log("Người dùng click thông báo ở foreground với payload: ${response.payload}"); - handleMessage(response.payload,controller); - }, - ); - dev.log("Local notifications initialized"); - }catch(e){ - dev.log("Error initializing local notifications: $e"); - } + await _notificationsPlugin.initialize( + initializationSettings, + onDidReceiveNotificationResponse: (NotificationResponse response) { + dev.log( + "Người dùng click thông báo ở foreground với payload: ${response.payload}"); + handleMessage(response.payload, controller); + }, + ); + dev.log("Local notifications initialized"); + } catch (e) { + dev.log("Error initializing local notifications: $e"); + } } void firebaseInit(BuildContext context) { @@ -62,11 +68,13 @@ class NotificationServices { try { if (Platform.isAndroid) { return await _notificationsPlugin - .resolvePlatformSpecificImplementation() + .resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>() ?.requestNotificationsPermission(); } else if (Platform.isIOS) { return await _notificationsPlugin - .resolvePlatformSpecificImplementation() + .resolvePlatformSpecificImplementation< + IOSFlutterLocalNotificationsPlugin>() ?.requestPermissions(alert: true, sound: true, badge: true); } return null; @@ -84,13 +92,14 @@ class NotificationServices { final type = message.data['type'] as String? ?? 'normal'; if (title == null || body == null) { - dev.log('Skipping notification: missing title or body', name: 'Notification'); + dev.log('Skipping notification: missing title or body', + name: 'Notification'); return; } // Handle smoke warning notifications if (type == 'smoke_warning') { - await alarmServices.showAlarm(title, body); + await alarmServices.showAlarm(title, body); dev.log('Displayed smoke warning notification', name: 'Notification'); return; } @@ -100,15 +109,16 @@ class NotificationServices { const channelName = 'High Importance Notification'; const channelDescription = 'Channel description'; - final androidChannel = AndroidNotificationChannel( - channelId, - channelName, - importance: Importance.max, - description: channelDescription, - ); + // final androidChannel = AndroidNotificationChannel( + // channelId, + // channelName, + // importance: Importance.max, + // description: channelDescription, + // ); - final androidPlugin = _notificationsPlugin - .resolvePlatformSpecificImplementation(); + final androidPlugin = + _notificationsPlugin.resolvePlatformSpecificImplementation< + AndroidFlutterLocalNotificationsPlugin>(); // Delete existing channel to prevent conflicts await androidPlugin?.deleteNotificationChannel(channelId); @@ -180,12 +190,13 @@ class NotificationServices { Future setupInteractMessage(PersistentTabController controller) async { // Khi app terminated - RemoteMessage? initialMessage = await FirebaseMessaging.instance.getInitialMessage(); + RemoteMessage? initialMessage = + await FirebaseMessaging.instance.getInitialMessage(); if (initialMessage != null) { WidgetsBinding.instance.addPostFrameCallback((_) { try { - handleMessage(initialMessage.data['type'],controller); + handleMessage(initialMessage.data['type'], controller); } catch (e, stack) { dev.log("Error handling initial message: $e\n$stack"); } @@ -194,14 +205,15 @@ class NotificationServices { // Khi app ở background FirebaseMessaging.onMessageOpenedApp.listen((message) { try { - handleMessage(message.data['type'],controller); + handleMessage(message.data['type'], controller); } catch (e, stack) { dev.log("Error in onMessageOpenedApp: $e\n$stack"); } }); } - Future showBackgroundOrTerminateNotification(RemoteMessage message) async { + Future showBackgroundOrTerminateNotification( + RemoteMessage message) async { try { // Early validation of notification data final title = message.data['title'] as String?; @@ -209,7 +221,8 @@ class NotificationServices { final type = message.data['type'] as String? ?? 'normal'; if (title == null || body == null) { - dev.log('Skipping notification: missing title or body', name: 'Notification'); + dev.log('Skipping notification: missing title or body', + name: 'Notification'); return; } @@ -218,7 +231,6 @@ class NotificationServices { const channelName = 'High Importance Notification'; const channelDescription = 'Channel description'; - // Configure notification details final androidDetails = AndroidNotificationDetails( channelId, @@ -273,10 +285,10 @@ Future firebaseMessagingBackgroundHandler(RemoteMessage message) async { await Alarm.init(); if (Firebase.apps.isEmpty) { await Firebase.initializeApp( - options: DefaultFirebaseOptions.currentPlatform, - name: "sfm-notification" - ); - AppLoggerUtils.warning("Firebase đã được khởi tạo trong background handler"); + options: DefaultFirebaseOptions.currentPlatform, + name: "sfm-notification"); + AppLoggerUtils.warning( + "Firebase đã được khởi tạo trong background handler"); } else { AppLoggerUtils.warning("Firebase đã được khởi tạo trước đó"); } @@ -316,4 +328,4 @@ Future firebaseMessagingBackgroundHandler(RemoteMessage message) async { } catch (e) { AppLoggerUtils.warning("Error in background handler: $e"); } -} \ No newline at end of file +} diff --git a/lib/product/shared/shared_background.dart b/lib/product/shared/shared_background.dart index 8df40f4..b5c1bd6 100644 --- a/lib/product/shared/shared_background.dart +++ b/lib/product/shared/shared_background.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:sfm_app/product/constant/image/image_constants.dart'; + +import '../constant/image/image_constants.dart'; class SharedBackground extends StatelessWidget { final Widget child; diff --git a/lib/product/shared/shared_language_switch.dart b/lib/product/shared/shared_language_switch.dart index da540b8..55cf1e8 100644 --- a/lib/product/shared/shared_language_switch.dart +++ b/lib/product/shared/shared_language_switch.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; -import 'package:sfm_app/product/constant/icon/icon_constants.dart'; + +import '../constant/icon/icon_constants.dart'; const int _kDuration = 300; const double _kWidth = 60; diff --git a/lib/product/shared/shared_snack_bar.dart b/lib/product/shared/shared_snack_bar.dart index 33a42f0..7117c42 100644 --- a/lib/product/shared/shared_snack_bar.dart +++ b/lib/product/shared/shared_snack_bar.dart @@ -1,8 +1,9 @@ import 'package:flutter/material.dart'; -import 'package:sfm_app/product/extension/context_extension.dart'; import 'package:top_snackbar_flutter/custom_snack_bar.dart'; import 'package:top_snackbar_flutter/top_snack_bar.dart'; +import '../extension/context_extension.dart'; + void showNoIconTopSnackBar(BuildContext context, String message, Color backgroundColor, Color textColor) { if (!context.mounted) return; diff --git a/lib/product/theme/app_theme_dark.dart b/lib/product/theme/app_theme_dark.dart index 4c50548..ac9bc5d 100644 --- a/lib/product/theme/app_theme_dark.dart +++ b/lib/product/theme/app_theme_dark.dart @@ -1,7 +1,7 @@ import 'package:flex_color_scheme/flex_color_scheme.dart'; import 'package:flutter/material.dart'; -import 'package:sfm_app/product/theme/app_theme.dart'; +import 'app_theme.dart'; class AppThemeDark extends AppTheme { static AppThemeDark? _instance; static AppThemeDark get instance { diff --git a/lib/product/utils/device_utils.dart b/lib/product/utils/device_utils.dart index 76907e1..750226e 100644 --- a/lib/product/utils/device_utils.dart +++ b/lib/product/utils/device_utils.dart @@ -1,11 +1,10 @@ - import 'package:flutter/material.dart'; -import 'package:sfm_app/feature/device_log/device_logs_model.dart'; -import 'package:sfm_app/product/services/api_services.dart'; -import 'package:sfm_app/product/services/language_services.dart'; -import 'package:sfm_app/product/shared/model/district_model.dart'; -import 'package:sfm_app/product/shared/model/province_model.dart'; +import '../../feature/device_log/device_logs_model.dart'; +import '../services/api_services.dart'; +import '../services/language_services.dart'; +import '../shared/model/district_model.dart'; +import '../shared/model/province_model.dart'; import '../../feature/devices/device_model.dart'; import '../constant/icon/icon_constants.dart'; import '../shared/model/ward_model.dart'; diff --git a/lib/product/utils/permission_handler.dart b/lib/product/utils/permission_handler.dart index bce5068..1a9f903 100644 --- a/lib/product/utils/permission_handler.dart +++ b/lib/product/utils/permission_handler.dart @@ -23,7 +23,8 @@ Future checkAndRequestPermission() async { } if (permission == LocationPermission.deniedForever) { - print('Quyền truy cập vị trí bị từ chối vĩnh viễn. Vui lòng cấp quyền trong cài đặt.'); + print( + 'Quyền truy cập vị trí bị từ chối vĩnh viễn. Vui lòng cấp quyền trong cài đặt.'); return permission; } @@ -54,9 +55,10 @@ Future requestLocationPermission() async { // Bước 2: Kiểm tra và yêu cầu quyền LocationPermission permission = await checkAndRequestPermission(); - + // Bước 3: Nếu quyền được cấp, lấy vị trí - if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) { + if (permission == LocationPermission.whileInUse || + permission == LocationPermission.always) { await getCurrentPosition(); } -} \ No newline at end of file +} diff --git a/lib/product/utils/qr_utils.dart b/lib/product/utils/qr_utils.dart index f2f42c4..7e9dda7 100644 --- a/lib/product/utils/qr_utils.dart +++ b/lib/product/utils/qr_utils.dart @@ -1,6 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_barcode_scanner_plus/flutter_barcode_scanner_plus.dart'; + import '../services/language_services.dart'; class QRScanUtils { diff --git a/lib/product/utils/response_status_utils.dart b/lib/product/utils/response_status_utils.dart index 576c924..037dfb9 100644 --- a/lib/product/utils/response_status_utils.dart +++ b/lib/product/utils/response_status_utils.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; + import '../constant/status_code/status_code_constants.dart'; import '../shared/shared_snack_bar.dart'; diff --git a/lib/product/utils/responsive_text_utils.dart b/lib/product/utils/responsive_text_utils.dart index a374982..1b76784 100644 --- a/lib/product/utils/responsive_text_utils.dart +++ b/lib/product/utils/responsive_text_utils.dart @@ -1,4 +1,5 @@ import 'package:flutter/cupertino.dart'; + import '../extension/context_extension.dart'; class ResponsiveText{