Flutter Webでレスポンシブ対応する


## はじめに
Flutter for Webでは、通常のCSSやSassと比較した場合レスポンシブ対応が難しい印象を持ちます。
昨今はAndroid系列の端末をはじめ、様々な画面サイズが登場しているためFlutter WebのOverflowエラーが発生しやすい原因となっています。

この記事では、２パターンのレスポンシブ対応について解説したいと思います。
手法自体は通常のWebと同じですが、画面幅を取得し、画面ごとPC用、Tablet用、Mobile用を用意して切り替えるケースと、画面幅に応じてスタイルを切り替えるケースです。

## ケース１：画面ごと切り替える
こちらの手法の場合、[responsive_builder](https://pub.dev/packages/responsive_builder)というパッケージがとても便利です

### 使い方
```Dart
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';

class SamplePage extends StatelessWidget {
  const SamplePage({super.key});

  @override
  Widget build(BuildContext context) {
    return ScreenTypeLayout(
      mobile: const SampleMobilePage(),
      tablet: const SampleTabletPage(),
      desktop: const SampleDesktopPage(),
    );
  }
}
```

また、私の所感的にデフォルトのブレイクポイントでも問題ありませんが、ブレイクポイントもカスタム可能です。
しかし、watchのブレイクポイントもrequiredのため指定しないといけない上、mobileは指定できないので注意が必要です。

```Dart
import 'package:flutter/material.dart';
import 'package:responsive_builder/responsive_builder.dart';

class SamplePage extends StatelessWidget {
  const SamplePage({super.key});

  @override
  Widget build(BuildContext context) {
    return ScreenTypeLayout(
      breakpoints: ScreenBreakpoints(
        tablet: 600,
        desktop: 950,
        watch: 300,
      ),
      mobile: const SampleMobilePage(),
      tablet: const SampleTabletPage(),
      desktop: const SampleDesktopPage(),
    );
  }
}
```

## ケース２：スタイル側で切り替える
やることは同じです。
こちらでブレイクポイントを指定し、それぞれ切り替えるだけです。
以下のようなenumとメソッドを用意してみました。

なお、ブレイクポイントはこちらのサイトを参考に設定しました！

https://caroa.jp/design/article/IuBFNKUn


```device_type.dart
import 'package:flutter/material.dart';

/// デバイスの種類を判定する
enum DeviceType {
  mobile,
  tablet,
  desktop,
}

DeviceType getDeviceType(BuildContext context) {
  final screenWidth = MediaQuery.of(context).size.width;
  if (screenWidth < 430) {
    return DeviceType.mobile;
  } else if (screenWidth < 1024) {
    return DeviceType.tablet;
  } else {
    return DeviceType.desktop;
  }
}
```

### 使い方
```sample_page.dart
class SamplePage extends StatelessWidget {
  const SamplePage({super.key});

  /// デバイス種別に応じてwidthを変更する
  double getDialogWidth(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final deviceType = getDeviceType(context);
    switch (deviceType) {
      case DeviceType.mobile:
        return screenWidth * 0.75;
      case DeviceType.tablet:
        return screenWidth * 0.56;
      case DeviceType.desktop:
        return screenWidth * 0.36;
    }
  }

  double getDialogHight(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    final screenHeight = MediaQuery.of(context).size.height;
    final deviceType = getDeviceType(context);
    switch (deviceType) {
      case DeviceType.mobile:
        return screenWidth * 0.85;
      case DeviceType.tablet:
        return screenWidth * 0.68;
      case DeviceType.desktop:
        return screenHeight * 0.64;
    }
  }

  @override
  Widget build(BuildContext context) {
    return Dialog(
      child: SizedBox(
        height: getDialogHight(context),
        width: getDialogWidth(context),
      ),
    );
  }
}
```

しかし、上記の例だとViewファイルが肥大化してしまいます。
そのため、`sample_page_style.dart`のようなファイルを別途設けることで
肥大化は避けられると思います。