From 42820b7e48862ff91e1987e5174e64e0055c4e21 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sat, 7 Sep 2019 00:31:49 +0900 Subject: [PATCH] info: landscape layout --- .../fullscreen/info/basic_section.dart | 40 +++++++++ lib/widgets/fullscreen/info/info_page.dart | 35 ++++---- .../fullscreen/info/location_section.dart | 14 +++- .../fullscreen/info/metadata_section.dart | 84 +++++++++++++------ 4 files changed, 127 insertions(+), 46 deletions(-) create mode 100644 lib/widgets/fullscreen/info/basic_section.dart diff --git a/lib/widgets/fullscreen/info/basic_section.dart b/lib/widgets/fullscreen/info/basic_section.dart new file mode 100644 index 000000000..b91fb4dee --- /dev/null +++ b/lib/widgets/fullscreen/info/basic_section.dart @@ -0,0 +1,40 @@ +import 'package:aves/model/image_entry.dart'; +import 'package:aves/utils/file_utils.dart'; +import 'package:aves/widgets/fullscreen/info/info_page.dart'; +import 'package:flutter/material.dart'; +import 'package:intl/intl.dart'; + +class BasicSection extends StatelessWidget { + final ImageEntry entry; + + const BasicSection({ + Key key, + @required this.entry, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + final date = entry.bestDate; + final dateText = '${DateFormat.yMMMd().format(date)} – ${DateFormat.Hm().format(date)}'; + final resolutionText = '${entry.width} × ${entry.height}${entry.isVideo ? '' : ' (${entry.megaPixels} MP)'}'; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + InfoRow('Title', entry.title), + InfoRow('Date', dateText), + if (entry.isVideo) ..._buildVideoRows(), + InfoRow('Resolution', resolutionText), + InfoRow('Size', formatFilesize(entry.sizeBytes)), + InfoRow('Uri', entry.uri), + InfoRow('Path', entry.path), + ], + ); + } + + List _buildVideoRows() { + final rotation = entry.catalogMetadata?.videoRotation; + if (rotation != null) InfoRow('Rotation', '$rotation°'); + return [InfoRow('Duration', entry.durationText), if (rotation != null) InfoRow('Rotation', '$rotation°')]; + } +} diff --git a/lib/widgets/fullscreen/info/info_page.dart b/lib/widgets/fullscreen/info/info_page.dart index c13ceda90..92cff2065 100644 --- a/lib/widgets/fullscreen/info/info_page.dart +++ b/lib/widgets/fullscreen/info/info_page.dart @@ -1,11 +1,10 @@ import 'package:aves/model/image_collection.dart'; import 'package:aves/model/image_entry.dart'; -import 'package:aves/utils/file_utils.dart'; +import 'package:aves/widgets/fullscreen/info/basic_section.dart'; import 'package:aves/widgets/fullscreen/info/location_section.dart'; import 'package:aves/widgets/fullscreen/info/metadata_section.dart'; import 'package:aves/widgets/fullscreen/info/xmp_section.dart'; import 'package:flutter/material.dart'; -import 'package:intl/intl.dart'; class InfoPage extends StatefulWidget { final ImageCollection collection; @@ -34,9 +33,8 @@ class InfoPageState extends State { @override Widget build(BuildContext context) { - final date = entry.bestDate; - final dateText = '${DateFormat.yMMMd().format(date)} – ${DateFormat.Hm().format(date)}'; - final resolutionText = '${entry.width} × ${entry.height}${entry.isVideo ? '' : ' (${entry.megaPixels} MP)'}'; + // use MediaQuery instead of unreliable OrientationBuilder + final orientation = MediaQuery.of(context).orientation; final bottomInsets = MediaQuery.of(context).viewInsets.bottom; return Scaffold( appBar: AppBar( @@ -53,14 +51,19 @@ class InfoPageState extends State { child: ListView( padding: EdgeInsets.all(8.0) + EdgeInsets.only(bottom: bottomInsets), children: [ - InfoRow('Title', entry.title), - InfoRow('Date', dateText), - if (entry.isVideo) ..._buildVideoRows(), - InfoRow('Resolution', resolutionText), - InfoRow('Size', formatFilesize(entry.sizeBytes)), - InfoRow('Path', entry.path), - InfoRow('Uri', entry.uri), - LocationSection(entry: entry), + if (orientation == Orientation.landscape && entry.hasGps) + Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: BasicSection(entry: entry)), + SizedBox(width: 8), + Expanded(child: LocationSection(entry: entry, showTitle: false)), + ], + ) + else ...[ + BasicSection(entry: entry), + LocationSection(entry: entry, showTitle: true), + ], XmpTagSection(collection: widget.collection, entry: entry), MetadataSection(entry: entry), ], @@ -71,12 +74,6 @@ class InfoPageState extends State { ); } - List _buildVideoRows() { - final rotation = entry.catalogMetadata?.videoRotation; - if (rotation != null) InfoRow('Rotation', '$rotation°'); - return [InfoRow('Duration', entry.durationText), if (rotation != null) InfoRow('Rotation', '$rotation°')]; - } - bool _handleTopScroll(Notification notification) { if (notification is ScrollNotification) { if (notification is ScrollStartNotification) { diff --git a/lib/widgets/fullscreen/info/location_section.dart b/lib/widgets/fullscreen/info/location_section.dart index 84a6cf914..50a5f0019 100644 --- a/lib/widgets/fullscreen/info/location_section.dart +++ b/lib/widgets/fullscreen/info/location_section.dart @@ -7,8 +7,13 @@ import 'package:google_maps_flutter/google_maps_flutter.dart'; class LocationSection extends AnimatedWidget { final ImageEntry entry; + final showTitle; - const LocationSection({Key key, this.entry}) : super(key: key, listenable: entry); + const LocationSection({ + Key key, + @required this.entry, + @required this.showTitle, + }) : super(key: key, listenable: entry); @override Widget build(BuildContext context) { @@ -17,8 +22,11 @@ class LocationSection extends AnimatedWidget { : Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - SectionRow('Location'), - SizedBox(height: 8), + if (showTitle) + Padding( + padding: EdgeInsets.only(bottom: 8), + child: SectionRow('Location'), + ), ImageMap( markerId: entry.path, latLng: LatLng( diff --git a/lib/widgets/fullscreen/info/metadata_section.dart b/lib/widgets/fullscreen/info/metadata_section.dart index 040c69807..889589fb9 100644 --- a/lib/widgets/fullscreen/info/metadata_section.dart +++ b/lib/widgets/fullscreen/info/metadata_section.dart @@ -44,36 +44,72 @@ class MetadataSectionState extends State { if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink(); final metadataMap = snapshot.data.cast(); final directoryNames = metadataMap.keys.toList()..sort(); + + Widget content; + // use MediaQuery instead of unreliable OrientationBuilder + final orientation = MediaQuery.of(context).orientation; + if (orientation == Orientation.landscape) { + final threshold = directoryNames.map((k) => metadataMap[k].length).reduce((v, e) => v + e) / 2; + final first = [], second = []; + var processed = 0; + for (int i = 0; i < directoryNames.length; i++) { + final directoryName = directoryNames[i]; + if (processed <= threshold) + first.add(directoryName); + else + second.add(directoryName); + processed += 1 + metadataMap[directoryName].length; + } + debugPrint('first=$first second=$second'); + content = Row( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded(child: getMetadataColumn(metadataMap, first)), + SizedBox(width: 8), + Expanded(child: getMetadataColumn(metadataMap, second)), + ], + ); + } else { + content = getMetadataColumn(metadataMap, directoryNames); + } + return Column( - crossAxisAlignment: CrossAxisAlignment.start, + crossAxisAlignment: CrossAxisAlignment.stretch, children: [ SectionRow('Metadata'), - ...directoryNames.expand( - (directoryName) { - final directory = metadataMap[directoryName]; - final tagKeys = directory.keys.toList()..sort(); - return [ - if (directoryName.isNotEmpty) - Padding( - padding: EdgeInsets.symmetric(vertical: 4.0), - child: Text(directoryName, - style: TextStyle( - fontSize: 18, - fontFamily: 'Concourse Caps', - )), - ), - ...tagKeys.map((tagKey) { - final value = directory[tagKey] as String; - if (value == null || value.isEmpty) return SizedBox.shrink(); - return InfoRow(tagKey, value.length > maxValueLength ? '${value.substring(0, maxValueLength)}…' : value); - }), - SizedBox(height: 16), - ]; - }, - ) + content, ], ); }, ); } + + Widget getMetadataColumn(Map metadataMap, List directoryNames) { + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + ...directoryNames.expand((directoryName) { + final directory = metadataMap[directoryName]; + final tagKeys = directory.keys.toList()..sort(); + return [ + if (directoryName.isNotEmpty) + Padding( + padding: EdgeInsets.symmetric(vertical: 4.0), + child: Text(directoryName, + style: TextStyle( + fontSize: 18, + fontFamily: 'Concourse Caps', + )), + ), + ...tagKeys.map((tagKey) { + final value = directory[tagKey] as String; + if (value == null || value.isEmpty) return SizedBox.shrink(); + return InfoRow(tagKey, value.length > maxValueLength ? '${value.substring(0, maxValueLength)}…' : value); + }), + SizedBox(height: 16), + ]; + }), + ], + ); + } }