gpt4 book ai didi

Flutter 自定义谷歌地图标记信息窗口

转载 作者:IT老高 更新时间:2023-10-28 12:34:26 27 4
gpt4 key购买 nike

我正在开发 Flutter 中的 Google Map Markers。

单击每个标记时,我想显示一个自定义信息窗口,其中可以包含按钮、图像等。但在 Flutter 中有一个属性 TextInfoWindow 只接受 String

如何实现在 map 标记的InfoWindow中添加按钮、图像。

最佳答案

偶然发现了这个问题并找到了适合我的解决方案:

为了解决这个问题,我写了一个 Custom Info Widget ,随意定制。例如,通过 ClipShadowPath 带有一些阴影.

实现

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:google_maps_flutter/google_maps_flutter.dart';

import 'custom_info_widget.dart';

void main() => runApp(MyApp());

class PointObject {
final Widget child;
final LatLng location;

PointObject({this.child, this.location});
}

class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
initialRoute: "/",
routes: {
"/": (context) => HomePage(),
},
);
}
}

class HomePage extends StatefulWidget {
@override
_HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
PointObject point = PointObject(
child: Text('Lorem Ipsum'),
location: LatLng(47.6, 8.8796),
);

StreamSubscription _mapIdleSubscription;
InfoWidgetRoute _infoWidgetRoute;
GoogleMapController _mapController;

@override
Widget build(BuildContext context) {
return Scaffold(
body: Container(
color: Colors.green,
child: GoogleMap(
initialCameraPosition: CameraPosition(
target: const LatLng(47.6, 8.6796),
zoom: 10,
),
circles: Set<Circle>()
..add(Circle(
circleId: CircleId('hi2'),
center: LatLng(47.6, 8.8796),
radius: 50,
strokeWidth: 10,
strokeColor: Colors.black,
)),
markers: Set<Marker>()
..add(Marker(
markerId: MarkerId(point.location.latitude.toString() +
point.location.longitude.toString()),
position: point.location,
onTap: () => _onTap(point),
)),
onMapCreated: (mapController) {
_mapController = mapController;
},

/// This fakes the onMapIdle, as the googleMaps on Map Idle does not always work
/// (see: https://github.com/flutter/flutter/issues/37682)
/// When the Map Idles and a _infoWidgetRoute exists, it gets displayed.
onCameraMove: (newPosition) {
_mapIdleSubscription?.cancel();
_mapIdleSubscription = Future.delayed(Duration(milliseconds: 150))
.asStream()
.listen((_) {
if (_infoWidgetRoute != null) {
Navigator.of(context, rootNavigator: true)
.push(_infoWidgetRoute)
.then<void>(
(newValue) {
_infoWidgetRoute = null;
},
);
}
});
},
),
),
);
}
/// now my _onTap Method. First it creates the Info Widget Route and then
/// animates the Camera twice:
/// First to a place near the marker, then to the marker.
/// This is done to ensure that onCameraMove is always called

_onTap(PointObject point) async {
final RenderBox renderBox = context.findRenderObject();
Rect _itemRect = renderBox.localToGlobal(Offset.zero) & renderBox.size;

_infoWidgetRoute = InfoWidgetRoute(
child: point.child,
buildContext: context,
textStyle: const TextStyle(
fontSize: 14,
color: Colors.black,
),
mapsWidgetSize: _itemRect,
);

await _mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
point.location.latitude - 0.0001,
point.location.longitude,
),
zoom: 15,
),
),
);
await _mapController.animateCamera(
CameraUpdate.newCameraPosition(
CameraPosition(
target: LatLng(
point.location.latitude,
point.location.longitude,
),
zoom: 15,
),
),
);
}
}

CustomInfoWidget:

import 'package:flutter/material.dart';
import 'package:flutter/painting.dart';
import 'package:meta/meta.dart';

class _InfoWidgetRouteLayout<T> extends SingleChildLayoutDelegate {
final Rect mapsWidgetSize;
final double width;
final double height;

_InfoWidgetRouteLayout(
{@required this.mapsWidgetSize,
@required this.height,
@required this.width});

/// Depending of the size of the marker or the widget, the offset in y direction has to be adjusted;
/// If the appear to be of different size, the commented code can be uncommented and
/// adjusted to get the right position of the Widget.
/// Or better: Adjust the marker size based on the device pixel ratio!!!!)

@override
Offset getPositionForChild(Size size, Size childSize) {
// if (Platform.isIOS) {
return Offset(
mapsWidgetSize.center.dx - childSize.width / 2,
mapsWidgetSize.center.dy - childSize.height - 50,
);
// } else {
// return Offset(
// mapsWidgetSize.center.dx - childSize.width / 2,
// mapsWidgetSize.center.dy - childSize.height - 10,
// );
// }
}

@override
BoxConstraints getConstraintsForChild(BoxConstraints constraints) {
//we expand the layout to our predefined sizes
return BoxConstraints.expand(width: width, height: height);
}

@override
bool shouldRelayout(_InfoWidgetRouteLayout oldDelegate) {
return mapsWidgetSize != oldDelegate.mapsWidgetSize;
}
}

class InfoWidgetRoute extends PopupRoute {
final Widget child;
final double width;
final double height;
final BuildContext buildContext;
final TextStyle textStyle;
final Rect mapsWidgetSize;

InfoWidgetRoute({
@required this.child,
@required this.buildContext,
@required this.textStyle,
@required this.mapsWidgetSize,
this.width = 150,
this.height = 50,
this.barrierLabel,
});

@override
Duration get transitionDuration => Duration(milliseconds: 100);

@override
bool get barrierDismissible => true;

@override
Color get barrierColor => null;

@override
final String barrierLabel;

@override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return MediaQuery.removePadding(
context: context,
removeBottom: true,
removeLeft: true,
removeRight: true,
removeTop: true,
child: Builder(builder: (BuildContext context) {
return CustomSingleChildLayout(
delegate: _InfoWidgetRouteLayout(
mapsWidgetSize: mapsWidgetSize, width: width, height: height),
child: InfoWidgetPopUp(
infoWidgetRoute: this,
),
);
}),
);
}
}

class InfoWidgetPopUp extends StatefulWidget {
const InfoWidgetPopUp({
Key key,
@required this.infoWidgetRoute,
}) : assert(infoWidgetRoute != null),
super(key: key);

final InfoWidgetRoute infoWidgetRoute;

@override
_InfoWidgetPopUpState createState() => _InfoWidgetPopUpState();
}

class _InfoWidgetPopUpState extends State<InfoWidgetPopUp> {
CurvedAnimation _fadeOpacity;

@override
void initState() {
super.initState();
_fadeOpacity = CurvedAnimation(
parent: widget.infoWidgetRoute.animation,
curve: Curves.easeIn,
reverseCurve: Curves.easeOut,
);
}

@override
Widget build(BuildContext context) {
return FadeTransition(
opacity: _fadeOpacity,
child: Material(
type: MaterialType.transparency,
textStyle: widget.infoWidgetRoute.textStyle,
child: ClipPath(
clipper: _InfoWidgetClipper(),
child: Container(
color: Colors.white,
padding: EdgeInsets.only(bottom: 10),
child: Center(child: widget.infoWidgetRoute.child),
),
),
),
);
}
}

class _InfoWidgetClipper extends CustomClipper<Path> {
@override
Path getClip(Size size) {
Path path = Path();
path.lineTo(0.0, size.height - 20);
path.quadraticBezierTo(0.0, size.height - 10, 10.0, size.height - 10);
path.lineTo(size.width / 2 - 10, size.height - 10);
path.lineTo(size.width / 2, size.height);
path.lineTo(size.width / 2 + 10, size.height - 10);
path.lineTo(size.width - 10, size.height - 10);
path.quadraticBezierTo(
size.width, size.height - 10, size.width, size.height - 20);
path.lineTo(size.width, 10.0);
path.quadraticBezierTo(size.width, 0.0, size.width - 10.0, 0.0);
path.lineTo(10, 0.0);
path.quadraticBezierTo(0.0, 0.0, 0.0, 10);
path.close();
return path;
}

@override
bool shouldReclip(CustomClipper<Path> oldClipper) => false;
}

关于Flutter 自定义谷歌地图标记信息窗口,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54104178/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com