Add SimDataScreen
This commit is contained in:
34
lib/bloc/sim_data_bloc.dart
Normal file
34
lib/bloc/sim_data_bloc.dart
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import '../product/services/api_services.dart';
|
||||||
|
import '../feature/devices/device_model.dart';
|
||||||
|
import '../product/base/bloc/base_bloc.dart';
|
||||||
|
|
||||||
|
class SimDataBloc extends BlocBase{
|
||||||
|
|
||||||
|
APIServices apiServices = APIServices();
|
||||||
|
|
||||||
|
final devices = StreamController<List<Device>>.broadcast();
|
||||||
|
StreamSink<List<Device>> get sinkDevices => devices.sink;
|
||||||
|
Stream<List<Device>> get streamDevices => devices.stream;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {}
|
||||||
|
|
||||||
|
void getOwnerDevices(BuildContext context) async {
|
||||||
|
await apiServices.execute(context, () async {
|
||||||
|
List<Device> devices = [];
|
||||||
|
devices = await apiServices.getOwnerDevices();
|
||||||
|
|
||||||
|
List<Device> publicDevices = [];
|
||||||
|
for (var device in devices) {
|
||||||
|
if (device.visibility == "PUBLIC") {
|
||||||
|
publicDevices.add(device);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sinkDevices.add(publicDevices);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -92,6 +92,11 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
appLocalization(context).profile_setting,
|
appLocalization(context).profile_setting,
|
||||||
userSnapshot.data ?? user),
|
userSnapshot.data ?? user),
|
||||||
SizedBox(height: context.lowValue),
|
SizedBox(height: context.lowValue),
|
||||||
|
cardContent(
|
||||||
|
Icons.sim_card,
|
||||||
|
appLocalization(context).profile_sim_data,
|
||||||
|
userSnapshot.data ?? user),
|
||||||
|
SizedBox(height: context.lowValue),
|
||||||
cardContent(
|
cardContent(
|
||||||
Icons.logout_outlined,
|
Icons.logout_outlined,
|
||||||
appLocalization(context).log_out,
|
appLocalization(context).log_out,
|
||||||
@@ -113,7 +118,10 @@ class _SettingsScreenState extends State<SettingsScreen> {
|
|||||||
changeUserPassword(context, settingsBloc);
|
changeUserPassword(context, settingsBloc);
|
||||||
} else if (icon == Icons.settings_outlined) {
|
} else if (icon == Icons.settings_outlined) {
|
||||||
context.push(ApplicationConstants.DEVICE_NOTIFICATIONS_SETTINGS);
|
context.push(ApplicationConstants.DEVICE_NOTIFICATIONS_SETTINGS);
|
||||||
} else {
|
} else if(icon == Icons.sim_card){
|
||||||
|
context.push(ApplicationConstants.SIM_DATA_SETTINGS);
|
||||||
|
}
|
||||||
|
else {
|
||||||
await apiServices.logOut(context);
|
await apiServices.logOut(context);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
170
lib/feature/settings/sim_data/shared_sim_component.dart
Normal file
170
lib/feature/settings/sim_data/shared_sim_component.dart
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:intl/intl.dart';
|
||||||
|
|
||||||
|
import '../../devices/device_model.dart';
|
||||||
|
import '../../../product/extension/context_extension.dart';
|
||||||
|
import '../../../product/services/language_services.dart';
|
||||||
|
|
||||||
|
import '../../../product/utils/device_utils.dart';
|
||||||
|
|
||||||
|
class SharedSimComponent extends StatelessWidget {
|
||||||
|
|
||||||
|
const SharedSimComponent({super.key, required this.device});
|
||||||
|
final Device device;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
double screenWidth = MediaQuery.of(context).size.width;
|
||||||
|
double screenHeight = MediaQuery.of(context).size.height;
|
||||||
|
return Padding(
|
||||||
|
padding: const EdgeInsets.all(8.0),
|
||||||
|
child: Container(
|
||||||
|
width: screenWidth,
|
||||||
|
height: screenHeight / 5.5,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: BorderRadius.circular(30),
|
||||||
|
gradient: getGradientColor(getMonthLeft())
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: context.paddingLow,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
device.name ?? "name",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
_buildStatusChip(context,device.state ?? -1),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SizedBox(height: context.lowValue),
|
||||||
|
// Time period
|
||||||
|
Text(
|
||||||
|
"${appLocalization(context).time_title}: ${convertStartTime()} - ${convertExpireTime()}",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
SizedBox(height: context.lowValue),
|
||||||
|
_buildDurationDisplay(context),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildStatusChip(BuildContext context,int state) {
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(color: Colors.white),
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
color: Colors.white,
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.circle,
|
||||||
|
color: DeviceUtils.instance.getTableRowColor(context, state),
|
||||||
|
size: 12,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 5),
|
||||||
|
Text(
|
||||||
|
DeviceUtils.instance.checkStateDevice(context, state),
|
||||||
|
style: TextStyle(
|
||||||
|
color: DeviceUtils.instance.getTableRowColor(context, state),
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _buildDurationDisplay(BuildContext context) {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.baseline,
|
||||||
|
textBaseline: TextBaseline.alphabetic,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"${getMonthLeft()}",
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 40,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.bold,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Text(
|
||||||
|
appLocalization(context).sim_data_month_left_message,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 20,
|
||||||
|
color: Colors.white,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
String convertStartTime(){
|
||||||
|
return DateFormat('dd/MM/yyyy').format(device.createdAt!);
|
||||||
|
}
|
||||||
|
|
||||||
|
String convertExpireTime() {
|
||||||
|
final expireDate = _calculateExpireDate();
|
||||||
|
return DateFormat('dd/MM/yyyy').format(expireDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
int getMonthLeft() {
|
||||||
|
final expireDate = _calculateExpireDate();
|
||||||
|
final now = DateTime.now();
|
||||||
|
|
||||||
|
int totalMonths = (expireDate.year - now.year) * 12 + (expireDate.month - now.month);
|
||||||
|
|
||||||
|
if (expireDate.day < now.day) {
|
||||||
|
totalMonths -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return totalMonths;
|
||||||
|
}
|
||||||
|
|
||||||
|
DateTime _calculateExpireDate() {
|
||||||
|
return DateTime(
|
||||||
|
device.createdAt!.year + 3,
|
||||||
|
device.createdAt!.month,
|
||||||
|
device.createdAt!.day,
|
||||||
|
device.createdAt!.hour,
|
||||||
|
device.createdAt!.minute,
|
||||||
|
device.createdAt!.second,
|
||||||
|
device.createdAt!.millisecond,
|
||||||
|
device.createdAt!.microsecond,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
LinearGradient getGradientColor(int monthLeft){
|
||||||
|
if(monthLeft <= 3){
|
||||||
|
return const LinearGradient(
|
||||||
|
colors: [Color(0xFFff4b1f), Color(0xFFff9068)],
|
||||||
|
);
|
||||||
|
}else{
|
||||||
|
return const LinearGradient(
|
||||||
|
colors: [Color(0xFF56ab2f), Color(0xFFa8e063)],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
55
lib/feature/settings/sim_data/sim_data_screen.dart
Normal file
55
lib/feature/settings/sim_data/sim_data_screen.dart
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:sfm_app/feature/settings/sim_data/shared_sim_component.dart';
|
||||||
|
|
||||||
|
import '../../../product/services/language_services.dart';
|
||||||
|
import '../../../product/shared/shared_loading_animation.dart';
|
||||||
|
import '../../../bloc/sim_data_bloc.dart';
|
||||||
|
import '../../../product/base/bloc/base_bloc.dart';
|
||||||
|
|
||||||
|
class SimDataScreen extends StatefulWidget {
|
||||||
|
const SimDataScreen({super.key});
|
||||||
|
@override
|
||||||
|
State<SimDataScreen> createState() => _SimDataScreenState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _SimDataScreenState extends State<SimDataScreen> {
|
||||||
|
late SimDataBloc simDataBloc;
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
simDataBloc = BlocProvider.of(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
title: Text(appLocalization(context).profile_sim_data),
|
||||||
|
centerTitle: true,
|
||||||
|
),
|
||||||
|
body: StreamBuilder(
|
||||||
|
stream: simDataBloc.streamDevices,
|
||||||
|
builder: (context, devicesSnapshot) {
|
||||||
|
if (devicesSnapshot.data == null) {
|
||||||
|
simDataBloc.getOwnerDevices(context);
|
||||||
|
return const SharedLoadingAnimation();
|
||||||
|
} else if (devicesSnapshot.data!.isEmpty) {
|
||||||
|
return Center(
|
||||||
|
child: Text(appLocalization(context).main_no_data),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return SafeArea(
|
||||||
|
child: Column(
|
||||||
|
children: devicesSnapshot.data!.map(
|
||||||
|
(device) {
|
||||||
|
return SharedSimComponent(device: device,);
|
||||||
|
},
|
||||||
|
).toList(),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
@@ -7,6 +9,7 @@ import 'firebase_options.dart';
|
|||||||
|
|
||||||
import 'product/lang/l10n/app_localizations.dart';
|
import 'product/lang/l10n/app_localizations.dart';
|
||||||
import 'product/services/api_services.dart';
|
import 'product/services/api_services.dart';
|
||||||
|
import 'product/services/notification_services.dart';
|
||||||
import 'product/services/theme_services.dart';
|
import 'product/services/theme_services.dart';
|
||||||
import 'product/services/language_services.dart';
|
import 'product/services/language_services.dart';
|
||||||
import 'bloc/main_bloc.dart';
|
import 'bloc/main_bloc.dart';
|
||||||
@@ -146,7 +149,7 @@ class _MyAppState extends State<MyApp> {
|
|||||||
// // notificationServices.firebaseInit(context);
|
// // notificationServices.firebaseInit(context);
|
||||||
// // notificationServices.setupInteractMessage();
|
// // notificationServices.setupInteractMessage();
|
||||||
// notificationServices.getDeviceToken().then((token){
|
// notificationServices.getDeviceToken().then((token){
|
||||||
// print("Firebase Token: $token");
|
// log("Firebase Token: $token");
|
||||||
// sendNotificationToken(token);
|
// sendNotificationToken(token);
|
||||||
// });
|
// });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ class ApplicationConstants {
|
|||||||
static const DEVICE_LOGS_PATH = "/device-logs";
|
static const DEVICE_LOGS_PATH = "/device-logs";
|
||||||
static const GROUP_PATH = "/groups";
|
static const GROUP_PATH = "/groups";
|
||||||
static const DEVICE_NOTIFICATIONS_SETTINGS = "/device-notifications-settings";
|
static const DEVICE_NOTIFICATIONS_SETTINGS = "/device-notifications-settings";
|
||||||
|
static const SIM_DATA_SETTINGS = "/sim-data-settings";
|
||||||
static const OWNER_GROUP = "owner";
|
static const OWNER_GROUP = "owner";
|
||||||
static const PARTICIPANT_GROUP = "participant";
|
static const PARTICIPANT_GROUP = "participant";
|
||||||
static const NO_DATA = "no_data";
|
static const NO_DATA = "no_data";
|
||||||
|
|||||||
@@ -13,4 +13,5 @@ enum AppRoutes {
|
|||||||
HISTORY,
|
HISTORY,
|
||||||
GROUPS,
|
GROUPS,
|
||||||
GROUP_DETAIL,
|
GROUP_DETAIL,
|
||||||
|
SIM_DATA_SETTING,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
import 'package:go_router/go_router.dart';
|
import 'package:go_router/go_router.dart';
|
||||||
|
|
||||||
|
import '../../../bloc/sim_data_bloc.dart';
|
||||||
|
import '../../../feature/settings/sim_data/sim_data_screen.dart';
|
||||||
import '../../../bloc/device_detail_bloc.dart';
|
import '../../../bloc/device_detail_bloc.dart';
|
||||||
import '../../../feature/devices/device_detail/device_detail_screen.dart';
|
import '../../../feature/devices/device_detail/device_detail_screen.dart';
|
||||||
import '../../../bloc/device_notification_settings_bloc.dart';
|
import '../../../bloc/device_notification_settings_bloc.dart';
|
||||||
@@ -151,6 +154,16 @@ GoRouter goRouter() {
|
|||||||
),
|
),
|
||||||
transitionsBuilder: transitionsRightToLeft),
|
transitionsBuilder: transitionsRightToLeft),
|
||||||
),
|
),
|
||||||
|
GoRoute(
|
||||||
|
path: ApplicationConstants.SIM_DATA_SETTINGS,
|
||||||
|
name: AppRoutes.SIM_DATA_SETTING.name,
|
||||||
|
pageBuilder: (context, state) => CustomTransitionPage(
|
||||||
|
child: BlocProvider(
|
||||||
|
child: const SimDataScreen(),
|
||||||
|
blocBuilder: () => SimDataBloc(),
|
||||||
|
),
|
||||||
|
transitionsBuilder: transitionsRightToLeft),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -111,7 +111,10 @@
|
|||||||
"profile_page_title": "Settings Page",
|
"profile_page_title": "Settings Page",
|
||||||
"profile_change_info": "Change information",
|
"profile_change_info": "Change information",
|
||||||
"profile_change_pass": "Change password",
|
"profile_change_pass": "Change password",
|
||||||
|
"profile_sim_data": "Device SIM information",
|
||||||
"profile_setting": "Notification Setting",
|
"profile_setting": "Notification Setting",
|
||||||
|
"sim_data_month_left_message": "months left",
|
||||||
|
"time_title": "Time",
|
||||||
"change_profile_title": "Personal information",
|
"change_profile_title": "Personal information",
|
||||||
"change_profile_username": "Username: ",
|
"change_profile_username": "Username: ",
|
||||||
"change_profile_username_hint": "Enter username ",
|
"change_profile_username_hint": "Enter username ",
|
||||||
|
|||||||
@@ -770,12 +770,30 @@ abstract class AppLocalizations {
|
|||||||
/// **'Change password'**
|
/// **'Change password'**
|
||||||
String get profile_change_pass;
|
String get profile_change_pass;
|
||||||
|
|
||||||
|
/// No description provided for @profile_sim_data.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Device SIM information'**
|
||||||
|
String get profile_sim_data;
|
||||||
|
|
||||||
/// No description provided for @profile_setting.
|
/// No description provided for @profile_setting.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
/// **'Notification Setting'**
|
/// **'Notification Setting'**
|
||||||
String get profile_setting;
|
String get profile_setting;
|
||||||
|
|
||||||
|
/// No description provided for @sim_data_month_left_message.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'months left'**
|
||||||
|
String get sim_data_month_left_message;
|
||||||
|
|
||||||
|
/// No description provided for @time_title.
|
||||||
|
///
|
||||||
|
/// In en, this message translates to:
|
||||||
|
/// **'Time'**
|
||||||
|
String get time_title;
|
||||||
|
|
||||||
/// No description provided for @change_profile_title.
|
/// No description provided for @change_profile_title.
|
||||||
///
|
///
|
||||||
/// In en, this message translates to:
|
/// In en, this message translates to:
|
||||||
|
|||||||
@@ -360,9 +360,18 @@ class AppLocalizationsEn extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get profile_change_pass => 'Change password';
|
String get profile_change_pass => 'Change password';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get profile_sim_data => 'Device SIM information';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get profile_setting => 'Notification Setting';
|
String get profile_setting => 'Notification Setting';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sim_data_month_left_message => 'months left';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get time_title => 'Time';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get change_profile_title => 'Personal information';
|
String get change_profile_title => 'Personal information';
|
||||||
|
|
||||||
|
|||||||
@@ -356,9 +356,18 @@ class AppLocalizationsVi extends AppLocalizations {
|
|||||||
@override
|
@override
|
||||||
String get profile_change_pass => 'Đổi mật khẩu';
|
String get profile_change_pass => 'Đổi mật khẩu';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get profile_sim_data => 'Thông tin sim thiết bị';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get profile_setting => 'Cài đặt thông báo';
|
String get profile_setting => 'Cài đặt thông báo';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get sim_data_month_left_message => 'tháng còn lại';
|
||||||
|
|
||||||
|
@override
|
||||||
|
String get time_title => 'Thời gian';
|
||||||
|
|
||||||
@override
|
@override
|
||||||
String get change_profile_title => 'Thông tin người dùng';
|
String get change_profile_title => 'Thông tin người dùng';
|
||||||
|
|
||||||
|
|||||||
@@ -112,6 +112,9 @@
|
|||||||
"profile_change_info": "Đổi thông tin cá nhân",
|
"profile_change_info": "Đổi thông tin cá nhân",
|
||||||
"profile_change_pass": "Đổi mật khẩu",
|
"profile_change_pass": "Đổi mật khẩu",
|
||||||
"profile_setting": "Cài đặt thông báo",
|
"profile_setting": "Cài đặt thông báo",
|
||||||
|
"profile_sim_data": "Thông tin sim thiết bị",
|
||||||
|
"sim_data_month_left_message": "tháng còn lại",
|
||||||
|
"time_title": "Thời gian",
|
||||||
"change_profile_title": "Thông tin người dùng",
|
"change_profile_title": "Thông tin người dùng",
|
||||||
"change_profile_username": "Tên người dùng: ",
|
"change_profile_username": "Tên người dùng: ",
|
||||||
"change_profile_username_hint": "Nhập tên ",
|
"change_profile_username_hint": "Nhập tên ",
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ class NotificationServices {
|
|||||||
|
|
||||||
Future<String> getDeviceToken() async {
|
Future<String> getDeviceToken() async {
|
||||||
print("GET FB TOKEN");
|
print("GET FB TOKEN");
|
||||||
String? token = await messaging.getAPNSToken();
|
String? token = await messaging.getToken();
|
||||||
print("GET FB: ${token}");
|
print("GET FB: ${token}");
|
||||||
return token!;
|
return token!;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user