🍔

Flutter Web で Drawer のレスポンシブ対応

2021/12/08に公開

はじめに

いきなりですが、ドロワー便利ですよね。web画面の左上のハンバーガーアイコンを押すと、左からニョキっと出てくるあれです。Flutterでは、ScaffoldウィジェットのdrawerにDrawerウィジェットを設置するだけで簡単に実装できちゃいます。

やりたいこと

そんなドロワーを、ブラウザの画面が狭いときはハンバーガーアイコンを出して隠す、画面が広いときはサイドメニューで表示・・と、よくある構成にしたというお話です。

準備

flutterプロジェクトをデフォルトで作成した状態からスタートして説明します。

// Flutterプロジェクトを作成
$ flutter create flutter_web_drawer

プロジェクトが作成できたら、早速ブラウザで確認してみましょう。chromeがPCにインストールされていれば、以下のコマンドで確認できます。

// デバッグ起動
$ flutter run -d chrome


Flutterアプリ起動時の画面

Drawerを設置

まず、ドロワーを設置していきます。
ScaffoldウィジェットのdrawerにDrawerウィジェットを追加するだけで、簡単に設置できます。

main.dart

  Widget build(BuildContext context) {
    return Scaffold(
      appBar: (省略),
      // ---- ここから追加 ----
      drawer: Drawer(
        child: ListView(
          children: [
            DrawerHeader(
              child: Text("ヘッダー"),
            ),
            ListTile(
              title: Text("ページ1")
            ),
            ListTile(
              title: Text("ページ2")
            ),
            ListTile(
              title: Text("ページ3")
            ),
          ],
        ),
      ),
      // ---- ここまで追加 ----
      body: (省略)

設置が終わったら、もう一度ブラウザで確認してみてください。
左上にハンバーガーアイコンが表示されて、ドロワーが追加されているのを確認できると思います。


ドロワー追加後

Drawerを部品化

追加したDrawerはサイドメニューとして使い回すので、外に出して部品化しましょう。
widgetsフォルダを作成して、その中にdrawerのファイルを作成します。例としては以下のような感じで良いでしょう。

widgets/common_drawer.dart
import 'package:flutter/material.dart';

/// ドロワー
class CommonDrawer extends StatelessWidget {
  const CommonDrawer({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Drawer(
      child: ListView(
        children: const [
          DrawerHeader(
            child: Text("ヘッダー"),
          ),
          ListTile(
            title: Text("ページ1"),
          ),
          ListTile(
            title: Text("ページ2"),
          ),
          ListTile(
            title: Text("ページ3"),
          ),
        ],
      ),
    );
  }
}

Drawerウィジェットを別のファイル(common_drawer.dart)で定義したので、scaffoldウィジェットのdrawerには、このDrawerウィジェットを使うようにします。

main.dart
      drawer: const CommonDrawer(),

ブラウザの横幅が広いときは、ドロワーを非表示にする

次に、ブラウザの横幅によって、ドロワーを表示させないようにします。
ブラウザの横幅は MediaQuery.of(context).size.width で取得できるので、この値で判断します。scaffoldウィジェットのdrawerを、以下のように書きかえます。

main.dart
drawer: MediaQuery.of(context).size.width <= 768 ? const CommonDrawer() : null,

画面を動かすと、以下のような感じになります。


横幅が広いときはドロワーを非表示

ブラウザの横幅が広いときは、サイドメニューを表示する

最後に、ブラウザの横幅が広いときはドロワーがなくなるので、ドロワーをそのままサイドメニューとして活用します。
scaffoldウィジェットのbodyを、以下のように書きかえます。

main.dart
      body: Row(
        children: [
          MediaQuery.of(context).size.width > 768
            ? const CommonDrawer()
            : Container(),
          Expanded(
            child: Center(
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                children: <Widget>[
                  const Text(
                    'You have pushed the button this many times:',
                  ),
                  Text(
                    '$_counter',
                    style: Theme.of(context).textTheme.headline4,
                  ),
                ],
              ),
            ),
          ),
        ],
      ),

これまでbodyに定義していた内容をRowウィジェットの中に入れて、Rowウィジェット内の最初にDrawerウィジェットを突っ込む感じです。
動かしてみると、以下のような感じでサイドメニューが表示されます。


横幅が広いときはサイドメニューを表示

おわりに

これがベストな方法かわかりませんが、とりあえずやりたいことの実装はできました。
作成したプロジェクトは、GitHubにあげてますので、参考にしてください。

https://github.com/ponwink/flutter_web_drawer

Flutter Web 自体はまだまだかゆいところに手が届かないところもありますが、触ってみている感じとしてはモバイルアプリを作成しているときと同じように構築できて、とても楽しいです。この先、どんどん進化していくのが楽しみですね。

まだまだ勉強中なので、他にもいろいろ触っていこうと思いますー。

Discussion