From 49a28c6d0968d07e98b45e36074882709135a455 Mon Sep 17 00:00:00 2001 From: Thibault Deckers Date: Sun, 4 Aug 2019 00:13:38 +0900 Subject: [PATCH] fullscreen: fixed overlay animation --- lib/widgets/common/draggable_scrollbar.dart | 1 + lib/widgets/fullscreen/image_page.dart | 57 +++++++++++++-------- lib/widgets/fullscreen/overlay.dart | 53 ++++++++++++++----- 3 files changed, 77 insertions(+), 34 deletions(-) diff --git a/lib/widgets/common/draggable_scrollbar.dart b/lib/widgets/common/draggable_scrollbar.dart index a92061f41..7cae7feed 100644 --- a/lib/widgets/common/draggable_scrollbar.dart +++ b/lib/widgets/common/draggable_scrollbar.dart @@ -366,6 +366,7 @@ class _DraggableScrollbarState extends State with TickerProv return NotificationListener( onNotification: (ScrollNotification notification) { changePosition(notification); + return false; }, child: Stack( children: [ diff --git a/lib/widgets/fullscreen/image_page.dart b/lib/widgets/fullscreen/image_page.dart index ecf9e7252..69f3bccea 100644 --- a/lib/widgets/fullscreen/image_page.dart +++ b/lib/widgets/fullscreen/image_page.dart @@ -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 with SingleTickerProvide bool _isInitialScale = true; int _currentHorizontalPage, _currentVerticalPage = 0; PageController _horizontalPager, _verticalPager; - ValueNotifier _overlayVisible = ValueNotifier(false); + ValueNotifier _overlayVisible = ValueNotifier(true); AnimationController _overlayAnimationController; - Animation _topOverlayOffset, _bottomOverlayOffset; + Animation _topOverlayScale; + Animation _bottomOverlayOffset; + EdgeInsets _frozenViewInsets, _frozenViewPadding; List get entries => widget.entries; @@ -41,12 +44,20 @@ class FullscreenPageState extends State 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 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 with SingleTickerProvide ], ), if (_currentHorizontalPage != null && _currentVerticalPage == 0) ...[ - SlideTransition( - position: _topOverlayOffset, - child: FullscreenTopOverlay( - entries: entries, - index: _currentHorizontalPage, - ), + FullscreenTopOverlay( + entries: entries, + index: _currentHorizontalPage, + scale: _topOverlayScale, + viewInsets: _frozenViewInsets, + viewPadding: _frozenViewPadding, ), Positioned( bottom: 0, @@ -110,6 +117,8 @@ class FullscreenPageState extends State with SingleTickerProvide child: FullscreenBottomOverlay( entries: entries, index: _currentHorizontalPage, + viewInsets: _frozenViewInsets, + viewPadding: _frozenViewPadding, ), ), ) @@ -148,11 +157,19 @@ class FullscreenPageState extends State 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; + } } } diff --git a/lib/widgets/fullscreen/overlay.dart b/lib/widgets/fullscreen/overlay.dart index e8917583d..c899ebe09 100644 --- a/lib/widgets/fullscreen/overlay.dart +++ b/lib/widgets/fullscreen/overlay.dart @@ -13,23 +13,35 @@ const kOverlayBackground = Colors.black26; class FullscreenTopOverlay extends StatelessWidget { final List entries; final int index; + final Animation 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 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 createState() => _FullscreenBottomOverlayState(); @@ -86,13 +105,15 @@ class _FullscreenBottomOverlayState extends State { 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,22 +208,26 @@ class _FullscreenBottomOverlayContent extends StatelessWidget { } class OverlayButton extends StatelessWidget { + final Animation 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( - child: Material( - type: MaterialType.circle, - color: kOverlayBackground, - child: Ink( - decoration: BoxDecoration( - border: Border.all(color: Colors.white30, width: 0.5), - shape: BoxShape.circle, + return ScaleTransition( + scale: scale, + child: BlurredOval( + child: Material( + type: MaterialType.circle, + color: kOverlayBackground, + child: Ink( + decoration: BoxDecoration( + border: Border.all(color: Colors.white30, width: 0.5), + shape: BoxShape.circle, + ), + child: child, ), - child: child, ), ), );