Я получаю List<LatLng>
назад от отслеженного маршрута и отображаю его на карте, используя его в параметре points:
. Поскольку я хочу иметь возможность нажать на него и вызвать нижний лист, чтобы очистить экран от ломаной линии, я использую плагин flutter_map_tappable_polyline
и TappablePolyline
вместо Polyline
. TappablePolyline
отображается правильно, но не может быть изменено. Я перепробовал все комбинации ордеров с параметрами слоя, и ничего не изменилось. Полилиния никогда не может быть изменена MarkerClusterGroupLayerOptions.
, поэтому я также реализовал плагин flutter_map_marker_cluster
и закомментировал MarkerLayerOptions
, передав вместо него параметр List<Markers> alertsMarkers
в MarkerClusterLayerOptions
markers:
. У меня сейчас две проблемы. 1ST: TappablePolyline
все еще не может быть изменено, но я предположил, так как я не вижу ни одного параметра polylines:
в MarkerClusterLayerOptions
, поэтому я не понимаю, как управлять им с помощью MarkerClusterLayerOptions
. 2ND: теперь List<Markers> alertsMarkers
, который обновляется с состоянием, больше не обновляет маркеры на экране.
Что мне не хватает для реализации ?? Большое спасибо. Это код для моего экрана:
class MapScreen extends StatefulWidget {
final User user;
MapScreen({Key key, this.user}) : super(key: key);
@override
_MapScreenState createState() => _MapScreenState();
}
class _MapScreenState extends State<MapScreen> {
final _scaffoldKey = GlobalKey<ScaffoldState>();
//alerts
List<Marker> alertsMarkers = [];
List<UserAlert> alerts;
String alertType;
// user location
LatLng userLocation;
// tracking
bool isTracking = false;
String routeName;
List<LatLng> route = [];
// route polyline
List<Polyline> routePolyline = [];
// String userName = widget.userName;
List<TaggedPolyline> tapPolyline = [];
MapController _mapController = MapController();
TextEditingController _textEditingController = TextEditingController();
@override
Widget build(BuildContext context) {
return MultiBlocListener(
listeners: [
BlocListener<LocationBloc, LocationState>(
listener: (BuildContext context, LocationState state) {
if (state is LocationStream) {
setState(() {
userLocation = (state).location;
// print(
// ' @@@@ MapBloc actual user location from stream is : $userLocation');
});
}
if (state is MapCenter) {
userLocation = (state).location;
// print(' @@@@ MapBloc initial center location is : $userLocation');
_mapController.move(userLocation, 16);
}
}),
BlocListener<TrackingBloc, TrackingState>(
// bloc: TrackingBloc(),
listener: (BuildContext context, TrackingState state) {
// userLocation = (state as LocationStream).location;
if (state is TrackedRoute) {
List<LatLng> route = (state).trackedRoute;
print('TrackingListener route is : $route');
// BlocProvider.of<DirectionsBloc>(context).add(GetDirections(route));
// TODO Delete mockRoute and BlocProvider after it and re activate the BlocProvider above here once finished with route coding.
List<LatLng> mockRoute = [
LatLng(44.501224, 11.335855),
LatLng(44.500714, 11.335623),
LatLng(44.50054, 11.335546),
LatLng(44.500518, 11.335627),
LatLng(44.500492, 11.335626),
LatLng(44.500471, 11.335714),
LatLng(44.500469, 11.335778),
LatLng(44.500472, 11.335865),
LatLng(44.50047, 11.335934),
LatLng(44.500466, 11.336009),
LatLng(44.500459, 11.336087),
LatLng(44.500448, 11.336169),
LatLng(44.500429, 11.336243),
LatLng(44.500413, 11.336312),
LatLng(44.500373, 11.336488),
LatLng(44.500305, 11.336641),
LatLng(44.500207, 11.336797),
LatLng(44.500187, 11.336866),
LatLng(44.50071, 11.337171),
LatLng(44.50078, 11.337216),
LatLng(44.500824, 11.337253),
LatLng(44.500888, 11.33712)
];
BlocProvider.of<DirectionsBloc>(context)
.add(GetDirections(mockRoute));
}
}),
BlocListener<DirectionsBloc, DirectionsState>(
// bloc: TrackingBloc(),
listener: (BuildContext context, DirectionsState state) {
if (state is SnappedDirections) {
//adding polyline for tracked route.
setState(() {
route.clear();
// print('route at SnappedDirection count is : ${route.length}');
// print(
// 'DirectionsListener directionRoute from state count is: ${(state).directionsRoute.length}');
// print(
// 'DirectionsListener directionRoute from state is: ${(state).directionsRoute}');
route = (state).directionsRoute;
// print(
// 'route with locations form (state).directionsRoute count is : ${route.length}');
// print(
// 'route with locations form (state).directionsRoute is : $route');
//
// routePolyline.clear();
tapPolyline.clear();
// print(
// 'routePolyline resetted and count is now :${routePolyline.length}');
// routePolyline.add(
// new Polyline(
// points: route,
// strokeWidth: 4.0,
//// color: Colors.greenAccent,
// gradientColors: [
// Colors.greenAccent,
// Colors.lightGreenAccent,
// Colors.redAccent
// ],
// ),
// );
tapPolyline.add(TaggedPolyline(
tag: routeName,
points: route,
strokeWidth: 4.0,
// color: Colors.greenAccent,
gradientColors: [
Colors.greenAccent,
Colors.lightGreenAccent,
Colors.redAccent
],
));
// print(
// 'total polylines in routePolyline are: ${routePolyline.length}');
// print(
// 'first routePolyline points count after adding Polyline is: ${routePolyline[0].points.length}');
}); // setState
}
}),
BlocListener<AlertBloc, AlertState>(
listener: (BuildContext context, AlertState state) {
if (state is AlertLoaded) {
setState(() {
// alerts = (state).alerts;
alertsMarkers = (state).alerts;
// print(
// '######### AlertListener userAlerts are: ${alerts.toString()} and total alerts are : ${alerts.length}');
});
}
}),
],
child: Scaffold(
key: _scaffoldKey,
drawerScrimColor: Colors.red[50],
drawerEdgeDragWidth: 20,
// endDrawer: ,
drawer: Drawer(
// semanticLabel: ,
child: Center(
child: Container(
width: 400,
color: Colors.redAccent,
child: Column(
children: <Widget>[
SizedBox(height: 50),
CircleAvatar(
radius: 70,
// minRadius: 60,
// maxRadius: 100,
backgroundImage: NetworkImage('${widget.user.photoUrl}'),
),
Text(
'${widget.user.name}',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w300,
fontSize: 35),
),
Text(
'${widget.user.email}',
style: TextStyle(
color: Colors.white,
fontWeight: FontWeight.w200,
fontSize: 15),
),
SizedBox(height: 15),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Scheduler button.png'),
tooltip: 'Route check scheduler',
color: Colors.white,
iconSize: 55,
onPressed: () {
// BlocProvider.of<AuthenticationBloc>(context).add(
// LoggedOut(),
// );
},
),
SizedBox(width: 20),
Text(
'Check scheduler',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Shop button.png'),
color: Colors.white,
tooltip: 'Bike shop',
iconSize: 55,
onPressed: () {
// BlocProvider.of<AuthenticationBloc>(context).add(
// LoggedOut(),
// );
},
),
SizedBox(width: 20),
Text(
'Bike shop',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Orders button.png'),
tooltip: 'My orders',
color: Colors.orange,
iconSize: 55,
onPressed: () {
// BlocProvider.of<AuthenticationBloc>(context).add(
// LoggedOut(),
// );
},
),
SizedBox(width: 20),
Text(
'My orders',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Bookings button.png'),
tooltip: 'My bookings',
color: Colors.orange,
iconSize: 55,
onPressed: () {
// BlocProvider.of<AuthenticationBloc>(context).add(
// LoggedOut(),
// );
},
),
SizedBox(width: 20),
Text(
'My bookings',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Routes button.png'),
tooltip: 'My routes',
iconSize: 55,
color: Colors.redAccent,
onPressed: () {
// BlocProvider.of<AuthenticationBloc>(context).add(
// LoggedOut(),
// );
},
),
SizedBox(width: 20),
Text(
'My routes',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Settings button.png'),
tooltip: 'Settings',
color: Colors.white,
iconSize: 55,
onPressed: () {
// BlocProvider.of<AuthenticationBloc>(context).add(
// LoggedOut(),
// );
},
),
SizedBox(width: 20),
Text(
'Settings',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: <Widget>[
SizedBox(width: 20),
IconButton(
icon: Image.asset('assets/Logout button.png'),
tooltip: 'Logout',
color: Colors.white,
iconSize: 60,
onPressed: () {
BlocProvider.of<AuthenticationBloc>(context).add(
LoggedOut(),
);
},
),
SizedBox(width: 15),
Text(
'Logout',
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.w400),
)
],
),
],
),
),
),
),
appBar: AppBar(
backgroundColor: Colors.transparent,
elevation: 0,
// title: Text(
// 'Home',
// style: TextStyle(color: Colors.orangeAccent, fontSize: 40),
// ),
leading: IconButton(
icon: Icon(
Icons.menu,
color: Colors.redAccent,
size: 30,
),
onPressed: () {
//TODO: open drawer
// if (_scaffoldKey.currentState != null &&
// _scaffoldKey.currentState.isDrawerOpen == true)
// Navigator.pop(context);
_scaffoldKey.currentState?.openDrawer();
// Scaffold.of(context).openDrawer();
}),
),
backgroundColor: Colors.white,
body: SafeArea(
minimum: EdgeInsets.symmetric(horizontal: 20),
child: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
Container(
height: 600,
width: 350,
child: FlutterMap(
options: MapOptions(
center: userLocation,
minZoom: 5.0,
maxZoom: 19.0,
interactive: true,
plugins: [
MarkerClusterPlugin(),
TappablePolylineMapPlugin(),
],
),
mapController: _mapController,
layers: [
TileLayerOptions(
urlTemplate:
'https://api.openrouteservice.org/mapsurfer/{z}/{x}/{y}.png?api_key=5b3ce3597851110001cf62484c4b65d85bc844eca3a2c6b9f300ddf4',
// urlTemplate:
// 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
subdomains: ['a', 'b', 'c'],
keepBuffer: 20),
new MarkerLayerOptions(
markers: [
Marker(
anchorPos: AnchorPos.align(AnchorAlign.center),
point: userLocation,
height: 70,
width: 70,
builder: (context) => IconButton(
icon: Icon(Icons.directions_bike),
color: Colors.red,
iconSize: 50,
onPressed: () {
print('icon tapped');
},
),
),
],
),
new TappablePolylineLayerOptions(
polylines:
tapPolyline, //TODO polyline IS NOT tappable!!
// onTap: (TaggedPolyline polyline) => print(polyline.tag),
onTap: (BuildContext context) {
// print('${polyline.tag}');
final action = CupertinoActionSheet(
title: Text(
'Percorso $routeName',
style: TextStyle(
fontSize: 25,
color: Colors.black,
fontWeight: FontWeight.w400),
),
message: Text(
'Il percorso $routeName è stato salvato, scegli ok per non visuallizzarlo sullo schermo.',
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w400),
),
actions: <Widget>[
CupertinoActionSheetAction(
child: Text('Ok'),
isDefaultAction: true,
isDestructiveAction: false,
onPressed: () {
print("Action 1 is been clicked");
setState(() {
routeName = null;
tapPolyline.clear();
route.clear();
});
},
)
],
cancelButton: CupertinoActionSheetAction(
child: Text("Cancella"),
onPressed: () {
Navigator.pop(context);
},
),
);
showCupertinoModalPopup(
context: context, builder: (context) => action);
},
),
// new PolylineLayerOptions(
// polylines: routePolyline,
// ),
MarkerClusterLayerOptions(
maxClusterRadius: 120,
size: Size(40, 40),
fitBoundsOptions: FitBoundsOptions(
padding: EdgeInsets.all(50),
),
markers:
alertsMarkers, // TODO alertsMarkers DON'T update with new state!!
polygonOptions: PolygonOptions(
borderColor: Colors.blueAccent,
color: Colors.black12,
borderStrokeWidth: 3),
builder: (context, markers) {
return FloatingActionButton(
child: Text(markers.length.toString()),
onPressed: null,
);
},
),
// new MarkerLayerOptions(
// markers: alertsMarkers, //TODO alertsMakers DO update with new state.
// ),
],
),
),
// SizedBox(
// height: 10,
// ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: <Widget>[
CustomImageButton(
onPressed: () {
print('alertButton pressed');
//TODO call the card to display alert details
final action = CupertinoActionSheet(
title: Text(
'Nuovo cloud avviso',
style: TextStyle(fontSize: 25),
),
actions: <Widget>[
CupertinoActionSheetAction(
child: Text("Senza immagine"),
isDefaultAction: true,
onPressed: () async {
print("Action 1 is been clicked");
Navigator.pop(context);
String alertType = await Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
AlertSelectionScreen(),
),
);
if (alertType != null) {
BlocProvider.of<AlertBloc>(context).add(
AddAlert(
widget.user.name,
alertType,
userLocation.latitude.toString(),
userLocation.longitude.toString()));
}
}),
CupertinoActionSheetAction(
child: Text("Con immagine"),
isDestructiveAction: true,
onPressed: () {
print("Action 2 is been clicked");
Navigator.pop(context);
},
)
],
cancelButton: CupertinoActionSheetAction(
child: Text("Cancella"),
onPressed: () {
Navigator.pop(context);
},
),
);
showCupertinoModalPopup(
context: context, builder: (context) => action);
},
image: 'assets/New alert button.png',
height: 60,
width: 60,
// backgroundColor: Colors.redAccent,
// padding: EdgeInsets.all(5),
),
CustomImageButton(
onPressed: () {
print('New route button pressed');
// BlocProvider.of<TrackingBloc>(context)
// .add(StartStopTracking());
final action = CupertinoActionSheet(
title: Text(
'Nuovo percorso',
style: TextStyle(
fontSize: 25,
color: Colors.black,
fontWeight: FontWeight.w400),
),
message: Text(
'Inserisci un nome per il tuo nuovo percorso, e scegli Inizia tracking. Quando sarai arrivato a destinazione premi di nuovo il bottone Tracking e scegli Fine tracking.',
style: TextStyle(
fontSize: 18,
color: Colors.black,
fontWeight: FontWeight.w400),
),
actions: <Widget>[
CupertinoActionSheetAction(
child: CupertinoTextField(
controller: _textEditingController,
showCursor: true,
placeholder:
isTracking ? routeName : 'nome percorso',
),
isDefaultAction: false,
isDestructiveAction: false,
onPressed: () async {
print("Action 1 is been clicked");
}),
CupertinoActionSheetAction(
child: Text(isTracking
? "Fine tracking"
: 'Inizia tracking'),
isDefaultAction: true,
isDestructiveAction: false,
onPressed: () {
print("Action 2 is been clicked");
routeName = _textEditingController.text;
Navigator.pop(context);
isTracking = !isTracking;
BlocProvider.of<TrackingBloc>(context)
.add(StartStopTracking());
},
)
],
cancelButton: CupertinoActionSheetAction(
child: Text("Cancella"),
onPressed: () {
Navigator.pop(context);
},
),
);
showCupertinoModalPopup(
context: context, builder: (context) => action);
},
image: 'assets/New route button.png',
height: 60,
width: 60,
// backgroundColor: Colors.redAccent,
// padding: EdgeInsets.all(5),
),
CustomImageButton(
onPressed: () {
print('Center map button pressed');
_mapController.move(userLocation, 16);
},
image: 'assets/Center map button.png',
height: 60,
width: 60,
// backgroundColor: Colors.redAccent,
// padding: EdgeInsets.all(5),
// decoration: BoxDecoration(
// borderRadius: BorderRadius.circular(5),
// shape: BoxShape.rectangle),
),
],
),
],
),
),
),
),
// ),
);
}
}
```