refactor(ui): update DetailDevice screen layout
This commit is contained in:
@@ -92,7 +92,8 @@ class DetailDeviceBloc extends BlocBase {
|
||||
'thing_id': thingID,
|
||||
'from': from,
|
||||
'to': now,
|
||||
'limit': '500',
|
||||
'limit': '100',
|
||||
'n': '7',
|
||||
};
|
||||
final body = await apiServices.getLogsOfDevice(thingID, params);
|
||||
if (body != "") {
|
||||
@@ -100,13 +101,7 @@ class DetailDeviceBloc extends BlocBase {
|
||||
DeviceLog devicesListLog = DeviceLog.fromJson(data);
|
||||
if (devicesListLog.sensors!.isNotEmpty) {
|
||||
for (var sensor in devicesListLog.sensors!) {
|
||||
if (sensor.name == "8") {
|
||||
if (sensorTemps.length < 100) {
|
||||
sensorTemps.add(sensor);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
sensorTemps.add(sensor);
|
||||
}
|
||||
sensorTemps = sensorTemps.reversed.toList();
|
||||
sinkSensorTemps.add(sensorTemps);
|
||||
|
||||
@@ -3,9 +3,10 @@ import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:sfm_app/feature/device_log/device_logs_model.dart';
|
||||
import 'package:sfm_app/product/constant/image/image_constants.dart';
|
||||
import 'package:sfm_app/product/shared/shared_line_chart.dart';
|
||||
import 'package:simple_ripple_animation/simple_ripple_animation.dart';
|
||||
import 'dart:math' as math;
|
||||
import '../../../product/shared/shared_curve.dart';
|
||||
import '../device_model.dart';
|
||||
import '../../../product/base/bloc/base_bloc.dart';
|
||||
import '../../../product/extention/context_extention.dart';
|
||||
@@ -28,14 +29,15 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
|
||||
IconConstants.instance.getIcon("offline_icon"),
|
||||
IconConstants.instance.getIcon("flame_icon"),
|
||||
];
|
||||
|
||||
String stateImgAssets(int state) {
|
||||
String imgStringAsset;
|
||||
if (state == 0) {
|
||||
imgStringAsset = imageAssets[0];
|
||||
} else if (state == 1) {
|
||||
imgStringAsset = imageAssets[1];
|
||||
} else {
|
||||
imgStringAsset = imageAssets[2];
|
||||
} else {
|
||||
imgStringAsset = imageAssets[1];
|
||||
}
|
||||
return imgStringAsset;
|
||||
}
|
||||
@@ -50,14 +52,31 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
|
||||
detailDeviceBloc = BlocProvider.of(context);
|
||||
}
|
||||
|
||||
TextStyle textstyle = const TextStyle(
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.w600,
|
||||
);
|
||||
|
||||
BoxDecoration boxDecoration = BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
color: Colors.grey.withOpacity(0.1),
|
||||
border: Border.all(
|
||||
width: 1,
|
||||
color: Colors.grey.withOpacity(0.6),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
double screenWidth = MediaQuery.of(context).size.width;
|
||||
return StreamBuilder<Device>(
|
||||
stream: detailDeviceBloc.streamDeviceInfo,
|
||||
builder: (context, deviceSnapshot) {
|
||||
if (deviceSnapshot.data?.extId == null) {
|
||||
detailDeviceBloc.getDeviceDetail(context, widget.thingID, controller);
|
||||
detailDeviceBloc.getDeviceDetail(
|
||||
context,
|
||||
widget.thingID,
|
||||
controller,
|
||||
);
|
||||
return const Center(
|
||||
child: CircularProgressIndicator(),
|
||||
);
|
||||
@@ -71,314 +90,557 @@ class _DetailDeviceScreenState extends State<DetailDeviceScreen> {
|
||||
title: Text(appLocalization(context).detail_message),
|
||||
centerTitle: true,
|
||||
),
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: [
|
||||
// device Name
|
||||
Card(
|
||||
child: Container(
|
||||
width: context.dynamicWidth(1),
|
||||
height: context.highValue,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12),
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'${appLocalization(context).device_title}: ${deviceSnapshot.data!.name}',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
body: SingleChildScrollView(
|
||||
child: Column(
|
||||
children: <Widget>[
|
||||
Stack(
|
||||
children: [
|
||||
ClipPath(
|
||||
clipper: CuveEdgesCustom(),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(0),
|
||||
child: SizedBox(
|
||||
height: context.dynamicHeight(0.25),
|
||||
child: Stack(
|
||||
children: [
|
||||
Positioned.fill(
|
||||
child: Image.asset(
|
||||
ImageConstants.instance
|
||||
.getImage('smoke-detector'),
|
||||
fit: BoxFit.fill,
|
||||
),
|
||||
),
|
||||
Center(
|
||||
child: Container(
|
||||
height: 50,
|
||||
width: 400,
|
||||
// color: Colors.blueAccent,
|
||||
alignment: Alignment.centerRight,
|
||||
margin: const EdgeInsets.fromLTRB(
|
||||
0, 0, 0, 50),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const SizedBox(),
|
||||
Text(
|
||||
deviceSnapshot.data?.name ?? "",
|
||||
style: const TextStyle(
|
||||
fontSize: 25,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Tinh trang va nhiet do
|
||||
Row(
|
||||
children: [
|
||||
Card(
|
||||
child: Container(
|
||||
width: (screenWidth - 20) / 2,
|
||||
height: context.highValue,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
left: MediaQuery.of(context).size.width / 2 - 100,
|
||||
child: Container(
|
||||
height: context.dynamicHeight(0.08),
|
||||
width: context.dynamicWidth(0.5),
|
||||
decoration: BoxDecoration(
|
||||
color: DeviceUtils.instance.getTableRowColor(
|
||||
deviceSnapshot.data?.state ?? 3),
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
SizedBox(
|
||||
height: context.mediumValue,
|
||||
width: context.mediumValue,
|
||||
child: deviceSnapshot.data?.state == 1
|
||||
? RippleAnimation(
|
||||
color: Colors.red,
|
||||
delay: context
|
||||
.dynamicMilliSecondDuration(
|
||||
800,
|
||||
),
|
||||
repeat: true,
|
||||
minRadius: 10,
|
||||
ripplesCount: 5,
|
||||
duration: context
|
||||
.dynamicMilliSecondDuration(
|
||||
1800,
|
||||
),
|
||||
child: CircleAvatar(
|
||||
minRadius: context.mediumValue,
|
||||
maxRadius: context.mediumValue,
|
||||
backgroundImage: AssetImage(
|
||||
stateImgAssets(
|
||||
deviceSnapshot.data!.state!,
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
: CircleAvatar(
|
||||
backgroundColor: DeviceUtils
|
||||
.instance
|
||||
.getTableRowColor(
|
||||
deviceSnapshot.data?.state ?? 3,
|
||||
),
|
||||
minRadius: context.mediumValue,
|
||||
maxRadius: context.mediumValue,
|
||||
backgroundImage: AssetImage(
|
||||
stateImgAssets(
|
||||
deviceSnapshot.data!.state!,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(5, 5, 0, 5),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Row(
|
||||
children: [
|
||||
// SizedBox(
|
||||
// height: 25,
|
||||
// width: 25,
|
||||
// child: RippleAnimation(
|
||||
// color: DeviceUtils.instance
|
||||
// .getColorRiple(
|
||||
// deviceSnapshot.data!.state!),
|
||||
// delay:
|
||||
// const Duration(milliseconds: 800),
|
||||
// repeat: true,
|
||||
// minRadius: 40,
|
||||
// ripplesCount: 6,
|
||||
// duration: const Duration(
|
||||
// milliseconds: 6 * 300),
|
||||
// child: CircleAvatar(
|
||||
// minRadius: 20,
|
||||
// maxRadius: 20,
|
||||
// backgroundImage: AssetImage(
|
||||
// stateImgAssets(
|
||||
// deviceSnapshot.data!.state!,
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
CircleAvatar(
|
||||
minRadius: 20,
|
||||
maxRadius: 20,
|
||||
backgroundImage: AssetImage(
|
||||
stateImgAssets(
|
||||
deviceSnapshot.data!.state!,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
Text(
|
||||
Center(
|
||||
child: Text(
|
||||
DeviceUtils.instance.checkStateDevice(
|
||||
context,
|
||||
deviceSnapshot.data!.state!,
|
||||
),
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Card(
|
||||
child: SizedBox(
|
||||
width: (screenWidth - 20) / 2,
|
||||
height: context.highValue,
|
||||
child: Container(
|
||||
alignment: Alignment.centerLeft,
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(5, 5, 0, 5),
|
||||
child: Row(
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: context.normalValue,
|
||||
),
|
||||
// Muc song va muc pin
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceAround,
|
||||
children: [
|
||||
Container(
|
||||
height: context.dynamicHeight(0.18),
|
||||
width: context.dynamicWidth(0.45),
|
||||
decoration: boxDecoration,
|
||||
child: Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.thermostat,
|
||||
color: Colors.blue,
|
||||
size: 30,
|
||||
),
|
||||
const SizedBox(
|
||||
width: 10,
|
||||
),
|
||||
Text(
|
||||
"${appLocalization(context).paginated_data_table_column_deviceTemperature}: ${sensorSnapshot.data?['sensorTemp'] ?? 100}",
|
||||
appLocalization(context)
|
||||
.paginated_data_table_column_deviceSignal,
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: context.dynamicWidth(0.12),
|
||||
width: context.dynamicWidth(0.12),
|
||||
child: Icon(
|
||||
DeviceUtils.instance.getSignalIcon(
|
||||
context,
|
||||
sensorSnapshot.data!['sensorCsq'],
|
||||
),
|
||||
size: 30,
|
||||
color: DeviceUtils.instance
|
||||
.getSignalIconColor(
|
||||
context,
|
||||
sensorSnapshot.data!['sensorCsq'],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
StreamBuilder<List<SensorLogs>>(
|
||||
stream: detailDeviceBloc.streamSensorTemps,
|
||||
builder: (context, sensorTempsSnapshot) {
|
||||
if (sensorTempsSnapshot.data == null) {
|
||||
detailDeviceBloc
|
||||
.getNearerSensorValue(widget.thingID);
|
||||
return const AspectRatio(
|
||||
aspectRatio: 1.5,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return AspectRatio(
|
||||
aspectRatio: 1.5,
|
||||
child: Container(
|
||||
margin: context.paddingLow,
|
||||
child: sharedLineChart(
|
||||
"Nhiệt độ đo được (°C)",
|
||||
sensorTempsSnapshot.data ?? [],
|
||||
60,
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Card(
|
||||
child: Container(
|
||||
width: (screenWidth - 20) / 2,
|
||||
height: context.highValue,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(12),
|
||||
),
|
||||
),
|
||||
alignment: Alignment.centerLeft,
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(10, 5, 0, 5),
|
||||
child: Row(
|
||||
children: [
|
||||
Transform.rotate(
|
||||
angle: 90 * math.pi / 180,
|
||||
child: Icon(
|
||||
DeviceUtils.instance.getBatteryIcon(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorBattery'],
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: context.dynamicHeight(0.09),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
sensorSnapshot.data!['sensorCsq'],
|
||||
style: TextStyle(
|
||||
color: DeviceUtils.instance
|
||||
.getSignalIconColor(
|
||||
context,
|
||||
sensorSnapshot
|
||||
.data!['sensorCsq'],
|
||||
),
|
||||
fontSize: 40,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
color: Colors.blue,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
Text(
|
||||
"${appLocalization(context).paginated_data_table_column_deviceBaterry}: ${sensorSnapshot.data!['sensorBattery']}%",
|
||||
style: const TextStyle(
|
||||
fontSize: 15,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Card(
|
||||
child: Container(
|
||||
width: (screenWidth - 20) / 2,
|
||||
height: context.highValue,
|
||||
alignment: Alignment.centerLeft,
|
||||
padding:
|
||||
const EdgeInsets.fromLTRB(10, 5, 0, 5),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
DeviceUtils.instance.getSignalIcon(
|
||||
context,
|
||||
sensorSnapshot.data!['sensorCsq'],
|
||||
),
|
||||
color: Colors.blue,
|
||||
size: 30,
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
Text(
|
||||
"${appLocalization(context).paginated_data_table_column_deviceSignal}: ${sensorSnapshot.data!['sensorCsq']}",
|
||||
style: const TextStyle(fontSize: 15),
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Card(
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(10.0),
|
||||
height: context.highValue,
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.blue,
|
||||
size: 30,
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
Expanded(
|
||||
child: StreamBuilder<String>(
|
||||
stream:
|
||||
detailDeviceBloc.streamDeviceLocation,
|
||||
builder: (context, locationSnapshot) {
|
||||
if (locationSnapshot.data != null) {
|
||||
return Text(
|
||||
locationSnapshot.data ?? "",
|
||||
style:
|
||||
const TextStyle(fontSize: 13),
|
||||
maxLines: 3,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: true,
|
||||
);
|
||||
} else {
|
||||
detailDeviceBloc.findLocation(context,
|
||||
deviceSnapshot.data!.areaPath!);
|
||||
return Text(appLocalization(context)
|
||||
.undefine_message);
|
||||
}
|
||||
},
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: context.dynamicHeight(0.18),
|
||||
width: context.dynamicWidth(0.45),
|
||||
decoration: boxDecoration,
|
||||
child: Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Column(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
appLocalization(context)
|
||||
.paginated_data_table_column_deviceBaterry,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: context.dynamicWidth(0.12),
|
||||
width: context.dynamicWidth(0.12),
|
||||
child: Image.asset(
|
||||
DeviceUtils.instance
|
||||
.getDeviceBatteryImg(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorBattery'],
|
||||
),
|
||||
),
|
||||
color: DeviceUtils.instance
|
||||
.getDeviceBatteryColor(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorBattery'],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.start,
|
||||
crossAxisAlignment:
|
||||
CrossAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
height: context.dynamicHeight(0.09),
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
sensorSnapshot
|
||||
.data!['sensorBattery'],
|
||||
style: TextStyle(
|
||||
color: DeviceUtils.instance
|
||||
.getDeviceBatteryColor(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorBattery'],
|
||||
),
|
||||
),
|
||||
fontSize: 50,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
Container(
|
||||
height: context.dynamicHeight(0.09),
|
||||
width: 60,
|
||||
alignment: Alignment.centerLeft,
|
||||
child: Text(
|
||||
'%',
|
||||
style: TextStyle(
|
||||
color: DeviceUtils.instance
|
||||
.getDeviceBatteryColor(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorBattery'],
|
||||
),
|
||||
),
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// Nhiet do
|
||||
Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Container(
|
||||
height: 150,
|
||||
width: MediaQuery.of(context).size.width,
|
||||
decoration: boxDecoration,
|
||||
child: Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
appLocalization(context)
|
||||
.paginated_data_table_column_deviceTemperature,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: context.dynamicWidth(0.12),
|
||||
width: context.dynamicWidth(0.12),
|
||||
child: Image.asset(
|
||||
'assets/icons/temperature.png',
|
||||
color: DeviceUtils.instance
|
||||
.getDeviceTempColor(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorTemp'],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
Stack(
|
||||
children: [
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey.withOpacity(0.3),
|
||||
borderRadius:
|
||||
BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
LayoutBuilder(
|
||||
builder: (context, constraints) =>
|
||||
Container(
|
||||
width: constraints.maxWidth *
|
||||
(int.parse(sensorSnapshot
|
||||
.data!['sensorTemp']) /
|
||||
75),
|
||||
height: 20,
|
||||
decoration: BoxDecoration(
|
||||
color: DeviceUtils.instance
|
||||
.getDeviceTempColor(
|
||||
int.parse(sensorSnapshot
|
||||
.data!['sensorTemp']),
|
||||
),
|
||||
borderRadius:
|
||||
BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
const SizedBox(
|
||||
height: 5,
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"${sensorSnapshot.data!['sensorTemp']} °C",
|
||||
style: TextStyle(
|
||||
color: DeviceUtils.instance
|
||||
.getDeviceTempColor(
|
||||
int.parse(
|
||||
sensorSnapshot
|
||||
.data!['sensorTemp'],
|
||||
),
|
||||
),
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.w900,
|
||||
),
|
||||
),
|
||||
const Text(
|
||||
"75 °C",
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
Card(
|
||||
child: Container(
|
||||
height: 300,
|
||||
padding: const EdgeInsets.fromLTRB(5, 5, 5, 5),
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(15),
|
||||
),
|
||||
// Dien ap
|
||||
Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
appLocalization(context)
|
||||
.paginated_data_table_column_devicePower,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
child: deviceSnapshot.data!.settings!.latitude !=
|
||||
""
|
||||
? GoogleMap(
|
||||
initialCameraPosition: initialCamera,
|
||||
mapType: MapType.normal,
|
||||
markers: {
|
||||
Marker(
|
||||
markerId: MarkerId(
|
||||
deviceSnapshot.data!.thingId!),
|
||||
position: LatLng(
|
||||
double.parse(deviceSnapshot
|
||||
.data!.settings!.latitude!),
|
||||
double.parse(deviceSnapshot
|
||||
.data!.settings!.longitude!),
|
||||
SizedBox(
|
||||
height: context.dynamicWidth(0.12),
|
||||
width: context.dynamicWidth(0.12),
|
||||
child: Image.asset(
|
||||
'assets/icons/volt.png',
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: context.lowValue,
|
||||
),
|
||||
// Bieu do dien ap
|
||||
StreamBuilder<List<SensorLogs>>(
|
||||
stream: detailDeviceBloc.streamSensorTemps,
|
||||
builder: (context, sensorTempsSnapshot) {
|
||||
if (sensorTempsSnapshot.data == null) {
|
||||
detailDeviceBloc
|
||||
.getNearerSensorValue(widget.thingID);
|
||||
return const AspectRatio(
|
||||
aspectRatio: 3,
|
||||
child: Center(
|
||||
child: CircularProgressIndicator(),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return AspectRatio(
|
||||
aspectRatio: 3,
|
||||
child: Container(
|
||||
margin: context.paddingLow,
|
||||
child: sharedLineChart(
|
||||
appLocalization(context)
|
||||
.detail_device_volt_message,
|
||||
sensorTempsSnapshot.data ?? [],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
SizedBox(
|
||||
height: context.lowValue,
|
||||
),
|
||||
// Map
|
||||
Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Container(
|
||||
decoration: boxDecoration,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
height: context.dynamicHeight(0.3),
|
||||
padding: context.paddingLow,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.all(
|
||||
Radius.circular(15),
|
||||
),
|
||||
),
|
||||
child: deviceSnapshot
|
||||
.data!.settings!.latitude !=
|
||||
""
|
||||
? StreamBuilder<String>(
|
||||
stream: detailDeviceBloc
|
||||
.streamDeviceLocation,
|
||||
builder: (context, locationSnapshot) {
|
||||
if (locationSnapshot.data == null) {
|
||||
detailDeviceBloc.findLocation(
|
||||
context,
|
||||
deviceSnapshot
|
||||
.data!.areaPath!);
|
||||
}
|
||||
return GoogleMap(
|
||||
initialCameraPosition:
|
||||
initialCamera,
|
||||
mapType: MapType.normal,
|
||||
markers: {
|
||||
Marker(
|
||||
infoWindow: InfoWindow(
|
||||
title:
|
||||
locationSnapshot.data ??
|
||||
"",
|
||||
),
|
||||
markerId: MarkerId(
|
||||
deviceSnapshot
|
||||
.data!.thingId!),
|
||||
position: LatLng(
|
||||
double.parse(deviceSnapshot
|
||||
.data!
|
||||
.settings!
|
||||
.latitude!),
|
||||
double.parse(deviceSnapshot
|
||||
.data!
|
||||
.settings!
|
||||
.longitude!),
|
||||
),
|
||||
),
|
||||
},
|
||||
onMapCreated: (mapcontroller) {
|
||||
controller
|
||||
.complete(mapcontroller);
|
||||
},
|
||||
mapToolbarEnabled: false,
|
||||
zoomControlsEnabled: false,
|
||||
liteModeEnabled: true,
|
||||
);
|
||||
})
|
||||
: Center(
|
||||
child: Text(
|
||||
appLocalization(context)
|
||||
.detail_device_dont_has_location_message,
|
||||
),
|
||||
),
|
||||
},
|
||||
onMapCreated: (mapcontroller) {
|
||||
controller.complete(mapcontroller);
|
||||
},
|
||||
mapToolbarEnabled: false,
|
||||
zoomControlsEnabled: false,
|
||||
liteModeEnabled: true,
|
||||
)
|
||||
: Center(
|
||||
child: Text(
|
||||
appLocalization(context)
|
||||
.detail_device_dont_has_location_message,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
appLocalization(context)
|
||||
.device_update_location,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -63,7 +63,6 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
|
||||
} else {
|
||||
return SingleChildScrollView(
|
||||
child: Column(
|
||||
// mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
StreamBuilder<String>(
|
||||
stream: devicesManagerBloc.streamUserRole,
|
||||
|
||||
@@ -10,13 +10,6 @@ class MainBloc extends BlocBase {
|
||||
StreamSink<Bell> get sinkBellBloc => bellBloc.sink;
|
||||
Stream<Bell> get streamBellBloc => bellBloc.stream;
|
||||
|
||||
final title = StreamController<String>.broadcast();
|
||||
StreamSink<String> get sinkTitle => title.sink;
|
||||
Stream<String> get streamTitle => title.stream;
|
||||
|
||||
final role = StreamController<String>.broadcast();
|
||||
StreamSink<String> get sinkRole => role.sink;
|
||||
Stream<String> get streamRole => role.stream;
|
||||
|
||||
final language = StreamController<Locale?>.broadcast();
|
||||
StreamSink<Locale?> get sinkLanguage => language.sink;
|
||||
@@ -30,10 +23,6 @@ class MainBloc extends BlocBase {
|
||||
StreamSink<bool> get sinkIsVNIcon => isVNIcon.sink;
|
||||
Stream<bool> get streamIsVNIcon => isVNIcon.stream;
|
||||
|
||||
final currentPageIndex = StreamController<int>.broadcast();
|
||||
StreamSink<int> get sinkCurrentPageIndex => currentPageIndex.sink;
|
||||
Stream<int> get streamCurrentPageIndex => currentPageIndex.stream;
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,8 @@ import 'package:sfm_app/product/constant/app/app_constants.dart';
|
||||
import 'package:sfm_app/product/constant/enums/app_route_enums.dart';
|
||||
import 'package:sfm_app/product/constant/enums/role_enums.dart';
|
||||
import 'package:sfm_app/product/permission/location_permission.dart';
|
||||
import 'package:sfm_app/product/shared/shared_language_switch.dart';
|
||||
import '../../product/shared/shared_light_dark_switch.dart';
|
||||
import '../devices/devices_manager_bloc.dart';
|
||||
import '../devices/devices_manager_screen.dart';
|
||||
import '../home/home_screen.dart';
|
||||
@@ -53,8 +55,6 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
Bell bell = Bell();
|
||||
|
||||
void initialCheck() async {
|
||||
role = await apiServices.getUserRole();
|
||||
mainBloc.sinkRole.add(role);
|
||||
String language = await apiServices.checkLanguage();
|
||||
String theme = await apiServices.checkTheme();
|
||||
if (language == LanguageConstants.VIETNAM) {
|
||||
@@ -73,9 +73,12 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
LocationPermissionRequest.instance.checkLocationPermission(context);
|
||||
}
|
||||
|
||||
// For test
|
||||
late bool dayNightToggle2;
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
dayNightToggle2 = false;
|
||||
mainBloc = BlocProvider.of(context);
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
initialCheck();
|
||||
@@ -176,303 +179,215 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
ThemeNotifier themeNotifier = context.watch<ThemeNotifier>();
|
||||
checkSelectedIndex(currentPageIndex);
|
||||
|
||||
List<Widget> userDestinations = [
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.home),
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.home_outlined),
|
||||
label: appLocalization(context).home_page_destination,
|
||||
tooltip: appLocalization(context).home_page_destination,
|
||||
),
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.settings),
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.settings_outlined),
|
||||
label: appLocalization(context).manager_page_destination,
|
||||
tooltip: appLocalization(context).device_manager_page_name,
|
||||
),
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.location_on),
|
||||
icon:
|
||||
IconConstants.instance.getMaterialIcon(Icons.location_on_outlined),
|
||||
label: appLocalization(context).map_page_destination,
|
||||
tooltip: appLocalization(context).map_page_destination,
|
||||
),
|
||||
NavigationDestination(
|
||||
// selectedIcon: IconConstants.instance.getMaterialIcon(Icons.histor),
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.history_rounded),
|
||||
label: appLocalization(context).history_page_destination,
|
||||
tooltip: appLocalization(context).history_page_destination_tooltip,
|
||||
),
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.group),
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.group_outlined),
|
||||
label: appLocalization(context).group_page_destination,
|
||||
tooltip: appLocalization(context).group_page_destination_tooltip,
|
||||
),
|
||||
];
|
||||
|
||||
List<Widget> userBody = [
|
||||
BlocProvider(child: const HomeScreen(), blocBuilder: () => HomeBloc()),
|
||||
BlocProvider(
|
||||
child: const DevicesManagerScreen(),
|
||||
blocBuilder: () => DevicesManagerBloc()),
|
||||
BlocProvider(
|
||||
child: const MapScreen(),
|
||||
blocBuilder: () => MapBloc(),
|
||||
),
|
||||
BlocProvider(
|
||||
child: const DeviceLogsScreen(),
|
||||
blocBuilder: () => DeviceLogsBloc(),
|
||||
),
|
||||
BlocProvider(
|
||||
child: const InterFamilyScreen(),
|
||||
blocBuilder: () => InterFamilyBloc(),
|
||||
),
|
||||
];
|
||||
|
||||
List<Widget> modDestinations = [
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.home),
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.home_outlined),
|
||||
label: appLocalization(context).home_page_destination,
|
||||
tooltip: appLocalization(context).home_page_destination,
|
||||
),
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.settings),
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.settings_outlined),
|
||||
label: appLocalization(context).manager_page_destination,
|
||||
tooltip: appLocalization(context).device_manager_page_name,
|
||||
),
|
||||
NavigationDestination(
|
||||
selectedIcon: IconConstants.instance.getMaterialIcon(Icons.location_on),
|
||||
icon:
|
||||
IconConstants.instance.getMaterialIcon(Icons.location_on_outlined),
|
||||
label: appLocalization(context).map_page_destination,
|
||||
tooltip: appLocalization(context).map_page_destination,
|
||||
),
|
||||
];
|
||||
|
||||
List<Widget> modBody = [
|
||||
BlocProvider(child: const HomeScreen(), blocBuilder: () => HomeBloc()),
|
||||
BlocProvider(
|
||||
child: const DevicesManagerScreen(),
|
||||
blocBuilder: () => DevicesManagerBloc()),
|
||||
BlocProvider(
|
||||
child: const MapScreen(),
|
||||
blocBuilder: () => MapBloc(),
|
||||
),
|
||||
];
|
||||
|
||||
return StreamBuilder<String>(
|
||||
stream: mainBloc.streamRole,
|
||||
initialData: role,
|
||||
builder: (context, roleSnapshot) {
|
||||
return StreamBuilder<int>(
|
||||
stream: mainBloc.streamCurrentPageIndex,
|
||||
initialData: currentPageIndex,
|
||||
builder: (context, indexSnapshot) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
// centerTitle: true,
|
||||
// title: StreamBuilder<String>(
|
||||
// stream: mainBloc.streamTitle,
|
||||
// initialData: titlePage,
|
||||
// builder: (context, titleSnapshot) {
|
||||
// return Text(
|
||||
// titleSnapshot.data ?? ApplicationConstants.APP_NAME,
|
||||
// );
|
||||
// },
|
||||
// ),
|
||||
actions: [
|
||||
StreamBuilder<bool>(
|
||||
stream: mainBloc.streamThemeMode,
|
||||
initialData: isLight,
|
||||
builder: (context, themeModeSnapshot) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
themeNotifier.changeTheme();
|
||||
isLight = !isLight;
|
||||
mainBloc.sinkThemeMode.add(isLight);
|
||||
},
|
||||
icon: Icon(
|
||||
themeModeSnapshot.data ?? isLight
|
||||
? Icons.light_mode_outlined
|
||||
: Icons.dark_mode_outlined,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
StreamBuilder<bool>(
|
||||
stream: mainBloc.streamIsVNIcon,
|
||||
initialData: isVN,
|
||||
builder: (context, isVnSnapshot) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
log("Locale: ${LanguageServices().getLocale()}");
|
||||
Locale locale = await LanguageServices().setLocale(
|
||||
isVN
|
||||
? LanguageConstants.ENGLISH
|
||||
: LanguageConstants.VIETNAM);
|
||||
MyApp.setLocale(context, locale);
|
||||
isVN = !isVN;
|
||||
mainBloc.sinkIsVNIcon.add(isVN);
|
||||
},
|
||||
icon: Image.asset(
|
||||
IconConstants.instance.getIcon(
|
||||
isVnSnapshot.data ?? isVN
|
||||
? 'vi_icon'
|
||||
: 'en_icon'),
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
StreamBuilder<Bell>(
|
||||
stream: mainBloc.streamBellBloc,
|
||||
builder: (context, bellSnapshot) {
|
||||
return checkStatus(bellSnapshot.data?.items ?? [])
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
context.pushNamed(AppRoutes.BELL.name);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.notifications,
|
||||
),
|
||||
)
|
||||
: GestureDetector(
|
||||
child: badges.Badge(
|
||||
badgeStyle: const badges.BadgeStyle(
|
||||
shape: badges.BadgeShape.twitter,
|
||||
),
|
||||
key: _badgeKey,
|
||||
badgeContent: const Icon(
|
||||
CupertinoIcons.circle_filled,
|
||||
color: Colors.red,
|
||||
size: 5,
|
||||
),
|
||||
badgeAnimation:
|
||||
const badges.BadgeAnimation.slide(
|
||||
animationDuration:
|
||||
Duration(milliseconds: 200),
|
||||
colorChangeAnimationDuration:
|
||||
Duration(seconds: 1),
|
||||
loopAnimation: false,
|
||||
curve: Curves.decelerate,
|
||||
colorChangeAnimationCurve: Curves.easeInCirc,
|
||||
),
|
||||
showBadge: true,
|
||||
// ignorePointer: false,
|
||||
child: const Icon(
|
||||
Icons.notifications,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
context.pushNamed(AppRoutes.BELL.name);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
PopupMenuButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: ApplicationConstants.SETTINGS_PATH,
|
||||
onTap: () {
|
||||
context.pushNamed(AppRoutes.SETTINGS.name);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.person),
|
||||
const SizedBox(width: 5),
|
||||
Text(appLocalization(context).profile_icon_title)
|
||||
],
|
||||
),
|
||||
),
|
||||
PopupMenuItem(
|
||||
value: ApplicationConstants.LOGOUT_PATH,
|
||||
onTap: () {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
() async {
|
||||
await apiServices.logOut(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.logout),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
appLocalization(context).log_out,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
// bottomNavigationBar: Container(
|
||||
// decoration:
|
||||
// BoxDecoration(borderRadius: BorderRadius.circular(50)),
|
||||
// padding: context.paddingLow,
|
||||
// child: NavigationBar(
|
||||
// onDestinationSelected: (index) {
|
||||
// currentPageIndex = index;
|
||||
// mainBloc.sinkCurrentPageIndex.add(currentPageIndex);
|
||||
// checkSelectedIndex(currentPageIndex);
|
||||
// },
|
||||
// selectedIndex: indexSnapshot.data ?? currentPageIndex,
|
||||
// destinations: roleSnapshot.data == RoleEnums.USER.name
|
||||
// ? userDestinations
|
||||
// : modDestinations,
|
||||
// ),
|
||||
// ),
|
||||
// body: IndexedStack(
|
||||
// index: indexSnapshot.data ?? currentPageIndex,
|
||||
// children: roleSnapshot.data == RoleEnums.USER.name
|
||||
// ? userBody
|
||||
// : modBody,
|
||||
// ),
|
||||
body: PersistentTabView(
|
||||
context,
|
||||
controller: controller,
|
||||
screens: _buildScreens(),
|
||||
items: _navBarsItems(),
|
||||
confineInSafeArea: true,
|
||||
handleAndroidBackButtonPress: true,
|
||||
resizeToAvoidBottomInset: true,
|
||||
stateManagement: true,
|
||||
hideNavigationBarWhenKeyboardShows: true,
|
||||
// backgroundColor: Colors.transparent,
|
||||
decoration: NavBarDecoration(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
actions: [
|
||||
// LightDarkSwitch(
|
||||
// value: !isLight,
|
||||
// onChanged: (value) {
|
||||
// themeNotifier.changeTheme();
|
||||
// isLight = !isLight;
|
||||
// },
|
||||
// ),
|
||||
// SizedBox(
|
||||
// width: context.lowValue,
|
||||
// ),
|
||||
// StreamBuilder<bool>(
|
||||
// stream: mainBloc.streamIsVNIcon,
|
||||
// builder: (context, isVNSnapshot) {
|
||||
// return LanguageSwitch(
|
||||
// value: isVNSnapshot.data ?? isVN,
|
||||
// onChanged: (value) async {
|
||||
// Locale locale = await LanguageServices().setLocale(isVN
|
||||
// ? LanguageConstants.ENGLISH
|
||||
// : LanguageConstants.VIETNAM);
|
||||
// MyApp.setLocale(context, locale);
|
||||
// isVN = !isVN;
|
||||
// mainBloc.sinkIsVNIcon.add(isVN);
|
||||
// },
|
||||
// );
|
||||
// }),
|
||||
// SizedBox(
|
||||
// width: context.lowValue,
|
||||
// ),
|
||||
StreamBuilder<bool>(
|
||||
stream: mainBloc.streamThemeMode,
|
||||
initialData: isLight,
|
||||
builder: (context, themeModeSnapshot) {
|
||||
return IconButton(
|
||||
onPressed: () {
|
||||
themeNotifier.changeTheme();
|
||||
isLight = !isLight;
|
||||
mainBloc.sinkThemeMode.add(isLight);
|
||||
},
|
||||
icon: Icon(
|
||||
themeModeSnapshot.data ?? isLight
|
||||
? Icons.light_mode_outlined
|
||||
: Icons.dark_mode_outlined,
|
||||
),
|
||||
popAllScreensOnTapOfSelectedTab: true,
|
||||
itemAnimationProperties: const ItemAnimationProperties(
|
||||
duration: Duration(milliseconds: 200),
|
||||
curve: Curves.bounceInOut,
|
||||
);
|
||||
},
|
||||
),
|
||||
StreamBuilder<bool>(
|
||||
stream: mainBloc.streamIsVNIcon,
|
||||
initialData: isVN,
|
||||
builder: (context, isVnSnapshot) {
|
||||
return IconButton(
|
||||
onPressed: () async {
|
||||
log("Locale: ${LanguageServices().getLocale()}");
|
||||
Locale locale = await LanguageServices().setLocale(isVN
|
||||
? LanguageConstants.ENGLISH
|
||||
: LanguageConstants.VIETNAM);
|
||||
MyApp.setLocale(context, locale);
|
||||
isVN = !isVN;
|
||||
mainBloc.sinkIsVNIcon.add(isVN);
|
||||
},
|
||||
icon: Image.asset(
|
||||
IconConstants.instance.getIcon(
|
||||
isVnSnapshot.data ?? isVN ? 'vi_icon' : 'en_icon'),
|
||||
height: 24,
|
||||
width: 24,
|
||||
),
|
||||
screenTransitionAnimation: const ScreenTransitionAnimation(
|
||||
animateTabTransition: true,
|
||||
curve: Curves.linear,
|
||||
duration: Duration(milliseconds: 200),
|
||||
);
|
||||
},
|
||||
),
|
||||
StreamBuilder<Bell>(
|
||||
stream: mainBloc.streamBellBloc,
|
||||
builder: (context, bellSnapshot) {
|
||||
return checkStatus(bellSnapshot.data?.items ?? [])
|
||||
? IconButton(
|
||||
onPressed: () {
|
||||
context.pushNamed(AppRoutes.BELL.name);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.notifications,
|
||||
),
|
||||
)
|
||||
: GestureDetector(
|
||||
child: badges.Badge(
|
||||
badgeStyle: const badges.BadgeStyle(
|
||||
shape: badges.BadgeShape.twitter,
|
||||
),
|
||||
key: _badgeKey,
|
||||
badgeContent: const Icon(
|
||||
CupertinoIcons.circle_filled,
|
||||
color: Colors.red,
|
||||
size: 5,
|
||||
),
|
||||
badgeAnimation: const badges.BadgeAnimation.slide(
|
||||
animationDuration: Duration(milliseconds: 200),
|
||||
colorChangeAnimationDuration: Duration(seconds: 1),
|
||||
loopAnimation: false,
|
||||
curve: Curves.decelerate,
|
||||
colorChangeAnimationCurve: Curves.easeInCirc,
|
||||
),
|
||||
showBadge: true,
|
||||
// ignorePointer: false,
|
||||
child: const Icon(
|
||||
Icons.notifications,
|
||||
size: 30,
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
context.pushNamed(AppRoutes.BELL.name);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
PopupMenuButton(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
icon: const Icon(Icons.more_vert),
|
||||
itemBuilder: (context) {
|
||||
return [
|
||||
PopupMenuItem(
|
||||
value: ApplicationConstants.SETTINGS_PATH,
|
||||
onTap: () {
|
||||
context.pushNamed(AppRoutes.SETTINGS.name);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.person),
|
||||
const SizedBox(width: 5),
|
||||
Text(appLocalization(context).profile_icon_title)
|
||||
],
|
||||
),
|
||||
),
|
||||
navBarStyle: NavBarStyle.style4,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
PopupMenuItem(
|
||||
value: ApplicationConstants.LOGOUT_PATH,
|
||||
onTap: () {
|
||||
Future.delayed(
|
||||
const Duration(milliseconds: 200),
|
||||
() async {
|
||||
await apiServices.logOut(context);
|
||||
},
|
||||
);
|
||||
},
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.logout),
|
||||
const SizedBox(width: 5),
|
||||
Text(
|
||||
appLocalization(context).log_out,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
];
|
||||
},
|
||||
)
|
||||
],
|
||||
),
|
||||
// bottomNavigationBar: Container(
|
||||
// decoration:
|
||||
// BoxDecoration(borderRadius: BorderRadius.circular(50)),
|
||||
// padding: context.paddingLow,
|
||||
// child: NavigationBar(
|
||||
// onDestinationSelected: (index) {
|
||||
// currentPageIndex = index;
|
||||
// mainBloc.sinkCurrentPageIndex.add(currentPageIndex);
|
||||
// checkSelectedIndex(currentPageIndex);
|
||||
// },
|
||||
// selectedIndex: indexSnapshot.data ?? currentPageIndex,
|
||||
// destinations: roleSnapshot.data == RoleEnums.USER.name
|
||||
// ? userDestinations
|
||||
// : modDestinations,
|
||||
// ),
|
||||
// ),
|
||||
// body: IndexedStack(
|
||||
// index: indexSnapshot.data ?? currentPageIndex,
|
||||
// children: roleSnapshot.data == RoleEnums.USER.name
|
||||
// ? userBody
|
||||
// : modBody,
|
||||
// ),
|
||||
body: PersistentTabView(
|
||||
context,
|
||||
controller: controller,
|
||||
screens: _buildScreens(),
|
||||
items: _navBarsItems(),
|
||||
confineInSafeArea: true,
|
||||
handleAndroidBackButtonPress: true,
|
||||
resizeToAvoidBottomInset: true,
|
||||
stateManagement: true,
|
||||
hideNavigationBarWhenKeyboardShows: true,
|
||||
// backgroundColor: Colors.transparent,
|
||||
decoration: NavBarDecoration(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
),
|
||||
popAllScreensOnTapOfSelectedTab: true,
|
||||
itemAnimationProperties: const ItemAnimationProperties(
|
||||
duration: Duration(milliseconds: 200),
|
||||
curve: Curves.bounceInOut,
|
||||
),
|
||||
screenTransitionAnimation: const ScreenTransitionAnimation(
|
||||
animateTabTransition: true,
|
||||
curve: Curves.linear,
|
||||
duration: Duration(milliseconds: 200),
|
||||
),
|
||||
navBarStyle: NavBarStyle.style4,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -487,23 +402,4 @@ class _MainScreenState extends State<MainScreen> with WidgetsBindingObserver {
|
||||
}
|
||||
return !bells.any((bell) => bell.status == 0);
|
||||
}
|
||||
|
||||
void checkSelectedIndex(int current) {
|
||||
if (current == 0) {
|
||||
titlePage = appLocalization(context).home_page_name;
|
||||
mainBloc.sinkTitle.add(titlePage);
|
||||
} else if (current == 1) {
|
||||
titlePage = appLocalization(context).device_manager_page_name;
|
||||
mainBloc.sinkTitle.add(titlePage);
|
||||
} else if (current == 2) {
|
||||
titlePage = appLocalization(context).map_page_destination;
|
||||
mainBloc.sinkTitle.add(titlePage);
|
||||
} else if (current == 3) {
|
||||
titlePage = appLocalization(context).device_log_page_name;
|
||||
mainBloc.sinkTitle.add(titlePage);
|
||||
} else if (current == 4) {
|
||||
titlePage = appLocalization(context).interfamily_page_name;
|
||||
mainBloc.sinkTitle.add(titlePage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,7 +38,6 @@ class _MyAppState extends State<MyApp> {
|
||||
Locale? _locale;
|
||||
late MainBloc mainBloc;
|
||||
LanguageServices languageServices = LanguageServices();
|
||||
// late ThemeNotifier themeNotifier;
|
||||
setLocale(Locale locale) {
|
||||
_locale = locale;
|
||||
mainBloc.sinkLanguage.add(_locale);
|
||||
@@ -48,10 +47,6 @@ class _MyAppState extends State<MyApp> {
|
||||
void initState() {
|
||||
super.initState();
|
||||
mainBloc = BlocProvider.of(context);
|
||||
// themeNotifier = Provider.of<ThemeNotifier>(context, listen: false);
|
||||
// ThemeNotifier().loadThemeFromPreferences();
|
||||
// log("ThemeKey1: ${LocaleManager.instance.getStringValue(PreferencesKeys.THEME)}");
|
||||
// log("Date: ${DateTime.parse("2024-11-16T07:17:36.785Z")}");
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -71,9 +71,8 @@ extension PageExtension on BuildContext {
|
||||
extension DurationExtension on BuildContext {
|
||||
Duration get lowDuration => const Duration(milliseconds: 150);
|
||||
Duration get normalDuration => const Duration(milliseconds: 500);
|
||||
Duration dynamicSecondDuration(int seconds) => Duration(seconds: seconds);
|
||||
Duration dynamicMilliSecondDuration(int milliseconds) => Duration(milliseconds: milliseconds);
|
||||
Duration dynamicMinutesDuration(int minutes) => Duration(minutes: minutes);
|
||||
|
||||
}
|
||||
|
||||
// RADIUS
|
||||
@@ -81,6 +80,7 @@ extension RadiusExtension on BuildContext {
|
||||
Radius get lowRadius => Radius.circular(width * 0.02);
|
||||
Radius get normalRadius => Radius.circular(width * 0.05);
|
||||
Radius get highRadius => Radius.circular(width * 0.1);
|
||||
Radius dynamicRadius(double radius) => Radius.circular(radius);
|
||||
}
|
||||
|
||||
extension TextStyleExtention on BuildContext {
|
||||
@@ -100,4 +100,3 @@ extension TextStyleExtention on BuildContext {
|
||||
TextStyle get headlineLargeTextStyle =>
|
||||
Theme.of(this).textTheme.headlineLarge!;
|
||||
}
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
"device_update_ward": "Ward/Commune",
|
||||
"description_NOTUSE10": "This is english language in DetailDevicePage",
|
||||
"detail_device_dont_has_location_message": "No location information available yet",
|
||||
"detail_device_volt_message": "Measured voltage (V)",
|
||||
"no_data_message": "No data yet",
|
||||
"normal_message": "Normal",
|
||||
"warning_status_message": "Warning",
|
||||
|
||||
@@ -178,6 +178,7 @@
|
||||
"device_update_ward": "Phường/Xã",
|
||||
"description_NOTUSE10": "This is vietnamese language in DetailDevicePage",
|
||||
"detail_device_dont_has_location_message": "Chưa có thông tin về vị trí",
|
||||
"detail_device_volt_message": "Nguồn điện đo được (V)",
|
||||
"no_data_message": "Chưa có",
|
||||
"normal_message": "Bình thường",
|
||||
"warning_status_message": "Cảnh báo",
|
||||
|
||||
30
lib/product/shared/shared_curve.dart
Normal file
30
lib/product/shared/shared_curve.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class CuveEdgesCustom extends CustomClipper<Path> {
|
||||
@override
|
||||
getClip(Size size) {
|
||||
Path path = Path();
|
||||
path.lineTo(0, size.height);
|
||||
final firstCurve = Offset(0, size.height - 30);
|
||||
final lastCurve = Offset(30, size.height - 30);
|
||||
path.quadraticBezierTo(
|
||||
firstCurve.dx, firstCurve.dy, lastCurve.dx, lastCurve.dy);
|
||||
final secondFirstCurve = Offset(0, size.height - 30);
|
||||
final secondLastCurve = Offset(size.width - 30, size.height - 30);
|
||||
path.quadraticBezierTo(secondFirstCurve.dx, secondFirstCurve.dy,
|
||||
secondLastCurve.dx, secondLastCurve.dy);
|
||||
final thirdFirstCurve = Offset(size.width, size.height - 30);
|
||||
final thirdLastCurve = Offset(size.width, size.height);
|
||||
path.quadraticBezierTo(thirdFirstCurve.dx, thirdFirstCurve.dy,
|
||||
thirdLastCurve.dx, thirdLastCurve.dy);
|
||||
|
||||
path.lineTo(size.width, 0);
|
||||
path.close();
|
||||
return path;
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReclip(covariant CustomClipper oldClipper) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
127
lib/product/shared/shared_language_switch.dart
Normal file
127
lib/product/shared/shared_language_switch.dart
Normal file
@@ -0,0 +1,127 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
|
||||
|
||||
const int _kDuration = 300;
|
||||
const double _kWidth = 60;
|
||||
const double _kheight = 30;
|
||||
|
||||
class LanguageSwitch extends StatefulWidget {
|
||||
const LanguageSwitch({
|
||||
super.key,
|
||||
required this.value,
|
||||
this.onChanged,
|
||||
});
|
||||
|
||||
/// Whether this switch is on or off.
|
||||
///
|
||||
/// This property must not be null.
|
||||
final bool value;
|
||||
|
||||
/// Called when the user toggles the switch on or off.
|
||||
///
|
||||
/// The switch passes the new value to the callback but does not actually
|
||||
/// change state until the parent widget rebuilds the switch with the new
|
||||
/// value.
|
||||
///
|
||||
/// If null, the switch will be displayed as disabled.
|
||||
///
|
||||
/// The callback provided to [onChanged] should update the state of the parent
|
||||
/// [StatefulWidget] using the [State.setState] method, so that the parent
|
||||
/// gets rebuilt; for example:
|
||||
///
|
||||
/// ```dart
|
||||
/// LanguageSwitch(
|
||||
/// value: _giveVerse,
|
||||
/// onChanged: (bool newValue) {
|
||||
/// setState(() {
|
||||
/// _giveVerse = newValue;
|
||||
/// });
|
||||
/// },
|
||||
/// )
|
||||
/// ```
|
||||
final ValueChanged<bool>? onChanged;
|
||||
|
||||
@override
|
||||
State<LanguageSwitch> createState() => _LanguageSwitchState();
|
||||
}
|
||||
|
||||
class _LanguageSwitchState extends State<LanguageSwitch> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool toggleState = widget.value;
|
||||
const dayColor = Colors.blue;
|
||||
const nightColor = Colors.grey;
|
||||
|
||||
return InkWell(
|
||||
onTap: () => setState(() {
|
||||
toggleState = !toggleState;
|
||||
widget.onChanged?.call(toggleState);
|
||||
}),
|
||||
customBorder: const StadiumBorder(),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
width: _kWidth,
|
||||
height: _kheight,
|
||||
decoration: ShapeDecoration(
|
||||
color: toggleState ? dayColor : nightColor,
|
||||
shape: const StadiumBorder(),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
//day icon
|
||||
AnimatedOpacity(
|
||||
opacity: toggleState ? 1 : 0,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedAlign(
|
||||
alignment: toggleState
|
||||
? Alignment.centerLeft
|
||||
: Alignment.centerRight,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
// child: const Icon(
|
||||
// Icons.circle,
|
||||
// size: 30,
|
||||
// // color: Colors.white,
|
||||
// ),
|
||||
child: Image.asset(
|
||||
IconConstants.instance.getIcon('vi_icon'),
|
||||
width: 30,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
//night Icon
|
||||
AnimatedOpacity(
|
||||
opacity: toggleState ? 0 : 1,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedAlign(
|
||||
alignment: toggleState
|
||||
? Alignment.centerLeft
|
||||
: Alignment.centerRight,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedRotation(
|
||||
turns: toggleState ? 0.0 : 0.5,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
// child: const Icon(
|
||||
// Icons.nightlight,
|
||||
// size: 30,
|
||||
// // color: Colors.white,
|
||||
// ),
|
||||
child: Image.asset(
|
||||
IconConstants.instance.getIcon('en_icon'),
|
||||
width: 30,
|
||||
height: 30,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
212
lib/product/shared/shared_light_dark_switch.dart
Normal file
212
lib/product/shared/shared_light_dark_switch.dart
Normal file
@@ -0,0 +1,212 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
const int _kDuration = 300;
|
||||
const double _kWidth = 60;
|
||||
const double _kheight = 30;
|
||||
|
||||
class LightDarkSwitch extends StatefulWidget {
|
||||
const LightDarkSwitch({
|
||||
super.key,
|
||||
required this.value,
|
||||
this.onChanged,
|
||||
});
|
||||
|
||||
/// Whether this switch is on or off.
|
||||
///
|
||||
/// This property must not be null.
|
||||
final bool value;
|
||||
|
||||
/// Called when the user toggles the switch on or off.
|
||||
///
|
||||
/// The switch passes the new value to the callback but does not actually
|
||||
/// change state until the parent widget rebuilds the switch with the new
|
||||
/// value.
|
||||
///
|
||||
/// If null, the switch will be displayed as disabled.
|
||||
///
|
||||
/// The callback provided to [onChanged] should update the state of the parent
|
||||
/// [StatefulWidget] using the [State.setState] method, so that the parent
|
||||
/// gets rebuilt; for example:
|
||||
///
|
||||
/// ```dart
|
||||
/// LightDarkSwitch(
|
||||
/// value: _giveVerse,
|
||||
/// onChanged: (bool newValue) {
|
||||
/// setState(() {
|
||||
/// _giveVerse = newValue;
|
||||
/// });
|
||||
/// },
|
||||
/// )
|
||||
/// ```
|
||||
final ValueChanged<bool>? onChanged;
|
||||
|
||||
@override
|
||||
State<LightDarkSwitch> createState() => _LightDarkSwitchState();
|
||||
}
|
||||
|
||||
class _LightDarkSwitchState extends State<LightDarkSwitch> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool toggleState = widget.value;
|
||||
const activeColor = Colors.blue;
|
||||
const inactiveColor = Colors.black;
|
||||
|
||||
return Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => setState(() {
|
||||
toggleState = !toggleState;
|
||||
widget.onChanged?.call(toggleState);
|
||||
}),
|
||||
customBorder: const StadiumBorder(),
|
||||
child: AnimatedContainer(
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
width: _kWidth,
|
||||
height: _kheight,
|
||||
decoration: ShapeDecoration(
|
||||
color: toggleState ? activeColor : inactiveColor,
|
||||
shape: const StadiumBorder(),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(4.0),
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
//Light icon
|
||||
AnimatedOpacity(
|
||||
opacity: toggleState ? 1 : 0,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedAlign(
|
||||
alignment: toggleState
|
||||
? Alignment.centerLeft
|
||||
: Alignment.centerRight,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.sunny,
|
||||
size: 25,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
AnimatedPositioned(
|
||||
top: 2,
|
||||
right: toggleState ? 6 : 40,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedOpacity(
|
||||
opacity: toggleState ? 1 : 0,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.circle,
|
||||
size: 8,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
AnimatedPositioned(
|
||||
top: 16,
|
||||
right: toggleState ? 14 : 40,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.circle,
|
||||
size: 3,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
|
||||
//Dark Icon
|
||||
AnimatedOpacity(
|
||||
opacity: toggleState ? 0 : 1,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedAlign(
|
||||
alignment: toggleState
|
||||
? Alignment.centerLeft
|
||||
: Alignment.centerRight,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.mode_night_sharp,
|
||||
size: 25,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
AnimatedPositioned(
|
||||
bottom: 3,
|
||||
left: toggleState ? 40 : 14,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedOpacity(
|
||||
opacity: toggleState ? 0 : 1,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.star,
|
||||
size: 8,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
AnimatedPositioned(
|
||||
top: 2,
|
||||
left: toggleState ? 40 : 4,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedOpacity(
|
||||
opacity: toggleState ? 0 : 1,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.star,
|
||||
size: 10,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
AnimatedPositioned(
|
||||
top: 10,
|
||||
left: toggleState ? 40 : 16,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedOpacity(
|
||||
opacity: toggleState ? 0 : 1,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.circle,
|
||||
size: 3,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
AnimatedPositioned(
|
||||
top: 4,
|
||||
left: toggleState ? 40 : 22,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: AnimatedOpacity(
|
||||
opacity: toggleState ? 0 : 1,
|
||||
duration: const Duration(milliseconds: _kDuration),
|
||||
child: const Icon(
|
||||
Icons.circle,
|
||||
size: 3,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// const SizedBox(
|
||||
// width: 8.0,
|
||||
// ),
|
||||
// Text(
|
||||
// "Dark",
|
||||
// style: TextStyle(
|
||||
// color: toggleState ? Colors.grey : inactiveColor,
|
||||
// fontWeight: FontWeight.bold,
|
||||
// ),
|
||||
// ),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -3,50 +3,40 @@ import 'package:flutter/material.dart';
|
||||
import 'package:sfm_app/feature/device_log/device_logs_model.dart';
|
||||
import 'package:sfm_app/product/utils/date_time_utils.dart';
|
||||
|
||||
Widget sharedLineChart(
|
||||
String chartName, List<SensorLogs> sensors, double maxValue) {
|
||||
double max = sensors
|
||||
.map((sensor) => sensor.value!) // Lấy giá trị của từng sensor
|
||||
.reduce((a, b) => a > b ? a : b)
|
||||
.toDouble();
|
||||
double averageValue = (0 + maxValue) / 2;
|
||||
Widget sharedLineChart(String chartName, List<SensorLogs> sensors) {
|
||||
return LineChart(
|
||||
LineChartData(
|
||||
minX: 0,
|
||||
minY: 0,
|
||||
maxY: max + 20,
|
||||
maxY: 4000,
|
||||
titlesData: FlTitlesData(
|
||||
show: true,
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
show: true,
|
||||
topTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameSize: 20,
|
||||
axisNameWidget: Text(
|
||||
chartName,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
rightTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
),
|
||||
leftTitles: const AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: false,
|
||||
),
|
||||
bottomTitles: AxisTitles(
|
||||
axisNameSize: 20,
|
||||
axisNameWidget: Text(chartName),
|
||||
),
|
||||
leftTitles: AxisTitles(
|
||||
sideTitles: SideTitles(
|
||||
showTitles: true,
|
||||
reservedSize: 40,
|
||||
getTitlesWidget: (value, meta) {
|
||||
if (value == 0) {
|
||||
return const Text("0");
|
||||
} else if (value == averageValue) {
|
||||
return Text(averageValue.toInt().toString());
|
||||
} else if (value == maxValue) {
|
||||
return Text(maxValue.toInt().toString());
|
||||
} else {
|
||||
return Container();
|
||||
}
|
||||
},
|
||||
))),
|
||||
),
|
||||
),
|
||||
lineTouchData: LineTouchData(
|
||||
touchTooltipData: LineTouchTooltipData(
|
||||
tooltipBgColor: Colors.grey.withOpacity(0.3),
|
||||
@@ -55,7 +45,7 @@ Widget sharedLineChart(
|
||||
final index = spot.x.toInt();
|
||||
final sensorData = sensors[index];
|
||||
return LineTooltipItem(
|
||||
'Time: ${DateTimeUtils.instance.convertCurrentMillisToDateTimeString(sensorData.time!)}\nValue: ${sensorData.value}',
|
||||
'Time: ${DateTimeUtils.instance.convertCurrentMillisToDateTimeString(sensorData.time!)}\nValue: ${(sensorData.value! / 1000).toDouble()}V',
|
||||
const TextStyle(),
|
||||
);
|
||||
}).toList();
|
||||
@@ -91,10 +81,13 @@ Widget sharedLineChart(
|
||||
gridData: const FlGridData(show: false),
|
||||
borderData: FlBorderData(
|
||||
border: Border(
|
||||
top: BorderSide.none,
|
||||
right: BorderSide.none,
|
||||
left: BorderSide(color: Colors.black.withOpacity(0.7)),
|
||||
bottom: BorderSide(color: Colors.black.withOpacity(0.7))),
|
||||
top: BorderSide.none,
|
||||
right: BorderSide.none,
|
||||
left: BorderSide.none,
|
||||
bottom: BorderSide(
|
||||
color: Colors.black.withOpacity(0.7),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -47,40 +47,40 @@ class SharedPieChart extends StatelessWidget {
|
||||
color: Colors.grey,
|
||||
value: offlineCount.toDouble(),
|
||||
title: offlineCount.toString(),
|
||||
radius: context.dynamicWidth(0.3),
|
||||
radius: context.dynamicWidth(0.2),
|
||||
titleStyle: titleStyle,
|
||||
),
|
||||
PieChartSectionData(
|
||||
color: Colors.green,
|
||||
value: normalCount.toDouble(),
|
||||
title: normalCount.toString(),
|
||||
radius: context.dynamicWidth(0.3),
|
||||
radius: context.dynamicWidth(0.2),
|
||||
titleStyle: titleStyle,
|
||||
),
|
||||
PieChartSectionData(
|
||||
color: Colors.red,
|
||||
value: warningCount.toDouble(),
|
||||
title: warningCount.toString(),
|
||||
radius: context.dynamicWidth(0.3),
|
||||
radius: context.dynamicWidth(0.2),
|
||||
titleStyle: titleStyle,
|
||||
),
|
||||
PieChartSectionData(
|
||||
color: Colors.yellow,
|
||||
value: inProgressCount.toDouble(),
|
||||
title: inProgressCount.toString(),
|
||||
radius: context.dynamicWidth(0.3),
|
||||
radius: context.dynamicWidth(0.2),
|
||||
titleStyle: titleStyle,
|
||||
),
|
||||
PieChartSectionData(
|
||||
color: Colors.black, // Có thể thêm màu cho trạng thái lỗi
|
||||
value: errorCount.toDouble(),
|
||||
title: errorCount.toString(),
|
||||
radius: context.dynamicWidth(0.3),
|
||||
radius: context.dynamicWidth(0.2),
|
||||
titleStyle: titleStyle,
|
||||
),
|
||||
],
|
||||
centerSpaceRadius: 0,
|
||||
sectionsSpace: 1,
|
||||
centerSpaceRadius: context.dynamicWidth(0.1),
|
||||
sectionsSpace: 2,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -82,6 +82,17 @@ class ThemeNotifier extends ChangeNotifier {
|
||||
AppThemes _currenThemeEnum = AppThemes.LIGHT;
|
||||
AppThemes get currenThemeEnum => _currenThemeEnum;
|
||||
|
||||
Future<void> loadThemeFromPreferences() async {
|
||||
// String themeKey =
|
||||
// LocaleManager.instance.getStringValue(PreferencesKeys.THEME);
|
||||
// if (themeKey == AppThemes.LIGHT.name) {
|
||||
// _currentTheme = AppThemeLight.instance.theme;
|
||||
// } else {
|
||||
// _currentTheme = AppThemeDark.instance.theme;
|
||||
// }
|
||||
// notifyListeners();
|
||||
}
|
||||
|
||||
void changeValue(AppThemes theme) {
|
||||
if (theme == AppThemes.LIGHT) {
|
||||
_currentTheme = AppThemeLight.instance.theme;
|
||||
|
||||
@@ -8,6 +8,7 @@ import 'package:sfm_app/product/shared/model/district_model.dart';
|
||||
import 'package:sfm_app/product/shared/model/province_model.dart';
|
||||
|
||||
import '../../feature/devices/device_model.dart';
|
||||
import '../constant/icon/icon_constants.dart';
|
||||
import '../shared/model/ward_model.dart';
|
||||
|
||||
class DeviceUtils {
|
||||
@@ -63,13 +64,13 @@ class DeviceUtils {
|
||||
}
|
||||
}
|
||||
if (sensor.name == "7") {
|
||||
map['sensorVolt'] = "${(sensor.value!) / 1000} V";
|
||||
map['sensorVolt'] = "${(sensor.value!) / 1000}";
|
||||
}
|
||||
if (sensor.name == "8") {
|
||||
map['sensorTemp'] = "${sensor.value}°C";
|
||||
map['sensorTemp'] = "${sensor.value}";
|
||||
}
|
||||
if (sensor.name == "9") {
|
||||
map['sensorHum'] = "${sensor.value} %";
|
||||
map['sensorHum'] = "${sensor.value}";
|
||||
}
|
||||
if (sensor.name == "10") {
|
||||
map['sensorBattery'] = "${sensor.value}";
|
||||
@@ -270,4 +271,53 @@ class DeviceUtils {
|
||||
return Colors.green;
|
||||
}
|
||||
}
|
||||
|
||||
List<String> deviceBatteryImg = [
|
||||
IconConstants.instance.getIcon("full-battery"),
|
||||
IconConstants.instance.getIcon("half-battery"),
|
||||
IconConstants.instance.getIcon("low-battery"),
|
||||
IconConstants.instance.getIcon("empty-battery"),
|
||||
];
|
||||
|
||||
String getDeviceBatteryImg(int battery) {
|
||||
if (battery <= 5) {
|
||||
return deviceBatteryImg[3];
|
||||
} else if (battery <= 20) {
|
||||
return deviceBatteryImg[2];
|
||||
} else if (battery <= 90) {
|
||||
return deviceBatteryImg[1];
|
||||
} else {
|
||||
return deviceBatteryImg[0];
|
||||
}
|
||||
}
|
||||
|
||||
Color getDeviceTempColor(int temp) {
|
||||
if (temp < 30) {
|
||||
return Colors.green;
|
||||
} else if (temp < 50) {
|
||||
return Colors.orange;
|
||||
} else {
|
||||
return Colors.red;
|
||||
}
|
||||
}
|
||||
|
||||
Color getDeviceBatteryColor(int battery) {
|
||||
if (battery < 20) {
|
||||
return Colors.red;
|
||||
} else if (battery < 80) {
|
||||
return Colors.orange;
|
||||
} else {
|
||||
return Colors.green;
|
||||
}
|
||||
}
|
||||
|
||||
Color getSignalIconColor(BuildContext context, String signal) {
|
||||
if (signal == appLocalization(context).gf_weak_signal_message) {
|
||||
return Colors.red;
|
||||
} else if (signal == appLocalization(context).gf_moderate_signal_message) {
|
||||
return Colors.yellow;
|
||||
} else {
|
||||
return Colors.green;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user