linting
This commit is contained in:
parent
f965e329ad
commit
cb28ad9272
33 changed files with 212 additions and 209 deletions
|
@ -3,3 +3,10 @@ include: package:pedantic/analysis_options.yaml
|
||||||
analyzer:
|
analyzer:
|
||||||
exclude:
|
exclude:
|
||||||
- lib/generated_plugin_registrant.dart
|
- lib/generated_plugin_registrant.dart
|
||||||
|
|
||||||
|
linter:
|
||||||
|
rules:
|
||||||
|
- always_declare_return_types
|
||||||
|
- prefer_const_constructors
|
||||||
|
- prefer_const_constructors_in_immutables
|
||||||
|
- prefer_const_declarations
|
||||||
|
|
|
@ -16,10 +16,10 @@ import 'package:pedantic/pedantic.dart';
|
||||||
import 'package:permission_handler/permission_handler.dart';
|
import 'package:permission_handler/permission_handler.dart';
|
||||||
import 'package:screen/screen.dart';
|
import 'package:screen/screen.dart';
|
||||||
|
|
||||||
final stopwatch = Stopwatch()..start();
|
final _stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
void main() {
|
void main() {
|
||||||
debugPrint('main start, elapsed=${stopwatch.elapsed}');
|
debugPrint('main start, elapsed=${_stopwatch.elapsed}');
|
||||||
// initialize binding/plugins to configure Skia before `runApp`
|
// initialize binding/plugins to configure Skia before `runApp`
|
||||||
WidgetsFlutterBinding.ensureInitialized(); // 220ms
|
WidgetsFlutterBinding.ensureInitialized(); // 220ms
|
||||||
// debugPrint('main WidgetsFlutterBinding.ensureInitialized done, elapsed=${stopwatch.elapsed}');
|
// debugPrint('main WidgetsFlutterBinding.ensureInitialized done, elapsed=${stopwatch.elapsed}');
|
||||||
|
@ -61,7 +61,7 @@ class HomePage extends StatefulWidget {
|
||||||
class _HomePageState extends State<HomePage> {
|
class _HomePageState extends State<HomePage> {
|
||||||
static const EventChannel eventChannel = EventChannel('deckers.thibault/aves/mediastore');
|
static const EventChannel eventChannel = EventChannel('deckers.thibault/aves/mediastore');
|
||||||
|
|
||||||
ImageCollection localMediaCollection = ImageCollection(entries: List());
|
ImageCollection localMediaCollection = ImageCollection(entries: []);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
|
@ -72,7 +72,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setup() async {
|
Future<void> setup() async {
|
||||||
debugPrint('$runtimeType setup start, elapsed=${stopwatch.elapsed}');
|
debugPrint('$runtimeType setup start, elapsed=${_stopwatch.elapsed}');
|
||||||
// TODO reduce permission check time
|
// TODO reduce permission check time
|
||||||
final permissions = await PermissionHandler().requestPermissions([
|
final permissions = await PermissionHandler().requestPermissions([
|
||||||
PermissionGroup.storage
|
PermissionGroup.storage
|
||||||
|
@ -91,7 +91,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
await settings.init(); // <20ms
|
await settings.init(); // <20ms
|
||||||
localMediaCollection.groupFactor = settings.collectionGroupFactor;
|
localMediaCollection.groupFactor = settings.collectionGroupFactor;
|
||||||
localMediaCollection.sortFactor = settings.collectionSortFactor;
|
localMediaCollection.sortFactor = settings.collectionSortFactor;
|
||||||
debugPrint('$runtimeType setup settings.init done, elapsed=${stopwatch.elapsed}');
|
debugPrint('$runtimeType setup settings.init done, elapsed=${_stopwatch.elapsed}');
|
||||||
|
|
||||||
await metadataDb.init(); // <20ms
|
await metadataDb.init(); // <20ms
|
||||||
final currentTimeZone = await FlutterNativeTimezone.getLocalTimezone(); // <20ms
|
final currentTimeZone = await FlutterNativeTimezone.getLocalTimezone(); // <20ms
|
||||||
|
@ -107,7 +107,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
eventChannel.receiveBroadcastStream().cast<Map>().listen(
|
eventChannel.receiveBroadcastStream().cast<Map>().listen(
|
||||||
(entryMap) => localMediaCollection.add(ImageEntry.fromMap(entryMap)),
|
(entryMap) => localMediaCollection.add(ImageEntry.fromMap(entryMap)),
|
||||||
onDone: () async {
|
onDone: () async {
|
||||||
debugPrint('$runtimeType mediastore stream done, elapsed=${stopwatch.elapsed}');
|
debugPrint('$runtimeType mediastore stream done, elapsed=${_stopwatch.elapsed}');
|
||||||
localMediaCollection.updateSections(); // <50ms
|
localMediaCollection.updateSections(); // <50ms
|
||||||
// TODO reduce setup time until here
|
// TODO reduce setup time until here
|
||||||
localMediaCollection.updateAlbums(); // <50ms
|
localMediaCollection.updateAlbums(); // <50ms
|
||||||
|
@ -115,7 +115,7 @@ class _HomePageState extends State<HomePage> {
|
||||||
await localMediaCollection.catalogEntries(); // <50ms
|
await localMediaCollection.catalogEntries(); // <50ms
|
||||||
await localMediaCollection.loadAddresses(); // 350ms
|
await localMediaCollection.loadAddresses(); // 350ms
|
||||||
await localMediaCollection.locateEntries(); // <50ms
|
await localMediaCollection.locateEntries(); // <50ms
|
||||||
debugPrint('$runtimeType setup end, elapsed=${stopwatch.elapsed}');
|
debugPrint('$runtimeType setup end, elapsed=${_stopwatch.elapsed}');
|
||||||
},
|
},
|
||||||
onError: (error) => debugPrint('$runtimeType mediastore stream error=$error'),
|
onError: (error) => debugPrint('$runtimeType mediastore stream error=$error'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -8,11 +8,11 @@ import 'package:path/path.dart';
|
||||||
|
|
||||||
class ImageCollection with ChangeNotifier {
|
class ImageCollection with ChangeNotifier {
|
||||||
final List<ImageEntry> _rawEntries;
|
final List<ImageEntry> _rawEntries;
|
||||||
Map<dynamic, List<ImageEntry>> sections = Map();
|
Map<dynamic, List<ImageEntry>> sections = {};
|
||||||
GroupFactor groupFactor = GroupFactor.month;
|
GroupFactor groupFactor = GroupFactor.month;
|
||||||
SortFactor sortFactor = SortFactor.date;
|
SortFactor sortFactor = SortFactor.date;
|
||||||
List<String> sortedAlbums = List.unmodifiable(Iterable.empty());
|
List<String> sortedAlbums = List.unmodifiable(const Iterable.empty());
|
||||||
List<String> sortedTags = List.unmodifiable(Iterable.empty());
|
List<String> sortedTags = List.unmodifiable(const Iterable.empty());
|
||||||
|
|
||||||
ImageCollection({
|
ImageCollection({
|
||||||
@required List<ImageEntry> entries,
|
@required List<ImageEntry> entries,
|
||||||
|
@ -140,7 +140,7 @@ class ImageCollection with ChangeNotifier {
|
||||||
Future<void> catalogEntries() async {
|
Future<void> catalogEntries() async {
|
||||||
final start = DateTime.now();
|
final start = DateTime.now();
|
||||||
final uncataloguedEntries = _rawEntries.where((entry) => !entry.isCatalogued).toList();
|
final uncataloguedEntries = _rawEntries.where((entry) => !entry.isCatalogued).toList();
|
||||||
final newMetadata = List<CatalogMetadata>();
|
final newMetadata = <CatalogMetadata>[];
|
||||||
await Future.forEach<ImageEntry>(uncataloguedEntries, (entry) async {
|
await Future.forEach<ImageEntry>(uncataloguedEntries, (entry) async {
|
||||||
await entry.catalog();
|
await entry.catalog();
|
||||||
newMetadata.add(entry.catalogMetadata);
|
newMetadata.add(entry.catalogMetadata);
|
||||||
|
@ -153,7 +153,7 @@ class ImageCollection with ChangeNotifier {
|
||||||
Future<void> locateEntries() async {
|
Future<void> locateEntries() async {
|
||||||
final start = DateTime.now();
|
final start = DateTime.now();
|
||||||
final unlocatedEntries = _rawEntries.where((entry) => entry.hasGps && !entry.isLocated).toList();
|
final unlocatedEntries = _rawEntries.where((entry) => entry.hasGps && !entry.isLocated).toList();
|
||||||
final newAddresses = List<AddressDetails>();
|
final newAddresses = <AddressDetails>[];
|
||||||
await Future.forEach<ImageEntry>(unlocatedEntries, (entry) async {
|
await Future.forEach<ImageEntry>(unlocatedEntries, (entry) async {
|
||||||
await entry.locate();
|
await entry.locate();
|
||||||
newAddresses.add(entry.addressDetails);
|
newAddresses.add(entry.addressDetails);
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
import 'dart:collection';
|
|
||||||
|
|
||||||
import 'package:aves/model/image_file_service.dart';
|
import 'package:aves/model/image_file_service.dart';
|
||||||
import 'package:aves/model/image_metadata.dart';
|
import 'package:aves/model/image_metadata.dart';
|
||||||
import 'package:aves/model/metadata_service.dart';
|
import 'package:aves/model/metadata_service.dart';
|
||||||
|
@ -50,19 +48,19 @@ class ImageEntry {
|
||||||
|
|
||||||
factory ImageEntry.fromMap(Map map) {
|
factory ImageEntry.fromMap(Map map) {
|
||||||
return ImageEntry(
|
return ImageEntry(
|
||||||
uri: map['uri'],
|
uri: map['uri'] as String,
|
||||||
path: map['path'],
|
path: map['path'] as String,
|
||||||
contentId: map['contentId'],
|
contentId: map['contentId'] as int,
|
||||||
mimeType: map['mimeType'],
|
mimeType: map['mimeType'] as String,
|
||||||
width: map['width'],
|
width: map['width'] as int,
|
||||||
height: map['height'],
|
height: map['height'] as int,
|
||||||
orientationDegrees: map['orientationDegrees'],
|
orientationDegrees: map['orientationDegrees'] as int,
|
||||||
sizeBytes: map['sizeBytes'],
|
sizeBytes: map['sizeBytes'] as int,
|
||||||
title: map['title'],
|
title: map['title'] as String,
|
||||||
dateModifiedSecs: map['dateModifiedSecs'],
|
dateModifiedSecs: map['dateModifiedSecs'] as int,
|
||||||
sourceDateTakenMillis: map['sourceDateTakenMillis'],
|
sourceDateTakenMillis: map['sourceDateTakenMillis'] as int,
|
||||||
bucketDisplayName: map['bucketDisplayName'],
|
bucketDisplayName: map['bucketDisplayName'] as String,
|
||||||
durationMillis: map['durationMillis'],
|
durationMillis: map['durationMillis'] as int,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,13 +178,11 @@ class ImageEntry {
|
||||||
|
|
||||||
// admin area examples: Seoul, Geneva, null
|
// admin area examples: Seoul, Geneva, null
|
||||||
// locality examples: Mapo-gu, Geneva, Annecy
|
// locality examples: Mapo-gu, Geneva, Annecy
|
||||||
return LinkedHashSet.of(
|
return {
|
||||||
[
|
|
||||||
addressDetails.countryName,
|
addressDetails.countryName,
|
||||||
addressDetails.adminArea,
|
addressDetails.adminArea,
|
||||||
addressDetails.locality
|
addressDetails.locality
|
||||||
],
|
}.where((part) => part != null && part.isNotEmpty).join(', ');
|
||||||
).where((part) => part != null && part.isNotEmpty).join(', ');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool search(String query) {
|
bool search(String query) {
|
||||||
|
@ -203,13 +199,13 @@ class ImageEntry {
|
||||||
if (newFields.isEmpty) return false;
|
if (newFields.isEmpty) return false;
|
||||||
|
|
||||||
final uri = newFields['uri'];
|
final uri = newFields['uri'];
|
||||||
if (uri != null) this.uri = uri;
|
if (uri is String) this.uri = uri;
|
||||||
final path = newFields['path'];
|
final path = newFields['path'];
|
||||||
if (path != null) this.path = path;
|
if (path is String) this.path = path;
|
||||||
final contentId = newFields['contentId'];
|
final contentId = newFields['contentId'];
|
||||||
if (contentId != null) this.contentId = contentId;
|
if (contentId is int) this.contentId = contentId;
|
||||||
final title = newFields['title'];
|
final title = newFields['title'];
|
||||||
if (title != null) this.title = title;
|
if (title is String) this.title = title;
|
||||||
metadataChangeNotifier.notifyListeners();
|
metadataChangeNotifier.notifyListeners();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -223,11 +219,11 @@ class ImageEntry {
|
||||||
if (newFields.isEmpty) return false;
|
if (newFields.isEmpty) return false;
|
||||||
|
|
||||||
final width = newFields['width'];
|
final width = newFields['width'];
|
||||||
if (width != null) this.width = width;
|
if (width is int) this.width = width;
|
||||||
final height = newFields['height'];
|
final height = newFields['height'];
|
||||||
if (height != null) this.height = height;
|
if (height is int) this.height = height;
|
||||||
final orientationDegrees = newFields['orientationDegrees'];
|
final orientationDegrees = newFields['orientationDegrees'];
|
||||||
if (orientationDegrees != null) this.orientationDegrees = orientationDegrees;
|
if (orientationDegrees is int) this.orientationDegrees = orientationDegrees;
|
||||||
imageChangeNotifier.notifyListeners();
|
imageChangeNotifier.notifyListeners();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,7 @@ class ImageFileService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('rename failed with exception=${e.message}');
|
debugPrint('rename failed with exception=${e.message}');
|
||||||
}
|
}
|
||||||
return Map();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Map> rotate(ImageEntry entry, {@required bool clockwise}) async {
|
static Future<Map> rotate(ImageEntry entry, {@required bool clockwise}) async {
|
||||||
|
@ -79,6 +79,6 @@ class ImageFileService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('rotate failed with exception=${e.message}');
|
debugPrint('rotate failed with exception=${e.message}');
|
||||||
}
|
}
|
||||||
return Map();
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ class MetadataDb {
|
||||||
|
|
||||||
Future<String> get path async => join(await getDatabasesPath(), 'metadata.db');
|
Future<String> get path async => join(await getDatabasesPath(), 'metadata.db');
|
||||||
|
|
||||||
static final metadataTable = 'metadata';
|
static const metadataTable = 'metadata';
|
||||||
static final addressTable = 'address';
|
static const addressTable = 'address';
|
||||||
|
|
||||||
MetadataDb._private();
|
MetadataDb._private();
|
||||||
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ class MetadataService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('getAllMetadata failed with exception=${e.message}');
|
debugPrint('getAllMetadata failed with exception=${e.message}');
|
||||||
}
|
}
|
||||||
return Map();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<CatalogMetadata> getCatalogMetadata(ImageEntry entry) async {
|
static Future<CatalogMetadata> getCatalogMetadata(ImageEntry entry) async {
|
||||||
|
|
|
@ -5,12 +5,12 @@ import 'package:shared_preferences/shared_preferences.dart';
|
||||||
|
|
||||||
final Settings settings = Settings._private();
|
final Settings settings = Settings._private();
|
||||||
|
|
||||||
typedef void SettingsCallback(String key, dynamic oldValue, dynamic newValue);
|
typedef SettingsCallback = void Function(String key, dynamic oldValue, dynamic newValue);
|
||||||
|
|
||||||
class Settings {
|
class Settings {
|
||||||
static SharedPreferences prefs;
|
static SharedPreferences prefs;
|
||||||
|
|
||||||
ObserverList<SettingsCallback> _listeners = ObserverList<SettingsCallback>();
|
final ObserverList<SettingsCallback> _listeners = ObserverList<SettingsCallback>();
|
||||||
|
|
||||||
Settings._private();
|
Settings._private();
|
||||||
|
|
||||||
|
@ -32,7 +32,7 @@ class Settings {
|
||||||
debugPrint('$runtimeType notifyListeners key=$key, old=$oldValue, new=$newValue');
|
debugPrint('$runtimeType notifyListeners key=$key, old=$oldValue, new=$newValue');
|
||||||
if (_listeners != null) {
|
if (_listeners != null) {
|
||||||
final List<SettingsCallback> localListeners = _listeners.toList();
|
final List<SettingsCallback> localListeners = _listeners.toList();
|
||||||
for (SettingsCallback listener in localListeners) {
|
for (final listener in localListeners) {
|
||||||
try {
|
try {
|
||||||
if (_listeners.contains(listener)) {
|
if (_listeners.contains(listener)) {
|
||||||
listener(key, oldValue, newValue);
|
listener(key, oldValue, newValue);
|
||||||
|
@ -66,7 +66,7 @@ class Settings {
|
||||||
|
|
||||||
T getEnumOrDefault<T>(String key, T defaultValue, Iterable<T> values) {
|
T getEnumOrDefault<T>(String key, T defaultValue, Iterable<T> values) {
|
||||||
final valueString = prefs.getString(key);
|
final valueString = prefs.getString(key);
|
||||||
for (T element in values) {
|
for (final element in values) {
|
||||||
if (element.toString() == valueString) {
|
if (element.toString() == valueString) {
|
||||||
return element;
|
return element;
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ class AndroidAppService {
|
||||||
} on PlatformException catch (e) {
|
} on PlatformException catch (e) {
|
||||||
debugPrint('getAppNames failed with exception=${e.message}');
|
debugPrint('getAppNames failed with exception=${e.message}');
|
||||||
}
|
}
|
||||||
return Map();
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
static Future<Uint8List> getAppIcon(String packageName, int size) async {
|
static Future<Uint8List> getAppIcon(String packageName, int size) async {
|
||||||
|
|
|
@ -27,7 +27,7 @@ String _decimal2sexagesimal(final double dec) {
|
||||||
final List<int> minParts = _split(min);
|
final List<int> minParts = _split(min);
|
||||||
final int minFractionalPart = minParts[1];
|
final int minFractionalPart = minParts[1];
|
||||||
|
|
||||||
final double sec = (double.parse('0.$minFractionalPart') * 60);
|
final double sec = double.parse('0.$minFractionalPart') * 60;
|
||||||
|
|
||||||
return '$deg° ${min.floor()}′ ${_round(sec, decimals: 2).toStringAsFixed(2)}″';
|
return '$deg° ${min.floor()}′ ${_round(sec, decimals: 2).toStringAsFixed(2)}″';
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,7 @@ bool isAtSameDayAs(DateTime d1, DateTime d2) => isAtSameMonthAs(d1, d2) && d1.da
|
||||||
|
|
||||||
bool isToday(DateTime d) => isAtSameDayAs(d, DateTime.now());
|
bool isToday(DateTime d) => isAtSameDayAs(d, DateTime.now());
|
||||||
|
|
||||||
bool isYesterday(DateTime d) => isAtSameDayAs(d, DateTime.now().subtract(Duration(days: 1)));
|
bool isYesterday(DateTime d) => isAtSameDayAs(d, DateTime.now().subtract(const Duration(days: 1)));
|
||||||
|
|
||||||
bool isThisMonth(DateTime d) => isAtSameMonthAs(d, DateTime.now());
|
bool isThisMonth(DateTime d) => isAtSameMonthAs(d, DateTime.now());
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,9 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
padding: EdgeInsets.only(bottom: window.viewInsets.bottom),
|
padding: EdgeInsets.only(bottom: window.viewInsets.bottom),
|
||||||
children: [
|
children: [
|
||||||
DrawerHeader(
|
DrawerHeader(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).accentColor,
|
||||||
|
),
|
||||||
child: SafeArea(
|
child: SafeArea(
|
||||||
child: Column(
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
@ -28,18 +31,18 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
CircleAvatar(
|
CircleAvatar(
|
||||||
|
backgroundColor: Colors.white,
|
||||||
|
radius: 44,
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.only(top: 6.0),
|
padding: const EdgeInsets.only(top: 6.0),
|
||||||
child: SvgPicture.asset(
|
child: SvgPicture.asset(
|
||||||
'assets/aves_logo.svg',
|
'assets/aves_logo.svg',
|
||||||
width: 64,
|
width: 64,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
backgroundColor: Colors.white,
|
|
||||||
radius: 44,
|
|
||||||
),
|
),
|
||||||
SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
Text(
|
const Text(
|
||||||
'Aves',
|
'Aves',
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 44,
|
fontSize: 44,
|
||||||
|
@ -48,28 +51,28 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
Row(
|
Row(
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
children: [
|
children: [
|
||||||
Row(children: [
|
Row(children: [
|
||||||
Icon(Icons.photo_library),
|
Icon(Icons.photo_library),
|
||||||
SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text('${collection.imageCount}')
|
Text('${collection.imageCount}')
|
||||||
]),
|
]),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
Icon(Icons.video_library),
|
Icon(Icons.video_library),
|
||||||
SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text('${collection.videoCount}')
|
Text('${collection.videoCount}')
|
||||||
]),
|
]),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
Icon(Icons.photo_album),
|
Icon(Icons.photo_album),
|
||||||
SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text('${collection.albumCount}')
|
Text('${collection.albumCount}')
|
||||||
]),
|
]),
|
||||||
Row(children: [
|
Row(children: [
|
||||||
Icon(Icons.label),
|
Icon(Icons.label),
|
||||||
SizedBox(width: 4),
|
const SizedBox(width: 4),
|
||||||
Text('${collection.tagCount}')
|
Text('${collection.tagCount}')
|
||||||
]),
|
]),
|
||||||
],
|
],
|
||||||
|
@ -77,9 +80,6 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Theme.of(context).accentColor,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
_buildFilteredCollectionNavTile(
|
_buildFilteredCollectionNavTile(
|
||||||
context: context,
|
context: context,
|
||||||
|
@ -87,14 +87,14 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
title: 'Videos',
|
title: 'Videos',
|
||||||
filter: (entry) => entry.isVideo,
|
filter: (entry) => entry.isVideo,
|
||||||
),
|
),
|
||||||
Divider(),
|
const Divider(),
|
||||||
...albums.map((album) => _buildFilteredCollectionNavTile(
|
...albums.map((album) => _buildFilteredCollectionNavTile(
|
||||||
context: context,
|
context: context,
|
||||||
leading: IconUtils.getAlbumIcon(context, album) ?? Icon(Icons.photo_album),
|
leading: IconUtils.getAlbumIcon(context, album) ?? Icon(Icons.photo_album),
|
||||||
title: collection.getUniqueAlbumName(album, albums),
|
title: collection.getUniqueAlbumName(album, albums),
|
||||||
filter: (entry) => entry.directory == album,
|
filter: (entry) => entry.directory == album,
|
||||||
)),
|
)),
|
||||||
Divider(),
|
const Divider(),
|
||||||
...tags.map((tag) => _buildFilteredCollectionNavTile(
|
...tags.map((tag) => _buildFilteredCollectionNavTile(
|
||||||
context: context,
|
context: context,
|
||||||
leading: Icon(Icons.label),
|
leading: Icon(Icons.label),
|
||||||
|
@ -106,7 +106,7 @@ class AllCollectionDrawer extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_buildFilteredCollectionNavTile({BuildContext context, Widget leading, String title, bool Function(ImageEntry) filter}) {
|
Widget _buildFilteredCollectionNavTile({BuildContext context, Widget leading, String title, bool Function(ImageEntry) filter}) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
bottom: false,
|
||||||
|
|
|
@ -16,7 +16,7 @@ class AllCollectionPage extends StatelessWidget {
|
||||||
return ThumbnailCollection(
|
return ThumbnailCollection(
|
||||||
collection: collection,
|
collection: collection,
|
||||||
appBar: SliverAppBar(
|
appBar: SliverAppBar(
|
||||||
title: Text('All'),
|
title: const Text('All'),
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.search),
|
icon: Icon(Icons.search),
|
||||||
|
@ -35,7 +35,7 @@ class AllCollectionPage extends StatelessWidget {
|
||||||
value: AlbumAction.sortBySize,
|
value: AlbumAction.sortBySize,
|
||||||
child: MenuRow(text: 'Sort by size', checked: collection.sortFactor == SortFactor.size),
|
child: MenuRow(text: 'Sort by size', checked: collection.sortFactor == SortFactor.size),
|
||||||
),
|
),
|
||||||
PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
if (collection.sortFactor == SortFactor.date) ...[
|
if (collection.sortFactor == SortFactor.date) ...[
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: AlbumAction.groupByAlbum,
|
value: AlbumAction.groupByAlbum,
|
||||||
|
@ -49,14 +49,14 @@ class AllCollectionPage extends StatelessWidget {
|
||||||
value: AlbumAction.groupByDay,
|
value: AlbumAction.groupByDay,
|
||||||
child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day),
|
child: MenuRow(text: 'Group by day', checked: collection.groupFactor == GroupFactor.day),
|
||||||
),
|
),
|
||||||
PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
],
|
],
|
||||||
PopupMenuItem(
|
PopupMenuItem(
|
||||||
value: AlbumAction.debug,
|
value: AlbumAction.debug,
|
||||||
child: MenuRow(text: 'Debug', icon: Icons.whatshot),
|
child: MenuRow(text: 'Debug', icon: Icons.whatshot),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
onSelected: (action) => onActionSelected(context, action),
|
onSelected: (action) => _onActionSelected(context, action),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
floating: true,
|
floating: true,
|
||||||
|
@ -64,7 +64,7 @@ class AllCollectionPage extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
onActionSelected(BuildContext context, AlbumAction action) {
|
void _onActionSelected(BuildContext context, AlbumAction action) {
|
||||||
switch (action) {
|
switch (action) {
|
||||||
case AlbumAction.debug:
|
case AlbumAction.debug:
|
||||||
goToDebug(context);
|
goToDebug(context);
|
||||||
|
|
|
@ -43,14 +43,14 @@ class ImageSearchDelegate extends SearchDelegate<ImageEntry> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildSuggestions(BuildContext context) {
|
Widget buildSuggestions(BuildContext context) {
|
||||||
return SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildResults(BuildContext context) {
|
Widget buildResults(BuildContext context) {
|
||||||
if (query.isEmpty) {
|
if (query.isEmpty) {
|
||||||
showSuggestions(context);
|
showSuggestions(context);
|
||||||
return SizedBox.shrink();
|
return const SizedBox.shrink();
|
||||||
}
|
}
|
||||||
final lowerQuery = query.toLowerCase();
|
final lowerQuery = query.toLowerCase();
|
||||||
final matches = collection.sortedEntries.where((entry) => entry.search(lowerQuery)).toList();
|
final matches = collection.sortedEntries.where((entry) => entry.search(lowerQuery)).toList();
|
||||||
|
|
|
@ -7,13 +7,13 @@ class DaySectionHeader extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
|
|
||||||
DaySectionHeader({Key key, DateTime date})
|
DaySectionHeader({Key key, DateTime date})
|
||||||
: text = formatDate(date),
|
: text = _formatDate(date),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
static DateFormat md = DateFormat.MMMMd();
|
static DateFormat md = DateFormat.MMMMd();
|
||||||
static DateFormat ymd = DateFormat.yMMMMd();
|
static DateFormat ymd = DateFormat.yMMMMd();
|
||||||
|
|
||||||
static formatDate(DateTime date) {
|
static String _formatDate(DateTime date) {
|
||||||
if (isToday(date)) return 'Today';
|
if (isToday(date)) return 'Today';
|
||||||
if (isYesterday(date)) return 'Yesterday';
|
if (isYesterday(date)) return 'Yesterday';
|
||||||
if (isThisYear(date)) return md.format(date);
|
if (isThisYear(date)) return md.format(date);
|
||||||
|
@ -30,13 +30,13 @@ class MonthSectionHeader extends StatelessWidget {
|
||||||
final String text;
|
final String text;
|
||||||
|
|
||||||
MonthSectionHeader({Key key, DateTime date})
|
MonthSectionHeader({Key key, DateTime date})
|
||||||
: text = formatDate(date),
|
: text = _formatDate(date),
|
||||||
super(key: key);
|
super(key: key);
|
||||||
|
|
||||||
static DateFormat m = DateFormat.MMMM();
|
static DateFormat m = DateFormat.MMMM();
|
||||||
static DateFormat ym = DateFormat.yMMMM();
|
static DateFormat ym = DateFormat.yMMMM();
|
||||||
|
|
||||||
static formatDate(DateTime date) {
|
static String _formatDate(DateTime date) {
|
||||||
if (isThisMonth(date)) return 'This month';
|
if (isThisMonth(date)) return 'This month';
|
||||||
if (isThisYear(date)) return m.format(date);
|
if (isThisYear(date)) return m.format(date);
|
||||||
return ym.format(date);
|
return ym.format(date);
|
||||||
|
@ -64,7 +64,7 @@ class TitleSectionHeader extends StatelessWidget {
|
||||||
fontFamily: 'Concourse Caps',
|
fontFamily: 'Concourse Caps',
|
||||||
shadows: [
|
shadows: [
|
||||||
Shadow(
|
Shadow(
|
||||||
offset: Offset(0, 2),
|
offset: const Offset(0, 2),
|
||||||
blurRadius: 3,
|
blurRadius: 3,
|
||||||
color: Colors.grey[900],
|
color: Colors.grey[900],
|
||||||
),
|
),
|
||||||
|
@ -74,12 +74,12 @@ class TitleSectionHeader extends StatelessWidget {
|
||||||
outlineWidth: 2,
|
outlineWidth: 2,
|
||||||
);
|
);
|
||||||
return Container(
|
return Container(
|
||||||
padding: EdgeInsets.all(16),
|
padding: const EdgeInsets.all(16),
|
||||||
child: leading != null
|
child: leading != null
|
||||||
? Row(
|
? Row(
|
||||||
children: [
|
children: [
|
||||||
leading,
|
leading,
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
text,
|
text,
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
|
@ -13,7 +13,7 @@ class ThumbnailCollection extends AnimatedWidget {
|
||||||
final ImageCollection collection;
|
final ImageCollection collection;
|
||||||
final Widget appBar;
|
final Widget appBar;
|
||||||
|
|
||||||
ThumbnailCollection({
|
const ThumbnailCollection({
|
||||||
Key key,
|
Key key,
|
||||||
this.collection,
|
this.collection,
|
||||||
this.appBar,
|
this.appBar,
|
||||||
|
@ -65,6 +65,12 @@ class ThumbnailCollectionContent extends StatelessWidget {
|
||||||
child: Selector<MediaQueryData, double>(
|
child: Selector<MediaQueryData, double>(
|
||||||
selector: (c, mq) => mq.viewInsets.bottom,
|
selector: (c, mq) => mq.viewInsets.bottom,
|
||||||
builder: (c, mqViewInsetsBottom, child) => DraggableScrollbar.arrows(
|
builder: (c, mqViewInsetsBottom, child) => DraggableScrollbar.arrows(
|
||||||
|
controller: _scrollController,
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
// top padding to adjust scroll thumb
|
||||||
|
top: topPadding,
|
||||||
|
bottom: mqViewInsetsBottom,
|
||||||
|
),
|
||||||
child: CustomScrollView(
|
child: CustomScrollView(
|
||||||
controller: _scrollController,
|
controller: _scrollController,
|
||||||
slivers: [
|
slivers: [
|
||||||
|
@ -89,12 +95,6 @@ class ThumbnailCollectionContent extends StatelessWidget {
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
controller: _scrollController,
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
// top padding to adjust scroll thumb
|
|
||||||
top: topPadding,
|
|
||||||
bottom: mqViewInsetsBottom,
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -117,7 +117,7 @@ class SectionSliver extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final columnCount = 4;
|
const columnCount = 4;
|
||||||
return SliverStickyHeader(
|
return SliverStickyHeader(
|
||||||
header: SectionHeader(
|
header: SectionHeader(
|
||||||
collection: collection,
|
collection: collection,
|
||||||
|
@ -143,7 +143,7 @@ class SectionSliver extends StatelessWidget {
|
||||||
addAutomaticKeepAlives: false,
|
addAutomaticKeepAlives: false,
|
||||||
addRepaintBoundaries: true,
|
addRepaintBoundaries: true,
|
||||||
),
|
),
|
||||||
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
|
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||||
crossAxisCount: columnCount,
|
crossAxisCount: columnCount,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -151,7 +151,7 @@ class SectionSliver extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_showFullscreen(BuildContext context, ImageEntry entry) {
|
void _showFullscreen(BuildContext context, ImageEntry entry) {
|
||||||
Navigator.push(
|
Navigator.push(
|
||||||
context,
|
context,
|
||||||
MaterialPageRoute(
|
MaterialPageRoute(
|
||||||
|
@ -178,11 +178,11 @@ class SectionHeader extends StatelessWidget {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget header = SizedBox.shrink();
|
Widget header = const SizedBox.shrink();
|
||||||
if (collection.sortFactor == SortFactor.date) {
|
if (collection.sortFactor == SortFactor.date) {
|
||||||
switch (collection.groupFactor) {
|
switch (collection.groupFactor) {
|
||||||
case GroupFactor.album:
|
case GroupFactor.album:
|
||||||
Widget albumIcon = IconUtils.getAlbumIcon(context, sectionKey);
|
Widget albumIcon = IconUtils.getAlbumIcon(context, sectionKey as String);
|
||||||
if (albumIcon != null) {
|
if (albumIcon != null) {
|
||||||
albumIcon = Material(
|
albumIcon = Material(
|
||||||
type: MaterialType.circle,
|
type: MaterialType.circle,
|
||||||
|
@ -194,14 +194,14 @@ class SectionHeader extends StatelessWidget {
|
||||||
}
|
}
|
||||||
header = TitleSectionHeader(
|
header = TitleSectionHeader(
|
||||||
leading: albumIcon,
|
leading: albumIcon,
|
||||||
title: collection.getUniqueAlbumName(sectionKey, sections.keys.cast<String>()),
|
title: collection.getUniqueAlbumName(sectionKey as String, sections.keys.cast<String>()),
|
||||||
);
|
);
|
||||||
break;
|
break;
|
||||||
case GroupFactor.month:
|
case GroupFactor.month:
|
||||||
header = MonthSectionHeader(date: sectionKey);
|
header = MonthSectionHeader(date: sectionKey as DateTime);
|
||||||
break;
|
break;
|
||||||
case GroupFactor.day:
|
case GroupFactor.day:
|
||||||
header = DaySectionHeader(date: sectionKey);
|
header = DaySectionHeader(date: sectionKey as DateTime);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -42,7 +42,7 @@ class AppIconState extends State<AppIcon> {
|
||||||
width: widget.size,
|
width: widget.size,
|
||||||
height: widget.size,
|
height: widget.size,
|
||||||
)
|
)
|
||||||
: SizedBox.shrink();
|
: const SizedBox.shrink();
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/widgets.dart';
|
||||||
class FakeAppBar extends StatelessWidget with PreferredSizeWidget {
|
class FakeAppBar extends StatelessWidget with PreferredSizeWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(child: SizedBox.shrink());
|
return const SafeArea(child: SizedBox.shrink());
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -61,10 +61,10 @@ class OverlayIcon extends StatelessWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return Container(
|
return Container(
|
||||||
margin: EdgeInsets.all(1),
|
margin: const EdgeInsets.all(1),
|
||||||
padding: text != null ? EdgeInsets.only(right: iconSize / 4) : null,
|
padding: text != null ? EdgeInsets.only(right: iconSize / 4) : null,
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Color(0xBB000000),
|
color: const Color(0xBB000000),
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(iconSize),
|
Radius.circular(iconSize),
|
||||||
),
|
),
|
||||||
|
@ -78,7 +78,7 @@ class OverlayIcon extends StatelessWidget {
|
||||||
size: iconSize,
|
size: iconSize,
|
||||||
),
|
),
|
||||||
if (text != null) ...[
|
if (text != null) ...[
|
||||||
SizedBox(width: 2),
|
const SizedBox(width: 2),
|
||||||
Text(text),
|
Text(text),
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
|
|
|
@ -40,33 +40,33 @@ class ImagePreviewState extends State<ImagePreview> with AfterInitMixin {
|
||||||
entry.imageChangeNotifier,
|
entry.imageChangeNotifier,
|
||||||
entry.metadataChangeNotifier
|
entry.metadataChangeNotifier
|
||||||
]);
|
]);
|
||||||
_entryChangeNotifier.addListener(onEntryChange);
|
_entryChangeNotifier.addListener(_onEntryChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didInitState() {
|
void didInitState() {
|
||||||
_devicePixelRatio = Provider.of<MediaQueryData>(context, listen: false).devicePixelRatio;
|
_devicePixelRatio = Provider.of<MediaQueryData>(context, listen: false).devicePixelRatio;
|
||||||
initByteLoader();
|
_initByteLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(ImagePreview old) {
|
void didUpdateWidget(ImagePreview old) {
|
||||||
super.didUpdateWidget(old);
|
super.didUpdateWidget(old);
|
||||||
if (widget.width == old.width && widget.height == old.height && uri == old.entry.uri && widget.entry.width == old.entry.width && widget.entry.height == old.entry.height && widget.entry.orientationDegrees == old.entry.orientationDegrees) return;
|
if (widget.width == old.width && widget.height == old.height && uri == old.entry.uri && widget.entry.width == old.entry.width && widget.entry.height == old.entry.height && widget.entry.orientationDegrees == old.entry.orientationDegrees) return;
|
||||||
initByteLoader();
|
_initByteLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
initByteLoader() {
|
void _initByteLoader() {
|
||||||
final width = (widget.width * _devicePixelRatio).round();
|
final width = (widget.width * _devicePixelRatio).round();
|
||||||
final height = (widget.height * _devicePixelRatio).round();
|
final height = (widget.height * _devicePixelRatio).round();
|
||||||
_byteLoader = ImageFileService.getImageBytes(widget.entry, width, height);
|
_byteLoader = ImageFileService.getImageBytes(widget.entry, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
onEntryChange() => setState(() => initByteLoader());
|
void _onEntryChange() => setState(() => _initByteLoader());
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_entryChangeNotifier.removeListener(onEntryChange);
|
_entryChangeNotifier.removeListener(_onEntryChange);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,11 +21,11 @@ class MenuRow extends StatelessWidget {
|
||||||
opacity: checked ? 1 : 0,
|
opacity: checked ? 1 : 0,
|
||||||
child: Icon(Icons.done),
|
child: Icon(Icons.done),
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
if (icon != null) ...[
|
if (icon != null) ...[
|
||||||
Icon(icon),
|
Icon(icon),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
],
|
],
|
||||||
Text(text),
|
Text(text),
|
||||||
],
|
],
|
||||||
|
|
|
@ -39,48 +39,48 @@ class DebugPageState extends State<DebugPage> {
|
||||||
return MediaQueryDataProvider(
|
return MediaQueryDataProvider(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text('Info'),
|
title: const Text('Info'),
|
||||||
),
|
),
|
||||||
body: Column(
|
body: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Text('Paths'),
|
const Text('Paths'),
|
||||||
Text('DCIM path: ${androidFileUtils.dcimPath}'),
|
Text('DCIM path: ${androidFileUtils.dcimPath}'),
|
||||||
Text('pictures path: ${androidFileUtils.picturesPath}'),
|
Text('pictures path: ${androidFileUtils.picturesPath}'),
|
||||||
Divider(),
|
const Divider(),
|
||||||
Text('Settings'),
|
const Text('Settings'),
|
||||||
Text('collectionGroupFactor: ${settings.collectionGroupFactor}'),
|
Text('collectionGroupFactor: ${settings.collectionGroupFactor}'),
|
||||||
Text('collectionSortFactor: ${settings.collectionSortFactor}'),
|
Text('collectionSortFactor: ${settings.collectionSortFactor}'),
|
||||||
Text('infoMapZoom: ${settings.infoMapZoom}'),
|
Text('infoMapZoom: ${settings.infoMapZoom}'),
|
||||||
Divider(),
|
const Divider(),
|
||||||
Text('Entries: ${entries.length}'),
|
Text('Entries: ${entries.length}'),
|
||||||
...byMimeTypes.keys.map((mimeType) => Text('- $mimeType: ${byMimeTypes[mimeType].length}')),
|
...byMimeTypes.keys.map((mimeType) => Text('- $mimeType: ${byMimeTypes[mimeType].length}')),
|
||||||
Text('Catalogued: ${catalogued.length}'),
|
Text('Catalogued: ${catalogued.length}'),
|
||||||
Text('With GPS: ${withGps.length}'),
|
Text('With GPS: ${withGps.length}'),
|
||||||
Text('With address: ${located.length}'),
|
Text('With address: ${located.length}'),
|
||||||
Divider(),
|
const Divider(),
|
||||||
RaisedButton(
|
RaisedButton(
|
||||||
onPressed: () => metadataDb.reset(),
|
onPressed: () => metadataDb.reset(),
|
||||||
child: Text('Reset DB'),
|
child: const Text('Reset DB'),
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _dbMetadataLoader,
|
future: _dbMetadataLoader,
|
||||||
builder: (futureContext, AsyncSnapshot<List<CatalogMetadata>> snapshot) {
|
builder: (futureContext, AsyncSnapshot<List<CatalogMetadata>> snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error);
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||||
return Text('DB metadata rows: ${snapshot.data.length}');
|
return Text('DB metadata rows: ${snapshot.data.length}');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
FutureBuilder(
|
FutureBuilder(
|
||||||
future: _dbAddressLoader,
|
future: _dbAddressLoader,
|
||||||
builder: (futureContext, AsyncSnapshot<List<AddressDetails>> snapshot) {
|
builder: (futureContext, AsyncSnapshot<List<AddressDetails>> snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error);
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink();
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||||
return Text('DB address rows: ${snapshot.data.length}');
|
return Text('DB address rows: ${snapshot.data.length}');
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
Divider(),
|
const Divider(),
|
||||||
Text('Time dilation'),
|
const Text('Time dilation'),
|
||||||
Slider(
|
Slider(
|
||||||
value: timeDilation,
|
value: timeDilation,
|
||||||
onChanged: (v) => setState(() => timeDilation = v),
|
onChanged: (v) => setState(() => timeDilation = v),
|
||||||
|
|
|
@ -61,14 +61,14 @@ class FullscreenActionDelegate {
|
||||||
void _showFeedback(BuildContext context, String message) {
|
void _showFeedback(BuildContext context, String message) {
|
||||||
Flushbar(
|
Flushbar(
|
||||||
message: message,
|
message: message,
|
||||||
margin: EdgeInsets.all(8),
|
margin: const EdgeInsets.all(8),
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
borderColor: Colors.white30,
|
borderColor: Colors.white30,
|
||||||
borderWidth: 0.5,
|
borderWidth: 0.5,
|
||||||
duration: Duration(seconds: 2),
|
duration: const Duration(seconds: 2),
|
||||||
flushbarPosition: FlushbarPosition.TOP,
|
flushbarPosition: FlushbarPosition.TOP,
|
||||||
animationDuration: Duration(milliseconds: 600),
|
animationDuration: const Duration(milliseconds: 600),
|
||||||
)..show(context);
|
).show(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _print(ImageEntry entry) async {
|
Future<void> _print(ImageEntry entry) async {
|
||||||
|
@ -94,15 +94,15 @@ class FullscreenActionDelegate {
|
||||||
context: context,
|
context: context,
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return AlertDialog(
|
return AlertDialog(
|
||||||
content: Text('Are you sure?'),
|
content: const Text('Are you sure?'),
|
||||||
actions: [
|
actions: [
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
child: Text('CANCEL'),
|
child: const Text('CANCEL'),
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () => Navigator.pop(context, true),
|
onPressed: () => Navigator.pop(context, true),
|
||||||
child: Text('DELETE'),
|
child: const Text('DELETE'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -130,11 +130,11 @@ class FullscreenActionDelegate {
|
||||||
actions: [
|
actions: [
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () => Navigator.pop(context),
|
onPressed: () => Navigator.pop(context),
|
||||||
child: Text('CANCEL'),
|
child: const Text('CANCEL'),
|
||||||
),
|
),
|
||||||
FlatButton(
|
FlatButton(
|
||||||
onPressed: () => Navigator.pop(context, controller.text),
|
onPressed: () => Navigator.pop(context, controller.text),
|
||||||
child: Text('APPLY'),
|
child: const Text('APPLY'),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
|
|
@ -61,13 +61,13 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
||||||
bool _isInitialScale = true;
|
bool _isInitialScale = true;
|
||||||
int _currentHorizontalPage, _currentVerticalPage = imagePage;
|
int _currentHorizontalPage, _currentVerticalPage = imagePage;
|
||||||
PageController _horizontalPager, _verticalPager;
|
PageController _horizontalPager, _verticalPager;
|
||||||
ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
|
final ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
|
||||||
AnimationController _overlayAnimationController;
|
AnimationController _overlayAnimationController;
|
||||||
Animation<double> _topOverlayScale;
|
Animation<double> _topOverlayScale;
|
||||||
Animation<Offset> _bottomOverlayOffset;
|
Animation<Offset> _bottomOverlayOffset;
|
||||||
EdgeInsets _frozenViewInsets, _frozenViewPadding;
|
EdgeInsets _frozenViewInsets, _frozenViewPadding;
|
||||||
FullscreenActionDelegate _actionDelegate;
|
FullscreenActionDelegate _actionDelegate;
|
||||||
List<Tuple2<String, VideoPlayerController>> _videoControllers = List();
|
final List<Tuple2<String, VideoPlayerController>> _videoControllers = [];
|
||||||
|
|
||||||
ImageCollection get collection => widget.collection;
|
ImageCollection get collection => widget.collection;
|
||||||
|
|
||||||
|
@ -85,14 +85,14 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
||||||
_horizontalPager = PageController(initialPage: _currentHorizontalPage);
|
_horizontalPager = PageController(initialPage: _currentHorizontalPage);
|
||||||
_verticalPager = PageController(initialPage: _currentVerticalPage);
|
_verticalPager = PageController(initialPage: _currentVerticalPage);
|
||||||
_overlayAnimationController = AnimationController(
|
_overlayAnimationController = AnimationController(
|
||||||
duration: Duration(milliseconds: 400),
|
duration: const Duration(milliseconds: 400),
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
_topOverlayScale = CurvedAnimation(
|
_topOverlayScale = CurvedAnimation(
|
||||||
parent: _overlayAnimationController,
|
parent: _overlayAnimationController,
|
||||||
curve: Curves.easeOutQuart,
|
curve: Curves.easeOutQuart,
|
||||||
);
|
);
|
||||||
_bottomOverlayOffset = Tween(begin: Offset(0, 1), end: Offset(0, 0)).animate(CurvedAnimation(
|
_bottomOverlayOffset = Tween(begin: const Offset(0, 1), end: const Offset(0, 0)).animate(CurvedAnimation(
|
||||||
parent: _overlayAnimationController,
|
parent: _overlayAnimationController,
|
||||||
curve: Curves.easeOutQuart,
|
curve: Curves.easeOutQuart,
|
||||||
));
|
));
|
||||||
|
@ -105,7 +105,7 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
||||||
_initOverlay();
|
_initOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
_initOverlay() async {
|
Future<void> _initOverlay() async {
|
||||||
// wait for MaterialPageRoute.transitionDuration
|
// wait for MaterialPageRoute.transitionDuration
|
||||||
// to show overlay after hero animation is complete
|
// to show overlay after hero animation is complete
|
||||||
await Future.delayed(Duration(milliseconds: (300 * timeDilation).toInt()));
|
await Future.delayed(Duration(milliseconds: (300 * timeDilation).toInt()));
|
||||||
|
@ -136,10 +136,10 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
||||||
PageView(
|
PageView(
|
||||||
scrollDirection: Axis.vertical,
|
scrollDirection: Axis.vertical,
|
||||||
controller: _verticalPager,
|
controller: _verticalPager,
|
||||||
physics: _isInitialScale ? PageScrollPhysics() : NeverScrollableScrollPhysics(),
|
physics: _isInitialScale ? const PageScrollPhysics() : const NeverScrollableScrollPhysics(),
|
||||||
onPageChanged: _onVerticalPageChanged,
|
onPageChanged: _onVerticalPageChanged,
|
||||||
children: [
|
children: [
|
||||||
SizedBox(),
|
const SizedBox(),
|
||||||
Container(
|
Container(
|
||||||
color: Colors.black,
|
color: Colors.black,
|
||||||
child: ImagePage(
|
child: ImagePage(
|
||||||
|
@ -208,12 +208,12 @@ class FullscreenBodyState extends State<FullscreenBody> with SingleTickerProvide
|
||||||
Future<void> _goToVerticalPage(int page) {
|
Future<void> _goToVerticalPage(int page) {
|
||||||
return _verticalPager.animateToPage(
|
return _verticalPager.animateToPage(
|
||||||
page,
|
page,
|
||||||
duration: Duration(milliseconds: 350),
|
duration: const Duration(milliseconds: 350),
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _onVerticalPageChanged(page) {
|
void _onVerticalPageChanged(int page) {
|
||||||
setState(() => _currentVerticalPage = page);
|
setState(() => _currentVerticalPage = page);
|
||||||
if (_currentVerticalPage == transitionPage) {
|
if (_currentVerticalPage == transitionPage) {
|
||||||
_onLeave();
|
_onLeave();
|
||||||
|
|
|
@ -51,7 +51,7 @@ class ImagePageState extends State<ImagePage> with AutomaticKeepAliveClientMixin
|
||||||
entry: entry,
|
entry: entry,
|
||||||
controller: videoController,
|
controller: videoController,
|
||||||
)
|
)
|
||||||
: SizedBox(),
|
: const SizedBox(),
|
||||||
childSize: mqSize,
|
childSize: mqSize,
|
||||||
// no hero as most videos fullscreen image is different from its thumbnail
|
// no hero as most videos fullscreen image is different from its thumbnail
|
||||||
minScale: PhotoViewComputedScale.contained,
|
minScale: PhotoViewComputedScale.contained,
|
||||||
|
@ -70,14 +70,14 @@ class ImagePageState extends State<ImagePage> with AutomaticKeepAliveClientMixin
|
||||||
onTapUp: (tapContext, details, value) => widget.onTap?.call(),
|
onTapUp: (tapContext, details, value) => widget.onTap?.call(),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
loadingChild: Center(
|
loadingChild: const Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
backgroundDecoration: BoxDecoration(color: Colors.transparent),
|
backgroundDecoration: BoxDecoration(color: Colors.transparent),
|
||||||
pageController: widget.pageController,
|
pageController: widget.pageController,
|
||||||
onPageChanged: widget.onPageChanged,
|
onPageChanged: widget.onPageChanged,
|
||||||
scaleStateChangedCallback: widget.onScaleChanged,
|
scaleStateChangedCallback: widget.onScaleChanged,
|
||||||
scrollPhysics: BouncingScrollPhysics(),
|
scrollPhysics: const BouncingScrollPhysics(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,7 +119,7 @@ class SectionRow extends StatelessWidget {
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Text(
|
child: Text(
|
||||||
title,
|
title,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 20,
|
fontSize: 20,
|
||||||
fontFamily: 'Concourse Caps',
|
fontFamily: 'Concourse Caps',
|
||||||
),
|
),
|
||||||
|
@ -142,9 +142,9 @@ class InfoRow extends StatelessWidget {
|
||||||
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
child: RichText(
|
child: RichText(
|
||||||
text: TextSpan(
|
text: TextSpan(
|
||||||
style: TextStyle(fontFamily: 'Concourse'),
|
style: const TextStyle(fontFamily: 'Concourse'),
|
||||||
children: [
|
children: [
|
||||||
TextSpan(text: '$label ', style: TextStyle(color: Colors.white70)),
|
TextSpan(text: '$label ', style: const TextStyle(color: Colors.white70)),
|
||||||
TextSpan(text: value),
|
TextSpan(text: value),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -7,7 +7,7 @@ import 'package:google_maps_flutter/google_maps_flutter.dart';
|
||||||
|
|
||||||
class LocationSection extends AnimatedWidget {
|
class LocationSection extends AnimatedWidget {
|
||||||
final ImageEntry entry;
|
final ImageEntry entry;
|
||||||
final showTitle;
|
final bool showTitle;
|
||||||
|
|
||||||
LocationSection({
|
LocationSection({
|
||||||
Key key,
|
Key key,
|
||||||
|
@ -23,12 +23,12 @@ class LocationSection extends AnimatedWidget {
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return !entry.hasGps
|
return !entry.hasGps
|
||||||
? SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Column(
|
: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
if (showTitle)
|
if (showTitle)
|
||||||
Padding(
|
const Padding(
|
||||||
padding: EdgeInsets.only(bottom: 8),
|
padding: EdgeInsets.only(bottom: 8),
|
||||||
child: SectionRow('Location'),
|
child: SectionRow('Location'),
|
||||||
),
|
),
|
||||||
|
@ -43,7 +43,7 @@ class LocationSection extends AnimatedWidget {
|
||||||
),
|
),
|
||||||
if (entry.isLocated)
|
if (entry.isLocated)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.only(top: 8),
|
padding: const EdgeInsets.only(top: 8),
|
||||||
child: InfoRow('Address', entry.addressDetails.addressLine),
|
child: InfoRow('Address', entry.addressDetails.addressLine),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
@ -90,7 +90,7 @@ class ImageMapState extends State<ImageMap> with AutomaticKeepAliveClientMixin {
|
||||||
child: SizedBox(
|
child: SizedBox(
|
||||||
height: 200,
|
height: 200,
|
||||||
child: ClipRRect(
|
child: ClipRRect(
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: const BorderRadius.all(
|
||||||
Radius.circular(16),
|
Radius.circular(16),
|
||||||
),
|
),
|
||||||
child: GoogleMap(
|
child: GoogleMap(
|
||||||
|
@ -104,18 +104,18 @@ class ImageMapState extends State<ImageMap> with AutomaticKeepAliveClientMixin {
|
||||||
zoomGesturesEnabled: false,
|
zoomGesturesEnabled: false,
|
||||||
tiltGesturesEnabled: false,
|
tiltGesturesEnabled: false,
|
||||||
myLocationButtonEnabled: false,
|
myLocationButtonEnabled: false,
|
||||||
markers: [
|
markers: {
|
||||||
Marker(
|
Marker(
|
||||||
markerId: MarkerId(widget.markerId),
|
markerId: MarkerId(widget.markerId),
|
||||||
icon: BitmapDescriptor.defaultMarkerWithHue(accentHue),
|
icon: BitmapDescriptor.defaultMarkerWithHue(accentHue),
|
||||||
position: widget.latLng,
|
position: widget.latLng,
|
||||||
)
|
)
|
||||||
].toSet(),
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Column(children: [
|
Column(children: [
|
||||||
IconButton(
|
IconButton(
|
||||||
icon: Icon(Icons.add),
|
icon: Icon(Icons.add),
|
||||||
|
@ -137,7 +137,7 @@ class ImageMapState extends State<ImageMap> with AutomaticKeepAliveClientMixin {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_zoomBy(double amount) {
|
void _zoomBy(double amount) {
|
||||||
settings.infoMapZoom += amount;
|
settings.infoMapZoom += amount;
|
||||||
_controller.animateCamera(CameraUpdate.zoomBy(amount));
|
_controller.animateCamera(CameraUpdate.zoomBy(amount));
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,16 +23,16 @@ class MetadataSectionState extends State<MetadataSection> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
initMetadataLoader();
|
_initMetadataLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(MetadataSection oldWidget) {
|
void didUpdateWidget(MetadataSection oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
initMetadataLoader();
|
_initMetadataLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
initMetadataLoader() async {
|
Future<void> _initMetadataLoader() async {
|
||||||
_metadataLoader = MetadataService.getAllMetadata(widget.entry);
|
_metadataLoader = MetadataService.getAllMetadata(widget.entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -43,9 +43,9 @@ class MetadataSectionState extends State<MetadataSection> {
|
||||||
builder: (c, mqWidth, child) => FutureBuilder(
|
builder: (c, mqWidth, child) => FutureBuilder(
|
||||||
future: _metadataLoader,
|
future: _metadataLoader,
|
||||||
builder: (futureContext, AsyncSnapshot<Map> snapshot) {
|
builder: (futureContext, AsyncSnapshot<Map> snapshot) {
|
||||||
if (snapshot.hasError) return Text(snapshot.error);
|
|
||||||
if (snapshot.connectionState != ConnectionState.done) return SizedBox.shrink();
|
|
||||||
final metadataMap = snapshot.data.cast<String, Map>();
|
final metadataMap = snapshot.data.cast<String, Map>();
|
||||||
|
if (snapshot.hasError) return Text(snapshot.error.toString());
|
||||||
|
if (snapshot.connectionState != ConnectionState.done) return const SizedBox.shrink();
|
||||||
final directoryNames = metadataMap.keys.toList()..sort();
|
final directoryNames = metadataMap.keys.toList()..sort();
|
||||||
|
|
||||||
Widget content;
|
Widget content;
|
||||||
|
@ -68,7 +68,7 @@ class MetadataSectionState extends State<MetadataSection> {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
Expanded(child: getMetadataColumn(metadataMap, first)),
|
Expanded(child: getMetadataColumn(metadataMap, first)),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
Expanded(child: getMetadataColumn(metadataMap, second)),
|
Expanded(child: getMetadataColumn(metadataMap, second)),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -79,7 +79,7 @@ class MetadataSectionState extends State<MetadataSection> {
|
||||||
return Column(
|
return Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||||
children: [
|
children: [
|
||||||
SectionRow('Metadata'),
|
const SectionRow('Metadata'),
|
||||||
content,
|
content,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -98,19 +98,19 @@ class MetadataSectionState extends State<MetadataSection> {
|
||||||
return [
|
return [
|
||||||
if (directoryName.isNotEmpty)
|
if (directoryName.isNotEmpty)
|
||||||
Padding(
|
Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 4.0),
|
padding: const EdgeInsets.symmetric(vertical: 4.0),
|
||||||
child: Text(directoryName,
|
child: Text(directoryName,
|
||||||
style: TextStyle(
|
style: const TextStyle(
|
||||||
fontSize: 18,
|
fontSize: 18,
|
||||||
fontFamily: 'Concourse Caps',
|
fontFamily: 'Concourse Caps',
|
||||||
)),
|
)),
|
||||||
),
|
),
|
||||||
...tagKeys.map((tagKey) {
|
...tagKeys.map((tagKey) {
|
||||||
final value = directory[tagKey] as String;
|
final value = directory[tagKey] as String;
|
||||||
if (value == null || value.isEmpty) return SizedBox.shrink();
|
if (value == null || value.isEmpty) return const SizedBox.shrink();
|
||||||
return InfoRow(tagKey, value.length > maxValueLength ? '${value.substring(0, maxValueLength)}…' : value);
|
return InfoRow(tagKey, value.length > maxValueLength ? '${value.substring(0, maxValueLength)}…' : value);
|
||||||
}),
|
}),
|
||||||
SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
];
|
];
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
|
|
|
@ -18,15 +18,15 @@ class XmpTagSection extends AnimatedWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final tags = entry.xmpSubjects;
|
final tags = entry.xmpSubjects;
|
||||||
return tags.isEmpty
|
return tags.isEmpty
|
||||||
? SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: Column(
|
: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
children: [
|
children: [
|
||||||
SectionRow('XMP Tags'),
|
const SectionRow('XMP Tags'),
|
||||||
Wrap(
|
Wrap(
|
||||||
children: tags
|
children: tags
|
||||||
.map((tag) => Padding(
|
.map((tag) => Padding(
|
||||||
padding: EdgeInsets.symmetric(horizontal: 4.0),
|
padding: const EdgeInsets.symmetric(horizontal: 4.0),
|
||||||
child: ActionChip(
|
child: ActionChip(
|
||||||
label: Text(tag),
|
label: Text(tag),
|
||||||
onPressed: () => Navigator.push(
|
onPressed: () => Navigator.push(
|
||||||
|
|
|
@ -45,16 +45,16 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
initDetailLoader();
|
_initDetailLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(FullscreenBottomOverlay oldWidget) {
|
void didUpdateWidget(FullscreenBottomOverlay oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
initDetailLoader();
|
_initDetailLoader();
|
||||||
}
|
}
|
||||||
|
|
||||||
initDetailLoader() {
|
void _initDetailLoader() {
|
||||||
_detailLoader = MetadataService.getOverlayMetadata(entry);
|
_detailLoader = MetadataService.getOverlayMetadata(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
|
||||||
_lastEntry = entry;
|
_lastEntry = entry;
|
||||||
}
|
}
|
||||||
return _lastEntry == null
|
return _lastEntry == null
|
||||||
? SizedBox.shrink()
|
? const SizedBox.shrink()
|
||||||
: _FullscreenBottomOverlayContent(
|
: _FullscreenBottomOverlayContent(
|
||||||
entry: _lastEntry,
|
entry: _lastEntry,
|
||||||
details: _lastDetails,
|
details: _lastDetails,
|
||||||
|
@ -115,7 +115,7 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
|
||||||
final String position;
|
final String position;
|
||||||
final double maxWidth;
|
final double maxWidth;
|
||||||
|
|
||||||
_FullscreenBottomOverlayContent({
|
const _FullscreenBottomOverlayContent({
|
||||||
this.entry,
|
this.entry,
|
||||||
this.details,
|
this.details,
|
||||||
this.position,
|
this.position,
|
||||||
|
|
|
@ -28,14 +28,14 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
minimum: (viewInsets ?? EdgeInsets.zero) + (viewPadding ?? EdgeInsets.zero),
|
minimum: (viewInsets ?? EdgeInsets.zero) + (viewPadding ?? EdgeInsets.zero),
|
||||||
child: Padding(
|
child: Padding(
|
||||||
padding: EdgeInsets.all(8.0),
|
padding: const EdgeInsets.all(8.0),
|
||||||
child: Row(
|
child: Row(
|
||||||
children: [
|
children: [
|
||||||
OverlayButton(
|
OverlayButton(
|
||||||
scale: scale,
|
scale: scale,
|
||||||
child: BackButton(),
|
child: const BackButton(),
|
||||||
),
|
),
|
||||||
Spacer(),
|
const Spacer(),
|
||||||
OverlayButton(
|
OverlayButton(
|
||||||
scale: scale,
|
scale: scale,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
@ -44,7 +44,7 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
tooltip: 'Share',
|
tooltip: 'Share',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
OverlayButton(
|
OverlayButton(
|
||||||
scale: scale,
|
scale: scale,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
@ -53,7 +53,7 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
tooltip: 'Delete',
|
tooltip: 'Delete',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
OverlayButton(
|
OverlayButton(
|
||||||
scale: scale,
|
scale: scale,
|
||||||
child: PopupMenuButton<FullscreenAction>(
|
child: PopupMenuButton<FullscreenAction>(
|
||||||
|
@ -81,21 +81,21 @@ class FullscreenTopOverlay extends StatelessWidget {
|
||||||
value: FullscreenAction.print,
|
value: FullscreenAction.print,
|
||||||
child: MenuRow(text: 'Print', icon: Icons.print),
|
child: MenuRow(text: 'Print', icon: Icons.print),
|
||||||
),
|
),
|
||||||
PopupMenuDivider(),
|
const PopupMenuDivider(),
|
||||||
PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: FullscreenAction.edit,
|
value: FullscreenAction.edit,
|
||||||
child: Text('Edit with…'),
|
child: Text('Edit with…'),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: FullscreenAction.open,
|
value: FullscreenAction.open,
|
||||||
child: Text('Open with…'),
|
child: Text('Open with…'),
|
||||||
),
|
),
|
||||||
PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: FullscreenAction.setAs,
|
value: FullscreenAction.setAs,
|
||||||
child: Text('Set as…'),
|
child: Text('Set as…'),
|
||||||
),
|
),
|
||||||
if (entry.hasGps)
|
if (entry.hasGps)
|
||||||
PopupMenuItem(
|
const PopupMenuItem(
|
||||||
value: FullscreenAction.openMap,
|
value: FullscreenAction.openMap,
|
||||||
child: Text('Show on map…'),
|
child: Text('Show on map…'),
|
||||||
),
|
),
|
||||||
|
|
|
@ -46,32 +46,32 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_playPauseAnimation = AnimationController(
|
_playPauseAnimation = AnimationController(
|
||||||
duration: Duration(milliseconds: 300),
|
duration: const Duration(milliseconds: 300),
|
||||||
vsync: this,
|
vsync: this,
|
||||||
);
|
);
|
||||||
registerWidget(widget);
|
_registerWidget(widget);
|
||||||
_onValueChange();
|
_onValueChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(VideoControlOverlay oldWidget) {
|
void didUpdateWidget(VideoControlOverlay oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
unregisterWidget(oldWidget);
|
_unregisterWidget(oldWidget);
|
||||||
registerWidget(widget);
|
_registerWidget(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
unregisterWidget(widget);
|
_unregisterWidget(widget);
|
||||||
_playPauseAnimation.dispose();
|
_playPauseAnimation.dispose();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
void registerWidget(VideoControlOverlay widget) {
|
void _registerWidget(VideoControlOverlay widget) {
|
||||||
widget.controller.addListener(_onValueChange);
|
widget.controller.addListener(_onValueChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void unregisterWidget(VideoControlOverlay widget) {
|
void _unregisterWidget(VideoControlOverlay widget) {
|
||||||
widget.controller.removeListener(_onValueChange);
|
widget.controller.removeListener(_onValueChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,7 +86,7 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
||||||
|
|
||||||
final viewInsets = widget.viewInsets ?? mqViewInsets;
|
final viewInsets = widget.viewInsets ?? mqViewInsets;
|
||||||
final viewPadding = widget.viewPadding ?? mqViewPadding;
|
final viewPadding = widget.viewPadding ?? mqViewPadding;
|
||||||
final safePadding = (viewInsets + viewPadding).copyWith(bottom: 8) + EdgeInsets.symmetric(horizontal: 8.0);
|
final safePadding = (viewInsets + viewPadding).copyWith(bottom: 8) + const EdgeInsets.symmetric(horizontal: 8.0);
|
||||||
|
|
||||||
return Padding(
|
return Padding(
|
||||||
padding: safePadding,
|
padding: safePadding,
|
||||||
|
@ -109,7 +109,7 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
||||||
Expanded(
|
Expanded(
|
||||||
child: _buildProgressBar(),
|
child: _buildProgressBar(),
|
||||||
),
|
),
|
||||||
SizedBox(width: 8),
|
const SizedBox(width: 8),
|
||||||
OverlayButton(
|
OverlayButton(
|
||||||
scale: scale,
|
scale: scale,
|
||||||
child: IconButton(
|
child: IconButton(
|
||||||
|
@ -130,7 +130,7 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildProgressBar() {
|
Widget _buildProgressBar() {
|
||||||
final progressBarBorderRadius = 123.0;
|
const progressBarBorderRadius = 123.0;
|
||||||
return SizeTransition(
|
return SizeTransition(
|
||||||
sizeFactor: scale,
|
sizeFactor: scale,
|
||||||
child: BlurredRRect(
|
child: BlurredRRect(
|
||||||
|
@ -150,11 +150,11 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
||||||
if (_playingOnDragStart) controller.play();
|
if (_playingOnDragStart) controller.play();
|
||||||
},
|
},
|
||||||
child: Container(
|
child: Container(
|
||||||
padding: EdgeInsets.symmetric(vertical: 4, horizontal: 16) + EdgeInsets.only(bottom: 16),
|
padding: const EdgeInsets.symmetric(vertical: 4, horizontal: 16) + const EdgeInsets.only(bottom: 16),
|
||||||
decoration: BoxDecoration(
|
decoration: BoxDecoration(
|
||||||
color: Colors.black26,
|
color: Colors.black26,
|
||||||
border: Border.all(color: Colors.white30, width: 0.5),
|
border: Border.all(color: Colors.white30, width: 0.5),
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: const BorderRadius.all(
|
||||||
Radius.circular(progressBarBorderRadius),
|
Radius.circular(progressBarBorderRadius),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
@ -164,7 +164,7 @@ class VideoControlOverlayState extends State<VideoControlOverlay> with SingleTic
|
||||||
Row(
|
Row(
|
||||||
children: [
|
children: [
|
||||||
Text(formatDuration(value.position ?? Duration.zero)),
|
Text(formatDuration(value.position ?? Duration.zero)),
|
||||||
Spacer(),
|
const Spacer(),
|
||||||
Text(formatDuration(value.duration ?? Duration.zero)),
|
Text(formatDuration(value.duration ?? Duration.zero)),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|
|
@ -29,34 +29,34 @@ class AvesVideoState extends State<AvesVideo> {
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
registerWidget(widget);
|
_registerWidget(widget);
|
||||||
_onValueChange();
|
_onValueChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void didUpdateWidget(AvesVideo oldWidget) {
|
void didUpdateWidget(AvesVideo oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
unregisterWidget(oldWidget);
|
_unregisterWidget(oldWidget);
|
||||||
registerWidget(widget);
|
_registerWidget(widget);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
unregisterWidget(widget);
|
_unregisterWidget(widget);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
registerWidget(AvesVideo widget) {
|
void _registerWidget(AvesVideo widget) {
|
||||||
widget.controller.addListener(_onValueChange);
|
widget.controller.addListener(_onValueChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
unregisterWidget(AvesVideo widget) {
|
void _unregisterWidget(AvesVideo widget) {
|
||||||
widget.controller.removeListener(_onValueChange);
|
widget.controller.removeListener(_onValueChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
if (value == null) return SizedBox();
|
if (value == null) return const SizedBox();
|
||||||
if (value.hasError) {
|
if (value.hasError) {
|
||||||
return Selector<MediaQueryData, double>(
|
return Selector<MediaQueryData, double>(
|
||||||
selector: (c, mq) => mq.size.width,
|
selector: (c, mq) => mq.size.width,
|
||||||
|
@ -79,12 +79,12 @@ class AvesVideoState extends State<AvesVideo> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
_onValueChange() {
|
void _onValueChange() {
|
||||||
if (!value.isPlaying && value.position == value.duration) goToBeginning();
|
if (!value.isPlaying && value.position == value.duration) _goToStart();
|
||||||
setState(() {});
|
setState(() {});
|
||||||
}
|
}
|
||||||
|
|
||||||
goToBeginning() async {
|
Future<void> _goToStart() async {
|
||||||
await widget.controller.seekTo(Duration.zero);
|
await widget.controller.seekTo(Duration.zero);
|
||||||
await widget.controller.pause();
|
await widget.controller.pause();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue