1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

refactor(mobile): map heatmap color and location picker (#6553)

* refactor(mobile): make location picker scaffold primary

* chore(mobile): update map heatmap colors

* style(mobile): map bottomsheet - only use borders on top

* fix(mobile): location picker show buttons above navigation bar

* fix: crash on iOS due to heatmap invalid color format

* disable rotate

---------

Co-authored-by: shenlong-tanwen <139912620+shalong-tanwen@users.noreply.github.com>
Co-authored-by: Alex Tran <alex.tran1502@gmail.com>
This commit is contained in:
shenlong 2024-02-08 03:07:43 +00:00 committed by GitHub
parent 2010c92b61
commit e0864768c2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 102 additions and 100 deletions

View file

@ -180,4 +180,4 @@ SPEC CHECKSUMS:
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
COCOAPODS: 1.12.1 COCOAPODS: 1.11.3

View file

@ -1,14 +1,14 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
extension ContextHelper on BuildContext { extension ContextHelper on BuildContext {
// Returns the current size from MediaQuery // Returns the current padding from MediaQuery
Size get size => MediaQuery.sizeOf(this); EdgeInsets get padding => MediaQuery.paddingOf(this);
// Returns the current width from MediaQuery // Returns the current width from MediaQuery
double get width => size.width; double get width => MediaQuery.sizeOf(this).width;
// Returns the current height from MediaQuery // Returns the current height from MediaQuery
double get height => size.height; double get height => MediaQuery.sizeOf(this).height;
// Returns true if the app is running on a mobile device (!tablets) // Returns true if the app is running on a mobile device (!tablets)
bool get isMobile => width < 550; bool get isMobile => width < 550;

View file

@ -19,17 +19,17 @@ class MapUtils {
["linear"], ["linear"],
["heatmap-density"], ["heatmap-density"],
0.0, 0.0,
"rgba(246,239,247,0.0)", "rgba(103,58,183,0.0)",
0.2, 0.3,
"rgb(208,209,230)", "rgb(103,58,183)",
0.4, 0.5,
"rgb(166,189,219)", "rgb(33,149,243)",
0.6, 0.7,
"rgb(103,169,207)", "rgb(76,175,79)",
0.8, 0.95,
"rgb(28,144,153)", "rgb(255,235,59)",
1.0, 1.0,
"rgb(1,108,89)", "rgb(255,86,34)",
], ],
heatmapIntensity: [ heatmapIntensity: [
Expressions.interpolate, ["linear"], // Expressions.interpolate, ["linear"], //
@ -44,6 +44,7 @@ class MapUtils {
4, 8, 4, 8,
9, 16, 9, 16,
], ],
heatmapOpacity: 0.7,
); );
static Map<String, dynamic> _addFeature(MapMarker marker) => { static Map<String, dynamic> _addFeature(MapMarker marker) => {

View file

@ -11,7 +11,6 @@ import 'package:immich_mobile/extensions/maplibrecontroller_extensions.dart';
import 'package:immich_mobile/modules/map/widgets/map_theme_override.dart'; import 'package:immich_mobile/modules/map/widgets/map_theme_override.dart';
import 'package:maplibre_gl/maplibre_gl.dart'; import 'package:maplibre_gl/maplibre_gl.dart';
import 'package:immich_mobile/modules/map/utils/map_utils.dart'; import 'package:immich_mobile/modules/map/utils/map_utils.dart';
import 'package:geolocator/geolocator.dart';
@RoutePage<LatLng?>() @RoutePage<LatLng?>()
class MapLocationPickerPage extends HookConsumerWidget { class MapLocationPickerPage extends HookConsumerWidget {
@ -46,15 +45,13 @@ class MapLocationPickerPage extends HookConsumerWidget {
} }
Future<void> getCurrentLocation() async { Future<void> getCurrentLocation() async {
var (currentLocation, locationPermission) = var (currentLocation, _) =
await MapUtils.checkPermAndGetLocation(context); await MapUtils.checkPermAndGetLocation(context);
if (locationPermission == LocationPermission.denied ||
locationPermission == LocationPermission.deniedForever) {
return;
}
if (currentLocation == null) { if (currentLocation == null) {
return; return;
} }
var currentLatLng = var currentLatLng =
LatLng(currentLocation.latitude, currentLocation.longitude); LatLng(currentLocation.latitude, currentLocation.longitude);
selectedLatLng.value = currentLatLng; selectedLatLng.value = currentLatLng;
@ -67,40 +64,35 @@ class MapLocationPickerPage extends HookConsumerWidget {
backgroundColor: ctx.themeData.cardColor, backgroundColor: ctx.themeData.cardColor,
appBar: _AppBar(onClose: onClose), appBar: _AppBar(onClose: onClose),
extendBodyBehindAppBar: true, extendBodyBehindAppBar: true,
body: Column( primary: true,
children: [ body: style.widgetWhen(
style.widgetWhen( onData: (style) => Container(
onData: (style) => Expanded( clipBehavior: Clip.antiAliasWithSaveLayer,
child: Container( decoration: const BoxDecoration(
clipBehavior: Clip.antiAliasWithSaveLayer, borderRadius: BorderRadius.only(
decoration: const BoxDecoration( bottomLeft: Radius.circular(40),
borderRadius: BorderRadius.only( bottomRight: Radius.circular(40),
bottomLeft: Radius.circular(40),
bottomRight: Radius.circular(40),
),
),
child: MaplibreMap(
initialCameraPosition:
CameraPosition(target: initialLatLng, zoom: 12),
styleString: style,
onMapCreated: (mapController) =>
controller.value = mapController,
onStyleLoadedCallback: onStyleLoaded,
onMapClick: onMapClick,
dragEnabled: false,
tiltGesturesEnabled: false,
myLocationEnabled: false,
attributionButtonMargins: const Point(20, 15),
),
),
), ),
), ),
_BottomBar( child: MaplibreMap(
selectedLatLng: selectedLatLng, initialCameraPosition:
onUseLocation: () => onClose(selectedLatLng.value), CameraPosition(target: initialLatLng, zoom: 12),
onGetCurrentLocation: getCurrentLocation, styleString: style,
onMapCreated: (mapController) =>
controller.value = mapController,
onStyleLoadedCallback: onStyleLoaded,
onMapClick: onMapClick,
dragEnabled: false,
tiltGesturesEnabled: false,
myLocationEnabled: false,
attributionButtonMargins: const Point(20, 15),
), ),
], ),
),
bottomNavigationBar: _BottomBar(
selectedLatLng: selectedLatLng,
onUseLocation: () => onClose(selectedLatLng.value),
onGetCurrentLocation: getCurrentLocation,
), ),
), ),
), ),
@ -116,17 +108,15 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
padding: EdgeInsets.only(top: MediaQuery.paddingOf(context).top + 25), padding: const EdgeInsets.only(top: 25),
child: Expanded( child: Align(
child: Align( alignment: Alignment.centerLeft,
alignment: Alignment.centerLeft, child: ElevatedButton(
child: ElevatedButton( onPressed: onClose,
onPressed: onClose, style: ElevatedButton.styleFrom(
style: ElevatedButton.styleFrom( shape: const CircleBorder(),
shape: const CircleBorder(),
),
child: const Icon(Icons.arrow_back_ios_new_rounded),
), ),
child: const Icon(Icons.arrow_back_ios_new_rounded),
), ),
), ),
); );
@ -150,38 +140,42 @@ class _BottomBar extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SizedBox( return SizedBox(
height: 150, height: 150 + context.padding.bottom,
child: Column( child: Padding(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, padding: EdgeInsets.only(bottom: context.padding.bottom),
crossAxisAlignment: CrossAxisAlignment.start, child: Column(
children: [ mainAxisAlignment: MainAxisAlignment.spaceEvenly,
Row( crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.center, children: [
children: [ Row(
const Icon(Icons.public, size: 18), mainAxisAlignment: MainAxisAlignment.center,
const SizedBox(width: 15), children: [
ValueListenableBuilder( const Icon(Icons.public, size: 18),
valueListenable: selectedLatLng, const SizedBox(width: 15),
builder: (_, value, __) => Text( ValueListenableBuilder(
"${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}", valueListenable: selectedLatLng,
builder: (_, value, __) => Text(
"${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}",
),
), ),
), ],
], ),
), Row(
Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [
children: [ ElevatedButton(
ElevatedButton( onPressed: onUseLocation,
onPressed: onUseLocation, child:
child: const Text("map_location_picker_page_use_location").tr(), const Text("map_location_picker_page_use_location").tr(),
), ),
ElevatedButton( ElevatedButton(
onPressed: onGetCurrentLocation, onPressed: onGetCurrentLocation,
child: const Icon(Icons.my_location), child: const Icon(Icons.my_location),
), ),
], ],
), ),
], ],
),
), ),
); );
} }

View file

@ -220,10 +220,9 @@ class MapPage extends HookConsumerWidget {
} }
void onZoomToLocation() async { void onZoomToLocation() async {
final location = await MapUtils.checkPermAndGetLocation(context); final (location, error) = await MapUtils.checkPermAndGetLocation(context);
if (location.$2 != null) { if (error != null) {
if (location.$2 == LocationPermission.unableToDetermine && if (error == LocationPermission.unableToDetermine && context.mounted) {
context.mounted) {
ImmichToast.show( ImmichToast.show(
context: context, context: context,
gravity: ToastGravity.BOTTOM, gravity: ToastGravity.BOTTOM,
@ -234,10 +233,10 @@ class MapPage extends HookConsumerWidget {
return; return;
} }
if (mapController.value != null && location.$1 != null) { if (mapController.value != null && location != null) {
mapController.value!.animateCamera( mapController.value!.animateCamera(
CameraUpdate.newLatLngZoom( CameraUpdate.newLatLngZoom(
LatLng(location.$1!.latitude, location.$1!.longitude), LatLng(location.latitude, location.longitude),
mapZoomToAssetLevel, mapZoomToAssetLevel,
), ),
duration: const Duration(milliseconds: 800), duration: const Duration(milliseconds: 800),
@ -389,6 +388,7 @@ class _MapWithMarker extends StatelessWidget {
dragEnabled: false, dragEnabled: false,
myLocationEnabled: false, myLocationEnabled: false,
attributionButtonPosition: AttributionButtonPosition.TopRight, attributionButtonPosition: AttributionButtonPosition.TopRight,
rotateGesturesEnabled: false,
), ),
), ),
ValueListenableBuilder( ValueListenableBuilder(

View file

@ -231,7 +231,14 @@ class _MapSheetDragRegion extends StatelessWidget {
physics: const ClampingScrollPhysics(), physics: const ClampingScrollPhysics(),
child: Card( child: Card(
margin: EdgeInsets.zero, margin: EdgeInsets.zero,
shape: context.isMobile ? null : const BeveledRectangleBorder(), shape: context.isMobile
? const RoundedRectangleBorder(
borderRadius: BorderRadius.only(
topRight: Radius.circular(20),
topLeft: Radius.circular(20),
),
)
: const BeveledRectangleBorder(),
elevation: 0.0, elevation: 0.0,
child: Stack( child: Stack(
children: [ children: [