Search

Reinhart Previano K.

Do you love to Ctrl-K, Ctrl-/, or / ? Now you can do three of them (>_ )!

No results so far...

11 February 2023

Why text labels on Flutter's MaterialApp looks worse on iOS and macOS?

Reinhart Previano Koentjoro's profile picture

Reinhart Previano Koentjoro (@reinhart)

Published on Q&A #flutter #ios #macos

Share Copy Link Print PDF Embed Share to Email Share to SMS Yahoo! Share to Yahoo! Mail Mastodon Share to Mastodon Share to KakaoStory Messenger Share to Messenger Pocket Share to Pocket Flipboard Share to Flipboard Pinterest Share to Pinterest Reddit Share to Reddit Y Combinator Share to Hacker News Odnoklassniki Share to Odnoklassniki Blogger Share to Blogger Pleroma Share to Pleroma Share to Friendica Share to KakaoTalk 1Artboard 1 copy 2 Share to Snapchat Xing Share to Xing Share to Misskey LINE Share to LINE Evernote Share to Evernote WhatsApp Share to WhatsApp LiveJournal Share to Livejournal Diaspora Share to Diaspora Share to Gmail Threads Share to Threads Threema Share to Threema Share to X Tumblr Share to Tumblr Buffer Share to Buffer LinkedIn Share to LinkedIn Mail.Ru Share to mail.ru VK Share to VKontakte Trello Share to Trello Facebook Share to Facebook Bluesky Share to Bluesky Skype Share to Skype Hatena Bookmark Share to Hatena Bookmark! Share via MastodonShare Telegram Share to Telegram WordPress Share to WordPress.com

Embed

This website supports oEmbed. To quickly use oEmbed, just copy this site's link to your oEmbed-supported apps and websites like WordPress.

Alternatively, copy and paste the HTML code below to embed this post in your website.

($_ )! We have made this thing responsive, but recommend at least 512x512 pixels for best results.
<iframe src="https://reinhart1010.id/blog/2023/02/11/why-text-labels-on-flutters-materialapp-looks-worse-on-ios-and-macos?embed" height="512" width="512" style="border:none;"><a href="{{ $canonical }}">https://reinhart1010.id/blog/2023/02/11/why-text-labels-on-flutters-materialapp-looks-worse-on-ios-and-macos</a></iframe>
Preview
Cover image for Why text labels on Flutter's MaterialApp looks worse on iOS and macOS?

There's a long-standing bug on Flutter that, when you decide to use MaterialApp on your project, the letters on the system font for iOS and macOS appears a little bit sparsed.

I've taken my time to investigate this issue, and found two interesting reasons behind the issue.

First, I think the changes in #41101 (the Flutter Pull Request that is blamed to cause this issue) should not be the one to blame. In fact, they have rendered the iOS' system fonts in the correct way. The major cause behind all of this is the Material Design's default letter spacing/tracking values which contradicts that of Apple. To give some examples:

StyleFont SizeMaterial's letter-spacingApple HIG's letter-spacingDifference
Body Large160.5-0.310.81
Body Medium140.25-0.150.4
Body Small120.400.4

And after adjusting from Material to Apple's letter spacing values, I can successfully turned my Flutter app from this:

to this (with tweaking the headings to use semibold instead):

But there's one more problem: the updated Display and Heading styles are still awkwardly spaced. This is where I learned that Flutter does not consider using SF Pro Display for text sized at 20px and larger, as recommended and implemented by Apple themselves. Overriding the font family is also necessary to fix this spacing issue, so my app can finally look more native than ever:

This is the hack that I used for it. But don't be excited yet because soon I found another issue.

  /// Adjusts font spacing for iOS and macOS devices
  /// See https://github.com/flutter/flutter/issues/43998
  static ThemeData adjustFontSpacing(ThemeData theme) {
    if (kIsWeb || !shouldUseCupertino()) return theme;
    return theme.copyWith(
      textTheme: theme.textTheme.copyWith(
        bodyLarge: theme.textTheme.bodyLarge?.copyWith(letterSpacing: -0.31),
        bodyMedium: theme.textTheme.bodyMedium?.copyWith(letterSpacing: -0.15),
        bodySmall: theme.textTheme.bodySmall?.copyWith(letterSpacing: 0),
        displayLarge: theme.textTheme.displayLarge?.copyWith(
          fontWeight: FontWeight.w600,
          fontFamily: "SF Pro Display",
          letterSpacing: 0.29,
        ),
        displayMedium: theme.textTheme.displayMedium?.copyWith(
          fontWeight: FontWeight.w600,
          fontFamily: "SF Pro Display",
          letterSpacing: 0.35,
        ),
        displaySmall: theme.textTheme.displaySmall?.copyWith(
          fontWeight: FontWeight.w600,
          fontFamily: "SF Pro Display",
          letterSpacing: 0.37,
        ),
        headlineLarge: theme.textTheme.headlineLarge?.copyWith(
          fontWeight: FontWeight.w600,
          fontFamily: "SF Pro Display",
          letterSpacing: 0.41,
        ),
        headlineMedium: theme.textTheme.headlineMedium?.copyWith(
          fontWeight: FontWeight.w600,
          fontFamily: "SF Pro Display",
          letterSpacing: 0.38,
        ),
        headlineSmall: theme.textTheme.headlineSmall?.copyWith(
          fontWeight: FontWeight.w600,
          fontFamily: "SF Pro Display",
          letterSpacing: 0.07,
        ),
        labelLarge: theme.textTheme.labelLarge?.copyWith(letterSpacing: -0.15),
        labelMedium: theme.textTheme.labelMedium?.copyWith(letterSpacing: 0),
        labelSmall: theme.textTheme.labelSmall?.copyWith(letterSpacing: 0.06),
        titleLarge: theme.textTheme.titleLarge?.copyWith(letterSpacing: -0.26),
        titleMedium: theme.textTheme.titleMedium?.copyWith(letterSpacing: -0.31),
        titleSmall: theme.textTheme.titleSmall?.copyWith(letterSpacing: -0.15),
      ),
    );
  }

Someone then said that the fix does not work in iOS and iPadOS apps. And after validating the issue, well, Flutter is still also stuck on another old bug where the "SF Pro Display" font is still resolved as "SF Pro Text" on those platforms.

That means, if your app supports iOS and iPadOS, you should use this hack instead (the above hack can also be used together if you mind detecting whether the platform is macOS or not.

  static ThemeData adjustFontSpacing(ThemeData theme) {
    if (!shouldUseCupertino()) return theme;
    return theme.copyWith(
      textTheme: theme.textTheme.copyWith(
        bodyLarge: theme.textTheme.bodyLarge?.copyWith(letterSpacing: -0.31),
        bodyMedium: theme.textTheme.bodyMedium?.copyWith(letterSpacing: -0.15),
        bodySmall: theme.textTheme.bodySmall?.copyWith(letterSpacing: 0),
        displayLarge: theme.textTheme.displayLarge?.copyWith(
          fontWeight: FontWeight.w600,
          letterSpacing: 0.29 - 1.5,
        ),
        displayMedium: theme.textTheme.displayMedium?.copyWith(
          fontWeight: FontWeight.w600,
          letterSpacing: 0.35 - 1.5,
        ),
        displaySmall: theme.textTheme.displaySmall?.copyWith(
          fontWeight: FontWeight.w600,
          letterSpacing: 0.37 - 1.5,
        ),
        headlineLarge: theme.textTheme.headlineLarge?.copyWith(
          fontWeight: FontWeight.w600,
          letterSpacing: 0.41 - 1.5,
        ),
        headlineMedium: theme.textTheme.headlineMedium?.copyWith(
          fontWeight: FontWeight.w600,
          letterSpacing: 0.38 - 1.5,
        ),
        headlineSmall: theme.textTheme.headlineSmall?.copyWith(
          fontWeight: FontWeight.w600,
          letterSpacing: 0.07 - 1.5,
        ),
        labelLarge: theme.textTheme.labelLarge?.copyWith(letterSpacing: -0.15),
        labelMedium: theme.textTheme.labelMedium?.copyWith(letterSpacing: 0),
        labelSmall: theme.textTheme.labelSmall?.copyWith(letterSpacing: 0.06),
        titleLarge: theme.textTheme.titleLarge?.copyWith(letterSpacing: -0.26),
        titleMedium:
            theme.textTheme.titleMedium?.copyWith(letterSpacing: -0.31),
        titleSmall: theme.textTheme.titleSmall?.copyWith(letterSpacing: -0.15),
      ),
    );
  }
Share Copy Link Print PDF Embed Share to Email Share to SMS Yahoo! Share to Yahoo! Mail Mastodon Share to Mastodon Share to KakaoStory Messenger Share to Messenger Pocket Share to Pocket Flipboard Share to Flipboard Pinterest Share to Pinterest Reddit Share to Reddit Y Combinator Share to Hacker News Odnoklassniki Share to Odnoklassniki Blogger Share to Blogger Pleroma Share to Pleroma Share to Friendica Share to KakaoTalk 1Artboard 1 copy 2 Share to Snapchat Xing Share to Xing Share to Misskey LINE Share to LINE Evernote Share to Evernote WhatsApp Share to WhatsApp LiveJournal Share to Livejournal Diaspora Share to Diaspora Share to Gmail Threads Share to Threads Threema Share to Threema Share to X Tumblr Share to Tumblr Buffer Share to Buffer LinkedIn Share to LinkedIn Mail.Ru Share to mail.ru VK Share to VKontakte Trello Share to Trello Facebook Share to Facebook Bluesky Share to Bluesky Skype Share to Skype Hatena Bookmark Share to Hatena Bookmark! Share via MastodonShare Telegram Share to Telegram WordPress Share to WordPress.com

Embed

This website supports oEmbed. To quickly use oEmbed, just copy this site's link to your oEmbed-supported apps and websites like WordPress.

Alternatively, copy and paste the HTML code below to embed this post in your website.

($_ )! We have made this thing responsive, but recommend at least 512x512 pixels for best results.
<iframe src="https://reinhart1010.id/blog/2023/02/11/why-text-labels-on-flutters-materialapp-looks-worse-on-ios-and-macos?embed" height="512" width="512" style="border:none;"><a href="{{ $canonical }}">https://reinhart1010.id/blog/2023/02/11/why-text-labels-on-flutters-materialapp-looks-worse-on-ios-and-macos</a></iframe>
Preview

Reinhart Previano Koentjoro
Reinhart Previano Koentjoro
Citra Manggala Dirgantara
Citra Manggala Dirgantara

A Reinhart company

Products

Company