mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Address follow up to https://github.com/flutter/flutter/pull/20354 (#21181)
This commit is contained in:
parent
44cdb049d8
commit
0e9cfe7df1
@ -7,8 +7,8 @@ import 'dart:ui' as ui show lerpDouble;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/physics.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
|
||||
import 'animation.dart';
|
||||
import 'curves.dart';
|
||||
@ -44,7 +44,7 @@ const Tolerance _kFlingTolerance = Tolerance(
|
||||
/// When [AccessibilityFeatures.disableAnimations] is true, the device is asking
|
||||
/// flutter to reduce or disable animations as much as possible. To honor this,
|
||||
/// we reduce the duration and the corresponding number of frames for animations.
|
||||
/// This enum is used to allow certain [AnimationControllers] to opt out of this
|
||||
/// This enum is used to allow certain [AnimationController]s to opt out of this
|
||||
/// behavior.
|
||||
///
|
||||
/// For example, the [AnimationController] which controls the physics simulation
|
||||
@ -200,9 +200,9 @@ class AnimationController extends Animation<double>
|
||||
/// The behavior of the controller when [AccessibilityFeatures.disableAnimations]
|
||||
/// is true.
|
||||
///
|
||||
/// Defaults to [AnimationBehavior.normal] for the [new AnimationBehavior]
|
||||
/// Defaults to [AnimationBehavior.normal] for the [new AnimationController]
|
||||
/// constructor, and [AnimationBehavior.preserve] for the
|
||||
/// [new AnimationBehavior.unbounded] constructor.
|
||||
/// [new AnimationController.unbounded] constructor.
|
||||
final AnimationBehavior animationBehavior;
|
||||
|
||||
/// Returns an [Animation<double>] for this animation controller, so that a
|
||||
@ -401,9 +401,12 @@ class AnimationController extends Animation<double>
|
||||
TickerFuture _animateToInternal(double target, { Duration duration, Curve curve = Curves.linear, AnimationBehavior animationBehavior }) {
|
||||
final AnimationBehavior behavior = animationBehavior ?? this.animationBehavior;
|
||||
double scale = 1.0;
|
||||
if (SemanticsBinding.instance.disableAnimations) {
|
||||
if (_ticker.disableAnimations) {
|
||||
switch (behavior) {
|
||||
case AnimationBehavior.normal:
|
||||
// Since the framework cannot handle zero duration animations, we run it at 5% of the normal
|
||||
// duration to limit most animations to a single frame.
|
||||
// TODO(jonahwilliams): determine a better process for setting duration.
|
||||
scale = 0.05;
|
||||
break;
|
||||
case AnimationBehavior.preserve:
|
||||
@ -487,15 +490,17 @@ class AnimationController extends Animation<double>
|
||||
/// The most recently returned [TickerFuture], if any, is marked as having been
|
||||
/// canceled, meaning the future never completes and its [TickerFuture.orCancel]
|
||||
/// derivative future completes with a [TickerCanceled] error.
|
||||
TickerFuture fling({ double velocity = 1.0, AnimationBehavior animationBehavior}) {
|
||||
TickerFuture fling({ double velocity = 1.0, AnimationBehavior animationBehavior }) {
|
||||
_direction = velocity < 0.0 ? _AnimationDirection.reverse : _AnimationDirection.forward;
|
||||
final double target = velocity < 0.0 ? lowerBound - _kFlingTolerance.distance
|
||||
: upperBound + _kFlingTolerance.distance;
|
||||
double scale = 1.0;
|
||||
final AnimationBehavior behavior = animationBehavior ?? this.animationBehavior;
|
||||
if (SemanticsBinding.instance.disableAnimations) {
|
||||
if (_ticker.disableAnimations) {
|
||||
switch (behavior) {
|
||||
case AnimationBehavior.normal:
|
||||
// TODO(jonahwilliams): determine a better process for setting velocity.
|
||||
// the value below was arbitrarily chosen because it worked for the drawer widget.
|
||||
scale = 200.0;
|
||||
break;
|
||||
case AnimationBehavior.preserve:
|
||||
|
@ -68,6 +68,12 @@ class Ticker {
|
||||
|
||||
TickerFuture _future;
|
||||
|
||||
/// Whether or not the platform is requesting that animations be disabled.
|
||||
///
|
||||
/// See also:
|
||||
/// * [AccessibilityFeatures.disableAnimations], for the setting this value comes from.
|
||||
bool disableAnimations = false;
|
||||
|
||||
/// Whether this ticker has been silenced.
|
||||
///
|
||||
/// While silenced, a ticker's clock can still run, but the callback will not
|
||||
@ -273,6 +279,7 @@ class Ticker {
|
||||
assert(_startTime == null);
|
||||
assert(_animationId == null);
|
||||
assert((originalTicker._future == null) == (originalTicker._startTime == null), 'Cannot absorb Ticker after it has been disposed.');
|
||||
disableAnimations = originalTicker.disableAnimations;
|
||||
if (originalTicker._future != null) {
|
||||
_future = originalTicker._future;
|
||||
_startTime = originalTicker._startTime;
|
||||
|
@ -5,12 +5,12 @@
|
||||
import 'dart:ui' as ui show AccessibilityFeatures, window;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
|
||||
export 'dart:ui' show AccessibilityFeatures;
|
||||
|
||||
/// The glue between the semantics layer and the Flutter engine.
|
||||
// TODO(jonahwilliams): move the remaining semantic related bindings here.
|
||||
class SemanticsBinding extends BindingBase with ServicesBinding {
|
||||
class SemanticsBinding extends BindingBase {
|
||||
// This class is intended to be used as a mixin, and should not be
|
||||
// extended directly.
|
||||
factory SemanticsBinding._() => null;
|
||||
@ -23,7 +23,7 @@ class SemanticsBinding extends BindingBase with ServicesBinding {
|
||||
void initInstances() {
|
||||
super.initInstances();
|
||||
_instance = this;
|
||||
_accessibilityFeatures = ui.window.accessibilityFeatures;
|
||||
_accessibilityFeatures = new ValueNotifier<ui.AccessibilityFeatures>(ui.window.accessibilityFeatures);
|
||||
}
|
||||
|
||||
/// Called when the platform accessibility features change.
|
||||
@ -31,7 +31,7 @@ class SemanticsBinding extends BindingBase with ServicesBinding {
|
||||
/// See [Window.onAccessibilityFeaturesChanged].
|
||||
@protected
|
||||
void handleAccessibilityFeaturesChanged() {
|
||||
_accessibilityFeatures = ui.window.accessibilityFeatures;
|
||||
_accessibilityFeatures.value = ui.window.accessibilityFeatures;
|
||||
}
|
||||
|
||||
/// The currently active set of [AccessibilityFeatures].
|
||||
@ -41,9 +41,6 @@ class SemanticsBinding extends BindingBase with ServicesBinding {
|
||||
///
|
||||
/// To listen to changes to accessibility features, create a
|
||||
/// [WidgetsBindingObserver] and listen to [didChangeAccessibilityFeatures].
|
||||
ui.AccessibilityFeatures get accessibilityFeatures => _accessibilityFeatures;
|
||||
ui.AccessibilityFeatures _accessibilityFeatures;
|
||||
|
||||
/// Whether the device is requesting that animations be disabled.
|
||||
bool get disableAnimations => accessibilityFeatures.disableAnimations;
|
||||
ValueListenable<ui.AccessibilityFeatures> get accessibilityFeatures => _accessibilityFeatures;
|
||||
ValueNotifier<ui.AccessibilityFeatures> _accessibilityFeatures;
|
||||
}
|
@ -4,6 +4,7 @@
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/scheduler.dart';
|
||||
import 'package:flutter/semantics.dart';
|
||||
|
||||
import 'framework.dart';
|
||||
|
||||
@ -94,7 +95,10 @@ abstract class SingleTickerProviderStateMixin<T extends StatefulWidget> extends
|
||||
'mixing in a SingleTickerProviderStateMixin, use a regular TickerProviderStateMixin.'
|
||||
);
|
||||
}());
|
||||
_ticker = new Ticker(onTick, debugLabel: 'created by $this');
|
||||
final ValueListenable<AccessibilityFeatures> accessibilityFeatures = SemanticsBinding.instance.accessibilityFeatures;
|
||||
_ticker = new Ticker(onTick, debugLabel: 'created by $this')
|
||||
..disableAnimations = accessibilityFeatures.value.disableAnimations;
|
||||
accessibilityFeatures.addListener(_handleAccessibilityFeaturesChanged);
|
||||
// We assume that this is called from initState, build, or some sort of
|
||||
// event handler, and that thus TickerMode.of(context) would return true. We
|
||||
// can't actually check that here because if we're in initState then we're
|
||||
@ -117,6 +121,8 @@ abstract class SingleTickerProviderStateMixin<T extends StatefulWidget> extends
|
||||
'The offending ticker was: ${_ticker.toString(debugIncludeStack: true)}'
|
||||
);
|
||||
}());
|
||||
final ValueListenable<AccessibilityFeatures> accessibilityFeatures = SemanticsBinding.instance.accessibilityFeatures;
|
||||
accessibilityFeatures.removeListener(_handleAccessibilityFeaturesChanged);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -144,6 +150,12 @@ abstract class SingleTickerProviderStateMixin<T extends StatefulWidget> extends
|
||||
properties.add(new DiagnosticsProperty<Ticker>('ticker', _ticker, description: tickerDescription, showSeparator: false, defaultValue: null));
|
||||
}
|
||||
|
||||
void _handleAccessibilityFeaturesChanged() {
|
||||
final ValueListenable<AccessibilityFeatures> accessibilityFeatures = SemanticsBinding.instance.accessibilityFeatures;
|
||||
if (_ticker != null) {
|
||||
_ticker.disableAnimations = accessibilityFeatures.value.disableAnimations;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides [Ticker] objects that are configured to only tick while the current
|
||||
@ -167,8 +179,11 @@ abstract class TickerProviderStateMixin<T extends StatefulWidget> extends State<
|
||||
@override
|
||||
Ticker createTicker(TickerCallback onTick) {
|
||||
_tickers ??= new Set<_WidgetTicker>();
|
||||
final _WidgetTicker result = new _WidgetTicker(onTick, this, debugLabel: 'created by $this');
|
||||
final ValueListenable<AccessibilityFeatures> accessibilityFeatures = SemanticsBinding.instance.accessibilityFeatures;
|
||||
final _WidgetTicker result = new _WidgetTicker(onTick, this, debugLabel: 'created by $this')
|
||||
..disableAnimations = accessibilityFeatures.value.disableAnimations;
|
||||
_tickers.add(result);
|
||||
accessibilityFeatures.addListener(_handleAccessibilityFeaturesChanged);
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -198,6 +213,8 @@ abstract class TickerProviderStateMixin<T extends StatefulWidget> extends State<
|
||||
}
|
||||
return true;
|
||||
}());
|
||||
final ValueListenable<AccessibilityFeatures> accessibilityFeatures = SemanticsBinding.instance.accessibilityFeatures;
|
||||
accessibilityFeatures.removeListener(_handleAccessibilityFeaturesChanged);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@ -205,8 +222,9 @@ abstract class TickerProviderStateMixin<T extends StatefulWidget> extends State<
|
||||
void didChangeDependencies() {
|
||||
final bool muted = !TickerMode.of(context);
|
||||
if (_tickers != null) {
|
||||
for (Ticker ticker in _tickers)
|
||||
for (Ticker ticker in _tickers) {
|
||||
ticker.muted = muted;
|
||||
}
|
||||
}
|
||||
super.didChangeDependencies();
|
||||
}
|
||||
@ -224,6 +242,14 @@ abstract class TickerProviderStateMixin<T extends StatefulWidget> extends State<
|
||||
));
|
||||
}
|
||||
|
||||
void _handleAccessibilityFeaturesChanged() {
|
||||
final ValueListenable<AccessibilityFeatures> accessibilityFeatures = SemanticsBinding.instance.accessibilityFeatures;
|
||||
if (_tickers != null) {
|
||||
for (Ticker ticker in _tickers) {
|
||||
ticker.disableAnimations = accessibilityFeatures.value.disableAnimations;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This class should really be called _DisposingTicker or some such, but this
|
||||
|
@ -581,17 +581,16 @@ void main() {
|
||||
|
||||
group('AnimationBehavior', () {
|
||||
test('Default values for constructor', () {
|
||||
final AnimationController controller = new AnimationController(vsync: const TestVSync());
|
||||
final AnimationController controller = new AnimationController(vsync: const TestVSync(disableAnimations: true));
|
||||
expect(controller.animationBehavior, AnimationBehavior.normal);
|
||||
|
||||
final AnimationController repeating = new AnimationController.unbounded(vsync: const TestVSync());
|
||||
final AnimationController repeating = new AnimationController.unbounded(vsync: const TestVSync(disableAnimations: true));
|
||||
expect(repeating.animationBehavior, AnimationBehavior.preserve);
|
||||
});
|
||||
|
||||
testWidgets('AnimationBehavior.preserve runs at normal speed when animatingTo', (WidgetTester tester) async {
|
||||
tester.binding.disableAnimations = true;
|
||||
test('AnimationBehavior.preserve runs at normal speed when animatingTo', () async {
|
||||
final AnimationController controller = new AnimationController(
|
||||
vsync: const TestVSync(),
|
||||
vsync: const TestVSync(disableAnimations: true),
|
||||
animationBehavior: AnimationBehavior.preserve,
|
||||
);
|
||||
|
||||
@ -610,13 +609,11 @@ void main() {
|
||||
|
||||
expect(controller.value, 1.0);
|
||||
expect(controller.status, AnimationStatus.completed);
|
||||
tester.binding.disableAnimations = false;
|
||||
});
|
||||
|
||||
testWidgets('AnimationBehavior.normal runs at 20x speed when animatingTo', (WidgetTester tester) async {
|
||||
tester.binding.disableAnimations = true;
|
||||
test('AnimationBehavior.normal runs at 20x speed when animatingTo', () async {
|
||||
final AnimationController controller = new AnimationController(
|
||||
vsync: const TestVSync(),
|
||||
vsync: const TestVSync(disableAnimations: true),
|
||||
animationBehavior: AnimationBehavior.normal,
|
||||
);
|
||||
|
||||
@ -635,17 +632,14 @@ void main() {
|
||||
|
||||
expect(controller.value, 1.0);
|
||||
expect(controller.status, AnimationStatus.completed);
|
||||
|
||||
tester.binding.disableAnimations = false;
|
||||
});
|
||||
|
||||
testWidgets('AnimationBehavior.normal runs "faster" whan AnimationBehavior.preserve', (WidgetTester tester) async {
|
||||
tester.binding.disableAnimations = true;
|
||||
test('AnimationBehavior.normal runs "faster" whan AnimationBehavior.preserve', () {
|
||||
final AnimationController controller = new AnimationController(
|
||||
vsync: const TestVSync(),
|
||||
vsync: const TestVSync(disableAnimations: true),
|
||||
);
|
||||
final AnimationController fastController = new AnimationController(
|
||||
vsync: const TestVSync(),
|
||||
vsync: const TestVSync(disableAnimations: true),
|
||||
);
|
||||
|
||||
controller.fling(velocity: 1.0, animationBehavior: AnimationBehavior.preserve);
|
||||
@ -655,7 +649,6 @@ void main() {
|
||||
|
||||
// We don't assert a specific faction that normal animation.
|
||||
expect(controller.value < fastController.value, true);
|
||||
tester.binding.disableAnimations = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@ -246,7 +246,7 @@ void main() {
|
||||
|
||||
class _FakeTickerProvider implements TickerProvider {
|
||||
@override
|
||||
Ticker createTicker(TickerCallback onTick) {
|
||||
Ticker createTicker(TickerCallback onTick, [bool disableAnimations = false]) {
|
||||
return new _FakeTicker();
|
||||
}
|
||||
}
|
||||
@ -273,6 +273,9 @@ class _FakeTicker implements Ticker {
|
||||
@override
|
||||
bool get shouldScheduleTick => null;
|
||||
|
||||
@override
|
||||
bool disableAnimations = false;
|
||||
|
||||
@override
|
||||
void dispose() {}
|
||||
|
||||
|
@ -128,9 +128,6 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
|
||||
@protected
|
||||
bool get checkIntrinsicSizes => false;
|
||||
|
||||
@override
|
||||
bool disableAnimations = false;
|
||||
|
||||
/// Creates and initializes the binding. This function is
|
||||
/// idempotent; calling it a second time will just return the
|
||||
/// previously-created instance.
|
||||
|
@ -10,8 +10,16 @@ import 'package:flutter/scheduler.dart';
|
||||
/// tree.
|
||||
class TestVSync implements TickerProvider {
|
||||
/// Creates a ticker provider that creates standalone tickers.
|
||||
const TestVSync();
|
||||
const TestVSync({this.disableAnimations = false});
|
||||
|
||||
/// Whether to disable the animations of tickers create from this picker.
|
||||
///
|
||||
/// See also:
|
||||
///
|
||||
/// * [AccessibilityFeatures.disableAnimations], for the setting that controls this flag.
|
||||
/// * [AnimationBehavior], for how animation controllers change when created from tickers with this flag.
|
||||
final bool disableAnimations;
|
||||
|
||||
@override
|
||||
Ticker createTicker(TickerCallback onTick) => new Ticker(onTick);
|
||||
Ticker createTicker(TickerCallback onTick) => new Ticker(onTick)..disableAnimations = disableAnimations;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user