fullscreen: fixed overlay animation

This commit is contained in:
Thibault Deckers 2019-08-04 00:13:38 +09:00
parent 51372d7b26
commit 49a28c6d09
3 changed files with 77 additions and 34 deletions

View file

@ -366,6 +366,7 @@ class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProv
return NotificationListener<ScrollNotification>( return NotificationListener<ScrollNotification>(
onNotification: (ScrollNotification notification) { onNotification: (ScrollNotification notification) {
changePosition(notification); changePosition(notification);
return false;
}, },
child: Stack( child: Stack(
children: <Widget>[ children: <Widget>[

View file

@ -5,6 +5,7 @@ import 'package:aves/model/image_entry.dart';
import 'package:aves/widgets/fullscreen/info_page.dart'; import 'package:aves/widgets/fullscreen/info_page.dart';
import 'package:aves/widgets/fullscreen/overlay.dart'; import 'package:aves/widgets/fullscreen/overlay.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:photo_view/photo_view.dart'; import 'package:photo_view/photo_view.dart';
import 'package:photo_view/photo_view_gallery.dart'; import 'package:photo_view/photo_view_gallery.dart';
@ -27,9 +28,11 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
bool _isInitialScale = true; bool _isInitialScale = true;
int _currentHorizontalPage, _currentVerticalPage = 0; int _currentHorizontalPage, _currentVerticalPage = 0;
PageController _horizontalPager, _verticalPager; PageController _horizontalPager, _verticalPager;
ValueNotifier<bool> _overlayVisible = ValueNotifier(false); ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
AnimationController _overlayAnimationController; AnimationController _overlayAnimationController;
Animation<Offset> _topOverlayOffset, _bottomOverlayOffset; Animation<double> _topOverlayScale;
Animation<Offset> _bottomOverlayOffset;
EdgeInsets _frozenViewInsets, _frozenViewPadding;
List<ImageEntry> get entries => widget.entries; List<ImageEntry> get entries => widget.entries;
@ -41,12 +44,20 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
_horizontalPager = PageController(initialPage: _currentHorizontalPage); _horizontalPager = PageController(initialPage: _currentHorizontalPage);
_verticalPager = PageController(initialPage: _currentVerticalPage); _verticalPager = PageController(initialPage: _currentVerticalPage);
_overlayAnimationController = AnimationController( _overlayAnimationController = AnimationController(
duration: Duration(milliseconds: 250), duration: Duration(milliseconds: 300),
vsync: this, vsync: this,
); );
_topOverlayOffset = 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, 0), end: Offset(0, 1)).animate(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); _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 @override
@ -70,11 +81,7 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
ImagePage( ImagePage(
entries: entries, entries: entries,
pageController: _horizontalPager, pageController: _horizontalPager,
onTap: () { onTap: () => _overlayVisible.value = !_overlayVisible.value,
final visible = !_overlayVisible.value;
_overlayVisible.value = visible;
SystemChrome.setEnabledSystemUIOverlays(visible ? []: SystemUiOverlay.values);
},
onPageChanged: (page) => setState(() => _currentHorizontalPage = page), onPageChanged: (page) => setState(() => _currentHorizontalPage = page),
onScaleChanged: (state) => setState(() => _isInitialScale = state == PhotoViewScaleState.initial), onScaleChanged: (state) => setState(() => _isInitialScale = state == PhotoViewScaleState.initial),
), ),
@ -96,12 +103,12 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
], ],
), ),
if (_currentHorizontalPage != null && _currentVerticalPage == 0) ...[ if (_currentHorizontalPage != null && _currentVerticalPage == 0) ...[
SlideTransition( FullscreenTopOverlay(
position: _topOverlayOffset, entries: entries,
child: FullscreenTopOverlay( index: _currentHorizontalPage,
entries: entries, scale: _topOverlayScale,
index: _currentHorizontalPage, viewInsets: _frozenViewInsets,
), viewPadding: _frozenViewPadding,
), ),
Positioned( Positioned(
bottom: 0, bottom: 0,
@ -110,6 +117,8 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
child: FullscreenBottomOverlay( child: FullscreenBottomOverlay(
entries: entries, entries: entries,
index: _currentHorizontalPage, index: _currentHorizontalPage,
viewInsets: _frozenViewInsets,
viewPadding: _frozenViewPadding,
), ),
), ),
) )
@ -148,11 +157,19 @@ class FullscreenPageState extends State<FullscreenPage> with SingleTickerProvide
); );
} }
onOverlayVisibleChange() { onOverlayVisibleChange() async {
if (_overlayVisible.value) if (_overlayVisible.value) {
SystemChrome.setEnabledSystemUIOverlays(SystemUiOverlay.values);
_overlayAnimationController.forward(); _overlayAnimationController.forward();
else } else {
_overlayAnimationController.reverse(); final mq = MediaQuery.of(context);
_frozenViewInsets = mq.viewInsets;
_frozenViewPadding = mq.viewPadding;
SystemChrome.setEnabledSystemUIOverlays([]);
await _overlayAnimationController.reverse();
_frozenViewInsets = null;
_frozenViewPadding = null;
}
} }
} }

View file

@ -13,23 +13,35 @@ const kOverlayBackground = Colors.black26;
class FullscreenTopOverlay extends StatelessWidget { class FullscreenTopOverlay extends StatelessWidget {
final List<ImageEntry> entries; final List<ImageEntry> entries;
final int index; final int index;
final Animation<double> scale;
final EdgeInsets viewInsets, viewPadding;
ImageEntry get entry => entries[index]; 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 @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return SafeArea( return SafeArea(
minimum: (viewInsets ?? EdgeInsets.zero) + (viewPadding ?? EdgeInsets.zero),
child: Padding( child: Padding(
padding: EdgeInsets.all(8.0), padding: EdgeInsets.all(8.0),
child: Row( child: Row(
children: [ children: [
OverlayButton( OverlayButton(
scale: scale,
child: BackButton(), child: BackButton(),
), ),
Spacer(), Spacer(),
OverlayButton( OverlayButton(
scale: scale,
child: IconButton( child: IconButton(
icon: Icon(Icons.share), icon: Icon(Icons.share),
onPressed: share, onPressed: share,
@ -52,8 +64,15 @@ class FullscreenTopOverlay extends StatelessWidget {
class FullscreenBottomOverlay extends StatefulWidget { class FullscreenBottomOverlay extends StatefulWidget {
final List<ImageEntry> entries; final List<ImageEntry> entries;
final int index; 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 @override
State<StatefulWidget> createState() => _FullscreenBottomOverlayState(); State<StatefulWidget> createState() => _FullscreenBottomOverlayState();
@ -86,13 +105,15 @@ class _FullscreenBottomOverlayState extends State<FullscreenBottomOverlay> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final innerPadding = EdgeInsets.all(8.0); final innerPadding = EdgeInsets.all(8.0);
final mediaQuery = MediaQuery.of(context); 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( return BlurredRect(
child: Container( child: Container(
color: kOverlayBackground, color: kOverlayBackground,
child: IgnorePointer( child: IgnorePointer(
child: Padding( child: Padding(
padding: mediaQuery.viewInsets + mediaQuery.viewPadding.copyWith(top: 0), padding: viewInsets + viewPadding.copyWith(top: 0),
child: Container( child: Container(
padding: innerPadding, padding: innerPadding,
child: FutureBuilder( child: FutureBuilder(
@ -187,22 +208,26 @@ class _FullscreenBottomOverlayContent extends StatelessWidget {
} }
class OverlayButton extends StatelessWidget { class OverlayButton extends StatelessWidget {
final Animation<double> scale;
final Widget child; final Widget child;
const OverlayButton({this.child}); const OverlayButton({Key key, this.scale, this.child}) : super(key: key);
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return BlurredOval( return ScaleTransition(
child: Material( scale: scale,
type: MaterialType.circle, child: BlurredOval(
color: kOverlayBackground, child: Material(
child: Ink( type: MaterialType.circle,
decoration: BoxDecoration( color: kOverlayBackground,
border: Border.all(color: Colors.white30, width: 0.5), child: Ink(
shape: BoxShape.circle, decoration: BoxDecoration(
border: Border.all(color: Colors.white30, width: 0.5),
shape: BoxShape.circle,
),
child: child,
), ),
child: child,
), ),
), ),
); );