Unverified Commit 7fab7f6d authored by Greg Spencer's avatar Greg Spencer Committed by GitHub

Adding edge avoidance, painting tests, general cleanup (#15078)

Fixed the real repaint problem the Slider had (missing addListener), and added tests for it.

Added GlobalKey reparenting test.

Added the ability for the value indicator to slide left and right to avoid falling off the edge of the screen.
It only shifts as much as it can without deforming, but even at large text scales, that is enough to keep the text on the screen.

Updated the formatting on theme_data.dart and others to use longer line length.

Also, removed a color tween that faded the value indicator in as it scaled, since that wasn't to spec.
parent 3a40d0ee
......@@ -149,15 +149,9 @@ class ThemeData extends Diagnosticable {
hintColor ??= isDark ? const Color(0x42FFFFFF) : const Color(0x4C000000);
errorColor ??= Colors.red[700];
inputDecorationTheme ??= const InputDecorationTheme();
iconTheme ??= isDark
? const IconThemeData(color: Colors.white)
: const IconThemeData(color: Colors.black);
primaryIconTheme ??= primaryIsDark
? const IconThemeData(color: Colors.white)
: const IconThemeData(color: Colors.black);
accentIconTheme ??= accentIsDark
? const IconThemeData(color: Colors.white)
: const IconThemeData(color: Colors.black);
iconTheme ??= isDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
primaryIconTheme ??= primaryIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
accentIconTheme ??= accentIsDark ? const IconThemeData(color: Colors.white) : const IconThemeData(color: Colors.black);
platform ??= defaultTargetPlatform;
final Typography typography = new Typography(platform: platform);
textTheme ??= isDark ? typography.white : typography.black;
......@@ -168,7 +162,7 @@ class ThemeData extends Diagnosticable {
primaryTextTheme = primaryTextTheme.apply(fontFamily: fontFamily);
accentTextTheme = accentTextTheme.apply(fontFamily: fontFamily);
}
sliderTheme ??= new SliderThemeData.materialDefaults(
sliderTheme ??= new SliderThemeData.fromPrimaryColors(
primaryColor: primaryColor,
primaryColorLight: primaryColorLight,
primaryColorDark: primaryColorDark,
......@@ -765,64 +759,36 @@ class ThemeData extends Diagnosticable {
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
final ThemeData defaultData = new ThemeData.fallback();
description.add(new EnumProperty<TargetPlatform>('platform', platform,
defaultValue: defaultTargetPlatform));
description.add(new EnumProperty<Brightness>('brightness', brightness,
defaultValue: defaultData.brightness));
description.add(new DiagnosticsProperty<Color>('primaryColor', primaryColor,
defaultValue: defaultData.primaryColor));
description.add(new EnumProperty<Brightness>('primaryColorBrightness', primaryColorBrightness,
defaultValue: defaultData.primaryColorBrightness));
description.add(new DiagnosticsProperty<Color>('accentColor', accentColor,
defaultValue: defaultData.accentColor));
description.add(new EnumProperty<Brightness>('accentColorBrightness', accentColorBrightness,
defaultValue: defaultData.accentColorBrightness));
description.add(new DiagnosticsProperty<Color>('canvasColor', canvasColor,
defaultValue: defaultData.canvasColor));
description.add(new DiagnosticsProperty<Color>(
'scaffoldBackgroundColor', scaffoldBackgroundColor,
defaultValue: defaultData.scaffoldBackgroundColor));
description.add(new DiagnosticsProperty<Color>('bottomAppBarColor', bottomAppBarColor,
defaultValue: defaultData.bottomAppBarColor));
description.add(new DiagnosticsProperty<Color>('cardColor', cardColor,
defaultValue: defaultData.cardColor));
description.add(new DiagnosticsProperty<Color>('dividerColor', dividerColor,
defaultValue: defaultData.dividerColor));
description.add(new DiagnosticsProperty<Color>('highlightColor', highlightColor,
defaultValue: defaultData.highlightColor));
description.add(new DiagnosticsProperty<Color>('splashColor', splashColor,
defaultValue: defaultData.splashColor));
description.add(new DiagnosticsProperty<Color>('selectedRowColor', selectedRowColor,
defaultValue: defaultData.selectedRowColor));
description.add(new DiagnosticsProperty<Color>('unselectedWidgetColor', unselectedWidgetColor,
defaultValue: defaultData.unselectedWidgetColor));
description.add(new DiagnosticsProperty<Color>('disabledColor', disabledColor,
defaultValue: defaultData.disabledColor));
description.add(new DiagnosticsProperty<Color>('buttonColor', buttonColor,
defaultValue: defaultData.buttonColor));
description.add(new DiagnosticsProperty<Color>('secondaryHeaderColor', secondaryHeaderColor,
defaultValue: defaultData.secondaryHeaderColor));
description.add(new DiagnosticsProperty<Color>('textSelectionColor', textSelectionColor,
defaultValue: defaultData.textSelectionColor));
description.add(new DiagnosticsProperty<Color>(
'textSelectionHandleColor', textSelectionHandleColor,
defaultValue: defaultData.textSelectionHandleColor));
description.add(new DiagnosticsProperty<Color>('backgroundColor', backgroundColor,
defaultValue: defaultData.backgroundColor));
description.add(new DiagnosticsProperty<Color>('dialogBackgroundColor', dialogBackgroundColor,
defaultValue: defaultData.dialogBackgroundColor));
description.add(new DiagnosticsProperty<Color>('indicatorColor', indicatorColor,
defaultValue: defaultData.indicatorColor));
description.add(new DiagnosticsProperty<Color>('hintColor', hintColor,
defaultValue: defaultData.hintColor));
description.add(new DiagnosticsProperty<Color>('errorColor', errorColor,
defaultValue: defaultData.errorColor));
description.add(new EnumProperty<TargetPlatform>('platform', platform, defaultValue: defaultTargetPlatform));
description.add(new EnumProperty<Brightness>('brightness', brightness, defaultValue: defaultData.brightness));
description.add(new DiagnosticsProperty<Color>('primaryColor', primaryColor, defaultValue: defaultData.primaryColor));
description.add(new EnumProperty<Brightness>('primaryColorBrightness', primaryColorBrightness, defaultValue: defaultData.primaryColorBrightness));
description.add(new DiagnosticsProperty<Color>('accentColor', accentColor, defaultValue: defaultData.accentColor));
description.add(new EnumProperty<Brightness>('accentColorBrightness', accentColorBrightness, defaultValue: defaultData.accentColorBrightness));
description.add(new DiagnosticsProperty<Color>('canvasColor', canvasColor, defaultValue: defaultData.canvasColor));
description.add(new DiagnosticsProperty<Color>('scaffoldBackgroundColor', scaffoldBackgroundColor, defaultValue: defaultData.scaffoldBackgroundColor));
description.add(new DiagnosticsProperty<Color>('bottomAppBarColor', bottomAppBarColor, defaultValue: defaultData.bottomAppBarColor));
description.add(new DiagnosticsProperty<Color>('cardColor', cardColor, defaultValue: defaultData.cardColor));
description.add(new DiagnosticsProperty<Color>('dividerColor', dividerColor, defaultValue: defaultData.dividerColor));
description.add(new DiagnosticsProperty<Color>('highlightColor', highlightColor, defaultValue: defaultData.highlightColor));
description.add(new DiagnosticsProperty<Color>('splashColor', splashColor, defaultValue: defaultData.splashColor));
description.add(new DiagnosticsProperty<Color>('selectedRowColor', selectedRowColor, defaultValue: defaultData.selectedRowColor));
description.add(new DiagnosticsProperty<Color>('unselectedWidgetColor', unselectedWidgetColor, defaultValue: defaultData.unselectedWidgetColor));
description.add(new DiagnosticsProperty<Color>('disabledColor', disabledColor, defaultValue: defaultData.disabledColor));
description.add(new DiagnosticsProperty<Color>('buttonColor', buttonColor, defaultValue: defaultData.buttonColor));
description.add(new DiagnosticsProperty<Color>('secondaryHeaderColor', secondaryHeaderColor, defaultValue: defaultData.secondaryHeaderColor));
description.add(new DiagnosticsProperty<Color>('textSelectionColor', textSelectionColor, defaultValue: defaultData.textSelectionColor));
description.add(new DiagnosticsProperty<Color>('textSelectionHandleColor', textSelectionHandleColor, defaultValue: defaultData.textSelectionHandleColor));
description.add(new DiagnosticsProperty<Color>('backgroundColor', backgroundColor, defaultValue: defaultData.backgroundColor));
description.add(new DiagnosticsProperty<Color>('dialogBackgroundColor', dialogBackgroundColor, defaultValue: defaultData.dialogBackgroundColor));
description.add(new DiagnosticsProperty<Color>('indicatorColor', indicatorColor, defaultValue: defaultData.indicatorColor));
description.add(new DiagnosticsProperty<Color>('hintColor', hintColor, defaultValue: defaultData.hintColor));
description.add(new DiagnosticsProperty<Color>('errorColor', errorColor, defaultValue: defaultData.errorColor));
description.add(new DiagnosticsProperty<ButtonThemeData>('buttonTheme', buttonTheme));
description.add(new DiagnosticsProperty<TextTheme>('textTheme', textTheme));
description.add(new DiagnosticsProperty<TextTheme>('primaryTextTheme', primaryTextTheme));
description.add(new DiagnosticsProperty<TextTheme>('accentTextTheme', accentTextTheme));
description.add(new DiagnosticsProperty<InputDecorationTheme>(
'inputDecorationTheme', inputDecorationTheme));
description.add(new DiagnosticsProperty<InputDecorationTheme>('inputDecorationTheme', inputDecorationTheme));
description.add(new DiagnosticsProperty<IconThemeData>('iconTheme', iconTheme));
description.add(new DiagnosticsProperty<IconThemeData>('primaryIconTheme', primaryIconTheme));
description.add(new DiagnosticsProperty<IconThemeData>('accentIconTheme', accentIconTheme));
......@@ -846,8 +812,7 @@ class _IdentityThemeDataCacheKey {
// We are explicitly ignoring the possibility that the types might not
// match in the interests of speed.
final _IdentityThemeDataCacheKey otherKey = other;
return identical(baseTheme, otherKey.baseTheme) &&
identical(localTextGeometry, otherKey.localTextGeometry);
return identical(baseTheme, otherKey.baseTheme) && identical(localTextGeometry, otherKey.localTextGeometry);
}
}
......
......@@ -34,7 +34,10 @@ class IconThemeData extends Diagnosticable {
/// the new values.
IconThemeData copyWith({Color color, double opacity, double size}) {
return new IconThemeData(
color: color ?? this.color, opacity: opacity ?? this.opacity, size: size ?? this.size);
color: color ?? this.color,
opacity: opacity ?? this.opacity,
size: size ?? this.size,
);
}
/// Returns a new icon theme that matches this icon theme but with some values
......@@ -43,7 +46,11 @@ class IconThemeData extends Diagnosticable {
IconThemeData merge(IconThemeData other) {
if (other == null)
return this;
return copyWith(color: other.color, opacity: other.opacity, size: other.size);
return copyWith(
color: other.color,
opacity: other.opacity,
size: other.size,
);
}
/// Whether all the properties of this object are non-null.
......@@ -97,11 +104,8 @@ class IconThemeData extends Diagnosticable {
@override
void debugFillProperties(DiagnosticPropertiesBuilder description) {
super.debugFillProperties(description);
if (color == null && _opacity == null && size == null) {
return;
}
description.add(new DiagnosticsProperty<Color>('color', color));
description.add(new DoubleProperty('opacity', _opacity));
description.add(new DoubleProperty('size', size));
description.add(new DiagnosticsProperty<Color>('color', color, defaultValue: null));
description.add(new DoubleProperty('opacity', opacity, defaultValue: null));
description.add(new DoubleProperty('size', size, defaultValue: null));
}
}
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show window;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -31,6 +33,8 @@ void main() {
Widget buildSlider(SliderThemeData data) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new Theme(
......@@ -43,6 +47,7 @@ void main() {
),
),
),
),
);
}
......@@ -50,15 +55,10 @@ void main() {
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
expect(
sliderBox,
paints
..rect(color: sliderTheme.disabledActiveRailColor)
..rect(color: sliderTheme.disabledInactiveRailColor));
expect(sliderBox, paints..rect(color: sliderTheme.disabledActiveRailColor)..rect(color: sliderTheme.disabledInactiveRailColor));
});
testWidgets('Slider overrides ThemeData theme if SliderTheme present',
(WidgetTester tester) async {
testWidgets('Slider overrides ThemeData theme if SliderTheme present', (WidgetTester tester) async {
final ThemeData theme = new ThemeData(
platform: TargetPlatform.android,
primarySwatch: Colors.red,
......@@ -72,6 +72,8 @@ void main() {
Widget buildSlider(SliderThemeData data) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new Theme(
......@@ -87,6 +89,7 @@ void main() {
),
),
),
),
);
}
......@@ -94,20 +97,15 @@ void main() {
final RenderBox sliderBox = tester.firstRenderObject<RenderBox>(find.byType(Slider));
expect(
sliderBox,
paints
..rect(color: customTheme.disabledActiveRailColor)
..rect(color: customTheme.disabledInactiveRailColor));
expect(sliderBox, paints..rect(color: customTheme.disabledActiveRailColor)..rect(color: customTheme.disabledInactiveRailColor));
});
testWidgets('SliderThemeData generates correct opacities for materialDefaults',
(WidgetTester tester) async {
testWidgets('SliderThemeData generates correct opacities for materialDefaults', (WidgetTester tester) async {
const Color customColor1 = const Color(0xcafefeed);
const Color customColor2 = const Color(0xdeadbeef);
const Color customColor3 = const Color(0xdecaface);
final SliderThemeData sliderTheme = new SliderThemeData.materialDefaults(
final SliderThemeData sliderTheme = new SliderThemeData.fromPrimaryColors(
primaryColor: customColor1,
primaryColorDark: customColor2,
primaryColorLight: customColor3,
......@@ -126,18 +124,17 @@ void main() {
expect(sliderTheme.overlayColor, equals(customColor1.withAlpha(0x29)));
expect(sliderTheme.valueIndicatorColor, equals(customColor1.withAlpha(0xff)));
expect(sliderTheme.thumbShape, equals(const isInstanceOf<RoundSliderThumbShape>()));
expect(sliderTheme.valueIndicatorShape,
equals(const isInstanceOf<PaddleSliderValueIndicatorShape>()));
expect(sliderTheme.valueIndicatorShape, equals(const isInstanceOf<PaddleSliderValueIndicatorShape>()));
expect(sliderTheme.showValueIndicator, equals(ShowValueIndicator.onlyForDiscrete));
});
testWidgets('SliderThemeData lerps correctly', (WidgetTester tester) async {
final SliderThemeData sliderThemeBlack = new SliderThemeData.materialDefaults(
final SliderThemeData sliderThemeBlack = new SliderThemeData.fromPrimaryColors(
primaryColor: Colors.black,
primaryColorDark: Colors.black,
primaryColorLight: Colors.black,
);
final SliderThemeData sliderThemeWhite = new SliderThemeData.materialDefaults(
final SliderThemeData sliderThemeWhite = new SliderThemeData.fromPrimaryColors(
primaryColor: Colors.white,
primaryColorDark: Colors.white,
primaryColorLight: Colors.white,
......@@ -172,6 +169,8 @@ void main() {
final ValueChanged<double> onChanged = enabled ? (double d) => value = d : null;
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new SliderTheme(
......@@ -185,6 +184,7 @@ void main() {
),
),
),
),
);
}
......@@ -225,23 +225,29 @@ void main() {
platform: TargetPlatform.android,
primarySwatch: Colors.blue,
);
final SliderThemeData sliderTheme = theme.sliderTheme
.copyWith(thumbColor: Colors.red.shade500, showValueIndicator: ShowValueIndicator.always);
Widget buildApp(String value) {
final SliderThemeData sliderTheme = theme.sliderTheme.copyWith(thumbColor: Colors.red.shade500, showValueIndicator: ShowValueIndicator.always);
Widget buildApp(String value, {double sliderValue = 0.5}) {
return new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new Row(
children: <Widget>[
new Expanded(
child: new SliderTheme(
data: sliderTheme,
child: new Slider(
value: 0.5,
value: sliderValue,
label: '$value',
divisions: 3,
onChanged: (double d) {},
),
),
),
],
),
),
),
);
}
......@@ -290,5 +296,47 @@ void main() {
excludes: <Offset>[const Offset(36.1, -40.0), const Offset(-36.1, -40.0)],
));
await gesture.up();
// Test that it avoids the left edge of the screen.
await tester.pumpWidget(buildApp('1000000', sliderValue: 0.0));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500));
expect(
sliderBox,
paints
..path(
color: sliderTheme.valueIndicatorColor,
includes: <Offset>[
const Offset(0.0, -40.0),
const Offset(98.0, -40.0),
const Offset(-16.0, -40.0),
],
excludes: <Offset>[const Offset(98.1, -40.0), const Offset(-16.1, -40.0)],
));
await gesture.up();
// Test that it avoids the right edge of the screen.
await tester.pumpWidget(buildApp('1000000', sliderValue: 1.0));
center = tester.getCenter(find.byType(Slider));
gesture = await tester.startGesture(center);
await tester.pump();
// Wait for value indicator animation to finish.
await tester.pump(const Duration(milliseconds: 500));
expect(
sliderBox,
paints
..path(
color: sliderTheme.valueIndicatorColor,
includes: <Offset>[
const Offset(0.0, -40.0),
const Offset(16.0, -40.0),
const Offset(-98.0, -40.0),
],
excludes: <Offset>[const Offset(16.1, -40.0), const Offset(-98.1, -40.0)],
));
await gesture.up();
});
}
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:ui' show window;
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter/rendering.dart';
......@@ -292,6 +294,8 @@ void main() {
child: new SemanticsDebugger(
child: new Directionality(
textDirection: TextDirection.ltr,
child: new MediaQuery(
data: new MediaQueryData.fromWindow(window),
child: new Material(
child: new Center(
child: new Slider(
......@@ -305,6 +309,7 @@ void main() {
),
),
),
),
);
// The fling below must be such that the velocity estimation examines an
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment