toApproximateMaterialTheme method
Converts this FThemeData to a Material ThemeData on a best-effort basis.
This method enables interoperability between Forui and Material Design widgets by mapping Forui's theme properties to their closest Material equivalents. Use this when you need to:
- Use Material widgets within a Forui application
- Apply your Forui theme consistently to both Forui and Material widgets
- Create a gradual migration path from Material Design to Forui
Note that this conversion is approximate and experimental. Some styling properties may not map perfectly between the two design systems, and the resulting Material theme might not capture all the nuances of your Forui theme.
// Apply a Forui theme to Material widgets
MaterialApp(
theme: FThemes.zinc.light.toApproximateMaterialTheme(),
// ...
)
Implementation
@experimental
ThemeData toApproximateMaterialTheme() {
// Material requires height to be 1, certain widgets will overflow without it.
// TextBaseline.alphabetic is required as TextField requires it.
final textTheme = TextTheme(
displayLarge: typography.xl4.copyWith(
height: 1,
textBaseline: typography.xl4.textBaseline ?? TextBaseline.alphabetic,
),
displayMedium: typography.xl3.copyWith(
height: 1,
textBaseline: typography.xl3.textBaseline ?? TextBaseline.alphabetic,
),
displaySmall: typography.xl2.copyWith(
height: 1,
textBaseline: typography.xl2.textBaseline ?? TextBaseline.alphabetic,
),
headlineLarge: typography.xl3.copyWith(
height: 1,
textBaseline: typography.xl3.textBaseline ?? TextBaseline.alphabetic,
),
headlineMedium: typography.xl2.copyWith(
height: 1,
textBaseline: typography.xl2.textBaseline ?? TextBaseline.alphabetic,
),
headlineSmall: typography.xl.copyWith(
height: 1,
textBaseline: typography.xl.textBaseline ?? TextBaseline.alphabetic,
),
titleLarge: typography.lg.copyWith(
height: 1,
textBaseline: typography.lg.textBaseline ?? TextBaseline.alphabetic,
),
titleMedium: typography.base.copyWith(
height: 1,
textBaseline: typography.base.textBaseline ?? TextBaseline.alphabetic,
),
titleSmall: typography.sm.copyWith(
height: 1,
textBaseline: typography.sm.textBaseline ?? TextBaseline.alphabetic,
),
labelLarge: typography.base.copyWith(
height: 1,
textBaseline: typography.base.textBaseline ?? TextBaseline.alphabetic,
),
labelMedium: typography.sm.copyWith(
height: 1,
textBaseline: typography.sm.textBaseline ?? TextBaseline.alphabetic,
),
labelSmall: typography.xs.copyWith(
height: 1,
textBaseline: typography.xs.textBaseline ?? TextBaseline.alphabetic,
),
bodyLarge: typography.base.copyWith(
height: 1,
textBaseline: typography.base.textBaseline ?? TextBaseline.alphabetic,
),
bodyMedium: typography.sm.copyWith(
height: 1,
textBaseline: typography.sm.textBaseline ?? TextBaseline.alphabetic,
),
bodySmall: typography.xs.copyWith(height: 1, textBaseline: typography.xs.textBaseline ?? TextBaseline.alphabetic),
)..apply(
fontFamily: typography.defaultFontFamily,
bodyColor: colorScheme.foreground,
displayColor: colorScheme.foreground,
);
return ThemeData(
colorScheme: ColorScheme(
brightness: colorScheme.brightness,
primary: colorScheme.primary,
onPrimary: colorScheme.primaryForeground,
secondary: colorScheme.secondary,
onSecondary: colorScheme.secondaryForeground,
error: colorScheme.error,
onError: colorScheme.errorForeground,
surface: colorScheme.background,
onSurface: colorScheme.foreground,
secondaryContainer: colorScheme.secondary,
onSecondaryContainer: colorScheme.secondaryForeground,
),
fontFamily: typography.defaultFontFamily,
typography: Typography(
black: textTheme,
white: textTheme,
englishLike: textTheme,
dense: textTheme,
tall: textTheme,
),
textTheme: textTheme,
splashFactory: NoSplash.splashFactory,
useMaterial3: true,
//// Navigation Bar
navigationBarTheme: NavigationBarThemeData(
indicatorShape: RoundedRectangleBorder(borderRadius: style.borderRadius),
),
//// Navigation Drawer
navigationDrawerTheme: NavigationDrawerThemeData(
indicatorShape: RoundedRectangleBorder(borderRadius: style.borderRadius),
),
//// Navigation Rail
navigationRailTheme: NavigationRailThemeData(
indicatorShape: RoundedRectangleBorder(borderRadius: style.borderRadius),
),
//// Card
cardTheme: CardTheme(
elevation: 0,
shape: RoundedRectangleBorder(
borderRadius: style.borderRadius,
side: BorderSide(width: style.borderWidth, color: colorScheme.border),
),
),
//// Chip
chipTheme: ChipThemeData(shape: RoundedRectangleBorder(borderRadius: style.borderRadius)),
//// Input
inputDecorationTheme: InputDecorationTheme(
border: OutlineInputBorder(
borderRadius: textFieldStyle.enabledStyle.unfocusedStyle.radius,
borderSide: BorderSide(
color: textFieldStyle.enabledStyle.unfocusedStyle.color,
width: textFieldStyle.enabledStyle.unfocusedStyle.width,
),
),
enabledBorder: OutlineInputBorder(
borderRadius: textFieldStyle.enabledStyle.unfocusedStyle.radius,
borderSide: BorderSide(
color: textFieldStyle.enabledStyle.unfocusedStyle.color,
width: textFieldStyle.enabledStyle.unfocusedStyle.width,
),
),
focusedBorder: OutlineInputBorder(
borderRadius: textFieldStyle.enabledStyle.focusedStyle.radius,
borderSide: BorderSide(
color: textFieldStyle.enabledStyle.focusedStyle.color,
width: textFieldStyle.enabledStyle.focusedStyle.width,
),
),
errorBorder: OutlineInputBorder(
borderRadius: textFieldStyle.errorStyle.unfocusedStyle.radius,
borderSide: BorderSide(
color: textFieldStyle.errorStyle.unfocusedStyle.color,
width: textFieldStyle.errorStyle.unfocusedStyle.width,
),
),
focusedErrorBorder: OutlineInputBorder(
borderRadius: textFieldStyle.errorStyle.focusedStyle.radius,
borderSide: BorderSide(
color: textFieldStyle.errorStyle.focusedStyle.color,
width: textFieldStyle.errorStyle.focusedStyle.width,
),
),
disabledBorder: OutlineInputBorder(
borderRadius: textFieldStyle.disabledStyle.unfocusedStyle.radius,
borderSide: BorderSide(
color: textFieldStyle.disabledStyle.unfocusedStyle.color,
width: textFieldStyle.disabledStyle.unfocusedStyle.width,
),
),
labelStyle: textFieldStyle.enabledStyle.descriptionTextStyle,
floatingLabelStyle: textFieldStyle.enabledStyle.labelTextStyle,
hintStyle: textFieldStyle.enabledStyle.hintTextStyle,
errorStyle: textFieldStyle.errorStyle.errorTextStyle,
helperStyle: textFieldStyle.enabledStyle.descriptionTextStyle,
counterStyle: textFieldStyle.enabledStyle.counterTextStyle,
contentPadding: textFieldStyle.contentPadding,
),
//// Date Picker
datePickerTheme: DatePickerThemeData(
shape: RoundedRectangleBorder(borderRadius: style.borderRadius),
dayShape: WidgetStateProperty.all(RoundedRectangleBorder(borderRadius: style.borderRadius)),
rangePickerShape: RoundedRectangleBorder(borderRadius: style.borderRadius),
),
//// Time Picker
timePickerTheme: TimePickerThemeData(
hourMinuteTextColor: colorScheme.secondaryForeground,
hourMinuteColor: colorScheme.secondary,
hourMinuteShape: RoundedRectangleBorder(borderRadius: style.borderRadius),
dayPeriodTextColor: colorScheme.foreground,
dayPeriodColor: colorScheme.secondary,
dayPeriodBorderSide: BorderSide(color: colorScheme.border),
dayPeriodShape: RoundedRectangleBorder(borderRadius: style.borderRadius),
dialBackgroundColor: colorScheme.secondary,
shape: RoundedRectangleBorder(borderRadius: style.borderRadius),
),
/// Slider
sliderTheme: SliderThemeData(
activeTrackColor: sliderStyles.horizontalStyle.enabledStyle.activeColor,
inactiveTrackColor: sliderStyles.horizontalStyle.enabledStyle.inactiveColor,
disabledActiveTrackColor: sliderStyles.horizontalStyle.disabledStyle.activeColor,
disabledInactiveTrackColor: sliderStyles.horizontalStyle.disabledStyle.inactiveColor,
activeTickMarkColor: sliderStyles.horizontalStyle.enabledStyle.markStyle.tickColor,
inactiveTickMarkColor: sliderStyles.horizontalStyle.enabledStyle.markStyle.tickColor,
disabledActiveTickMarkColor: sliderStyles.horizontalStyle.disabledStyle.markStyle.tickColor,
disabledInactiveTickMarkColor: sliderStyles.horizontalStyle.disabledStyle.markStyle.tickColor,
thumbColor: sliderStyles.horizontalStyle.enabledStyle.thumbStyle.borderColor,
disabledThumbColor: sliderStyles.horizontalStyle.disabledStyle.thumbStyle.borderColor,
valueIndicatorColor: sliderStyles.horizontalStyle.enabledStyle.tooltipStyle.decoration.color,
valueIndicatorTextStyle: sliderStyles.horizontalStyle.enabledStyle.tooltipStyle.textStyle,
),
//// Switch
switchTheme: SwitchThemeData(
thumbColor: WidgetStateColor.fromMap({
WidgetState.disabled: switchStyle.disabledStyle.thumbColor,
WidgetState.any: switchStyle.enabledStyle.thumbColor,
}),
trackColor: WidgetStateColor.fromMap({
WidgetState.disabled & WidgetState.selected: switchStyle.disabledStyle.checkedColor,
WidgetState.disabled: switchStyle.disabledStyle.uncheckedColor,
WidgetState.selected: switchStyle.enabledStyle.checkedColor,
WidgetState.any: switchStyle.enabledStyle.uncheckedColor,
}),
trackOutlineColor: WidgetStateColor.fromMap({
WidgetState.disabled & WidgetState.selected: switchStyle.disabledStyle.checkedColor,
WidgetState.disabled: switchStyle.disabledStyle.uncheckedColor,
WidgetState.selected: switchStyle.enabledStyle.checkedColor,
WidgetState.any: switchStyle.enabledStyle.uncheckedColor,
}),
),
//// Buttons
elevatedButtonTheme: ElevatedButtonThemeData(
style: ButtonStyle(
textStyle: WidgetStateTextStyle.fromMap({
WidgetState.disabled: buttonStyles.secondary.contentStyle.disabledTextStyle,
WidgetState.any: buttonStyles.secondary.contentStyle.enabledTextStyle,
}),
backgroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.secondary.disabledBoxDecoration.color,
WidgetState.hovered: buttonStyles.secondary.enabledHoverBoxDecoration.color,
WidgetState.any: buttonStyles.secondary.enabledBoxDecoration.color,
}),
foregroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.secondary.contentStyle.disabledTextStyle.color,
WidgetState.any: buttonStyles.secondary.contentStyle.enabledTextStyle.color,
}),
padding: WidgetStateProperty.all(buttonStyles.secondary.contentStyle.padding),
shape: WidgetStateProperty.all(RoundedRectangleBorder(borderRadius: style.borderRadius)),
),
),
filledButtonTheme: FilledButtonThemeData(
style: ButtonStyle(
textStyle: WidgetStateTextStyle.fromMap({
WidgetState.disabled: buttonStyles.primary.contentStyle.disabledTextStyle,
WidgetState.any: buttonStyles.primary.contentStyle.enabledTextStyle,
}),
backgroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.primary.disabledBoxDecoration.color,
WidgetState.hovered: buttonStyles.primary.enabledHoverBoxDecoration.color,
WidgetState.any: buttonStyles.primary.enabledBoxDecoration.color,
}),
foregroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.primary.contentStyle.disabledTextStyle.color,
WidgetState.any: buttonStyles.primary.contentStyle.enabledTextStyle.color,
}),
padding: WidgetStateProperty.all(buttonStyles.primary.contentStyle.padding),
shape: WidgetStateProperty.all(RoundedRectangleBorder(borderRadius: style.borderRadius)),
),
),
outlinedButtonTheme: OutlinedButtonThemeData(
style: ButtonStyle(
textStyle: WidgetStateTextStyle.fromMap({
WidgetState.disabled: buttonStyles.outline.contentStyle.disabledTextStyle,
~WidgetState.disabled: buttonStyles.outline.contentStyle.enabledTextStyle,
}),
backgroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.outline.disabledBoxDecoration.color,
WidgetState.hovered: buttonStyles.outline.enabledHoverBoxDecoration.color,
WidgetState.any: buttonStyles.outline.enabledBoxDecoration.color,
}),
foregroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.outline.contentStyle.disabledTextStyle.color,
WidgetState.any: buttonStyles.outline.contentStyle.enabledTextStyle.color,
}),
padding: WidgetStateProperty.all(buttonStyles.outline.contentStyle.padding),
side: WidgetStateBorderSide.fromMap({
WidgetState.disabled: BorderSide(
color:
buttonStyles.outline.disabledBoxDecoration.border?.top.color ??
colorScheme.disable(colorScheme.border),
width: buttonStyles.outline.disabledBoxDecoration.border?.top.width ?? style.borderWidth,
),
WidgetState.hovered: BorderSide(
color:
buttonStyles.outline.enabledHoverBoxDecoration.border?.top.color ??
colorScheme.hover(colorScheme.border),
width: buttonStyles.outline.enabledHoverBoxDecoration.border?.top.width ?? style.borderWidth,
),
WidgetState.any: BorderSide(
color: buttonStyles.outline.enabledBoxDecoration.border?.top.color ?? colorScheme.border,
width: buttonStyles.outline.enabledBoxDecoration.border?.top.width ?? style.borderWidth,
),
}),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: buttonStyles.outline.enabledBoxDecoration.borderRadius ?? style.borderRadius,
),
),
),
),
textButtonTheme: TextButtonThemeData(
style: ButtonStyle(
textStyle: WidgetStateTextStyle.fromMap({
WidgetState.disabled: buttonStyles.ghost.contentStyle.disabledTextStyle,
WidgetState.any: buttonStyles.ghost.contentStyle.enabledTextStyle,
}),
backgroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.ghost.disabledBoxDecoration.color,
WidgetState.hovered: buttonStyles.ghost.enabledHoverBoxDecoration.color,
WidgetState.any: buttonStyles.ghost.enabledBoxDecoration.color,
}),
foregroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.ghost.contentStyle.disabledTextStyle.color,
WidgetState.any: buttonStyles.ghost.contentStyle.enabledTextStyle.color,
}),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: buttonStyles.ghost.enabledBoxDecoration.borderRadius ?? style.borderRadius,
),
),
),
),
floatingActionButtonTheme: FloatingActionButtonThemeData(
backgroundColor: buttonStyles.primary.enabledBoxDecoration.color,
foregroundColor: buttonStyles.primary.contentStyle.enabledTextStyle.color,
hoverColor: buttonStyles.primary.enabledHoverBoxDecoration.color,
disabledElevation: 0,
shape: RoundedRectangleBorder(
borderRadius: buttonStyles.primary.enabledBoxDecoration.borderRadius ?? style.borderRadius,
),
),
iconButtonTheme: IconButtonThemeData(
style: ButtonStyle(
backgroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.ghost.disabledBoxDecoration.color,
WidgetState.hovered: buttonStyles.ghost.enabledHoverBoxDecoration.color,
WidgetState.any: buttonStyles.ghost.enabledBoxDecoration.color,
}),
foregroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.ghost.contentStyle.disabledTextStyle.color,
WidgetState.any: buttonStyles.ghost.contentStyle.enabledTextStyle.color,
}),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: buttonStyles.ghost.enabledBoxDecoration.borderRadius ?? style.borderRadius,
),
),
),
),
segmentedButtonTheme: SegmentedButtonThemeData(
style: ButtonStyle(
textStyle: WidgetStateTextStyle.fromMap({
WidgetState.disabled: buttonStyles.ghost.contentStyle.disabledTextStyle,
WidgetState.any: buttonStyles.ghost.contentStyle.enabledTextStyle,
}),
backgroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.ghost.disabledBoxDecoration.color,
WidgetState.hovered: buttonStyles.ghost.enabledHoverBoxDecoration.color,
WidgetState.any: buttonStyles.ghost.enabledBoxDecoration.color,
}),
foregroundColor: WidgetStateMapper({
WidgetState.disabled: buttonStyles.ghost.contentStyle.disabledTextStyle.color,
WidgetState.any: buttonStyles.ghost.contentStyle.enabledTextStyle.color,
}),
shape: WidgetStateProperty.all(
RoundedRectangleBorder(
borderRadius: buttonStyles.ghost.enabledBoxDecoration.borderRadius ?? style.borderRadius,
),
),
),
),
/// Dialog
dialogTheme: DialogTheme(shape: RoundedRectangleBorder(borderRadius: style.borderRadius)),
/// Bottom Sheet
bottomSheetTheme: BottomSheetThemeData(shape: RoundedRectangleBorder(borderRadius: style.borderRadius)),
/// Snack Bar
snackBarTheme: SnackBarThemeData(shape: RoundedRectangleBorder(borderRadius: style.borderRadius)),
/// List Tile
listTileTheme: ListTileThemeData(shape: RoundedRectangleBorder(borderRadius: style.borderRadius)),
/// Divider
dividerTheme: DividerThemeData(
color: dividerStyles.horizontalStyle.color,
thickness: dividerStyles.horizontalStyle.width,
),
);
}