Guides

Customizing Themes

Generate and customize themes and widget styles using the CLI.

This guide uses FAccordionStyle as an example, but the same principles apply to all Forui widgets. It relies on the CLI.

Generate main.dart

Navigate to your project directory.

Generate a main.dart:

dart run forui init

This generates a main.dart file where you will add your generated theme:

1
2import 'package:flutter/material.dart';
3
4import 'package:forui/forui.dart';
5
6import 'theme.dart';
7
8void main() {
9 runApp(const Application());
10}
11
12class Application extends StatelessWidget {
13 const Application({super.key});
14
15 @override
16 Widget build(BuildContext context) {
17 // Assign the generated theme to `theme`.
18 final theme = neutralLight;
19
20 return MaterialApp(
21 localizationsDelegates: FLocalizations.localizationsDelegates,
22 supportedLocales: FLocalizations.supportedLocales,
23 builder: (_, child) => FTheme(
24 data: theme,
25 child: FToaster(child: FTooltipGroup(child: child!)),
26 ),
27 theme: theme.toApproximateMaterialTheme(),
28 home: const FScaffold(
29 // TODO: replace with your widget.
30 child: Placeholder(),
31 ),
32 );
33 }
34}
35

Generate a Style

Tip: Run dart run forui style ls to see all available styles.

Generate a FAccordionStyle:

dart run forui style create accordion

This generates an accordion style file which you can add to your theme:

1FAccordionStyle accordionStyle({
2 required FColors colors,
3 required FTypography typography,
4 required FStyle style,
5}) => FAccordionStyle(
6 titleTextStyle: FVariants.from(
7 // This text style is applied when the accordion is NOT hovered OR pressed.
8 typography.md.copyWith(fontWeight: .w500, color: colors.foreground),
9 variants: {
10 // This text style is applied when the accordion is hovered OR pressed.
11 [.hovered, .pressed]: .delta(decoration: () => .underline),
12 // {@endhiglight}
13 },
14 ),
15 childTextStyle: typography.sm.copyWith(color: colors.foreground),
16 iconStyle: .all(IconThemeData(color: colors.mutedForeground, size: 20)),
17 focusedOutlineStyle: style.focusedOutlineStyle,
18 dividerStyle: FDividerStyle(color: colors.border, padding: .zero),
19 tappableStyle: style.tappableStyle.copyWith(
20 motion: const .delta(bounceTween: FTappableMotion.noBounceTween),
21 ),
22 titlePadding: const .symmetric(vertical: 15),
23 childPadding: const .only(bottom: 15),
24 motion: const FAccordionMotion(),
25);
26

To learn how to customize widget styles, see the customizing widget styles guide.

Generate a Theme

Tip: Run dart run forui theme ls to see all available themes.

Generate a theme based on neutral's light variant:

dart run forui theme create neutral-light

This generates a theme file which you can:

  • add to your generated main.dart.
  • add the generated styles to.
1
2import 'package:flutter/material.dart';
3
4import 'package:forui/forui.dart';
5
6import 'accordion_style.dart';
7
8
9FThemeData get neutralLight {
10 // Change this to false to generate a desktop variant of the theme.
11 const touch = true;
12
13 const colors = FColors(
14 brightness: .light,
15 systemOverlayStyle: .dark,
16 barrier: Color(0x33000000),
17 background: Color(0xFFFFFFFF),
18 foreground: Color(0xFF0A0A0A),
19 primary: Color(0xFF171717),
20 primaryForeground: Color(0xFFFAFAFA),
21 secondary: Color(0xFFF5F5F5),
22 secondaryForeground: Color(0xFF171717),
23 muted: Color(0xFFF5F5F5),
24 mutedForeground: Color(0xFF737373),
25 destructive: Color(0xFFE7000B),
26 destructiveForeground: Color(0xFFFAFAFA),
27 error: Color(0xFFE7000B),
28 errorForeground: Color(0xFFFAFAFA),
29 card: Color(0xFFFFFFFF),
30 border: Color(0xFFE5E5E5),
31 );
32
33 final typography = _typography(colors: colors, touch: touch);
34 final style = _style(colors: colors, typography: typography, touch: touch);
35
36 return FThemeData(
37 colors: colors,
38 typography: typography,
39 style: style,
40 touch: touch,
41 );
42}
43
44FTypography _typography({
45 required FColors colors,
46 required bool touch,
47 String defaultFontFamily = 'packages/forui/Inter',
48}) {
49 assert(
50 defaultFontFamily.isNotEmpty,
51 'defaultFontFamily ($defaultFontFamily) should not be empty.',
52 );
53 final color = colors.foreground;
54 final font = defaultFontFamily;
55 if (touch) {
56 return FTypography(
57 defaultFontFamily: defaultFontFamily,
58 xs3: TextStyle(color: color, fontFamily: font, fontSize: 10, height: 1),
59 xs2: TextStyle(color: color, fontFamily: font, fontSize: 12, height: 1),
60 xs: TextStyle(color: color, fontFamily: font, fontSize: 14, height: 1.25),
61 sm: TextStyle(color: color, fontFamily: font, fontSize: 16, height: 1.5),
62 md: TextStyle(color: color, fontFamily: font, fontSize: 18, height: 1.75),
63 lg: TextStyle(color: color, fontFamily: font, fontSize: 20, height: 1.75),
64 xl: TextStyle(color: color, fontFamily: font, fontSize: 22, height: 2),
65 xl2: TextStyle(
66 color: color,
67 fontFamily: font,
68 fontSize: 30,
69 height: 2.25,
70 ),
71 xl3: TextStyle(color: color, fontFamily: font, fontSize: 36, height: 2.5),
72 xl4: TextStyle(color: color, fontFamily: font, fontSize: 48, height: 1),
73 xl5: TextStyle(color: color, fontFamily: font, fontSize: 60, height: 1),
74 xl6: TextStyle(color: color, fontFamily: font, fontSize: 72, height: 1),
75 xl7: TextStyle(color: color, fontFamily: font, fontSize: 96, height: 1),
76 xl8: TextStyle(color: color, fontFamily: font, fontSize: 108, height: 1),
77 );
78 } else {
79 return FTypography(
80 defaultFontFamily: defaultFontFamily,
81 xs3: TextStyle(color: color, fontFamily: font, fontSize: 8, height: 1),
82 xs2: TextStyle(color: color, fontFamily: font, fontSize: 10, height: 1),
83 xs: TextStyle(color: color, fontFamily: font, fontSize: 12, height: 1),
84 sm: TextStyle(color: color, fontFamily: font, fontSize: 14, height: 1.25),
85 md: TextStyle(color: color, fontFamily: font, fontSize: 16, height: 1.5),
86 lg: TextStyle(color: color, fontFamily: font, fontSize: 18, height: 1.75),
87 xl: TextStyle(color: color, fontFamily: font, fontSize: 20, height: 1.75),
88 xl2: TextStyle(color: color, fontFamily: font, fontSize: 22, height: 2),
89 xl3: TextStyle(
90 color: color,
91 fontFamily: font,
92 fontSize: 30,
93 height: 2.25,
94 ),
95 xl4: TextStyle(color: color, fontFamily: font, fontSize: 36, height: 2.5),
96 xl5: TextStyle(color: color, fontFamily: font, fontSize: 48, height: 1),
97 xl6: TextStyle(color: color, fontFamily: font, fontSize: 60, height: 1),
98 xl7: TextStyle(color: color, fontFamily: font, fontSize: 72, height: 1),
99 xl8: TextStyle(color: color, fontFamily: font, fontSize: 96, height: 1),
100 );
101 }
102}
103
104FStyle _style({
105 required FColors colors,
106 required FTypography typography,
107 required bool touch,
108}) {
109 const borderRadius = FBorderRadius();
110 return FStyle(
111 formFieldStyle: .inherit(
112 colors: colors,
113 typography: typography,
114 touch: touch,
115 ),
116 focusedOutlineStyle: FFocusedOutlineStyle(
117 color: colors.primary,
118 borderRadius: borderRadius.md,
119 ),
120 iconStyle: IconThemeData(
121 color: colors.foreground,
122 size: typography.lg.fontSize,
123 ),
124 sizes: FSizes.inherit(touch: touch),
125 tappableStyle: FTappableStyle(),
126 borderRadius: const FBorderRadius(),
127 borderWidth: 1,
128 pagePadding: const .symmetric(vertical: 8, horizontal: 12),
129 shadow: const [
130 BoxShadow(color: Color(0x0d000000), offset: Offset(0, 1), blurRadius: 2),
131 ],
132 );
133}
134

On this page