-
Notifications
You must be signed in to change notification settings - Fork 5
Spec: Text ‐ API Design
Matt Carroll edited this page Feb 25, 2024
·
2 revisions
Text Widget API:
class Text extends StatefulWidget {
/// Creates a [Text] widget that automatically attempts to localize the given
/// [localizedTextKey].
const Text(this.localizedTextKey, {
this.tableName,
this.bundle,
this.comment,
this.accessibility = defaultTextAccessibility,
}) : assert(localizedString == null), assert(text == null);
const Text.fromLocalizedString(this.localizedString, {
this.accessibility = defaultTextAccessibility,
}) : assert(localizedTextKey == null), assert(text == null);
/// Creates a [Text] widget that displays the given [text] without any localization.
const Text.verbatim(String text, {
this.accessibility = defaultTextAccessibility,
}) : assert(localizedTextKey == null), assert(localizedString == null);
/// The name of a localized text key, which is used to lookup the actual content that's
/// displayed by this [Text] widget.
///
/// If no corresponding localized content can be found, the [localizedTextKey] value is
/// displayed literally as text content.
final String? localizedTextKey;
/// The localization table name to use for [localizedTextKey] lookup - uses the default
/// table if not provided.
// TODO: how does this relate to Flutter's localization system?
final String? tableName;
/// The bundle containing the strings file for localization - defaults to the main bundle
/// if not provided.
// TODO: how does this relate to Flutter's localization system?
final String? bundle;
/// Contextual information about the combination of the [localizedTextKey] and its
/// localized value.
final String? comment;
/// Fully configured, localized text to display as the text content within this [Text] widget.
final LocalizedStringResource? localizedString;
/// Non-localized text to display as content in this [Text] widget.
final String? text;
/// The accessibility configuration for this [Text] widget.
final TextAccessibility accessibility;
final ShapeStyle foregroundStyle;
final ShapeStyle background;
final SwiftUiFont? font;
final FontWeights? fontWeight;
final TextDecorations? decoration;
final FontDesigns? fontDesign;
final FontWidths? fontWidth;
final TextStyles? style;
final DynamicTextSizes? dynamicTextSize;
final TextScale? scale;
final double? minimumScaleFactor;
final TruncationModes? truncationMode;
final double? kerning;
final bool allowsTightening;
final double? baselineOffset;
final int? lineLimit;
final double? lineSpacing;
final MultilineTextAlignments? multilineTextAlignment;
}
Typography classes and methods, which the Text
widget requires:
class Font extends StatelessWidget {
const Font(this.font);
final SwiftUiFont font;
}
class SwiftUiFont {
static const largeTitle = //
static const title = //
static const title2 = //
static const title3 = //
static const headline = //
static const body = //
static const callout = //
static const subheadline = //
static const footnote = //
static const caption = //
static const caption2 = //
const SwiftUiFont.system({...});
const SwiftUiFont.custom({...});
final String? family;
final FontDesigns? design;
/// The size of the font, in points, which may change based on the system's
/// Dynamic Type settings.
final Points? size; // Note the typedef for Points
/// The font, relative to which this font will apply [size] when accounting
/// for Dynamic Type settings.
///
/// If [relativeTo] is `null`, it defaults to the [body] font.
final SwiftUiFont? relativeTo;
/// The size of the font, in points, which remains the same regardless of
/// the system's Dynamic Type settings.
final Points? fixedSize;
final FontWeightValue? weight;
// TODO:
}
typedef Points = int;
class FontWeight extends StatelessWidget {
const FontWeight(this.weight);
final FontWeights weight;
}
// FIXME: I'm making enums plural so that we can have a modifier widget with the
// same name. I'm not a fan of this, but I haven't found a less bad
// alternative.
enum FontWeights {
ultraLight,
thin,
light,
regular,
medium,
semibold,
bold,
heavy,
black;
/// Returns the font-rendering weight number associated with the given
/// [FontWeights], e.g., `400` for [regular].
int get asWeightNumber;
}
class FontDesign extends StatelessWidget {
const FontDesign(this.design);
final FontDesigns design;
}
enum FontDesigns {
default,
rounded,
serif,
monospaced;
}
class FontWidth extends StatelessWidget {
const FontWidth(this.width);
final FontWidths width;
}
enum FontWidths {
compressed,
condensed,
standard,
expanded;
}
enum DynamicTextSizes {
xSmall,
small,
medium,
large,
xLarge,
xxLarge,
xxxLarge,
ax1,
ax2,
ax3,
ax4,
ax5;
}
class Bold extends StatelessWidget {
const Bold();
}
class Italic extends StatelessWidget {
const Italic();
}
class Monospaced extends StatelessWidget {
const Monospaced();
}
class MonospacedDigit extends StatelessWidget {
const MonospacedDigit();
}
class Uppercase extends StatelessWidget {
const Uppercase();
}
class Lowercase extends StatelessWidget {
const Lowercase();
}
enum TextStyles {
bold,
italic,
monospaced,
monospacedDigit,
uppercase,
lowercase;
}
class Underline extends StatelessWidget {
const Underline();
}
class Strikethrough extends StatelessWidget {
const Strikethrough();
}
enum TextDecorations {
underline,
strikethrough;
}
class TruncationMode extends StatelessWidget {
const TruncationMode(this.mode);
final TruncationModes mode;
}
enum TruncationModes {
tail,
middle,
head;
}
class AllowsTightening extends StatelessWidget {
const AllowsTightening(this.allowsTightening);
final bool allowsTightening;
}
class TextScale extend StatelessWidget {
const TextScale(this.scale);
final TextScales scale;
}
enum TextScales {
default,
secondary;
}
class MinimumScaleFactor extends StatelessWidget {
const MinimumScaleFactor(this.scaleFactor);
final double scaleFactor;
}
class BaselineOffset extends StatelessWidget {
const BaselineOffset(this.offset);
final double offset;
}
class Kerning extends StatelessWidget {
const Kerning(this.kerning);
final double kerning;
}
class Tracking extends StatelessWidget {
const Tracking(this.tracking);
final double tracking;
}
class MultilineTextAlignment extends StatelessWidget {
const MultilineTextAlignment(this.alignment);
final MultilineTextAlignments alignment;
}
enum MultilineTextAlignments {
leading,
center,
trailing;
}
class LineLimit extends StatelessWidget {
const LineLimit(this.lineLimit);
final int? lineLimit;
}
class LineSpacing extends StatelessWidget {
const LineSpacing(this.spacing);
final double spacing;
}
class ForegroundStyle extends StatelessWidget {
const ForegroundStyle(this.style);
final ShapeStyle style;
}
class Background extends StatelessWidget {
const Background(this.style);
final ShapeStyle style;
}
abstract interface class ShapeStyle {
// TODO: we probably need to define this method in terms of a Canvas, along
// with the Paragraph that needs to be painted. TextStyle probably
// doesn't cover enough out-of-the-box capabilities.
TextStyle applyToText();
Widget applyToBackground();
}
class Color implements ShapeStyle {
static const black = Color(0xFF000000);
const Color(this.rgba);
final int rgba;
TextStyle applyToText();
Widget applyToBackground();
}
class LinearGradient implements ShapeStyle {
TextStyle applyToText();
Widget applyToBackground();
}
class AngularGradient implements ShapeStyle {
TextStyle applyToText();
Widget applyToBackground();
}
class ConicGradient implements ShapeStyle {
TextStyle applyToText();
Widget applyToBackground();
}
class EllipticalGradient implements ShapeStyle {
TextStyle applyToText();
Widget applyToBackground();
}
class RadialGradient implements ShapeStyle {
TextStyle applyToText();
Widget applyToBackground();
}
TODO: Add an explicit definition for default system fonts. On iOS, the environment sets a default system font. Typically, the default is San Francisco, which includes different families: SF Pro, SF Pro Rounded, SF Mono, etc. We need this font to change, as expected, when moving from iOS to iPadOS to macOS, etc. We also need the concept of an Apple/iOS default to play nice on other platforms. Also, we have a licensing issue - SF font can only be used on Apple devices.
Classes and methods for localization, which the Text
widget requires.
class LocalizedStringResource {
const LocalizedStringResource(this.key, {
this.defaultValue,
required this.locale,
this.table,
this.bundle,
this.comment,
});
/// The name of a localized text key, which is used to lookup the actual content associated
/// with this [LocalizedStringResource].
///
/// If no corresponding localized content can be found, the [defaultValue] is used. If no
/// [defaultValue] is provided, the [key], itself, is used as the localized value.
final String key;
/// The value to be used for this string when [key] doesn't map to any value in the
/// given localization table.
final String? defaultValue;
/// The locale to use for localization lookup for this string.
final Locale locale;
/// The localization table name to use for [localizedTextKey] lookup - uses the default
/// table if not provided.
// TODO: how does this relate to Flutter's localization system?
final String? table;
/// The bundle containing the strings file for localization - defaults to the main bundle
/// if not provided.
// TODO: how does this relate to Flutter's localization system?
final String? bundle;
/// Contextual information about the combination of the [localizedTextKey] and its
/// localized value.
final String? comment;
}
/// DIRECTION: Create an `AttributedString` class using `AttributedText` for implementation.
Accessibility classes and functions, which the Text
widget requires:
/// The default [TextAccessibility] configuration for a [Text] widget when no accessibility
/// configuration is provided.
const defaultTextAccessibility = TextAccessibility();
class TextAccessibility {
const TextAccessibility({/*...*/});
final double speechAdjustedPitch;
final bool speechAlwaysIncludesPunctuation;
final bool speechSpellsOutCharacters;
final AccessibilityHeading accessibilityHeading;
final Set<AccessibilityTrait> accessibilityTraits;
final String accessibilityLabel;
// DIRECTION: Include equality override to avoid needless widget rebuilds
}