From ad9373312b2260d45638feaa2f05ffbd92766d00 Mon Sep 17 00:00:00 2001 From: martyfuhry Date: Fri, 10 Feb 2023 21:31:15 -0500 Subject: [PATCH] feat(mobile): Responsive display of exif data in bottom sheet (#1725) * two column view of exif * fixes padding * fixed divider when no map * fixed map visibility in two column --- .../asset_viewer/ui/exif_bottom_sheet.dart | 327 +++++++++++------- 1 file changed, 200 insertions(+), 127 deletions(-) diff --git a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart index 7977742bb1..e528bb1b78 100644 --- a/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart +++ b/mobile/lib/modules/asset_viewer/ui/exif_bottom_sheet.dart @@ -14,53 +14,60 @@ class ExifBottomSheet extends HookConsumerWidget { const ExifBottomSheet({Key? key, required this.assetDetail}) : super(key: key); + bool get showMap => assetDetail.latitude != null && assetDetail.longitude != null; + @override Widget build(BuildContext context, WidgetRef ref) { buildMap() { return Padding( padding: const EdgeInsets.symmetric(vertical: 16.0), - child: Container( - height: 150, - width: MediaQuery.of(context).size.width, - decoration: const BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(15)), - ), - child: FlutterMap( - options: MapOptions( - center: LatLng( - assetDetail.latitude ?? 0, - assetDetail.longitude ?? 0, + child: LayoutBuilder( + builder: (context, constraints) { + return Container( + height: 150, + width: constraints.maxWidth, + decoration: const BoxDecoration( + borderRadius: BorderRadius.all(Radius.circular(15)), ), - zoom: 16.0, - ), - layers: [ - TileLayerOptions( - urlTemplate: - "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", - subdomains: ['a', 'b', 'c'], - attributionBuilder: (_) { - return const Text( - "© OpenStreetMap", - style: TextStyle(fontSize: 10), - ); - }, - ), - MarkerLayerOptions( - markers: [ - Marker( - anchorPos: AnchorPos.align(AnchorAlign.top), - point: LatLng( - assetDetail.latitude ?? 0, - assetDetail.longitude ?? 0, - ), - builder: (ctx) => const Image( - image: AssetImage('assets/location-pin.png'), - ), + child: FlutterMap( + options: MapOptions( + interactiveFlags: InteractiveFlag.none, + center: LatLng( + assetDetail.latitude ?? 0, + assetDetail.longitude ?? 0, + ), + zoom: 16.0, + ), + layers: [ + TileLayerOptions( + urlTemplate: + "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", + subdomains: ['a', 'b', 'c'], + attributionBuilder: (_) { + return const Text( + "© OpenStreetMap", + style: TextStyle(fontSize: 10), + ); + }, + ), + MarkerLayerOptions( + markers: [ + Marker( + anchorPos: AnchorPos.align(AnchorAlign.top), + point: LatLng( + assetDetail.latitude ?? 0, + assetDetail.longitude ?? 0, + ), + builder: (ctx) => const Image( + image: AssetImage('assets/location-pin.png'), + ), + ), + ], ), ], ), - ], - ), + ); + }, ), ); } @@ -91,6 +98,107 @@ class ExifBottomSheet extends HookConsumerWidget { return text.isEmpty ? null : Text(text); } + buildDragHeader() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: const [ + SizedBox(height: 12), + Align( + alignment: Alignment.center, + child: CustomDraggingHandle(), + ), + SizedBox(height: 12), + ], + ); + } + + buildLocation() { + // Guard no lat/lng + if (!showMap) { + return Container(); + } + + return Column( + children: [ + // Location + Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + "exif_bottom_sheet_location", + style: TextStyle(fontSize: 11, color: textColor), + ).tr(), + buildMap(), + if (exifInfo != null && + exifInfo.city != null && + exifInfo.state != null) + buildLocationText(), + Text( + "${assetDetail.latitude!.toStringAsFixed(4)}, ${assetDetail.longitude!.toStringAsFixed(4)}", + style: const TextStyle(fontSize: 12), + ) + ], + ), + ], + ); + } + + buildDate() { + return Text( + DateFormat('date_format'.tr()).format( + assetDetail.createdAt.toLocal(), + ), + style: const TextStyle( + fontWeight: FontWeight.bold, + fontSize: 14, + ), + ); + } + + buildDetail() { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.only(bottom: 8.0), + child: Text( + "exif_bottom_sheet_details", + style: TextStyle(fontSize: 11, color: textColor), + ).tr(), + ), + ListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + leading: const Icon(Icons.image), + title: Text( + assetDetail.fileName, + style: TextStyle( + fontWeight: FontWeight.bold, + color: textColor, + ), + ), + subtitle: buildSizeText(assetDetail), + ), + if (exifInfo?.make != null) + ListTile( + contentPadding: const EdgeInsets.all(0), + dense: true, + leading: const Icon(Icons.camera), + title: Text( + "${exifInfo!.make} ${exifInfo.model}", + style: TextStyle( + color: textColor, + fontWeight: FontWeight.bold, + ), + ), + subtitle: Text( + "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO${exifInfo.iso} ", + ), + ), + ], + ); + } + return SingleChildScrollView( child: Card( shape: const RoundedRectangleBorder( @@ -102,104 +210,69 @@ class ExifBottomSheet extends HookConsumerWidget { margin: const EdgeInsets.all(0), child: Container( margin: const EdgeInsets.symmetric(horizontal: 8.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 12), - const Align( - alignment: Alignment.center, - child: CustomDraggingHandle(), - ), - const SizedBox(height: 12), - Text( - DateFormat('date_format'.tr()).format( - assetDetail.createdAt.toLocal(), - ), - style: const TextStyle( - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ), - - // Location - if (assetDetail.latitude != null && assetDetail.longitude != null) - Padding( - padding: const EdgeInsets.only(top: 32.0), + child: LayoutBuilder( + builder: (context, constraints) { + if (constraints.maxWidth > 600) { + // Two column + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 12.0), child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ - const Divider( - thickness: 1, + buildDragHeader(), + buildDate(), + const SizedBox(height: 32.0), + Row( + mainAxisAlignment: MainAxisAlignment.spaceEvenly, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Flexible( + flex: showMap ? 5 : 0, + child: Padding( + padding: const EdgeInsets.only(right: 8.0), + child: buildLocation(), + ), + ), + Flexible( + flex: 5, + child: Padding( + padding: const EdgeInsets.only(left: 8.0), + child: buildDetail(), + ), + ), + ], ), - Text( - "exif_bottom_sheet_location", - style: TextStyle(fontSize: 11, color: textColor), - ).tr(), - buildMap(), - if (exifInfo != null && - exifInfo.city != null && - exifInfo.state != null) - buildLocationText(), - Text( - "${assetDetail.latitude!.toStringAsFixed(4)}, ${assetDetail.longitude!.toStringAsFixed(4)}", - style: const TextStyle(fontSize: 12), - ) + const SizedBox(height: 50), ], ), - ), - // Detail - Padding( - padding: const EdgeInsets.only(top: 32.0), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ + ); + } + + // One column + return Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + buildDragHeader(), + buildDate(), + const SizedBox(height: 16.0), + if (showMap) Divider( thickness: 1, color: Colors.grey[600], ), - Padding( - padding: const EdgeInsets.only(bottom: 8.0), - child: Text( - "exif_bottom_sheet_details", - style: TextStyle(fontSize: 11, color: textColor), - ).tr(), - ), - ListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - leading: const Icon(Icons.image), - title: Text( - assetDetail.fileName, - style: TextStyle( - fontWeight: FontWeight.bold, - color: textColor, - ), - ), - subtitle: buildSizeText(assetDetail), - ), - if (exifInfo?.make != null) - ListTile( - contentPadding: const EdgeInsets.all(0), - dense: true, - leading: const Icon(Icons.camera), - title: Text( - "${exifInfo!.make} ${exifInfo.model}", - style: TextStyle( - color: textColor, - fontWeight: FontWeight.bold, - ), - ), - subtitle: Text( - "ƒ/${exifInfo.fNumber} ${exifInfo.exposureTime} ${exifInfo.focalLength} mm ISO${exifInfo.iso} ", - ), - ), - ], - ), - ), - const SizedBox( - height: 50, - ), - ], + const SizedBox(height: 16.0), + buildLocation(), + const SizedBox(height: 16.0), + Divider( + thickness: 1, + color: Colors.grey[600], + ), + const SizedBox(height: 16.0), + buildDetail(), + const SizedBox(height: 50), + ], + ); + }, ), ), ),