Last active
May 4, 2023 09:31
-
-
Save tchafack/f8f0ca1df9d16915af14fad54b644872 to your computer and use it in GitHub Desktop.
Flutter Material 3 from design to deployment. Gist based on Flutter Forward https://www.youtube.com/watch?v=7nrhTdS7dHg
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'theme.dart'; | |
.. | |
class MyApp extends StatelessWidget { | |
const MyApp({super.key}); | |
@override | |
Widget build(BuildContext context) { | |
const basilTheme = BasilTheme(); | |
return MaterialApp.router( | |
... | |
theme: basilTheme.toThemeData(), | |
darkTheme: basilTheme.toDarkThemeData(), | |
themeMode: ThemeMode.light, | |
... | |
); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import 'package:material_color_utilities/material_color_utilities.dart'; | |
import 'package:flutter/services.dart'; | |
import 'package:google_fonts/google_fonts.dart'; | |
... | |
@immutable | |
class BasilTheme extends ThemeExtension<BasilTheme> { | |
final Color primaryColor, tertiaryColor, neutralColor; | |
const BasilTheme({ | |
this.primaryColor = const Color(0xFF356859), | |
this.tertiaryColor = const Color(0xFFFF5722), | |
this.neutralColor = const Color(0xFFFFFBE6), | |
}); | |
@override | |
ThemeExtension<BasilTheme> copyWith({ | |
Color? primaryColor, | |
Color? tertiaryColor, | |
Color? neutralColor, | |
}) => | |
BasilTheme( | |
primaryColor: primaryColor ?? this.primaryColor, | |
tertiaryColor: tertiaryColor ?? this.tertiaryColor, | |
neutralColor: neutralColor ?? this.neutralColor, | |
); | |
@override | |
ThemeExtension<BasilTheme> lerp( | |
covariant ThemeExtension<BasilTheme>? other, double t) { | |
if (other is! BasilTheme) return this; | |
return BasilTheme( | |
primaryColor: Color.lerp(primaryColor, other.primaryColor, t)!, | |
tertiaryColor: Color.lerp(tertiaryColor, other.tertiaryColor, t)!, | |
neutralColor: Color.lerp(neutralColor, other.neutralColor, t)!, | |
); | |
} | |
Scheme _scheme() { | |
final base = CorePalette.of(primaryColor.value); | |
final primary = base.primary; | |
final tertiary = CorePalette.of(tertiaryColor.value).primary; | |
final neutral = CorePalette.of(neutralColor.value).neutral; | |
return Scheme( | |
primary: primary.get(40), | |
onPrimary: primary.get(100), | |
primaryContainer: primary.get(90), | |
onPrimaryContainer: primary.get(10), | |
secondary: base.secondary.get(40), | |
onSecondary: base.secondary.get(100), | |
secondaryContainer: base.secondary.get(90), | |
onSecondaryContainer: base.secondary.get(10), | |
tertiary: tertiary.get(40), | |
onTertiary: tertiary.get(100), | |
tertiaryContainer: tertiary.get(90), | |
onTertiaryContainer: tertiary.get(10), | |
error: base.error.get(40), | |
onError: base.error.get(100), | |
errorContainer: base.error.get(90), | |
onErrorContainer: base.error.get(10), | |
background: neutral.get(98), | |
onBackground: neutral.get(10), | |
surface: neutral.get(98), | |
onSurface: neutral.get(10), | |
surfaceVariant: base.neutralVariant.get(90), | |
onSurfaceVariant: base.neutralVariant.get(30), | |
outline: base.neutralVariant.get(50), | |
outlineVariant: base.neutralVariant.get(80), | |
shadow: neutral.get(0), | |
scrim: neutral.get(0), | |
inverseSurface: neutral.get(20), | |
inverseOnSurface: neutral.get(95), | |
inversePrimary: primary.get(80)); | |
} | |
Scheme _darkScheme() { | |
final base = CorePalette.of(primaryColor.value); | |
final primary = base.primary; | |
final tertiary = CorePalette.of(tertiaryColor.value).primary; | |
final neutral = CorePalette.of(neutralColor.value).neutral; | |
return Scheme( | |
primary: primary.get(80), | |
onPrimary: primary.get(20), | |
primaryContainer: primary.get(30), | |
onPrimaryContainer: primary.get(90), | |
secondary: base.secondary.get(80), | |
onSecondary: base.secondary.get(20), | |
secondaryContainer: base.secondary.get(30), | |
onSecondaryContainer: base.secondary.get(90), | |
tertiary: tertiary.get(80), | |
onTertiary: tertiary.get(20), | |
tertiaryContainer: tertiary.get(30), | |
onTertiaryContainer: tertiary.get(90), | |
error: base.error.get(80), | |
onError: base.error.get(20), | |
errorContainer: base.error.get(30), | |
onErrorContainer: base.error.get(90), | |
background: neutral.get(6), | |
onBackground: neutral.get(90), | |
surface: neutral.get(6), | |
onSurface: neutral.get(90), | |
surfaceVariant: base.neutralVariant.get(30), | |
onSurfaceVariant: base.neutralVariant.get(80), | |
outline: base.neutralVariant.get(60), | |
outlineVariant: base.neutralVariant.get(30), | |
shadow: neutral.get(0), | |
scrim: neutral.get(0), | |
inverseSurface: neutral.get(90), | |
inverseOnSurface: neutral.get(20), | |
inversePrimary: primary.get(40)); | |
} | |
ThemeData _base(ColorScheme colorScheme) { | |
final isLight = colorScheme.brightness == Brightness.light; | |
final primaryTexTheme = GoogleFonts.lektonTextTheme(); | |
final secondaryTexTheme = GoogleFonts.montserratTextTheme(); | |
final textTheme = primaryTexTheme.copyWith( | |
displaySmall: secondaryTexTheme.displaySmall, | |
); | |
return ThemeData( | |
useMaterial3: true, | |
extensions: [this], | |
textTheme: textTheme, | |
colorScheme: colorScheme, | |
scaffoldBackgroundColor: isLight ? neutralColor : colorScheme.background, | |
tabBarTheme: TabBarTheme( | |
labelColor: colorScheme.onSurface, | |
unselectedLabelColor: colorScheme.onSurface, | |
indicator: BoxDecoration( | |
border: Border( | |
bottom: BorderSide(color: colorScheme.primary, width: 2,), | |
), | |
), | |
), | |
floatingActionButtonTheme: FloatingActionButtonThemeData( | |
backgroundColor: colorScheme.error, | |
foregroundColor: colorScheme.onError, | |
), | |
navigationRailTheme: NavigationRailThemeData( | |
backgroundColor: isLight ? neutralColor : colorScheme.surface, | |
selectedIconTheme: IconThemeData(color: colorScheme.onSecondaryContainer,), | |
indicatorColor: colorScheme.secondaryContainer, | |
), | |
appBarTheme: AppBarTheme(backgroundColor: isLight?neutralColor : colorScheme.surface, | |
), | |
chipTheme: ChipThemeData( | |
backgroundColor: isLight ? neutralColor : colorScheme.surface, | |
) | |
); | |
} | |
ThemeData toThemeData() { | |
final colorScheme = _scheme().toColorScheme(Brightness.light); | |
return _base(colorScheme).copyWith(brightness: colorScheme.brightness); | |
} | |
ThemeData toDarkThemeData() { | |
final colorScheme = _darkScheme().toColorScheme(Brightness.dark); | |
return _base(colorScheme).copyWith(brightness: colorScheme.brightness); | |
} | |
} | |
extension on Scheme { | |
ColorScheme toColorScheme(Brightness brightness) { | |
return ColorScheme( | |
brightness: brightness, | |
primary: Color(primary), | |
onPrimary: Color(onPrimary), | |
secondary: Color(secondary), | |
onSecondary: Color(onSecondary), | |
error: Color(error), | |
onError: Color(onError), | |
background: Color(background), | |
onBackground: Color(onBackground), | |
surface: Color(surface), | |
onSurface: Color(onSurface)); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment