import 'dart:async'; import 'dart:convert'; import 'dart:developer'; import 'package:flutter/services.dart'; import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:google_maps_cluster_manager_2/google_maps_cluster_manager_2.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart' hide ClusterManager, Cluster; import 'package:sfm_app/feature/devices/device_model.dart'; import 'package:sfm_app/bloc/map_bloc.dart'; import 'package:sfm_app/feature/map/widget/on_tap_marker_widget.dart'; import 'package:sfm_app/product/base/bloc/base_bloc.dart'; 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 'package:sfm_app/product/utils/permission_handler.dart'; import '../../product/constant/enums/app_theme_enums.dart'; class MapScreen extends StatefulWidget { const MapScreen({super.key}); @override State createState() => _MapScreenState(); } class _MapScreenState extends State with WidgetsBindingObserver { late BitmapDescriptor normalIcon; late BitmapDescriptor offlineIcon; late BitmapDescriptor abnormalIcon; late BitmapDescriptor flameIcon; late BitmapDescriptor hospitalIcon; late BitmapDescriptor fireStationIcon; late MapBloc mapBloc; late ClusterManager clusterManager; // MapType mapType = MapType.terrain; APIServices apiServices = APIServices(); final streamController = StreamController.broadcast(); List devices = []; Completer _controller = Completer(); List imageAssets = [ IconConstants.instance.getIcon("normal_icon"), IconConstants.instance.getIcon("offline_icon"), IconConstants.instance.getIcon("flame_icon"), IconConstants.instance.getIcon("hospital_marker"), IconConstants.instance.getIcon("fire_station_marker"), ]; static const CameraPosition _myPosition = CameraPosition( target: LatLng(20.976108, 105.791666), zoom: 12, ); Set markersAll = {}; List markers = []; // LatLng myLocation = const LatLng(213761, 123123); // Position? position; // bool isAllowLocationPermission = false; Timer? checkThemeTimer; Timer? getMarker; String themeMode = ''; @override void initState() { super.initState(); mapBloc = BlocProvider.of(context); _loadIcons(); getAllMarkers(); clusterManager = _initClusterManager(); const duration = Duration(seconds: 2); checkThemeTimer = Timer.periodic(duration, (Timer t) => checkTheme()); const Duration markerDuration = Duration(seconds: 5); getMarker = Timer.periodic(markerDuration, (Timer t) => getAllMarkers()); } @override void dispose() { super.dispose(); checkThemeTimer?.cancel(); getMarker?.cancel(); _controller = Completer(); streamController.close(); } void onMapCreated(GoogleMapController controller) { _controller.complete(controller); streamController.add(controller); clusterManager.setMapId(controller.mapId); checkTheme(); } @override Widget build(BuildContext context) { return Scaffold( body: Stack( children: [ StreamBuilder>( stream: mapBloc.streamAllMarker, builder: (context, markerSnapshot) { return StreamBuilder>( stream: mapBloc.streamPolylines, builder: (context, polylinesSnapshot) { return StreamBuilder( stream: mapBloc.streamMapTheme, builder: (context, mapThemeSnapshot) { return GoogleMap( initialCameraPosition: _myPosition, // mapType: mapType, onMapCreated: onMapCreated, markers: markerSnapshot.data ?? markersAll ..addAll(markers), zoomControlsEnabled: true, myLocationEnabled: true, mapToolbarEnabled: false, onCameraMove: (position) { clusterManager.onCameraMove(position); }, onCameraIdle: () { clusterManager.updateMap(); }, polylines: { if (polylinesSnapshot.data != null && polylinesSnapshot.data!.isNotEmpty) ... [ Polyline( polylineId: const PolylineId('router'), points: polylinesSnapshot.data!, color: Colors.deepPurpleAccent, width: 8, ), ] }, style: mapThemeSnapshot.data, ); }, ); }, ); }, ), ], ), ); } void checkTheme() async { String theme = await apiServices.checkTheme(); if (theme == AppThemes.DARK.name) { themeMode = await _getFileData('assets/map_themes/maps_dark_theme.json'); } else { themeMode = ''; } mapBloc.sinkMapTheme.add(themeMode); } Future _getFileData(String path) async { return rootBundle.loadString(path); } Future _loadIcons() async { List> iconFutures = imageAssets.map((asset) { return BitmapDescriptor.asset( const ImageConfiguration(size: Size(50, 50)), asset); }).toList(); List icons = await Future.wait(iconFutures); normalIcon = icons[0]; offlineIcon = icons[1]; flameIcon = icons[2]; hospitalIcon = icons[3]; fireStationIcon = icons[4]; } ClusterManager _initClusterManager() { return ClusterManager( devices, _updateMarkers, markerBuilder: _getmarkerBuilder(), ); } Future Function(Cluster) _getmarkerBuilder() => (cluster) async { return Marker( markerId: MarkerId( cluster.getId(), ), position: cluster.location, onTap: () async { LocationPermission permission = await checkAndRequestPermission(); if (permission == LocationPermission.whileInUse || permission == LocationPermission.always) { Position position = await Geolocator.getCurrentPosition(); onTapMarker( // ignore: use_build_context_synchronously context, _controller, mapBloc, LatLng(position.latitude, position.longitude), cluster.items, imageAssets, markers, hospitalIcon, fireStationIcon, ); } else { // ignore: use_build_context_synchronously ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text("Cannot get your Location"))); } }, icon: getMarkerIcon(cluster), ); }; BitmapDescriptor getMarkerIcon(Cluster cluster) { if (cluster.items.length == 1) { Device item = cluster.items.first; if (item.state == 0) { return normalIcon; } else if (item.state == 1) { return flameIcon; } else { return offlineIcon; } } bool hasStateOne = false; bool hasOtherState = false; for (var item in cluster.items) { if (item.state == 1) { hasStateOne = true; break; } else if (item.state != 0) { hasOtherState = true; } } // log("Has state = 1: $hasStateOne, Has other state: $hasOtherState"); if (hasStateOne) { return flameIcon; // flameIcon } else if (hasOtherState) { return offlineIcon; // normalIcon } else { return normalIcon; // offlineIcon } } bool checkStateMarker(Cluster cluster) { bool hasStateOne = false; bool hasOtherState = false; for (var item in cluster.items) { if (item.state == 1) { hasStateOne = true; break; } else if (item.state != 0) { hasOtherState = true; } } if (hasStateOne) { return true; // flameIcon } else if (hasOtherState) { return false; // normalIcon } else { return true; // offlineIcon } } void _updateMarkers(Set marker) { log("Update Marker"); checkTheme(); markersAll = marker; mapBloc.sinkAllMarker.add(marker); } void getAllMarkers() async { String response = await apiServices.getOwnerDevices(); if (response != "") { final data = jsonDecode(response); List result = data['items']; if (result.isNotEmpty) { devices.clear(); final devicesList = Device.fromJsonDynamicList(result); for (var device in devicesList) { devices.add(device); } } else {} } } // Future checkLocationPermission(context) async { // bool check = await LocationPermissionRequest.instance // .checkLocationPermission(context); // return check; // } }