make ColorScheme configurable

leigha_m3_demo
Qun Cheng 1 year ago committed by Leigha Jarett
parent 4287e7caed
commit b0023ead5d

@ -4,6 +4,10 @@
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:material_3_demo/home.dart';
import 'package:url_launcher/url_launcher.dart';
const Widget divider = SizedBox(height: 10);
@ -14,20 +18,19 @@ const Widget divider = SizedBox(height: 10);
const double narrowScreenWidthThreshold = 400;
class ColorPalettesScreen extends StatelessWidget {
const ColorPalettesScreen({super.key});
const ColorPalettesScreen({
super.key,
required this.handleColorRoleChange,
required this.lightColorScheme,
required this.darkColorScheme,
});
final ConfigColorSchemeCallback handleColorRoleChange;
final ColorScheme lightColorScheme;
final ColorScheme darkColorScheme;
@override
Widget build(BuildContext context) {
Color selectedColor = Theme.of(context).primaryColor;
ThemeData lightTheme = ThemeData(
colorSchemeSeed: selectedColor,
brightness: Brightness.light,
);
ThemeData darkTheme = ThemeData(
colorSchemeSeed: selectedColor,
brightness: Brightness.dark,
);
Widget schemeLabel(String brightness) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 15),
@ -38,11 +41,13 @@ class ColorPalettesScreen extends StatelessWidget {
);
}
Widget schemeView(ThemeData theme) {
Widget schemeView(Brightness brightness) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 15),
child: ColorSchemeView(
colorScheme: theme.colorScheme,
handleColorRoleChange: handleColorRoleChange,
colorScheme: brightness == Brightness.light ? lightColorScheme : darkColorScheme,
brightness: brightness,
),
);
}
@ -83,11 +88,11 @@ class ColorPalettesScreen extends StatelessWidget {
dynamicColorNotice(),
divider,
schemeLabel('Light ColorScheme'),
schemeView(lightTheme),
schemeView(Brightness.light),
divider,
divider,
schemeLabel('Dark ColorScheme'),
schemeView(darkTheme),
schemeView(Brightness.dark),
],
),
);
@ -104,7 +109,7 @@ class ColorPalettesScreen extends StatelessWidget {
child: Column(
children: [
schemeLabel('Light ColorScheme'),
schemeView(lightTheme),
schemeView(Brightness.light),
],
),
),
@ -112,7 +117,7 @@ class ColorPalettesScreen extends StatelessWidget {
child: Column(
children: [
schemeLabel('Dark ColorScheme'),
schemeView(darkTheme),
schemeView(Brightness.dark),
],
),
),
@ -128,10 +133,38 @@ class ColorPalettesScreen extends StatelessWidget {
}
}
class ColorSchemeView extends StatelessWidget {
const ColorSchemeView({super.key, required this.colorScheme});
class ColorSchemeView extends StatefulWidget {
const ColorSchemeView({
super.key,
required this.handleColorRoleChange,
required this.colorScheme,
required this.brightness,
});
final ConfigColorSchemeCallback handleColorRoleChange;
final ColorScheme colorScheme;
final Brightness brightness;
@override
State<ColorSchemeView> createState() => _ColorSchemeViewState();
}
class _ColorSchemeViewState extends State<ColorSchemeView> {
late ColorScheme _colorScheme;
@override
void initState() {
_colorScheme = widget.colorScheme;
super.initState();
}
@override
void didUpdateWidget(covariant ColorSchemeView oldWidget) {
if (oldWidget.colorScheme != widget.colorScheme) {
_colorScheme = widget.colorScheme;
}
super.didUpdateWidget(oldWidget);
}
@override
Widget build(BuildContext context) {
@ -139,185 +172,395 @@ class ColorSchemeView extends StatelessWidget {
children: [
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'primary',
color: colorScheme.primary,
onColor: colorScheme.onPrimary,
color: _colorScheme.primary,
onColor: _colorScheme.onPrimary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(primary: color);
widget.handleColorRoleChange(
widget.brightness,
primary: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onPrimary',
color: colorScheme.onPrimary,
onColor: colorScheme.primary,
color: _colorScheme.onPrimary,
onColor: _colorScheme.primary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onPrimary: color);
widget.handleColorRoleChange(
widget.brightness,
onPrimary: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'primaryContainer',
color: colorScheme.primaryContainer,
onColor: colorScheme.onPrimaryContainer,
color: _colorScheme.primaryContainer,
onColor: _colorScheme.onPrimaryContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(primaryContainer: color);
widget.handleColorRoleChange(
widget.brightness,
primaryContainer: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onPrimaryContainer',
color: colorScheme.onPrimaryContainer,
onColor: colorScheme.primaryContainer,
color: _colorScheme.onPrimaryContainer,
onColor: _colorScheme.primaryContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onPrimaryContainer: color);
widget.handleColorRoleChange(
widget.brightness,
onPrimaryContainer: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'secondary',
color: colorScheme.secondary,
onColor: colorScheme.onSecondary,
color: _colorScheme.secondary,
onColor: _colorScheme.onSecondary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(secondary: color);
widget.handleColorRoleChange(
widget.brightness,
secondary: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onSecondary',
color: colorScheme.onSecondary,
onColor: colorScheme.secondary,
color: _colorScheme.onSecondary,
onColor: _colorScheme.secondary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onSecondary: color);
widget.handleColorRoleChange(
widget.brightness,
onSecondary: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'secondaryContainer',
color: colorScheme.secondaryContainer,
onColor: colorScheme.onSecondaryContainer,
color: _colorScheme.secondaryContainer,
onColor: _colorScheme.onSecondaryContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(secondaryContainer: color);
widget.handleColorRoleChange(
widget.brightness,
secondaryContainer: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onSecondaryContainer',
color: colorScheme.onSecondaryContainer,
onColor: colorScheme.secondaryContainer,
color: _colorScheme.onSecondaryContainer,
onColor: _colorScheme.secondaryContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onSecondaryContainer: color);
widget.handleColorRoleChange(
widget.brightness,
onSecondaryContainer: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'tertiary',
color: colorScheme.tertiary,
onColor: colorScheme.onTertiary,
color: _colorScheme.tertiary,
onColor: _colorScheme.onTertiary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(tertiary: color);
widget.handleColorRoleChange(
widget.brightness,
tertiary: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onTertiary',
color: colorScheme.onTertiary,
onColor: colorScheme.tertiary,
color: _colorScheme.onTertiary,
onColor: _colorScheme.tertiary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onTertiary: color);
widget.handleColorRoleChange(
widget.brightness,
onTertiary: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'tertiaryContainer',
color: colorScheme.tertiaryContainer,
onColor: colorScheme.onTertiaryContainer,
color: _colorScheme.tertiaryContainer,
onColor: _colorScheme.onTertiaryContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(tertiaryContainer: color);
widget.handleColorRoleChange(
widget.brightness,
tertiaryContainer: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onTertiaryContainer',
color: colorScheme.onTertiaryContainer,
onColor: colorScheme.tertiaryContainer,
color: _colorScheme.onTertiaryContainer,
onColor: _colorScheme.tertiaryContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onTertiaryContainer: color);
widget.handleColorRoleChange(
widget.brightness,
onTertiaryContainer: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'error',
color: colorScheme.error,
onColor: colorScheme.onError,
color: _colorScheme.error,
onColor: _colorScheme.onError,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(error: color);
widget.handleColorRoleChange(
widget.brightness,
error: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onError',
color: colorScheme.onError,
onColor: colorScheme.error,
color: _colorScheme.onError,
onColor: _colorScheme.error,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onError: color);
widget.handleColorRoleChange(
widget.brightness,
onError: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'errorContainer',
color: colorScheme.errorContainer,
onColor: colorScheme.onErrorContainer,
color: _colorScheme.errorContainer,
onColor: _colorScheme.onErrorContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(errorContainer: color);
widget.handleColorRoleChange(
widget.brightness,
errorContainer: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onErrorContainer',
color: colorScheme.onErrorContainer,
onColor: colorScheme.errorContainer,
color: _colorScheme.onErrorContainer,
onColor: _colorScheme.errorContainer,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onErrorContainer: color);
widget.handleColorRoleChange(
widget.brightness,
onErrorContainer: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'surface',
color: colorScheme.surface,
onColor: colorScheme.onSurface,
color: _colorScheme.surface,
onColor: _colorScheme.onSurface,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(surface: color);
widget.handleColorRoleChange(
widget.brightness,
surface: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onSurface',
color: colorScheme.onSurface,
onColor: colorScheme.surface,
color: _colorScheme.onSurface,
onColor: _colorScheme.surface,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onSurface: color);
widget.handleColorRoleChange(
widget.brightness,
onSurface: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'surfaceVariant',
color: colorScheme.surfaceVariant,
onColor: colorScheme.onSurfaceVariant,
color: _colorScheme.surfaceVariant,
onColor: _colorScheme.onSurfaceVariant,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(surfaceVariant: color);
widget.handleColorRoleChange(
widget.brightness,
surfaceVariant: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onSurfaceVariant',
color: colorScheme.onSurfaceVariant,
onColor: colorScheme.surfaceVariant,
color: _colorScheme.onSurfaceVariant,
onColor: _colorScheme.surfaceVariant,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onSurfaceVariant: color);
widget.handleColorRoleChange(
widget.brightness,
onSurfaceVariant: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'surfaceTint',
color: colorScheme.surfaceTint,
color: _colorScheme.surfaceTint,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(surfaceTint: color);
widget.handleColorRoleChange(
widget.brightness,
surfaceTint: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'outline',
color: colorScheme.outline,
color: _colorScheme.outline,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(outline: color);
widget.handleColorRoleChange(
widget.brightness,
outline: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'outlineVariant',
color: colorScheme.outlineVariant,
color: _colorScheme.outlineVariant,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(outlineVariant: color);
widget.handleColorRoleChange(
widget.brightness,
outlineVariant: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'inverseSurface',
color: colorScheme.inverseSurface,
onColor: colorScheme.onInverseSurface,
color: _colorScheme.inverseSurface,
onColor: _colorScheme.onInverseSurface,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(inverseSurface: color);
widget.handleColorRoleChange(
widget.brightness,
inverseSurface: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onInverseSurface',
color: colorScheme.onInverseSurface,
onColor: colorScheme.inverseSurface,
color: _colorScheme.onInverseSurface,
onColor: _colorScheme.inverseSurface,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onInverseSurface: color);
widget.handleColorRoleChange(
widget.brightness,
onInverseSurface: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'inversePrimary',
color: colorScheme.inversePrimary,
onColor: colorScheme.primary,
color: _colorScheme.inversePrimary,
onColor: _colorScheme.primary,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(inversePrimary: color);
widget.handleColorRoleChange(
widget.brightness,
inversePrimary: color,
);
},
),
],
),
divider,
ColorGroup(
children: [
ColorChip(
EditableColorChip(
label: 'background',
color: colorScheme.background,
onColor: colorScheme.onBackground,
color: _colorScheme.background,
onColor: _colorScheme.onBackground,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(background: color);
widget.handleColorRoleChange(
widget.brightness,
background: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'onBackground',
color: colorScheme.onBackground,
onColor: colorScheme.background,
color: _colorScheme.onBackground,
onColor: _colorScheme.background,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(onBackground: color);
widget.handleColorRoleChange(
widget.brightness,
onBackground: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'scrim',
color: colorScheme.scrim,
color: _colorScheme.scrim,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(scrim: color);
widget.handleColorRoleChange(
widget.brightness,
scrim: color,
);
},
),
ColorChip(
EditableColorChip(
label: 'shadow',
color: colorScheme.shadow,
color: _colorScheme.shadow,
updateColorScheme: (color) {
_colorScheme = _colorScheme.copyWith(shadow: color);
widget.handleColorRoleChange(
widget.brightness,
shadow: color,
);
},
),
],
),
@ -327,6 +570,85 @@ class ColorSchemeView extends StatelessWidget {
}
}
class EditableColorChip extends StatefulWidget {
const EditableColorChip({
super.key,
this.onColor,
required this.label,
required this.color,
required this.updateColorScheme,
});
final String label;
final Color color;
final Color? onColor;
final void Function(Color) updateColorScheme;
@override
State<EditableColorChip> createState() => _EditableColorChipState();
}
class _EditableColorChipState extends State<EditableColorChip> {
TextEditingController textController = TextEditingController();
@override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
showDialog<void>(
context: context,
builder: (context) {
return AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ColorPicker(
pickerColor: widget.color,
onColorChanged: widget.updateColorScheme,
paletteType: PaletteType.hsvWithHue,
labelTypes: const [
ColorLabelType.rgb,
ColorLabelType.hsv,
ColorLabelType.hsl
],
pickerAreaBorderRadius: const BorderRadius.all(Radius.circular(10)),
hexInputController: textController,
portraitOnly: true,
),
Padding(
padding: const EdgeInsets.fromLTRB(16, 0, 16, 16),
child: TextField(
controller: textController,
decoration: InputDecoration(
prefixText: '#',
suffix: IconButton(
icon: const Icon(Icons.content_paste_rounded),
onPressed: () => copyToClipboard(textController.text),
),
),
autofocus: true,
maxLength: 8,
inputFormatters: [
UpperCaseTextFormatter(),
FilteringTextInputFormatter.allow(RegExp(kValidHexPattern)),
],
),
)
],
),
);
}
);
},
child: ColorChip(
label: widget.label,
color: widget.color,
onColor: widget.onColor,
),
);
}
}
class ColorGroup extends StatelessWidget {
const ColorGroup({super.key, required this.children});
@ -336,6 +658,7 @@ class ColorGroup extends StatelessWidget {
Widget build(BuildContext context) {
return RepaintBoundary(
child: Card(
elevation: 0.0,
clipBehavior: Clip.antiAlias,
child: Column(
children: children,
@ -378,9 +701,23 @@ class ColorChip extends StatelessWidget {
child: Row(
children: [
Expanded(child: Text(label, style: TextStyle(color: labelColor))),
Text(
color.value.toRadixString(16).substring(2),
style: GoogleFonts.spaceMono(
color: labelColor,
),
),
],
),
),
);
}
}
void copyToClipboard(String input) {
String textToCopy = input.replaceFirst('#', '').toUpperCase();
if (textToCopy.startsWith('FF') && textToCopy.length == 8) {
textToCopy = textToCopy.replaceFirst('FF', '');
}
Clipboard.setData(ClipboardData(text: '#$textToCopy'));
}

@ -10,14 +10,50 @@ import 'constants.dart';
import 'elevation_screen.dart';
import 'typography_screen.dart';
typedef ConfigColorSchemeCallback = void Function(Brightness brightness,
{ 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? background,
Color? onBackground,
Color? surface,
Color? onSurface,
Color? surfaceVariant,
Color? onSurfaceVariant,
Color? outline,
Color? outlineVariant,
Color? shadow,
Color? scrim,
Color? inverseSurface,
Color? onInverseSurface,
Color? inversePrimary,
Color? surfaceTint,
});
class Home extends StatefulWidget {
const Home({
super.key,
required this.useLightMode,
required this.useMaterial3,
required this.colorSelected,
required this.lightColors,
required this.darkColors,
required this.handleBrightnessChange,
required this.handleMaterialVersionChange,
required this.handleColorRoleChange,
required this.handleColorSelect,
required this.handleImageSelect,
required this.colorSelectionMethod,
@ -27,6 +63,8 @@ class Home extends StatefulWidget {
final bool useLightMode;
final bool useMaterial3;
final ColorSeed colorSelected;
final ColorScheme lightColors;
final ColorScheme darkColors;
final ColorImageProvider imageSelected;
final ColorSelectionMethod colorSelectionMethod;
@ -34,6 +72,7 @@ class Home extends StatefulWidget {
final void Function() handleMaterialVersionChange;
final void Function(int value) handleColorSelect;
final void Function(int value) handleImageSelect;
final ConfigColorSchemeCallback handleColorRoleChange;
@override
State<Home> createState() => _HomeState();
@ -124,7 +163,11 @@ class _HomeState extends State<Home> with SingleTickerProviderStateMixin {
),
);
case ScreenSelected.color:
return const ColorPalettesScreen();
return ColorPalettesScreen(
handleColorRoleChange: widget.handleColorRoleChange,
lightColorScheme: widget.lightColors,
darkColorScheme: widget.darkColors,
);
case ScreenSelected.typography:
return const TypographyScreen();
case ScreenSelected.elevation:

@ -27,6 +27,8 @@ class _AppState extends State<App> {
ColorImageProvider imageSelected = ColorImageProvider.leaves;
ColorScheme? imageColorScheme = const ColorScheme.light();
ColorSelectionMethod colorSelectionMethod = ColorSelectionMethod.colorSeed;
ColorScheme? lightColorScheme;
ColorScheme? darkColorScheme;
bool get useLightMode {
switch (themeMode) {
@ -53,6 +55,7 @@ class _AppState extends State<App> {
}
void handleColorSelect(int value) {
clearColorScheme();
setState(() {
colorSelectionMethod = ColorSelectionMethod.colorSeed;
colorSelected = ColorSeed.values[value];
@ -60,6 +63,7 @@ class _AppState extends State<App> {
}
void handleImageSelect(int value) {
clearColorScheme();
final String url = ColorImageProvider.values[value].url;
ColorScheme.fromImageProvider(provider: NetworkImage(url))
.then((newScheme) {
@ -71,13 +75,92 @@ class _AppState extends State<App> {
});
}
void handleColorRoleChange(Brightness brightness,
{ 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? background,
Color? onBackground,
Color? surface,
Color? onSurface,
Color? surfaceVariant,
Color? onSurfaceVariant,
Color? outline,
Color? outlineVariant,
Color? shadow,
Color? scrim,
Color? inverseSurface,
Color? onInverseSurface,
Color? inversePrimary,
Color? surfaceTint,
}
) {
ColorScheme? copyWith(ColorScheme? colorScheme) {
return colorScheme?.copyWith(
primary: primary,
onPrimary: onPrimary,
primaryContainer: primaryContainer,
onPrimaryContainer: onPrimaryContainer,
secondary: secondary,
onSecondary: onSecondary,
secondaryContainer: secondaryContainer,
onSecondaryContainer: onSecondaryContainer,
tertiary: tertiary,
onTertiary: onTertiary,
tertiaryContainer: tertiaryContainer,
onTertiaryContainer: onTertiaryContainer,
error: error,
onError: onError,
errorContainer: errorContainer,
onErrorContainer: onErrorContainer,
background: background,
onBackground: onBackground,
surface: surface,
onSurface: onSurface,
surfaceVariant: surfaceVariant,
onSurfaceVariant: onSurfaceVariant,
outline: outline,
outlineVariant: outlineVariant,
shadow: shadow,
scrim: scrim,
inverseSurface: inverseSurface,
onInverseSurface: onInverseSurface,
inversePrimary: inversePrimary,
surfaceTint: surfaceTint,
);
}
setState(() {
switch (brightness) {
case Brightness.light:
lightColorScheme = copyWith(lightColorScheme);
case Brightness.dark:
darkColorScheme = copyWith(darkColorScheme);
}
});
}
void clearColorScheme() {
lightColorScheme = null;
darkColorScheme = null;
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material 3',
themeMode: themeMode,
theme: ThemeData(
ThemeData lightTheme = ThemeData(
colorSchemeSeed: colorSelectionMethod == ColorSelectionMethod.colorSeed
? colorSelected.color
: null,
@ -86,23 +169,41 @@ class _AppState extends State<App> {
: null,
useMaterial3: useMaterial3,
brightness: Brightness.light,
),
darkTheme: ThemeData(
).copyWith(
colorScheme: lightColorScheme,
);
ThemeData darkTheme = ThemeData(
colorSchemeSeed: colorSelectionMethod == ColorSelectionMethod.colorSeed
? colorSelected.color
: imageColorScheme!.primary,
useMaterial3: useMaterial3,
brightness: Brightness.dark,
),
).copyWith(
colorScheme: darkColorScheme,
);
lightColorScheme = lightTheme.colorScheme;
darkColorScheme = darkTheme.colorScheme;
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Material 3',
themeMode: themeMode,
theme: lightTheme,
darkTheme: darkTheme,
home: Home(
useLightMode: useLightMode,
useMaterial3: useMaterial3,
colorSelected: colorSelected,
lightColors: lightTheme.colorScheme,
darkColors: darkTheme.colorScheme,
imageSelected: imageSelected,
handleBrightnessChange: handleBrightnessChange,
handleMaterialVersionChange: handleMaterialVersionChange,
handleColorSelect: handleColorSelect,
handleImageSelect: handleImageSelect,
handleColorRoleChange: handleColorRoleChange,
colorSelectionMethod: colorSelectionMethod,
),
);

@ -16,6 +16,8 @@ dependencies:
cupertino_icons: ^1.0.2
url_launcher: ^6.1.8
google_fonts: ^6.1.0
flutter_colorpicker: ^1.0.3
dev_dependencies:
analysis_defaults:

@ -4,6 +4,7 @@
// ignore_for_file: avoid_types_on_closure_parameters
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:material_3_demo/color_palettes_screen.dart';
import 'package:material_3_demo/main.dart';
@ -65,11 +66,134 @@ void main() {
});
testWidgets('Color screen shows correct content', (tester) async {
await tester.pumpWidget(const MaterialApp(
home: Scaffold(body: Row(children: [ColorPalettesScreen()])),
));
widgetSetup(tester, 1200);
addTearDown(tester.view.resetPhysicalSize);
await tester.pumpWidget(const App());
Finder colorPageIndicator = find.descendant(of: find.byType(NavigationRail),
matching: find.byIcon(Icons.format_paint_outlined));
expect(colorPageIndicator, findsOneWidget);
await tester.tap(colorPageIndicator);
await tester.pumpAndSettle();
expect(find.text('Light ColorScheme'), findsOneWidget);
expect(find.text('Dark ColorScheme'), findsOneWidget);
expect(find.byType(ColorGroup, skipOffstage: false), findsNWidgets(16));
});
testWidgets('Color screen shows correct content', (tester) async {
widgetSetup(tester, 1200);
addTearDown(tester.view.resetPhysicalSize);
await tester.pumpWidget(const App());
Finder colorPageIndicator = find.descendant(of: find.byType(NavigationRail),
matching: find.byIcon(Icons.format_paint_outlined));
expect(colorPageIndicator, findsOneWidget);
await tester.tap(colorPageIndicator);
await tester.pumpAndSettle();
expect(find.text('Light ColorScheme'), findsOneWidget);
expect(find.text('Dark ColorScheme'), findsOneWidget);
expect(find.byType(ColorGroup, skipOffstage: false), findsNWidgets(16));
});
testWidgets('ColorScheme are editable', (tester) async {
widgetSetup(tester, 1200);
addTearDown(tester.view.resetPhysicalSize);
await tester.pumpWidget(const App());
await updatePrimaryChipColor(tester, const Color(0xFF73A450), true);
expect(find.text('73a450'), findsOneWidget);
// Test if the chip color is changed to 73a450.
Finder primaryChip = find.descendant(of: find.widgetWithText(ColorChip, 'primary').first, matching: find.byType(Container));
Container container = tester.widget<Container>(primaryChip);
expect(container.color, const Color(0xFF73A450));
});
testWidgets('Light ColorScheme is configurable; changes can be applied to the widget page', (tester) async {
widgetSetup(tester, 1200);
addTearDown(tester.view.resetPhysicalSize);
await tester.pumpWidget(const App());
Color textColor(Finder text) {
return tester.renderObject<RenderParagraph>(text).text.style!.color!;
}
await updatePrimaryChipColor(tester, const Color(0xff123456), true);
// Go to the component screen
Finder componentPageIndicator = find.descendant(of: find.byType(NavigationRail),
matching: find.byIcon(Icons.widgets_outlined));
expect(componentPageIndicator, findsOneWidget);
await tester.tap(componentPageIndicator);
await tester.pumpAndSettle();
Finder elevatedButtonText = find.text('Elevated').first;
expect(textColor(elevatedButtonText), const Color(0xff123456));
Finder outlinedButtonText = find.text('Outlined').first;
expect(textColor(outlinedButtonText), const Color(0xff123456));
Finder textButtonText = find.text('Text').first;
expect(textColor(textButtonText), const Color(0xff123456));
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(FilledButton).first,
matching: find.byType(Material),
));
expect(material.color, const Color(0xff123456));
});
testWidgets('Dark ColorScheme is configurable; changes can be applied to the widget page', (tester) async {
widgetSetup(tester, 1200);
addTearDown(tester.view.resetPhysicalSize);
await tester.pumpWidget(const App());
Color textColor(Finder text) {
return tester.renderObject<RenderParagraph>(text).text.style!.color!;
}
// Switch to dark mode
await tester.tap(find.byIcon(Icons.dark_mode_outlined));
await tester.pumpAndSettle();
await updatePrimaryChipColor(tester, const Color(0xff654321), false);
// Go to the component screen
Finder componentPageIndicator = find.descendant(of: find.byType(NavigationRail),
matching: find.byIcon(Icons.widgets_outlined));
expect(componentPageIndicator, findsOneWidget);
await tester.tap(componentPageIndicator);
await tester.pumpAndSettle();
Finder elevatedButtonText = find.text('Elevated').first;
expect(textColor(elevatedButtonText), const Color(0xff654321));
Finder outlinedButtonText = find.text('Outlined').first;
expect(textColor(outlinedButtonText), const Color(0xff654321));
Finder textButtonText = find.text('Text').first;
expect(textColor(textButtonText), const Color(0xff654321));
final Material material = tester.widget<Material>(find.descendant(
of: find.byType(FilledButton).first,
matching: find.byType(Material),
));
expect(material.color, const Color(0xff654321));
});
}
Future<void> updatePrimaryChipColor(WidgetTester tester, Color newColor, bool isLight) async {
Finder colorPageIndicator = find.descendant(of: find.byType(NavigationRail),
matching: find.byIcon(Icons.format_paint_outlined));
expect(colorPageIndicator, findsOneWidget);
await tester.tap(colorPageIndicator);
await tester.pumpAndSettle();
expect(isLight ? find.text('Light ColorScheme') : find.text('Dark ColorScheme'), findsOneWidget);
Finder primaryColorChip = isLight
? find.widgetWithText(EditableColorChip, 'primary').first
: find.widgetWithText(EditableColorChip, 'primary').last;
await tester.tap(primaryColorChip);
await tester.pump();
expect(find.byType(AlertDialog), findsOneWidget);
await tester.enterText(find.byType(TextField), newColor.value.toRadixString(16));
// Tap on the barrier.
await tester.tapAt(const Offset(10.0, 10.0));
await tester.pumpAndSettle();
expect(find.byType(AlertDialog), findsNothing);
}

Loading…
Cancel
Save