Themes
Forui themes allows you to customize the look and feel of your Flutter application. Our theming solution is designed to help you get started quickly while offering powerful and flexible customization options.
Predefined Themes
Forui does not manage the theme brightness (light or dark) automatically.
You need to specify the theme explicitly in FTheme(...)
.
@override
Widget build(BuildContext context) => FTheme(
data: FThemes.zinc.light, // or FThemes.zinc.dark
child: const FScaffold(...),
);
Forui provides a set of predefined themes that you can use out of the box. The color schemes are heavily inspired by shadcn/ui themes.
Theme | Light Accessor | Dark Accessor |
---|---|---|
Zinc | FThemes.zinc.light | FThemes.zinc.dark |
Slate | FThemes.slate.light | FThemes.slate.dark |
Red | FThemes.red.light | FThemes.red.dark |
Rose | FThemes.rose.light | FThemes.rose.dark |
Orange | FThemes.orange.light | FThemes.orange.dark |
Green | FThemes.green.light | FThemes.green.dark |
Blue | FThemes.blue.light | FThemes.blue.dark |
Yellow | FThemes.yellow.light | FThemes.yellow.dark |
Violet | FThemes.violet.light | FThemes.violet.dark |
Usage
FTheme
uses inherited widgets to provide the
FThemeData
to all widgets in the subtree.
Forui provides an extension on BuildContext
which allows for direct access to FThemeData
through context.theme
.
@override
Widget build(BuildContext context) {
final theme = context.theme; // FThemeData
final colorScheme = context.theme.colorScheme; // FColorScheme
final typography = context.theme.typography; // FTypography
final style = context.theme.style; // FStyle
return const Placeholder();
}
A high level overview of the theme data structure is as follows:
FThemeData
containsFColorScheme
,FTypography
,FStyle
, and widget styles (eg.FCardStyle
).FColorScheme
contains the color scheme (eg.background
,foreground
, andprimary
).FTypography
contains thedefaultFontFamily
and variousTextStyle
s.FStyle
contains other miscellaneous styling options (eg.borderRadius
).
A more detailed explanation of each class can be found in the Class Diagram section.
Customize Themes
It’s recommended to use the predefined themes as a starting point and customize them with the
inherit(...)
constructor
and copyWith(...)
method to suit your needs.
@override
Widget build(BuildContext context) {
final theme = FThemeData.inherit(
colorScheme: FThemes.zinc.light.colorScheme.copyWith(
primary: const Color(0xFF0D47A1), // dark blue
primaryForeground: const Color(0xFFFFFFFF), // white
),
typography: FThemes.zinc.light.typography.copyWith(
defaultFontFamily: 'Roboto',
).scale(sizeScalar: 0.8),
style: FThemes.zinc.light.style.copyWith(
borderRadius: BorderRadius.zero,
),
);
return FTheme(
data: theme.copyWith(
cardStyle: theme.cardStyle.copyWith(
decoration: theme.cardStyle.decoration.copyWith(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
),
),
child: const FScaffold(...),
);
}
The example above uses FThemes.zinc.light
theme as a starting point and customizes the following:
- Update the
primary
andprimaryForeground
colors. - Change the default font family to
Roboto
and scale the font size by2
. - Remove the
borderRadius
through the style. - Although we removed the
borderRadius
for all widgets, override theborderRadius
to8
for theFCard()
widget.
It’s important to use the newly created theme
instead of FThemes.zinc.light
. This allows colorScheme
,
typography
, and style
to be inherited by widget-specific themes.
return FTheme(
data: theme.copyWith(
cardStyle: theme.cardStyle.copyWith(
decoration: theme.cardStyle.decoration.copyWith(
borderRadius: const BorderRadius.all(Radius.circular(8)),
),
),
),
child: const FScaffold(...),
);
While the example may seem overwhelming, most use cases will only require customizations to the FColorScheme
, FTypography
, and FStyle
class.
Sensible defaults for each Forui widget will be automatically inherited.
Class Diagram
Color Scheme
The FColorScheme
class contains the color scheme for the theme.
This usually consist of a set of colors with its corresponding foreground color (eg. primary
and primaryForeground
).
In most cases, the color will be used as the background color, and the foreground color will be used as the text/icon color.
@override
Widget build(BuildContext context) {
final colorScheme = context.theme.colorScheme;
final typography = context.theme.typography;
return ColoredBox(
color: colorScheme.primary,
child: Text(
'Hello World!',
style: typography.xs.copyWith(
color: colorScheme.primaryForeground
),
),
);
}
Hovered and Disabled Colors
Hovered and disabled colors are derived by adjusting the opacity. To derive these colors, use the FColorScheme.hover
and FColorScheme.disable
methods. The opacity can be adjusted with FColorScheme.enabledHoveredOpacity
and
FColorScheme.disabledOpacity
.
Typography
The FTypography
class contains the typography settings for the theme.
This includes the default font family and various TextStyle
s for different use cases.
The TextStyle
s stored in FTypography
are based on Tailwind CSS Font Size.
For instance, FTypography.sm
is closely related to text-sm
in Tailwind CSS.
It is recommended to use the copyWith(...)
to apply colors and other styles as the TextStyle
s stored only contain the fontSize
and height
properties.
@override
Widget build(BuildContext context) {
final colorScheme = context.theme.colorScheme;
final typography = context.theme.typography;
return Text(
'Hello World!',
style: typography.xs.copyWith(
color: colorScheme.primaryForeground,
fontWeight: FontWeight.bold,
fontStyle: FontStyle.italic,
),
);
}
Custom Font Family
To change the default font family, use the copyWith(...)
method to apply the new font family.
As some fonts may have different sizes, the scale(...)
method is provided to quickly scale all the font sizes.
@override
Widget build(BuildContext context) => FTheme(
data: FThemes.zinc.light.copyWith(
typography: FThemes.zinc.light.typography.copyWith(
defaultFontFamily: 'Roboto',
).scale(sizeScalar: 0.8),
),
child: const FScaffold(...),
);
Style
The FStyle
class contains other miscellaneous styling options for the theme.
@override
Widget build(BuildContext context) {
final colorScheme = context.theme.colorScheme;
final style = context.theme.style;
return DecoratedBox(
decoration: BoxDecoration(
border: Border.all(
color: colorScheme.border,
width: style.borderWidth,
),
borderRadius: style.borderRadius,
color: colorScheme.primary,
),
child: const Placeholder(),
);
}
Override Individual Widgets
In certain cases, you may want to override the theme for a specific widget.
This can be easily achieved by using the style(...)
parameter.
In the example below, styles are only overridden for this specific FCard
widget.
FCard
widgets that are used elsewhere will still inherit from theme data.
@override
Widget build(BuildContext context) {
final theme = context.theme;
return FCard(
title: 'Notification',
subtitle: 'You have 3 new messages',
style: theme.cardStyle.copyWith(
decoration: theme.cardStyle.decoration.copyWith(
borderRadius: BorderRadius.zero, // Remove border radius.
),
),
);
}