mirror of
https://github.com/immich-app/immich
synced 2025-11-07 17:27:20 +00:00
fix(mobile): map markers not loading with int coordinates (#3957)
* fix(mobile): increase zoom-level for map zoom to asset * refactor(mobile): map-view - rename lastAssetOffsetInSheet * Workaround OpenAPI Dart generator bug * fix(mobile): map - increase appbar top padding * fix(mobile): navigation bar overlapping map bottom sheet * fix(mobile): map - do not animate the drag handle of bottom sheet on scroll * fix(mobile): F-Droid build failure due to map view * fix(mobile): remove jank on map asset marker update * fix(mobile): map view app-bar padding is made dynamic * fix(mobile): reduce debounce time in bottom sheet asset scroll * fix(mobile): bottom sheet - reduce drag handle total height --------- Co-authored-by: Daniele Ricci <daniele@casaricci.it>
This commit is contained in:
parent
816d040d81
commit
f8d26bd865
10 changed files with 178 additions and 120 deletions
|
|
@ -1,4 +1,5 @@
|
|||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:easy_localization/easy_localization.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
|
@ -41,7 +42,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
// Non-State variables
|
||||
bool userTappedOnMap = false;
|
||||
RenderList? _cachedRenderList;
|
||||
int lastAssetOffsetInSheet = -1;
|
||||
int assetOffsetInSheet = -1;
|
||||
late final DraggableScrollableController bottomSheetController;
|
||||
late final Debounce debounce;
|
||||
|
||||
|
|
@ -50,14 +51,16 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
super.initState();
|
||||
bottomSheetController = DraggableScrollableController();
|
||||
debounce = Debounce(
|
||||
const Duration(milliseconds: 200),
|
||||
const Duration(milliseconds: 100),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
double maxHeight = MediaQuery.of(context).size.height;
|
||||
final isDarkMode = Theme.of(context).brightness == Brightness.dark;
|
||||
final bottomPadding =
|
||||
Platform.isAndroid ? MediaQuery.of(context).padding.bottom - 10 : 0.0;
|
||||
final maxHeight = MediaQuery.of(context).size.height - bottomPadding;
|
||||
final isSheetScrolled = useState(false);
|
||||
final isSheetExpanded = useState(false);
|
||||
final assetsInBound = useState(<Asset>[]);
|
||||
|
|
@ -68,7 +71,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
assetsInBound.value = event.assets;
|
||||
} else if (event is MapPageOnTapEvent) {
|
||||
userTappedOnMap = true;
|
||||
lastAssetOffsetInSheet = -1;
|
||||
assetOffsetInSheet = -1;
|
||||
bottomSheetController.animateTo(
|
||||
0.1,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
|
|
@ -98,8 +101,8 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
columnOffset = columnOffset < renderElement.totalCount
|
||||
? columnOffset
|
||||
: renderElement.totalCount - 1;
|
||||
lastAssetOffsetInSheet = rowOffset + columnOffset;
|
||||
final asset = _cachedRenderList?.allAssets?[lastAssetOffsetInSheet];
|
||||
assetOffsetInSheet = rowOffset + columnOffset;
|
||||
final asset = _cachedRenderList?.allAssets?[assetOffsetInSheet];
|
||||
userTappedOnMap = false;
|
||||
if (!userTappedOnMap && isSheetExpanded.value) {
|
||||
widget.bottomSheetEventSC.add(
|
||||
|
|
@ -162,10 +165,10 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
}
|
||||
|
||||
void onTapMapButton() {
|
||||
if (lastAssetOffsetInSheet != -1) {
|
||||
if (assetOffsetInSheet != -1) {
|
||||
widget.bottomSheetEventSC.add(
|
||||
MapPageZoomToAsset(
|
||||
_cachedRenderList?.allAssets?[lastAssetOffsetInSheet],
|
||||
_cachedRenderList?.allAssets?[assetOffsetInSheet],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
@ -176,7 +179,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
? "${assetsInBound.value.length} photo${assetsInBound.value.length > 1 ? "s" : ""}"
|
||||
: "map_no_assets_in_bounds".tr();
|
||||
final dragHandle = Container(
|
||||
height: 75,
|
||||
height: 60,
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
|
||||
|
|
@ -187,9 +190,9 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 5),
|
||||
const CustomDraggingHandle(),
|
||||
const SizedBox(height: 12),
|
||||
const SizedBox(height: 15),
|
||||
Text(
|
||||
textToDisplay,
|
||||
style: TextStyle(
|
||||
|
|
@ -199,6 +202,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
),
|
||||
),
|
||||
Divider(
|
||||
height: 10,
|
||||
color: Theme.of(context)
|
||||
.textTheme
|
||||
.displayLarge
|
||||
|
|
@ -226,6 +230,7 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
);
|
||||
return SingleChildScrollView(
|
||||
controller: scrollController,
|
||||
physics: const ClampingScrollPhysics(),
|
||||
child: dragHandle,
|
||||
);
|
||||
}
|
||||
|
|
@ -238,118 +243,125 @@ class AssetsInBoundBottomSheetState extends ConsumerState<MapPageBottomSheet> {
|
|||
if (!sheetExtended) {
|
||||
// reset state
|
||||
userTappedOnMap = false;
|
||||
lastAssetOffsetInSheet = -1;
|
||||
assetOffsetInSheet = -1;
|
||||
isSheetScrolled.value = false;
|
||||
}
|
||||
|
||||
return true;
|
||||
},
|
||||
child: Stack(
|
||||
children: [
|
||||
DraggableScrollableSheet(
|
||||
controller: bottomSheetController,
|
||||
initialChildSize: 0.1,
|
||||
minChildSize: 0.1,
|
||||
maxChildSize: 0.55,
|
||||
snap: true,
|
||||
builder: (
|
||||
BuildContext context,
|
||||
ScrollController scrollController,
|
||||
) {
|
||||
return Card(
|
||||
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 18.0,
|
||||
margin: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
children: [
|
||||
buildDragHandle(scrollController),
|
||||
if (isSheetExpanded.value && assetsInBound.value.isNotEmpty)
|
||||
ref
|
||||
.watch(
|
||||
renderListProvider(
|
||||
assetsInBound.value,
|
||||
),
|
||||
)
|
||||
.when(
|
||||
data: (renderList) {
|
||||
_cachedRenderList = renderList;
|
||||
final assetGrid = ImmichAssetGrid(
|
||||
shrinkWrap: true,
|
||||
renderList: renderList,
|
||||
showDragScroll: false,
|
||||
selectionActive: widget.selectionEnabled,
|
||||
showMultiSelectIndicator: false,
|
||||
listener: widget.selectionlistener,
|
||||
visibleItemsListener: visibleItemsListener,
|
||||
);
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(
|
||||
bottom: bottomPadding,
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
DraggableScrollableSheet(
|
||||
controller: bottomSheetController,
|
||||
initialChildSize: 0.1,
|
||||
minChildSize: 0.1,
|
||||
maxChildSize: 0.55,
|
||||
snap: true,
|
||||
builder: (
|
||||
BuildContext context,
|
||||
ScrollController scrollController,
|
||||
) {
|
||||
return Card(
|
||||
color: isDarkMode ? Colors.grey[900] : Colors.grey[100],
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 18.0,
|
||||
margin: const EdgeInsets.all(0),
|
||||
child: Column(
|
||||
children: [
|
||||
buildDragHandle(scrollController),
|
||||
if (isSheetExpanded.value &&
|
||||
assetsInBound.value.isNotEmpty)
|
||||
ref
|
||||
.watch(
|
||||
renderListProvider(
|
||||
assetsInBound.value,
|
||||
),
|
||||
)
|
||||
.when(
|
||||
data: (renderList) {
|
||||
_cachedRenderList = renderList;
|
||||
final assetGrid = ImmichAssetGrid(
|
||||
shrinkWrap: true,
|
||||
renderList: renderList,
|
||||
showDragScroll: false,
|
||||
selectionActive: widget.selectionEnabled,
|
||||
showMultiSelectIndicator: false,
|
||||
listener: widget.selectionlistener,
|
||||
visibleItemsListener: visibleItemsListener,
|
||||
);
|
||||
|
||||
return Expanded(child: assetGrid);
|
||||
},
|
||||
error: (error, stackTrace) {
|
||||
log.warning(
|
||||
"Cannot get assets in the current map bounds ${error.toString()}",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
return Expanded(child: assetGrid);
|
||||
},
|
||||
error: (error, stackTrace) {
|
||||
log.warning(
|
||||
"Cannot get assets in the current map bounds ${error.toString()}",
|
||||
error,
|
||||
stackTrace,
|
||||
);
|
||||
return const SizedBox.shrink();
|
||||
},
|
||||
loading: () => const SizedBox.shrink(),
|
||||
),
|
||||
if (isSheetExpanded.value && assetsInBound.value.isEmpty)
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: buildNoPhotosWidget(),
|
||||
),
|
||||
if (isSheetExpanded.value && assetsInBound.value.isEmpty)
|
||||
Expanded(
|
||||
child: SingleChildScrollView(
|
||||
child: buildNoPhotosWidget(),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
bottom: maxHeight * currentExtend.value,
|
||||
left: 0,
|
||||
child: GestureDetector(
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse('https://openstreetmap.org/copyright'),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
Positioned(
|
||||
bottom: maxHeight * currentExtend.value,
|
||||
left: 0,
|
||||
child: GestureDetector(
|
||||
onTap: () => launchUrl(
|
||||
Uri.parse('https://openstreetmap.org/copyright'),
|
||||
),
|
||||
child: ColoredBox(
|
||||
color:
|
||||
(widget.isDarkTheme ? Colors.grey[900] : Colors.grey[100])!,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Text(
|
||||
'© OpenStreetMap contributors',
|
||||
style: TextStyle(
|
||||
fontSize: 6,
|
||||
color: !widget.isDarkTheme
|
||||
? Colors.grey[900]
|
||||
: Colors.grey[100],
|
||||
child: ColoredBox(
|
||||
color: (widget.isDarkTheme
|
||||
? Colors.grey[900]
|
||||
: Colors.grey[100])!,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(3),
|
||||
child: Text(
|
||||
'© OpenStreetMap contributors',
|
||||
style: TextStyle(
|
||||
fontSize: 6,
|
||||
color: !widget.isDarkTheme
|
||||
? Colors.grey[900]
|
||||
: Colors.grey[100],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
Positioned(
|
||||
bottom: maxHeight * (0.14 + (currentExtend.value - 0.1)),
|
||||
right: 15,
|
||||
child: ElevatedButton(
|
||||
onPressed: () =>
|
||||
widget.bottomSheetEventSC.add(const MapPageZoomToLocation()),
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
padding: const EdgeInsets.all(12),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.my_location,
|
||||
size: 22,
|
||||
fill: 1,
|
||||
Positioned(
|
||||
bottom: maxHeight * (0.14 + (currentExtend.value - 0.1)),
|
||||
right: 15,
|
||||
child: ElevatedButton(
|
||||
onPressed: () => widget.bottomSheetEventSC
|
||||
.add(const MapPageZoomToLocation()),
|
||||
style: ElevatedButton.styleFrom(
|
||||
shape: const CircleBorder(),
|
||||
padding: const EdgeInsets.all(12),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.my_location,
|
||||
size: 22,
|
||||
fill: 1,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue