From 37fa690c1d14f3ea767b678e66d715664d9759ba Mon Sep 17 00:00:00 2001 From: James Williams <66931+jwill@users.noreply.github.com> Date: Thu, 12 Sep 2024 14:27:11 -0400 Subject: [PATCH] Add color schematic to m3_demo. Change threshold to show wider schematic on wide screens. SchemePreview displays colors in a similar fashion to MTB and Design Kit aligning with other design materials. --- material_3_demo/lib/color_box.dart | 91 ++++ .../lib/color_palettes_screen.dart | 33 +- material_3_demo/lib/scheme.dart | 441 ++++++++++++++++++ material_3_demo/lib/typography_screen.dart | 2 +- 4 files changed, 543 insertions(+), 24 deletions(-) create mode 100644 material_3_demo/lib/color_box.dart create mode 100644 material_3_demo/lib/scheme.dart diff --git a/material_3_demo/lib/color_box.dart b/material_3_demo/lib/color_box.dart new file mode 100644 index 000000000..a54f34271 --- /dev/null +++ b/material_3_demo/lib/color_box.dart @@ -0,0 +1,91 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +class ColorBox extends StatefulWidget { + const ColorBox({ + super.key, + required this.label, + required this.tone, + required this.color, + required this.onColor, + required this.height, + required this.width, + this.displayPaletteInfo = false + }); + + final String label; + final String tone; + final Color color, onColor; + final double height, width; + final bool displayPaletteInfo; + + @override + State createState() => _ColorBoxState(); +} + +class _ColorBoxState extends State { + bool hovered = false; + + @override + Widget build(BuildContext context) { + final fonts = Theme.of(context).textTheme; + return MouseRegion( + onEnter: (_) { + if (mounted) setState(() => hovered = true); + }, + onExit: (_) { + if (mounted) setState(() => hovered = false); + }, + child: Container( + color: widget.color, + height: widget.height, + width: widget.width, + child: DefaultTextStyle( + style: fonts.labelSmall!.copyWith(color: widget.onColor), + child: Stack( + children: [ + Positioned( + top: 10, + left: 10, + child: Text(widget.label), + ), + Positioned( + bottom: 10, + right: 10, + child: Text(widget.displayPaletteInfo ? widget.tone : ''), + ), + if (hovered) + Positioned( + top: 0, + right: 0, + child: IconButton( + padding: EdgeInsets.zero, + color: widget.onColor, + tooltip: 'Copy hex color', + icon: const Icon(Icons.copy, size:24), + onPressed: () async { + final messenger = ScaffoldMessenger.of(context); + // Copy color as hex to clipboard + var hex = '#'; + final c = widget.color; + hex += c.red.toRadixString(16).padLeft(2, '0'); + hex += c.green.toRadixString(16).padLeft(2, '0'); + hex += c.blue.toRadixString(16).padLeft(2, '0'); + final data = ClipboardData(text: hex); + await Clipboard.setData(data); + messenger.hideCurrentSnackBar(); + messenger.showSnackBar( + SnackBar( + content: Text('Copied $hex to clipboard'), + ), + ); + }, + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/material_3_demo/lib/color_palettes_screen.dart b/material_3_demo/lib/color_palettes_screen.dart index a3462c454..2deac64b0 100644 --- a/material_3_demo/lib/color_palettes_screen.dart +++ b/material_3_demo/lib/color_palettes_screen.dart @@ -4,6 +4,7 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; +import 'package:material_3_demo/scheme.dart'; import 'package:url_launcher/url_launcher.dart'; const Widget divider = SizedBox(height: 10); @@ -11,7 +12,7 @@ const Widget divider = SizedBox(height: 10); // If screen content width is greater or equal to this value, the light and dark // color schemes will be displayed in a column. Otherwise, they will // be displayed in a row. -const double narrowScreenWidthThreshold = 400; +const double narrowScreenWidthThreshold = 500; class ColorPalettesScreen extends StatelessWidget { const ColorPalettesScreen({super.key}); @@ -94,32 +95,18 @@ class ColorPalettesScreen extends StatelessWidget { ), ); } else { + var seed = Theme.of(context).colorScheme.primary; + var lightScheme = ColorScheme.fromSeed(seedColor: seed, brightness: Brightness.light); + var darkScheme = ColorScheme.fromSeed(seedColor: seed, brightness: Brightness.dark); return SingleChildScrollView( child: Padding( - padding: const EdgeInsets.only(top: 5), + padding: const EdgeInsets.all(8), child: Column( children: [ - dynamicColorNotice(), - Row( - children: [ - Expanded( - child: Column( - children: [ - schemeLabel('Light ColorScheme'), - schemeView(lightTheme), - ], - ), - ), - Expanded( - child: Column( - children: [ - schemeLabel('Dark ColorScheme'), - schemeView(darkTheme), - ], - ), - ), - ], - ), + SchemePreview(label:"Light ColorScheme", scheme: lightScheme, brightness: Brightness.light, contrast: 1.0, colorMatch: false,), + const SizedBox(height:16), + SchemePreview(label:"Dark ColorScheme", scheme: darkScheme, brightness: Brightness.dark, contrast: 1.0, colorMatch: false,), + const SizedBox(height:16), ], ), ), diff --git a/material_3_demo/lib/scheme.dart b/material_3_demo/lib/scheme.dart new file mode 100644 index 000000000..24891ab54 --- /dev/null +++ b/material_3_demo/lib/scheme.dart @@ -0,0 +1,441 @@ +import 'package:flutter/material.dart'; + +import '../color_box.dart'; + +class SchemePreview extends StatefulWidget { + const SchemePreview({ + super.key, + required this.label, + required this.scheme, + required this.brightness, + //required this.extendedColors, + required this.colorMatch, + required this.contrast, + }); + + final String label; + final ColorScheme scheme; + //final List extendedColors; + final Brightness brightness; + final bool colorMatch; + final double contrast; + + @override + State createState() => _SchemePreviewState(contrast); +} + +class _SchemePreviewState extends State { + double contrast = 0; + + _SchemePreviewState(this.contrast); + + void setContrast(double value) { + if (mounted) { + setState(() { + contrast = value; + }); + } + } + + @override + void didUpdateWidget(covariant SchemePreview oldWidget) { + if (oldWidget.colorMatch != widget.colorMatch ) { + if (mounted) setState(() {}); + } + super.didUpdateWidget(oldWidget); + } + + @override + Widget build(BuildContext context) { + + final theme = Theme.of(context); + final fonts = theme.textTheme; + final colors = theme.colorScheme; + final dark = widget.brightness == Brightness.dark; + + final scheme = widget.scheme; + // TODO state.scheme(widget.brightness, contrastLevel: state.contrastLevel); + + return Theme( + data: theme.copyWith(colorScheme: scheme), + child: FittedBox( + fit: BoxFit.fitWidth, + child: Container( + width: 902, + decoration: BoxDecoration( + color: scheme.surface, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: theme.brightness == widget.brightness + ? colors.outlineVariant + : Colors.transparent, + ), + ), + padding: const EdgeInsets.only( + top: 16, + left: 16, + right: 16, + ), + child: Column( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Row( + children: [ + Expanded( + child: Text( + widget.label, + style: fonts.titleMedium!.copyWith( + color: scheme.onSurface, + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.start, + ), + ), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisSize: MainAxisSize.min, + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisSize: MainAxisSize.min, + children: [ + Column( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Primary', + tone: dark ? 'P-80' : 'P-40', + color: scheme.primary, + onColor: scheme.onPrimary, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Primary', + tone: dark ? 'P-20' : 'P-100', + color: scheme.onPrimary, + onColor: scheme.primary, + height: 40, + width: 208, + ), + const SizedBox(height: 5), + ColorBox( + label: 'Primary Container', + tone: dark ? 'P-30' : 'P-90', + color: scheme.primaryContainer, + onColor: scheme.onPrimaryContainer, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Primary Container', + tone: dark ? 'P-90' : 'P-10', + color: scheme.onPrimaryContainer, + onColor: scheme.primaryContainer, + height: 40, + width: 208, + ), + ], + ), + const SizedBox(width: 5), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Secondary', + tone: dark ? 'S-80' : 'S-40', + color: scheme.secondary, + onColor: scheme.onSecondary, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Secondary', + tone: dark ? 'S-20' : 'S-100', + color: scheme.onSecondary, + onColor: scheme.secondary, + height: 40, + width: 208, + ), + const SizedBox(height: 5), + ColorBox( + label: 'Secondary Container', + tone: dark ? 'S-30' : 'S-90', + color: scheme.secondaryContainer, + onColor: scheme.onSecondaryContainer, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Secondary Container', + tone: dark ? 'S-90' : 'S-10', + color: scheme.onSecondaryContainer, + onColor: scheme.secondaryContainer, + height: 40, + width: 208, + ), + ], + ), + const SizedBox(width: 5), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Tertiary', + tone: dark ? 'T-80' : 'T-40', + color: scheme.tertiary, + onColor: scheme.onTertiary, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Tertiary', + tone: dark ? 'T-20' : 'T-100', + color: scheme.onTertiary, + onColor: scheme.tertiary, + height: 40, + width: 208, + ), + const SizedBox(height: 5), + ColorBox( + label: 'Tertiary Container', + tone: dark ? 'T-30' : 'T-90', + color: scheme.tertiaryContainer, + onColor: scheme.onTertiaryContainer, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Tertiary Container', + tone: dark ? 'T-90' : 'T-10', + color: scheme.onTertiaryContainer, + onColor: scheme.tertiaryContainer, + height: 40, + width: 208, + ), + ], + ), + ], + ), + const SizedBox(height: 20), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Surface Dim', + tone: dark ? 'N-6' : 'N-87', + color: scheme.surfaceDim, + onColor: scheme.onSurface, + height: 105, + width: 211.45, + ), + ColorBox( + label: 'Surface', + tone: dark ? 'N-6' : 'N-98', + color: scheme.surface, + onColor: scheme.onSurface, + height: 105, + width: 211.45, + ), + ColorBox( + label: 'Surface Bright', + tone: dark ? 'N-24' : 'N-98', + color: scheme.surfaceBright, + onColor: scheme.onSurface, + height: 105, + width: 211.45, + ), + ], + ), + const SizedBox(height: 5), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Surf. Container\nLowest', + tone: dark ? 'N-4' : 'N-100', + color: scheme.surfaceContainerLowest, + onColor: scheme.onSurface, + height: 105, + width: 126.87, + ), + ColorBox( + label: 'Surf. Container\nLow', + tone: dark ? 'N-10' : 'N-96', + color: scheme.surfaceContainerLow, + onColor: scheme.onSurface, + height: 105, + width: 126.87, + ), + ColorBox( + label: 'Surf. Container', + tone: dark ? 'N-12' : 'N-94', + color: scheme.surfaceContainer, + onColor: scheme.onSurface, + height: 105, + width: 126.87, + ), + ColorBox( + label: 'Surf. Container\nHigh', + tone: dark ? 'N-17' : 'N-92', + color: scheme.surfaceContainerHigh, + onColor: scheme.onSurface, + height: 105, + width: 126.87, + ), + ColorBox( + label: 'Surf. Container\nHighest', + tone: dark ? 'N-24' : 'N-90', + color: scheme.surfaceContainerHighest, + onColor: scheme.onSurface, + height: 105, + width: 126.87, + ), + ], + ), + const SizedBox(height: 5), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'On Surface', + tone: dark ? 'N-90' : 'N-10', + color: scheme.onSurface, + onColor: scheme.surface, + height: 40, + width: 158.59, + ), + ColorBox( + label: 'On Surface Var.', + tone: dark ? 'NV-90' : 'NV-30', + color: scheme.onSurfaceVariant, + onColor: scheme.surfaceVariant, + height: 40, + width: 158.59, + ), + ColorBox( + label: 'Outline', + tone: dark ? 'NV-60' : 'NV-50', + color: scheme.outline, + onColor: scheme.surface, + height: 40, + width: 158.59, + ), + ColorBox( + label: 'Outline Variant', + tone: dark ? 'NV-30' : 'NV-80', + color: scheme.outlineVariant, + onColor: scheme.onSurface, + height: 40, + width: 158.59, + ), + ], + ), + ], + ), + const SizedBox(width: 20), + Column( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Error', + tone: dark ? 'E-80' : 'E-40', + color: scheme.error, + onColor: scheme.onError, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Error', + tone: dark ? 'E-20' : 'E-100', + color: scheme.onError, + onColor: scheme.error, + height: 40, + width: 208, + ), + const SizedBox(height: 5), + ColorBox( + label: 'Error Container', + tone: dark ? 'E-30' : 'E-90', + color: scheme.errorContainer, + onColor: scheme.onErrorContainer, + height: 87, + width: 208, + ), + ColorBox( + label: 'On Error Container', + tone: dark ? 'E-90' : 'E-10', + color: scheme.onErrorContainer, + onColor: scheme.errorContainer, + height: 40, + width: 208, + ), + const SizedBox(height: 20), + ColorBox( + label: 'Inverse Surface', + tone: dark ? 'N-90' : 'N-20', + color: scheme.inverseSurface, + onColor: scheme.onInverseSurface, + height: 120, + width: 208, + ), + ColorBox( + label: 'Inverse On Surface', + tone: dark ? 'N-20' : 'N-95', + color: scheme.onInverseSurface, + onColor: scheme.inverseSurface, + height: 40, + width: 208, + ), + const SizedBox(height: 5), + ColorBox( + label: 'Inverse Primary', + tone: dark ? 'P-40' : 'P-80', + color: scheme.inversePrimary, + onColor: scheme.onSurface, + height: 40, + width: 208, + ), + const SizedBox(height: 16), + Row( + mainAxisSize: MainAxisSize.min, + children: [ + ColorBox( + label: 'Scrim', + tone: 'N-0', + color: scheme.scrim, + onColor: Colors.white, + height: 40, + width: 96.31, + ), + const SizedBox(width: 20), + ColorBox( + label: 'Shadow', + tone: 'N-0', + color: scheme.shadow, + onColor: Colors.white, + height: 40, + width: 96.31, + ), + ], + ), + const SizedBox(height: 8), + ], + ), + ], + ), + ], + ), + ), + ), + ); + } +} diff --git a/material_3_demo/lib/typography_screen.dart b/material_3_demo/lib/typography_screen.dart index 6950e9575..08888115b 100644 --- a/material_3_demo/lib/typography_screen.dart +++ b/material_3_demo/lib/typography_screen.dart @@ -15,7 +15,7 @@ class TypographyScreen extends StatelessWidget { return Expanded( child: ListView( children: [ - const SizedBox(height: 7), + const SizedBox(height: 8), TextStyleExample( name: 'Display Large', style: textTheme.displayLarge!), TextStyleExample(