From 6203b98ff4d2c4f1edfca96bdcec6a5f379b2bcf Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Wed, 20 Nov 2019 08:26:18 +0900 Subject: [PATCH] misc --- lib/main.dart | 2 + lib/model/image_file_service.dart | 1 + lib/model/settings.dart | 47 ++++++++++++++++++-- lib/widgets/album/all_collection_drawer.dart | 4 +- lib/widgets/album/thumbnail.dart | 33 ++++++++++---- lib/widgets/album/thumbnail_collection.dart | 21 ++++++--- lib/widgets/common/icons.dart | 4 +- lib/widgets/fullscreen/fullscreen_page.dart | 7 +-- lib/widgets/fullscreen/video.dart | 3 +- pubspec.lock | 8 ++-- 10 files changed, 100 insertions(+), 30 deletions(-) diff --git a/lib/main.dart b/lib/main.dart index 58b5975f1..f3b849bcc 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -12,6 +12,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_native_timezone/flutter_native_timezone.dart'; import 'package:permission_handler/permission_handler.dart'; +import 'package:screen/screen.dart'; void main() { // initialize binding/plugins to configure Skia before `runApp` @@ -60,6 +61,7 @@ class _HomePageState extends State { super.initState(); imageCache.maximumSizeBytes = 100 * 1024 * 1024; setup(); + Screen.keepOn(true); } setup() async { diff --git a/lib/model/image_file_service.dart b/lib/model/image_file_service.dart index 36c9e53ff..7ced381ed 100644 --- a/lib/model/image_file_service.dart +++ b/lib/model/image_file_service.dart @@ -17,6 +17,7 @@ class ImageFileService { static Future getImageBytes(ImageEntry entry, int width, int height) async { if (width > 0 && height > 0) { +// debugPrint('getImageBytes width=$width path=${entry.path}'); try { final result = await platform.invokeMethod('getImageBytes', { 'entry': entry.toMap(), diff --git a/lib/model/settings.dart b/lib/model/settings.dart index de0479dbd..ab8b43a5d 100644 --- a/lib/model/settings.dart +++ b/lib/model/settings.dart @@ -1,5 +1,8 @@ +import 'dart:ui'; + import 'package:aves/model/image_collection.dart'; import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; import 'package:shared_preferences/shared_preferences.dart'; final Settings settings = Settings._private(); @@ -19,15 +22,35 @@ class Settings { static const infoMapZoomKey = 'info_map_zoom'; static const catalogTimeZoneKey = 'catalog_time_zone'; + // state + static const windowMetricsKey = 'window_metrics'; + init() async { prefs = await SharedPreferences.getInstance(); + // TODO TLAD try this as an alternative to MediaQuery, in order to rebuild only on specific property change +// window.onMetricsChanged = onMetricsChanged; } - void addListener(SettingsCallback listener) => _listeners.add(listener); + WindowMetrics _metrics; - void removeListener(SettingsCallback listener) => _listeners.remove(listener); + onMetricsChanged() { + final newValue = WindowMetrics( + devicePixelRatio: window.devicePixelRatio, + physicalSize: window.physicalSize, + viewInsets: window.viewInsets, + viewPadding: window.viewPadding, + systemGestureInsets: window.systemGestureInsets, + padding: window.padding, + ); + notifyListeners(windowMetricsKey, _metrics, newValue); + _metrics = newValue; + } - void notifyListeners(String key, dynamic oldValue, dynamic newValue) { + addListener(SettingsCallback listener) => _listeners.add(listener); + + removeListener(SettingsCallback listener) => _listeners.remove(listener); + + notifyListeners(String key, dynamic oldValue, dynamic newValue) { debugPrint('$runtimeType notifyListeners key=$key, old=$oldValue, new=$newValue'); if (_listeners != null) { final List localListeners = _listeners.toList(); @@ -95,3 +118,21 @@ class Settings { } } } + +class WindowMetrics { + final double devicePixelRatio; + final Size physicalSize; + final WindowPadding viewInsets; + final WindowPadding viewPadding; + final WindowPadding systemGestureInsets; + final WindowPadding padding; + + const WindowMetrics({ + this.devicePixelRatio, + this.physicalSize, + this.viewInsets, + this.viewPadding, + this.systemGestureInsets, + this.padding, + }); +} diff --git a/lib/widgets/album/all_collection_drawer.dart b/lib/widgets/album/all_collection_drawer.dart index 79f9a4fcb..71308e3bc 100644 --- a/lib/widgets/album/all_collection_drawer.dart +++ b/lib/widgets/album/all_collection_drawer.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:aves/model/image_collection.dart'; import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/album/filtered_collection_page.dart'; @@ -16,7 +18,7 @@ class AllCollectionDrawer extends StatelessWidget { final tags = collection.sortedTags; return Drawer( child: ListView( - padding: EdgeInsets.only(bottom: MediaQuery.of(context).viewInsets.bottom), + padding: EdgeInsets.only(bottom: window.viewInsets.bottom), children: [ DrawerHeader( child: SafeArea( diff --git a/lib/widgets/album/thumbnail.dart b/lib/widgets/album/thumbnail.dart index 286f204a9..e9b402547 100644 --- a/lib/widgets/album/thumbnail.dart +++ b/lib/widgets/album/thumbnail.dart @@ -27,15 +27,30 @@ class Thumbnail extends StatelessWidget { builder: (bytes) { return Hero( tag: entry.uri, - child: LayoutBuilder(builder: (context, constraints) { - final dim = min(constraints.maxWidth, constraints.maxHeight); - return Image.memory( - bytes, - width: dim, - height: dim, - fit: BoxFit.cover, - ); - }), + flightShuttleBuilder: ( + BuildContext flightContext, + Animation animation, + HeroFlightDirection flightDirection, + BuildContext fromHeroContext, + BuildContext toHeroContext, + ) { + // use LayoutBuilder only during hero animation + return LayoutBuilder(builder: (context, constraints) { + final dim = min(constraints.maxWidth, constraints.maxHeight); + return Image.memory( + bytes, + width: dim, + height: dim, + fit: BoxFit.cover, + ); + }); + }, + child: Image.memory( + bytes, + width: extent, + height: extent, + fit: BoxFit.cover, + ), ); }, ); diff --git a/lib/widgets/album/thumbnail_collection.dart b/lib/widgets/album/thumbnail_collection.dart index e114d8949..2bb9fe31e 100644 --- a/lib/widgets/album/thumbnail_collection.dart +++ b/lib/widgets/album/thumbnail_collection.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:aves/model/image_collection.dart'; import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/album/sections.dart'; @@ -23,6 +25,7 @@ class ThumbnailCollection extends AnimatedWidget { return ThumbnailCollectionContent( collection: collection, appBar: appBar, + screenWidth: MediaQuery.of(context).size.width, ); } } @@ -30,14 +33,16 @@ class ThumbnailCollection extends AnimatedWidget { class ThumbnailCollectionContent extends StatelessWidget { final ImageCollection collection; final Widget appBar; + final double screenWidth; final Map> _sections; final ScrollController _scrollController = ScrollController(); ThumbnailCollectionContent({ Key key, - this.collection, - this.appBar, + @required this.collection, + @required this.appBar, + @required this.screenWidth, }) : _sections = collection.sections, super(key: key); @@ -69,6 +74,7 @@ class ThumbnailCollectionContent extends StatelessWidget { collection: collection, sections: _sections, sectionKey: sectionKey, + screenWidth: screenWidth, ); if (sectionKey == sectionKeys.last) { sliver = SliverPadding( @@ -94,12 +100,14 @@ class SectionSliver extends StatelessWidget { final ImageCollection collection; final Map> sections; final dynamic sectionKey; + final double screenWidth; const SectionSliver({ Key key, @required this.collection, @required this.sections, @required this.sectionKey, + @required this.screenWidth, }) : super(key: key); @override @@ -113,28 +121,29 @@ class SectionSliver extends StatelessWidget { ), sliver: SliverGrid( delegate: SliverChildBuilderDelegate( + // TODO TLAD find out why thumbnails are rebuilt when config change (show/hide status bar) (sliverContext, index) { final sectionEntries = sections[sectionKey]; if (index >= sectionEntries.length) return null; final entry = sectionEntries[index]; - final mediaQuery = MediaQuery.of(sliverContext); return GestureDetector( onTap: () => _showFullscreen(sliverContext, entry), child: Thumbnail( entry: entry, - extent: mediaQuery.size.width / columnCount, - devicePixelRatio: mediaQuery.devicePixelRatio, + extent: screenWidth / columnCount, + devicePixelRatio: window.devicePixelRatio, ), ); }, childCount: sections[sectionKey].length, addAutomaticKeepAlives: false, - addRepaintBoundaries: false, + addRepaintBoundaries: true, ), gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( crossAxisCount: columnCount, ), ), + overlapsContent: false, ); } diff --git a/lib/widgets/common/icons.dart b/lib/widgets/common/icons.dart index 54d402a65..a1679cb44 100644 --- a/lib/widgets/common/icons.dart +++ b/lib/widgets/common/icons.dart @@ -1,3 +1,5 @@ +import 'dart:ui'; + import 'package:aves/model/image_entry.dart'; import 'package:aves/utils/android_app_service.dart'; import 'package:aves/utils/android_file_utils.dart'; @@ -104,7 +106,7 @@ class IconUtils { return AppIcon( packageName: packageName, size: IconTheme.of(context).size, - devicePixelRatio: MediaQuery.of(context).devicePixelRatio, + devicePixelRatio: window.devicePixelRatio, ); } return null; diff --git a/lib/widgets/fullscreen/fullscreen_page.dart b/lib/widgets/fullscreen/fullscreen_page.dart index 001b505cb..dfe60b5d0 100644 --- a/lib/widgets/fullscreen/fullscreen_page.dart +++ b/lib/widgets/fullscreen/fullscreen_page.dart @@ -10,9 +10,9 @@ import 'package:aves/widgets/fullscreen/overlay/bottom.dart'; import 'package:aves/widgets/fullscreen/overlay/top.dart'; import 'package:aves/widgets/fullscreen/overlay/video.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/scheduler.dart'; import 'package:flutter/services.dart'; import 'package:photo_view/photo_view.dart'; -import 'package:screen/screen.dart'; import 'package:tuple/tuple.dart'; import 'package:video_player/video_player.dart'; @@ -122,15 +122,13 @@ class FullscreenBodyState extends State with SingleTickerProvide showInfo: () => goToVerticalPage(1), ); initVideoController(); - - Screen.keepOn(true); initOverlay(); } initOverlay() async { // wait for MaterialPageRoute.transitionDuration // to show overlay after hero animation is complete - await Future.delayed(Duration(milliseconds: 300)); + await Future.delayed(Duration(milliseconds: (300 * timeDilation).toInt())); onOverlayVisibleChange(); } @@ -150,7 +148,6 @@ class FullscreenBodyState extends State with SingleTickerProvide goToVerticalPage(0); return Future.value(false); } - Screen.keepOn(false); SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values); return Future.value(true); }, diff --git a/lib/widgets/fullscreen/video.dart b/lib/widgets/fullscreen/video.dart index c7806a0d7..8eeab3313 100644 --- a/lib/widgets/fullscreen/video.dart +++ b/lib/widgets/fullscreen/video.dart @@ -1,4 +1,5 @@ import 'dart:math'; +import 'dart:ui'; import 'package:aves/model/image_entry.dart'; import 'package:aves/widgets/common/image_preview.dart'; @@ -62,7 +63,7 @@ class AvesVideoState extends State { entry: entry, width: width, height: width / entry.aspectRatio, - devicePixelRatio: mediaQuery.devicePixelRatio, + devicePixelRatio: window.devicePixelRatio, builder: (bytes) => Image.memory(bytes), ); } diff --git a/pubspec.lock b/pubspec.lock index 5484d508e..fddf88f60 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -100,7 +100,7 @@ packages: name: flutter_svg url: "https://pub.dartlang.org" source: hosted - version: "0.14.3" + version: "0.14.4" flutter_test: dependency: "direct dev" description: flutter @@ -175,7 +175,7 @@ packages: name: pdf url: "https://pub.dartlang.org" source: hosted - version: "1.3.23" + version: "1.3.24" pedantic: dependency: transitive description: @@ -189,7 +189,7 @@ packages: name: permission_handler url: "https://pub.dartlang.org" source: hosted - version: "3.3.0" + version: "4.0.0" petitparser: dependency: transitive description: @@ -203,7 +203,7 @@ packages: name: photo_view url: "https://pub.dartlang.org" source: hosted - version: "0.7.0" + version: "0.8.0" printing: dependency: "direct main" description: