diff --git a/assets/fonts/IBMPlexSans-Bold.ttf b/assets/fonts/IBMPlexSans-Bold.ttf new file mode 100644 index 0000000..94717b8 Binary files /dev/null and b/assets/fonts/IBMPlexSans-Bold.ttf differ diff --git a/assets/fonts/IBMPlexSans-Light.ttf b/assets/fonts/IBMPlexSans-Light.ttf new file mode 100644 index 0000000..5c25d87 Binary files /dev/null and b/assets/fonts/IBMPlexSans-Light.ttf differ diff --git a/assets/fonts/IBMPlexSans-Medium.ttf b/assets/fonts/IBMPlexSans-Medium.ttf new file mode 100644 index 0000000..4c74ec6 Binary files /dev/null and b/assets/fonts/IBMPlexSans-Medium.ttf differ diff --git a/assets/fonts/IBMPlexSans-Regular.ttf b/assets/fonts/IBMPlexSans-Regular.ttf new file mode 100644 index 0000000..702c637 Binary files /dev/null and b/assets/fonts/IBMPlexSans-Regular.ttf differ diff --git a/assets/fonts/IBMPlexSans-SemiBold.ttf b/assets/fonts/IBMPlexSans-SemiBold.ttf new file mode 100644 index 0000000..a0cde8e Binary files /dev/null and b/assets/fonts/IBMPlexSans-SemiBold.ttf differ diff --git a/lib/designsystem/theme/AppColorsExtension.dart b/lib/designsystem/theme/AppColorsExtension.dart new file mode 100644 index 0000000..c56cce9 --- /dev/null +++ b/lib/designsystem/theme/AppColorsExtension.dart @@ -0,0 +1,96 @@ +import 'package:flutter/material.dart'; + +class AppColorsExtension extends ThemeExtension { + AppColorsExtension({ + required this.primary, + required this.onPrimary, + required this.secondary, + required this.onSecondary, + required this.error, + required this.onError, + required this.background, + required this.onBackground, + required this.surface, + required this.onSurface, + }); + + final Color primary; + final Color onPrimary; + final Color secondary; + final Color onSecondary; + final Color error; + final Color onError; + final Color background; + final Color onBackground; + final Color surface; + final Color onSurface; + + @override + ThemeExtension copyWith({ + Color? primary, + Color? onPrimary, + Color? secondary, + Color? onSecondary, + Color? error, + Color? onError, + Color? background, + Color? onBackground, + Color? surface, + Color? onSurface, + }) { + return AppColorsExtension( + primary: primary ?? this.primary, + onPrimary: onPrimary ?? this.onPrimary, + secondary: secondary ?? this.secondary, + onSecondary: onSecondary ?? this.onSecondary, + error: error ?? this.error, + onError: onError ?? this.onError, + background: background ?? this.background, + onBackground: onBackground ?? this.onBackground, + surface: surface ?? this.surface, + onSurface: onSurface ?? this.onSurface, + ); + } + + @override + ThemeExtension lerp( + covariant ThemeExtension? other, + double t, + ) { + if (other is! AppColorsExtension) { + return this; + } + + return AppColorsExtension( + primary: Color.lerp(primary, other.primary, t)!, + onPrimary: Color.lerp(onPrimary, other.onPrimary, t)!, + secondary: Color.lerp(secondary, other.secondary, t)!, + onSecondary: Color.lerp(onSecondary, other.onSecondary, t)!, + error: Color.lerp(error, other.error, t)!, + onError: Color.lerp(onError, other.onError, t)!, + background: Color.lerp(background, other.background, t)!, + onBackground: Color.lerp(onBackground, other.onBackground, t)!, + surface: Color.lerp(surface, other.surface, t)!, + onSurface: Color.lerp(onSurface, other.onSurface, t)!, + ); + } +} + +/// Optional. If you also want to assign colors in the `ColorScheme`. +extension ColorSchemeBuilder on AppColorsExtension { + ColorScheme toColorScheme(Brightness brightness) { + return ColorScheme( + brightness: brightness, + primary: primary, + onPrimary: onPrimary, + secondary: secondary, + onSecondary: onSecondary, + error: error, + onError: onError, + background: background, + onBackground: onBackground, + surface: surface, + onSurface: onSurface, + ); + } +} diff --git a/lib/designsystem/theme/AppPalette.dart b/lib/designsystem/theme/AppPalette.dart new file mode 100644 index 0000000..41951cc --- /dev/null +++ b/lib/designsystem/theme/AppPalette.dart @@ -0,0 +1,68 @@ +import 'dart:ui'; + +import 'package:flutter/material.dart'; + +abstract class AppPalette { + //Primary + static const primaryMineralBlue = Color(0xFF48A2AE); + static const primaryMineralBlue80 = Color(0xFF6DB5BE); + static const primaryMineralBlue60 = Color(0xFF91C7CE); + static const primaryMineralBlue40 = Color(0xFFB6DADF); + static const primaryMineralBlue20 = Color(0xFFDAECEF); + static const primaryMineralBlue10 = Color(0xFFECF6F7); + static const primaryMineralBlue05 = Color(0xFFF6FAFB); + + //Secondary + static const secondaryDarkBlue = Color(0xFF052535); + static const secondaryOcean140 = Color(0xFF013243); + static const secondaryOcean120 = Color(0xFF01475F); + static const secondaryOceanBlue = Color(0xFF005A78); + static const secondaryOcean80 = Color(0xFF337B93); + static const secondaryOcean160 = Color(0xFF7FACBB); + static const secondaryEggShellWhite = Color(0xFFFFFDE4); + static const secondaryOffWhite = Color(0xFFEBF0F0); + + //Neutral + static const neutralBlack = Color(0xFF000000); + static const neutralGray90 = Color(0xFF191919); + static const neutralGray80 = Color(0xFF333333); + static const neutralGray70 = Color(0xFF4c4c4c); + static const neutralGray60 = Color(0xFF666666); + static const neutralGray50 = Color(0xFF7f7f7f); + static const neutralGray40 = Color(0xFF999999); + static const neutralGray30 = Color(0xFFb2b2b2); + static const neutralGray20 = Color(0xFFcccccc); + static const neutralGray10 = Color(0xFFe5e5e5); + static const neutralGray05 = Color(0xFFf2f2f2); + static const neutralWhite = Color(0xFFFFFFFF); + + //Dark Mode + static const darkModeMidnight = Color(0xFF111D2B); + static const darkModeMidnightDark = Color(0xFF2D415A); + static const darkModeMidnightLight = Color(0xFF5F7897); + + //State - Negative + static const negativeDark = Color(0xFFab2b2b); + static const negativeBase = Color(0xFFDC5050); + static const negativeLight = Color(0xFFE87E90); + + //State - Positive + static const positiveDark = Color(0xFF189e46); + static const positiveBase = Color(0xFF33C364); + static const positiveLight = Color(0xFF8BE7AA); + + //State - Warning + static const warningDark = Color(0xFFc89e0a); + static const warningBase = Color(0xFFFBCD29); + static const warningLight = Color(0xFFF6DB9A); + + //State - Info + static const infoDark = Color(0xFF075cab); + static const infoBase = Color(0xFF2485DF); + static const infoLight = Color(0xFF65A4DD); + + //State - Help + static const helpDark = Color(0xFF53198e); + static const helpBase = Color(0xFF7939BA); + static const helpLight = Color(0xFFB37CDF); +} diff --git a/lib/designsystem/theme/AppTextThemeExtension.dart b/lib/designsystem/theme/AppTextThemeExtension.dart new file mode 100644 index 0000000..d154a9a --- /dev/null +++ b/lib/designsystem/theme/AppTextThemeExtension.dart @@ -0,0 +1,92 @@ +import 'package:flutter/material.dart'; + +class AppTextThemeExtension extends ThemeExtension { + const AppTextThemeExtension({ + required this.displayLarge, + required this.displayMedium, + required this.displaySmall, + required this.headlineLarge, + required this.headlineMedium, + required this.headlineSmall, + required this.titleLarge, + required this.titleMedium, + required this.titleSmall, + required this.bodyLarge, + required this.bodyMedium, + required this.bodySmall, + required this.labelLarge, + }); + + final TextStyle displayLarge; + final TextStyle displayMedium; + final TextStyle displaySmall; + final TextStyle headlineLarge; + final TextStyle headlineMedium; + final TextStyle headlineSmall; + final TextStyle titleLarge; + final TextStyle titleMedium; + final TextStyle titleSmall; + final TextStyle bodyLarge; + final TextStyle bodyMedium; + final TextStyle bodySmall; + final TextStyle labelLarge; + + @override + ThemeExtension copyWith({ + TextStyle? displayLarge, + TextStyle? displayMedium, + TextStyle? displaySmall, + TextStyle? headlineLarge, + TextStyle? headlineMedium, + TextStyle? headlineSmall, + TextStyle? titleLarge, + TextStyle? titleMedium, + TextStyle? titleSmall, + TextStyle? bodyLarge, + TextStyle? bodyMedium, + TextStyle? bodySmall, + TextStyle? labelLarge, + }) { + return AppTextThemeExtension( + displayLarge: displayLarge ?? this.displayLarge, + displayMedium: displayMedium ?? this.displayMedium, + displaySmall: displaySmall ?? this.displaySmall, + headlineLarge: headlineLarge ?? this.headlineLarge, + headlineMedium: headlineMedium ?? this.headlineMedium, + headlineSmall: headlineSmall ?? this.headlineSmall, + titleLarge: titleLarge ?? this.titleLarge, + titleMedium: titleMedium ?? this.titleMedium, + titleSmall: titleSmall ?? this.titleSmall, + bodyLarge: bodyLarge ?? this.bodyLarge, + bodyMedium: bodyMedium ?? this.bodyMedium, + bodySmall: bodySmall ?? this.bodySmall, + labelLarge: labelLarge ?? this.labelLarge, + ); + } + + @override + ThemeExtension lerp( + covariant ThemeExtension? other, + double t, + ) { + if (other is! AppTextThemeExtension) { + return this; + } + + return AppTextThemeExtension( + displayLarge: TextStyle.lerp(displayLarge, other.displayLarge, t)!, + displayMedium: TextStyle.lerp(displayMedium, other.displayMedium, t)!, + displaySmall: TextStyle.lerp(displaySmall, other.displaySmall, t)!, + headlineLarge: TextStyle.lerp(headlineLarge, other.headlineLarge, t)!, + headlineMedium: TextStyle.lerp(headlineMedium, other.headlineMedium, t)!, + headlineSmall: TextStyle.lerp(headlineSmall, other.headlineSmall, t)!, + titleLarge: TextStyle.lerp(titleLarge, other.titleLarge, t)!, + titleMedium: TextStyle.lerp(titleMedium, other.titleMedium, t)!, + titleSmall: TextStyle.lerp(titleSmall, other.titleSmall, t)!, + bodyLarge: TextStyle.lerp(bodyLarge, other.bodyLarge, t)!, + bodyMedium: TextStyle.lerp(bodyMedium, other.bodyMedium, t)!, + bodySmall: TextStyle.lerp(bodySmall, other.bodySmall, t)!, + labelLarge: TextStyle.lerp(labelLarge, other.labelLarge, t)!, + ); + } +} diff --git a/lib/designsystem/theme/AppTheme.dart b/lib/designsystem/theme/AppTheme.dart new file mode 100644 index 0000000..8923f12 --- /dev/null +++ b/lib/designsystem/theme/AppTheme.dart @@ -0,0 +1,20 @@ +import 'package:concordium_wallet/designsystem/theme/ThemeState.dart'; +import 'package:flutter_bloc/flutter_bloc.dart'; + +enum ThemeEvent { toggleDark, toggleLight } + +class AppTheme extends Bloc { + AppTheme() : super(ThemeState.lightTheme); + + @override + Stream mapEventToState(ThemeEvent event) async* { + switch (event) { + case ThemeEvent.toggleDark: + yield ThemeState.darkTheme; + break; + case ThemeEvent.toggleLight: + yield ThemeState.lightTheme; + break; + } + } +} diff --git a/lib/designsystem/theme/AppTypography.dart b/lib/designsystem/theme/AppTypography.dart new file mode 100644 index 0000000..bf863e1 --- /dev/null +++ b/lib/designsystem/theme/AppTypography.dart @@ -0,0 +1,137 @@ +import 'package:flutter/cupertino.dart'; + +abstract class AppTypography { + //Display + static const display1 = //bold + TextStyle( + fontSize: 40, + fontWeight: FontWeight.w700, + height: 47, + fontFamily: 'IBMPlexSans',); + static const display2 = TextStyle( + fontSize: 32, + fontWeight: FontWeight.w700, + height: 36, + fontFamily: 'IBMPlexSans'); + static const display3 = TextStyle( + fontSize: 25, + fontWeight: FontWeight.w700, + height: 30, + fontFamily: 'IBMPlexSans'); + static const display4 = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w700, + height: 24, + fontFamily: 'IBMPlexSans'); + static const display5 = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w700, + height: 19, + fontFamily: 'IBMPlexSans'); + static const display6 = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w700, + height: 17, + fontFamily: 'IBMPlexSans'); + + //Heading + static const heading1 = //medium + TextStyle( + fontSize: 28, + fontWeight: FontWeight.w500, + height: 36, + fontFamily: 'IBMPlexSans'); + static const heading2 = TextStyle( + fontSize: 24, + fontWeight: FontWeight.w500, + height: 32, + fontFamily: 'IBMPlexSans'); + static const heading3 = TextStyle( + fontSize: 20, + fontWeight: FontWeight.w500, + height: 28, + fontFamily: 'IBMPlexSans'); + static const heading4 = TextStyle( + fontSize: 18, + fontWeight: FontWeight.w500, + height: 26, + fontFamily: 'IBMPlexSans'); + static const heading5 = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w500, + height: 20, + fontFamily: 'IBMPlexSans'); + static const heading6 = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w500, + height: 18, + fontFamily: 'IBMPlexSans'); + static const heading7 = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w500, + height: 16, + fontFamily: 'IBMPlexSans'); + + //Body + static const bodyXL = //regular + TextStyle( + fontSize: 20, + fontWeight: FontWeight.w400, + height: 26, + fontFamily: 'IBMPlexSans'); + static const bodyL = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w400, + height: 20, + fontFamily: 'IBMPlexSans'); + static const bodyM = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w400, + height: 18, + fontFamily: 'IBMPlexSans'); + static const bodyS = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w400, + height: 16, + fontFamily: 'IBMPlexSans'); + static const bodyXS = TextStyle( + fontSize: 11, + fontWeight: FontWeight.w400, + height: 14, + fontFamily: 'IBMPlexSans'); + + //Body Light + static const bodyLightXL = //light + TextStyle( + fontSize: 20, + fontWeight: FontWeight.w300, + height: 26, + fontFamily: 'IBMPlexSans'); + static const bodyLightL = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w300, + height: 20, + fontFamily: 'IBMPlexSans'); + static const bodyLightM = TextStyle( + fontSize: 14, + fontWeight: FontWeight.w300, + height: 18, + fontFamily: 'IBMPlexSans'); + static const bodyLightS = TextStyle( + fontSize: 12, + fontWeight: FontWeight.w300, + height: 16, + fontFamily: 'IBMPlexSans'); + static const bodyLightXS = TextStyle( + fontSize: 11, + fontWeight: FontWeight.w300, + height: 14, + fontFamily: 'IBMPlexSans'); + + //Button + static const button = TextStyle( + fontSize: 16, + fontWeight: FontWeight.w600, + height: 20, + fontFamily: 'IBMPlexSans'); +} diff --git a/lib/designsystem/theme/ThemeState.dart b/lib/designsystem/theme/ThemeState.dart new file mode 100644 index 0000000..789e51f --- /dev/null +++ b/lib/designsystem/theme/ThemeState.dart @@ -0,0 +1,93 @@ +import 'package:concordium_wallet/designsystem/theme/AppPalette.dart'; +import 'package:concordium_wallet/designsystem/theme/AppTypography.dart'; +import 'package:flutter/material.dart'; + +import 'AppColorsExtension.dart'; +import 'AppTextThemeExtension.dart'; + +class ThemeState { + final ThemeData themeData; + + ThemeState(this.themeData); + + static ThemeState get darkTheme => ThemeState(ThemeData.dark().copyWith( + extensions: [_darkAppColors, _darkTextTheme], + )); + + static ThemeState get lightTheme => ThemeState(ThemeData.light().copyWith( + extensions: [ + _lightAppColors, + _lightTextTheme, + ], + )); + + static final _lightAppColors = AppColorsExtension( + primary: AppPalette.primaryMineralBlue, + onPrimary: AppPalette.neutralWhite, + secondary: AppPalette.secondaryDarkBlue, + onSecondary: AppPalette.neutralBlack, + error: AppPalette.negativeBase, + onError: AppPalette.neutralWhite, + background: AppPalette.neutralWhite, + onBackground: AppPalette.neutralBlack, + surface: AppPalette.neutralWhite, + onSurface: AppPalette.neutralBlack, + ); + + static final _darkAppColors = AppColorsExtension( + primary: AppPalette.darkModeMidnight, + onPrimary: AppPalette.neutralBlack, + secondary: AppPalette.secondaryDarkBlue, + onSecondary: AppPalette.neutralBlack, + error: AppPalette.negativeBase, + onError: AppPalette.neutralBlack, + background: AppPalette.darkModeMidnightDark, + onBackground: AppPalette.neutralWhite, + surface: AppPalette.darkModeMidnightDark, + onSurface: AppPalette.neutralWhite, + ); + + static final _lightTextTheme = AppTextThemeExtension( + displayLarge: AppTypography.display1.copyWith(color: Colors.black), + displayMedium: AppTypography.display2.copyWith(color: Colors.black), + displaySmall: AppTypography.display3.copyWith(color: Colors.black), + headlineLarge: AppTypography.heading1.copyWith(color: Colors.black), + headlineMedium: AppTypography.heading2.copyWith(color: Colors.black), + headlineSmall: AppTypography.heading3.copyWith(color: Colors.black), + titleLarge: AppTypography.heading4.copyWith(color: Colors.black), + titleMedium: AppTypography.heading5.copyWith(color: Colors.black), + titleSmall: AppTypography.heading6.copyWith(color: Colors.black), + bodyLarge: AppTypography.bodyLightL.copyWith(color: Colors.black), + bodyMedium: AppTypography.bodyLightM.copyWith(color: Colors.black), + bodySmall: AppTypography.bodyLightS.copyWith(color: Colors.black), + labelLarge: AppTypography.button.copyWith(color: Colors.black), + ); + + static final _darkTextTheme = AppTextThemeExtension( + displayLarge: AppTypography.display1.copyWith(color: Colors.white), + displayMedium: AppTypography.display2.copyWith(color: Colors.white), + displaySmall: AppTypography.display3.copyWith(color: Colors.white), + headlineLarge: AppTypography.heading1.copyWith(color: Colors.white), + headlineMedium: AppTypography.heading2.copyWith(color: Colors.white), + headlineSmall: AppTypography.heading3.copyWith(color: Colors.white), + titleLarge: AppTypography.heading4.copyWith(color: Colors.white), + titleMedium: AppTypography.heading5.copyWith(color: Colors.white), + titleSmall: AppTypography.heading6.copyWith(color: Colors.white), + bodyLarge: AppTypography.bodyLightL.copyWith(color: Colors.white), + bodyMedium: AppTypography.bodyLightM.copyWith(color: Colors.white), + bodySmall: AppTypography.bodyLightS.copyWith(color: Colors.white), + labelLarge: AppTypography.button.copyWith(color: Colors.white), + ); +} + +extension AppThemeExtension on ThemeData { + AppColorsExtension get appColors => + extension() ?? ThemeState._lightAppColors; + + AppTextThemeExtension get appTextTheme => + extension() ?? ThemeState._lightTextTheme; +} + +extension ThemeGetter on BuildContext { + ThemeData get theme => Theme.of(this); +} diff --git a/lib/main.dart b/lib/main.dart index 4cf4ec5..bb0c52e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,4 +1,5 @@ import 'package:concordium_wallet/bootstrap.dart'; +import 'package:concordium_wallet/designsystem/theme/ThemeState.dart'; import 'package:concordium_wallet/screens/routes.dart'; import 'package:concordium_wallet/screens/start/screen.dart'; import 'package:concordium_wallet/state/network.dart'; @@ -6,6 +7,8 @@ import 'package:concordium_wallet/theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'designsystem/theme/AppTheme.dart'; + // TODO: We can probably defer network activation until we hit the landing page. const _initialNetwork = NetworkName.testnet; diff --git a/pubspec.yaml b/pubspec.yaml index 65ae0f3..147aaed 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -103,3 +103,11 @@ flutter: # see https://flutter.dev/custom-fonts/#from-packages assets: - assets/graphics/ + fonts: + - family: IBMPlexSans + fonts: + - asset: assets/fonts/IBMPlexSans-Bold.ttf + - asset: assets/fonts/IBMPlexSans-Light.ttf + - asset: assets/fonts/IBMPlexSans-Medium.ttf + - asset: assets/fonts/IBMPlexSans-Regular.ttf + - asset: assets/fonts/IBMPlexSans-SemiBold.ttf \ No newline at end of file