chore(group): fix delay show group when switch and convert to relative import
This commit is contained in:
@@ -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:
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../feature/settings/device_notification_settings/device_notification_settings_model.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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<DeviceWithAlias> allDevices) async {
|
||||
// int notificationCount = 0;
|
||||
Map<String, List<DeviceWithAlias>> ownerDevicesStatus = {};
|
||||
List<String> ownerDevicesState = [];
|
||||
|
||||
if (!context.mounted) return;
|
||||
sinkOwnerDevicesStatus.add(ownerDevicesStatus);
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../product/services/language_services.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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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<String, String> _extractLocationComponents(
|
||||
@@ -248,24 +248,24 @@ Future<void> _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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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});
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../../product/extension/context_extension.dart';
|
||||
|
||||
class StatusCard extends StatelessWidget {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<GroupsScreen> createState() => _GroupsScreenState();
|
||||
}
|
||||
|
||||
class _GroupsScreenState extends State<GroupsScreen> {
|
||||
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<Group> groups;
|
||||
final InterFamilyBloc interFamilyBloc;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (widget.role == ApplicationConstants.OWNER_GROUP ||
|
||||
widget.role == ApplicationConstants.PARTICIPANT_GROUP) {
|
||||
return StreamBuilder<List<Group>>(
|
||||
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,
|
||||
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": groupsSnapshot.data![index].id!
|
||||
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,
|
||||
);
|
||||
},
|
||||
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(
|
||||
);
|
||||
}
|
||||
|
||||
Widget _ownerPopupMenu(Group group, BuildContext context) {
|
||||
return PopupMenuButton<int>(
|
||||
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),
|
||||
),
|
||||
),
|
||||
borderRadius: BorderRadius.all(Radius.circular(8.0))),
|
||||
itemBuilder: (ctx) => [
|
||||
_buildPopupMenuItem(group, context,
|
||||
appLocalization(context).share_group_title, Icons.share, 4),
|
||||
_buildPopupMenuItem(
|
||||
groupsSnapshot.data![index],
|
||||
group,
|
||||
context,
|
||||
appLocalization(context)
|
||||
.share_group_title,
|
||||
Icons.share,
|
||||
4),
|
||||
_buildPopupMenuItem(
|
||||
groupsSnapshot.data![index],
|
||||
context,
|
||||
appLocalization(context)
|
||||
.change_group_infomation_title,
|
||||
appLocalization(context).change_group_infomation_title,
|
||||
Icons.settings_backup_restore,
|
||||
2),
|
||||
_buildPopupMenuItem(
|
||||
groupsSnapshot.data![index],
|
||||
group,
|
||||
context,
|
||||
appLocalization(context)
|
||||
.delete_group_title,
|
||||
appLocalization(context).delete_group_title,
|
||||
Icons.delete_forever_rounded,
|
||||
3),
|
||||
],
|
||||
icon: const Icon(Icons.more_horiz),
|
||||
)
|
||||
: const SizedBox.shrink(),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}
|
||||
|
||||
PopupMenuItem _buildPopupMenuItem(Group group, BuildContext context,
|
||||
String title, IconData iconData, int position) {
|
||||
return PopupMenuItem(
|
||||
PopupMenuItem<int> _buildPopupMenuItem(Group group, BuildContext context,
|
||||
String title, IconData iconData, int value) {
|
||||
return PopupMenuItem<int>(
|
||||
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, () {
|
||||
if (title == appLocalization(context).share_group_title) {
|
||||
shareGroup(context, group);
|
||||
});
|
||||
} else if (title ==
|
||||
appLocalization(context).change_group_infomation_title) {
|
||||
Future.delayed(context.lowDuration, () {
|
||||
createOrJoinGroupDialog(
|
||||
context,
|
||||
interFamilyBloc,
|
||||
widget.role,
|
||||
role,
|
||||
appLocalization(context).change_group_infomation_content,
|
||||
appLocalization(context).group_name_title,
|
||||
group.name!,
|
||||
group.name ?? '',
|
||||
false,
|
||||
group.id!,
|
||||
group.id ?? '',
|
||||
appLocalization(context).description_group,
|
||||
group.description ?? "");
|
||||
});
|
||||
group.description ?? '',
|
||||
);
|
||||
} else if (title == appLocalization(context).delete_group_title) {
|
||||
Future.delayed(context.lowDuration, () {
|
||||
showActionDialog(
|
||||
context,
|
||||
widget.role,
|
||||
role,
|
||||
interFamilyBloc,
|
||||
appLocalization(context).delete_group_title,
|
||||
appLocalization(context).delete_group_content,
|
||||
group);
|
||||
group,
|
||||
);
|
||||
}
|
||||
});
|
||||
} else {}
|
||||
},
|
||||
value: position,
|
||||
child: Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Icon(
|
||||
iconData,
|
||||
color: Colors.black,
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Text(title),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,20 +26,29 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
|
||||
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 = <Widget>[
|
||||
BlocProvider(
|
||||
blocBuilder: () => InterFamilyBloc(),
|
||||
child: const GroupsScreen(
|
||||
List<Group> ownerGroups = [];
|
||||
List<Group> participantGroups = [];
|
||||
|
||||
List<Widget> get _widgetOptions => [
|
||||
GroupsScreen(
|
||||
role: ApplicationConstants.OWNER_GROUP,
|
||||
groups: ownerGroups,
|
||||
interFamilyBloc: interFamilyBloc,
|
||||
),
|
||||
),
|
||||
BlocProvider(
|
||||
blocBuilder: () => InterFamilyBloc(),
|
||||
child: const GroupsScreen(
|
||||
GroupsScreen(
|
||||
role: ApplicationConstants.PARTICIPANT_GROUP,
|
||||
),
|
||||
groups: participantGroups,
|
||||
interFamilyBloc: interFamilyBloc,
|
||||
),
|
||||
];
|
||||
|
||||
@@ -49,6 +59,20 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
|
||||
stream: interFamilyBloc.streamSelectedScreen,
|
||||
initialData: _selectedIndex,
|
||||
builder: (context, selectSnapshot) {
|
||||
// subscribe to groups stream and update local lists so child widgets render instantly
|
||||
return StreamBuilder<List<Group>>(
|
||||
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: [
|
||||
@@ -58,35 +82,32 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
|
||||
createOrJoinGroupDialog(
|
||||
context,
|
||||
interFamilyBloc,
|
||||
selectSnapshot.data! == 0
|
||||
? ApplicationConstants.OWNER_GROUP
|
||||
: ApplicationConstants.PARTICIPANT_GROUP,
|
||||
ApplicationConstants.OWNER_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,
|
||||
ApplicationConstants.PARTICIPANT_GROUP,
|
||||
appLocalization(context).join_group,
|
||||
appLocalization(context).group_id_title,
|
||||
'',
|
||||
true,
|
||||
"",
|
||||
appLocalization(context).group_name_title,
|
||||
"");
|
||||
"",
|
||||
);
|
||||
}
|
||||
},
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
style:
|
||||
ElevatedButton.styleFrom(shape: const CircleBorder()),
|
||||
child: IconConstants.instance.getMaterialIcon(Icons.add),
|
||||
),
|
||||
],
|
||||
@@ -142,6 +163,8 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void checkTitle(int index) {
|
||||
@@ -159,5 +182,11 @@ class _InterFamilyScreenState extends State<InterFamilyScreen> {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -95,7 +95,7 @@ abstract class AppLocalizations {
|
||||
/// A list of this localizations delegate's supported locales.
|
||||
static const List<Locale> supportedLocales = <Locale>[
|
||||
Locale('en'),
|
||||
Locale('vi'),
|
||||
Locale('vi')
|
||||
];
|
||||
|
||||
/// No description provided for @description_NOTUSE.
|
||||
@@ -1623,6 +1623,5 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
|
||||
'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.',
|
||||
);
|
||||
'that was used.');
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -59,6 +59,5 @@ class MapServices {
|
||||
log("Lỗi khi tìm đường");
|
||||
return [];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,10 +26,13 @@ class NotificationServices {
|
||||
}
|
||||
|
||||
Future<void> initializeLocalNotifications() async {
|
||||
try{
|
||||
const AndroidInitializationSettings androidInitializationSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const DarwinInitializationSettings iosInitializationSettings = DarwinInitializationSettings();
|
||||
const InitializationSettings initializationSettings = InitializationSettings(
|
||||
try {
|
||||
const AndroidInitializationSettings androidInitializationSettings =
|
||||
AndroidInitializationSettings('@mipmap/ic_launcher');
|
||||
const DarwinInitializationSettings iosInitializationSettings =
|
||||
DarwinInitializationSettings();
|
||||
const InitializationSettings initializationSettings =
|
||||
InitializationSettings(
|
||||
android: androidInitializationSettings,
|
||||
iOS: iosInitializationSettings,
|
||||
);
|
||||
@@ -35,12 +40,13 @@ class NotificationServices {
|
||||
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(
|
||||
"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){
|
||||
} catch (e) {
|
||||
dev.log("Error initializing local notifications: $e");
|
||||
}
|
||||
}
|
||||
@@ -62,11 +68,13 @@ class NotificationServices {
|
||||
try {
|
||||
if (Platform.isAndroid) {
|
||||
return await _notificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>()
|
||||
?.requestNotificationsPermission();
|
||||
} else if (Platform.isIOS) {
|
||||
return await _notificationsPlugin
|
||||
.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>()
|
||||
.resolvePlatformSpecificImplementation<
|
||||
IOSFlutterLocalNotificationsPlugin>()
|
||||
?.requestPermissions(alert: true, sound: true, badge: true);
|
||||
}
|
||||
return null;
|
||||
@@ -84,7 +92,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;
|
||||
}
|
||||
|
||||
@@ -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<AndroidFlutterLocalNotificationsPlugin>();
|
||||
final androidPlugin =
|
||||
_notificationsPlugin.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin>();
|
||||
|
||||
// Delete existing channel to prevent conflicts
|
||||
await androidPlugin?.deleteNotificationChannel(channelId);
|
||||
@@ -180,12 +190,13 @@ class NotificationServices {
|
||||
|
||||
Future<void> 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<void> showBackgroundOrTerminateNotification(RemoteMessage message) async {
|
||||
Future<void> 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,
|
||||
@@ -274,9 +286,9 @@ Future<void> firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
||||
if (Firebase.apps.isEmpty) {
|
||||
await Firebase.initializeApp(
|
||||
options: DefaultFirebaseOptions.currentPlatform,
|
||||
name: "sfm-notification"
|
||||
);
|
||||
AppLoggerUtils.warning("Firebase đã được khởi tạo trong background handler");
|
||||
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 đó");
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -23,7 +23,8 @@ Future<LocationPermission> 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;
|
||||
}
|
||||
|
||||
@@ -56,7 +57,8 @@ Future<void> requestLocationPermission() async {
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../constant/status_code/status_code_constants.dart';
|
||||
import '../shared/shared_snack_bar.dart';
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
|
||||
import '../extension/context_extension.dart';
|
||||
|
||||
class ResponsiveText{
|
||||
|
||||
Reference in New Issue
Block a user