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; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment