Add initial configuration, tokens, and widgets for M3E components
- Introduced `.gitignore` and `.metadata` for apps and examples. - Added Flutter/Dart analysis configurations (`analysis_options.yaml`). - Implemented foundational tokens and themes for M3E (colors, shapes). - Created base implementations for `IconButtonM3E` and `SplitButtonM3E`. - Set up CI workflow (`ci.yaml`) to automate testing and analysis.
This commit is contained in:
parent
2c0f2df0b8
commit
62ecb86b76
184 changed files with 9872 additions and 0 deletions
4
packages/m3e_design/README.md
Normal file
4
packages/m3e_design/README.md
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# m3e_design
|
||||
|
||||
Design language core for Material 3 Expressive (Flutter).
|
||||
Provides ThemeExtension and token accessors for color, typography, shapes, spacing, motion.
|
||||
10
packages/m3e_design/lib/m3e_design.dart
Normal file
10
packages/m3e_design/lib/m3e_design.dart
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
library m3e_design;
|
||||
|
||||
export 'theme/m3e_theme.dart';
|
||||
export 'tokens/color_tokens.dart';
|
||||
export 'tokens/motion_tokens.dart';
|
||||
export 'tokens/shape_tokens.dart';
|
||||
export 'tokens/spacing_tokens.dart';
|
||||
export 'tokens/typography_tokens.dart';
|
||||
export 'utils/build_context_x.dart';
|
||||
export 'utils/semantics_x.dart';
|
||||
99
packages/m3e_design/lib/theme/m3e_theme.dart
Normal file
99
packages/m3e_design/lib/theme/m3e_theme.dart
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../tokens/color_tokens.dart';
|
||||
import '../tokens/motion_tokens.dart';
|
||||
import '../tokens/shape_tokens.dart';
|
||||
import '../tokens/spacing_tokens.dart';
|
||||
import '../tokens/typography_tokens.dart';
|
||||
|
||||
@immutable
|
||||
class M3ETheme extends ThemeExtension<M3ETheme> {
|
||||
final M3EColors colors;
|
||||
final M3ETypography typography;
|
||||
final M3EShapes shapes;
|
||||
final M3ESpacing spacing;
|
||||
final M3EMotion motion;
|
||||
|
||||
const M3ETheme({
|
||||
required this.colors,
|
||||
required this.typography,
|
||||
required this.shapes,
|
||||
required this.spacing,
|
||||
required this.motion,
|
||||
});
|
||||
|
||||
// Convenience proxy for commonly used text styles in packages (m3e.type.*)
|
||||
_M3ETypeProxy get type => _M3ETypeProxy(typography);
|
||||
|
||||
static M3ETheme defaults(ColorScheme scheme) => M3ETheme(
|
||||
colors: M3EColors.from(scheme),
|
||||
typography: M3ETypography.defaultFor(scheme.brightness),
|
||||
shapes: M3EShapes.expressive(),
|
||||
spacing: const M3ESpacing.regular(),
|
||||
motion: const M3EMotion.expressive(),
|
||||
);
|
||||
|
||||
@override
|
||||
M3ETheme copyWith({
|
||||
M3EColors? colors,
|
||||
M3ETypography? typography,
|
||||
M3EShapes? shapes,
|
||||
M3ESpacing? spacing,
|
||||
M3EMotion? motion,
|
||||
}) =>
|
||||
M3ETheme(
|
||||
colors: colors ?? this.colors,
|
||||
typography: typography ?? this.typography,
|
||||
shapes: shapes ?? this.shapes,
|
||||
spacing: spacing ?? this.spacing,
|
||||
motion: motion ?? this.motion,
|
||||
);
|
||||
|
||||
@override
|
||||
M3ETheme lerp(covariant M3ETheme? other, double t) {
|
||||
if (other == null) return this;
|
||||
return M3ETheme(
|
||||
colors: M3EColors.lerp(colors, other.colors, t),
|
||||
typography: M3ETypography.lerp(typography, other.typography, t),
|
||||
shapes: M3EShapes.lerp(shapes, other.shapes, t),
|
||||
spacing: M3ESpacing.lerp(spacing, other.spacing, t),
|
||||
motion: M3EMotion.lerp(motion, other.motion, t),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Inject (or replace) the M3ETheme extension on a ThemeData.
|
||||
ThemeData withM3ETheme(ThemeData base, {M3ETheme? override}) {
|
||||
// Use any existing M3ETheme, else the provided override, else defaults.
|
||||
final current = base.extension<M3ETheme>();
|
||||
final next = override ?? current ?? M3ETheme.defaults(base.colorScheme);
|
||||
|
||||
// Merge existing extensions (values) with our M3ETheme, replacing prior ones.
|
||||
final Iterable<ThemeExtension<dynamic>> existing = base.extensions.values;
|
||||
final List<ThemeExtension<dynamic>> merged = <ThemeExtension<dynamic>>[];
|
||||
for (final e in existing) {
|
||||
if (e is! M3ETheme) {
|
||||
merged.add(e);
|
||||
}
|
||||
}
|
||||
merged.add(next);
|
||||
|
||||
return base.copyWith(extensions: merged);
|
||||
}
|
||||
|
||||
// Internal proxy for typography shortcuts used by components.
|
||||
class _M3ETypeProxy {
|
||||
const _M3ETypeProxy(this._t);
|
||||
final M3ETypography _t;
|
||||
|
||||
TextStyle get _empty => const TextStyle();
|
||||
TextStyle get titleLarge => _t.base.titleLarge ?? _empty;
|
||||
TextStyle get titleSmall => _t.base.titleSmall ?? _empty;
|
||||
TextStyle get bodySmall => _t.base.bodySmall ?? _empty;
|
||||
TextStyle get labelLarge => _t.base.labelLarge ?? _empty;
|
||||
TextStyle get labelMedium => _t.base.labelMedium ?? _empty;
|
||||
TextStyle get labelSmall => _t.base.labelSmall ?? _empty;
|
||||
|
||||
TextStyle get headlineSmallEmphasized =>
|
||||
(_t.base.headlineSmall ?? _empty).merge(_t.emphasized.headline);
|
||||
}
|
||||
196
packages/m3e_design/lib/tokens/color_tokens.dart
Normal file
196
packages/m3e_design/lib/tokens/color_tokens.dart
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class M3EColors {
|
||||
final Color emphasis;
|
||||
final Color onEmphasis;
|
||||
final Color info;
|
||||
final Color success;
|
||||
final Color warning;
|
||||
final Color danger;
|
||||
|
||||
final Color surfaceStrong;
|
||||
final Color onSurfaceStrong;
|
||||
final Color outlineStrong;
|
||||
|
||||
// New: proxy common ColorScheme fields used across packages
|
||||
final Color primary;
|
||||
final Color onPrimary;
|
||||
final Color primaryContainer;
|
||||
final Color onPrimaryContainer;
|
||||
|
||||
final Color secondary;
|
||||
final Color onSecondary;
|
||||
final Color secondaryContainer;
|
||||
final Color onSecondaryContainer;
|
||||
|
||||
final Color tertiary;
|
||||
final Color onTertiary;
|
||||
final Color tertiaryContainer;
|
||||
final Color onTertiaryContainer;
|
||||
|
||||
final Color surface;
|
||||
final Color onSurface;
|
||||
final Color onSurfaceVariant;
|
||||
|
||||
final Color error;
|
||||
final Color onError;
|
||||
final Color errorContainer;
|
||||
final Color onErrorContainer;
|
||||
|
||||
final Color outline;
|
||||
final Color outlineVariant;
|
||||
|
||||
// New: container surface tokens not always present on older ColorScheme
|
||||
final Color surfaceContainerHigh;
|
||||
final Color surfaceContainerLowest;
|
||||
|
||||
const M3EColors({
|
||||
required this.emphasis,
|
||||
required this.onEmphasis,
|
||||
required this.info,
|
||||
required this.success,
|
||||
required this.warning,
|
||||
required this.danger,
|
||||
required this.surfaceStrong,
|
||||
required this.onSurfaceStrong,
|
||||
required this.outlineStrong,
|
||||
// New fields
|
||||
required this.primary,
|
||||
required this.onPrimary,
|
||||
required this.primaryContainer,
|
||||
required this.onPrimaryContainer,
|
||||
required this.secondary,
|
||||
required this.onSecondary,
|
||||
required this.secondaryContainer,
|
||||
required this.onSecondaryContainer,
|
||||
required this.tertiary,
|
||||
required this.onTertiary,
|
||||
required this.tertiaryContainer,
|
||||
required this.onTertiaryContainer,
|
||||
required this.surface,
|
||||
required this.onSurface,
|
||||
required this.onSurfaceVariant,
|
||||
required this.error,
|
||||
required this.onError,
|
||||
required this.errorContainer,
|
||||
required this.onErrorContainer,
|
||||
required this.outline,
|
||||
required this.outlineVariant,
|
||||
required this.surfaceContainerHigh,
|
||||
required this.surfaceContainerLowest,
|
||||
});
|
||||
|
||||
factory M3EColors.from(ColorScheme s) {
|
||||
// Compute container surface variants if not available on the ColorScheme version in use.
|
||||
// We prefer mild blends that work in both light/dark.
|
||||
Color computeSurfaceContainerHigh() =>
|
||||
Color.alphaBlend(s.primary.withValues(alpha: 0.12), s.surface);
|
||||
Color computeSurfaceContainerLowest() =>
|
||||
Color.alphaBlend(s.onSurface.withValues(alpha: 0.05), s.surface);
|
||||
|
||||
return M3EColors(
|
||||
emphasis: s.primary,
|
||||
onEmphasis: s.onPrimary,
|
||||
info: s.tertiary,
|
||||
success: Color.alphaBlend(
|
||||
Colors.green.shade400.withValues(alpha: 0.2), s.primaryContainer),
|
||||
warning: Color.alphaBlend(
|
||||
Colors.orange.shade400.withValues(alpha: 0.2), s.secondaryContainer),
|
||||
danger: Color.alphaBlend(
|
||||
Colors.red.shade400.withValues(alpha: 0.2), s.errorContainer),
|
||||
surfaceStrong:
|
||||
Color.alphaBlend(s.primary.withValues(alpha: 0.06), s.surface),
|
||||
onSurfaceStrong: s.onSurface,
|
||||
outlineStrong:
|
||||
Color.alphaBlend(s.primary.withValues(alpha: 0.40), s.outlineVariant),
|
||||
// New fields mapped from ColorScheme
|
||||
primary: s.primary,
|
||||
onPrimary: s.onPrimary,
|
||||
primaryContainer: s.primaryContainer,
|
||||
onPrimaryContainer: s.onPrimaryContainer,
|
||||
secondary: s.secondary,
|
||||
onSecondary: s.onSecondary,
|
||||
secondaryContainer: s.secondaryContainer,
|
||||
onSecondaryContainer: s.onSecondaryContainer,
|
||||
tertiary: s.tertiary,
|
||||
onTertiary: s.onTertiary,
|
||||
tertiaryContainer: s.tertiaryContainer,
|
||||
onTertiaryContainer: s.onTertiaryContainer,
|
||||
surface: s.surface,
|
||||
onSurface: s.onSurface,
|
||||
onSurfaceVariant: s.onSurfaceVariant,
|
||||
error: s.error,
|
||||
onError: s.onError,
|
||||
errorContainer: s.errorContainer,
|
||||
onErrorContainer: s.onErrorContainer,
|
||||
outline: s.outline,
|
||||
outlineVariant: s.outlineVariant,
|
||||
surfaceContainerHigh: (() {
|
||||
// If the ColorScheme already has a matching field, prefer that via dynamic access; otherwise compute.
|
||||
try {
|
||||
final dynamic dyn = s;
|
||||
final c = dyn.surfaceContainerHigh as Color?;
|
||||
return c ?? computeSurfaceContainerHigh();
|
||||
} catch (_) {
|
||||
return computeSurfaceContainerHigh();
|
||||
}
|
||||
})(),
|
||||
surfaceContainerLowest: (() {
|
||||
try {
|
||||
final dynamic dyn = s;
|
||||
final c = dyn.surfaceContainerLowest as Color?;
|
||||
return c ?? computeSurfaceContainerLowest();
|
||||
} catch (_) {
|
||||
return computeSurfaceContainerLowest();
|
||||
}
|
||||
})(),
|
||||
);
|
||||
}
|
||||
|
||||
static M3EColors lerp(M3EColors a, M3EColors b, double t) => M3EColors(
|
||||
emphasis: Color.lerp(a.emphasis, b.emphasis, t)!,
|
||||
onEmphasis: Color.lerp(a.onEmphasis, b.onEmphasis, t)!,
|
||||
info: Color.lerp(a.info, b.info, t)!,
|
||||
success: Color.lerp(a.success, b.success, t)!,
|
||||
warning: Color.lerp(a.warning, b.warning, t)!,
|
||||
danger: Color.lerp(a.danger, b.danger, t)!,
|
||||
surfaceStrong: Color.lerp(a.surfaceStrong, b.surfaceStrong, t)!,
|
||||
onSurfaceStrong: Color.lerp(a.onSurfaceStrong, b.onSurfaceStrong, t)!,
|
||||
outlineStrong: Color.lerp(a.outlineStrong, b.outlineStrong, t)!,
|
||||
// New fields
|
||||
primary: Color.lerp(a.primary, b.primary, t)!,
|
||||
onPrimary: Color.lerp(a.onPrimary, b.onPrimary, t)!,
|
||||
primaryContainer:
|
||||
Color.lerp(a.primaryContainer, b.primaryContainer, t)!,
|
||||
onPrimaryContainer:
|
||||
Color.lerp(a.onPrimaryContainer, b.onPrimaryContainer, t)!,
|
||||
secondary: Color.lerp(a.secondary, b.secondary, t)!,
|
||||
onSecondary: Color.lerp(a.onSecondary, b.onSecondary, t)!,
|
||||
secondaryContainer:
|
||||
Color.lerp(a.secondaryContainer, b.secondaryContainer, t)!,
|
||||
onSecondaryContainer:
|
||||
Color.lerp(a.onSecondaryContainer, b.onSecondaryContainer, t)!,
|
||||
tertiary: Color.lerp(a.tertiary, b.tertiary, t)!,
|
||||
onTertiary: Color.lerp(a.onTertiary, b.onTertiary, t)!,
|
||||
tertiaryContainer:
|
||||
Color.lerp(a.tertiaryContainer, b.tertiaryContainer, t)!,
|
||||
onTertiaryContainer:
|
||||
Color.lerp(a.onTertiaryContainer, b.onTertiaryContainer, t)!,
|
||||
surface: Color.lerp(a.surface, b.surface, t)!,
|
||||
onSurface: Color.lerp(a.onSurface, b.onSurface, t)!,
|
||||
onSurfaceVariant:
|
||||
Color.lerp(a.onSurfaceVariant, b.onSurfaceVariant, t)!,
|
||||
error: Color.lerp(a.error, b.error, t)!,
|
||||
onError: Color.lerp(a.onError, b.onError, t)!,
|
||||
errorContainer: Color.lerp(a.errorContainer, b.errorContainer, t)!,
|
||||
onErrorContainer:
|
||||
Color.lerp(a.onErrorContainer, b.onErrorContainer, t)!,
|
||||
outline: Color.lerp(a.outline, b.outline, t)!,
|
||||
outlineVariant: Color.lerp(a.outlineVariant, b.outlineVariant, t)!,
|
||||
surfaceContainerHigh:
|
||||
Color.lerp(a.surfaceContainerHigh, b.surfaceContainerHigh, t)!,
|
||||
surfaceContainerLowest:
|
||||
Color.lerp(a.surfaceContainerLowest, b.surfaceContainerLowest, t)!,
|
||||
);
|
||||
}
|
||||
43
packages/m3e_design/lib/tokens/motion_tokens.dart
Normal file
43
packages/m3e_design/lib/tokens/motion_tokens.dart
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class M3EMotion {
|
||||
final SpringDescription spatialFast;
|
||||
final SpringDescription spatialMedium;
|
||||
final SpringDescription spatialGentle;
|
||||
|
||||
final SpringDescription effectsFast;
|
||||
final SpringDescription effectsMedium;
|
||||
|
||||
final Duration fast;
|
||||
final Duration medium;
|
||||
final Duration slow;
|
||||
|
||||
const M3EMotion({
|
||||
required this.spatialFast,
|
||||
required this.spatialMedium,
|
||||
required this.spatialGentle,
|
||||
required this.effectsFast,
|
||||
required this.effectsMedium,
|
||||
required this.fast,
|
||||
required this.medium,
|
||||
required this.slow,
|
||||
});
|
||||
|
||||
const M3EMotion.expressive()
|
||||
: spatialFast = const SpringDescription(mass: 1, stiffness: 500, damping: 30),
|
||||
spatialMedium = const SpringDescription(mass: 1, stiffness: 350, damping: 28),
|
||||
spatialGentle = const SpringDescription(mass: 1, stiffness: 220, damping: 24),
|
||||
effectsFast = const SpringDescription(mass: 1, stiffness: 420, damping: 32),
|
||||
effectsMedium = const SpringDescription(mass: 1, stiffness: 280, damping: 28),
|
||||
fast = const Duration(milliseconds: 150),
|
||||
medium = const Duration(milliseconds: 250),
|
||||
slow = const Duration(milliseconds: 400);
|
||||
|
||||
static M3EMotion lerp(M3EMotion a, M3EMotion b, double t) => a;
|
||||
}
|
||||
|
||||
class SpringDescription {
|
||||
final double mass, stiffness, damping;
|
||||
const SpringDescription({required this.mass, required this.stiffness, required this.damping});
|
||||
}
|
||||
51
packages/m3e_design/lib/tokens/shape_tokens.dart
Normal file
51
packages/m3e_design/lib/tokens/shape_tokens.dart
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
enum M3EShapeVariant { round, square }
|
||||
|
||||
@immutable
|
||||
class M3EShapeSet {
|
||||
final BorderRadius xs;
|
||||
final BorderRadius sm;
|
||||
final BorderRadius md;
|
||||
final BorderRadius lg;
|
||||
final BorderRadius xl;
|
||||
const M3EShapeSet({required this.xs, required this.sm, required this.md, required this.lg, required this.xl});
|
||||
}
|
||||
|
||||
@immutable
|
||||
class M3EShapes {
|
||||
final M3EShapeSet round;
|
||||
final M3EShapeSet square;
|
||||
|
||||
const M3EShapes({required this.round, required this.square});
|
||||
|
||||
factory M3EShapes.expressive() => const M3EShapes(
|
||||
round: M3EShapeSet(
|
||||
xs: BorderRadius.all(Radius.circular(999)),
|
||||
sm: BorderRadius.all(Radius.circular(20)),
|
||||
md: BorderRadius.all(Radius.circular(28)),
|
||||
lg: BorderRadius.all(Radius.circular(44)),
|
||||
xl: BorderRadius.all(Radius.circular(64)),
|
||||
),
|
||||
square: M3EShapeSet(
|
||||
xs: BorderRadius.all(Radius.circular(6)),
|
||||
sm: BorderRadius.all(Radius.circular(8)),
|
||||
md: BorderRadius.all(Radius.circular(12)),
|
||||
lg: BorderRadius.all(Radius.circular(16)),
|
||||
xl: BorderRadius.all(Radius.circular(20)),
|
||||
),
|
||||
);
|
||||
|
||||
static M3EShapes lerp(M3EShapes a, M3EShapes b, double t) => M3EShapes(
|
||||
round: _lerpSet(a.round, b.round, t),
|
||||
square: _lerpSet(a.square, b.square, t),
|
||||
);
|
||||
|
||||
static M3EShapeSet _lerpSet(M3EShapeSet a, M3EShapeSet b, double t) => M3EShapeSet(
|
||||
xs: BorderRadius.lerp(a.xs, b.xs, t)!,
|
||||
sm: BorderRadius.lerp(a.sm, b.sm, t)!,
|
||||
md: BorderRadius.lerp(a.md, b.md, t)!,
|
||||
lg: BorderRadius.lerp(a.lg, b.lg, t)!,
|
||||
xl: BorderRadius.lerp(a.xl, b.xl, t)!,
|
||||
);
|
||||
}
|
||||
37
packages/m3e_design/lib/tokens/spacing_tokens.dart
Normal file
37
packages/m3e_design/lib/tokens/spacing_tokens.dart
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class M3ESpacing {
|
||||
final double xs; // 4
|
||||
final double sm; // 8
|
||||
final double md; // 12
|
||||
final double lg; // 16
|
||||
final double xl; // 24
|
||||
final double xxl; // 32
|
||||
|
||||
const M3ESpacing({
|
||||
required this.xs,
|
||||
required this.sm,
|
||||
required this.md,
|
||||
required this.lg,
|
||||
required this.xl,
|
||||
required this.xxl,
|
||||
});
|
||||
|
||||
const M3ESpacing.regular()
|
||||
: xs = 4,
|
||||
sm = 8,
|
||||
md = 12,
|
||||
lg = 16,
|
||||
xl = 24,
|
||||
xxl = 32;
|
||||
|
||||
static M3ESpacing lerp(M3ESpacing a, M3ESpacing b, double t) => M3ESpacing(
|
||||
xs: a.xs + (b.xs - a.xs) * t,
|
||||
sm: a.sm + (b.sm - a.sm) * t,
|
||||
md: a.md + (b.md - a.md) * t,
|
||||
lg: a.lg + (b.lg - a.lg) * t,
|
||||
xl: a.xl + (b.xl - a.xl) * t,
|
||||
xxl: a.xxl + (b.xxl - a.xxl) * t,
|
||||
);
|
||||
}
|
||||
54
packages/m3e_design/lib/tokens/typography_tokens.dart
Normal file
54
packages/m3e_design/lib/tokens/typography_tokens.dart
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
@immutable
|
||||
class M3EEmphasized {
|
||||
final TextStyle display;
|
||||
final TextStyle headline;
|
||||
final TextStyle title;
|
||||
final TextStyle label;
|
||||
|
||||
const M3EEmphasized({
|
||||
required this.display,
|
||||
required this.headline,
|
||||
required this.title,
|
||||
required this.label,
|
||||
});
|
||||
|
||||
static M3EEmphasized forBrightness(Brightness b) {
|
||||
return const M3EEmphasized(
|
||||
display: TextStyle(fontWeight: FontWeight.w800, letterSpacing: -0.5),
|
||||
headline: TextStyle(fontWeight: FontWeight.w700, letterSpacing: -0.25),
|
||||
title: TextStyle(fontWeight: FontWeight.w700),
|
||||
label: TextStyle(fontWeight: FontWeight.w700),
|
||||
);
|
||||
}
|
||||
|
||||
static M3EEmphasized lerp(M3EEmphasized a, M3EEmphasized b, double t) =>
|
||||
M3EEmphasized(
|
||||
display: TextStyle.lerp(a.display, b.display, t)!,
|
||||
headline: TextStyle.lerp(a.headline, b.headline, t)!,
|
||||
title: TextStyle.lerp(a.title, b.title, t)!,
|
||||
label: TextStyle.lerp(a.label, b.label, t)!,
|
||||
);
|
||||
}
|
||||
|
||||
@immutable
|
||||
class M3ETypography {
|
||||
final TextTheme base;
|
||||
final M3EEmphasized emphasized;
|
||||
|
||||
const M3ETypography({required this.base, required this.emphasized});
|
||||
|
||||
factory M3ETypography.defaultFor(Brightness b) {
|
||||
// Use a minimal baseline; app's ThemeData will provide fuller TextTheme.
|
||||
const textTheme = TextTheme();
|
||||
return M3ETypography(
|
||||
base: textTheme, emphasized: M3EEmphasized.forBrightness(b));
|
||||
}
|
||||
|
||||
static M3ETypography lerp(M3ETypography a, M3ETypography b, double t) =>
|
||||
M3ETypography(
|
||||
base: TextTheme.lerp(a.base, b.base, t),
|
||||
emphasized: M3EEmphasized.lerp(a.emphasized, b.emphasized, t),
|
||||
);
|
||||
}
|
||||
7
packages/m3e_design/lib/utils/build_context_x.dart
Normal file
7
packages/m3e_design/lib/utils/build_context_x.dart
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import '../theme/m3e_theme.dart';
|
||||
|
||||
extension BuildContextM3EX on BuildContext {
|
||||
M3ETheme get m3e =>
|
||||
Theme.of(this).extension<M3ETheme>() ?? M3ETheme.defaults(Theme.of(this).colorScheme);
|
||||
}
|
||||
5
packages/m3e_design/lib/utils/semantics_x.dart
Normal file
5
packages/m3e_design/lib/utils/semantics_x.dart
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
extension SemanticsX on Widget {
|
||||
Widget withLabel(String label) => Semantics(label: label, child: this);
|
||||
}
|
||||
29
packages/m3e_design/melos_m3e_design.iml
Normal file
29
packages/m3e_design/melos_m3e_design.iml
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$" isTestSource="false" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.dart_tool" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/.pub" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/build" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/android/.gradle" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/android/.idea" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/Flutter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/Pods" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/ios/.symlinks" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/macos/Flutter" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/macos/Pods" />
|
||||
<excludeFolder url="file://$MODULE_DIR$/example/macos/.symlinks" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="Dart SDK" level="project" />
|
||||
<orderEntry type="library" name="Flutter Plugins" level="project" />
|
||||
<orderEntry type="library" name="Dart Packages" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
16
packages/m3e_design/pubspec.yaml
Normal file
16
packages/m3e_design/pubspec.yaml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
name: m3e_design
|
||||
description: Material 3 Expressive design language for Flutter (tokens, ThemeExtension, motion).
|
||||
version: 0.1.0
|
||||
publish_to: none
|
||||
repository: https://example.com/your-repo
|
||||
|
||||
environment:
|
||||
sdk: ">=3.5.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
Loading…
Add table
Add a link
Reference in a new issue