Media View improvements (#128)

This commit is contained in:
Filip Krawczyk 2021-02-08 12:15:48 +01:00 committed by GitHub
parent 79fb42785c
commit 430d196997
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 88 additions and 36 deletions

View File

@ -1,3 +1,9 @@
## Unreleased
### Changed
- Changed image viewer dismissal to be more fun. The image now also moves on the x axis, changes scale and rotates a bit for more user enjoyment
## v0.2.2 - 2021-02-03
Minimum Lemmy version supported: `v0.9.4`

View File

@ -1,8 +1,10 @@
import 'dart:math' show max, min;
import 'package:cached_network_image/cached_network_image.dart';
import 'package:esys_flutter_share/esys_flutter_share.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:matrix4_transform/matrix4_transform.dart';
import 'package:photo_view/photo_view.dart';
import '../widgets/bottom_modal.dart';
@ -11,6 +13,8 @@ import '../widgets/bottom_modal.dart';
class MediaViewPage extends HookWidget {
final String url;
final GlobalKey<ScaffoldState> _key = GlobalKey();
static const yThreshold = 150;
static const speedThreshold = 45;
MediaViewPage(this.url);
@ -18,30 +22,19 @@ class MediaViewPage extends HookWidget {
Widget build(BuildContext context) {
final showButtons = useState(true);
final isZoomedOut = useState(true);
final scaleIsInitial = useState(true);
final isDragging = useState(false);
final offset = useState(Offset.zero);
final prevOffset = usePrevious(offset.value);
notImplemented() {
_key.currentState.showSnackBar(const SnackBar(
content: Text("this feature hasn't been implemented yet 😰")));
}
useEffect(() {
if (showButtons.value) {
SystemChrome.setEnabledSystemUIOverlays([
SystemUiOverlay.bottom,
SystemUiOverlay.top,
]);
} else {
SystemChrome.setEnabledSystemUIOverlays([]);
}
return null;
}, [showButtons.value]);
useEffect(
() => () => SystemChrome.setEnabledSystemUIOverlays([
SystemUiOverlay.bottom,
SystemUiOverlay.top,
]),
[]);
// TODO: hide navbar and topbar on android without a content jump
share() {
showModalBottomSheet(
@ -77,6 +70,8 @@ class MediaViewPage extends HookWidget {
key: _key,
extendBodyBehindAppBar: true,
extendBody: true,
backgroundColor:
Colors.black.withOpacity(max(0, 1.0 - (offset.value.dy.abs() / 200))),
appBar: showButtons.value
? AppBar(
backgroundColor: Colors.black38,
@ -96,26 +91,67 @@ class MediaViewPage extends HookWidget {
],
)
: null,
body: GestureDetector(
onTapUp: (details) => showButtons.value = !showButtons.value,
onVerticalDragEnd: isZoomedOut.value
? (details) {
if (details.primaryVelocity.abs() > 1000) {
body: Listener(
onPointerMove: scaleIsInitial.value
? (event) {
if (!isDragging.value &&
event.delta.dx.abs() > event.delta.dy.abs()) return;
isDragging.value = true;
offset.value += event.delta;
}
: (_) => isDragging.value = false,
onPointerCancel: (_) => offset.value = Offset.zero,
onPointerUp: isZoomedOut.value
? (_) {
if (!isDragging.value) {
showButtons.value = !showButtons.value;
return;
}
isDragging.value = false;
final speed = (offset.value - prevOffset).distance;
if (speed > speedThreshold ||
offset.value.dy.abs() > yThreshold) {
Navigator.of(context).pop();
} else {
offset.value = Offset.zero;
}
}
: null,
child: PhotoView(
scaleStateChangedCallback: (value) {
isZoomedOut.value = value == PhotoViewScaleState.zoomedOut ||
value == PhotoViewScaleState.initial;
},
minScale: PhotoViewComputedScale.contained,
initialScale: PhotoViewComputedScale.contained,
imageProvider: CachedNetworkImageProvider(url),
heroAttributes: PhotoViewHeroAttributes(tag: url),
loadingBuilder: (context, event) =>
const Center(child: CircularProgressIndicator()),
: (_) {
offset.value = Offset.zero;
isDragging.value = false;
},
child: AnimatedContainer(
transform: Matrix4Transform()
.scale(max(0.9, 1 - offset.value.dy.abs() / 1000))
.translateOffset(offset.value)
.rotate(min(-offset.value.dx / 2000, 0.1))
.matrix4,
duration: isDragging.value
? Duration.zero
: const Duration(milliseconds: 200),
child: PhotoView(
backgroundDecoration:
const BoxDecoration(color: Colors.transparent),
scaleStateChangedCallback: (value) {
isZoomedOut.value = value == PhotoViewScaleState.zoomedOut ||
value == PhotoViewScaleState.initial;
showButtons.value = isZoomedOut.value;
scaleIsInitial.value = value == PhotoViewScaleState.initial;
isDragging.value = false;
offset.value = Offset.zero;
},
onTapUp: isZoomedOut.value
? null
: (_, __, ___) => showButtons.value = !showButtons.value,
minScale: PhotoViewComputedScale.contained,
initialScale: PhotoViewComputedScale.contained,
imageProvider: CachedNetworkImageProvider(url),
heroAttributes: PhotoViewHeroAttributes(tag: url),
loadingBuilder: (context, event) =>
const Center(child: CircularProgressIndicator()),
),
),
),
);

View File

@ -67,6 +67,7 @@ void goToMedia(BuildContext context, String url) => Navigator.push(
PageRouteBuilder(
pageBuilder: (_, __, ___) => MediaViewPage(url),
transitionDuration: const Duration(milliseconds: 300),
opaque: false,
transitionsBuilder: (_, animation, __, child) =>
FadeTransition(opacity: animation, child: child),
),

View File

@ -282,6 +282,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "0.12.10-nullsafety.1"
matrix4_transform:
dependency: "direct main"
description:
name: matrix4_transform
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.7"
meta:
dependency: transitive
description:

View File

@ -44,6 +44,8 @@ dependencies:
timeago: ^2.0.27
fuzzy: <1.0.0
lemmy_api_client: ^0.10.2
matrix4_transform: ^1.1.7
flutter:
sdk: flutter