Skip to content

Commit 4d50910

Browse files
Adds support for applying delta/factor transformations for TextTheme height, letter and word spacing (#158103)
Closes flutter/flutter#158102 The text theme has the `apply` method which does bulk operations on multiple text styles. It supports delta/factor ajustements for font size. This is very helpful for changing all the font sizes at once using a ratio or a simple delta. This PR add support for height, letter spacing and and word spacing too. ### Why is this so useful? Adjusting these in bulk is really helpful for using custom fonts. The Material font comes which its own default text styes and they're usually great. But many times they need to be nudged tighter. ### Doc Comment `apply` has no doc comments for `fontSizeFactor`/`fontSizeDelta` so i did not add any for the new `letterSpacingFactor`, `letterSpacingDelta`... either. If we want to add it, I'll do it. ### Tests Done! ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [ ] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --------- Co-authored-by: Tong Mu <dkwingsmt@users.noreply.github.com>
1 parent bb306c5 commit 4d50910

2 files changed

Lines changed: 245 additions & 0 deletions

File tree

packages/flutter/lib/src/material/text_theme.dart

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,12 @@ class TextTheme with Diagnosticable {
396396
String? package,
397397
double fontSizeFactor = 1.0,
398398
double fontSizeDelta = 0.0,
399+
double letterSpacingFactor = 1.0,
400+
double letterSpacingDelta = 0.0,
401+
double wordSpacingFactor = 1.0,
402+
double wordSpacingDelta = 0.0,
403+
double heightFactor = 1.0,
404+
double heightDelta = 0.0,
399405
Color? displayColor,
400406
Color? bodyColor,
401407
TextDecoration? decoration,
@@ -412,6 +418,12 @@ class TextTheme with Diagnosticable {
412418
fontFamilyFallback: fontFamilyFallback,
413419
fontSizeFactor: fontSizeFactor,
414420
fontSizeDelta: fontSizeDelta,
421+
letterSpacingDelta: letterSpacingDelta,
422+
letterSpacingFactor: letterSpacingFactor,
423+
wordSpacingDelta: wordSpacingDelta,
424+
wordSpacingFactor: wordSpacingFactor,
425+
heightFactor: heightFactor,
426+
heightDelta: heightDelta,
415427
package: package,
416428
),
417429
displayMedium: displayMedium?.apply(
@@ -423,6 +435,12 @@ class TextTheme with Diagnosticable {
423435
fontFamilyFallback: fontFamilyFallback,
424436
fontSizeFactor: fontSizeFactor,
425437
fontSizeDelta: fontSizeDelta,
438+
letterSpacingDelta: letterSpacingDelta,
439+
letterSpacingFactor: letterSpacingFactor,
440+
wordSpacingDelta: wordSpacingDelta,
441+
wordSpacingFactor: wordSpacingFactor,
442+
heightFactor: heightFactor,
443+
heightDelta: heightDelta,
426444
package: package,
427445
),
428446
displaySmall: displaySmall?.apply(
@@ -434,6 +452,12 @@ class TextTheme with Diagnosticable {
434452
fontFamilyFallback: fontFamilyFallback,
435453
fontSizeFactor: fontSizeFactor,
436454
fontSizeDelta: fontSizeDelta,
455+
letterSpacingDelta: letterSpacingDelta,
456+
letterSpacingFactor: letterSpacingFactor,
457+
wordSpacingDelta: wordSpacingDelta,
458+
wordSpacingFactor: wordSpacingFactor,
459+
heightFactor: heightFactor,
460+
heightDelta: heightDelta,
437461
package: package,
438462
),
439463
headlineLarge: headlineLarge?.apply(
@@ -445,6 +469,12 @@ class TextTheme with Diagnosticable {
445469
fontFamilyFallback: fontFamilyFallback,
446470
fontSizeFactor: fontSizeFactor,
447471
fontSizeDelta: fontSizeDelta,
472+
letterSpacingDelta: letterSpacingDelta,
473+
letterSpacingFactor: letterSpacingFactor,
474+
wordSpacingDelta: wordSpacingDelta,
475+
wordSpacingFactor: wordSpacingFactor,
476+
heightFactor: heightFactor,
477+
heightDelta: heightDelta,
448478
package: package,
449479
),
450480
headlineMedium: headlineMedium?.apply(
@@ -456,6 +486,12 @@ class TextTheme with Diagnosticable {
456486
fontFamilyFallback: fontFamilyFallback,
457487
fontSizeFactor: fontSizeFactor,
458488
fontSizeDelta: fontSizeDelta,
489+
letterSpacingDelta: letterSpacingDelta,
490+
letterSpacingFactor: letterSpacingFactor,
491+
wordSpacingDelta: wordSpacingDelta,
492+
wordSpacingFactor: wordSpacingFactor,
493+
heightFactor: heightFactor,
494+
heightDelta: heightDelta,
459495
package: package,
460496
),
461497
headlineSmall: headlineSmall?.apply(
@@ -467,6 +503,12 @@ class TextTheme with Diagnosticable {
467503
fontFamilyFallback: fontFamilyFallback,
468504
fontSizeFactor: fontSizeFactor,
469505
fontSizeDelta: fontSizeDelta,
506+
letterSpacingDelta: letterSpacingDelta,
507+
letterSpacingFactor: letterSpacingFactor,
508+
wordSpacingDelta: wordSpacingDelta,
509+
wordSpacingFactor: wordSpacingFactor,
510+
heightFactor: heightFactor,
511+
heightDelta: heightDelta,
470512
package: package,
471513
),
472514
titleLarge: titleLarge?.apply(
@@ -478,6 +520,12 @@ class TextTheme with Diagnosticable {
478520
fontFamilyFallback: fontFamilyFallback,
479521
fontSizeFactor: fontSizeFactor,
480522
fontSizeDelta: fontSizeDelta,
523+
letterSpacingDelta: letterSpacingDelta,
524+
letterSpacingFactor: letterSpacingFactor,
525+
wordSpacingDelta: wordSpacingDelta,
526+
wordSpacingFactor: wordSpacingFactor,
527+
heightFactor: heightFactor,
528+
heightDelta: heightDelta,
481529
package: package,
482530
),
483531
titleMedium: titleMedium?.apply(
@@ -489,6 +537,12 @@ class TextTheme with Diagnosticable {
489537
fontFamilyFallback: fontFamilyFallback,
490538
fontSizeFactor: fontSizeFactor,
491539
fontSizeDelta: fontSizeDelta,
540+
letterSpacingDelta: letterSpacingDelta,
541+
letterSpacingFactor: letterSpacingFactor,
542+
wordSpacingDelta: wordSpacingDelta,
543+
wordSpacingFactor: wordSpacingFactor,
544+
heightFactor: heightFactor,
545+
heightDelta: heightDelta,
492546
package: package,
493547
),
494548
titleSmall: titleSmall?.apply(
@@ -500,6 +554,12 @@ class TextTheme with Diagnosticable {
500554
fontFamilyFallback: fontFamilyFallback,
501555
fontSizeFactor: fontSizeFactor,
502556
fontSizeDelta: fontSizeDelta,
557+
letterSpacingDelta: letterSpacingDelta,
558+
letterSpacingFactor: letterSpacingFactor,
559+
wordSpacingDelta: wordSpacingDelta,
560+
wordSpacingFactor: wordSpacingFactor,
561+
heightFactor: heightFactor,
562+
heightDelta: heightDelta,
503563
package: package,
504564
),
505565
bodyLarge: bodyLarge?.apply(
@@ -511,6 +571,12 @@ class TextTheme with Diagnosticable {
511571
fontFamilyFallback: fontFamilyFallback,
512572
fontSizeFactor: fontSizeFactor,
513573
fontSizeDelta: fontSizeDelta,
574+
letterSpacingDelta: letterSpacingDelta,
575+
letterSpacingFactor: letterSpacingFactor,
576+
wordSpacingDelta: wordSpacingDelta,
577+
wordSpacingFactor: wordSpacingFactor,
578+
heightFactor: heightFactor,
579+
heightDelta: heightDelta,
514580
package: package,
515581
),
516582
bodyMedium: bodyMedium?.apply(
@@ -522,6 +588,12 @@ class TextTheme with Diagnosticable {
522588
fontFamilyFallback: fontFamilyFallback,
523589
fontSizeFactor: fontSizeFactor,
524590
fontSizeDelta: fontSizeDelta,
591+
letterSpacingDelta: letterSpacingDelta,
592+
letterSpacingFactor: letterSpacingFactor,
593+
wordSpacingDelta: wordSpacingDelta,
594+
wordSpacingFactor: wordSpacingFactor,
595+
heightFactor: heightFactor,
596+
heightDelta: heightDelta,
525597
package: package,
526598
),
527599
bodySmall: bodySmall?.apply(
@@ -533,6 +605,12 @@ class TextTheme with Diagnosticable {
533605
fontFamilyFallback: fontFamilyFallback,
534606
fontSizeFactor: fontSizeFactor,
535607
fontSizeDelta: fontSizeDelta,
608+
letterSpacingDelta: letterSpacingDelta,
609+
letterSpacingFactor: letterSpacingFactor,
610+
wordSpacingDelta: wordSpacingDelta,
611+
wordSpacingFactor: wordSpacingFactor,
612+
heightFactor: heightFactor,
613+
heightDelta: heightDelta,
536614
package: package,
537615
),
538616
labelLarge: labelLarge?.apply(
@@ -544,6 +622,12 @@ class TextTheme with Diagnosticable {
544622
fontFamilyFallback: fontFamilyFallback,
545623
fontSizeFactor: fontSizeFactor,
546624
fontSizeDelta: fontSizeDelta,
625+
letterSpacingDelta: letterSpacingDelta,
626+
letterSpacingFactor: letterSpacingFactor,
627+
wordSpacingDelta: wordSpacingDelta,
628+
wordSpacingFactor: wordSpacingFactor,
629+
heightFactor: heightFactor,
630+
heightDelta: heightDelta,
547631
package: package,
548632
),
549633
labelMedium: labelMedium?.apply(
@@ -555,6 +639,12 @@ class TextTheme with Diagnosticable {
555639
fontFamilyFallback: fontFamilyFallback,
556640
fontSizeFactor: fontSizeFactor,
557641
fontSizeDelta: fontSizeDelta,
642+
letterSpacingDelta: letterSpacingDelta,
643+
letterSpacingFactor: letterSpacingFactor,
644+
wordSpacingDelta: wordSpacingDelta,
645+
wordSpacingFactor: wordSpacingFactor,
646+
heightFactor: heightFactor,
647+
heightDelta: heightDelta,
558648
package: package,
559649
),
560650
labelSmall: labelSmall?.apply(
@@ -566,6 +656,12 @@ class TextTheme with Diagnosticable {
566656
fontFamilyFallback: fontFamilyFallback,
567657
fontSizeFactor: fontSizeFactor,
568658
fontSizeDelta: fontSizeDelta,
659+
letterSpacingDelta: letterSpacingDelta,
660+
letterSpacingFactor: letterSpacingFactor,
661+
wordSpacingDelta: wordSpacingDelta,
662+
wordSpacingFactor: wordSpacingFactor,
663+
heightFactor: heightFactor,
664+
heightDelta: heightDelta,
569665
package: package,
570666
),
571667
);

packages/flutter/test/material/text_theme_test.dart

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,155 @@ void main() {
171171
expect(sizeTheme.labelSmall!.fontSize, baseTheme.labelSmall!.fontSize! * 2.0 + 5.0);
172172
});
173173

174+
test('TextTheme apply letterSpacingFactor letterSpacingDelta', () {
175+
final Typography typography = Typography.material2018();
176+
final TextTheme baseTheme = Typography.englishLike2018.merge(typography.black);
177+
final TextTheme sizeTheme = baseTheme.apply(letterSpacingFactor: 2.0, letterSpacingDelta: 5.0);
178+
179+
expect(
180+
sizeTheme.displayLarge!.letterSpacing,
181+
baseTheme.displayLarge!.letterSpacing! * 2.0 + 5.0,
182+
);
183+
expect(
184+
sizeTheme.displayMedium!.letterSpacing,
185+
baseTheme.displayMedium!.letterSpacing! * 2.0 + 5.0,
186+
);
187+
expect(
188+
sizeTheme.displaySmall!.letterSpacing,
189+
baseTheme.displaySmall!.letterSpacing! * 2.0 + 5.0,
190+
);
191+
expect(
192+
sizeTheme.headlineLarge!.letterSpacing,
193+
baseTheme.headlineLarge!.letterSpacing! * 2.0 + 5.0,
194+
);
195+
expect(
196+
sizeTheme.headlineMedium!.letterSpacing,
197+
baseTheme.headlineMedium!.letterSpacing! * 2.0 + 5.0,
198+
);
199+
expect(
200+
sizeTheme.headlineSmall!.letterSpacing,
201+
baseTheme.headlineSmall!.letterSpacing! * 2.0 + 5.0,
202+
);
203+
expect(sizeTheme.titleLarge!.letterSpacing, baseTheme.titleLarge!.letterSpacing! * 2.0 + 5.0);
204+
expect(sizeTheme.titleMedium!.letterSpacing, baseTheme.titleMedium!.letterSpacing! * 2.0 + 5.0);
205+
expect(sizeTheme.titleSmall!.letterSpacing, baseTheme.titleSmall!.letterSpacing! * 2.0 + 5.0);
206+
expect(sizeTheme.bodyLarge!.letterSpacing, baseTheme.bodyLarge!.letterSpacing! * 2.0 + 5.0);
207+
expect(sizeTheme.bodyMedium!.letterSpacing, baseTheme.bodyMedium!.letterSpacing! * 2.0 + 5.0);
208+
expect(sizeTheme.bodySmall!.letterSpacing, baseTheme.bodySmall!.letterSpacing! * 2.0 + 5.0);
209+
expect(sizeTheme.labelLarge!.letterSpacing, baseTheme.labelLarge!.letterSpacing! * 2.0 + 5.0);
210+
expect(sizeTheme.labelMedium!.letterSpacing, baseTheme.labelMedium!.letterSpacing! * 2.0 + 5.0);
211+
expect(sizeTheme.labelSmall!.letterSpacing, baseTheme.labelSmall!.letterSpacing! * 2.0 + 5.0);
212+
});
213+
214+
test('TextTheme apply wordSpacingFactor wordSpacingDelta', () {
215+
final Typography typography = Typography.material2018();
216+
final TextTheme baseTheme = Typography.englishLike2018.merge(typography.black);
217+
final TextTheme baseThemeWithWordSpacing = baseTheme.copyWith(
218+
displayLarge: baseTheme.displayLarge!.copyWith(wordSpacing: 1.0),
219+
displayMedium: baseTheme.displayMedium!.copyWith(wordSpacing: 1.0),
220+
displaySmall: baseTheme.displaySmall!.copyWith(wordSpacing: 1.0),
221+
headlineLarge: baseTheme.headlineLarge!.copyWith(wordSpacing: 1.0),
222+
headlineMedium: baseTheme.headlineMedium!.copyWith(wordSpacing: 1.0),
223+
headlineSmall: baseTheme.headlineSmall!.copyWith(wordSpacing: 1.0),
224+
titleLarge: baseTheme.titleLarge!.copyWith(wordSpacing: 1.0),
225+
titleMedium: baseTheme.titleMedium!.copyWith(wordSpacing: 1.0),
226+
titleSmall: baseTheme.titleSmall!.copyWith(wordSpacing: 1.0),
227+
bodyLarge: baseTheme.bodyLarge!.copyWith(wordSpacing: 1.0),
228+
bodyMedium: baseTheme.bodyMedium!.copyWith(wordSpacing: 1.0),
229+
bodySmall: baseTheme.bodySmall!.copyWith(wordSpacing: 1.0),
230+
labelLarge: baseTheme.labelLarge!.copyWith(wordSpacing: 1.0),
231+
labelMedium: baseTheme.labelMedium!.copyWith(wordSpacing: 1.0),
232+
labelSmall: baseTheme.labelSmall!.copyWith(wordSpacing: 1.0),
233+
);
234+
final TextTheme sizeTheme = baseThemeWithWordSpacing.apply(
235+
wordSpacingFactor: 2.0,
236+
wordSpacingDelta: 5.0,
237+
);
238+
239+
expect(
240+
sizeTheme.displayLarge!.wordSpacing,
241+
baseThemeWithWordSpacing.displayLarge!.wordSpacing! * 2.0 + 5.0,
242+
);
243+
expect(
244+
sizeTheme.displayMedium!.wordSpacing,
245+
baseThemeWithWordSpacing.displayMedium!.wordSpacing! * 2.0 + 5.0,
246+
);
247+
expect(
248+
sizeTheme.displaySmall!.wordSpacing,
249+
baseThemeWithWordSpacing.displaySmall!.wordSpacing! * 2.0 + 5.0,
250+
);
251+
expect(
252+
sizeTheme.headlineLarge!.wordSpacing,
253+
baseThemeWithWordSpacing.headlineLarge!.wordSpacing! * 2.0 + 5.0,
254+
);
255+
expect(
256+
sizeTheme.headlineMedium!.wordSpacing,
257+
baseThemeWithWordSpacing.headlineMedium!.wordSpacing! * 2.0 + 5.0,
258+
);
259+
expect(
260+
sizeTheme.headlineSmall!.wordSpacing,
261+
baseThemeWithWordSpacing.headlineSmall!.wordSpacing! * 2.0 + 5.0,
262+
);
263+
expect(
264+
sizeTheme.titleLarge!.wordSpacing,
265+
baseThemeWithWordSpacing.titleLarge!.wordSpacing! * 2.0 + 5.0,
266+
);
267+
expect(
268+
sizeTheme.titleMedium!.wordSpacing,
269+
baseThemeWithWordSpacing.titleMedium!.wordSpacing! * 2.0 + 5.0,
270+
);
271+
expect(
272+
sizeTheme.titleSmall!.wordSpacing,
273+
baseThemeWithWordSpacing.titleSmall!.wordSpacing! * 2.0 + 5.0,
274+
);
275+
expect(
276+
sizeTheme.bodyLarge!.wordSpacing,
277+
baseThemeWithWordSpacing.bodyLarge!.wordSpacing! * 2.0 + 5.0,
278+
);
279+
expect(
280+
sizeTheme.bodyMedium!.wordSpacing,
281+
baseThemeWithWordSpacing.bodyMedium!.wordSpacing! * 2.0 + 5.0,
282+
);
283+
expect(
284+
sizeTheme.bodySmall!.wordSpacing,
285+
baseThemeWithWordSpacing.bodySmall!.wordSpacing! * 2.0 + 5.0,
286+
);
287+
expect(
288+
sizeTheme.labelLarge!.wordSpacing,
289+
baseThemeWithWordSpacing.labelLarge!.wordSpacing! * 2.0 + 5.0,
290+
);
291+
expect(
292+
sizeTheme.labelMedium!.wordSpacing,
293+
baseThemeWithWordSpacing.labelMedium!.wordSpacing! * 2.0 + 5.0,
294+
);
295+
expect(
296+
sizeTheme.labelSmall!.wordSpacing,
297+
baseThemeWithWordSpacing.labelSmall!.wordSpacing! * 2.0 + 5.0,
298+
);
299+
});
300+
301+
test('TextTheme apply heightFactor heightDelta', () {
302+
final Typography typography = Typography.material2021();
303+
final TextTheme baseTheme = Typography.englishLike2021.merge(typography.black);
304+
final TextTheme sizeTheme = baseTheme.apply(heightFactor: 2.0, heightDelta: 5.0);
305+
306+
expect(sizeTheme.displayLarge!.height, baseTheme.displayLarge!.height! * 2.0 + 5.0);
307+
expect(sizeTheme.displayMedium!.height, baseTheme.displayMedium!.height! * 2.0 + 5.0);
308+
expect(sizeTheme.displaySmall!.height, baseTheme.displaySmall!.height! * 2.0 + 5.0);
309+
expect(sizeTheme.headlineLarge!.height, baseTheme.headlineLarge!.height! * 2.0 + 5.0);
310+
expect(sizeTheme.headlineMedium!.height, baseTheme.headlineMedium!.height! * 2.0 + 5.0);
311+
expect(sizeTheme.headlineSmall!.height, baseTheme.headlineSmall!.height! * 2.0 + 5.0);
312+
expect(sizeTheme.titleLarge!.height, baseTheme.titleLarge!.height! * 2.0 + 5.0);
313+
expect(sizeTheme.titleMedium!.height, baseTheme.titleMedium!.height! * 2.0 + 5.0);
314+
expect(sizeTheme.titleSmall!.height, baseTheme.titleSmall!.height! * 2.0 + 5.0);
315+
expect(sizeTheme.bodyLarge!.height, baseTheme.bodyLarge!.height! * 2.0 + 5.0);
316+
expect(sizeTheme.bodyMedium!.height, baseTheme.bodyMedium!.height! * 2.0 + 5.0);
317+
expect(sizeTheme.bodySmall!.height, baseTheme.bodySmall!.height! * 2.0 + 5.0);
318+
expect(sizeTheme.labelLarge!.height, baseTheme.labelLarge!.height! * 2.0 + 5.0);
319+
expect(sizeTheme.labelMedium!.height, baseTheme.labelMedium!.height! * 2.0 + 5.0);
320+
expect(sizeTheme.labelSmall!.height, baseTheme.labelSmall!.height! * 2.0 + 5.0);
321+
});
322+
174323
test('TextTheme lerp with second parameter null', () {
175324
final TextTheme theme = Typography.material2018().black;
176325
final TextTheme lerped = TextTheme.lerp(theme, null, 0.25);

0 commit comments

Comments
 (0)