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 && (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!
Originally published at http://blog.gskinner.com on March 4, 2020.