leaks: curved animations
This commit is contained in:
parent
404bc5471b
commit
4116a55f8d
19 changed files with 169 additions and 95 deletions
|
@ -51,6 +51,7 @@ class _RandomTextSpanHighlighter extends StatefulWidget {
|
|||
|
||||
class _RandomTextSpanHighlighterState extends State<_RandomTextSpanHighlighter> with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final CurvedAnimation _animation;
|
||||
late final Animation<TextStyle> _animatedStyle;
|
||||
late final TextStyle _baseStyle;
|
||||
int _highlightedIndex = 0;
|
||||
|
@ -90,14 +91,16 @@ class _RandomTextSpanHighlighterState extends State<_RandomTextSpanHighlighter>
|
|||
}
|
||||
})
|
||||
..repeat(reverse: true);
|
||||
_animatedStyle = ShadowedTextStyleTween(begin: _baseStyle, end: highlightStyle).animate(CurvedAnimation(
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.easeInOutCubic,
|
||||
));
|
||||
);
|
||||
_animatedStyle = ShadowedTextStyleTween(begin: _baseStyle, end: highlightStyle).animate(_animation);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ abstract class ChooserQuickButton<T> extends StatefulWidget {
|
|||
|
||||
abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> extends State<T> with SingleTickerProviderStateMixin {
|
||||
AnimationController? _animationController;
|
||||
Animation<double>? _animation;
|
||||
CurvedAnimation? _animation;
|
||||
OverlayEntry? _chooserOverlayEntry;
|
||||
final ValueNotifier<U?> _chooserValueNotifier = ValueNotifier(null);
|
||||
final StreamController<LongPressMoveUpdateDetails> _moveUpdateStreamController = StreamController.broadcast();
|
||||
|
@ -47,6 +47,7 @@ abstract class ChooserQuickButtonState<T extends ChooserQuickButton<U>, U> exten
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation?.dispose();
|
||||
_animationController?.dispose();
|
||||
_clearChooserOverlayEntry();
|
||||
_chooserValueNotifier.dispose();
|
||||
|
|
|
@ -178,7 +178,7 @@ class ReportOverlay<T> extends StatefulWidget {
|
|||
class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerProviderStateMixin {
|
||||
final processed = <T>{};
|
||||
late AnimationController _animationController;
|
||||
late Animation<double> _animation;
|
||||
late CurvedAnimation _animation;
|
||||
|
||||
Stream<T> get opStream => widget.opStream;
|
||||
|
||||
|
@ -212,6 +212,7 @@ class _ReportOverlayState<T> extends State<ReportOverlay<T>> with SingleTickerPr
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation.dispose();
|
||||
_animationController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
@ -317,6 +318,7 @@ class _FeedbackMessage extends StatefulWidget {
|
|||
|
||||
class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerProviderStateMixin {
|
||||
AnimationController? _animationController;
|
||||
CurvedAnimation? _animation;
|
||||
Animation<int>? _remainingDurationMillis;
|
||||
int? _totalDurationMillis;
|
||||
|
||||
|
@ -333,19 +335,21 @@ class _FeedbackMessageState extends State<_FeedbackMessage> with SingleTickerPro
|
|||
duration: effectiveDuration,
|
||||
vsync: this,
|
||||
);
|
||||
_animation = CurvedAnimation(
|
||||
parent: _animationController!,
|
||||
curve: Curves.linear,
|
||||
);
|
||||
_remainingDurationMillis = IntTween(
|
||||
begin: effectiveDuration.inMilliseconds,
|
||||
end: 0,
|
||||
).animate(CurvedAnimation(
|
||||
parent: _animationController!,
|
||||
curve: Curves.linear,
|
||||
));
|
||||
).animate(_animation!);
|
||||
_animationController!.forward();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation?.dispose();
|
||||
_animationController?.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
|
|||
|
||||
// adapted from Flutter `SnackBar` in `/material/snack_bar.dart`
|
||||
|
||||
// As of Flutter v3.0.1, `SnackBar` is not customizable enough to add margin
|
||||
// As of Flutter v3.23.0, `SnackBar` is not customizable enough to add margin
|
||||
// and ignore pointers in that area, so we use an overlay entry instead.
|
||||
// This overlay entry is not under a `Scaffold` (which is expected by `SnackBar`
|
||||
// and `SnackBarAction`), and is not dismissed the same way.
|
||||
|
@ -73,10 +73,17 @@ class OverlaySnackBar extends StatefulWidget {
|
|||
class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
||||
bool _wasVisible = false;
|
||||
|
||||
CurvedAnimation? _heightAnimation;
|
||||
CurvedAnimation? _fadeInAnimation;
|
||||
CurvedAnimation? _fadeInM3Animation;
|
||||
CurvedAnimation? _fadeOutAnimation;
|
||||
CurvedAnimation? _heightM3Animation;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
widget.animation!.addStatusListener(_onAnimationStatusChanged);
|
||||
_setAnimations();
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -85,26 +92,55 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
if (widget.animation != oldWidget.animation) {
|
||||
oldWidget.animation!.removeStatusListener(_onAnimationStatusChanged);
|
||||
widget.animation!.addStatusListener(_onAnimationStatusChanged);
|
||||
_disposeAnimations();
|
||||
_setAnimations();
|
||||
}
|
||||
}
|
||||
|
||||
void _setAnimations() {
|
||||
assert(widget.animation != null);
|
||||
_heightAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarHeightCurve);
|
||||
_fadeInAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarFadeInCurve);
|
||||
_fadeInM3Animation = CurvedAnimation(parent: widget.animation!, curve: _snackBarM3FadeInCurve);
|
||||
_fadeOutAnimation = CurvedAnimation(
|
||||
parent: widget.animation!,
|
||||
curve: _snackBarFadeOutCurve,
|
||||
reverseCurve: const Threshold(0.0),
|
||||
);
|
||||
// Material 3 Animation has a height animation on entry, but a direct fade out on exit.
|
||||
_heightM3Animation = CurvedAnimation(
|
||||
parent: widget.animation!,
|
||||
curve: _snackBarM3HeightCurve,
|
||||
reverseCurve: const Threshold(0.0),
|
||||
);
|
||||
}
|
||||
|
||||
void _disposeAnimations() {
|
||||
_heightAnimation?.dispose();
|
||||
_fadeInAnimation?.dispose();
|
||||
_fadeInM3Animation?.dispose();
|
||||
_fadeOutAnimation?.dispose();
|
||||
_heightM3Animation?.dispose();
|
||||
_heightAnimation = null;
|
||||
_fadeInAnimation = null;
|
||||
_fadeInM3Animation = null;
|
||||
_fadeOutAnimation = null;
|
||||
_heightM3Animation = null;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
widget.animation!.removeStatusListener(_onAnimationStatusChanged);
|
||||
_disposeAnimations();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onAnimationStatusChanged(AnimationStatus animationStatus) {
|
||||
switch (animationStatus) {
|
||||
case AnimationStatus.dismissed:
|
||||
case AnimationStatus.forward:
|
||||
case AnimationStatus.reverse:
|
||||
break;
|
||||
case AnimationStatus.completed:
|
||||
if (widget.onVisible != null && !_wasVisible) {
|
||||
widget.onVisible!();
|
||||
}
|
||||
_wasVisible = true;
|
||||
if (animationStatus == AnimationStatus.completed) {
|
||||
if (widget.onVisible != null && !_wasVisible) {
|
||||
widget.onVisible!();
|
||||
}
|
||||
_wasVisible = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,28 +209,13 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
|
||||
final double iconHorizontalMargin = (widget.padding?.resolve(TextDirection.ltr).right ?? horizontalPadding) / 12.0;
|
||||
|
||||
final CurvedAnimation heightAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarHeightCurve);
|
||||
final CurvedAnimation fadeInAnimation = CurvedAnimation(parent: widget.animation!, curve: _snackBarFadeInCurve);
|
||||
final CurvedAnimation fadeInM3Animation = CurvedAnimation(parent: widget.animation!, curve: _snackBarM3FadeInCurve);
|
||||
|
||||
final CurvedAnimation fadeOutAnimation = CurvedAnimation(
|
||||
parent: widget.animation!,
|
||||
curve: _snackBarFadeOutCurve,
|
||||
reverseCurve: const Threshold(0.0),
|
||||
);
|
||||
// Material 3 Animation has a height animation on entry, but a direct fade out on exit.
|
||||
final CurvedAnimation heightM3Animation = CurvedAnimation(
|
||||
parent: widget.animation!,
|
||||
curve: _snackBarM3HeightCurve,
|
||||
reverseCurve: const Threshold(0.0),
|
||||
);
|
||||
|
||||
final IconButton? iconButton = showCloseIcon
|
||||
? IconButton(
|
||||
icon: const Icon(Icons.close),
|
||||
iconSize: 24.0,
|
||||
color: widget.closeIconColor ?? snackBarTheme.closeIconColor ?? defaults.closeIconColor,
|
||||
onPressed: () => ScaffoldMessenger.of(context).hideCurrentSnackBar(reason: SnackBarClosedReason.dismiss),
|
||||
tooltip: MaterialLocalizations.of(context).closeButtonTooltip,
|
||||
)
|
||||
: null;
|
||||
|
||||
|
@ -253,7 +274,7 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
child: accessibleNavigation || theme.useMaterial3
|
||||
? snackBar
|
||||
: FadeTransition(
|
||||
opacity: fadeOutAnimation,
|
||||
opacity: _fadeOutAnimation!,
|
||||
child: snackBar,
|
||||
),
|
||||
),
|
||||
|
@ -288,7 +309,7 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
key: const Key('dismissible'),
|
||||
direction: widget.dismissDirection,
|
||||
resizeDuration: null,
|
||||
behavior: widget.hitTestBehavior ?? (widget.margin != null ? HitTestBehavior.deferToChild : HitTestBehavior.opaque),
|
||||
behavior: widget.hitTestBehavior ?? (widget.margin != null || snackBarTheme.insetPadding != null ? HitTestBehavior.deferToChild : HitTestBehavior.opaque),
|
||||
onDismissed: (direction) => widget.onDismiss(),
|
||||
child: snackBar,
|
||||
),
|
||||
|
@ -299,19 +320,19 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
snackBarTransition = snackBar;
|
||||
} else if (isFloatingSnackBar && !theme.useMaterial3) {
|
||||
snackBarTransition = FadeTransition(
|
||||
opacity: fadeInAnimation,
|
||||
opacity: _fadeInAnimation!,
|
||||
child: snackBar,
|
||||
);
|
||||
// Is Material 3 Floating Snack Bar.
|
||||
} else if (isFloatingSnackBar && theme.useMaterial3) {
|
||||
snackBarTransition = FadeTransition(
|
||||
opacity: fadeInM3Animation,
|
||||
child: AnimatedBuilder(
|
||||
animation: heightM3Animation,
|
||||
builder: (context, child) {
|
||||
opacity: _fadeInM3Animation!,
|
||||
child: ValueListenableBuilder<double>(
|
||||
valueListenable: _heightM3Animation!,
|
||||
builder: (context, value, child) {
|
||||
return Align(
|
||||
alignment: AlignmentDirectional.bottomStart,
|
||||
heightFactor: heightM3Animation.value,
|
||||
alignment: Alignment.bottomLeft,
|
||||
heightFactor: value,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
|
@ -319,12 +340,12 @@ class _OverlaySnackBarState extends State<OverlaySnackBar> {
|
|||
),
|
||||
);
|
||||
} else {
|
||||
snackBarTransition = AnimatedBuilder(
|
||||
animation: heightAnimation,
|
||||
builder: (context, child) {
|
||||
snackBarTransition = ValueListenableBuilder<double>(
|
||||
valueListenable: _heightAnimation!,
|
||||
builder: (context, value, child) {
|
||||
return Align(
|
||||
alignment: AlignmentDirectional.topStart,
|
||||
heightFactor: heightAnimation.value,
|
||||
heightFactor: value,
|
||||
child: child,
|
||||
);
|
||||
},
|
||||
|
|
|
@ -121,9 +121,9 @@ class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProv
|
|||
late Offset _longPressLastGlobalPosition;
|
||||
|
||||
late AnimationController _thumbAnimationController;
|
||||
late Animation<double> _thumbAnimation;
|
||||
late CurvedAnimation _thumbAnimation;
|
||||
late AnimationController _labelAnimationController;
|
||||
late Animation<double> _labelAnimation;
|
||||
late CurvedAnimation _labelAnimation;
|
||||
Timer? _fadeoutTimer;
|
||||
Map<double, String>? _percentCrumbs;
|
||||
final Map<double, String> _viewportCrumbs = {};
|
||||
|
@ -167,7 +167,9 @@ class _DraggableScrollbarState extends State<DraggableScrollbar> with TickerProv
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_thumbAnimation.dispose();
|
||||
_thumbAnimationController.dispose();
|
||||
_labelAnimation.dispose();
|
||||
_labelAnimationController.dispose();
|
||||
_fadeoutTimer?.cancel();
|
||||
super.dispose();
|
||||
|
|
|
@ -26,7 +26,7 @@ class AnimatedDiffText extends StatefulWidget {
|
|||
|
||||
class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final Animation<double> _animation;
|
||||
late final CurvedAnimation _animation;
|
||||
final List<_TextDiff> _diffs = [];
|
||||
|
||||
TextStyle get _textStyle {
|
||||
|
@ -66,6 +66,7 @@ class _AnimatedDiffTextState extends State<AnimatedDiffText> with SingleTickerPr
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ class ChangeHighlightText extends StatefulWidget {
|
|||
|
||||
class _ChangeHighlightTextState extends State<ChangeHighlightText> with SingleTickerProviderStateMixin {
|
||||
late final AnimationController _controller;
|
||||
late final CurvedAnimation _animation;
|
||||
late final Animation<TextStyle> _style;
|
||||
|
||||
@override
|
||||
|
@ -33,10 +34,11 @@ class _ChangeHighlightTextState extends State<ChangeHighlightText> with SingleTi
|
|||
)
|
||||
..value = 1
|
||||
..addListener(() => setState(() {}));
|
||||
_style = ShadowedTextStyleTween(begin: widget.changedStyle, end: widget.style).animate(CurvedAnimation(
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: widget.curve,
|
||||
));
|
||||
);
|
||||
_style = ShadowedTextStyleTween(begin: widget.changedStyle, end: widget.style).animate(_animation);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -51,6 +53,7 @@ class _ChangeHighlightTextState extends State<ChangeHighlightText> with SingleTi
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_animation.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,7 @@ class Sweeper extends StatefulWidget {
|
|||
|
||||
class _SweeperState extends State<Sweeper> with SingleTickerProviderStateMixin {
|
||||
late AnimationController _angleAnimationController;
|
||||
late CurvedAnimation _angleAnimation;
|
||||
late Animation<double> _angle;
|
||||
bool _isAppearing = false;
|
||||
|
||||
|
@ -46,13 +47,14 @@ class _SweeperState extends State<Sweeper> with SingleTickerProviderStateMixin {
|
|||
final startAngle = widget.startAngle;
|
||||
final sweepAngle = widget.sweepAngle;
|
||||
final centerSweep = widget.centerSweep;
|
||||
_angleAnimation = CurvedAnimation(
|
||||
parent: _angleAnimationController,
|
||||
curve: widget.curve,
|
||||
);
|
||||
_angle = Tween(
|
||||
begin: startAngle - sweepAngle * (centerSweep ? .5 : 0),
|
||||
end: startAngle + pi * 2 - sweepAngle * (centerSweep ? .5 : 1),
|
||||
).animate(CurvedAnimation(
|
||||
parent: _angleAnimationController,
|
||||
curve: widget.curve,
|
||||
));
|
||||
).animate(_angleAnimation);
|
||||
_angleAnimationController.addStatusListener(_onAnimationStatusChanged);
|
||||
_registerWidget(widget);
|
||||
}
|
||||
|
@ -66,7 +68,7 @@ class _SweeperState extends State<Sweeper> with SingleTickerProviderStateMixin {
|
|||
|
||||
@override
|
||||
void dispose() {
|
||||
_angleAnimationController.removeStatusListener(_onAnimationStatusChanged);
|
||||
_angleAnimation.dispose();
|
||||
_angleAnimationController.dispose();
|
||||
_unregisterWidget(widget);
|
||||
super.dispose();
|
||||
|
|
|
@ -286,9 +286,8 @@ class _EntryLeafletMapState<T> extends State<EntryLeafletMap<T>> with TickerProv
|
|||
final animation = CurvedAnimation(parent: controller, curve: Curves.fastOutSlowIn);
|
||||
controller.addListener(() => animate(animation));
|
||||
animation.addStatusListener((status) {
|
||||
if (status == AnimationStatus.completed) {
|
||||
controller.dispose();
|
||||
} else if (status == AnimationStatus.dismissed) {
|
||||
if (status == AnimationStatus.completed || status == AnimationStatus.dismissed) {
|
||||
animation.dispose();
|
||||
controller.dispose();
|
||||
}
|
||||
});
|
||||
|
|
|
@ -43,7 +43,7 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
|||
final ValueNotifier<Rect> _outlineNotifier = ValueNotifier(Rect.zero);
|
||||
final ValueNotifier<int> _gridDivisionNotifier = ValueNotifier(0);
|
||||
late AnimationController _gridAnimationController;
|
||||
late Animation<double> _gridOpacity;
|
||||
late CurvedAnimation _gridOpacity;
|
||||
|
||||
static const double minDimension = Cropper.handleDimension;
|
||||
static const int panResizeGridDivision = 3;
|
||||
|
@ -87,6 +87,7 @@ class _CropperState extends State<Cropper> with SingleTickerProviderStateMixin {
|
|||
_viewportSizeNotifier.dispose();
|
||||
_outlineNotifier.dispose();
|
||||
_gridDivisionNotifier.dispose();
|
||||
_gridOpacity.dispose();
|
||||
_gridAnimationController.dispose();
|
||||
_unregisterWidget(widget);
|
||||
super.dispose();
|
||||
|
|
|
@ -103,7 +103,7 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
final ValueNotifier<double> _overlayOpacityNotifier = ValueNotifier(1);
|
||||
final ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
|
||||
late AnimationController _overlayAnimationController;
|
||||
late Animation<double> _overlayScale, _scrollerSize;
|
||||
late CurvedAnimation _overlayScale, _scrollerSize;
|
||||
CoordinateFilter? _regionFilter;
|
||||
|
||||
CollectionLens? get regionCollection => _regionCollectionNotifier.value;
|
||||
|
@ -170,6 +170,8 @@ class _ContentState extends State<_Content> with SingleTickerProviderStateMixin
|
|||
_dotEntryNotifier.dispose();
|
||||
_overlayOpacityNotifier.dispose();
|
||||
_overlayVisible.dispose();
|
||||
_overlayScale.dispose();
|
||||
_scrollerSize.dispose();
|
||||
_overlayAnimationController.dispose();
|
||||
|
||||
// provided collection should be a new instance specifically created
|
||||
|
|
|
@ -25,7 +25,8 @@ class FloatingNavBar extends StatefulWidget {
|
|||
class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProviderStateMixin {
|
||||
final List<StreamSubscription> _subscriptions = [];
|
||||
late AnimationController _controller;
|
||||
late Animation<Offset> _offsetAnimation;
|
||||
late CurvedAnimation _animation;
|
||||
late Animation<Offset> _offset;
|
||||
double? _lastOffset;
|
||||
bool _isDragging = false;
|
||||
|
||||
|
@ -36,13 +37,14 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
|
|||
duration: const Duration(milliseconds: 200),
|
||||
vsync: this,
|
||||
);
|
||||
_offsetAnimation = Tween<Offset>(
|
||||
begin: const Offset(0, 0),
|
||||
end: const Offset(0, 1),
|
||||
).animate(CurvedAnimation(
|
||||
_animation = CurvedAnimation(
|
||||
parent: _controller,
|
||||
curve: Curves.linear,
|
||||
))
|
||||
);
|
||||
_offset = Tween<Offset>(
|
||||
begin: const Offset(0, 0),
|
||||
end: const Offset(0, 1),
|
||||
).animate(_animation)
|
||||
..addListener(() {
|
||||
if (!mounted) return;
|
||||
setState(() {});
|
||||
|
@ -63,6 +65,8 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
|
|||
@override
|
||||
void dispose() {
|
||||
_unregisterWidget(widget);
|
||||
_animation.dispose();
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
@ -82,7 +86,7 @@ class _FloatingNavBarState extends State<FloatingNavBar> with SingleTickerProvid
|
|||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return SlideTransition(
|
||||
position: _offsetAnimation,
|
||||
position: _offset,
|
||||
child: widget.child,
|
||||
);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ class ViewerController with CastMixin {
|
|||
late final ValueNotifier<bool> _autopilotNotifier;
|
||||
Timer? _playTimer;
|
||||
final StreamController _streamController = StreamController.broadcast();
|
||||
final Map<TickerProvider, AnimationController> _autopilotAnimationControllers = {};
|
||||
final Map<TickerProvider, _AutopilotAnimators> _autopilotAnimators = {};
|
||||
ScaleLevel? _autopilotInitialScale;
|
||||
|
||||
Stream<dynamic> get _events => _streamController.stream;
|
||||
|
@ -94,9 +94,12 @@ class ViewerController with CastMixin {
|
|||
|
||||
void _stopPlayTimer() => _playTimer?.cancel();
|
||||
|
||||
void _clearAutopilotAnimations() => _autopilotAnimationControllers.keys.toSet().forEach((v) => stopAutopilotAnimation(vsync: v));
|
||||
void _clearAutopilotAnimations() => _autopilotAnimators.keys.toSet().forEach((v) => stopAutopilotAnimation(vsync: v));
|
||||
|
||||
void stopAutopilotAnimation({required TickerProvider vsync}) => _autopilotAnimationControllers.remove(vsync)?.dispose();
|
||||
void stopAutopilotAnimation({required TickerProvider vsync}) {
|
||||
final animationController = _autopilotAnimators.remove(vsync);
|
||||
return animationController?.dispose();
|
||||
}
|
||||
|
||||
void startAutopilotAnimation({
|
||||
required TickerProvider vsync,
|
||||
|
@ -113,16 +116,37 @@ class ViewerController with CastMixin {
|
|||
duration: autopilotInterval,
|
||||
vsync: vsync,
|
||||
);
|
||||
animationController.addListener(() => onUpdate.call(
|
||||
scaleLevel: ScaleLevel(
|
||||
ref: scaleLevelRef,
|
||||
factor: scaleFactorTween.evaluate(CurvedAnimation(
|
||||
parent: animationController,
|
||||
curve: Curves.linear,
|
||||
)),
|
||||
),
|
||||
));
|
||||
_autopilotAnimationControllers[vsync] = animationController;
|
||||
Future.delayed(ADurations.viewerHorizontalPageAnimation).then((_) => _autopilotAnimationControllers[vsync]?.forward());
|
||||
final animation = CurvedAnimation(
|
||||
parent: animationController,
|
||||
curve: Curves.linear,
|
||||
);
|
||||
animationController.addListener(() {
|
||||
return onUpdate.call(
|
||||
scaleLevel: ScaleLevel(
|
||||
ref: scaleLevelRef,
|
||||
factor: scaleFactorTween.evaluate(animation),
|
||||
),
|
||||
);
|
||||
});
|
||||
_autopilotAnimators[vsync] = _AutopilotAnimators(
|
||||
controller: animationController,
|
||||
animation: animation,
|
||||
);
|
||||
Future.delayed(ADurations.viewerHorizontalPageAnimation).then((_) => _autopilotAnimators[vsync]?.controller.forward());
|
||||
}
|
||||
}
|
||||
|
||||
class _AutopilotAnimators {
|
||||
final AnimationController controller;
|
||||
final CurvedAnimation animation;
|
||||
|
||||
_AutopilotAnimators({
|
||||
required this.controller,
|
||||
required this.animation,
|
||||
});
|
||||
|
||||
void dispose() {
|
||||
animation.dispose();
|
||||
controller.dispose();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -73,7 +73,7 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
final ValueNotifier<bool> _viewLocked = ValueNotifier(false);
|
||||
final ValueNotifier<bool> _overlayExpandedNotifier = ValueNotifier(false);
|
||||
late AnimationController _verticalPageAnimationController, _overlayAnimationController;
|
||||
late Animation<double> _overlayButtonScale, _overlayVideoControlScale, _overlayOpacity;
|
||||
late CurvedAnimation _overlayButtonScale, _overlayVideoControlScale, _overlayOpacity, _overlayTopOffsetAnimation;
|
||||
late Animation<Offset> _overlayTopOffset;
|
||||
EdgeInsets? _frozenViewInsets, _frozenViewPadding;
|
||||
late VideoActionDelegate _videoActionDelegate;
|
||||
|
@ -158,10 +158,11 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
parent: _overlayAnimationController,
|
||||
curve: Curves.easeOutQuad,
|
||||
);
|
||||
_overlayTopOffset = Tween(begin: const Offset(0, -1), end: const Offset(0, 0)).animate(CurvedAnimation(
|
||||
_overlayTopOffsetAnimation = CurvedAnimation(
|
||||
parent: _overlayAnimationController,
|
||||
curve: Curves.easeOutQuad,
|
||||
));
|
||||
);
|
||||
_overlayTopOffset = Tween(begin: const Offset(0, -1), end: const Offset(0, 0)).animate(_overlayTopOffsetAnimation);
|
||||
_overlayVisible.value = settings.showOverlayOnOpening && !viewerController.autopilot;
|
||||
_overlayVisible.addListener(_onOverlayVisibleChanged);
|
||||
_viewLocked.addListener(_onViewLockedChanged);
|
||||
|
@ -188,6 +189,10 @@ class _EntryViewerStackState extends State<EntryViewerStack> with EntryViewContr
|
|||
cleanEntryControllers(entryNotifier.value);
|
||||
_videoActionDelegate.dispose();
|
||||
_verticalPageAnimationController.dispose();
|
||||
_overlayButtonScale.dispose();
|
||||
_overlayVideoControlScale.dispose();
|
||||
_overlayOpacity.dispose();
|
||||
_overlayTopOffsetAnimation.dispose();
|
||||
_overlayAnimationController.dispose();
|
||||
_overlayVisible.dispose();
|
||||
_viewLocked.dispose();
|
||||
|
|
|
@ -133,7 +133,7 @@ class _BottomOverlayContent extends StatefulWidget {
|
|||
|
||||
class _BottomOverlayContentState extends State<_BottomOverlayContent> {
|
||||
final FocusScopeNode _buttonRowFocusScopeNode = FocusScopeNode();
|
||||
late Animation<double> _buttonScale, _thumbnailOpacity;
|
||||
late CurvedAnimation _buttonScale, _thumbnailOpacity;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -170,7 +170,8 @@ class _BottomOverlayContentState extends State<_BottomOverlayContent> {
|
|||
}
|
||||
|
||||
void _unregisterWidget(_BottomOverlayContent widget) {
|
||||
// nothing
|
||||
_buttonScale.dispose();
|
||||
_thumbnailOpacity.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -25,7 +25,7 @@ class ViewerLockedOverlay extends StatefulWidget {
|
|||
}
|
||||
|
||||
class _ViewerLockedOverlayState extends State<ViewerLockedOverlay> {
|
||||
late Animation<double> _buttonScale;
|
||||
late CurvedAnimation _buttonScale;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
|
@ -55,7 +55,7 @@ class _ViewerLockedOverlayState extends State<ViewerLockedOverlay> {
|
|||
}
|
||||
|
||||
void _unregisterWidget(ViewerLockedOverlay widget) {
|
||||
// nothing
|
||||
_buttonScale.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -25,7 +25,7 @@ class SlideshowButtons extends StatefulWidget {
|
|||
|
||||
class _SlideshowButtonsState extends State<SlideshowButtons> {
|
||||
final FocusScopeNode _buttonRowFocusScopeNode = FocusScopeNode();
|
||||
late Animation<double> _buttonScale;
|
||||
late CurvedAnimation _buttonScale;
|
||||
|
||||
static const List<SlideshowAction> _actions = [
|
||||
SlideshowAction.resume,
|
||||
|
@ -65,7 +65,7 @@ class _SlideshowButtonsState extends State<SlideshowButtons> {
|
|||
}
|
||||
|
||||
void _unregisterWidget(SlideshowButtons widget) {
|
||||
// nothing
|
||||
_buttonScale.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
|
|
|
@ -71,7 +71,7 @@ class EntryEditor extends StatefulWidget {
|
|||
class _EntryEditorState extends State<EntryEditor> with EntryViewControllerMixin, SingleTickerProviderStateMixin {
|
||||
final ValueNotifier<bool> _overlayVisible = ValueNotifier(true);
|
||||
late AnimationController _overlayAnimationController;
|
||||
late Animation<double> _overlayVideoControlScale;
|
||||
late CurvedAnimation _overlayVideoControlScale;
|
||||
EdgeInsets? _frozenViewInsets, _frozenViewPadding;
|
||||
late VideoActionDelegate _videoActionDelegate;
|
||||
late final ViewerController _viewerController;
|
||||
|
@ -120,6 +120,7 @@ class _EntryEditorState extends State<EntryEditor> with EntryViewControllerMixin
|
|||
void dispose() {
|
||||
cleanEntryControllers(entry);
|
||||
_overlayVisible.dispose();
|
||||
_overlayVideoControlScale.dispose();
|
||||
_overlayAnimationController.dispose();
|
||||
_videoActionDelegate.dispose();
|
||||
_viewerController.dispose();
|
||||
|
|
|
@ -176,7 +176,7 @@ flutter:
|
|||
# and `_PackageLicensePage` in `/material/about.dart`
|
||||
#
|
||||
# `OverlaySnackBar` in `/widgets/common/action_mixins/overlay_snack_bar.dart`
|
||||
# adapts from Flutter v3.16.0 `SnackBar` in `/material/snack_bar.dart`
|
||||
# adapts from Flutter v3.23.0 `SnackBar` in `/material/snack_bar.dart`
|
||||
#
|
||||
# `EagerScaleGestureRecognizer` in `/widgets/common/behaviour/eager_scale_gesture_recognizer.dart`
|
||||
# adapts from Flutter v3.16.0 `ScaleGestureRecognizer` in `/gestures/scale.dart`
|
||||
|
|
Loading…
Reference in a new issue