Media View improvements (#128)
This commit is contained in:
parent
79fb42785c
commit
430d196997
|
@ -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
|
## v0.2.2 - 2021-02-03
|
||||||
|
|
||||||
Minimum Lemmy version supported: `v0.9.4`
|
Minimum Lemmy version supported: `v0.9.4`
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
|
import 'dart:math' show max, min;
|
||||||
|
|
||||||
import 'package:cached_network_image/cached_network_image.dart';
|
import 'package:cached_network_image/cached_network_image.dart';
|
||||||
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
import 'package:esys_flutter_share/esys_flutter_share.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:matrix4_transform/matrix4_transform.dart';
|
||||||
import 'package:photo_view/photo_view.dart';
|
import 'package:photo_view/photo_view.dart';
|
||||||
|
|
||||||
import '../widgets/bottom_modal.dart';
|
import '../widgets/bottom_modal.dart';
|
||||||
|
@ -11,6 +13,8 @@ import '../widgets/bottom_modal.dart';
|
||||||
class MediaViewPage extends HookWidget {
|
class MediaViewPage extends HookWidget {
|
||||||
final String url;
|
final String url;
|
||||||
final GlobalKey<ScaffoldState> _key = GlobalKey();
|
final GlobalKey<ScaffoldState> _key = GlobalKey();
|
||||||
|
static const yThreshold = 150;
|
||||||
|
static const speedThreshold = 45;
|
||||||
|
|
||||||
MediaViewPage(this.url);
|
MediaViewPage(this.url);
|
||||||
|
|
||||||
|
@ -18,30 +22,19 @@ class MediaViewPage extends HookWidget {
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
final showButtons = useState(true);
|
final showButtons = useState(true);
|
||||||
final isZoomedOut = 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() {
|
notImplemented() {
|
||||||
_key.currentState.showSnackBar(const SnackBar(
|
_key.currentState.showSnackBar(const SnackBar(
|
||||||
content: Text("this feature hasn't been implemented yet 😰")));
|
content: Text("this feature hasn't been implemented yet 😰")));
|
||||||
}
|
}
|
||||||
|
|
||||||
useEffect(() {
|
// TODO: hide navbar and topbar on android without a content jump
|
||||||
if (showButtons.value) {
|
|
||||||
SystemChrome.setEnabledSystemUIOverlays([
|
|
||||||
SystemUiOverlay.bottom,
|
|
||||||
SystemUiOverlay.top,
|
|
||||||
]);
|
|
||||||
} else {
|
|
||||||
SystemChrome.setEnabledSystemUIOverlays([]);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}, [showButtons.value]);
|
|
||||||
|
|
||||||
useEffect(
|
|
||||||
() => () => SystemChrome.setEnabledSystemUIOverlays([
|
|
||||||
SystemUiOverlay.bottom,
|
|
||||||
SystemUiOverlay.top,
|
|
||||||
]),
|
|
||||||
[]);
|
|
||||||
|
|
||||||
share() {
|
share() {
|
||||||
showModalBottomSheet(
|
showModalBottomSheet(
|
||||||
|
@ -77,6 +70,8 @@ class MediaViewPage extends HookWidget {
|
||||||
key: _key,
|
key: _key,
|
||||||
extendBodyBehindAppBar: true,
|
extendBodyBehindAppBar: true,
|
||||||
extendBody: true,
|
extendBody: true,
|
||||||
|
backgroundColor:
|
||||||
|
Colors.black.withOpacity(max(0, 1.0 - (offset.value.dy.abs() / 200))),
|
||||||
appBar: showButtons.value
|
appBar: showButtons.value
|
||||||
? AppBar(
|
? AppBar(
|
||||||
backgroundColor: Colors.black38,
|
backgroundColor: Colors.black38,
|
||||||
|
@ -96,26 +91,67 @@ class MediaViewPage extends HookWidget {
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
: null,
|
: null,
|
||||||
body: GestureDetector(
|
body: Listener(
|
||||||
onTapUp: (details) => showButtons.value = !showButtons.value,
|
onPointerMove: scaleIsInitial.value
|
||||||
onVerticalDragEnd: isZoomedOut.value
|
? (event) {
|
||||||
? (details) {
|
if (!isDragging.value &&
|
||||||
if (details.primaryVelocity.abs() > 1000) {
|
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();
|
Navigator.of(context).pop();
|
||||||
|
} else {
|
||||||
|
offset.value = Offset.zero;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
: null,
|
: (_) {
|
||||||
child: PhotoView(
|
offset.value = Offset.zero;
|
||||||
scaleStateChangedCallback: (value) {
|
isDragging.value = false;
|
||||||
isZoomedOut.value = value == PhotoViewScaleState.zoomedOut ||
|
},
|
||||||
value == PhotoViewScaleState.initial;
|
child: AnimatedContainer(
|
||||||
},
|
transform: Matrix4Transform()
|
||||||
minScale: PhotoViewComputedScale.contained,
|
.scale(max(0.9, 1 - offset.value.dy.abs() / 1000))
|
||||||
initialScale: PhotoViewComputedScale.contained,
|
.translateOffset(offset.value)
|
||||||
imageProvider: CachedNetworkImageProvider(url),
|
.rotate(min(-offset.value.dx / 2000, 0.1))
|
||||||
heroAttributes: PhotoViewHeroAttributes(tag: url),
|
.matrix4,
|
||||||
loadingBuilder: (context, event) =>
|
duration: isDragging.value
|
||||||
const Center(child: CircularProgressIndicator()),
|
? 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()),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -67,6 +67,7 @@ void goToMedia(BuildContext context, String url) => Navigator.push(
|
||||||
PageRouteBuilder(
|
PageRouteBuilder(
|
||||||
pageBuilder: (_, __, ___) => MediaViewPage(url),
|
pageBuilder: (_, __, ___) => MediaViewPage(url),
|
||||||
transitionDuration: const Duration(milliseconds: 300),
|
transitionDuration: const Duration(milliseconds: 300),
|
||||||
|
opaque: false,
|
||||||
transitionsBuilder: (_, animation, __, child) =>
|
transitionsBuilder: (_, animation, __, child) =>
|
||||||
FadeTransition(opacity: animation, child: child),
|
FadeTransition(opacity: animation, child: child),
|
||||||
),
|
),
|
||||||
|
|
|
@ -282,6 +282,13 @@ packages:
|
||||||
url: "https://pub.dartlang.org"
|
url: "https://pub.dartlang.org"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.12.10-nullsafety.1"
|
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:
|
meta:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
|
|
@ -44,6 +44,8 @@ dependencies:
|
||||||
timeago: ^2.0.27
|
timeago: ^2.0.27
|
||||||
fuzzy: <1.0.0
|
fuzzy: <1.0.0
|
||||||
lemmy_api_client: ^0.10.2
|
lemmy_api_client: ^0.10.2
|
||||||
|
matrix4_transform: ^1.1.7
|
||||||
|
|
||||||
|
|
||||||
flutter:
|
flutter:
|
||||||
sdk: flutter
|
sdk: flutter
|
||||||
|
|
Loading…
Reference in New Issue