refactor(ui): update DetailDevice screen layout

This commit is contained in:
anhtunz
2024-12-26 11:31:21 +07:00
parent 77afc09d19
commit a69429b05f
26 changed files with 1231 additions and 671 deletions

View File

@@ -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);

View File

@@ -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,
),
)
],
),
),
],
),
),
],
),
),
);

View File

@@ -63,7 +63,6 @@ class _DevicesManagerScreenState extends State<DevicesManagerScreen> {
} else {
return SingleChildScrollView(
child: Column(
// mainAxisSize: MainAxisSize.min,
children: [
StreamBuilder<String>(
stream: devicesManagerBloc.streamUserRole,