info: landscape layout

This commit is contained in:
Thibault Deckers 2019-09-07 00:31:49 +09:00
parent d919cd6022
commit 42820b7e48
4 changed files with 127 additions and 46 deletions

View file

@ -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<Widget> _buildVideoRows() {
final rotation = entry.catalogMetadata?.videoRotation;
if (rotation != null) InfoRow('Rotation', '$rotation°');
return [InfoRow('Duration', entry.durationText), if (rotation != null) InfoRow('Rotation', '$rotation°')];
}
}

View file

@ -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<InfoPage> {
@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<InfoPage> {
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<InfoPage> {
);
}
List<Widget> _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) {

View file

@ -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(

View file

@ -44,36 +44,72 @@ class MetadataSectionState extends State<MetadataSection> {
if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink();
final metadataMap = snapshot.data.cast<String, Map>();
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 = <String>[], second = <String>[];
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<String, Map> metadataMap, List<String> 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),
];
}),
],
);
}
}