Unverified Commit 87ac905a authored by Eilidh Southren's avatar Eilidh Southren Committed by GitHub

Add content-based colorScheme functionality (#122919)

* add shadowColor property

* add to bottom app bar

* basic MCU connection demo

* more image tests

* demo update

* remove branch changes

* demo cleanup

* cleanup

* update algo

* working consistent example

* demo cleanup

* Added tests

* fix merge

* rebase

* basic MCU connection demo

* more image tests

* demo update

* remove branch changes

* demo cleanup

* cleanup

* update algo

* working consistent example

* demo cleanup

* Added tests

* lint fixes

* fix theme error

* whitespace fixes

* revert old commit

* update test formatting

* modify image source to external repo

* remove pngs

* add blank line

* fix web tests

* comment responses

* remove whitespace

* whitespace

* whitespace
parent 9fc1fd15
// 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.
// Flutter code sample for [ColorScheme.fromImageProvider] with content-based dynamic color.
import 'package:flutter/material.dart';
const Widget divider = SizedBox(height: 10);
const double narrowScreenWidthThreshold = 400;
const double imageSize = 150;
void main() => runApp(DynamicColorExample());
class DynamicColorExample extends StatefulWidget {
DynamicColorExample({super.key});
final List<ImageProvider> images = <NetworkImage>[
const NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_1.png'),
const NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_2.png'),
const NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_3.png'),
const NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_4.png'),
const NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_5.png'),
const NetworkImage('https://flutter.github.io/assets-for-api-docs/assets/material/content_based_color_scheme_6.png'),
];
@override
State<DynamicColorExample> createState() => _DynamicColorExampleState();
}
class _DynamicColorExampleState extends State<DynamicColorExample> {
late ColorScheme currentColorScheme;
String currentHyperlinkImage = '';
late int selectedImage;
late bool isLight;
late bool isLoading;
@override
void initState() {
super.initState();
selectedImage = 0;
isLight = true;
isLoading = true;
currentColorScheme = const ColorScheme.light();
WidgetsBinding.instance.addPostFrameCallback((_) {
_updateImage(widget.images[selectedImage]);
isLoading = false;
});
}
@override
Widget build(BuildContext context) {
final ColorScheme colorScheme = currentColorScheme;
final Color selectedColor = currentColorScheme.primary;
final ThemeData lightTheme = ThemeData(
colorSchemeSeed: selectedColor,
brightness: Brightness.light,
useMaterial3: false,
);
final ThemeData darkTheme = ThemeData(
colorSchemeSeed: selectedColor,
brightness: Brightness.dark,
useMaterial3: false,
);
Widget schemeLabel(String brightness, ColorScheme colorScheme) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
child: Text(
brightness,
style: TextStyle(
fontWeight: FontWeight.bold,
color: colorScheme.onSecondaryContainer),
),
);
}
Widget schemeView(ThemeData theme) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ColorSchemeView(colorScheme: theme.colorScheme),
);
}
return MaterialApp(
title: 'Content Based Dynamic Color',
theme: ThemeData(useMaterial3: true, colorScheme: colorScheme),
debugShowCheckedModeBanner: false,
home: Builder(
builder: (BuildContext context) => Scaffold(
appBar: AppBar(
title: const Text('Content Based Dynamic Color'),
backgroundColor: colorScheme.primary,
foregroundColor: colorScheme.onPrimary,
actions: <Widget>[
const Icon(Icons.light_mode),
Switch(
activeColor: colorScheme.primary,
activeTrackColor: colorScheme.surface,
inactiveTrackColor: colorScheme.onSecondary,
value: isLight,
onChanged: (bool value) {
setState(() {
isLight = value;
_updateImage(widget.images[selectedImage]);
});
})
],
),
body: Center(
child: isLoading
? const CircularProgressIndicator()
: ColoredBox(
color: colorScheme.secondaryContainer,
child: Column(
children: <Widget>[
divider,
_imagesRow(
context,
widget.images,
colorScheme,
),
divider,
Expanded(
child: ColoredBox(
color: colorScheme.background,
child: LayoutBuilder(builder: (BuildContext context,
BoxConstraints constraints) {
if (constraints.maxWidth <
narrowScreenWidthThreshold) {
return SingleChildScrollView(
child: Column(
children: <Widget>[
divider,
schemeLabel(
'Light ColorScheme', colorScheme),
schemeView(lightTheme),
divider,
divider,
schemeLabel(
'Dark ColorScheme', colorScheme),
schemeView(darkTheme),
],
),
);
} else {
return SingleChildScrollView(
child: Padding(
padding: const EdgeInsets.only(top: 5),
child: Column(
children: <Widget>[
Row(
children: <Widget>[
Expanded(
child: Column(
children: <Widget>[
schemeLabel(
'Light ColorScheme',
colorScheme),
schemeView(lightTheme),
],
),
),
Expanded(
child: Column(
children: <Widget>[
schemeLabel(
'Dark ColorScheme',
colorScheme),
schemeView(darkTheme),
],
),
),
],
),
],
),
),
);
}
}),
),
),
],
),
),
),
),
),
);
}
Future<void> _updateImage(ImageProvider provider) async {
final ColorScheme newColorScheme = await ColorScheme.fromImageProvider(
provider: provider,
brightness: isLight ? Brightness.light : Brightness.dark);
setState(() {
selectedImage = widget.images.indexOf(provider);
currentColorScheme = newColorScheme;
});
}
// For small screens, have two rows of image selection. For wide screens,
// fit them onto one row.
Widget _imagesRow(BuildContext context, List<ImageProvider> images,
ColorScheme colorScheme) {
final double windowHeight = MediaQuery.of(context).size.height;
final double windowWidth = MediaQuery.of(context).size.width;
return Padding(
padding: const EdgeInsets.all(8.0),
child: LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
if (constraints.maxWidth > 800) {
return _adaptiveLayoutImagesRow(images, colorScheme, windowHeight);
} else {
return Column(children: <Widget>[
_adaptiveLayoutImagesRow(
images.sublist(0, 3), colorScheme, windowWidth),
_adaptiveLayoutImagesRow(
images.sublist(3), colorScheme, windowWidth),
]);
}
}),
);
}
Widget _adaptiveLayoutImagesRow(
List<ImageProvider> images, ColorScheme colorScheme, double windowWidth) {
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: images
.map(
(ImageProvider image) => Flexible(
flex: (images.length / 3).floor(),
child: GestureDetector(
onTap: () => _updateImage(image),
child: Card(
color: widget.images.indexOf(image) == selectedImage
? colorScheme.primaryContainer
: colorScheme.background,
child: Padding(
padding: const EdgeInsets.all(5.0),
child: ConstrainedBox(
constraints: BoxConstraints(maxWidth: windowWidth * .25),
child: ClipRRect(
borderRadius: BorderRadius.circular(8.0),
child: Image(image: image),
),
),
),
),
),
),
)
.toList(),
);
}
}
class ColorSchemeView extends StatelessWidget {
const ColorSchemeView({super.key, required this.colorScheme});
final ColorScheme colorScheme;
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
ColorGroup(children: <ColorChip>[
ColorChip(
label: 'primary',
color: colorScheme.primary,
onColor: colorScheme.onPrimary),
ColorChip(
label: 'onPrimary',
color: colorScheme.onPrimary,
onColor: colorScheme.primary),
ColorChip(
label: 'primaryContainer',
color: colorScheme.primaryContainer,
onColor: colorScheme.onPrimaryContainer),
ColorChip(
label: 'onPrimaryContainer',
color: colorScheme.onPrimaryContainer,
onColor: colorScheme.primaryContainer),
]),
divider,
ColorGroup(children: <ColorChip>[
ColorChip(
label: 'secondary',
color: colorScheme.secondary,
onColor: colorScheme.onSecondary),
ColorChip(
label: 'onSecondary',
color: colorScheme.onSecondary,
onColor: colorScheme.secondary),
ColorChip(
label: 'secondaryContainer',
color: colorScheme.secondaryContainer,
onColor: colorScheme.onSecondaryContainer),
ColorChip(
label: 'onSecondaryContainer',
color: colorScheme.onSecondaryContainer,
onColor: colorScheme.secondaryContainer),
]),
divider,
ColorGroup(
children: <ColorChip>[
ColorChip(
label: 'tertiary',
color: colorScheme.tertiary,
onColor: colorScheme.onTertiary),
ColorChip(
label: 'onTertiary',
color: colorScheme.onTertiary,
onColor: colorScheme.tertiary),
ColorChip(
label: 'tertiaryContainer',
color: colorScheme.tertiaryContainer,
onColor: colorScheme.onTertiaryContainer),
ColorChip(
label: 'onTertiaryContainer',
color: colorScheme.onTertiaryContainer,
onColor: colorScheme.tertiaryContainer),
],
),
divider,
ColorGroup(
children: <ColorChip>[
ColorChip(
label: 'error',
color: colorScheme.error,
onColor: colorScheme.onError),
ColorChip(
label: 'onError',
color: colorScheme.onError,
onColor: colorScheme.error),
ColorChip(
label: 'errorContainer',
color: colorScheme.errorContainer,
onColor: colorScheme.onErrorContainer),
ColorChip(
label: 'onErrorContainer',
color: colorScheme.onErrorContainer,
onColor: colorScheme.errorContainer),
],
),
divider,
ColorGroup(
children: <ColorChip>[
ColorChip(
label: 'background',
color: colorScheme.background,
onColor: colorScheme.onBackground),
ColorChip(
label: 'onBackground',
color: colorScheme.onBackground,
onColor: colorScheme.background),
],
),
divider,
ColorGroup(
children: <ColorChip>[
ColorChip(
label: 'surface',
color: colorScheme.surface,
onColor: colorScheme.onSurface),
ColorChip(
label: 'onSurface',
color: colorScheme.onSurface,
onColor: colorScheme.surface),
ColorChip(
label: 'surfaceVariant',
color: colorScheme.surfaceVariant,
onColor: colorScheme.onSurfaceVariant),
ColorChip(
label: 'onSurfaceVariant',
color: colorScheme.onSurfaceVariant,
onColor: colorScheme.surfaceVariant),
],
),
divider,
ColorGroup(
children: <ColorChip>[
ColorChip(label: 'outline', color: colorScheme.outline),
ColorChip(label: 'shadow', color: colorScheme.shadow),
ColorChip(
label: 'inverseSurface',
color: colorScheme.inverseSurface,
onColor: colorScheme.onInverseSurface),
ColorChip(
label: 'onInverseSurface',
color: colorScheme.onInverseSurface,
onColor: colorScheme.inverseSurface),
ColorChip(
label: 'inversePrimary',
color: colorScheme.inversePrimary,
onColor: colorScheme.primary),
],
),
],
);
}
}
class ColorGroup extends StatelessWidget {
const ColorGroup({super.key, required this.children});
final List<Widget> children;
@override
Widget build(BuildContext context) {
return RepaintBoundary(
child:
Card(clipBehavior: Clip.antiAlias, child: Column(children: children)),
);
}
}
class ColorChip extends StatelessWidget {
const ColorChip({
super.key,
required this.color,
required this.label,
this.onColor,
});
final Color color;
final Color? onColor;
final String label;
static Color contrastColor(Color color) {
final Brightness brightness = ThemeData.estimateBrightnessForColor(color);
switch (brightness) {
case Brightness.dark:
return Colors.white;
case Brightness.light:
return Colors.black;
}
}
@override
Widget build(BuildContext context) {
final Color labelColor = onColor ?? contrastColor(color);
return ColoredBox(
color: color,
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: <Expanded>[
Expanded(child: Text(label, style: TextStyle(color: labelColor))),
],
),
),
);
}
}
......@@ -2,6 +2,9 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'dart:ui' as ui;
import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:material_color_utilities/material_color_utilities.dart';
......@@ -988,4 +991,198 @@ class ColorScheme with Diagnosticable {
properties.add(ColorProperty('secondaryVariant', secondaryVariant, defaultValue: defaultScheme.secondaryVariant));
properties.add(ColorProperty('surfaceTint', surfaceTint, defaultValue: defaultScheme.surfaceTint));
}
/// Generate a [ColorScheme] derived from the given `imageProvider`.
///
/// Material Color Utilities extracts the dominant color from the
/// supplied [ImageProvider]. Using this color, a set of tonal palettes are
/// constructed. These tonal palettes are based on the Material 3 Color
/// system and provide all the needed colors for a [ColorScheme]. These
/// colors are designed to work well together and meet contrast
/// requirements for accessibility.
///
/// If any of the optional color parameters are non-null, they will be
/// used in place of the generated colors for that field in the resulting
/// color scheme. This allows apps to override specific colors for their
/// needs.
///
/// Given the nature of the algorithm, the most dominant color of the
/// `imageProvider` may not wind up as one of the ColorScheme colors.
///
/// The provided image will be scaled down to a maximum size of 112x112 pixels
/// during color extraction.
///
/// See also:
///
/// * <https://m3.material.io/styles/color/the-color-system/color-roles>, the
/// Material 3 Color system specification.
/// * <https://pub.dev/packages/material_color_utilities>, the package
/// used to generate the base color and tonal palettes needed for the scheme.
static Future<ColorScheme> fromImageProvider({
required ImageProvider provider,
Brightness brightness = Brightness.light,
Color? primary,
Color? onPrimary,
Color? primaryContainer,
Color? onPrimaryContainer,
Color? secondary,
Color? onSecondary,
Color? secondaryContainer,
Color? onSecondaryContainer,
Color? tertiary,
Color? onTertiary,
Color? tertiaryContainer,
Color? onTertiaryContainer,
Color? error,
Color? onError,
Color? errorContainer,
Color? onErrorContainer,
Color? outline,
Color? outlineVariant,
Color? background,
Color? onBackground,
Color? surface,
Color? onSurface,
Color? surfaceVariant,
Color? onSurfaceVariant,
Color? inverseSurface,
Color? onInverseSurface,
Color? inversePrimary,
Color? shadow,
Color? scrim,
Color? surfaceTint,
}) async {
// Extract dominant colors from image.
final QuantizerResult quantizerResult =
await _extractColorsFromImageProvider(provider);
final Map<int, int> colorToCount = quantizerResult.colorToCount.map(
(int key, int value) => MapEntry<int, int>(_getArgbFromAbgr(key), value),
);
// Score colors for color scheme suitability.
final List<int> scoredResults = Score.score(colorToCount, desired: 1);
final ui.Color baseColor = Color(scoredResults.first);
final Scheme scheme;
switch (brightness) {
case Brightness.light:
scheme = Scheme.light(baseColor.value);
break;
case Brightness.dark:
scheme = Scheme.dark(baseColor.value);
break;
}
return ColorScheme(primary: primary ?? Color(scheme.primary),
onPrimary: onPrimary ?? Color(scheme.onPrimary),
primaryContainer: primaryContainer ?? Color(scheme.primaryContainer),
onPrimaryContainer: onPrimaryContainer ?? Color(scheme.onPrimaryContainer),
secondary: secondary ?? Color(scheme.secondary),
onSecondary: onSecondary ?? Color(scheme.onSecondary),
secondaryContainer: secondaryContainer ?? Color(scheme.secondaryContainer),
onSecondaryContainer: onSecondaryContainer ?? Color(scheme.onSecondaryContainer),
tertiary: tertiary ?? Color(scheme.tertiary),
onTertiary: onTertiary ?? Color(scheme.onTertiary),
tertiaryContainer: tertiaryContainer ?? Color(scheme.tertiaryContainer),
onTertiaryContainer: onTertiaryContainer ?? Color(scheme.onTertiaryContainer),
error: error ?? Color(scheme.error),
onError: onError ?? Color(scheme.onError),
errorContainer: errorContainer ?? Color(scheme.errorContainer),
onErrorContainer: onErrorContainer ?? Color(scheme.onErrorContainer),
outline: outline ?? Color(scheme.outline),
outlineVariant: outlineVariant ?? Color(scheme.outlineVariant),
background: background ?? Color(scheme.background),
onBackground: onBackground ?? Color(scheme.onBackground),
surface: surface ?? Color(scheme.surface),
onSurface: onSurface ?? Color(scheme.onSurface),
surfaceVariant: surfaceVariant ?? Color(scheme.surfaceVariant),
onSurfaceVariant: onSurfaceVariant ?? Color(scheme.onSurfaceVariant),
inverseSurface: inverseSurface ?? Color(scheme.inverseSurface),
onInverseSurface: onInverseSurface ?? Color(scheme.inverseOnSurface),
inversePrimary: inversePrimary ?? Color(scheme.inversePrimary),
shadow: shadow ?? Color(scheme.shadow),
scrim: scrim ?? Color(scheme.scrim),
surfaceTint: surfaceTint ?? Color(scheme.primary),
brightness: brightness,
);
}
// ColorScheme.fromImageProvider() utilities.
// Extracts bytes from an [ImageProvider] and returns a [QuantizerResult]
// containing the most dominant colors.
static Future<QuantizerResult> _extractColorsFromImageProvider(ImageProvider imageProvider) async {
final ui.Image scaledImage = await _imageProviderToScaled(imageProvider);
final ByteData? imageBytes = await scaledImage.toByteData();
final QuantizerResult quantizerResult = await QuantizerCelebi().quantize(
imageBytes!.buffer.asUint32List(),
128,
returnInputPixelToClusterPixel: true,
);
return quantizerResult;
}
// Scale image size down to reduce computation time of color extraction.
static Future<ui.Image> _imageProviderToScaled(ImageProvider imageProvider) async {
const double maxDimension = 112.0;
final ImageStream stream = imageProvider.resolve(
const ImageConfiguration(size: Size(maxDimension, maxDimension)));
final Completer<ui.Image> imageCompleter = Completer<ui.Image>();
late ImageStreamListener listener;
late ui.Image scaledImage;
Timer? loadFailureTimeout;
listener = ImageStreamListener((ImageInfo info, bool sync) async {
loadFailureTimeout?.cancel();
stream.removeListener(listener);
final ui.Image image = info.image;
final int width = image.width;
final int height = image.height;
double paintWidth = width.toDouble();
double paintHeight = height.toDouble();
assert(width > 0 && height > 0);
final bool rescale = width > maxDimension || height > maxDimension;
if (rescale) {
paintWidth = (width > height) ? maxDimension : (maxDimension / height) * width;
paintHeight = (height > width) ? maxDimension : (maxDimension / width) * height;
}
final ui.PictureRecorder pictureRecorder = ui.PictureRecorder();
final Canvas canvas = Canvas(pictureRecorder);
paintImage(
canvas: canvas,
rect: Rect.fromLTRB(0, 0, paintWidth, paintHeight),
image: image,
filterQuality: FilterQuality.none);
final ui.Picture picture = pictureRecorder.endRecording();
scaledImage = await picture.toImage(paintWidth.toInt(), paintHeight.toInt());
imageCompleter.complete(info.image);
}, onError: (Object exception, StackTrace? stackTrace) {
stream.removeListener(listener);
throw Exception('Failed to render image: $exception');
});
loadFailureTimeout = Timer(const Duration(seconds: 5), () {
stream.removeListener(listener);
imageCompleter.completeError(
TimeoutException('Timeout occurred trying to load image'));
});
stream.addListener(listener);
await imageCompleter.future;
return scaledImage;
}
// Converts AABBGGRR color int to AARRGGBB format.
static int _getArgbFromAbgr(int abgr) {
const int exceptRMask = 0xFF00FFFF;
const int onlyRMask = ~exceptRMask;
const int exceptBMask = 0xFFFFFF00;
const int onlyBMask = ~exceptBMask;
final int r = (abgr & onlyRMask) >> 16;
final int b = abgr & onlyBMask;
return (abgr & exceptRMask & exceptBMask) | (b << 16) | r;
}
}
......@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../image_data.dart';
void main() {
test('ColorScheme lerp special cases', () {
const ColorScheme scheme = ColorScheme.light();
......@@ -364,6 +368,104 @@ void main() {
expect(scheme.brightness, baseScheme.brightness);
});
test('can generate a light scheme from an imageProvider', () async {
final Uint8List blueSquareBytes = Uint8List.fromList(kBlueSquarePng);
final ImageProvider image = MemoryImage(blueSquareBytes);
final ColorScheme scheme =
await ColorScheme.fromImageProvider(provider: image);
expect(scheme.brightness, Brightness.light);
expect(scheme.primary, const Color(0xff4040f3));
expect(scheme.onPrimary, const Color(0xffffffff));
expect(scheme.primaryContainer, const Color(0xffe1e0ff));
expect(scheme.onPrimaryContainer, const Color(0xff06006c));
expect(scheme.secondary, const Color(0xff5d5c72));
expect(scheme.onSecondary, const Color(0xffffffff));
expect(scheme.secondaryContainer, const Color(0xffe2e0f9));
expect(scheme.onSecondaryContainer, const Color(0xff191a2c));
expect(scheme.tertiary, const Color(0xff79536a));
expect(scheme.onTertiary, const Color(0xffffffff));
expect(scheme.tertiaryContainer, const Color(0xffffd8ec));
expect(scheme.onTertiaryContainer, const Color(0xff2e1125));
expect(scheme.error, const Color(0xffba1a1a));
expect(scheme.onError, const Color(0xffffffff));
expect(scheme.errorContainer, const Color(0xffffdad6));
expect(scheme.onErrorContainer, const Color(0xff410002));
expect(scheme.background, const Color(0xfffffbff));
expect(scheme.onBackground, const Color(0xff1c1b1f));
expect(scheme.surface, const Color(0xfffffbff));
expect(scheme.onSurface, const Color(0xff1c1b1f));
expect(scheme.surfaceVariant, const Color(0xffe4e1ec));
expect(scheme.onSurfaceVariant, const Color(0xff46464f));
expect(scheme.outline, const Color(0xff777680));
expect(scheme.outlineVariant, const Color(0xffc8c5d0));
expect(scheme.shadow, const Color(0xff000000));
expect(scheme.scrim, const Color(0xff000000));
expect(scheme.inverseSurface, const Color(0xff313034));
expect(scheme.onInverseSurface, const Color(0xfff3eff4));
expect(scheme.inversePrimary, const Color(0xffc0c1ff));
expect(scheme.surfaceTint, const Color(0xff4040f3));
expect(scheme.primaryVariant, const Color(0xff4040f3));
expect(scheme.secondaryVariant, const Color(0xff5d5c72));
}, skip: isBrowser, // [intended] uses dart:typed_data.
);
test('can generate a dark scheme from an imageProvider', () async {
final Uint8List blueSquareBytes = Uint8List.fromList(kBlueSquarePng);
final ImageProvider image = MemoryImage(blueSquareBytes);
final ColorScheme scheme = await ColorScheme.fromImageProvider(
provider: image, brightness: Brightness.dark);
expect(scheme.primary, const Color(0xffc0c1ff));
expect(scheme.onPrimary, const Color(0xff0f00aa));
expect(scheme.primaryContainer, const Color(0xff2218dd));
expect(scheme.onPrimaryContainer, const Color(0xffe1e0ff));
expect(scheme.secondary, const Color(0xffc6c4dd));
expect(scheme.onSecondary, const Color(0xff2e2f42));
expect(scheme.secondaryContainer, const Color(0xff454559));
expect(scheme.onSecondaryContainer, const Color(0xffe2e0f9));
expect(scheme.tertiary, const Color(0xffe9b9d3));
expect(scheme.onTertiary, const Color(0xff46263a));
expect(scheme.tertiaryContainer, const Color(0xff5f3c51));
expect(scheme.onTertiaryContainer, const Color(0xffffd8ec));
expect(scheme.error, const Color(0xffffb4ab));
expect(scheme.onError, const Color(0xff690005));
expect(scheme.errorContainer, const Color(0xff93000a));
expect(scheme.onErrorContainer, const Color(0xffffb4ab));
expect(scheme.background, const Color(0xff1c1b1f));
expect(scheme.onBackground, const Color(0xffe5e1e6));
expect(scheme.surface, const Color(0xff1c1b1f));
expect(scheme.onSurface, const Color(0xffe5e1e6));
expect(scheme.surfaceVariant, const Color(0xff46464f));
expect(scheme.onSurfaceVariant, const Color(0xffc8c5d0));
expect(scheme.outline, const Color(0xff918f9a));
expect(scheme.outlineVariant, const Color(0xff46464f));
expect(scheme.inverseSurface, const Color(0xffe5e1e6));
expect(scheme.onInverseSurface, const Color(0xff313034));
expect(scheme.inversePrimary, const Color(0xff4040f3));
expect(scheme.primaryVariant, const Color(0xffc0c1ff));
expect(scheme.secondaryVariant, const Color(0xffc6c4dd));
expect(scheme.surfaceTint, const Color(0xffc0c1ff));
}, skip: isBrowser, // [intended] uses dart:isolate and io.
);
test('fromImageProvider() propogates TimeoutException when image cannot be rendered', () async {
final Uint8List blueSquareBytes = Uint8List.fromList(kBlueSquarePng);
// Corrupt the image's bytelist so it cannot be read.
final Uint8List corruptImage = blueSquareBytes.sublist(5);
final ImageProvider image = MemoryImage(corruptImage);
expect(() async => ColorScheme.fromImageProvider(provider: image), throwsA(
isA<Exception>().having((Exception e) => e.toString(),
'Timeout occurred trying to load image', contains('TimeoutException')),
),
);
});
testWidgets('generated scheme "on" colors meet a11y contrast guidelines', (WidgetTester tester) async {
final ColorScheme colors = ColorScheme.fromSeed(seedColor: Colors.teal);
......
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