blob: 5f76c10514eeea3abcb1bf0ccbb87792308efb29 [file] [log] [blame]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../rendering/mock_canvas.dart';
class DependentWidget extends StatelessWidget {
const DependentWidget({
Key? key,
required this.color,
}) : super(key: key);
final Color color;
@override
Widget build(BuildContext context) {
final Color resolved = CupertinoDynamicColor.resolve(color, context);
return DecoratedBox(
decoration: BoxDecoration(color: resolved),
child: const SizedBox.expand(),
);
}
}
const Color color0 = Color(0xFF000000);
const Color color1 = Color(0xFF000001);
const Color color2 = Color(0xFF000002);
const Color color3 = Color(0xFF000003);
const Color color4 = Color(0xFF000004);
const Color color5 = Color(0xFF000005);
const Color color6 = Color(0xFF000006);
const Color color7 = Color(0xFF000007);
// A color that depends on color vibrancy, accessibility contrast, as well as user
// interface elevation.
const CupertinoDynamicColor dynamicColor = CupertinoDynamicColor(
color: color0,
darkColor: color1,
elevatedColor: color2,
highContrastColor: color3,
darkElevatedColor: color4,
darkHighContrastColor: color5,
highContrastElevatedColor: color6,
darkHighContrastElevatedColor: color7,
);
// A color that uses [color0] in every circumstance.
const Color notSoDynamicColor1 = CupertinoDynamicColor(
color: color0,
darkColor: color0,
darkHighContrastColor: color0,
darkElevatedColor: color0,
darkHighContrastElevatedColor: color0,
highContrastColor: color0,
highContrastElevatedColor: color0,
elevatedColor: color0,
);
// A color that uses [color1] for light mode, and [color0] for dark mode.
const Color vibrancyDependentColor1 = CupertinoDynamicColor(
color: color1,
elevatedColor: color1,
highContrastColor: color1,
highContrastElevatedColor: color1,
darkColor: color0,
darkHighContrastColor: color0,
darkElevatedColor: color0,
darkHighContrastElevatedColor: color0,
);
// A color that uses [color1] for normal contrast mode, and [color0] for high
// contrast mode.
const Color contrastDependentColor1 = CupertinoDynamicColor(
color: color1,
darkColor: color1,
elevatedColor: color1,
darkElevatedColor: color1,
highContrastColor: color0,
darkHighContrastColor: color0,
highContrastElevatedColor: color0,
darkHighContrastElevatedColor: color0,
);
// A color that uses [color1] for base interface elevation, and [color0] for elevated
// interface elevation.
const Color elevationDependentColor1 = CupertinoDynamicColor(
color: color1,
darkColor: color1,
highContrastColor: color1,
darkHighContrastColor: color1,
elevatedColor: color0,
darkElevatedColor: color0,
highContrastElevatedColor: color0,
darkHighContrastElevatedColor: color0,
);
void main() {
test('== works as expected', () {
expect(dynamicColor, const CupertinoDynamicColor(
color: color0,
darkColor: color1,
elevatedColor: color2,
highContrastColor: color3,
darkElevatedColor: color4,
darkHighContrastColor: color5,
highContrastElevatedColor: color6,
darkHighContrastElevatedColor: color7,
),
);
expect(notSoDynamicColor1, isNot(vibrancyDependentColor1));
expect(notSoDynamicColor1, isNot(contrastDependentColor1));
expect(vibrancyDependentColor1, isNot(const CupertinoDynamicColor(
color: color0,
elevatedColor: color0,
highContrastColor: color0,
highContrastElevatedColor: color0,
darkColor: color0,
darkHighContrastColor: color0,
darkElevatedColor: color0,
darkHighContrastElevatedColor: color0,
)));
});
test('CupertinoDynamicColor.toString() works', () {
expect(
dynamicColor.toString(),
contains(
'CupertinoDynamicColor(*color = Color(0xff000000)*, '
'darkColor = Color(0xff000001), '
'highContrastColor = Color(0xff000003), '
'darkHighContrastColor = Color(0xff000005), '
'elevatedColor = Color(0xff000002), '
'darkElevatedColor = Color(0xff000004), '
'highContrastElevatedColor = Color(0xff000006), '
'darkHighContrastElevatedColor = Color(0xff000007)',
),
);
expect(notSoDynamicColor1.toString(), contains('CupertinoDynamicColor(*color = Color(0xff000000)*'));
expect(vibrancyDependentColor1.toString(), contains('CupertinoDynamicColor(*color = Color(0xff000001)*, darkColor = Color(0xff000000)'));
expect(contrastDependentColor1.toString(), contains('CupertinoDynamicColor(*color = Color(0xff000001)*, highContrastColor = Color(0xff000000)'));
expect(elevationDependentColor1.toString(), contains('CupertinoDynamicColor(*color = Color(0xff000001)*, elevatedColor = Color(0xff000000)'));
expect(
const CupertinoDynamicColor.withBrightnessAndContrast(
color: color0,
darkColor: color1,
highContrastColor: color2,
darkHighContrastColor: color3,
).toString(),
contains(
'CupertinoDynamicColor(*color = Color(0xff000000)*, '
'darkColor = Color(0xff000001), '
'highContrastColor = Color(0xff000002), '
'darkHighContrastColor = Color(0xff000003)',
),
);
});
test('can resolve null color', () {
expect(CupertinoDynamicColor.maybeResolve(null, _NullElement.instance), isNull);
});
test('withVibrancy constructor creates colors that may depend on vibrancy', () {
expect(vibrancyDependentColor1, const CupertinoDynamicColor.withBrightness(
color: color1,
darkColor: color0,
));
});
test('withVibrancyAndContrast constructor creates colors that may depend on contrast and vibrancy', () {
expect(contrastDependentColor1, const CupertinoDynamicColor.withBrightnessAndContrast(
color: color1,
darkColor: color1,
highContrastColor: color0,
darkHighContrastColor: color0,
));
expect(
const CupertinoDynamicColor(
color: color0,
darkColor: color1,
highContrastColor: color2,
darkHighContrastColor: color3,
elevatedColor: color0,
darkElevatedColor: color1,
highContrastElevatedColor: color2,
darkHighContrastElevatedColor: color3,
),
const CupertinoDynamicColor.withBrightnessAndContrast(
color: color0,
darkColor: color1,
highContrastColor: color2,
darkHighContrastColor: color3,
),
);
});
testWidgets(
'Dynamic colors that are not actually dynamic should not claim dependencies',
(WidgetTester tester) async {
await tester.pumpWidget(const DependentWidget(color: notSoDynamicColor1));
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color0));
},
);
testWidgets(
'Dynamic colors that are only dependent on vibrancy should not claim unnecessary dependencies, '
'and its resolved color should change when its dependency changes',
(WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(),
child: DependentWidget(color: vibrancyDependentColor1),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color1));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color0)));
// Changing color vibrancy works.
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(platformBrightness: Brightness.dark),
child: DependentWidget(color: vibrancyDependentColor1),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color0));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color1)));
// CupertinoTheme should take precedence over MediaQuery.
await tester.pumpWidget(
const CupertinoTheme(
data: CupertinoThemeData(brightness: Brightness.light),
child: MediaQuery(
data: MediaQueryData(platformBrightness: Brightness.dark),
child: DependentWidget(color: vibrancyDependentColor1),
),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color1));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color0)));
},
);
testWidgets(
'Dynamic colors that are only dependent on accessibility contrast should not claim unnecessary dependencies, '
'and its resolved color should change when its dependency changes',
(WidgetTester tester) async {
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(),
child: DependentWidget(color: contrastDependentColor1),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color1));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color0)));
// Changing accessibility contrast works.
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(highContrast: true),
child: DependentWidget(color: contrastDependentColor1),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color0));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color1)));
},
);
testWidgets(
'Dynamic colors that are only dependent on elevation level should not claim unnecessary dependencies, '
'and its resolved color should change when its dependency changes',
(WidgetTester tester) async {
await tester.pumpWidget(
const CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: DependentWidget(color: elevationDependentColor1),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color1));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color0)));
// Changing UI elevation works.
await tester.pumpWidget(
const CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: DependentWidget(color: elevationDependentColor1),
),
);
expect(tester.takeException(), null);
expect(find.byType(DependentWidget), paints..rect(color: color0));
expect(find.byType(DependentWidget), isNot(paints..rect(color: color1)));
},
);
testWidgets('Dynamic color with all 3 dependencies works', (WidgetTester tester) async {
const Color dynamicRainbowColor1 = CupertinoDynamicColor(
color: color0,
darkColor: color1,
highContrastColor: color2,
darkHighContrastColor: color3,
darkElevatedColor: color4,
highContrastElevatedColor: color5,
darkHighContrastElevatedColor: color6,
elevatedColor: color7,
);
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color0));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(platformBrightness: Brightness.dark),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color1));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color2));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(platformBrightness: Brightness.dark, highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color3));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(platformBrightness: Brightness.dark),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color4));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color5));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(platformBrightness: Brightness.dark, highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color6));
await tester.pumpWidget(
const MediaQuery(
data: MediaQueryData(),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: DependentWidget(color: dynamicRainbowColor1),
),
),
);
expect(find.byType(DependentWidget), paints..rect(color: color7));
});
testWidgets('CupertinoDynamicColor used in a CupertinoTheme', (WidgetTester tester) async {
late CupertinoDynamicColor color;
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(
brightness: Brightness.dark,
primaryColor: dynamicColor,
),
home: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor as CupertinoDynamicColor;
return const Placeholder();
},
),
),
);
expect(color.value, dynamicColor.darkColor.value);
// Changing dependencies works.
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(
brightness: Brightness.light,
primaryColor: dynamicColor,
),
home: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor as CupertinoDynamicColor;
return const Placeholder();
},
),
),
);
expect(color.value, dynamicColor.color.value);
// Having a dependency below the CupertinoTheme widget works.
await tester.pumpWidget(
CupertinoApp(
theme: const CupertinoThemeData(primaryColor: dynamicColor),
home: MediaQuery(
data: const MediaQueryData(),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor as CupertinoDynamicColor;
return const Placeholder();
},
),
),
),
),
);
expect(color.value, dynamicColor.color.value);
// Changing dependencies works.
await tester.pumpWidget(
CupertinoApp(
// No brightness is explicitly specified here so it should defer to MediaQuery.
theme: const CupertinoThemeData(primaryColor: dynamicColor),
home: MediaQuery(
data: const MediaQueryData(platformBrightness: Brightness.dark, highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor as CupertinoDynamicColor;
return const Placeholder();
},
),
),
),
),
);
expect(color.value, dynamicColor.darkHighContrastElevatedColor.value);
});
group('MaterialApp:', () {
Color? color;
setUp(() { color = null; });
testWidgets('dynamic color works in cupertino override theme', (WidgetTester tester) async {
CupertinoDynamicColor typedColor() => color! as CupertinoDynamicColor;
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
cupertinoOverrideTheme: const CupertinoThemeData(
brightness: Brightness.dark,
primaryColor: dynamicColor,
),
),
home: MediaQuery(
data: const MediaQueryData(),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.base,
child: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor;
return const Placeholder();
},
),
),
),
),
);
// Explicit brightness is respected.
expect(typedColor().value, dynamicColor.darkColor.value);
color = null;
// Changing dependencies works.
await tester.pumpWidget(
MaterialApp(
theme: ThemeData(
cupertinoOverrideTheme: const CupertinoThemeData(
brightness: Brightness.dark,
primaryColor: dynamicColor,
),
),
home: MediaQuery(
data: const MediaQueryData(platformBrightness: Brightness.dark, highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor;
return const Placeholder();
},
),
),
),
),
);
expect(typedColor().value, dynamicColor.darkHighContrastElevatedColor.value);
});
testWidgets('dynamic color does not work in a material theme', (WidgetTester tester) async {
await tester.pumpWidget(
MaterialApp(
// This will create a MaterialBasedCupertinoThemeData with primaryColor set to `dynamicColor`.
theme: ThemeData(colorScheme: const ColorScheme.dark(primary: dynamicColor)),
home: MediaQuery(
data: const MediaQueryData(platformBrightness: Brightness.dark, highContrast: true),
child: CupertinoUserInterfaceLevel(
data: CupertinoUserInterfaceLevelData.elevated,
child: Builder(
builder: (BuildContext context) {
color = CupertinoTheme.of(context).primaryColor;
return const Placeholder();
},
),
),
),
),
);
// The color is not resolved.
expect(color, dynamicColor);
expect(color, isNot(dynamicColor.darkHighContrastElevatedColor));
});
});
}
class _NullElement extends Element {
_NullElement() : super(_NullWidget());
static _NullElement instance = _NullElement();
@override
bool get debugDoingBuild => throw UnimplementedError();
@override
void performRebuild() { }
}
class _NullWidget extends Widget {
@override
Element createElement() => throw UnimplementedError();
}