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:
parent
2010c92b61
commit
e0864768c2
6 changed files with 102 additions and 100 deletions
|
@ -180,4 +180,4 @@ SPEC CHECKSUMS:
|
|||
|
||||
PODFILE CHECKSUM: 64c9b5291666c0ca3caabdfe9865c141ac40321d
|
||||
|
||||
COCOAPODS: 1.12.1
|
||||
COCOAPODS: 1.11.3
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
extension ContextHelper on BuildContext {
|
||||
// Returns the current size from MediaQuery
|
||||
Size get size => MediaQuery.sizeOf(this);
|
||||
// Returns the current padding from MediaQuery
|
||||
EdgeInsets get padding => MediaQuery.paddingOf(this);
|
||||
|
||||
// Returns the current width from MediaQuery
|
||||
double get width => size.width;
|
||||
double get width => MediaQuery.sizeOf(this).width;
|
||||
|
||||
// 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)
|
||||
bool get isMobile => width < 550;
|
||||
|
|
|
@ -19,17 +19,17 @@ class MapUtils {
|
|||
["linear"],
|
||||
["heatmap-density"],
|
||||
0.0,
|
||||
"rgba(246,239,247,0.0)",
|
||||
0.2,
|
||||
"rgb(208,209,230)",
|
||||
0.4,
|
||||
"rgb(166,189,219)",
|
||||
0.6,
|
||||
"rgb(103,169,207)",
|
||||
0.8,
|
||||
"rgb(28,144,153)",
|
||||
"rgba(103,58,183,0.0)",
|
||||
0.3,
|
||||
"rgb(103,58,183)",
|
||||
0.5,
|
||||
"rgb(33,149,243)",
|
||||
0.7,
|
||||
"rgb(76,175,79)",
|
||||
0.95,
|
||||
"rgb(255,235,59)",
|
||||
1.0,
|
||||
"rgb(1,108,89)",
|
||||
"rgb(255,86,34)",
|
||||
],
|
||||
heatmapIntensity: [
|
||||
Expressions.interpolate, ["linear"], //
|
||||
|
@ -44,6 +44,7 @@ class MapUtils {
|
|||
4, 8,
|
||||
9, 16,
|
||||
],
|
||||
heatmapOpacity: 0.7,
|
||||
);
|
||||
|
||||
static Map<String, dynamic> _addFeature(MapMarker marker) => {
|
||||
|
|
|
@ -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:maplibre_gl/maplibre_gl.dart';
|
||||
import 'package:immich_mobile/modules/map/utils/map_utils.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
@RoutePage<LatLng?>()
|
||||
class MapLocationPickerPage extends HookConsumerWidget {
|
||||
|
@ -46,15 +45,13 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
Future<void> getCurrentLocation() async {
|
||||
var (currentLocation, locationPermission) =
|
||||
var (currentLocation, _) =
|
||||
await MapUtils.checkPermAndGetLocation(context);
|
||||
if (locationPermission == LocationPermission.denied ||
|
||||
locationPermission == LocationPermission.deniedForever) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentLocation == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
var currentLatLng =
|
||||
LatLng(currentLocation.latitude, currentLocation.longitude);
|
||||
selectedLatLng.value = currentLatLng;
|
||||
|
@ -67,40 +64,35 @@ class MapLocationPickerPage extends HookConsumerWidget {
|
|||
backgroundColor: ctx.themeData.cardColor,
|
||||
appBar: _AppBar(onClose: onClose),
|
||||
extendBodyBehindAppBar: true,
|
||||
body: Column(
|
||||
children: [
|
||||
style.widgetWhen(
|
||||
onData: (style) => Expanded(
|
||||
child: Container(
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
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),
|
||||
),
|
||||
),
|
||||
primary: true,
|
||||
body: style.widgetWhen(
|
||||
onData: (style) => Container(
|
||||
clipBehavior: Clip.antiAliasWithSaveLayer,
|
||||
decoration: const BoxDecoration(
|
||||
borderRadius: BorderRadius.only(
|
||||
bottomLeft: Radius.circular(40),
|
||||
bottomRight: Radius.circular(40),
|
||||
),
|
||||
),
|
||||
_BottomBar(
|
||||
selectedLatLng: selectedLatLng,
|
||||
onUseLocation: () => onClose(selectedLatLng.value),
|
||||
onGetCurrentLocation: getCurrentLocation,
|
||||
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),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
bottomNavigationBar: _BottomBar(
|
||||
selectedLatLng: selectedLatLng,
|
||||
onUseLocation: () => onClose(selectedLatLng.value),
|
||||
onGetCurrentLocation: getCurrentLocation,
|
||||
),
|
||||
),
|
||||
),
|
||||
|
@ -116,17 +108,15 @@ class _AppBar extends StatelessWidget implements PreferredSizeWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Padding(
|
||||
padding: EdgeInsets.only(top: MediaQuery.paddingOf(context).top + 25),
|
||||
child: Expanded(
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ElevatedButton(
|
||||
onPressed: onClose,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
child: const Icon(Icons.arrow_back_ios_new_rounded),
|
||||
padding: const EdgeInsets.only(top: 25),
|
||||
child: Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: ElevatedButton(
|
||||
onPressed: onClose,
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
),
|
||||
child: const Icon(Icons.arrow_back_ios_new_rounded),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
@ -150,38 +140,42 @@ class _BottomBar extends StatelessWidget {
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SizedBox(
|
||||
height: 150,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.public, size: 18),
|
||||
const SizedBox(width: 15),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: selectedLatLng,
|
||||
builder: (_, value, __) => Text(
|
||||
"${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}",
|
||||
height: 150 + context.padding.bottom,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: context.padding.bottom),
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.public, size: 18),
|
||||
const SizedBox(width: 15),
|
||||
ValueListenableBuilder(
|
||||
valueListenable: selectedLatLng,
|
||||
builder: (_, value, __) => Text(
|
||||
"${value.latitude.toStringAsFixed(4)}, ${value.longitude.toStringAsFixed(4)}",
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: onUseLocation,
|
||||
child: const Text("map_location_picker_page_use_location").tr(),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: onGetCurrentLocation,
|
||||
child: const Icon(Icons.my_location),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||
children: [
|
||||
ElevatedButton(
|
||||
onPressed: onUseLocation,
|
||||
child:
|
||||
const Text("map_location_picker_page_use_location").tr(),
|
||||
),
|
||||
ElevatedButton(
|
||||
onPressed: onGetCurrentLocation,
|
||||
child: const Icon(Icons.my_location),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -220,10 +220,9 @@ class MapPage extends HookConsumerWidget {
|
|||
}
|
||||
|
||||
void onZoomToLocation() async {
|
||||
final location = await MapUtils.checkPermAndGetLocation(context);
|
||||
if (location.$2 != null) {
|
||||
if (location.$2 == LocationPermission.unableToDetermine &&
|
||||
context.mounted) {
|
||||
final (location, error) = await MapUtils.checkPermAndGetLocation(context);
|
||||
if (error != null) {
|
||||
if (error == LocationPermission.unableToDetermine && context.mounted) {
|
||||
ImmichToast.show(
|
||||
context: context,
|
||||
gravity: ToastGravity.BOTTOM,
|
||||
|
@ -234,10 +233,10 @@ class MapPage extends HookConsumerWidget {
|
|||
return;
|
||||
}
|
||||
|
||||
if (mapController.value != null && location.$1 != null) {
|
||||
if (mapController.value != null && location != null) {
|
||||
mapController.value!.animateCamera(
|
||||
CameraUpdate.newLatLngZoom(
|
||||
LatLng(location.$1!.latitude, location.$1!.longitude),
|
||||
LatLng(location.latitude, location.longitude),
|
||||
mapZoomToAssetLevel,
|
||||
),
|
||||
duration: const Duration(milliseconds: 800),
|
||||
|
@ -389,6 +388,7 @@ class _MapWithMarker extends StatelessWidget {
|
|||
dragEnabled: false,
|
||||
myLocationEnabled: false,
|
||||
attributionButtonPosition: AttributionButtonPosition.TopRight,
|
||||
rotateGesturesEnabled: false,
|
||||
),
|
||||
),
|
||||
ValueListenableBuilder(
|
||||
|
|
|
@ -231,7 +231,14 @@ class _MapSheetDragRegion extends StatelessWidget {
|
|||
physics: const ClampingScrollPhysics(),
|
||||
child: Card(
|
||||
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,
|
||||
child: Stack(
|
||||
children: [
|
||||
|
|
Loading…
Reference in a new issue