Complete refactoring SFM App Source Code
This commit is contained in:
74
lib/feature/map/map_bloc.dart
Normal file
74
lib/feature/map/map_bloc.dart
Normal file
@@ -0,0 +1,74 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import '../../product/services/map_services.dart';
|
||||
import '../../product/shared/shared_snack_bar.dart';
|
||||
import '../devices/device_model.dart';
|
||||
import '../../product/base/bloc/base_bloc.dart';
|
||||
import '../../product/services/api_services.dart';
|
||||
|
||||
class MapBloc extends BlocBase {
|
||||
APIServices apiServices = APIServices();
|
||||
MapServices mapServices = MapServices();
|
||||
@override
|
||||
void dispose() {}
|
||||
|
||||
final mapType = StreamController<MapType>.broadcast();
|
||||
StreamSink<MapType> get sinkmapType => mapType.sink;
|
||||
Stream<MapType> get streammapType => mapType.stream;
|
||||
|
||||
final mapController = StreamController<GoogleMapController>.broadcast();
|
||||
StreamSink<GoogleMapController> get sinkmapController => mapController.sink;
|
||||
Stream<GoogleMapController> get streammapController => mapController.stream;
|
||||
|
||||
final allDevices = StreamController<List<Device>>.broadcast();
|
||||
StreamSink<List<Device>> get sinkAllDevices => allDevices.sink;
|
||||
Stream<List<Device>> get streamAllDevices => allDevices.stream;
|
||||
|
||||
final allMarker = StreamController<Set<Marker>>.broadcast();
|
||||
StreamSink<Set<Marker>> get sinkAllMarker => allMarker.sink;
|
||||
Stream<Set<Marker>> get streamAllMarker => allMarker.stream;
|
||||
|
||||
final polylines = StreamController<List<LatLng>>.broadcast();
|
||||
StreamSink<List<LatLng>> get sinkPolylines => polylines.sink;
|
||||
Stream<List<LatLng>> get streamPolylines => polylines.stream;
|
||||
|
||||
Future<void> updateCameraPosition(
|
||||
Completer<GoogleMapController> controller,
|
||||
double latitude,
|
||||
double longitude,
|
||||
double zoom,
|
||||
) async {
|
||||
final CameraPosition cameraPosition =
|
||||
CameraPosition(target: LatLng(latitude, longitude), zoom: zoom);
|
||||
final GoogleMapController mapController = await controller.future;
|
||||
mapController.animateCamera(CameraUpdate.newCameraPosition(cameraPosition));
|
||||
}
|
||||
|
||||
Future<void> findTheWay(
|
||||
BuildContext context,
|
||||
Completer<GoogleMapController> controller,
|
||||
LatLng origin,
|
||||
LatLng destination,
|
||||
) async {
|
||||
List<LatLng> polylines = [];
|
||||
final polylineCoordinates =
|
||||
await mapServices.findTheWay(origin, destination);
|
||||
if (polylineCoordinates.isNotEmpty) {
|
||||
polylines = polylineCoordinates;
|
||||
sinkPolylines.add(polylines);
|
||||
await updateCameraPosition(
|
||||
controller,
|
||||
destination.latitude,
|
||||
destination.longitude,
|
||||
13.0,
|
||||
);
|
||||
} else {
|
||||
showNoIconTopSnackBar(
|
||||
context, "Không tìm thấy đường", Colors.orange, Colors.white);
|
||||
}
|
||||
}
|
||||
}
|
||||
237
lib/feature/map/map_screen.dart
Normal file
237
lib/feature/map/map_screen.dart
Normal file
@@ -0,0 +1,237 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_cluster_manager/google_maps_cluster_manager.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:sfm_app/feature/devices/device_model.dart';
|
||||
import 'package:sfm_app/feature/map/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/services/api_services.dart';
|
||||
|
||||
class MapScreen extends StatefulWidget {
|
||||
const MapScreen({super.key});
|
||||
|
||||
@override
|
||||
State<MapScreen> createState() => _MapScreenState();
|
||||
}
|
||||
|
||||
class _MapScreenState extends State<MapScreen> {
|
||||
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<GoogleMapController>.broadcast();
|
||||
List<Device> devices = [];
|
||||
Completer<GoogleMapController> _controller = Completer();
|
||||
List<String> 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<Marker> markersAll = {};
|
||||
List<Marker> markers = [];
|
||||
LatLng myLocation = const LatLng(213761, 123123);
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
mapBloc = BlocProvider.of(context);
|
||||
_loadIcons();
|
||||
getAllMarkers();
|
||||
clusterManager = _initClusterManager();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
streamController.close();
|
||||
_controller = Completer();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
body: Stack(
|
||||
children: [
|
||||
StreamBuilder<Set<Marker>>(
|
||||
stream: mapBloc.streamAllMarker,
|
||||
builder: (context, markerSnapshot) {
|
||||
return StreamBuilder<List<LatLng>>(
|
||||
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,
|
||||
),
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _loadIcons() async {
|
||||
List<Future<BitmapDescriptor>> iconFutures = imageAssets.map((asset) {
|
||||
return BitmapDescriptor.fromAssetImage(const ImageConfiguration(), asset);
|
||||
}).toList();
|
||||
|
||||
List<BitmapDescriptor> icons = await Future.wait(iconFutures);
|
||||
|
||||
normalIcon = icons[0];
|
||||
offlineIcon = icons[1];
|
||||
flameIcon = icons[2];
|
||||
hospitalIcon = icons[3];
|
||||
fireStationIcon = icons[4];
|
||||
}
|
||||
|
||||
ClusterManager _initClusterManager() {
|
||||
return ClusterManager<Device>(
|
||||
devices,
|
||||
_updateMarkers,
|
||||
markerBuilder: _getmarkerBuilder(),
|
||||
);
|
||||
}
|
||||
|
||||
Future<Marker> Function(Cluster<Device>) _getmarkerBuilder() =>
|
||||
(cluster) async {
|
||||
return Marker(
|
||||
markerId: MarkerId(
|
||||
cluster.getId(),
|
||||
),
|
||||
position: cluster.location,
|
||||
onTap: () {
|
||||
onTapMarker(
|
||||
context,
|
||||
_controller,
|
||||
mapBloc,
|
||||
myLocation,
|
||||
cluster.items,
|
||||
imageAssets,
|
||||
markers,
|
||||
hospitalIcon,
|
||||
fireStationIcon);
|
||||
},
|
||||
icon: getMarkerIcon(cluster),
|
||||
);
|
||||
};
|
||||
|
||||
BitmapDescriptor getMarkerIcon(Cluster<Device> 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 normalIcon; // normalIcon
|
||||
} else {
|
||||
return offlineIcon; // offlineIcon
|
||||
}
|
||||
}
|
||||
|
||||
bool checkStateMarker(Cluster<Device> 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;
|
||||
}
|
||||
}
|
||||
|
||||
// log("Has state = 1: $hasStateOne, Has other state: $hasOtherState");
|
||||
|
||||
if (hasStateOne) {
|
||||
return true; // flameIcon
|
||||
} else if (hasOtherState) {
|
||||
return false; // normalIcon
|
||||
} else {
|
||||
return true; // offlineIcon
|
||||
}
|
||||
}
|
||||
|
||||
void _updateMarkers(Set<Marker> marker) {
|
||||
log("Update Marker");
|
||||
markersAll = marker;
|
||||
mapBloc.sinkAllMarker.add(marker);
|
||||
}
|
||||
|
||||
void getAllMarkers() async {
|
||||
String response = await apiServices.getOwnerDevices();
|
||||
if (response != "") {
|
||||
final data = jsonDecode(response);
|
||||
List<dynamic> result = data['items'];
|
||||
final devicesList = Device.fromJsonDynamicList(result);
|
||||
for (var device in devicesList) {
|
||||
devices.add(device);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
344
lib/feature/map/widget/on_tap_marker_widget.dart
Normal file
344
lib/feature/map/widget/on_tap_marker_widget.dart
Normal file
@@ -0,0 +1,344 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'show_direction_widget.dart';
|
||||
import 'show_nearest_place.dart';
|
||||
import '../../../product/constant/icon/icon_constants.dart';
|
||||
import '../../../product/extention/context_extention.dart';
|
||||
import '../map_bloc.dart';
|
||||
import '../../../product/services/api_services.dart';
|
||||
import '../../../product/services/language_services.dart';
|
||||
import '../../../product/utils/device_utils.dart';
|
||||
|
||||
import '../../devices/device_model.dart';
|
||||
|
||||
onTapMarker(
|
||||
BuildContext context,
|
||||
Completer<GoogleMapController> controller,
|
||||
MapBloc mapBloc,
|
||||
LatLng myLocation,
|
||||
Iterable<Device> devices,
|
||||
List<String> imageAssets,
|
||||
List<Marker> otherMarkers,
|
||||
BitmapDescriptor hospitalIcon,
|
||||
BitmapDescriptor fireStationIcon,
|
||||
) {
|
||||
LatLng testLocation = const LatLng(20.985453, 105.738381);
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (BuildContext modalBottomSheetContext) {
|
||||
if (devices.length == 1) {
|
||||
List<Device> devicesList = devices.toList();
|
||||
final device = devicesList[0];
|
||||
String deviceState = DeviceUtils.instance
|
||||
.checkStateDevice(modalBottomSheetContext, device.state ?? 3);
|
||||
String imgStringAsset;
|
||||
if (device.state == 0) {
|
||||
imgStringAsset = imageAssets[0];
|
||||
} else if (device.state == 1) {
|
||||
imgStringAsset = imageAssets[2];
|
||||
} else {
|
||||
imgStringAsset = imageAssets[1];
|
||||
}
|
||||
return Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
"${appLocalization(context).device_title}: ${device.name}",
|
||||
style: context.titleLargeTextStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: context.lowValue,
|
||||
),
|
||||
SingleChildScrollView(
|
||||
scrollDirection: Axis.horizontal,
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(modalBottomSheetContext);
|
||||
var destination = LatLng(
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
);
|
||||
mapBloc.findTheWay(
|
||||
context,
|
||||
controller,
|
||||
testLocation,
|
||||
destination,
|
||||
);
|
||||
String deviceLocations = await DeviceUtils.instance
|
||||
.getFullDeviceLocation(context, device.areaPath!);
|
||||
String yourLocation =
|
||||
appLocalization(context).map_your_location;
|
||||
showDirections(
|
||||
context,
|
||||
controller,
|
||||
otherMarkers,
|
||||
mapBloc,
|
||||
yourLocation,
|
||||
deviceLocations,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
);
|
||||
},
|
||||
style: const ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.blue),
|
||||
foregroundColor: MaterialStatePropertyAll(Colors.white),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
IconConstants.instance
|
||||
.getMaterialIcon(Icons.turn_right),
|
||||
Text(appLocalization(modalBottomSheetContext)
|
||||
.map_show_direction),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.pop(modalBottomSheetContext);
|
||||
showNearPlacesSideSheet(
|
||||
context,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
"Bệnh viện gần nhất",
|
||||
5000,
|
||||
"hospital",
|
||||
mapBloc,
|
||||
controller,
|
||||
otherMarkers,
|
||||
hospitalIcon,
|
||||
fireStationIcon,
|
||||
);
|
||||
},
|
||||
style: const ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.pink),
|
||||
foregroundColor: MaterialStatePropertyAll(Colors.white),
|
||||
),
|
||||
icon: IconConstants.instance
|
||||
.getMaterialIcon(Icons.local_hospital),
|
||||
label: Text(appLocalization(modalBottomSheetContext)
|
||||
.map_nearby_hospital),
|
||||
),
|
||||
SizedBox(
|
||||
width: context.lowValue,
|
||||
),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () {
|
||||
Navigator.pop(modalBottomSheetContext);
|
||||
showNearPlacesSideSheet(
|
||||
context,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
"đội pccc gần nhất",
|
||||
5000,
|
||||
"fire_station",
|
||||
mapBloc,
|
||||
controller,
|
||||
otherMarkers,
|
||||
hospitalIcon,
|
||||
fireStationIcon,
|
||||
);
|
||||
},
|
||||
style: const ButtonStyle(
|
||||
backgroundColor: MaterialStatePropertyAll(Colors.red),
|
||||
foregroundColor: MaterialStatePropertyAll(Colors.white),
|
||||
),
|
||||
icon: IconConstants.instance
|
||||
.getMaterialIcon(Icons.fire_truck_outlined),
|
||||
label: Text(appLocalization(modalBottomSheetContext)
|
||||
.map_nearby_firestation),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: context.lowValue),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
'${appLocalization(modalBottomSheetContext).paginated_data_table_column_deviceStatus}: '),
|
||||
SizedBox(
|
||||
height: 25,
|
||||
width: 25,
|
||||
child: CircleAvatar(
|
||||
minRadius: 20,
|
||||
maxRadius: 20,
|
||||
backgroundImage: AssetImage(imgStringAsset),
|
||||
),
|
||||
),
|
||||
SizedBox(width: context.lowValue),
|
||||
Text(
|
||||
deviceState,
|
||||
style: context.bodyMediumTextStyle,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
// Tiếp tục xử lý mảng devices
|
||||
} else if (devices.length > 1) {
|
||||
DeviceSource deviceSource = DeviceSource(
|
||||
DeviceUtils.instance.sortDeviceByState(devices.toList()),
|
||||
modalBottomSheetContext,
|
||||
controller,
|
||||
mapBloc,
|
||||
);
|
||||
// Devices là một phần tử
|
||||
return Padding(
|
||||
padding: context.paddingLow,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
appLocalization(modalBottomSheetContext)
|
||||
.paginated_data_table_title,
|
||||
style: context.titleLargeTextStyle,
|
||||
),
|
||||
),
|
||||
PaginatedDataTable(
|
||||
columns: [
|
||||
DataColumn(
|
||||
label: Center(
|
||||
child: Text(
|
||||
appLocalization(modalBottomSheetContext)
|
||||
.input_name_device_device,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Center(
|
||||
child: Text(
|
||||
appLocalization(modalBottomSheetContext)
|
||||
.paginated_data_table_column_deviceStatus,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Center(
|
||||
child: Text(
|
||||
appLocalization(modalBottomSheetContext)
|
||||
.paginated_data_table_column_deviceBaterry,
|
||||
),
|
||||
),
|
||||
),
|
||||
DataColumn(
|
||||
label: Center(
|
||||
child: Text(
|
||||
appLocalization(modalBottomSheetContext)
|
||||
.paginated_data_table_column_deviceSignal,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
source: deviceSource,
|
||||
rowsPerPage: 5,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return Text(appLocalization(modalBottomSheetContext).undefine_message);
|
||||
}
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
class DeviceSource extends DataTableSource {
|
||||
BuildContext context;
|
||||
MapBloc mapBloc;
|
||||
final List<Device> devices;
|
||||
final Completer<GoogleMapController> controller;
|
||||
DeviceSource(this.devices, this.context, this.controller, this.mapBloc);
|
||||
APIServices apiServices = APIServices();
|
||||
@override
|
||||
DataRow? getRow(int index) {
|
||||
if (index >= devices.length) {
|
||||
return null;
|
||||
}
|
||||
final device = devices[index];
|
||||
Map<String, dynamic> sensorMap = DeviceUtils.instance
|
||||
.getDeviceSensors(context, device.status?.sensors ?? []);
|
||||
String deviceState =
|
||||
DeviceUtils.instance.checkStateDevice(context, device.state!);
|
||||
return DataRow.byIndex(
|
||||
color: MaterialStatePropertyAll(
|
||||
DeviceUtils.instance.getTableRowColor(device.state ?? -1),
|
||||
),
|
||||
index: index,
|
||||
cells: [
|
||||
DataCell(
|
||||
Text(device.name!),
|
||||
onTap: () {
|
||||
mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
16,
|
||||
);
|
||||
Navigator.pop(context);
|
||||
},
|
||||
),
|
||||
DataCell(
|
||||
Text(deviceState),
|
||||
onTap: () {
|
||||
mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
16,
|
||||
);
|
||||
},
|
||||
),
|
||||
DataCell(
|
||||
Text(sensorMap['sensorBattery'] + "%"),
|
||||
onTap: () {
|
||||
mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
16,
|
||||
);
|
||||
},
|
||||
),
|
||||
DataCell(
|
||||
Text(sensorMap['sensorCsq']),
|
||||
onTap: () {
|
||||
mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
double.parse(device.settings!.latitude!),
|
||||
double.parse(device.settings!.longitude!),
|
||||
16,
|
||||
);
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isRowCountApproximate => false;
|
||||
@override
|
||||
int get rowCount => devices.length;
|
||||
@override
|
||||
int get selectedRowCount => 0;
|
||||
}
|
||||
124
lib/feature/map/widget/show_direction_widget.dart
Normal file
124
lib/feature/map/widget/show_direction_widget.dart
Normal file
@@ -0,0 +1,124 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import 'package:maps_launcher/maps_launcher.dart';
|
||||
import 'package:sfm_app/product/constant/icon/icon_constants.dart';
|
||||
import 'package:sfm_app/product/extention/context_extention.dart';
|
||||
import 'package:sfm_app/product/services/language_services.dart';
|
||||
|
||||
import '../map_bloc.dart';
|
||||
|
||||
showDirections(
|
||||
BuildContext context,
|
||||
Completer<GoogleMapController> controller,
|
||||
List<Marker> markers,
|
||||
MapBloc mapBloc,
|
||||
String originalName,
|
||||
String destinationLocation,
|
||||
double devicelat,
|
||||
double devicelng,
|
||||
) {
|
||||
TextEditingController originController =
|
||||
TextEditingController(text: originalName);
|
||||
TextEditingController destinationController =
|
||||
TextEditingController(text: destinationLocation);
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
backgroundColor: Colors.transparent,
|
||||
// dismissDirection: DismissDirection.none,
|
||||
duration: const Duration(minutes: 5),
|
||||
content: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
appLocalization(context).map_show_direction,
|
||||
style: context.titleLargeTextStyle,
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton.outlined(
|
||||
onPressed: () async {
|
||||
await mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
devicelat,
|
||||
devicelng,
|
||||
13.0,
|
||||
);
|
||||
List<LatLng> polylineCoordinates = [];
|
||||
mapBloc.sinkPolylines.add(polylineCoordinates);
|
||||
markers.clear();
|
||||
if (context.mounted) {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
}
|
||||
},
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.close),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'${appLocalization(context).map_start}: ',
|
||||
),
|
||||
SizedBox(width: context.lowValue),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: originController,
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(
|
||||
height: context.lowValue,
|
||||
),
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
'${appLocalization(context).map_destination}: ',
|
||||
),
|
||||
SizedBox(width: context.lowValue),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: destinationController,
|
||||
readOnly: true,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
SizedBox(height: context.lowValue),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () async {
|
||||
ScaffoldMessenger.of(context).hideCurrentSnackBar();
|
||||
List<LatLng> polylineCoordinates = [];
|
||||
mapBloc.sinkPolylines.add(polylineCoordinates);
|
||||
MapsLauncher.launchCoordinates(devicelat, devicelng);
|
||||
},
|
||||
icon: IconConstants.instance
|
||||
.getMaterialIcon(Icons.near_me_rounded),
|
||||
label: Text(
|
||||
appLocalization(context).map_stream,
|
||||
),
|
||||
style: ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStateProperty.all<Color>(Colors.blue[300]!),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
226
lib/feature/map/widget/show_nearest_place.dart
Normal file
226
lib/feature/map/widget/show_nearest_place.dart
Normal file
@@ -0,0 +1,226 @@
|
||||
// ignore_for_file: use_build_context_synchronously
|
||||
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||
import '../map_bloc.dart';
|
||||
import 'show_direction_widget.dart';
|
||||
import '../../../product/constant/icon/icon_constants.dart';
|
||||
import '../../../product/extention/context_extention.dart';
|
||||
import '../../../product/services/language_services.dart';
|
||||
import '../../../product/services/map_services.dart';
|
||||
import '../../../product/shared/model/near_by_search_model.dart';
|
||||
|
||||
showNearPlacesSideSheet(
|
||||
BuildContext context,
|
||||
double latitude,
|
||||
double longitude,
|
||||
String searchKey,
|
||||
int radius,
|
||||
String type,
|
||||
MapBloc mapBloc,
|
||||
Completer<GoogleMapController> controller,
|
||||
List<Marker> markers,
|
||||
BitmapDescriptor hospitalIcon,
|
||||
BitmapDescriptor fireStationIcon,
|
||||
) async {
|
||||
double screenWidth = MediaQuery.of(context).size.width;
|
||||
double screenHeight = MediaQuery.of(context).size.height;
|
||||
LatLng testLocation = LatLng(latitude, longitude);
|
||||
List<PlaceDetails> placeDetails = [];
|
||||
placeDetails =
|
||||
await findLocation(latitude, longitude, searchKey, radius, type);
|
||||
await mapBloc.updateCameraPosition(controller, latitude, longitude, 12.0);
|
||||
if (placeDetails.isNotEmpty) {
|
||||
for (int i = 0; i < placeDetails.length; i++) {
|
||||
Marker marker = Marker(
|
||||
markerId: MarkerId(placeDetails[i].result!.placeId!),
|
||||
position: LatLng(placeDetails[i].result!.geometry!.location!.lat!,
|
||||
placeDetails[i].result!.geometry!.location!.lng!),
|
||||
infoWindow: InfoWindow(title: placeDetails[i].result!.name!),
|
||||
icon:
|
||||
searchKey == "Bệnh viện gần nhất" ? hospitalIcon : fireStationIcon,
|
||||
);
|
||||
markers.add(marker);
|
||||
}
|
||||
}
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
builder: (modalBottomSheetContext) {
|
||||
return Container(
|
||||
padding: context.paddingLow,
|
||||
width: screenWidth,
|
||||
height: screenHeight / 3,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Center(
|
||||
child: Text(
|
||||
'${appLocalization(modalBottomSheetContext).map_result}: ',
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
alignment: Alignment.centerRight,
|
||||
child: IconButton(
|
||||
onPressed: () async {
|
||||
Navigator.pop(modalBottomSheetContext);
|
||||
markers.clear();
|
||||
await mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
latitude,
|
||||
longitude,
|
||||
14.0,
|
||||
);
|
||||
},
|
||||
icon: IconConstants.instance.getMaterialIcon(Icons.close),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (placeDetails.isNotEmpty)
|
||||
Expanded(
|
||||
child: ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: placeDetails.length,
|
||||
itemBuilder: (BuildContext listViewContext, int index) {
|
||||
final place = placeDetails[index];
|
||||
return GestureDetector(
|
||||
onTap: () async {
|
||||
await mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
place.result!.geometry!.location!.lat!,
|
||||
place.result!.geometry!.location!.lng!,
|
||||
17.0,
|
||||
);
|
||||
},
|
||||
child: Card(
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
child: Container(
|
||||
padding: listViewContext.paddingLow,
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(20.0),
|
||||
),
|
||||
width: screenWidth / 1.5,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
place.result!.name!,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
SizedBox(height: listViewContext.lowValue),
|
||||
Text(
|
||||
'${appLocalization(listViewContext).change_profile_address} ${place.result!.formattedAddress} '),
|
||||
SizedBox(height: listViewContext.lowValue),
|
||||
if (place.result?.openingHours?.openNow == null)
|
||||
Text(
|
||||
appLocalization(listViewContext)
|
||||
.map_always_opened,
|
||||
style: const TextStyle(
|
||||
color: Colors.green,
|
||||
),
|
||||
)
|
||||
else
|
||||
place.result?.openingHours?.openNow ?? false
|
||||
? Text(
|
||||
appLocalization(listViewContext)
|
||||
.map_openning,
|
||||
style: const TextStyle(
|
||||
color: Colors.green,
|
||||
),
|
||||
)
|
||||
: Text(
|
||||
appLocalization(listViewContext)
|
||||
.map_closed,
|
||||
style: const TextStyle(
|
||||
color: Colors.red,
|
||||
),
|
||||
),
|
||||
SizedBox(
|
||||
height: listViewContext.lowValue,
|
||||
),
|
||||
Center(
|
||||
child: ElevatedButton.icon(
|
||||
style: const ButtonStyle(
|
||||
backgroundColor:
|
||||
MaterialStatePropertyAll(Colors.blue),
|
||||
foregroundColor:
|
||||
MaterialStatePropertyAll(Colors.white),
|
||||
),
|
||||
onPressed: () async {
|
||||
Navigator.pop(modalBottomSheetContext);
|
||||
var destination = LatLng(
|
||||
place.result!.geometry!.location!.lat!,
|
||||
place.result!.geometry!.location!.lng!);
|
||||
mapBloc.findTheWay(context, controller,
|
||||
testLocation, destination);
|
||||
// findTheWay(myLocation, destination);
|
||||
await mapBloc.updateCameraPosition(
|
||||
controller,
|
||||
place.result!.geometry!.location!.lat!,
|
||||
place.result!.geometry!.location!.lng!,
|
||||
13.0,
|
||||
);
|
||||
showDirections(
|
||||
context,
|
||||
controller,
|
||||
markers,
|
||||
mapBloc,
|
||||
appLocalization(context)
|
||||
.map_your_location,
|
||||
place.result!.name!,
|
||||
latitude,
|
||||
longitude,
|
||||
);
|
||||
},
|
||||
icon: IconConstants.instance
|
||||
.getMaterialIcon(Icons.turn_right),
|
||||
label: Text(appLocalization(listViewContext)
|
||||
.map_show_direction),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
else
|
||||
Center(
|
||||
child: Text(
|
||||
appLocalization(modalBottomSheetContext).map_no_results,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Future<List<PlaceDetails>> findLocation(double latitude, double longitude,
|
||||
String searchKey, int radius, String type) async {
|
||||
MapServices mapServices = MapServices();
|
||||
|
||||
final nearByPlaces = await mapServices.getNearbyPlaces(
|
||||
latitude, longitude, searchKey, radius, type);
|
||||
|
||||
return nearByPlaces.isNotEmpty ? nearByPlaces : [];
|
||||
}
|
||||
Reference in New Issue
Block a user