Flutter: Simplify Platform & Screen Size Detection

In the never-ending quest to reduce boilerplate and DRY up our code in Flutter, we have noticed that using the MediaQuery class can be a bit cumbersome, and it’s also missing a couple of key pieces of information.

The issues we see are:

  • MediaQuery.of(context) is a bit verbose on its face
  • Checking for orientation especially is too long:
    bool isLandscape = MediaQuery.of(context).orientation == Orientation.landscape
  • There is no diagonal size parameter, so you can’t easily get the true screen size of the device, helpful for determining your form factor
  • There is no way to get the size in inches, which can be useful when thinking about breakpoints (for most people, 4.5″ is easier to picture, than 720 logical pixels)

To that end, we have small Screen helper class, that we use across all our new projects:

class Screen {
  static double get _ppi => (Platform.isAndroid || Platform.isIOS)? 150 : 96;
  static bool isLandscape(BuildContext c) => MediaQuery.of(c).orientation == Orientation.landscape;
  //PIXELS
  static Size size(BuildContext c) => MediaQuery.of(c).size;
  static double width(BuildContext c) => size(c).width;
  static double height(BuildContext c) => size(c).height;
  static double diagonal(BuildContext c) {
    Size s = size(c);
    return sqrt((s.width * s.width) + (s.height * s.height));
  }
  //INCHES
  static Size inches(BuildContext c) {
    Size pxSize = size(c);
    return Size(pxSize.width / _ppi, pxSize.height/ _ppi);
  }
  static double widthInches(BuildContext c) => inches(c).width;
  static double heightInches(BuildContext c) => inches(c).height;
  static double diagonalInches(BuildContext c) => diagonal(c) / _ppi;
}

This exposes just a bunch of syntactic sugar to help you clean up your code:

bool isLandscape = Screen.isLandscape(context)
bool isLargePhone = Screen.diagonal(context) > 720;
bool isTablet = Screen.diagonalInches(context) >= 7;
bool isNarrow = Screen.widthInches(context) < 3.5;

Device Detection

Another area that can be a bit cumbersome is the Platform detection. Building a truly universal app usually means you need to know which underlying platform you’re on, so you can make some high level decisions.

Currently if you want to check if you’re on desktop, you need to use the Platform class, and would do something like:

bool isDesktop = Platform.isWindows || Platform.isLinux || Platform.isMacOS;

To check if you’re on mobile it’s:

bool isMobile = Platform.isAndroid || Platform.isIOS;

And somewhat out of left field, is web, just using a global constant, defined in the foundations.dart class:

bool isWeb = kIsWeb;

So, that is pretty verbose, and also inconsistent. Clearly less than ideal. To help smooth this over, we use a Device helper class that defines a few extra functions, and then proxies through the core Platform API as well, just to keep everything in one import:

class Device {
  static bool get isDesktop => !isWeb &amp;&amp; (isWindows || isLinux || isMacOS);
  static bool get isMobile => isAndroid || isIOS;
  static bool get isWeb => kIsWeb;
 
  static bool get isWindows => Platform.isWindows;
  static bool get isLinux => Platform.isLinux;
  static bool get isMacOS => Platform.isMacOS;
  static bool get isAndroid => Platform.isAndroid;
  static bool get isFuchsia => Platform.isFuchsia;
  static bool get isIOS => Platform.isIOS;
}

Now everything has consolidated behavior and is much more succinct:

bool isDesktop = Device.isDesktop;
bool isWeb = Device.isWeb
bool isMobile = Device.isMobile;

We hope you find these snippets useful. Until next time!

NOTE: Due to current restrictions with Web builds, the above scripts will not work on Web as they both relies on the dart.io package which can not be imported on that platform. We hope that this is just temporary and Flutter team will address this before Web leaves beta. In the meantime, we are working on a Universal Platform Detection plugin that should be coming soon!

shawn.blais

Shawn has worked as programmer and product designer for over 20 years, shipping several games on Steam and Playstation and dozens of apps on mobile. He has extensive programming experience with Dart, C#, ActionScript, SQL, PHP and Javascript and a deep proficiency with motion graphics and UX design. Shawn is currently the Technical Director for gskinner.

@tree_fortress

7 Comments

  1. Jonny Hinojosa March 5, 2020 at 7:19am

    Typo on line 14 of Screen :^)

    How close to physical size in inches do you expect the inches values to be?

    Thanks for sharing.
    Jonny

  2. Ah, thanks for the heads up! From our testing so far, it’s extremely close, but we still need to test on a wider variety of devices to say for sure.

  3. Jonny Hinojosa March 5, 2020 at 7:53am

    Thanks, I asked because my note10+ is 6.8 and diagonalInches says 9.869.

    Thanks again for sharing.

  4. Interesting, thanks for the data. We’ve measure on Desktop and Web and it’s spot on, but we’ll look into it some more. I think the docs may actually be wrong when they state there’s 96px/inch: https://api.flutter.dev/flutter/dart-ui/Window/devicePixelRatio.html

  5. Based on this issue, we may have to do a platform check, and use 96 for desktop/web, and 150 for devices. Oh Flutter.

  6. Jonny Hinojosa March 5, 2020 at 9:17am

    yes, i believe 150 is the correct number for mobile.

  7. Jonny Hinojosa March 5, 2020 at 2:48pm

    I apologize if it seems like nit picking but on line 2 of Screen, should “get” precede “_ppi”?

Leave a Reply

Your email address will not be published. Required fields are marked *