Flutter Tips
【Flutter&Dart Tips#1】デバイスの種類・画面サイズを取得する方法

本記事は2022年5月に発表されたDart 2.17のenhanced enumを使用しています。それ以前のバージョンでは動作しませんのでご注意ください。

2022年5月にFlutter3.0とDart2.17が公開され、ますます便利になってきましたね。

これまで単純な値の列挙しかできなかったenumですが、Javaのenumのようにコンストラクタやメソッドを持てるようになったことで、格段に使いやすくなりました。

今回はDartの新機能をフル活用して、デバイスの種類と画面サイズを取得するenumを作ってみました。コピペや改造もOKですので、ご自由にご利用ください。

コード

まずは以下のコードをご覧ください。

//[pubspec.yaml]
//  environment:
//    sdk: ">=2.17.1 <3.0.1"

import 'dart:math';

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

enum DeviceType {
  iPhoneSE(9.0 / 16.0, 16.0 / 9.0), //0.5625 , 1.7777
  iPhone13(9.0 / 19.5, 19.5 / 9.0), //0.4615 , 2.1666
  iPad(3.0 / 4.0, 4.0 / 3.0); //0.75 , 1.3333

  final double aspectRatioV; //縦アスペクト比
  final double aspectRatioH; //横アスペクト比
  const DeviceType(this.aspectRatioV, this.aspectRatioH);

  //アスペクト比から最も近いデバイスタイプを計算して返すメソッド
  static DeviceType nearest(double aspectRatio) {
    //各アスペクト比との差を絶対値で計算する
    var iPhoneSEabs =
        ((aspectRatio > 1 ? iPhoneSE.aspectRatioH : iPhoneSE.aspectRatioV) -
                aspectRatio)
            .abs();
    var iPhone13abs =
        ((aspectRatio > 1 ? iPhone13.aspectRatioH : iPhone13.aspectRatioV) -
                aspectRatio)
            .abs();
    var iPadabs = ((aspectRatio > 1 ? iPad.aspectRatioH : iPad.aspectRatioV) -
            aspectRatio)
        .abs();

    // print("iPhoneSEabs: $iPhoneSEabs");
    // print("iPhone13abs: $iPhone13abs");
    // print("iPadabs: $iPadabs");

    //enumと同じ順番でリストに格納する
    var list = <double>[iPhoneSEabs, iPhone13abs, iPadabs];

    //最大値を取得する
    var minValue = list.reduce(min);
    //print("minvalue: $minValue");

    //最大値から要素番号を取得する
    var index = list.indexOf(minValue);
    //print("index: $index");

    //enumのvaluesリストのindex番目を返す
    return DeviceType.values[index];
  }
}

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(home: Material());
  }
}

class Material extends StatelessWidget {
  const Material({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    //MediaQuery.of(context).sizeで画面サイズを取得できる
    Size size = MediaQuery.of(context).size;

    //size.aspectRatioで画面のアスペクト比を取得できる
    //上記enumのnearestを呼び出し、最も近い画面比のデバイスを特定する
    DeviceType deviceType = DeviceType.nearest(size.aspectRatio);

    //アスペクト比が1を超えていれば横画面、そうでなければ縦画面
    String rotation = size.aspectRatio > 1 ? "横" : "縦";

    return Scaffold(
        body: Center(
      child: Text("$deviceType $rotation画面  横:${size.width} 縦:${size.height}"),
    ));
  }
}

動作確認

iPhone13 Pro Maxシミュレータ(縦画面)
iPhone13 Pro Maxシミュレータ(横画面)
iPhoneSEシミュレータ(縦画面)
iPhoneSEシミュレータ(横画面)

解説

ポイントは以下の通りです。

  1. MediaQuery.of(context).sizeで画面情報を取得できます。(※)
    1. size.width …… 画面の幅
    2. size.height …… 画面の高さ
    3. size.aspectRatio …… アスペクト比(縦横比)
  2. DeviceTypeがデバイスの種類を表すenumです。
    1. DeviceType.nearestの引数にaspectRatioを指定するだけで、最も近いデバイスタイプを取得できます。
    2. Androidについては記載していませんが、Android端末であっても、最もアスペクト比の近いiOSデバイスが得られるので、マルチデバイス対応の画面レイアウトが容易になります。

MediaQuery.of(context)はMaterialAppを返すMyAppのbuildでは動作しません。Scaffold以降を別Widgetに分離すると分かりやすいと思います!

ご感想・ご要望など、コメントをしていただけると励みになります! ここまでお読みくださりありがとうございました!

GitHub Gistにも同じコードを掲載しておりますので、お好きな方をご使用下さい。

おすすめの記事