Refactor theme implementation to use ColorScheme.fromSeed for light and dark themes; update README with usage examples and add widgetbook build configuration.

This commit is contained in:
Emily Pauli 2025-10-25 22:58:10 +02:00
commit 2f84b1559f
6 changed files with 139 additions and 15 deletions

View file

@ -0,0 +1,17 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="widgetbook build" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="dart run build_runner build -d" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="false" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/widgetbook" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="false" />
<option name="INTERPRETER_PATH" value="&quot;C:/Program Files/PowerShell/7/pwsh.exe&quot;" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs />
<method v="2" />
</configuration>
</component>

View file

@ -33,6 +33,85 @@ flutter run
See `melos.yaml`, `analysis_options.yaml`, and the package-level READMEs. See `melos.yaml`, `analysis_options.yaml`, and the package-level READMEs.
## Using the M3E Theme
The design tokens and helpers live in the `m3e_design` package. Import it once in files where you access the theme:
```dart
import 'package:m3e_design/m3e_design.dart';
```
- Install the theme once at the app level (one-liner):
```dart
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ColorScheme.fromSeed(seedColor: Colors.teal).toM3EThemeData(),
home: const MyHomePage(),
);
}
}
```
- With dynamic color (Android 12+), setting both light and dark themes:
```dart
Widget buildDynamicApp() {
return DynamicColorBuilder(
builder: (lightDynamic, darkDynamic) {
final light = lightDynamic ?? ColorScheme.fromSeed(seedColor: Colors.teal);
final dark = darkDynamic ??
ColorScheme.fromSeed(seedColor: Colors.teal, brightness: Brightness.dark);
return MaterialApp(
theme: light.toM3EThemeData(),
darkTheme: dark.toM3EThemeData(),
home: const MyHomePage(),
);
},
);
}
```
- Access M3E tokens anywhere using the new ThemeData accessor:
```dart
final m3e = Theme.of(context).m3e; // ThemeData extension
// Examples
final br = m3e.shapes.round.sm; // BorderRadius
final pad = EdgeInsets.all(m3e.spacing.md); // Spacing scale
final bg = m3e.colors.surfaceContainerHigh; // Colors mapped to ColorScheme
final curve = m3e.motion.emphasized; // Motion/curves
final title = m3e.typography.base.titleLarge; // Typography
```
- Apply a rounded shape to a widget:
```dart
Widget roundedExample(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).colorScheme.surface,
borderRadius: Theme.of(context).m3e.shapes.round.sm,
),
);
}
```
- Optional context sugar if you prefer:
```dart
// Provided by BuildContext extension in m3e_design
final m3e = context.m3e;
```
Notes:
- In debug, accessing `Theme.of(context).m3e` asserts if the extension isn't installed, helping catch setup issues.
- In release, it safely falls back to sensible defaults derived from the active `ColorScheme`.
--- ---
@ -49,4 +128,4 @@ cd apps/gallery
flutter run -d chrome flutter run -d chrome
``` ```
_Last updated: 2025-10-23_ _Last updated: 2025-10-25_

View file

@ -31,20 +31,14 @@ class _GalleryAppState extends State<GalleryApp> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final baseLight = ThemeData( final light = ColorScheme.fromSeed(
useMaterial3: true, seedColor: Colors.purple, brightness: Brightness.light);
colorSchemeSeed: Colors.purple, final dark = ColorScheme.fromSeed(
brightness: Brightness.light, seedColor: Colors.purple, brightness: Brightness.dark);
);
final baseDark = ThemeData(
useMaterial3: true,
colorSchemeSeed: Colors.purple,
brightness: Brightness.dark,
);
return MaterialApp( return MaterialApp(
title: 'M3E Gallery', title: 'M3E Gallery',
theme: withM3ETheme(baseLight), theme: light.toM3EThemeData(),
darkTheme: withM3ETheme(baseDark), darkTheme: dark.toM3EThemeData(),
themeMode: _mode, themeMode: _mode,
home: GalleryHome( home: GalleryHome(
isDark: _mode == ThemeMode.dark, isDark: _mode == ThemeMode.dark,
@ -56,7 +50,8 @@ class _GalleryAppState extends State<GalleryApp> {
} }
class GalleryHome extends StatefulWidget { class GalleryHome extends StatefulWidget {
const GalleryHome({super.key, required this.isDark, required this.onToggleBrightness}); const GalleryHome(
{super.key, required this.isDark, required this.onToggleBrightness});
final bool isDark; final bool isDark;
final VoidCallback onToggleBrightness; final VoidCallback onToggleBrightness;
@ -77,7 +72,8 @@ class _GalleryHomeState extends State<GalleryHome> {
titleText: 'M3E Gallery', titleText: 'M3E Gallery',
actions: [ actions: [
IconButton( IconButton(
tooltip: widget.isDark ? 'Switch to light mode' : 'Switch to dark mode', tooltip:
widget.isDark ? 'Switch to light mode' : 'Switch to dark mode',
onPressed: widget.onToggleBrightness, onPressed: widget.onToggleBrightness,
icon: Icon(widget.isDark ? Icons.dark_mode : Icons.light_mode), icon: Icon(widget.isDark ? Icons.dark_mode : Icons.light_mode),
), ),

View file

@ -11,6 +11,7 @@ dependencies:
m3e_collection: m3e_collection:
path: ../../packages/m3e_collection path: ../../packages/m3e_collection
material_color_utilities: ^0.11.0 material_color_utilities: ^0.11.0
widgetbook: ^3.18.0
flutter: flutter:
uses-material-design: true uses-material-design: true

View file

@ -0,0 +1,30 @@
# melos_managed_dependency_overrides: app_bar_m3e,button_group_m3e,button_m3e,fab_m3e,icon_button_m3e,loading_indicator_m3e,m3e_collection,m3e_design,navigation_bar_m3e,navigation_rail_m3e,progress_indicator_m3e,slider_m3e,split_button_m3e,toolbar_m3e
dependency_overrides:
app_bar_m3e:
path: ..\\..\\packages\\app_bar_m3e
button_group_m3e:
path: ..\\..\\packages\\button_group_m3e
button_m3e:
path: ..\\..\\packages\\button_m3e
fab_m3e:
path: ..\\..\\packages\\fab_m3e
icon_button_m3e:
path: ..\\..\\packages\\icon_button_m3e
loading_indicator_m3e:
path: ..\\..\\packages\\loading_indicator_m3e
m3e_collection:
path: ..\\..\\packages\\m3e_collection
m3e_design:
path: ..\\..\\packages\\m3e_design
navigation_bar_m3e:
path: ..\\..\\packages\\navigation_bar_m3e
navigation_rail_m3e:
path: ..\\..\\packages\\navigation_rail_m3e
progress_indicator_m3e:
path: ..\\..\\packages\\progress_indicator_m3e
slider_m3e:
path: ..\\..\\packages\\slider_m3e
split_button_m3e:
path: ..\\..\\packages\\split_button_m3e
toolbar_m3e:
path: ..\\..\\packages\\toolbar_m3e

View file

@ -2,6 +2,7 @@ name: m3e
packages: packages:
- "packages/*" - "packages/*"
- "apps/*" - "apps/*"
- "widgetbook/"
command: command:
bootstrap: bootstrap: