🦔

【Flutter Widget of the Week #1】SafeAreaを使ってみた

2022/09/16に公開

はじめに

初めまして。
Flutter の勉強とアウトプットの練習を兼ねて Flutter Widget of the Weekを1から触ってみることにしました。文章を書くのも不慣れなので、不自然な部分があるかもしれません。
暖かい目で見ていただけると嬉しいです!

自己紹介はここまでとして、さっそく今回のテーマである
Flutter Widget of the Week #1 SafeArea の紹介に移っていきます。
https://youtu.be/lkF0TQJO0bA

SafeAreaとは

OS や端末によって、画面上部にあるステータスバーや下部にあるホームバー、丸角やノッチなどがコンテンツを隠してしまいレイアウトが崩れてしまうことがあります。
こうした問題の解決に SafeArea が役立ちます。
SafeArea を使うことで OS に関わらず適切な領域にコンテンツを表示してくれるので、レイアウトの調整が楽になる Widget です。

SafeArea を使っていない場合

下図を見ていただければ分かるように上部にあるステータスバーの時間や下部にある横棒が文字を重なってしまっています。
unsafe のときの画面
SafeAreaを使っていないときの画面
これが SafeArea を使うことで、、、

SafeArea を使った場合

このように適切な領域に表示してくれるようになりました。
これがないと iOS と Android といった OS の違いやモデルの違いによっていちいち確認と調整をしないといけないと考えると、すごく便利なことがわかるかと思います。
SafeArea を使った画面
SafeArea を使ったときの画面

ソース

main.dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Flutter SafeArea Sample',
      home: SafeAreaSample(),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: ListView(
          children: List.generate(100, (i) => Text('This is text $i 行目')),
        ),
      ),
    );
  }
}

SafeArea のプロパティについて

SafeArea にはいくつかのプロパティがありますので、紹介します。

(new) SafeArea SafeArea({
  Key? key,
  bool left = true,
  bool top = true,
  bool right = true,
  bool bottom = true,
  EdgeInsets minimum = EdgeInsets.zero,
  bool maintainBottomViewPadding = false,
  required Widget child,
})

top, bottom, left, right

上下左右で SafeArea の適応をなくしたいときに使います。
デフォルトでは全て true になっており、全範囲に適応されるようになってます。

試しに top を false にしてみると、上部が SafeArea なしのときと同じ画面表示になりました。
topをfalseにしたとき
top を false にしたときの画面

main.dart
return Scaffold(
  body: SafeArea(
    top: false, //<-ここを変える
    child: ListView(
      children: List.generate(
        100,
        (i) => Text('This is text $i 行目'),
      ),
    ),
  ),
);

画面を回転させて left を true/false 変えて比べてみると、、、

left が true(デフォルト)の場合

leftがtrue(デフォルト)の場合
leftがtrue(デフォルト)の場合

leftが false の場合

leftがfalseの場合
leftがfalseの場合

個人的な感覚ですが、このプロパティの使い道としては top,bottom を false にすることはあまりなく、左右に余白を作りたくないときに left, right を false にすることがあるのかなと思います。

maintainBottomViewPadding

これは TextField を使ってキーボードを表示させたときに関係してくるプロパティです。
SafeArea を入れたことで生まれたパディング(余白)をキーボードを表示するときにも使うかどうかを変更できます。

画面下部に TextField を配置して maintainBottomViewPadding プロパティを切り替えて比べてみましょう。
まずキーボードが表示されていない時の表示は以下です。
キーボードが表示されていない時の表示
キーボードが表示されていない時の表示

main.dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      title: 'Flutter SafeArea Sample',
      home: const MainApp(),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    final mq = MediaQuery.of(context);
    final bottomOffset = mq.viewInsets.bottom + mq.padding.bottom;
    return Scaffold(
      resizeToAvoidBottomInset: false,
      appBar: AppBar(title: const Text('SafeAreaテスト')),
      body: AnimatedContainer(
        duration: const Duration(milliseconds: 200),
        padding: EdgeInsets.only(bottom: bottomOffset),
        child: SafeArea(
          bottom: true,
          maintainBottomViewPadding: true, // ここの値を切り替える
          child: Stack(children: const [
            Align(
              alignment: Alignment.bottomLeft,
              child: TextContainer(),
            ),
          ]),
        ),
      ),
      backgroundColor: const Color.fromARGB(255, 147, 247, 105),
    );
  }
}

class TextContainer extends StatefulWidget {
  const TextContainer({super.key});

  
  TextContainerState createState() => TextContainerState();
}

class TextContainerState extends State<TextContainer> {
  
  Widget build(BuildContext context) {
    return Container(
      color: Colors.white,
      padding: const EdgeInsets.symmetric(vertical: 6),
      child: Row(
        children: const [
          SizedBox(
            width: 60,
            child: Text('入力欄'),
          ),
          Flexible(
            child: TextField(
              decoration: InputDecoration(
                border: InputBorder.none,
                hintText: 'ここに入力してください!',
              ),
            ),
          ),
        ],
      ),
    );
  }
}

maintainBottomViewPadding プロパティを切り替えて比較すると、、、

maintainBottomViewPadding が false(デフォルト)の場合

maintainBottomViewPadding が true の場合

ご覧の通り、maintainBottomViewPadding を true にすると bottom のパディングがキーボードを表示するときにもそのまま適応されるようになりました。

最後に

まだまだ勉強を始めたばかりで間違えている箇所があるかもしれません。もし見つけましたら、ご指摘いただけるとありがたいです。今後も定期的にアウトプットしながら力をつけていき、Flutterの発展に貢献できるようなエンジニアを目指していきます。

参考記事

https://api.flutter.dev/flutter/widgets/SafeArea-class.html
https://dev.classmethod.jp/articles/flutter_widget_intro_safearea/

Discussion