misc
This commit is contained in:
parent
993f189377
commit
6203b98ff4
10 changed files with 100 additions and 30 deletions
|
@ -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<HomePage> {
|
|||
super.initState();
|
||||
imageCache.maximumSizeBytes = 100 * 1024 * 1024;
|
||||
setup();
|
||||
Screen.keepOn(true);
|
||||
}
|
||||
|
||||
setup() async {
|
||||
|
|
|
@ -17,6 +17,7 @@ class ImageFileService {
|
|||
|
||||
static Future<Uint8List> 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', <String, dynamic>{
|
||||
'entry': entry.toMap(),
|
||||
|
|
|
@ -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<SettingsCallback> 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,
|
||||
});
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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<double> 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,
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
|
|
@ -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<dynamic, List<ImageEntry>> _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<dynamic, List<ImageEntry>> 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,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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<FullscreenBody> 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<FullscreenBody> with SingleTickerProvide
|
|||
goToVerticalPage(0);
|
||||
return Future.value(false);
|
||||
}
|
||||
Screen.keepOn(false);
|
||||
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
|
||||
return Future.value(true);
|
||||
},
|
||||
|
|
|
@ -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<AvesVideo> {
|
|||
entry: entry,
|
||||
width: width,
|
||||
height: width / entry.aspectRatio,
|
||||
devicePixelRatio: mediaQuery.devicePixelRatio,
|
||||
devicePixelRatio: window.devicePixelRatio,
|
||||
builder: (bytes) => Image.memory(bytes),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue