From 77afc09d1941aafe6c39735d2ee16f1a183ca328 Mon Sep 17 00:00:00 2001 From: anhtunz Date: Tue, 24 Dec 2024 11:16:00 +0700 Subject: [PATCH] Update Pie Chart In DeviceManagerScreen --- lib/feature/devices/devices_manager_bloc.dart | 40 +++- .../devices/devices_manager_screen.dart | 93 ++++++--- lib/product/constant/app/app_constants.dart | 5 + lib/product/lang/l10n/app_vi.arb | 2 +- lib/product/shared/shared_pie_chart.dart | 180 ++++++++++++++++++ lib/product/utils/device_utils.dart | 6 +- 6 files changed, 297 insertions(+), 29 deletions(-) create mode 100644 lib/product/shared/shared_pie_chart.dart diff --git a/lib/feature/devices/devices_manager_bloc.dart b/lib/feature/devices/devices_manager_bloc.dart index f265938..b475e73 100644 --- a/lib/feature/devices/devices_manager_bloc.dart +++ b/lib/feature/devices/devices_manager_bloc.dart @@ -1,6 +1,8 @@ import 'dart:async'; import 'dart:convert'; +import 'package:sfm_app/product/constant/app/app_constants.dart'; + import 'device_model.dart'; import '../../product/base/bloc/base_bloc.dart'; import '../../product/services/api_services.dart'; @@ -18,18 +20,54 @@ class DevicesManagerBloc extends BlocBase { StreamSink> get sinkAllDevices => allDevices.sink; Stream> get streamAllDevices => allDevices.stream; + final deviceByState = StreamController>>.broadcast(); + StreamSink>> get sinkDeviceByState => + deviceByState.sink; + Stream>> get streamDeviceByState => + deviceByState.stream; + @override void dispose() {} void getDevice() async { String body = await apiServices.getOwnerDevices(); - if (body != "") { + Map> deviceByState = { + ApplicationConstants.OFFLINE_STATE: [], + ApplicationConstants.NORMAL_STATE: [], + ApplicationConstants.WARNING_STATE: [], + ApplicationConstants.INPROGRESS_STATE: [], + ApplicationConstants.ERROR_STATE: [], + }; + + if (body.isNotEmpty) { final data = jsonDecode(body); List items = data['items']; List originalDevices = Device.fromJsonDynamicList(items); List devices = DeviceUtils.instance.sortDeviceByState(originalDevices); + for (var device in devices) { + String stateKey; + switch (device.state) { + case -1: + stateKey = ApplicationConstants.OFFLINE_STATE; + break; + case 0: + stateKey = ApplicationConstants.NORMAL_STATE; + break; + case 1: + stateKey = ApplicationConstants.WARNING_STATE; + break; + case 2: + stateKey = ApplicationConstants.INPROGRESS_STATE; + break; + default: + stateKey = ApplicationConstants.ERROR_STATE; + break; + } + deviceByState[stateKey]!.add(device); + } sinkAllDevices.add(devices); + sinkDeviceByState.add(deviceByState); } } } diff --git a/lib/feature/devices/devices_manager_screen.dart b/lib/feature/devices/devices_manager_screen.dart index 0155cec..feda0ec 100644 --- a/lib/feature/devices/devices_manager_screen.dart +++ b/lib/feature/devices/devices_manager_screen.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; +import 'package:sfm_app/product/shared/shared_pie_chart.dart'; import 'add_new_device_widget.dart'; import 'delete_device_widget.dart'; import 'device_model.dart'; @@ -50,6 +51,7 @@ class _DevicesManagerScreenState extends State { @override Widget build(BuildContext context) { return Scaffold( + // backgroundColor: Colors.grey.withOpacity(0.6), body: SafeArea( child: StreamBuilder>( stream: devicesManagerBloc.streamAllDevices, @@ -61,7 +63,7 @@ class _DevicesManagerScreenState extends State { } else { return SingleChildScrollView( child: Column( - mainAxisSize: MainAxisSize.min, + // mainAxisSize: MainAxisSize.min, children: [ StreamBuilder( stream: devicesManagerBloc.streamUserRole, @@ -79,37 +81,53 @@ class _DevicesManagerScreenState extends State { if (roleSnapshot.data == RoleEnums.ADMIN.name || roleSnapshot.data == RoleEnums.USER.name) DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_action))), - DataColumn( label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_deviceName))), + child: Text(appLocalization(context) + .paginated_data_table_column_action), + ), + ), DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_deviceStatus))), + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_deviceName), + ), + ), DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_deviceBaterry))), + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_deviceStatus), + ), + ), DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_deviceSignal))), + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_deviceBaterry), + ), + ), DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_deviceTemperature))), + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_deviceSignal), + ), + ), DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_deviceHump))), + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_deviceTemperature), + ), + ), DataColumn( - label: Center( - child: Text(appLocalization(context) - .paginated_data_table_column_devicePower))), + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_deviceHump), + ), + ), + DataColumn( + label: Center( + child: Text(appLocalization(context) + .paginated_data_table_column_devicePower), + ), + ), ], onPageChanged: (int pageIndex) { // log('Chuyen page: $pageIndex'); @@ -143,7 +161,30 @@ class _DevicesManagerScreenState extends State { ), ); }, - ) + ), + SizedBox(height: context.lowValue), + Text( + appLocalization(context).overview_message, + style: const TextStyle( + fontSize: 20, + fontWeight: FontWeight.bold, + ), + ), + SizedBox(height: context.lowValue), + StreamBuilder>>( + stream: devicesManagerBloc.streamDeviceByState, + builder: (context, devicesByStateSnapshot) { + if (devicesByStateSnapshot.data == null) { + devicesManagerBloc.getDevice(); + return const Center( + child: CircularProgressIndicator()); + } else { + return SharedPieChart( + deviceByState: devicesByStateSnapshot.data ?? {}); + } + }, + ), + SizedBox(height: context.mediumValue), ], ), ); diff --git a/lib/product/constant/app/app_constants.dart b/lib/product/constant/app/app_constants.dart index 94eab82..d02ee77 100644 --- a/lib/product/constant/app/app_constants.dart +++ b/lib/product/constant/app/app_constants.dart @@ -3,6 +3,11 @@ class ApplicationConstants { static const APP_NAME = "Smatec SFM"; static const DOMAIN = "sfm.smatec.com.vn"; + static const OFFLINE_STATE = "offline"; + static const NORMAL_STATE = "normal"; + static const WARNING_STATE = "warning"; + static const INPROGRESS_STATE = "inprogress"; + static const ERROR_STATE = "error"; static const MAP_KEY = "AIzaSyDI8b-PUgKUgj5rHdtgEHCwWjUXYJrqYhE"; static const LOGIN_PATH = "/login"; static const LOGOUT_PATH = "/logout"; diff --git a/lib/product/lang/l10n/app_vi.arb b/lib/product/lang/l10n/app_vi.arb index cf179c8..a97bcf3 100644 --- a/lib/product/lang/l10n/app_vi.arb +++ b/lib/product/lang/l10n/app_vi.arb @@ -21,7 +21,7 @@ "let_PCCC_handle_message": "Hãy để Đội PCCC xử lý!", "overview_message": "Tổng quan", "total_nof_devices_message": "Tổng số", - "active_devices_message": "Đang hoạt động", + "active_devices_message": "Bình thường", "inactive_devices_message": "Đang tắt", "warning_devices_message": "Cảnh báo", "unused_devices_message": "Không sử dụng", diff --git a/lib/product/shared/shared_pie_chart.dart b/lib/product/shared/shared_pie_chart.dart new file mode 100644 index 0000000..4b5c2b0 --- /dev/null +++ b/lib/product/shared/shared_pie_chart.dart @@ -0,0 +1,180 @@ +import 'package:fl_chart/fl_chart.dart'; +import 'package:flutter/material.dart'; +import 'package:sfm_app/feature/devices/device_model.dart'; +import 'package:sfm_app/product/extention/context_extention.dart'; +import 'package:sfm_app/product/services/language_services.dart'; + +import '../constant/app/app_constants.dart'; + +class SharedPieChart extends StatelessWidget { + const SharedPieChart({ + super.key, + required this.deviceByState, + }); + + final Map> deviceByState; + + @override + Widget build(BuildContext context) { + TextStyle titleStyle = const TextStyle( + color: Colors.white, + fontSize: 20, + fontWeight: FontWeight.bold, + ); + int offlineCount = + deviceByState[ApplicationConstants.OFFLINE_STATE]?.length ?? 0; + int normalCount = + deviceByState[ApplicationConstants.NORMAL_STATE]?.length ?? 0; + int warningCount = + deviceByState[ApplicationConstants.WARNING_STATE]?.length ?? 0; + int inProgressCount = + deviceByState[ApplicationConstants.INPROGRESS_STATE]?.length ?? 0; + int errorCount = + deviceByState[ApplicationConstants.ERROR_STATE]?.length ?? 0; + return Padding( + padding: context.paddingLowHorizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Container( + width: context.dynamicWidth(0.5), + height: context.dynamicHeight(0.3), + padding: const EdgeInsets.all(10), + child: PieChart( + PieChartData( + sections: [ + PieChartSectionData( + color: Colors.grey, + value: offlineCount.toDouble(), + title: offlineCount.toString(), + radius: context.dynamicWidth(0.3), + titleStyle: titleStyle, + ), + PieChartSectionData( + color: Colors.green, + value: normalCount.toDouble(), + title: normalCount.toString(), + radius: context.dynamicWidth(0.3), + titleStyle: titleStyle, + ), + PieChartSectionData( + color: Colors.red, + value: warningCount.toDouble(), + title: warningCount.toString(), + radius: context.dynamicWidth(0.3), + titleStyle: titleStyle, + ), + PieChartSectionData( + color: Colors.yellow, + value: inProgressCount.toDouble(), + title: inProgressCount.toString(), + radius: context.dynamicWidth(0.3), + 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), + titleStyle: titleStyle, + ), + ], + centerSpaceRadius: 0, + sectionsSpace: 1, + ), + ), + ), + Column( + mainAxisAlignment: MainAxisAlignment.end, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Indicator( + color: Colors.grey, + text: appLocalization(context).inactive_devices_message, + isSquare: true, + ), + SizedBox( + height: context.lowValue, + ), + Indicator( + color: Colors.green, + text: appLocalization(context).active_devices_message, + isSquare: true, + ), + SizedBox( + height: context.lowValue, + ), + Indicator( + color: Colors.red, + text: appLocalization(context).warning_devices_message, + isSquare: true, + ), + SizedBox( + height: context.lowValue, + ), + Indicator( + color: Colors.yellow, + text: appLocalization(context).in_progress_message, + isSquare: true, + ), + SizedBox( + height: context.lowValue, + ), + Indicator( + color: Colors.black, + text: appLocalization(context).error_message_uppercase, + isSquare: true, + ), + // const SizedBox( + // height: 18, + // ), + ], + ), + ], + ), + ); + } +} + +class Indicator extends StatelessWidget { + const Indicator({ + super.key, + required this.color, + required this.text, + required this.isSquare, + this.size = 16, + this.textColor, + }); + final Color color; + final String text; + final bool isSquare; + final double size; + final Color? textColor; + + @override + Widget build(BuildContext context) { + return Row( + children: [ + Container( + width: size, + height: size, + decoration: BoxDecoration( + shape: isSquare ? BoxShape.rectangle : BoxShape.circle, + color: color, + ), + ), + SizedBox( + width: context.lowValue, + ), + Text( + text, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: textColor, + ), + ) + ], + ); + } +} diff --git a/lib/product/utils/device_utils.dart b/lib/product/utils/device_utils.dart index 9343304..fb31386 100644 --- a/lib/product/utils/device_utils.dart +++ b/lib/product/utils/device_utils.dart @@ -148,8 +148,12 @@ class DeviceUtils { return Colors.red; } else if (state == 0) { return Colors.green; - } else { + } else if (state == 2) { + return Colors.orange; + } else if (state == -1) { return Colors.grey; + } else { + return Colors.black87; } }