flex_color_scheme 8.0.0-dev.1 copy "flex_color_scheme: ^8.0.0-dev.1" to clipboard
flex_color_scheme: ^8.0.0-dev.1 copied to clipboard

A Flutter package to use and make beautiful Material design based themes.

example/lib/main.dart

import 'package:flex_color_scheme/flex_color_scheme.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:google_fonts/google_fonts.dart';

import 'shared/const/app.dart';
import 'shared/widgets/examples/responsive_scaffold.dart';
import 'shared/widgets/examples/show_color_scheme_colors.dart';
import 'shared/widgets/examples/show_sub_pages.dart';
import 'shared/widgets/examples/show_sub_theme_colors.dart';
import 'shared/widgets/examples/show_theme_data_colors.dart';
import 'shared/widgets/universal/responsive_center.dart';
import 'shared/widgets/universal/showcase_material.dart';
import 'shared/widgets/universal/theme_mode_switch.dart';

/// DEFAULT EXAMPLE - Hot Reload Playground
///
/// This example shows how you can define custom colors, use [FlexColorScheme]
/// to theme your app with them, or use a predefined theme.
///
/// It offers a playground you can use to experiment with all its
/// theming properties and optional opinionated sub-theming.
///
/// It also demonstrates how to use a [GoogleFonts] based font as the default
/// font for your app theme, and how to customize the used [TextTheme].
///
/// To learn more about how to use [FlexColorScheme] and all its features,
/// please go through the five tutorial examples in the readme documentation.
void main() => runApp(const DemoApp());

// This default example contains a long list of const and final property values
// that are just passed in to the corresponding properties in
// FlexThemeData.light() and FlexThemeData.dark() convenience extension on
// ThemeData to FlexColorScheme.light().toTheme and
// FlexColorScheme.dark().toTheme.
//
// The purpose is to provide an easy to use in-code based playground that
// you can experiment with and use as a quick starter template to start using
// FlexColorScheme to make beautiful Flutter themes for your applications.
// It is also a code and comment based quick guide for developers that
// don't read long documentation.
//
// This setup is convenient since you can edit the values for both the light
// and dark theme mode via shared property values and observe the changes
// in the built app using hot reload.
//
// In a real app you might tuck away your color definitions and FlexColorScheme
// settings in a static class with const and final values, and static functions
// as required. The other tutorials show examples of this as well.
//
// To learn more about using FlexColorScheme, it is recommended to go through
// the step-by-step tutorial that uses examples 1 to 5 that explain and
// demonstrate the features with increasing complexity. Example 5 represents
// the full bonanza where pretty much everything can be changed dynamically
// while running the app. It doubles as the web online Themes Playground app
// that can also generate FlexColorScheme setup code for the theme you have
// configured. Using it and studying the setup code it generates is another
// quick way to learn about the different APIs and features it provides.

// For our custom color scheme we define primary and secondary colors,
// but no container or other colors.
final FlexSchemeColor _schemeLight = FlexSchemeColor.from(
  primary: const Color(0xFF00296B),
  // If you do not want to define secondary, primaryVariant and
  // secondaryVariant, error and appBar colors you do not have to,
  // they will get defined automatically when using the FlexSchemeColor.from()
  // factory. When using FlexSchemeColor.from() you only have to define the
  // primary color, anything not defined will get derived automatically from
  // the primary color and you get a theme that is based just on shades of
  // the provided primary color.
  //
  // With the default constructor FlexSchemeColor() you have to define
  // all 4 main color properties required for a complete color scheme. If you
  // do define them all, then prefer using it, since it can be const.
  //
  // Here we define a secondary color, but if you don't it will get a
  // default shade based on the primary color. When you do define a secondary
  // color, but not a secondaryVariant color, the secondary container will get
  // derived from the secondary color, instead of from the primary color.
  secondary: const Color(0xFFFF7B00),
  // New in version 5.
  // Specifying a brightness value computes missing colors based on given colors
  // using a strategy that is compatible with Material3 based ColorScheme
  // introduced in Flutter SDK in version 2.10.0. If you don't specify a
  // brightness value, you still get all needed colors computed, but the used
  // strategy for missing colors is more inline with the usage as it was
  // in Material 2 design. The new M3 colors primary.container and
  // secondary.container receives the color values that would have been given to
  // deprecated colors primary.variant and secondary.variant, the color values
  // are OK for using them as variant colors in an M2 based app, but the values
  // are not ideal for an M3 design. For better M3 results, prefer giving also
  // the brightness you are targeting.
  brightness: Brightness.light,
);

// These are custom defined matching dark mode colors. Further below we show
// how to compute them based on the light color scheme. You can swap them in the
// code example further below and compare the result of these manually defined
// matching dark mode colors, to the ones computed via the "lazy" designer
// matching dark colors.
final FlexSchemeColor _schemeDark = FlexSchemeColor.from(
  primary: const Color(0xFF6B8BC3),
  secondary: const Color(0xffff7155),
  brightness: Brightness.dark,
);

// To use a pre-defined color scheme, don't assign any FlexSchemeColor to
// `colors` instead, just pick a FlexScheme enum based value and assign it
// to the `scheme`. Try eg the new "flutterDash" color scheme, based on colors
// found in the 4k wallpaper Google shared before the Flutter 2.10.0 release.
const FlexScheme _scheme = FlexScheme.flutterDash;

// To make it easy to toggle between using the above custom colors, or the
// selected predefined scheme in this example, set _useScheme to true to use the
// selected predefined scheme above, set it to false to use the custom colors
// defined earlier above.
const bool _useScheme = true;

// A quick setting for the themed app bar elevation, it defaults to 0.
// A very low, like 0.5 is pretty nice too, since it gives an underline effect
// visible with e.g. white or light colored app bars.
const double _appBarElevation = 0.5;

// There is setting to put an opacity value on the app bar. If used, we can see
// content scroll behind it, if we also extend the Scaffold behind the AppBar.
const double _appBarOpacity = 0.94;

// If you set _computeDarkTheme below to true, the dark scheme will be computed
// both for the selected scheme and the custom colors, from the light scheme.
// There is a bit of logic hoops below to make it happen via this bool toggle.
//
// Going "toDark()" on your light FlexSchemeColor definition is just a quick
// way you can make a dark scheme from a light color scheme definition, without
// figuring out usable color values yourself. This is useful during development,
// when you test custom colors. For production and final colors you probably
// want to fine tune your custom dark color scheme colors and use const values.
const bool _computeDarkTheme = false;

// When you use _computeDarkTheme, use this de-saturation % level to calculate
// the dark scheme from the light scheme colors. The default is 35%, but values
// from 20% might work on less saturated light scheme colors. For more
// deep and colorful starting values, you can try 40%. Trivia: The default
// red dark error color in the Material design guide, is computed from the light
// theme error color value, by using 40% with the same algorithm used here.
const int _toDarkLevel = 30;

// To swap primary and secondary colors, set to true. With some color schemes
// interesting and even useful inverted primary-secondary themes can be obtained
// by only swapping the colors on your dark scheme. Some schemes where even
// designed with this usage in mind, but not all look so well when using it.
const bool _swapColors = false;

// The `usedColors` is a convenience property that allows you to vary which
// colors to use of the primary, secondary and variant colors included in
// `colors` in `FlexSchemeColor`, or the `FlexSchemeColor` the enum based
// selection specifies. The integer number corresponds to using:
//
// * 1 = Only the primary color
// * 2 = Primary & Secondary colors
// * 3 = Primary + container & Secondary colors
// * 4 = Primary + container & Secondary + container
// * 5 = Primary + container & Secondary + container & tertiary colors
// * 6 = Primary + container & Secondary + container & tertiary + container
// * 7 = PST, Primary, Secondary and Tertiary, containers computed.
//
// This can be a quick way to try what you theme looks like when using less
// source colors and just different shades of the same color, that are still
// correctly tuned for their ColorScheme color values.
//
// The values default to 6, so that any color values that are defined are always
// used as defined and given.
const int _usedColors = 6;

// New in version 5: Key color seed based theming.
//
// If you want to use Material 3 based seed generated color schemes, using
// the current FlexColorScheme's colors as input to the seed generation. You
// can do so by passing in just a default `FlexKeyColors()` object to the
// `keyColors` property in FlexColorScheme.light and .dark factories.
//
// FlexKeyColors can be also configured, if its `useKeyColors` is false it is
// no being used, likewise it is not if the property `keyColors` is null.
//
// The default constructor `FlexKeyColors()` has the properties `useKeyColors`,
// `useSecondary` and `useTertiary` defaulting to true. This means the primary,
// secondary and tertiary colors from your active FlexColorScheme's colors will
// all be used as key colors to generate the theme's ColorScheme.
//
// The primary color is always using useKeyColors is true, but using secondary
// and tertiary colors to generate the ColorScheme are optional.
// They are on by default in the default constructor, to omit them set any
// of them to false.
//
// Flutter SDK `ColorScheme.fromSeed` only accepts a/ single color,
// the main/primary color as a seed color for the Material 3 ColorScheme
// it generates from a seed color. If you set both `useSecondary`
// and `useTertiary` to false, the result is the same as if you would have
// provided the current primary color value from the active FlexColorScheme
// to `ColorScheme.fromSeed` to generate the theme used `ColorScheme`.
// When you also use secondary and tertiary colors as input to generate the
// ColorScheme, their color values are based on them, instead of being sourced
// in fixed manner from the single primary color. This makes the generated
// ColorScheme follow the colors in your specified keys to a larger degree
// than Flutter SDK `ColorScheme.fromSeed` does.
//
// When you use seeded ColorSchemes, the key color used as seed color as primary
// color, secondary and tertiary usually do not end up in the resulting
// ColorScheme. This can be problematic when your spec calls for a specific
// specific e.g. brand color for certain color properties.
//
// With FlexColorscheme you can, for e.g. branding or other purposes, decide to
// keep one or more of the defined color values in your FlexColorScheme at its
// defined color value, despite otherwise using seeded color values to produce
// the resulting `ColorScheme`from them. There is a `keep` toggle in
// `FlexKeyColors` for all the six main colors in a `ColorScheme`, you can
// set any of them to true, to keep the color in question it has as input
// in your FlexColorScheme.
const FlexKeyColors _keyColors = FlexKeyColors(
  useKeyColors: false, // <-- set to true enable M3 seeded ColorScheme.
  useSecondary: true,
  useTertiary: true,
  keepPrimary: true, // <-- Keep defined value, do not use the seeded result.
  keepPrimaryContainer: false,
  keepSecondary: false,
  keepSecondaryContainer: false,
  keepTertiary: false,
  keepTertiaryContainer: false,
);

// New in version 5: Custom configuration for seed color calculations.
//
// Not only does FlexColorScheme enable using more than one seed color, you
// can also completely customize the tone mapping and CAM16 chroma limits
// imposed on used seed generation algorithm.
//
// When using Material 3 design and key colors, it generates 6 different tonal
// palettes `TonalPalette` for the colors in a M3 ColorScheme:
//
// * Primary tonal palette
// * Secondary tonal palette
// * Tertiary tonal palette
// * Error tonal palette
// * Neutral tonal palette
// * Neutral variant tonal palette
//
// Each palette contains 13 colors starting from black and ending in white, with
// different "tones" in-between of the color used for the palette.
// ColorScheme.from generates all the palettes from a single input color, and
// a hard coded value for the error palettes. FlexColorScheme allows you to as
// seen also specify the input colors for secondary and tertiary tonal palette.
// The neutral palettes are also generated from the input primary color, but
// with very little chroma of it left it, a bit more in the variant palette.
// this is a bit like the surface alpha blend that FlexColorScheme has
// been using since its first version.
//
// The algorithm used by ColorScheme.from also lock chroma for secondary and
// tertiary to a given value, and primary is min 48, after tha it uses
// chroma from the provided color. When tonal palettes have been created, it
// uses fixed tones (indexes) from relevant tonal palette and assigns them
// to given color properties in the ColorScheme. It is also worth noticing
// to you should use the same key color for both dark and light theme mode.
// the algorithm uses the same tonal palette for light and dark modes, but
// different tones from same palette.
//
// FlexColorScheme opens up this algorithm and logic and enables you to
// modify the color seed logic and behavior. The used algorithm is really
// fascinating, and the M3 usage of it is fine too. But maybe you want to it
// produce colors that are even more earthy and softer than M3, that is pretty
// soft already. Maybe your want more vivid tones, more in classic M2 style, or
// perhaps you need to seed schemes with much higher contrast for accessibility
// reasons. With FlexColorScheme you can. You do this by making a custom
// FlexTones data class to configure how the seeding engine maps palette colors
// the ColorScheme and how it uses chroma values in the key colors.
//
// The `FlexTones` has a `FlexTones.light` and `FlexTones.dark` factory, that
// are used for respective theme mode when using key colors in FlexColorScheme
// by default.
//
// The `FlexTones.light` factory by default provides the same chroma limits and
// tone mappings as used by:
// `ColorScheme.fromSeed(seedColor: color, brightness: Brightness.light)`
//
// Likewise the `FlexTones.dark` corresponds to same chroma limits and tone
// mappings as used by:
// `ColorScheme.fromSeed(seedColor: color, brightness: Brightness.dark)`.
//
// However, with the factories you can customize which tone each ColorScheme
// color properties uses as its color from its corresponding tonal palette.
// You can also change if primary, secondary and tertiary colors use the
// chroma in their key color value, if it should have a at least a given
// minimum chroma value, and after that use the key color's chroma value,
// or if it should be locked to a given chroma value.
//
// There is also a static that returns a default FlexTones.light and
// FlexTones.dark, when you pass it a brightness, called FlexTones.material,
// to indicate that it is using the default Material 3 specification.
//
// There are few more pre-made static configurations, for example:
//
// * FlexTones.soft
// * FlexTones.vivid
// * FlexTones.highContrast
// * FlexTones.chroma
//
// You can swap in them in below to try slightly different styles on generated
// seeded ColorScheme. The `FlexTones.chroma` for example, keeps the chroma as
// is in key colors for secondary and tertiary, and will thus produce a seeded
// ColorScheme that is closer to the provided key/seed colors, than the Flutter
// SDK M3 spec version does.
final FlexTones _flexTonesLight = FlexTones.material(Brightness.light);
final FlexTones _flexTonesDark = FlexTones.material(Brightness.dark);

// Use a GoogleFonts font as default font for your theme.
final String? _fontFamily = GoogleFonts.notoSans().fontFamily;

// Define a custom text theme for the app. Here we have decided that
// display fonts are too big to be useful for us, so we make them a bit smaller
// and that labelSmall is a bit too small and has weird letter spacing, so we
// make it bigger and change its letter spacing.
const TextTheme _textTheme = TextTheme(
  displayMedium: TextStyle(fontSize: 41),
  displaySmall: TextStyle(fontSize: 36),
  labelSmall: TextStyle(fontSize: 11, letterSpacing: 0.5),
);

// The `surfaceMode` takes `FlexSurfaceMode` that is used to select the used
// strategy for blending primary color into different surface colors.
const FlexSurfaceMode _surfaceMode = FlexSurfaceMode.highBackgroundLowScaffold;

// The alpha blend level strength can be defined separately from the
// SurfaceMode strategy, and has 40 alpha blend level strengths.
const int _blendLevel = 8;

// The `useSubThemes` sets weather you want to opt-in or not on additional
// opinionated sub-theming. By default FlexColorScheme as before does very
// little styling on widgets, other than a few important adjustments, described
// in detail in the readme. By using the sub-theme opt-in, it now also offers
// easy to use additional out-of the box opinionated styling of SDK UI Widgets.
// One key feature is the rounded corners on Widgets that support it.
const bool _useSubThemes = true;

// The opt-in opinionated sub-theming offers easy to use consistent corner
// radius rounding setting on all sub-themes and a ToggleButtons design that
// matches the normal buttons style and size.
// It comes with Material 3 like rounded defaults, but you can adjust
// its configuration via simple parameters in a passed in configuration class
// called FlexSubThemesData.
//
// Here are some some configuration examples:
const FlexSubThemesData _subThemesData = FlexSubThemesData(
  // Opt in for themed hover, focus, highlight and splash effects.
  // New buttons use primary themed effects by default, this setting makes
  // the general ThemeData hover, focus, highlight and splash match that.
  // True by default when opting in on sub themes, but you can turn it off.
  interactionEffects: true,

  // When it is null = undefined, the sub themes will use their default style
  // behavior that aims to follow new Material 3 (M3) standard for all widget
  // corner radius. Current Flutter SDK corner radius is 4, as defined by
  // the Material 2 design guide. M3 uses much higher corner radius, and it
  // varies by widget type.
  //
  // When you set [defaultRadius] to a value, it will override these defaults
  // with this global default. You can still set and lock each individual
  // border radius back for these widget sub themes to some specific value, or
  // to its Material3 standard, which is mentioned in each theme as the used
  // default when its value is null.
  //
  // Set global corner radius. Default is null, resulting in M3 styles, but make
  // it whatever you like, even 0 for a hip to be square style.
  defaultRadius: null,
  // You can also override individual corner radius for each sub-theme to make
  // it different from the global `cornerRadius`. Here eg. the bottom sheet
  // radius is defined to always be 24:
  bottomSheetRadius: 24,

  // Use the Material3 text theme and typography. Defaults to null.
  // When null, the correct M2/M3 TextTheme and Typography for respective mode
  // is used. If true, M3 style is enforced, if false, M2 mode is enforced.
  useMaterial3Typography: true,

  // Select input decorator type, only SDK options outline and underline
  // supported no, but custom ones may be added later.
  inputDecoratorBorderType: FlexInputBorderType.outline,
  // For a primary color tinted background on the input decorator set to true.
  inputDecoratorIsFilled: true,
  // If you do not want any underline/outline on the input decorator when it is
  // not in focus, then set this to false.
  inputDecoratorUnfocusedHasBorder: false,
  // Select the ColorScheme color used for input decoration border.
  // Primary is default so no need to set that, used here as placeholder to
  // enable easy selection of other options.
  inputDecoratorSchemeColor: SchemeColor.primary,
  // Set some alpha channel opacity value input decorator.
  inputDecoratorBackgroundAlpha: 20,

  // Some FAB (Floating Action Button) settings.
  //
  // // If fabUseShape is false, no shape will be added to FAB theme, it will get
  // // whatever default shape the widget default behavior applies.
  // //
  // fabUseShape: false,
  // //
  // // Select the ColorScheme color used by FABs as their base/background color
  // // Secondary is default so no need to set that, used here as placeholder to
  // // enable easy selection of other options.
  // //
  // fabSchemeColor: SchemeColor.secondaryContainer,

  // Set some custom colors used by Chips.
  chipSchemeColor: SchemeColor.surfaceContainerHighest,
  chipSelectedSchemeColor: SchemeColor.tertiaryContainer,
  chipSecondarySelectedSchemeColor: SchemeColor.primaryContainer,
  chipBlendColors: false,

  // Elevations have easy override values as well.
  elevatedButtonElevation: 1,
  // Widgets that use outline borders can be easily adjusted via these
  // properties, they affect the outline input decorator, outlined button and
  // toggle buttons.
  thickBorderWidth: 1.5, // Default is 2.0.
  thinBorderWidth: 1, // Default is 1.0.

  // Select the ColorScheme color used for selected TabBar indicator.
  // Defaults to same color as selected tab if not defined.
  // tabBarIndicatorSchemeColor: SchemeColor.secondary,

  // Select the ColorScheme color used for selected bottom navigation bar item.
  // Primary is default so no need to set that, used here as placeholder to
  // enable easy selection of other options.
  bottomNavigationBarSelectedLabelSchemeColor: SchemeColor.primary,

  // Select the ColorScheme color used for bottom navigation bar background.
  // Background is default so no need to set that, provided here as placeholder
  // to enable easy selection of other options.
  bottomNavigationBarBackgroundSchemeColor: SchemeColor.surfaceContainer,

  // Below are some example quick override properties that you can use on the
  // M3 based NavigationBar. The section is double commented out, so it its
  // easy to uncomment to try them all.
  //
  // // SchemeColor based color for [NavigationBar]'s selected item icon.
  // // navigationBarSelectedIconSchemeColor: SchemeColor.tertiary,
  // // SchemeColor based color for [NavigationBar]'s selected item label.
  // navigationBarSelectedLabelSchemeColor: SchemeColor.tertiary,
  // // SchemeColor based color for [NavigationBar]'s unselected item icons.
  // navigationBarUnselectedIconSchemeColor: SchemeColor.onSurface,
  // // SchemeColor based color for [NavigationBar]'s unselected item icons.
  // navigationBarUnselectedLabelSchemeColor: SchemeColor.onSurface,
  // // SchemeColor based color for [NavigationBar]'s selected item highlight.
  // navigationBarIndicatorSchemeColor: SchemeColor.tertiaryContainer,
  // // If you use suitable M3 designed container color for the indicator, it
  // // does not need any opacity.
  // navigationBarIndicatorOpacity: 1,
  // // Select the ColorScheme color used for [NavigationBar]'s background.
  // navigationBarBackgroundSchemeColor: SchemeColor.surfaceContainerHigh,
  // // When set to true [NavigationBar] unselected icons use a more muted
  // // version of color defined by [navigationBarUnselectedIconSchemeColor].
  // navigationBarMutedUnselectedIcon: true,
  // // When set to true [NavigationBar] unselected labels use a more muted
  // // version of color defined by [navigationBarUnselectedLabelSchemeColor].
  // navigationBarMutedUnselectedLabel: true,
  // // Set size of labels.
  // navigationBarSelectedLabelSize: 12,
  // navigationBarUnselectedLabelSize: 10,
  // // Set the size of icons icons.
  // navigationBarSelectedIconSize: 26,
  // navigationBarUnselectedIconSize: 22,
);

// If true, the top part of the Android AppBar has no scrim, it then becomes
// one colored like on iOS.
const bool _transparentStatusBar = true;

// Usually the TabBar is used in an AppBar. This style themes it right for
// that, regardless of what FlexAppBarStyle you use for the `appBarStyle`.
// If you will use the TabBar on Scaffold or other background colors, then
// use the style FlexTabBarStyle.forBackground.
const FlexTabBarStyle _tabBarForAppBar = FlexTabBarStyle.forAppBar;

// If true, tooltip background brightness is same as background brightness.
// False by default, which is inverted background brightness compared to theme.
// Setting this to true is more Windows desktop like.
const bool _tooltipsMatchBackground = true;

// The visual density setting defaults to same as SDK default value,
// which is `VisualDensity.adaptivePlatformDensity`. You can define a fixed one
// or try `FlexColorScheme.comfortablePlatformDensity`.
// The `comfortablePlatformDensity` is an alternative adaptive density to the
// default `adaptivePlatformDensity`. It makes the density `comfortable` on
// desktops, instead of `compact` as the `adaptivePlatformDensity` does.
// This is useful on desktop with touch screens, since it keeps tap targets
// a bit larger but not as large as `standard` intended for phones and tablets.
final VisualDensity _visualDensity = FlexColorScheme.comfortablePlatformDensity;

// This is just standard `platform` property in `ThemeData`, handy to have as
// a direct property, you can use it to test how things changes on different
// platforms without using `copyWith` on the resulting theme data.
final TargetPlatform _platform = defaultTargetPlatform;

/// A theme Extension example with a single custom brand color property.
///
/// You can add as many colors and other theme properties as you need, and
/// you can add multiple different ThemeExtension sub classes as well.
class BrandTheme extends ThemeExtension<BrandTheme> {
  const BrandTheme({
    this.brandColor,
  });
  final Color? brandColor;

  // You must override the copyWith method.
  @override
  BrandTheme copyWith({
    Color? brandColor,
  }) =>
      BrandTheme(
        brandColor: brandColor ?? this.brandColor,
      );

  // You must override the lerp method.
  @override
  BrandTheme lerp(ThemeExtension<BrandTheme>? other, double t) {
    if (other is! BrandTheme) {
      return this;
    }
    return BrandTheme(
      brandColor: Color.lerp(brandColor, other.brandColor, t),
    );
  }
}

// Custom const theme with our brand color in light mode.
const BrandTheme lightBrandTheme = BrandTheme(
  brandColor: Color.fromARGB(255, 8, 79, 71),
);

// Custom const theme with our brand color in dark mode.
const BrandTheme darkBrandTheme = BrandTheme(
  brandColor: Color.fromARGB(255, 167, 227, 218),
);

class DemoApp extends StatefulWidget {
  const DemoApp({super.key});

  @override
  State<DemoApp> createState() => _DemoAppState();
}

class _DemoAppState extends State<DemoApp> {
  ThemeMode themeMode = ThemeMode.system;
  bool useMaterial3 = true;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Hot Reload Playground',
      // Define the light theme for the app, based on defined colors and
      // properties above.
      theme: FlexThemeData.light(
        // Use local state to toggle usage of Material 3.
        useMaterial3: useMaterial3,
        // Want to use a built in scheme? Don't assign any value to colors.
        // We just use the _useScheme bool toggle here from above, only for easy
        // switching via code params so you can try options handily.
        colors: _useScheme ? null : _schemeLight,
        scheme: _scheme,
        swapColors: _swapColors, // If true, swap primary and secondaries.
        usedColors: _usedColors,

        // For an optional white look set lightIsWhite to true.
        // This is the counterpart to darkIsTrueBlack mode in dark theme mode,
        // which is much more useful than this feature.
        lightIsWhite: false,

        // If you want to use an existing fully specified `ColorScheme` that
        // your designer or you yourself made, you can do so too. This is
        // useful e.g. if you want the use FlexColorScheme for its component
        // sub-theming with an existing `ColorScheme`. To use a`ColorScheme`
        // object as color sources for your `FlexColorscheme` just pass it to
        // the `colorScheme` property. The `surfaceMode` and `blendLevel` will
        // still adjust surface and background colors on surfaces in passed
        // `ColorScheme` if they are used. This can be demonstrated here with
        // the default Flutter M2 based light ColorScheme set, if uncommented
        // below.

        // colorScheme: const ColorScheme.light(),

        // If you provide a color value to a direct color property, the color
        // value will override anything specified via the other properties.
        // The order from lowest to highest color property priority, to
        // determine effective colors are:
        // 1. scheme 2. colors 3. colorScheme 4. individual color values.
        // Normally you would make a custom scheme using the colors property,
        // but if you want to override just one or two colors in a pre-existing
        // scheme, this can be handy way to do it.
        // Uncomment a color property below on the light theme to try it:

        // primary: FlexColor.indigo.light.primary,
        // primaryContainer: FlexColor.greenLightPrimaryContainer,
        // secondary: FlexColor.indigo.light.secondary,
        // secondaryContainer: FlexColor.indigo.light.secondaryContainer,
        // surface: FlexColor.lightSurface,
        // background: FlexColor.lightBackground,
        // error: FlexColor.materialLightErrorHc,
        // scaffoldBackground: FlexColor.lightScaffoldBackground,
        // dialogBackground: FlexColor.lightSurface,
        // appBarBackground: FlexColor.barossaLightPrimary,

        // The default style of AppBar in Flutter SDK light mode uses scheme
        // primary color as its background color. The appBarStyle
        // FlexAppBarStyle.primary, results in this too, and is the default in
        // light mode. You can also choose other themed styles. Like
        // FlexAppBarStyle.background, that gets active color blend from used
        // surfaceMode or surfaceStyle, depending on which one is being used.
        // You may often want a different style on the app bar in dark and
        // light theme mode, therefore it was not set via a shared value
        // above in this template.
        appBarStyle: null, // Try different style, e.g.FlexAppBarStyle.primary,
        appBarElevation: _appBarElevation,
        appBarOpacity: _appBarOpacity,
        transparentStatusBar: _transparentStatusBar,
        tabBarStyle: _tabBarForAppBar,
        surfaceMode: _surfaceMode,
        blendLevel: _blendLevel,
        tooltipsMatchBackground: _tooltipsMatchBackground,
        // You can try another font too.
        // Prefer using fully defined TextThemes when using fonts, rather than
        // just setting the fontFamily name, even with GoogleFonts. For
        // quick tests this is fine if the same font style is good
        // as is for all the styles in the TextTheme, then just the fontFamily
        // works well too.
        fontFamily: _fontFamily,
        textTheme: _textTheme,
        primaryTextTheme: _textTheme,
        keyColors: _keyColors,
        tones: _flexTonesLight,
        subThemesData: _useSubThemes ? _subThemesData : null,
        visualDensity: _visualDensity,
        platform: _platform,
        // Add all our custom theme extensions, in this case we only have one.
        extensions: <ThemeExtension<dynamic>>{
          lightBrandTheme,
        },
      ),

      // Define the corresponding dark theme for the app.
      darkTheme: FlexThemeData.dark(
        // Use local state to toggle usage of Material 3.
        useMaterial3: useMaterial3,
        // If you want to base the dark scheme on your light colors,
        // you can also compute it from the light theme's FlexSchemeColors.
        // Here you can do so by setting _computeDarkTheme above to true.
        // The FlexSchemeColors class has a toDark() method that can convert
        // a color scheme designed for a light theme, to corresponding colors
        // suitable for a dark theme. For the built in themes there is no
        // need to do so, they all have hand tuned dark scheme colors.
        // Regardless, below we anyway demonstrate how you can do that too.
        //
        // Normally you would not do things like this complicated logic, you
        // would just use the colors or scheme property based on what you want.
        // This logic is just here so you can toggle the two booleans earlier
        // above to try the options without commenting any code.
        colors: (_useScheme && _computeDarkTheme)
            // If we use predefined schemes and want to compute a dark
            // theme from its light colors, we can grab the light scheme colors
            // for _schemes from the FlexColor.schemes map and use toDark(),
            // that takes a white blend saturation %, where 0 is same colors as
            // the input light scheme colors, and 100% makes it white.
            ? FlexColor.schemes[_scheme]!.light.toDark(_toDarkLevel)
            // If we use a predefined scheme, then pass, null so we get
            // selected _scheme via the scheme property.
            : _useScheme
                ? null
                // If we compute a scheme from our custom data, then use the
                // toDark() method on our custom light FlexSchemeColor data.
                // New in version 5:
                // For better dark mapping of the light color
                // based values, set parameter swapColor to true in toDark.
                : _computeDarkTheme
                    ? _schemeLight.toDark(_toDarkLevel, true)
                    // And finally, use the defined custom dark colors.
                    : _schemeDark,
        // To use a built-in scheme based on enum, don't assign colors above.
        scheme: _scheme,
        swapColors: _swapColors,
        usedColors: _usedColors,
        // For an optional ink black dark mode, set darkIsTrueBlack to true.
        darkIsTrueBlack: false,
        //
        // The SDK default style of the AppBar in dark mode uses a fixed dark
        // background color, defined via colorScheme.surface color. The
        // appBarStyle FlexAppBarStyle.material results in the same color value.
        // It is also the default if you do not define the style.
        // You can also use other themed styles. Here we use background, that
        // also gets active color blend from used SurfaceMode or SurfaceStyle.
        // You may often want a different style on the AppBar in dark and light
        // theme mode, therefore it was not set via a shared value value
        // above in this template.
        appBarStyle: null, // Try styles like: FlexAppBarStyle.background,
        appBarElevation: _appBarElevation,
        appBarOpacity: _appBarOpacity,
        transparentStatusBar: _transparentStatusBar,
        tabBarStyle: _tabBarForAppBar,
        surfaceMode: _surfaceMode,
        blendLevel: _blendLevel,
        tooltipsMatchBackground: _tooltipsMatchBackground,
        fontFamily: _fontFamily,
        textTheme: _textTheme,
        primaryTextTheme: _textTheme,
        keyColors: _keyColors,
        tones: _flexTonesDark,
        subThemesData: _useSubThemes ? _subThemesData : null,
        visualDensity: _visualDensity,
        platform: _platform,
        // Add all our custom theme extensions, in this case we only have one.
        extensions: <ThemeExtension<dynamic>>{
          darkBrandTheme,
        },
      ),
      themeMode: themeMode,
      home: HomePage(
        themeMode: themeMode,
        onThemeModeChanged: (ThemeMode mode) {
          setState(() {
            themeMode = mode;
          });
        },
        useMaterial3: useMaterial3,
        onMaterial3Changed: (bool value) {
          setState(() {
            useMaterial3 = value;
          });
        },
      ),
    );
  }
}

// -----------------------------------------------------------------------------
// Home Page for the Default Example - Hot Reload Playground
// -----------------------------------------------------------------------------
//
// The content of HomePage below is not so relevant for using FlexColorScheme
// based application theming. The critical parts are in the MaterialApp
// theme definitions above. The HomePage contains UI to visually show what
// the defined example looks like in an application and with common Widgets.
//
// The AnnotatedRegion using FlexColorScheme.themedSystemNavigationBar demo
// is however relevant if you want to change the style of the system
// navigation bar on Android.
//
// The contents below makes it easy to see what your theme looks
// like, and the purpose of this demo app is to show that. You can use this
// example app to experiment with your own themes in code and see the results
// via this HomePage.
class HomePage extends StatefulWidget {
  const HomePage({
    super.key,
    required this.themeMode,
    required this.onThemeModeChanged,
    required this.useMaterial3,
    required this.onMaterial3Changed,
  });

  final ThemeMode themeMode;
  final ValueChanged<ThemeMode> onThemeModeChanged;
  final bool useMaterial3;
  final ValueChanged<bool> onMaterial3Changed;

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  late final ScrollController scrollController;
  // Enabled state of each menuItem.
  late List<bool> menuItemsEnabled;
  // Active state of each menuItem.
  late List<ResponsiveMenuItemIconState> menuItemsIconState;

  @override
  void initState() {
    super.initState();
    scrollController =
        ScrollController(keepScrollOffset: true, initialScrollOffset: 0);
    // Set enabled menu items.
    menuItemsEnabled =
        List<bool>.generate(App.menuItems.length, (int i) => false);
    menuItemsEnabled[0] = true;
    menuItemsEnabled[1] = true;
    // Set menu icons states to initial states, some are a loaded from
    // persisted values via the theme controller.
    menuItemsIconState = List<ResponsiveMenuItemIconState>.generate(
        App.menuItems.length, (int i) => ResponsiveMenuItemIconState.primary);
  }

  @override
  void didChangeDependencies() {
    super.didChangeDependencies();
    final ThemeData theme = Theme.of(context);
    final bool isLight = theme.brightness == Brightness.light;
    menuItemsIconState[0] = isLight
        ? ResponsiveMenuItemIconState.primary
        : ResponsiveMenuItemIconState.secondary;
    menuItemsIconState[1] = theme.useMaterial3
        ? ResponsiveMenuItemIconState.primary
        : ResponsiveMenuItemIconState.secondary;
  }

  @override
  void dispose() {
    scrollController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    final Size mediaSize = MediaQuery.sizeOf(context);
    final EdgeInsets mediaPadding = MediaQuery.paddingOf(context);
    final double margins = App.responsiveInsets(mediaSize.width);
    final double topPadding = mediaPadding.top + kToolbarHeight + margins;
    final double bottomPadding = mediaPadding.bottom + margins;
    final bool isPhone = mediaSize.width < App.phoneWidthBreakpoint ||
        mediaSize.height < App.phoneHeightBreakpoint;
    final ThemeData theme = Theme.of(context);
    final TextTheme textTheme = theme.textTheme;
    final TextStyle headlineMedium = textTheme.headlineSmall!;
    final bool isDark = theme.brightness == Brightness.dark;

    // Get our custom brand color from the BrandTheme extension, with a
    // fallback to primary color.
    final Color brandColor =
        theme.extension<BrandTheme>()!.brandColor ?? theme.colorScheme.primary;

    return AnnotatedRegion<SystemUiOverlayStyle>(
      // FlexColorScheme contains a static helper that can be use to theme
      // the system navigation bar using an AnnotatedRegion. Without this
      // wrapper the system navigation bar in Android will not change
      // theme color as we change themes for the page. This is normal Flutter
      // behavior. By using an annotated region with the helper function
      // FlexColorScheme.themedSystemNavigationBar, we can make the
      // navigation bar follow desired background color and theme mode.
      // This looks much better and as it should on Android devices.
      // It also supports system navbar with opacity or fully transparent
      // Android system navigation bar on Android SDK >= 29.
      value: FlexColorScheme.themedSystemNavigationBar(
        context,
        // On Android SDK >= 29, try changing this to transparent, then on the
        // sub page demo with a bottom navigation bar and some opacity set
        // further below in [opacity].
        // You then get one homogeneously slightly transparent area, shared
        // with the bottom navigation bar and system navigation bar.
        systemNavBarStyle: FlexSystemNavBarStyle.background,
        // You can use a top divider on the navigation bar, but it does
        // add an extra scrim, which becomes visible when using bars with
        // opacity or fully transparent.
        useDivider: false,
        // You can set opacity on the Android system navigation bar, this will
        // result in content being visible behind it if Scaffold uses
        // extendBody.
        opacity: 0.60,
      ),
      child: ResponsiveScaffold(
        title: Text(App.title(context)),
        menuTitle: const Text(App.packageName),
        menuLeadingTitle: Text(
          App.title(context),
          style:
              theme.textTheme.titleSmall!.copyWith(fontWeight: FontWeight.bold),
        ),
        menuLeadingSubtitle: const Text('Version ${App.versionMajor}'),
        menuLeadingAvatarLabel: 'FCS',
        menuItems: App.menuItems,
        menuItemsEnabled: menuItemsEnabled,
        menuItemsIconState: menuItemsIconState,
        railWidth: isPhone ? 52 : 66,
        breakpointShowFullMenu: App.desktopWidthBreakpoint,
        extendBodyBehindAppBar: true,
        extendBody: true,
        onSelect: (int index) {
          if (index == 0) {
            if (isDark) {
              widget.onThemeModeChanged(ThemeMode.light);
            } else {
              widget.onThemeModeChanged(ThemeMode.dark);
            }
          }
          if (index == 1) {
            if (widget.useMaterial3) {
              widget.onMaterial3Changed(false);
            } else {
              widget.onMaterial3Changed(true);
            }
          }
        },
        body: ResponsiveCenter(
          controller: scrollController,
          constraints: const BoxConstraints(maxWidth: App.maxBodyWidth),
          child: ListView(
            controller: scrollController,
            padding: EdgeInsets.fromLTRB(
                margins, topPadding, margins, bottomPadding),
            children: <Widget>[
              Text('Theme Extension Colored Header',
                  style: headlineMedium.copyWith(color: brandColor)),
              const Text(
                'This is FlexColorScheme developers Hot Reload '
                'Playground. It has a large number of property values '
                'configured and documented, that you can modify and '
                'hot reload the app to try different options and features.',
              ),
              const SizedBox(height: 8),
              ListTile(
                contentPadding: EdgeInsets.zero,
                title: const Text('Theme mode'),
                subtitle: Text('Theme '
                    '${widget.themeMode.toString().dotTail}'),
                trailing: ThemeModeSwitch(
                  themeMode: widget.themeMode,
                  onChanged: widget.onThemeModeChanged,
                ),
                onTap: () {
                  if (isDark) {
                    widget.onThemeModeChanged(ThemeMode.light);
                  } else {
                    widget.onThemeModeChanged(ThemeMode.dark);
                  }
                },
              ),
              const SizedBox(height: 8),
              SwitchListTile(
                contentPadding: EdgeInsets.zero,
                title: const Text('Use Material 3'),
                value: widget.useMaterial3,
                onChanged: (bool value) {
                  widget.onMaterial3Changed(value);
                },
              ),
              const SizedBox(height: 8),
              const ShowColorSchemeColors(),
              const SizedBox(height: 8),
              const ShowThemeDataColors(),
              const SizedBox(height: 8),
              const ShowSubThemeColors(),
              const SizedBox(height: 8),
              const ShowSubPages(),
              const Divider(),
              Text('Widget Showcase', style: headlineMedium),
              const SizedBox(height: 8),
              const ShowcaseMaterial(),
            ],
          ),
        ),
      ),
    );
  }
}