Last active
June 29, 2022 12:43
-
-
Save hleinone/59fe6276fb56850e42c83e0c611f551f to your computer and use it in GitHub Desktop.
Google Maps for Flutter Customizable Buttons
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'dart:async'; | |
import 'dart:math'; | |
import 'package:flutter/material.dart'; | |
import 'package:flutter_svg/svg.dart'; | |
import 'package:google_maps_flutter/google_maps_flutter.dart'; | |
/// A button resembling the built-in 'compass' indicator from Google Maps SDK. | |
/// This supports both light and dark themes. Requires material, | |
/// google_maps_flutter and flutter_svg. | |
@immutable | |
class Compass extends StatelessWidget { | |
const Compass({ | |
Key? key, | |
Completer<GoogleMapController>? controller, | |
required this.cameraPosition, | |
this.onPressed, | |
this.tooltip, | |
}) : assert( | |
controller != null || onPressed != null, | |
'Either controller or onPressed must be provided', | |
), | |
_controller = controller, | |
super(key: key); | |
final Completer<GoogleMapController>? _controller; | |
final Stream<CameraPosition> cameraPosition; | |
final void Function()? onPressed; | |
final String? tooltip; | |
@override | |
Widget build(BuildContext context) { | |
return StreamBuilder( | |
stream: cameraPosition, | |
builder: (context, AsyncSnapshot<CameraPosition> cameraPosition) { | |
final data = cameraPosition.data; | |
final isStraight = data?.isStraight ?? true; | |
final tiltRadians = data?.tiltRadians ?? 0; | |
final bearingRadians = data?.bearingRadians ?? 0; | |
final button = AnimatedOpacity( | |
duration: isStraight | |
? const Duration(seconds: 2) | |
: kThemeChangeDuration, | |
opacity: data?.isStraight ?? true ? 0 : 1, | |
curve: const Interval(0.8, 1), | |
child: OutlinedButton( | |
onPressed: isStraight | |
? () {} | |
: onPressed ?? | |
() async { | |
final controller = await _controller!.future; | |
final center = data?.target ?? await () async { | |
final visibleRegion = await controller.getVisibleRegion(); | |
final northeastScreen = await controller | |
.getScreenCoordinate(visibleRegion.northeast); | |
final southwestScreen = await controller | |
.getScreenCoordinate(visibleRegion.southwest); | |
final centerScreen = ScreenCoordinate( | |
x: ((northeastScreen.x + southwestScreen.x) / 2) | |
.round(), | |
y: ((northeastScreen.y + southwestScreen.y) / 2) | |
.round(), | |
); | |
return await controller.getLatLng(centerScreen); | |
}(); | |
final zoom = data?.zoom ?? await controller.getZoomLevel(); | |
controller.animateCamera( | |
CameraUpdate.newCameraPosition( | |
CameraPosition( | |
tilt: 0, | |
bearing: 0, | |
target: center, | |
zoom: zoom, | |
), | |
), | |
); | |
}, | |
style: OutlinedButton.styleFrom( | |
elevation: 0, | |
backgroundColor: | |
Theme.of(context).colorScheme.surface.withAlpha(191), | |
padding: const EdgeInsets.all(0), | |
fixedSize: const Size(36, 36), | |
minimumSize: const Size(36, 36), | |
primary: Theme.of(context).colorScheme.onSurface.withAlpha(191), | |
shape: const CircleBorder(), | |
side: BorderSide( | |
color: Theme.of(context).colorScheme.onSurface.withAlpha(64), | |
width: 1, | |
), | |
), | |
child: Padding( | |
padding: const EdgeInsets.all(6), | |
child: Transform( | |
transform: Matrix4.rotationX(tiltRadians), | |
alignment: Alignment.center, | |
transformHitTests: true, | |
child: Transform.rotate( | |
transformHitTests: true, | |
angle: bearingRadians, | |
child: SvgPicture.asset('assets/vector/compass_needle.svg'), | |
), | |
), | |
), | |
), | |
); | |
return IgnorePointer( | |
ignoring: isStraight, | |
child: tooltip != null ? | |
Tooltip(message: tooltip, child: button) : | |
button, | |
); | |
}, | |
); | |
} | |
} | |
extension CameraPositionExtension on CameraPosition { | |
bool get isStraight => bearing == 0 && tilt == 0; | |
double get bearingRadians => bearing * -pi / 180; | |
double get tiltRadians => tilt * pi / 180; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<svg width="36px" height="36px" viewBox="0 0 36 36" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"> | |
<title>Compass needle</title> | |
<g id="Compass-needle" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"> | |
<path d="M18.3162278,2.21359436 C18.6148328,2.31312936 18.8491483,2.5474449 18.9486833,2.84604989 L24,18 L18,18 L17.9998483,2.16200636 C18.1046837,2.1620052 18.2113054,2.17862025 18.3162278,2.21359436 Z" id="Combined-Shape" fill="#E90000"></path> | |
<path d="M12.3162278,2.21359436 C12.6148328,2.31312936 12.8491483,2.5474449 12.9486833,2.84604989 L18,18 L12,18 L11.9998483,2.16200636 C12.1046837,2.1620052 12.2113054,2.17862025 12.3162278,2.21359436 Z" id="Combined-Shape" fill="#FF0004" transform="translate(14.999924, 10.081003) scale(-1, 1) translate(-14.999924, -10.081003) "></path> | |
<path d="M18.3162278,18.051588 C18.6148328,18.151123 18.8491483,18.3854385 18.9486833,18.6840435 L24,33.8379936 L18,33.8379936 L17.9998483,18 C18.1046837,17.9999988 18.2113054,18.0166139 18.3162278,18.051588 Z" id="Combined-Shape" fill="#DADADA" transform="translate(20.999924, 25.918997) scale(1, -1) translate(-20.999924, -25.918997) "></path> | |
<path d="M12.3162278,18.051588 C12.6148328,18.151123 12.8491483,18.3854385 12.9486833,18.6840435 L18,33.8379936 L12,33.8379936 L11.9998483,18 C12.1046837,17.9999988 12.2113054,18.0166139 12.3162278,18.051588 Z" id="Combined-Shape" fill="#C2C2C2" transform="translate(14.999924, 25.918997) scale(-1, -1) translate(-14.999924, -25.918997) "></path> | |
<circle id="Oval" fill="#FFFFFF" cx="18" cy="18" r="2"></circle> | |
</g> | |
</svg> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment