accessibility: remove animations (overlay)

This commit is contained in:
Thibault Deckers 2021-09-28 14:32:15 +09:00
parent 2280a3fa65
commit 808d2de3bd
15 changed files with 69 additions and 57 deletions

View file

@ -16,9 +16,6 @@ class Durations {
static const iconAnimation = Duration(milliseconds: 300);
static const sweeperOpacityAnimation = Duration(milliseconds: 150);
static const sweepingAnimation = Duration(milliseconds: 650);
// static const staggeredAnimation = Duration(milliseconds: 375);
// static const staggeredAnimationPageTarget = Duration(milliseconds: 800);
static const dialogFieldReachAnimation = Duration(milliseconds: 300);
static const appBarTitleAnimation = Duration(milliseconds: 300);
@ -43,9 +40,6 @@ class Durations {
static const filterRowExpandAnimation = Duration(milliseconds: 300);
// viewer animations
static const viewerVerticalPageScrollAnimation = Duration(milliseconds: 500);
static const viewerOverlayAnimation = Duration(milliseconds: 200);
static const viewerOverlayChangeAnimation = Duration(milliseconds: 150);
static const thumbnailScrollerScrollAnimation = Duration(milliseconds: 200);
static const thumbnailScrollerShadeAnimation = Duration(milliseconds: 150);
static const viewerVideoPlayerTransition = Duration(milliseconds: 500);
@ -67,8 +61,6 @@ class Durations {
static const highlightScrollInitDelay = Duration(milliseconds: 800);
static const videoOverlayHideDelay = Duration(milliseconds: 500);
static const videoProgressTimerInterval = Duration(milliseconds: 300);
// static Duration staggeredAnimationDelay = Durations.staggeredAnimation ~/ 6 * timeDilation;
static const doubleBackTimerDelay = Duration(milliseconds: 1000);
static const softKeyboardDisplayDelay = Duration(milliseconds: 300);
static const searchDebounceDelay = Duration(milliseconds: 250);
@ -107,6 +99,11 @@ class DurationsData {
final Duration staggeredAnimation;
final Duration staggeredAnimationPageTarget;
// viewer animations
final Duration viewerVerticalPageScrollAnimation;
final Duration viewerOverlayAnimation;
final Duration viewerOverlayChangeAnimation;
// delays & refresh intervals
final Duration staggeredAnimationDelay;
@ -114,13 +111,20 @@ class DurationsData {
this.expansionTileAnimation = const Duration(milliseconds: 200),
this.staggeredAnimation = const Duration(milliseconds: 375),
this.staggeredAnimationPageTarget = const Duration(milliseconds: 800),
this.viewerVerticalPageScrollAnimation = const Duration(milliseconds: 500),
this.viewerOverlayAnimation = const Duration(milliseconds: 200),
this.viewerOverlayChangeAnimation = const Duration(milliseconds: 150),
}) : staggeredAnimationDelay = staggeredAnimation ~/ 6;
factory DurationsData.noAnimation() {
return DurationsData(
expansionTileAnimation: const Duration(microseconds: 1), // as of Flutter v2.5.1, `ExpansionPanelList` throws if animation duration is zero
// as of Flutter v2.5.1, `ExpansionPanelList` throws if animation duration is zero
expansionTileAnimation: const Duration(microseconds: 1),
staggeredAnimation: Duration.zero,
staggeredAnimationPageTarget: Duration.zero,
viewerVerticalPageScrollAnimation: Duration.zero,
viewerOverlayAnimation: Duration.zero,
viewerOverlayChangeAnimation: Duration.zero,
);
}
}

View file

@ -72,7 +72,7 @@ class InteractiveThumbnail extends StatelessWidget {
context,
TransparentMaterialPageRoute(
settings: const RouteSettings(name: EntryViewerPage.routeName),
pageBuilder: (c, a, sa) {
pageBuilder: (context, a, sa) {
final viewerCollection = CollectionLens(
source: collection.source,
filters: collection.filters,

View file

@ -14,8 +14,8 @@ class BottomGestureAreaProtector extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Selector<MediaQueryData, double>(
selector: (c, mq) => mq.systemGestureInsets.bottom,
builder: (c, systemGestureBottom, child) {
selector: (context, mq) => mq.systemGestureInsets.bottom,
builder: (context, systemGestureBottom, child) {
return Positioned(
left: 0,
right: 0,

View file

@ -19,7 +19,7 @@ class DirectMaterialPageRoute<T> extends PageRouteBuilder<T> {
}) : super(
settings: settings,
transitionDuration: Duration.zero,
pageBuilder: (c, a, sa) => builder(c),
pageBuilder: (context, a, sa) => builder(context),
);
@override

View file

@ -88,11 +88,12 @@ class MapButtonPanel extends StatelessWidget {
builder: (context, bounds, child) {
final degrees = bounds.rotation;
final opacity = degrees == 0 ? .0 : 1.0;
final animationDuration = context.select<DurationsData, Duration>((v) => v.viewerOverlayAnimation);
return IgnorePointer(
ignoring: opacity == 0,
child: AnimatedOpacity(
opacity: opacity,
duration: Durations.viewerOverlayAnimation,
duration: animationDuration,
child: MapOverlayButton(
icon: Transform(
origin: iconSize.center(Offset.zero),

View file

@ -70,8 +70,8 @@ class _AppDrawerState extends State<AppDrawer> {
child: ListTileTheme.merge(
selectedColor: Theme.of(context).colorScheme.secondary,
child: Selector<MediaQueryData, double>(
selector: (c, mq) => mq.effectiveBottomPadding,
builder: (c, mqPaddingBottom, child) {
selector: (context, mq) => mq.effectiveBottomPadding,
builder: (context, mqPaddingBottom, child) {
final iconTheme = IconTheme.of(context);
return SingleChildScrollView(
padding: EdgeInsets.only(bottom: mqPaddingBottom),

View file

@ -108,7 +108,7 @@ class _MapPageContentState extends State<MapPageContent> with SingleTickerProvid
_dotEntryNotifier.addListener(_updateInfoEntry);
_overlayAnimationController = AnimationController(
duration: Durations.viewerOverlayAnimation,
duration: context.read<DurationsData>().viewerOverlayAnimation,
vsync: this,
);
_overlayScale = CurvedAnimation(
@ -238,8 +238,8 @@ class _MapPageContentState extends State<MapPageContent> with SingleTickerProvid
),
const SizedBox(height: 8),
Selector<MediaQueryData, double>(
selector: (c, mq) => mq.size.width,
builder: (c, mqWidth, child) => ValueListenableBuilder<CollectionLens?>(
selector: (context, mq) => mq.size.width,
builder: (context, mqWidth, child) => ValueListenableBuilder<CollectionLens?>(
valueListenable: _regionCollectionNotifier,
builder: (context, regionCollection, child) {
final regionEntries = regionCollection?.sortedEntries ?? [];
@ -333,7 +333,7 @@ class _MapPageContentState extends State<MapPageContent> with SingleTickerProvid
context,
TransparentMaterialPageRoute(
settings: const RouteSettings(name: EntryViewerPage.routeName),
pageBuilder: (c, a, sa) {
pageBuilder: (context, a, sa) {
return EntryViewerPage(
collection: regionCollection,
initialEntry: initialEntry,

View file

@ -75,7 +75,7 @@ class EmbeddedDataOpener extends StatelessWidget with FeedbackMixin {
context,
TransparentMaterialPageRoute(
settings: const RouteSettings(name: EntryViewerPage.routeName),
pageBuilder: (c, a, sa) => EntryViewerPage(
pageBuilder: (context, a, sa) => EntryViewerPage(
initialEntry: tempEntry,
),
),

View file

@ -101,7 +101,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
_horizontalPager = PageController(initialPage: _currentHorizontalPage);
_verticalPager = PageController(initialPage: _currentVerticalPage.value)..addListener(_onVerticalPageControllerChange);
_overlayAnimationController = AnimationController(
duration: Durations.viewerOverlayAnimation,
duration: context.read<DurationsData>().viewerOverlayAnimation,
vsync: this,
);
_topOverlayScale = CurvedAnimation(
@ -355,8 +355,8 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
);
child = Selector<MediaQueryData, double>(
selector: (c, mq) => mq.size.height,
builder: (c, mqHeight, child) {
selector: (context, mq) => mq.size.height,
builder: (context, mqHeight, child) {
// when orientation change, the `PageController` offset is not updated right away
// and it does not trigger its listeners when it does, so we force a refresh in the next frame
WidgetsBinding.instance!.addPostFrameCallback((_) => _onVerticalPageControllerChange());
@ -412,13 +412,18 @@ class _EntryViewerStackState extends State<EntryViewerStack> with FeedbackMixin,
);
}
Future<void> _goToVerticalPage(int page) {
// duration & curve should feel similar to changing page by vertical fling
return _verticalPager.animateToPage(
page,
duration: Durations.viewerVerticalPageScrollAnimation,
curve: Curves.easeOutQuart,
);
Future<void> _goToVerticalPage(int page) async {
final animationDuration = context.read<DurationsData>().viewerVerticalPageScrollAnimation;
if (animationDuration > Duration.zero) {
// duration & curve should feel similar to changing page by vertical fling
await _verticalPager.animateToPage(
page,
duration: animationDuration,
curve: Curves.easeOutQuart,
);
} else {
_verticalPager.jumpToPage(page);
}
}
void _onVerticalPageChanged(int page) {

View file

@ -47,8 +47,8 @@ class _InfoPageState extends State<InfoPage> {
child: NotificationListener<ScrollNotification>(
onNotification: _handleTopScroll,
child: Selector<MediaQueryData, double>(
selector: (c, mq) => mq.size.width,
builder: (c, mqWidth, child) {
selector: (context, mq) => mq.size.width,
builder: (context, mqWidth, child) {
return ValueListenableBuilder<AvesEntry?>(
valueListenable: widget.entryNotifier,
builder: (context, mainEntry, child) {
@ -109,10 +109,11 @@ class _InfoPageState extends State<InfoPage> {
}
void _goToViewer() {
final animationDuration = context.read<DurationsData>().viewerVerticalPageScrollAnimation;
ShowImageNotification().dispatch(context);
_scrollController.animateTo(
0,
duration: Durations.viewerVerticalPageScrollAnimation,
duration: animationDuration,
curve: Curves.easeInOut,
);
}

View file

@ -82,8 +82,8 @@ class _ViewerBottomOverlayState extends State<ViewerBottomOverlay> {
return BlurredRect(
enabled: hasEdgeContent && blurred,
child: Selector<MediaQueryData, Tuple3<double, EdgeInsets, EdgeInsets>>(
selector: (c, mq) => Tuple3(mq.size.width, mq.viewInsets, mq.viewPadding),
builder: (c, mq, child) {
selector: (context, mq) => Tuple3(mq.size.width, mq.viewInsets, mq.viewPadding),
builder: (context, mq, child) {
final mqWidth = mq.item1;
final mqViewInsets = mq.item2;
final mqViewPadding = mq.item3;
@ -176,12 +176,12 @@ class _BottomOverlayContent extends AnimatedWidget {
child: SizedBox(
width: availableWidth,
child: Selector<MediaQueryData, Orientation>(
selector: (c, mq) => mq.orientation,
builder: (c, orientation, child) {
selector: (context, mq) => mq.orientation,
builder: (context, orientation, child) {
Widget? infoColumn;
if (settings.showOverlayInfo) {
infoColumn = _buildInfoColumn(orientation);
infoColumn = _buildInfoColumn(context, orientation);
}
if (mainEntry.isMultiPage && multiPageController != null) {
@ -205,12 +205,13 @@ class _BottomOverlayContent extends AnimatedWidget {
);
}
Widget _buildInfoColumn(Orientation orientation) {
Widget _buildInfoColumn(BuildContext context, Orientation orientation) {
final infoMaxWidth = availableWidth - infoPadding.horizontal;
final twoColumns = orientation == Orientation.landscape && infoMaxWidth / 2 > _subRowMinWidth;
final subRowWidth = twoColumns ? min(_subRowMinWidth, infoMaxWidth / 2) : infoMaxWidth;
final positionTitle = _PositionTitleRow(entry: pageEntry, collectionPosition: position, multiPageController: multiPageController);
final hasShootingDetails = details != null && !details!.isEmpty && settings.showOverlayShootingDetails;
final animationDuration = context.select<DurationsData, Duration>((v) => v.viewerOverlayChangeAnimation);
return Padding(
padding: infoPadding,
@ -219,7 +220,7 @@ class _BottomOverlayContent extends AnimatedWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (positionTitle.isNotEmpty) positionTitle,
_buildSoloLocationRow(),
_buildSoloLocationRow(animationDuration),
if (twoColumns)
Padding(
padding: const EdgeInsets.only(top: _interRowPadding),
@ -231,7 +232,7 @@ class _BottomOverlayContent extends AnimatedWidget {
entry: pageEntry,
multiPageController: multiPageController,
)),
_buildDuoShootingRow(subRowWidth, hasShootingDetails),
_buildDuoShootingRow(subRowWidth, hasShootingDetails, animationDuration),
],
),
)
@ -244,15 +245,15 @@ class _BottomOverlayContent extends AnimatedWidget {
multiPageController: multiPageController,
),
),
_buildSoloShootingRow(subRowWidth, hasShootingDetails),
_buildSoloShootingRow(subRowWidth, hasShootingDetails, animationDuration),
],
],
),
);
}
Widget _buildSoloLocationRow() => AnimatedSwitcher(
duration: Durations.viewerOverlayChangeAnimation,
Widget _buildSoloLocationRow(Duration animationDuration) => AnimatedSwitcher(
duration: animationDuration,
switchInCurve: Curves.easeInOutCubic,
switchOutCurve: Curves.easeInOutCubic,
transitionBuilder: _soloTransition,
@ -264,8 +265,8 @@ class _BottomOverlayContent extends AnimatedWidget {
: const SizedBox(),
);
Widget _buildSoloShootingRow(double subRowWidth, bool hasShootingDetails) => AnimatedSwitcher(
duration: Durations.viewerOverlayChangeAnimation,
Widget _buildSoloShootingRow(double subRowWidth, bool hasShootingDetails, Duration animationDuration) => AnimatedSwitcher(
duration: animationDuration,
switchInCurve: Curves.easeInOutCubic,
switchOutCurve: Curves.easeInOutCubic,
transitionBuilder: _soloTransition,
@ -278,8 +279,8 @@ class _BottomOverlayContent extends AnimatedWidget {
: const SizedBox(),
);
Widget _buildDuoShootingRow(double subRowWidth, bool hasShootingDetails) => AnimatedSwitcher(
duration: Durations.viewerOverlayChangeAnimation,
Widget _buildDuoShootingRow(double subRowWidth, bool hasShootingDetails, Duration animationDuration) => AnimatedSwitcher(
duration: animationDuration,
switchInCurve: Curves.easeInOutCubic,
switchOutCurve: Curves.easeInOutCubic,
transitionBuilder: (child, animation) => FadeTransition(

View file

@ -81,8 +81,8 @@ class _VideoControlOverlayState extends State<VideoControlOverlay> with SingleTi
);
} else {
child = Selector<MediaQueryData, double>(
selector: (c, mq) => mq.size.width - mq.padding.horizontal,
builder: (c, mqWidth, child) {
selector: (context, mq) => mq.size.width - mq.padding.horizontal,
builder: (context, mqWidth, child) {
final buttonWidth = OverlayButton.getSize(context);
final availableCount = ((mqWidth - outerPadding * 2) / (buttonWidth + innerPadding)).floor();
final quickActions = settings.videoQuickActions.take(availableCount - 1).toList();

View file

@ -45,8 +45,8 @@ class ViewerTopOverlay extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(outerPadding),
child: Selector<MediaQueryData, double>(
selector: (c, mq) => mq.size.width - mq.padding.horizontal,
builder: (c, mqWidth, child) {
selector: (context, mq) => mq.size.width - mq.padding.horizontal,
builder: (context, mqWidth, child) {
final buttonWidth = OverlayButton.getSize(context);
final availableCount = ((mqWidth - outerPadding * 2 - buttonWidth) / (buttonWidth + innerPadding)).floor();

View file

@ -90,8 +90,8 @@ class _PanoramaPageState extends State<PanoramaPage> {
return Visibility(
visible: overlayVisible,
child: Selector<MediaQueryData, EdgeInsets>(
selector: (c, mq) => mq.viewPadding + mq.viewInsets,
builder: (c, mqPadding, child) {
selector: (context, mq) => mq.viewPadding + mq.viewInsets,
builder: (context, mqPadding, child) {
return Padding(
padding: const EdgeInsets.all(8) + EdgeInsets.only(right: mqPadding.right, bottom: mqPadding.bottom),
child: OverlayButton(

View file

@ -48,8 +48,8 @@ class VideoSubtitles extends StatelessWidget {
);
return Selector<MediaQueryData, Orientation>(
selector: (c, mq) => mq.orientation,
builder: (c, orientation, child) {
selector: (context, mq) => mq.orientation,
builder: (context, orientation, child) {
final bottom = orientation == Orientation.portrait ? .5 : .8;
final viewportSize = context.read<MediaQueryData>().size;