|
1 | 1 | import 'dart:io';
|
2 | 2 | import 'package:flutter/foundation.dart';
|
3 |
| -import 'package:flutter/widgets.dart'; |
| 3 | +import 'package:flutter/material.dart'; |
| 4 | + |
| 5 | +/// An app-wide [Typography] for Zulip, customized from the Material default. |
| 6 | +/// |
| 7 | +/// Include this in the app-wide [MaterialApp.theme]. |
| 8 | +/// |
| 9 | +/// We expect these text styles to be the basis of all the styles chosen by the |
| 10 | +/// Material library's widgets, such as the default styling of |
| 11 | +/// an [AppBar]'s title, of an [ElevatedButton]'s label, and so on. |
| 12 | +/// |
| 13 | +/// Applies [kDefaultFontFamily] and [kDefaultFontFamilyFallback], |
| 14 | +/// being faithful to the Material-default font weights |
| 15 | +/// by running them through [weightVariableTextStyle]. |
| 16 | +/// (That is needed because [kDefaultFontFamily] is a variable-weight font). |
| 17 | +/// |
| 18 | +/// When building on top of these [TextStyles], callers that wish to specify |
| 19 | +/// a different font weight are still responsible for reprocessing the style |
| 20 | +/// with [weightVariableTextStyle] before passing it to a [Text]. |
| 21 | +/// (Widgets in the Material library won't do this; they aren't yet equipped |
| 22 | +/// to set font weights on variable-weight fonts. If this causes visible bugs, |
| 23 | +/// we should investigate and fix, but such bugs should become less likely as |
| 24 | +/// we transition from Material's widgets to our own bespoke ones.) |
| 25 | +Typography zulipTypography(BuildContext context) { |
| 26 | + final typography = Theme.of(context).typography; |
| 27 | + |
| 28 | + Typography result = typography.copyWith( |
| 29 | + black: typography.black.apply( |
| 30 | + fontFamily: kDefaultFontFamily, |
| 31 | + fontFamilyFallback: defaultFontFamilyFallback), |
| 32 | + white: typography.white.apply( |
| 33 | + fontFamily: kDefaultFontFamily, |
| 34 | + fontFamilyFallback: defaultFontFamilyFallback), |
| 35 | + |
| 36 | + dense: _weightVariableTextTheme(context, typography.dense), |
| 37 | + englishLike: _weightVariableTextTheme(context, typography.englishLike), |
| 38 | + tall: _weightVariableTextTheme(context, typography.tall), |
| 39 | + ); |
| 40 | + |
| 41 | + assert(() { |
| 42 | + // Set [TextStyle.debugLabel] for all styles, like: |
| 43 | + // "zulipTypography black titleMedium" |
| 44 | + |
| 45 | + mkAddLabel(String debugTextThemeLabel) |
| 46 | + => (TextStyle? maybeInputStyle, String debugStyleLabel) |
| 47 | + => maybeInputStyle?.copyWith(debugLabel: '$debugTextThemeLabel $debugStyleLabel'); |
| 48 | + |
| 49 | + result = result.copyWith( |
| 50 | + black: _convertTextTheme(result.black, mkAddLabel('zulipTypography black')), |
| 51 | + white: _convertTextTheme(result.white, mkAddLabel('zulipTypography white')), |
| 52 | + englishLike: _convertTextTheme(result.englishLike, mkAddLabel('zulipTypography englishLike')), |
| 53 | + dense: _convertTextTheme(result.dense, mkAddLabel('zulipTypography dense')), |
| 54 | + tall: _convertTextTheme(result.tall, mkAddLabel('zulipTypography tall')), |
| 55 | + ); |
| 56 | + return true; |
| 57 | + }()); |
| 58 | + |
| 59 | + return result; |
| 60 | +} |
| 61 | + |
| 62 | +/// Convert a geometry [TextTheme] to one that works with "wght"-variable fonts. |
| 63 | +/// |
| 64 | +/// A "geometry [TextTheme]" is a [TextTheme] that's meant to specify |
| 65 | +/// font weight and other parameters about shape, size, distance, etc. |
| 66 | +/// See [Typography]. |
| 67 | +/// |
| 68 | +/// This looks at each of the [TextStyle]s found on the input [TextTheme] |
| 69 | +/// (such as [TextTheme.bodyMedium]), |
| 70 | +/// and uses [weightVariableTextStyle] to adjust the [TextStyle]. |
| 71 | +/// Fields that are null in the input [TextTheme] remain null in the output. |
| 72 | +/// |
| 73 | +/// For each input [TextStyle], the `wght` value passed |
| 74 | +/// to [weightVariableTextStyle] is based on the input's [TextStyle.fontWeight]. |
| 75 | +/// A null [TextStyle.fontWeight] is interpreted as the normal font weight. |
| 76 | +TextTheme _weightVariableTextTheme(BuildContext context, TextTheme input) { |
| 77 | + TextStyle? convert(TextStyle? maybeInputStyle, _) { |
| 78 | + if (maybeInputStyle == null) { |
| 79 | + return null; |
| 80 | + } |
| 81 | + final inputFontWeight = maybeInputStyle.fontWeight; |
| 82 | + return maybeInputStyle.merge(weightVariableTextStyle(context, |
| 83 | + wght: inputFontWeight != null |
| 84 | + ? wghtFromFontWeight(inputFontWeight) |
| 85 | + : null)); |
| 86 | + } |
| 87 | + |
| 88 | + return _convertTextTheme(input, convert); |
| 89 | +} |
| 90 | + |
| 91 | +TextTheme _convertTextTheme( |
| 92 | + TextTheme input, |
| 93 | + TextStyle? Function(TextStyle?, String debugStyleLabel) converter, |
| 94 | +) => TextTheme( |
| 95 | + displayLarge: converter(input.displayLarge, 'displayLarge'), |
| 96 | + displayMedium: converter(input.displayMedium, 'displayMedium'), |
| 97 | + displaySmall: converter(input.displaySmall, 'displaySmall'), |
| 98 | + headlineLarge: converter(input.headlineLarge, 'headlineLarge'), |
| 99 | + headlineMedium: converter(input.headlineMedium, 'headlineMedium'), |
| 100 | + headlineSmall: converter(input.headlineSmall, 'headlineSmall'), |
| 101 | + titleLarge: converter(input.titleLarge, 'titleLarge'), |
| 102 | + titleMedium: converter(input.titleMedium, 'titleMedium'), |
| 103 | + titleSmall: converter(input.titleSmall, 'titleSmall'), |
| 104 | + bodyLarge: converter(input.bodyLarge, 'bodyLarge'), |
| 105 | + bodyMedium: converter(input.bodyMedium, 'bodyMedium'), |
| 106 | + bodySmall: converter(input.bodySmall, 'bodySmall'), |
| 107 | + labelLarge: converter(input.labelLarge, 'labelLarge'), |
| 108 | + labelMedium: converter(input.labelMedium, 'labelMedium'), |
| 109 | + labelSmall: converter(input.labelSmall, 'labelSmall'), |
| 110 | +); |
4 | 111 |
|
5 | 112 | /// The [TextStyle.fontFamily] to use in most of the app.
|
6 | 113 | ///
|
@@ -145,3 +252,12 @@ FontWeight clampVariableFontWeight(double wght) {
|
145 | 252 | }
|
146 | 253 | }
|
147 | 254 | }
|
| 255 | + |
| 256 | +/// A good guess at a font's "wght" value to match a given [FontWeight]. |
| 257 | +/// |
| 258 | +/// Returns [FontWeight.value] as a double. |
| 259 | +/// |
| 260 | +/// This might not be exactly where the font designer would land on their |
| 261 | +/// font's own custom-defined "wght" axis. But it's a great guess, |
| 262 | +/// at least without knowledge of the particular font. |
| 263 | +double wghtFromFontWeight(FontWeight fontWeight) => fontWeight.value.toDouble(); |
0 commit comments