サイドメニューとは、スマホ画面の左側から中央に向かってスライド表示されるメニューのこと。
ハンバーガーに例えられる三本線をクリックすると出てくるので、ハンバーガーメニューとも呼ばれます。
Flutterでサイドメニューを作るには「Drawerウィジェット」が一般的です。
この記事では「Drawer」の基本から、「Drawer」をより実践的に便利に使えるヘッダーの作成方法や、画像表示や色、アイコンをクリックせずにプログラム内部から開閉する方法など、盛りだくさんで解説します。
- Flutter : 3.27.1
- Dart : 3.6.0
基本編:Drawerで作るサイドメニュー

Drawer()
で簡単に作成できるサイドメニューは、アプリの機能にアクセスするショートカットや、よく使う設定の変更項目などを配置することで、ユーザーの利便性向上に役立ちます。
Drawerの土台

AppBar()
でDrawer()
のアイコンを表示するサイドメニューを表示するにはDrawer()
ウィジェットを使いますが、その土台としてScaffold()
が必要です。
その理由は、サイドメニューを表示する「三本線のハンバーガー」は、Scaffold()
のappBar:
プロパティに指定するAppBar()
の中に表示されるためです。
Scaffold()
:appBar:
とdrawer:
class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(),
drawer: Drawer(...)
...
実際にDrawer()
を作成する場所は、drawer:
プロパティです。(上記行番号6)
もし仮に、drawer:
でDrawer()
を作ったとしても、AppBar()
がなければクリックする三本線アイコンが表示されません。
AppBar()
にはたくさんのプロパティがありますが、何も指定しなければ、画面上部にスペースだけが確保されます。
サンプルコードでは、Drawer(...)
表示のためだけにAppBar()
を入れています。
メニュー項目の作成

ListTile()
を使うDrawer()
の中にメニュー項目を表示するには、Column()
の中でListTile()
ウィジェットを使います。(下記行番号4〜)
drawer: Drawer(
child: Column(
children: [
ListTile(
leading: Icon(Icons.restaurant),
title: Text("レストラン"),
onTap: () {},
),
ListTile(...
leading:
:先頭にアイコンマークを表示title:
:メニュー項目としてテキスト表示onTap:
:項目をタップしたときの処理
onTap:
(上記行番号7)では、たとえば下記のようにNavigator
でジャンプ先のスクリーンへの移動処理を書いて、アクションをつけます。
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => RestaurantScreen()));
},
Drawer()
は、Padding()
やSizedBox()
でスペースを確保しないと、画面の上部から表示されて時刻表示などと重なってしまいます。
上記のスクショ画面は、ListTile()
の前に、SizedBox(height: 100.0),
を入れて作成しています。
スペースを作らないとこんな感じ↓

ヘッダーをつける

DrawerHeader()
でヘッダー領域を確保サイドメニューのメニュー項目の上には、アプリアイコンやアプリ名などを表示するとアクセントになります。
ヘッダーを作るには、Drawer()
の中でDrawerHeader()
ウィジェットを使うと、自動でヘッダー領域が確保されます。(下記行番号4)
drawer: Drawer(
child: Column(
children: [
DrawerHeader(
child: Row(
children: [
Icon(Icons.fastfood),
SizedBox(width: 18),
Text("サイドメニュー"),
],
),
),
ListTile(...
DrawerHeader()
の領域の中には、Row()
やColumn()
を使ってアイコンやテキストなどを自由に配置できます。

Row()
やColumn()
で自由に作れる DrawerHeader(
child: Padding(
padding: const EdgeInsets.all(14.0),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Icon(Icons.fastfood, size: 80),
SizedBox(width: 12),
Text("サイドメニュー"),
],
),
),
),
実践編:Drawerのカスタマイズ

Drawer()
を作ってみるカスタムウィジェットを作成

Drawer()
ウィジェットは別のDartファイルにするScaffold()
のdrawer:
にDrawer()
ウィジェットを直接書くと、コードの見通しが悪くなるので、カスタムウィジェットとして別ファイルを作成します。
ここでは、MainDrawer()
というウィジェット名でDrawer()
を作成し、冒頭のimport
文で[main_drawer.dart]をインポートします。
import 'package:(パッケージ名)/main_drawer.dart';
Scaffold()
のdrawer:
では、作成したカスタムウィジェットを指定します。
return Scaffold(
appBar: AppBar(elevation: 16),
drawer: MainDrawer(),
この[main_drawer.dart]のreturn文でDrawer()
ウィジェットを返します。
[main_drawer.dart]の全文
import 'package:flutter/material.dart';
import 'restaurant_screen.dart';
import 'shopping_screen.dart';
class MainDrawer extends StatelessWidget {
const MainDrawer({super.key});
@override
Widget build(BuildContext context) {
return Drawer(
child: Column(
children: [
DrawerHeader(
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(color: Theme.of(context).colorScheme.primaryContainer),
child: Row(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Icon(Icons.fastfood, size: 80),
SizedBox(width: 8.0),
Text("サイドメニュー",style: TextStyle(fontSize: 14.0)),
],
),
),
ListTile(
leading: Icon(Icons.restaurant),
title: Text("レストラン"),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => RestaurantScreen()));
},
),
ListTile(
leading: Icon(Icons.shopping_cart),
title: Text("ショッピング"),
onTap: () {
Navigator.of(context).push(MaterialPageRoute(
builder: (context) => ShoppingScreen()));
},
),
],
),
);
}
}
上記サンプルコードでは、メニューからのリンク先をNavigator.of(context).push()
で直接指定しています。
メニュー項目が限定的で少ないなら、これで十分と思います。
ヘッダーにユーザー情報を表示

UserAccountsDrawerHeader()
でユーザー情報を表示DrawerHeader()
の代わりにUserAccountsDrawerHeader()
を使うと、名前・メールアドレス・アイコンとなる写真をプロパティに指定するだけで、いい感じのヘッダーが作れます。
UserAccountsDrawerHeader()
という名前のウィジェットということもあって、アプリにログイン中のアカウント名・メールアドレス・アイコン画像を表示するのに使います。
UserAccountsDrawerHeader(
accountName: Text("Fluttoco"),
accountEmail: Text("example@example.com"),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage("https://~~~~"),
),
onDetailsPressed: (){
Navigator.of(context).push(MaterialPageRoute(
builder: (_) => AccountScreen()));
},
),
currentAccountPicture:
には、CircleAvatar()
を使うのが一般的です。(行番号4)
イメージ画像がネットワークを介さないと表示できない場合(例:Googleアカウントのアイコンなど)は、NetworkImage("https://~~~~"),
を使えばOK。(行番号5)
onDetailsPressed:
でNavigator.
を使えば、アカウントの詳細情報を表示する画面へとリンクさせるのも簡単です。(行番号7-10)
onDetailsPressed:
を入れると、画面上には「▼」が表示されます。

ヘッダー部分の装飾

decoration:
でデザインをカスタマイズ背景色の指定
UserAccountsDrawerHeader()
のdecoration:
を使って色味を変えてみます。
ここでは、BoxDecoration()
のgradient:
を使って、グラデーションにしました。(行番号16)
指定した色は、Color()
で直接色指定することもできますが、アプリのテーマに指定されているprimaryContainer
を使って左上から右下へ向かってグラデーションをかけています。(行番号17〜23)
UserAccountsDrawerHeader(
accountName:
Text("Fluttoco", style: TextStyle(color: Colors.indigo)),
accountEmail: Text(
"example@example.com",
style: TextStyle(color: Colors.indigo),
),
currentAccountPicture: CircleAvatar(
backgroundImage: NetworkImage("https://~~~~"),
),
onDetailsPressed: () {
Navigator.of(context)
.push(MaterialPageRoute(builder: (_) => AccountScreen()));
},
arrowColor: Colors.indigo,
decoration: BoxDecoration(
gradient: LinearGradient(
colors: [
Theme.of(context).colorScheme.primaryContainer,
Theme.of(context).colorScheme.primaryContainer.withAlpha(128),
],
begin: Alignment.topLeft,
end: Alignment.bottomRight,
),
),
),
onDetailsPressed:
を指定したときに表示される下向き三角の色は、デフォフトでは白なのでarrowColor:
でindigo
にしています。(上記行番号15)
アカウント名などのテキスト色も、TextStyle(color: Colors.indigo)
にしました。(上記行番号3, 6)
背景画像の指定

decoration:
では、背景画像を表示することもできます。
UserAccountsDrawerHeader()
でも背景画像を表示できますが、サンプルコードではDrawerHeader()
でdecoration:
を使っています。
ここで表示した画像は、プロジェクト内の[assets]フォルダに入れた画像を読み込んでいるので、その場合は[pubspec.yaml]への記述とPub get
は必須。
assets:
- assets/ec-main.jpg
画像をヘッダー領域全体に表示するために、child:
でExpanded()
を指定して幅いっぱいの領域を確保しています。
DrawerHeader(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/ec-main.jpg'),
),
),
child: Expanded(child: Flexible(child: Container())),
),
背景画像はchild:
に合わせて表示されるので、下記のようにchild:
でテキスト表示(下記コードの行番号8)した場合は、そのテキストの背景になります。
下記ではfit: BoxFit.scaleDown,
として、テキストの長さにフィットさせました(下記コードの行番号5)。

DrawerHeader(
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/ec-main.jpg'),
fit: BoxFit.scaleDown,
),
),
child: Text("テキストテキストテキスト"),
),
領域いっぱいに画像表示すると、画面上部の時間表示の部分にかぶってしまうので、Drawer()
全体をSafeArea()
で囲むと、サイドメニュー全体を下に下げることができます。(下記行番号2)
Widget build(BuildContext context) {
return SafeArea(
child: Drawer(
child: Column(
children: [
DrawerHeader(...

SafeArea()
あり
SafeArea()
なしメニュー項目の装飾
![FlutterのDrawerで作成したサイドメニューのメニュー項目の末尾に、[trailing:]プロパティを使って矢印アイコンを表示した例](https://fluttoco.com/wp-content/uploads/2025/05/drawer-0125-listtile.png)
ListTile()
には、先頭につけるleading:
の他に、末尾を指定するtrailing:
もあるので、ここにもアイコンをつけました。(下記行番号3)
ListTile(
leading: Icon(Icons.restaurant),
trailing: Icon(Icons.arrow_circle_right_outlined, size: 20.0),
title: Text("レストラン"),
onTap: () {
Navigator.of(context).push(
MaterialPageRoute(builder: (context) => RestaurantScreen()));
},
),
サイドメニュー全体の色

Drawer()
の装飾機能はとても柔軟Drawer()
自体には色を指定するプロパティがありませんが、Drawer()
の直下でContainer()
を使えば、全体に背景色をつけることができます。
下記のサンプルコードでは、withAlpha()
で透過させました。
child: Drawer(
child: Container(
color: Colors.teal.withAlpha(50),
child: Column(
children: [
DrawerHeader(
左開き・右開き

![FlutterのDrawerで、左右の両側にサイドメニューのアイコンを表示した例。 左アイコンの表示は[drawer:]、右アイコンの表示には[endDrawer:]プロパティを使う。](https://fluttoco.com/wp-content/uploads/2025/05/drawer-0130-left-and-right01.png)

サイドメニューは一般的に画面の左から開きますが、Drawer()
では右側から表示することもできます。
また、左と右の両方に別のサイドメニューを同時に指定することも可能です。
左から開くときはdrawer:
、右から開くときはendDrawer:
です。
return Scaffold(
appBar: AppBar(),
drawer: MainDrawerHeaderImage(),
endDrawer: MainDrawer(),
);
AppBarのアイコンを使わずにDrawer表示

Drawer()
を開くDrawer()
表示は、AppBar()
内のアイコンをクリックするのが一般的ですが、Scaffold()
でkey:
を使うと、プログラム内からメソッドを実行して開くことができるようになります。
ユーザーが何かの操作を完了したときなどに、Drawer()
を自動表示したい場合など便利です。
Scaffold()
のkey:
プロパティに指定するGlobalKey
を作成します。
GlobalKey
は、<ScaffoldState>
で_key
という名前の変数を作成しました。
(下記行番号4)
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
final GlobalKey<ScaffoldState> _key = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return Scaffold(...
Scaffold()
のkey:
プロパティに、いま作成した_key
を設定します。
appBar: AppBar(),
がなくても、Drawer()
の表示そのものには影響しません。
下記のサンプルコードでは、わかりやすいように削除しました。
drawer:
は、必要です。(下記行番号3)
return Scaffold(
key: _key,
drawer: MainDrawer(),
body: ...
Drawer()を開くには、.openDrawer()
を使います。
下記のサンプルでは、ElevatedButton()
を押したときの処理に埋め込みました。
ElevatedButton(
child: Text("サイドメニューを表示"),
onPressed: () {
_key.currentState!.openDrawer();
},
),
Drawer()
の開閉
- 開く:
.openDrawer()
- 閉じる:
.closeDrawer()
表示されたDrawer()
は、サイドメニュー以外の画面をタップすると自動で閉じますが、Navigator.pop()
を使って閉じることもできます。
下記コードでは、Drawer()
内のListTile()
で表示したメニュー項目をタップして閉じます。
ListTile(
title: Text("閉じる"),
onTap: () {
Navigator.pop(context);
},
),
「項目を押して」 閉じる
これで、AppBar()
を配置しなくても、サイドメニューが表示できるようになりました。
class MyHomePage extends StatelessWidget {
MyHomePage({super.key});
final GlobalKey<ScaffoldState> _key = GlobalKey<ScaffoldState>();
@override
Widget build(BuildContext context) {
return Scaffold(
key: _key,
drawer: MainDrawer(),
body: Center(
child: ElevatedButton(
child: Text("サイドメニューを表示"),
onPressed: () {
_key.currentState!.openDrawer();
},
),
),
);
}
参考書・サイトなど
書籍:「Flutter 3 入門」
Drawerについては3ページ分ほどで書かれていますが、要点を押さえた解説で、構造を理解するにはわかりやすいです。
「さくしん」さんのサイト
UdemyにもFlutter講座を持っている「さくしん」さんのサイト。
文字が多めで、画像少ないですが、詳しく解説しています。

「さくしん」さんのUdemy講座さく しん @Flutter修行中
Udemy講座「Flutter & Dart – The Complete Guide [2025 Edition]」
おしゃれなサンプルアプリを作りながら、Flutterの基本から解説。

コメント