62

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?

Many thanks Mahi

Mahi
  • 5,726
  • 13
  • 31
  • 41
  • How exactly do you define the difference between smartphone and tablet? Is it about whether the device can make phone calls? – Günter Zöchbauer Mar 26 '18 at 05:43
  • 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. – Mahi Mar 26 '18 at 11:26
  • I don't think this is currently supported directly. You can always call out to a native service like https://stackoverflow.com/questions/5196833/android-device-phone-call-ability using a plugin https://flutter.io/developing-packages/. There are lots of examples https://github.com/flutter/plugins/tree/master/packages – Günter Zöchbauer Mar 26 '18 at 11:31
  • 1
    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. – Mahi Mar 26 '18 at 12:35
  • Why rule out VOIP calls like Skype? Just because it doesn't have a phone number doesn't mean it can't make calls. – Randal Schwartz Mar 26 '18 at 14:34
  • 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. – Mahi Mar 27 '18 at 15:06
  • I know you may have solved the problem...but found this plugin..it seem to have some convenience functions...https://pub.dev/packages/flutter_device_type – anoop4real Nov 07 '19 at 13:00

12 Answers12

54

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';
  }
bakua
  • 13,704
  • 7
  • 43
  • 62
  • 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. – Roc Boronat Jan 22 '21 at 10:14
53
// 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 https://flutter.rocks/2018/01/28/implementing-adaptive-master-detail-layouts/

Thanks @Sergi

Chandler
  • 3,061
  • 3
  • 22
  • 30
  • 1
    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 Flutter – Kirill Karmazin Feb 11 '20 at 13:46
  • 4
    this give me 552 on my galaxy a tablet & nexus 7 tab emulator – cahyowhy Apr 12 '20 at 14:11
  • 2
    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. – Roc Boronat Jan 22 '21 at 10:14
19

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;
  }
}
b4rtaz
  • 231
  • 2
  • 4
  • 1
    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. – Nick N Sep 04 '21 at 14:06
  • 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? – Ahmed Almahdie Aug 10 '22 at 09:20
16

Here's the same than in other aswers, 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 :·)

Roc Boronat
  • 11,395
  • 5
  • 47
  • 59
  • 2
    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! – Roc Boronat Jan 22 '21 at 10:10
  • 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 { ... } – Jason Perfetto Jun 25 '21 at 17:32
  • @JasonPerfetto that's a good idea too :·) – Roc Boronat Feb 07 '22 at 15:08
15

Despite iOS has a clear separation between phone and 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: https://flutter.rocks/2018/01/28/implementing-adaptive-master-detail-layouts/

  • hi thank you for your reply, i will give this example a try and see if that helps. many thanks – Mahi Mar 26 '18 at 11:27
7

For Android as @Chandler said you should check the smallest size of the screen, but for iOS, you can identify if it's iPad with 100% accuracy using 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;
    }
  }
Kirill Karmazin
  • 6,256
  • 2
  • 54
  • 42
  • 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 launch – Apoleo Jun 27 '22 at 11:27
1

Usually the Aspect ratio ( width : height) of Android Tablet and iPad is in the range of from 0.75 ~ 1.4, 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;
}
persec10000
  • 820
  • 2
  • 10
  • 17
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.

user2233706
  • 6,148
  • 5
  • 44
  • 86
  • Hello, the last release date for this package was September 15, 2021 (14 months ago). Is this package suitable? – My Car Dec 06 '22 at 11:59
  • 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. – user2233706 Dec 06 '22 at 14:59
0

You can use device_info_plus package from flutter https://pub.dev/packages/device_info_plus

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..
     }
}
Ankur Yadav
  • 131
  • 2
  • 3
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;
Deepak Yadav
  • 227
  • 3
  • 4
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;
MBH
  • 16,271
  • 19
  • 99
  • 149
0

2023, 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;
}

Praxder
  • 2,315
  • 4
  • 32
  • 51