diff --git a/packages/flutter/lib/src/cupertino/slider.dart b/packages/flutter/lib/src/cupertino/slider.dart index 42cc96ab7a..e2bb103428 100644 --- a/packages/flutter/lib/src/cupertino/slider.dart +++ b/packages/flutter/lib/src/cupertino/slider.dart @@ -190,12 +190,13 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc @required double value, int divisions, Color activeColor, - this.onChanged, + ValueChanged onChanged, TickerProvider vsync, }) : assert(value != null && value >= 0.0 && value <= 1.0), _value = value, _divisions = divisions, _activeColor = activeColor, + _onChanged = onChanged, super(additionalConstraints: const BoxConstraints.tightFor(width: _kSliderWidth, height: _kSliderHeight)) { _drag = new HorizontalDragGestureRecognizer() ..onStart = _handleDragStart @@ -239,7 +240,16 @@ class _RenderCupertinoSlider extends RenderConstrainedBox implements SemanticsAc markNeedsPaint(); } - ValueChanged onChanged; + ValueChanged get onChanged => _onChanged; + ValueChanged _onChanged; + set onChanged(ValueChanged value) { + if (value == _onChanged) + return; + final bool wasInteractive = isInteractive; + _onChanged = value; + if (wasInteractive != isInteractive) + markNeedsSemanticsUpdate(noGeometry: true); + } AnimationController _position; diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart index 903c047c72..8af054a63b 100644 --- a/packages/flutter/lib/src/material/slider.dart +++ b/packages/flutter/lib/src/material/slider.dart @@ -288,7 +288,7 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler { Color inactiveColor, bool thumbOpenAtMin, TextTheme textTheme, - this.onChanged, + ValueChanged onChanged, TickerProvider vsync, }) : assert(value != null && value >= 0.0 && value <= 1.0), _value = value, @@ -296,7 +296,8 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler { _activeColor = activeColor, _inactiveColor = inactiveColor, _thumbOpenAtMin = thumbOpenAtMin, - _textTheme = textTheme { + _textTheme = textTheme, + _onChanged = onChanged { this.label = label; final GestureArenaTeam team = new GestureArenaTeam(); _drag = new HorizontalDragGestureRecognizer() @@ -401,7 +402,18 @@ class _RenderSlider extends RenderBox implements SemanticsActionHandler { markNeedsPaint(); } - ValueChanged onChanged; + ValueChanged get onChanged => _onChanged; + ValueChanged _onChanged; + set onChanged(ValueChanged value) { + if (value == _onChanged) + return; + final bool wasInteractive = isInteractive; + _onChanged = value; + if (wasInteractive != isInteractive) { + markNeedsPaint(); + markNeedsSemanticsUpdate(noGeometry: true); + } + } double get _trackLength => size.width - 2.0 * _kReactionRadius; diff --git a/packages/flutter/test/cupertino/slider_test.dart b/packages/flutter/test/cupertino/slider_test.dart index 5dd5258dee..9bf222b74d 100644 --- a/packages/flutter/test/cupertino/slider_test.dart +++ b/packages/flutter/test/cupertino/slider_test.dart @@ -4,9 +4,12 @@ import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_test/flutter_test.dart'; +import '../widgets/semantics_tester.dart'; + void main() { testWidgets('Slider does not move when tapped', (WidgetTester tester) async { final Key sliderKey = new UniqueKey(); @@ -77,4 +80,44 @@ void main() { // that no animation is running. expect(SchedulerBinding.instance.transientCallbackCount, equals(0)); }); + + testWidgets('Slider Semantics', (WidgetTester tester) async { + final SemanticsTester semantics = new SemanticsTester(tester); + + await tester.pumpWidget( + new CupertinoSlider( + value: 0.5, + onChanged: (double v) {}, + ), + ); + + expect(semantics, hasSemantics( + new TestSemantics.root( + children: [ + new TestSemantics.rootChild( + id: 1, + actions: SemanticsAction.decrease.index | SemanticsAction.increase.index, + ), + ] + ), + ignoreRect: true, + ignoreTransform: true, + )); + + // Disable slider + await tester.pumpWidget( + new CupertinoSlider( + value: 0.5, + onChanged: null, + ), + ); + + expect(semantics, hasSemantics( + new TestSemantics.root(), + ignoreRect: true, + ignoreTransform: true, + )); + + semantics.dispose(); + }); } diff --git a/packages/flutter/test/material/slider_test.dart b/packages/flutter/test/material/slider_test.dart index 05f876bc20..ddfc27a30e 100644 --- a/packages/flutter/test/material/slider_test.dart +++ b/packages/flutter/test/material/slider_test.dart @@ -2,11 +2,15 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui'; + import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; import 'package:flutter/scheduler.dart'; import 'package:flutter_test/flutter_test.dart'; import '../rendering/mock_canvas.dart'; +import '../widgets/semantics_tester.dart'; void main() { testWidgets('Slider can move when tapped', (WidgetTester tester) async { @@ -295,4 +299,48 @@ void main() { )); expect(tester.renderObject(find.byType(Slider)).size, const Size(144.0 + 2.0 * 16.0, 32.0)); }); + + testWidgets('Slider Semantics', (WidgetTester tester) async { + final SemanticsTester semantics = new SemanticsTester(tester); + + await tester.pumpWidget( + new Material( + child: new Slider( + value: 0.5, + onChanged: (double v) {}, + ), + ), + ); + + expect(semantics, hasSemantics( + new TestSemantics.root( + children: [ + new TestSemantics.rootChild( + id: 1, + actions: SemanticsAction.decrease.index | SemanticsAction.increase.index, + ), + ] + ), + ignoreRect: true, + ignoreTransform: true, + )); + + // Disable slider + await tester.pumpWidget( + new Material( + child: new Slider( + value: 0.5, + onChanged: null, + ), + ), + ); + + expect(semantics, hasSemantics( + new TestSemantics.root(), + ignoreRect: true, + ignoreTransform: true, + )); + + semantics.dispose(); + }); }