fullscreen: fixed overlay animation
This commit is contained in:
parent
51372d7b26
commit
49a28c6d09
3 changed files with 77 additions and 34 deletions
|
@ -366,6 +366,7 @@ class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProv
|
|||
return NotificationListener<ScrollNotification>(
|
||||
onNotification: (ScrollNotification notification) {
|
||||
changePosition(notification);
|
||||
return false;
|
||||
},
|
||||
child: Stack(
|
||||
children: <Widget>[
|
||||
|
|
|
@ -5,6 +5,7 @@ import 'package:aves/model/image_entry.dart';
|
|||
import 'package:aves/widgets/fullscreen/info_page.dart';
|
||||
import 'package:aves/widgets/fullscreen/overlay.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:photo_view/photo_view_gallery.dart';
|
||||
|
@ -27,9 +28,11 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
|
|||
bool _isInitialScale = true;
|
||||
int _currentHorizontalPage, _currentVerticalPage = 0;
|
||||
PageController _horizontalPager, _verticalPager;
|
||||
ValueNotifier<bool> _overlayVisible = ValueNotifier(false);
|
||||
ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
|
||||
AnimationController _overlayAnimationController;
|
||||
Animation<Offset> _topOverlayOffset, _bottomOverlayOffset;
|
||||
Animation<double> _topOverlayScale;
|
||||
Animation<Offset> _bottomOverlayOffset;
|
||||
EdgeInsets _frozenViewInsets, _frozenViewPadding;
|
||||
|
||||
List<ImageEntry> get entries => widget.entries;
|
||||
|
||||
|
@ -41,12 +44,20 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
|
|||
_horizontalPager = PageController(initialPage: _currentHorizontalPage);
|
||||
_verticalPager = PageController(initialPage: _currentVerticalPage);
|
||||
_overlayAnimationController = AnimationController(
|
||||
duration: Duration(milliseconds: 250),
|
||||
duration: Duration(milliseconds: 300),
|
||||
vsync: this,
|
||||
);
|
||||
_topOverlayOffset = Tween(begin: Offset(0, 0), end: Offset(0, -1)).animate(CurvedAnimation(parent: _overlayAnimationController, curve: Curves.easeOutQuart, reverseCurve: Curves.easeInQuart));
|
||||
_bottomOverlayOffset = Tween(begin: Offset(0, 0), end: Offset(0, 1)).animate(CurvedAnimation(parent: _overlayAnimationController, curve: Curves.easeOutQuart, reverseCurve: Curves.easeInQuart));
|
||||
_topOverlayScale = CurvedAnimation(parent: _overlayAnimationController, curve: Curves.easeOutQuart, reverseCurve: Curves.easeInQuart);
|
||||
_bottomOverlayOffset = Tween(begin: Offset(0, 1), end: Offset(0, 0)).animate(CurvedAnimation(parent: _overlayAnimationController, curve: Curves.easeOutQuart, reverseCurve: Curves.easeInQuart));
|
||||
_overlayVisible.addListener(onOverlayVisibleChange);
|
||||
initOverlay();
|
||||
}
|
||||
|
||||
initOverlay() async {
|
||||
// wait for MaterialPageRoute.transitionDuration
|
||||
// to show overlay after hero animation is complete
|
||||
await Future.delayed(Duration(milliseconds: 300));
|
||||
onOverlayVisibleChange();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -70,11 +81,7 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
|
|||
ImagePage(
|
||||
entries: entries,
|
||||
pageController: _horizontalPager,
|
||||
onTap: () {
|
||||
final visible = !_overlayVisible.value;
|
||||
_overlayVisible.value = visible;
|
||||
SystemChrome.setEnabledSystemUIOverlays(visible ? []: SystemUiOverlay.values);
|
||||
},
|
||||
onTap: () => _overlayVisible.value = !_overlayVisible.value,
|
||||
onPageChanged: (page) => setState(() => _currentHorizontalPage = page),
|
||||
onScaleChanged: (state) => setState(() => _isInitialScale = state == PhotoViewScaleState.initial),
|
||||
),
|
||||
|
@ -96,12 +103,12 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
|
|||
],
|
||||
),
|
||||
if (_currentHorizontalPage != null && _currentVerticalPage == 0) ...[
|
||||
SlideTransition(
|
||||
position: _topOverlayOffset,
|
||||
child: FullscreenTopOverlay(
|
||||
FullscreenTopOverlay(
|
||||
entries: entries,
|
||||
index: _currentHorizontalPage,
|
||||
),
|
||||
scale: _topOverlayScale,
|
||||
viewInsets: _frozenViewInsets,
|
||||
viewPadding: _frozenViewPadding,
|
||||
),
|
||||
Positioned(
|
||||
bottom: 0,
|
||||
|
@ -110,6 +117,8 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
|
|||
child: FullscreenBottomOverlay(
|
||||
entries: entries,
|
||||
index: _currentHorizontalPage,
|
||||
viewInsets: _frozenViewInsets,
|
||||
viewPadding: _frozenViewPadding,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
@ -148,11 +157,19 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
|
|||
);
|
||||
}
|
||||
|
||||
onOverlayVisibleChange() {
|
||||
if (_overlayVisible.value)
|
||||
onOverlayVisibleChange() async {
|
||||
if (_overlayVisible.value) {
|
||||
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
|
||||
_overlayAnimationController.forward();
|
||||
else
|
||||
_overlayAnimationController.reverse();
|
||||
} else {
|
||||
final mq = MediaQuery.of(context);
|
||||
_frozenViewInsets = mq.viewInsets;
|
||||
_frozenViewPadding = mq.viewPadding;
|
||||
SystemChrome.setEnabledSystemUIOverlays([]);
|
||||
await _overlayAnimationController.reverse();
|
||||
_frozenViewInsets = null;
|
||||
_frozenViewPadding = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,23 +13,35 @@ const kOverlayBackground = Colors.black26;
|
|||
class FullscreenTopOverlay extends StatelessWidget {
|
||||
final List<ImageEntry> entries;
|
||||
final int index;
|
||||
final Animation<double> scale;
|
||||
final EdgeInsets viewInsets, viewPadding;
|
||||
|
||||
ImageEntry get entry => entries[index];
|
||||
|
||||
const FullscreenTopOverlay({Key key, this.entries, this.index}) : super(key: key);
|
||||
const FullscreenTopOverlay({
|
||||
Key key,
|
||||
this.entries,
|
||||
this.index,
|
||||
this.scale,
|
||||
this.viewInsets,
|
||||
this.viewPadding,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SafeArea(
|
||||
minimum: (viewInsets ?? EdgeInsets.zero) + (viewPadding ?? EdgeInsets.zero),
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
OverlayButton(
|
||||
scale: scale,
|
||||
child: BackButton(),
|
||||
),
|
||||
Spacer(),
|
||||
OverlayButton(
|
||||
scale: scale,
|
||||
child: IconButton(
|
||||
icon: Icon(Icons.share),
|
||||
onPressed: share,
|
||||
|
@ -52,8 +64,15 @@ class FullscreenTopOverlay extends StatelessWidget {
|
|||
class FullscreenBottomOverlay extends StatefulWidget {
|
||||
final List<ImageEntry> entries;
|
||||
final int index;
|
||||
final EdgeInsets viewInsets, viewPadding;
|
||||
|
||||
const FullscreenBottomOverlay({Key key, this.entries, this.index}) : super(key: key);
|
||||
const FullscreenBottomOverlay({
|
||||
Key key,
|
||||
this.entries,
|
||||
this.index,
|
||||
this.viewInsets,
|
||||
this.viewPadding,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<StatefulWidget> createState() => _FullscreenBottomOverlayState();
|
||||
|
@ -86,13 +105,15 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
|
|||
Widget build(BuildContext context) {
|
||||
final innerPadding = EdgeInsets.all(8.0);
|
||||
final mediaQuery = MediaQuery.of(context);
|
||||
final overlayContentMaxWidth = mediaQuery.size.width - mediaQuery.viewPadding.horizontal - innerPadding.horizontal;
|
||||
final viewInsets = widget.viewInsets ?? mediaQuery.viewInsets;
|
||||
final viewPadding = widget.viewPadding ?? mediaQuery.viewPadding;
|
||||
final overlayContentMaxWidth = mediaQuery.size.width - viewPadding.horizontal - innerPadding.horizontal;
|
||||
return BlurredRect(
|
||||
child: Container(
|
||||
color: kOverlayBackground,
|
||||
child: IgnorePointer(
|
||||
child: Padding(
|
||||
padding: mediaQuery.viewInsets + mediaQuery.viewPadding.copyWith(top: 0),
|
||||
padding: viewInsets + viewPadding.copyWith(top: 0),
|
||||
child: Container(
|
||||
padding: innerPadding,
|
||||
child: FutureBuilder(
|
||||
|
@ -187,13 +208,16 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
|
|||
}
|
||||
|
||||
class OverlayButton extends StatelessWidget {
|
||||
final Animation<double> scale;
|
||||
final Widget child;
|
||||
|
||||
const OverlayButton({this.child});
|
||||
const OverlayButton({Key key, this.scale, this.child}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlurredOval(
|
||||
return ScaleTransition(
|
||||
scale: scale,
|
||||
child: BlurredOval(
|
||||
child: Material(
|
||||
type: MaterialType.circle,
|
||||
color: kOverlayBackground,
|
||||
|
@ -205,6 +229,7 @@ class OverlayButton extends StatelessWidget {
|
|||
child: child,
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue