diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index c8e16ef..db85996 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,4 +1,5 @@ + diff --git a/assets/map_themes/maps_dark_theme.json b/assets/map_themes/maps_dark_theme.json new file mode 100644 index 0000000..c97ec13 --- /dev/null +++ b/assets/map_themes/maps_dark_theme.json @@ -0,0 +1,178 @@ +[ + { + "elementType": "geometry", + "stylers": [ + { + "color": "#212121" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#212121" + } + ] + }, + { + "featureType": "administrative", + "elementType": "geometry", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "administrative.country", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#9e9e9e" + } + ] + }, + { + "featureType": "administrative.land_parcel", + "stylers": [ + { + "visibility": "off" + } + ] + }, + { + "featureType": "administrative.locality", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#bdbdbd" + } + ] + }, + { + "featureType": "poi", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "geometry", + "stylers": [ + { + "color": "#181818" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#616161" + } + ] + }, + { + "featureType": "poi.park", + "elementType": "labels.text.stroke", + "stylers": [ + { + "color": "#1b1b1b" + } + ] + }, + { + "featureType": "road", + "elementType": "geometry.fill", + "stylers": [ + { + "color": "#2c2c2c" + } + ] + }, + { + "featureType": "road", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#8a8a8a" + } + ] + }, + { + "featureType": "road.arterial", + "elementType": "geometry", + "stylers": [ + { + "color": "#373737" + } + ] + }, + { + "featureType": "road.highway", + "elementType": "geometry", + "stylers": [ + { + "color": "#3c3c3c" + } + ] + }, + { + "featureType": "road.highway.controlled_access", + "elementType": "geometry", + "stylers": [ + { + "color": "#4e4e4e" + } + ] + }, + { + "featureType": "road.local", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#616161" + } + ] + }, + { + "featureType": "transit", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#757575" + } + ] + }, + { + "featureType": "water", + "elementType": "geometry", + "stylers": [ + { + "color": "#000000" + } + ] + }, + { + "featureType": "water", + "elementType": "labels.text.fill", + "stylers": [ + { + "color": "#3d3d3d" + } + ] + } +] \ No newline at end of file diff --git a/assets/map_themes/maps_light_theme.json b/assets/map_themes/maps_light_theme.json new file mode 100644 index 0000000..bcc349d --- /dev/null +++ b/assets/map_themes/maps_light_theme.json @@ -0,0 +1,65 @@ +[ + { + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "geometry", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "geometry.fill", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "geometry.stroke", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "labels.icon", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "labels.text", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "labels.text.fill", + "stylers": [ + { + "visibility": "on" + } + ] + }, + { + "elementType": "labels.text.stroke", + "stylers": [ + { + "visibility": "on" + } + ] + } +] \ No newline at end of file diff --git a/lib/feature/auth/login/screen/login_screen.dart b/lib/feature/auth/login/screen/login_screen.dart index 1de4387..a153e93 100644 --- a/lib/feature/auth/login/screen/login_screen.dart +++ b/lib/feature/auth/login/screen/login_screen.dart @@ -209,12 +209,6 @@ class _LoginScreenState extends State { log("Role: ${LocaleManager.instance.getStringValue(PreferencesKeys.ROLE)}"); log("Theme: ${LocaleManager.instance.getStringValue(PreferencesKeys.THEME)}"); log("Lang: ${LocaleManager.instance.getStringValue(PreferencesKeys.LANGUAGE_CODE)}"); - // log("Theme: $theme"); - // if (theme == AppThemes.DARK.name) { - // themeNotifier.changeValue(AppThemes.DARK); - // } else { - // themeNotifier.changeValue(AppThemes.LIGHT); - // } int timeNow = DateTime.now().millisecondsSinceEpoch ~/ 1000; if (token != "" && (exp - timeNow) > 7200) { context.goNamed(AppRoutes.HOME.name); diff --git a/lib/feature/device_log/device_logs_screen.dart b/lib/feature/device_log/device_logs_screen.dart index c0eca4f..d40adf4 100644 --- a/lib/feature/device_log/device_logs_screen.dart +++ b/lib/feature/device_log/device_logs_screen.dart @@ -3,6 +3,7 @@ import 'dart:developer'; import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import 'package:sfm_app/feature/device_log/widgets/tag_widget.dart'; import 'package:sfm_app/feature/devices/device_model.dart'; import 'package:sfm_app/feature/device_log/device_logs_bloc.dart'; import 'package:sfm_app/product/constant/icon/icon_constants.dart'; @@ -179,6 +180,8 @@ class _DeviceLogsScreenState extends State { style: const ButtonStyle( backgroundColor: MaterialStatePropertyAll( Colors.green), + foregroundColor: MaterialStatePropertyAll( + Colors.white), ), onPressed: () { if (fromDateApi.isEmpty) { @@ -196,8 +199,8 @@ class _DeviceLogsScreenState extends State { dateTime!, sensors); } - log("ThingID: $thingID"); - log("From Date: ${DateTimeUtils.instance.formatDateTimeToString(dateTime!)}"); + // log("ThingID: $thingID"); + // log("From Date: ${DateTimeUtils.instance.formatDateTimeToString(dateTime!)}"); }, icon: IconConstants.instance .getMaterialIcon(Icons.search), @@ -275,8 +278,7 @@ class _DeviceLogsScreenState extends State { ListTile( subtitle: Text(DeviceUtils.instance.getDeviceSensorsLog(context, sensor)), - // leading: const Icon(Icons.sensors_outlined), - leading: Text(index.toString()), + leading: leadingList(sensor), title: Text( DateTimeUtils.instance .convertCurrentMillisToDateTimeString(sensor.time ?? 0), @@ -299,4 +301,23 @@ class _DeviceLogsScreenState extends State { deviceLogsBloc.sinkHasMore.add(hasMore); deviceLogsBloc.getDeviceLogByThingID(offset, thingID, dateTime!, sensors); } + + Widget leadingList(SensorLogs sensor) { + Color boxColor; + String content; + if (sensor.name == "1" || sensor.name == "11") { + boxColor = Colors.blue; + content = "Điều khiển"; + } else { + boxColor = Colors.lightGreen; + content = "Sự kiện"; + } + + return TagWidgetShared( + boxColor: boxColor, + tagContent: content, + boxHeight: context.mediumValue, + boxWidth: context.dynamicWidth(0.2), + ); + } } diff --git a/lib/feature/device_log/widgets/tag_widget.dart b/lib/feature/device_log/widgets/tag_widget.dart new file mode 100644 index 0000000..791f350 --- /dev/null +++ b/lib/feature/device_log/widgets/tag_widget.dart @@ -0,0 +1,38 @@ +import 'package:flutter/material.dart'; + +class TagWidgetShared extends StatelessWidget { + const TagWidgetShared({ + super.key, + required this.boxColor, + this.boxRadius, + this.boxHeight, + this.boxWidth, + required this.tagContent, + }); + final Color? boxColor; + final double? boxRadius; + final double? boxHeight; + final double? boxWidth; + final String? tagContent; + @override + Widget build(BuildContext context) { + return Container( + decoration: BoxDecoration( + color: boxColor, + borderRadius: BorderRadius.circular(boxRadius ?? 10), + ), + height: boxHeight ?? 40, + width: boxWidth ?? 100, + child: Center( + child: Text( + tagContent ?? "", + style: const TextStyle( + color: Colors.white, + fontWeight: FontWeight.bold, + fontSize: 15, + ), + ), + ), + ); + } +} diff --git a/lib/feature/map/map_bloc.dart b/lib/feature/map/map_bloc.dart index aa4a75f..ab41c5c 100644 --- a/lib/feature/map/map_bloc.dart +++ b/lib/feature/map/map_bloc.dart @@ -16,6 +16,10 @@ class MapBloc extends BlocBase { @override void dispose() {} + final mapTheme = StreamController.broadcast(); + StreamSink get sinkMapTheme => mapTheme.sink; + Stream get streamMapTheme => mapTheme.stream; + final mapType = StreamController.broadcast(); StreamSink get sinkMapType => mapType.sink; Stream get streamMapType => mapType.stream; diff --git a/lib/feature/map/map_screen.dart b/lib/feature/map/map_screen.dart index 08566d8..4f7828d 100644 --- a/lib/feature/map/map_screen.dart +++ b/lib/feature/map/map_screen.dart @@ -14,6 +14,8 @@ import 'package:sfm_app/product/constant/icon/icon_constants.dart'; import 'package:sfm_app/product/permission/location_permission.dart'; import 'package:sfm_app/product/services/api_services.dart'; +import '../../product/constant/enums/app_theme_enums.dart'; + class MapScreen extends StatefulWidget { const MapScreen({super.key}); @@ -51,13 +53,20 @@ class _MapScreenState extends State with WidgetsBindingObserver { LatLng myLocation = const LatLng(213761, 123123); Position? position; bool isAllowLocationPermission = false; + String themeMode = ""; @override void initState() { super.initState(); mapBloc = BlocProvider.of(context); + checkTheme(); _loadIcons(); getAllMarkers(); clusterManager = _initClusterManager(); + DefaultAssetBundle.of(context) + .loadString("assets/map_themes/maps_dark_theme.json") + .then( + (value) => {themeMode = value}, + ); } @override @@ -78,36 +87,41 @@ class _MapScreenState extends State with WidgetsBindingObserver { return StreamBuilder>( stream: mapBloc.streamPolylines, builder: (context, polylinesSnapshot) { - return GoogleMap( - initialCameraPosition: _myPosition, - mapType: mapType, - onMapCreated: (GoogleMapController controller) { - if (!_controller.isCompleted) { - _controller.complete(controller); - } - streamController.sink.add(controller); - clusterManager.setMapId(controller.mapId); - }, - markers: markerSnapshot.data ?? markersAll - ..addAll(markers), - zoomControlsEnabled: true, - myLocationEnabled: true, - mapToolbarEnabled: false, - onCameraMove: (position) { - clusterManager.onCameraMove(position); - }, - onCameraIdle: () { - clusterManager.updateMap(); - }, - polylines: { - Polyline( - polylineId: const PolylineId('router'), - points: polylinesSnapshot.data ?? [], - color: Colors.deepPurpleAccent, - width: 8, - ), - }, - ); + return StreamBuilder( + stream: mapBloc.streamMapTheme, + builder: (context, mapThemeSnapshot) { + return GoogleMap( + initialCameraPosition: _myPosition, + mapType: mapType, + onMapCreated: (GoogleMapController controller) { + controller.setMapStyle(themeMode); + if (!_controller.isCompleted) { + _controller.complete(controller); + } + streamController.sink.add(controller); + clusterManager.setMapId(controller.mapId); + }, + markers: markerSnapshot.data ?? markersAll + ..addAll(markers), + zoomControlsEnabled: true, + myLocationEnabled: true, + mapToolbarEnabled: false, + onCameraMove: (position) { + clusterManager.onCameraMove(position); + }, + onCameraIdle: () { + clusterManager.updateMap(); + }, + polylines: { + Polyline( + polylineId: const PolylineId('router'), + points: polylinesSnapshot.data ?? [], + color: Colors.deepPurpleAccent, + width: 8, + ), + }, + ); + }); }, ); }, @@ -117,6 +131,22 @@ class _MapScreenState extends State with WidgetsBindingObserver { ); } + void checkTheme() async { + String theme = await apiServices.checkTheme(); + if (theme == AppThemes.LIGHT.name) { + getThemeMode('assets/map_themes/maps_light_theme.json'); + } else { + getThemeMode('assets/map_themes/maps_dark_theme.json'); + } + } + + void getThemeMode(String path) { + DefaultAssetBundle.of(context) + .loadString(path) + .then((value) => {themeMode = value}); + mapBloc.sinkMapTheme.add(themeMode); + } + Future _loadIcons() async { List> iconFutures = imageAssets.map((asset) { return BitmapDescriptor.fromAssetImage(const ImageConfiguration(), asset); diff --git a/lib/product/utils/date_time_utils.dart b/lib/product/utils/date_time_utils.dart index 1a6bb44..063eedc 100644 --- a/lib/product/utils/date_time_utils.dart +++ b/lib/product/utils/date_time_utils.dart @@ -11,6 +11,6 @@ class DateTimeUtils { String convertCurrentMillisToDateTimeString(int time) { DateTime dateTime = DateTime.fromMillisecondsSinceEpoch((time) * 1000); - return DateFormat('yyyy-MM-dd HH:mm:ss').format(dateTime); + return DateFormat('HH:mm:ss dd-MM-yyyy').format(dateTime); } } diff --git a/pubspec.lock b/pubspec.lock index d7b26b1..8e528ae 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -517,14 +517,6 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" - nested: - dependency: transitive - description: - name: nested - sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20" - url: "https://pub.dev" - source: hosted - version: "1.0.0" path: dependency: transitive description: @@ -629,14 +621,6 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" - provider: - dependency: "direct main" - description: - name: provider - sha256: c8a055ee5ce3fd98d6fc872478b03823ffdb448699c6ebdbbc71d59b596fd48c - url: "https://pub.dev" - source: hosted - version: "6.1.2" qr: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 87e81a8..323101a 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -42,7 +42,6 @@ dependencies: flutter_local_notifications: ^17.2.4 permission_handler: ^11.0.1 app_settings: ^5.1.1 - provider: ^6.1.2 flex_color_scheme: ^7.2.0 go_router: ^13.1.0 http: ^1.1.0 @@ -88,6 +87,7 @@ flutter: assets: - assets/images/ - assets/icons/ + - assets/map_themes/ # An image asset can refer to one or more resolution-specific "variants", see # https://flutter.dev/assets-and-images/#resolution-aware