Can we check the device to be a smartphone or a tablet in Flutter?
Asked Answered
F

12

69

I am actually trying to figure out if the app is running on a smartphone or tablet in my Flutter app, but the package device_info can only tell about the device, but not whether the device is a smartphone or tablet.

Is there a way we can do this by checking the size of the device?

Fontainebleau answered 26/3, 2018 at 5:33 Comment(7)
How exactly do you define the difference between smartphone and tablet? Is it about whether the device can make phone calls?Dominiquedominium
hi @GünterZöchbauer yes, the smartphone i mean a device that can make phone calls, as i have an option in my app to call a contact but I want to hide this option if the device cannot make phone calls.Fontainebleau
I don't think this is currently supported directly. You can always call out to a native service like #5197333 using a plugin flutter.io/developing-packages. There are lots of examples github.com/flutter/plugins/tree/master/packagesDominiquedominium
Thanks @GünterZöchbauer I'll give this a try and see if this works, i'm not sure about the iOS but will check that as well as mentioned in the answer thanks for your help.Fontainebleau
Why rule out VOIP calls like Skype? Just because it doesn't have a phone number doesn't mean it can't make calls.Plemmons
thanks @RandalSchwartz yes that's exactly what is happening in my app, but the phone option i am using is exclusively a telephone call so i just wanted to have only phone calls. Anyways, i will try the suggested answer and post the details here. Thanks everyone for their help.Fontainebleau
I know you may have solved the problem...but found this plugin..it seem to have some convenience functions...pub.dev/packages/flutter_device_typeLavish
P
15

Despite iOS having a clear separation between a phone and a tablet, this doesn't happen in Android. You need to base the choice based on screen width.

Check this article to see an example on how to discriminate: Implementing master-detail layouts in Flutter

Plugboard answered 26/3, 2018 at 7:30 Comment(1)
hi thank you for your reply, i will give this example a try and see if that helps. many thanksFontainebleau
R
57

Use:

// The equivalent of the "smallestWidth" qualifier on Android.
var shortestSide = MediaQuery.of(context).size.shortestSide;

// Determine if we should use mobile layout or not, 600 here is
// a common breakpoint for a typical 7-inch tablet.
final bool useMobileLayout = shortestSide < 600;

Copied from Implementing master-detail layouts in Flutter

Thanks to Sergi.

Retrenchment answered 10/1, 2019 at 20:55 Comment(3)
Note: using screen width as a base to determine tablet/handset is official Android way to deal with it, so it also should be good for FlutterBenedetta
this give me 552 on my galaxy a tablet & nexus 7 tab emulatorCholera
Yep! Just changing my answer, but as I used your solution, you have the same issue I had. My answer now uses 550 instead of 600 as the magic number. That's because this value changes depending on the device orientation. I guess it's because it only takes account of the window space, so when the device is in landscape orientation, the status bar and the navigation bar get some space from the shortest side. So, 600 works with common tablets, but when you go to smaller ones like the Nexus 7 2012, it treats it as phone.Handshake
V
55

You can use this if you don't have an access to BuildContext. I took it out from sdk/flutter/packages/flutter/lib/src/widgets/app.dart:1252.

  String getDeviceType() {
    final data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
    return data.size.shortestSide < 600 ? 'phone' :'tablet';
  }
Verboten answered 11/3, 2020 at 12:27 Comment(3)
Yep! Just changing my answer, but as I used your solution, you have the same issue I had. My answer now uses 550 instead of 600 as the magic number. That's because this value changes depending on the device orientation. I guess it's because it only takes account of the window space, so when the device is in landscape orientation, the status bar and the navigation bar get some space from the shortest side. So, 600 works with common tablets, but when you go to smaller ones like the Nexus 7 2012, it treats it as phone.Handshake
fromWindow is deprecated.Sentient
As mentioned some methods are deprecated now. You can use final display = PlatformDispatcher.instance.views.first.display; to get the display object and use predicate display.size.shortestSide / display.devicePixelRatio < 600.Tuatara
N
20

One of the ways is calculated diagonal for screen resolution.

import 'package:flutter/widgets.dart';
import 'dart:math';

class TabletDetector {

  // iPhone 6S 
  // |_ [portrait]
  //    |_ size: 375.0x667.0, pixelRatio: 2.0, pixels: 750.0x1334.0
  //       |_ diagonal: 765.1888655750291
  // |_ [horizontal]
  //    |_ size: 667.0x375.0, pixelRatio: 2.0, pixels: 1334.0x750.0
  //       |_ diagonal: 765.1888655750291

  // iPhone X 
  // |_ [portrait]
  //    |_ size: 375.0x812.0, pixelRatio: 3.0, pixels: 1125.0x2436.0
  //       |_ diagonal: 894.4098613052072
  // |_ [horizontal]
  //    |_ size: 812.0x375.0, pixelRatio: 3.0, pixels: 2436.0x1125.0
  //       |_ diagonal: 894.4098613052072

  // iPhone XS Max 
  // |_ [portrait]
  //    |_ size: 414.0x896.0, pixelRatio: 3.0, pixels: 1242.0x2688.0
  //       |_ diagonal: 987.0217829409845
  // |_ [horizontal]
  //    |_ size: 896.0x414.0, pixelRatio: 3.0, pixels: 2688.0x1242.0
  //       |_ diagonal: 987.0217829409845

  // iPad Pro (9.7-inch) 
  // |_ [portrait]
  //    |_ size: 768.0x1024.0, pixelRatio: 2.0, pixels: 1536.0x2048.0
  //       |_ diagonal: 1280.0
  // |_ [horizontal]
  //    |_ size: 1024.0x768.0, pixelRatio: 2.0, pixels: 2048.0x1536.0
  //       |_ diagonal: 1280.0

  // iPad Pro (10.5-inch) 
  // |_ [portrait]
  //    |_ size: 834.0x1112.0, pixelRatio: 2.0, pixels: 1668.0x2224.0
  //       |_ diagonal: 1390.0
  // |_ [horizontal]
  //    |_ size: 1112.0x834.0, pixelRatio: 2.0, pixels: 2224.0x1668.0
  //       |_ diagonal: 1390.0

  // iPad Pro (12.9-inch) 
  // |_ [portrait]
  //    |_ size: 1024.0x1366.0, pixelRatio: 2.0, pixels: 2048.0x2732.0
  //       |_ diagonal: 1707.2000468603555
  // |_ [horizontal]
  //    |_ size: 1366.0x1024.0, pixelRatio: 2.0, pixels: 2732.0x2048.0
  //       |_ diagonal: 1707.2000468603555

  static bool isTablet(MediaQueryData query) {
    var size = query.size;
    var diagonal = sqrt(
      (size.width * size.width) + 
      (size.height * size.height)
    );

    /*
    print(
      'size: ${size.width}x${size.height}\n'
      'pixelRatio: ${query.devicePixelRatio}\n'
      'pixels: ${size.width * query.devicePixelRatio}x${size.height * query.devicePixelRatio}\n'
      'diagonal: $diagonal'
    );
    */

    var isTablet = diagonal > 1100.0;
    return isTablet;
  }
}
Nuriel answered 24/12, 2018 at 10:20 Comment(3)
I changed this a bit by making the function internal _isTablet with no parameter. Then exposed a no args method that uses MediaQueryData.fromWindow(WidgetsBinding.instance!.window) inside it. Learning here a bit. I'm a convert from Swift land dipping my toes in the Dart pool.Dialectal
Let's consider the Samsung S21, Resolution of 1080x2265 and a pixel ratio of 2.8125. These stats will result in isTablet = true according to your calculations! how should we handle this?Supportable
What do you mean by "is calculated diagonal for screen resolution" (seems incomprehensible)? Is a word, like an article, missing? Diagonal what? The length of the diagonal? Or something else?Wishful
P
15

Despite iOS having a clear separation between a phone and a tablet, this doesn't happen in Android. You need to base the choice based on screen width.

Check this article to see an example on how to discriminate: Implementing master-detail layouts in Flutter

Plugboard answered 26/3, 2018 at 7:30 Comment(1)
hi thank you for your reply, i will give this example a try and see if that helps. many thanksFontainebleau
H
15

Here's the same as in other answers, but returning an enum instead of a bool or a String. As it's more closed, it's easier to use it.

import 'package:flutter/widgets.dart';

enum DeviceType { Phone, Tablet }

DeviceType getDeviceType() {
  final data = MediaQueryData.fromWindow(WidgetsBinding.instance.window);
  return data.size.shortestSide < 550 ? DeviceType.Phone : DeviceType.Tablet;
}

Thanks to Chandler and bakua for the inspiration :·)

Handshake answered 7/11, 2020 at 22:38 Comment(3)
Edit! Now the magic number is 550 instead of 600. That's because this value changes depending on the device orientation. I guess it's because it only takes account of the window space, so when the device is in landscape, the status bar and the navigation bar get some space from the shortest side. So, 600 works with common tablets, but when you go to smaller ones like the Nexus 7 2012, it treats it as a phone. In that case, when the device is in landscape, the shortest side gives 552. So, big up to 550 as magic number!Handshake
I love this solution, but I feel it could be improved further still by converting it to a K getter so that it feels like the other flutter system constants like kIsWeb DeviceType get kDeviceType { ... }Millymilman
@JasonPerfetto that's a good idea too :·)Handshake
B
9

For Android, as Chandler said, you should check the smallest size of the screen, but for iOS, you can identify if it's an iPad with 100% accuracy using the device_info package:

Add in pubspec.yaml:

device_info: ^0.4.2+4

  static Future<bool> isTablet(BuildContext context) async {

    if (Platform.isIOS) {
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
      IosDeviceInfo iosInfo = await deviceInfo.iosInfo;

      return iosInfo.model.toLowerCase() == "ipad";
    } else {
      // The equivalent of the "smallestWidth" qualifier on Android.
      var shortestSide = MediaQuery.of(context).size.shortestSide;

      // Determine if we should use mobile layout or not, 600 here is
      // a common breakpoint for a typical 7-inch tablet.
      return shortestSide > 600;
    }
  }
Benedetta answered 13/7, 2020 at 11:37 Comment(1)
this method probably is more reliable, but it is async and it needs a context, so it is not very useful to init configurations on launchBelemnite
M
8

2023, a Dart 3 solution inspired from bakua's answer, and also this answer:

bool get isTablet {
  final firstView = WidgetsBinding.instance.platformDispatcher.views.first;
  final logicalShortestSide = firstView.physicalSize.shortestSide / firstView.devicePixelRatio;
  return logicalShortestSide > 600;
}
Moon answered 14/8, 2023 at 21:56 Comment(0)
H
1

Usually, the aspect ratio (width : height) of an Android tablet and iPad is in the range of from 0.75 to 1.4, and following is the fastest way to check. We can adjust the UI according to the aspect ratio.

bool isTablet;
double ratio = MediaQuery.of(context).size.width / MediaQuery.of(context).size.height;
if((ratio >= 0.74) && (ratio < 1.5))
{
  isTablet = true;
} else {
  isTablet = false;
}
Heterogenesis answered 28/11, 2020 at 12:0 Comment(0)
B
1

You can use DeviceType.tablet from the sizer package. You would do:

SizerUtil.deviceType == DeviceType.tablet

The field is set using this logic, which is the same as the most accepted answer here. I already use sizer to make my app responsive, so using it to determine whether the device is a tablet is convenient.

Bitternut answered 1/8, 2022 at 5:38 Comment(2)
Hello, the last release date for this package was September 15, 2021 (14 months ago). Is this package suitable?Dye
The code and feature is pretty simple (< 200 LOC), so there's not much to add in terms of features/bug fixes. I'm still using it for both Android/iOS without issue.Bitternut
C
0

You can use the device_info_plus package from Flutter:

static Future<bool> isTablet(BuildContext context) async {
  bool isTab = false;
  if (Platform.isIOS) {
    DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
    IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
    if(iosInfo.model?.toLowerCase() == "ipad") {
      isTab = true;
    } else {
      isTab = false;
    }
    return isTab;
  } else {
    var shortestSide = MediaQuery.of(context).size.shortestSide;
    if(shortestSide > 600) {
      isTab = true;
    } else {
      isTab = false;
    }
    return isTab;
  }
}

Calling..

Future checkDeviceType() async {

    bool iPad = await isTablet(context);

    if(iPad) {
       // Write your logic here..
    }
}
Candice answered 3/11, 2022 at 13:17 Comment(0)
C
0

Create a Dart file, size_config.dart

import 'dart:io';
import 'package:flutter/widgets.dart';
import 'dart:async';
import 'package:device_info_plus/device_info_plus.dart';

class SizeConfig {
  static late MediaQueryData _mediaQueryData;
  static late double screenWidth;
  static late double screenHeight;
  static late double blockSizeHorizontal;
  static late double blockSizeVertical;
  static late double safeBlockHorizontal;
  static late double safeBlockVertical;
  static bool isIpad = false;

  void init(BuildContext context) async {
    _mediaQueryData = MediaQuery.of(context);
    screenWidth = _mediaQueryData.size.width;
    screenHeight = _mediaQueryData.size.height;
    blockSizeHorizontal = screenWidth / 100;
    blockSizeVertical = screenHeight / 100;
    isIpad = await isTablet(context);
  }

  static Future<bool> isTablet(BuildContext context) async {
    bool isTab = false;
    if (Platform.isIOS) {
      DeviceInfoPlugin deviceInfo = DeviceInfoPlugin();
      IosDeviceInfo iosInfo = await deviceInfo.iosInfo;
      if (iosInfo.model.toLowerCase() == "ipad") {
        isTab = true;
      } else {
        isTab = false;
      }
      return isTab;
    } else {
      var shortestSide = MediaQuery.of(context).size.shortestSide;
      if (shortestSide > 600) {
        isTab = true;
      } else {
        isTab = false;
      }
      return isTab;
    }
  }
}

Calling

@override
  Widget build(BuildContext context) {
    SizeConfig().init(context);

    var height = SizeConfig.screenHeight;
    var width = SizeConfig.screenWidth;

    bool isIpad = SizeConfig.isIpad;
Cowherd answered 30/5, 2023 at 19:54 Comment(1)
This is essentially a code dump. Will it be improved? Probably not.Wishful
T
0

I wrote this context extension depending on the other answers. It works for me:

extension ContextExt on BuildContext {
    bool get isPhone => MediaQuery.of(this).size.width < 600.0;
    bool get isTablet => MediaQuery.of(this).size.width >= 600.0;
}

Usage:

final iconSize = context.isTablet ? 50.0 : 30.0;
Tendance answered 12/8, 2023 at 16:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.