Created
July 19, 2024 15:47
-
-
Save fredgrott/9a29ebb23c6950b81b1b60dc947c9af3 to your computer and use it in GitHub Desktop.
custom color palette screen
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright 2024 Fredrick Allan Grott. All rights reserved. | |
// Use of this source code is governed by a BSD-style | |
// license that can be found in the LICENSE file. | |
// | |
// Modified from the Flutter Samples Material Demo | |
// Copyright 2021 under BSD license by Flutter Team | |
import 'package:auto_animated/auto_animated.dart'; | |
import 'package:flutter/gestures.dart'; | |
import 'package:flutter/material.dart'; | |
import 'package:raw_md3_demo/build_animated_item.dart'; | |
import 'package:url_launcher/url_launcher.dart'; | |
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; | |
// ScrollAdapter always requires animating the individual items in a list | |
class CustomColorPaletteScreen extends StatelessWidget { | |
const CustomColorPaletteScreen({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
final Color selectedColor = Theme.of(context).primaryColor; | |
final ThemeData lightTheme = ThemeData( | |
colorSchemeSeed: selectedColor, | |
brightness: Brightness.light, | |
); | |
final ThemeData darkTheme = ThemeData( | |
colorSchemeSeed: selectedColor, | |
brightness: Brightness.dark, | |
); | |
Widget schemeLabel(String brightness) { | |
return Padding( | |
padding: const EdgeInsets.symmetric(vertical: 15), | |
child: Text( | |
brightness, | |
style: const TextStyle(fontWeight: FontWeight.bold), | |
), | |
); | |
} | |
Widget schemeView(ThemeData theme) { | |
return Padding( | |
padding: const EdgeInsets.symmetric(horizontal: 15), | |
child: ColorSchemeView( | |
colorScheme: theme.colorScheme, | |
), | |
); | |
} | |
Widget dynamicColorNotice() => RichText( | |
textAlign: TextAlign.center, | |
text: TextSpan( | |
style: Theme.of(context).textTheme.bodySmall, | |
children: [ | |
const TextSpan( | |
text: 'To create color schemes based on a ' | |
"platform's implementation of dynamic color, " | |
'use the ', | |
), | |
TextSpan( | |
text: 'dynamic_color', | |
style: const TextStyle(decoration: TextDecoration.underline), | |
recognizer: TapGestureRecognizer() | |
..onTap = () async { | |
final url = Uri.parse( | |
'https://pub.dev/packages/dynamic_color', | |
); | |
if (!await launchUrl(url)) { | |
throw Exception('Could not launch $url'); | |
} | |
}, | |
), | |
const TextSpan(text: ' package.'), | |
], | |
), | |
); | |
final colorScrollController = ScrollController(); | |
return Expanded( | |
child: LayoutBuilder( | |
builder: (context, constraints) { | |
if (constraints.maxWidth < narrowScreenWidthThreshold) { | |
return AnimateIfVisibleWrapper( | |
// ignore: avoid_redundant_argument_values | |
showItemInterval: const Duration(milliseconds: 150), | |
child: SingleChildScrollView( | |
key: const PageStorageKey("colorOne"), | |
restorationId: "one", | |
controller: colorScrollController, | |
child: Column( | |
children: <Widget>[ | |
AnimateIfVisible( | |
key: const Key("one"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
dynamicColorNotice as Widget, | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("two"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
divider, | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("three"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
schemeLabel('Light ColorScheme'), | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("four"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
schemeView(lightTheme), | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("five"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
divider, | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("six"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
divider, | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("seven"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
schemeLabel('Dark ColorScheme'), | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("eight"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
schemeView(darkTheme), | |
), | |
), | |
], | |
), | |
), | |
); | |
} else{ | |
return AnimateIfVisibleWrapper( | |
// ignore: avoid_redundant_argument_values | |
showItemInterval: const Duration(milliseconds: 150), | |
child: SingleChildScrollView( | |
key: const PageStorageKey("colorTwo"), | |
restorationId: "two", | |
controller: colorScrollController, | |
child: Padding( | |
padding: const EdgeInsets.only(top: 5), | |
child: Column( | |
children: <Widget>[ | |
AnimateIfVisible( | |
key: const Key("nine"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
dynamicColorNotice(), | |
), | |
), | |
AnimateIfVisible( | |
key: const Key("ten"), | |
duration: const Duration(milliseconds: 500), | |
builder: animationBuilder( | |
Row( | |
children: [ | |
Expanded( | |
child: Column( | |
children: [ | |
schemeLabel('Light ColorScheme'), | |
schemeView(lightTheme), | |
], | |
), | |
), | |
Expanded( | |
child: Column( | |
children: [ | |
schemeLabel('Dark ColorScheme'), | |
schemeView(darkTheme), | |
], | |
), | |
), | |
], | |
), | |
), | |
), | |
], | |
), | |
), | |
), | |
); | |
} | |
}, | |
), | |
); | |
} | |
} | |
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( | |
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( | |
label: 'primaryFixed', | |
color: colorScheme.primaryFixed, | |
onColor: colorScheme.onPrimaryFixed, | |
), | |
ColorChip( | |
label: 'onPrimaryFixed', | |
color: colorScheme.onPrimaryFixed, | |
onColor: colorScheme.primaryFixed, | |
), | |
ColorChip( | |
label: 'primaryFixedDim', | |
color: colorScheme.primaryFixedDim, | |
onColor: colorScheme.onPrimaryFixedVariant, | |
), | |
ColorChip( | |
label: 'onPrimaryFixedVariant', | |
color: colorScheme.onPrimaryFixedVariant, | |
onColor: colorScheme.primaryFixedDim, | |
), | |
], | |
), | |
divider, | |
ColorGroup( | |
children: [ | |
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( | |
label: 'secondaryFixed', | |
color: colorScheme.secondaryFixed, | |
onColor: colorScheme.onSecondaryFixed, | |
), | |
ColorChip( | |
label: 'onSecondaryFixed', | |
color: colorScheme.onSecondaryFixed, | |
onColor: colorScheme.secondaryFixed, | |
), | |
ColorChip( | |
label: 'secondaryFixedDim', | |
color: colorScheme.secondaryFixedDim, | |
onColor: colorScheme.onSecondaryFixedVariant, | |
), | |
ColorChip( | |
label: 'onSecondaryFixedVariant', | |
color: colorScheme.onSecondaryFixedVariant, | |
onColor: colorScheme.secondaryFixedDim, | |
), | |
], | |
), | |
divider, | |
ColorGroup( | |
children: [ | |
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( | |
label: 'tertiaryFixed', | |
color: colorScheme.tertiaryFixed, | |
onColor: colorScheme.onTertiaryFixed, | |
), | |
ColorChip( | |
label: 'onTertiaryFixed', | |
color: colorScheme.onTertiaryFixed, | |
onColor: colorScheme.tertiaryFixed, | |
), | |
ColorChip( | |
label: 'tertiaryFixedDim', | |
color: colorScheme.tertiaryFixedDim, | |
onColor: colorScheme.onTertiaryFixedVariant, | |
), | |
ColorChip( | |
label: 'onTertiaryFixedVariant', | |
color: colorScheme.onTertiaryFixedVariant, | |
onColor: colorScheme.tertiaryFixedDim, | |
), | |
], | |
), | |
divider, | |
ColorGroup( | |
children: [ | |
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( | |
label: 'surfaceDim', | |
color: colorScheme.surfaceDim, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surface', | |
color: colorScheme.surface, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surfaceBright', | |
color: colorScheme.surfaceBright, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surfaceContainerLowest', | |
color: colorScheme.surfaceContainerLowest, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surfaceContainerLow', | |
color: colorScheme.surfaceContainerLow, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surfaceContainer', | |
color: colorScheme.surfaceContainer, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surfaceContainerHigh', | |
color: colorScheme.surfaceContainerHigh, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'surfaceContainerHighest', | |
color: colorScheme.surfaceContainerHighest, | |
onColor: colorScheme.onSurface, | |
), | |
ColorChip( | |
label: 'onSurface', | |
color: colorScheme.onSurface, | |
onColor: colorScheme.surface, | |
), | |
ColorChip( | |
label: 'onSurfaceVariant', | |
color: colorScheme.onSurfaceVariant, | |
onColor: colorScheme.surfaceContainerHighest, | |
), | |
], | |
), | |
divider, | |
ColorGroup( | |
children: [ | |
ColorChip( | |
label: 'outline', | |
color: colorScheme.outline, | |
// ignore: avoid_redundant_argument_values | |
onColor: null, | |
), | |
ColorChip( | |
label: 'shadow', | |
color: colorScheme.shadow, | |
// ignore: avoid_redundant_argument_values | |
onColor: null, | |
), | |
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<ColorChip> 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) => switch (ThemeData.estimateBrightnessForColor(color)) { | |
Brightness.dark => Colors.white, | |
Brightness.light => 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(child: Text(label, style: TextStyle(color: labelColor))), | |
], | |
), | |
), | |
); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment