accessibility: remove animations (overlay)
This commit is contained in:
parent
2280a3fa65
commit
808d2de3bd
15 changed files with 69 additions and 57 deletions
|
@ -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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
),
|
||||
),
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in a new issue