【Flutter/Dart】Scaffoldの使い方!アプリバーやドロワーの実装方法

この記事からわかること
- Flutter/DartのScaffoldとは?
- AndroidライクなUIの実装方法
- アプリバーやドロワーの実装方法
index
[open]
\ アプリをリリースしました /
環境
- Android Studio:Koala
- Xcode:16.0
- Flutter:3.29.2
- Dart:3.7.2
- Mac M1:Sonoma 14.6.1
Scaffold
FlutterのScaffold
はアプリの基本的なレイアウト構造を、Material Designをベースにして構築するための土台となるWidgetです。Material DesignベースなのでAndroidライクなUIを簡単に実装できるようになります。
Scaffold
ではアプリバー(上部バー)やドロワー(スライドメニュー)、フローティングボタン、ボトムバーなどをMaterial Designを使用して簡単に実装することが可能になります。Scaffoldは「足場」という意味なので、意味をわかっていれば想像しやすいかもしれません。
定義を確認してみると色々なプロパティが存在することがわかります。今回はこの中でもよく使うappBar
、body
、floatingActionButton
などの使い方を解説していきます。
class Scaffold extends StatefulWidget {
const Scaffold({
super.key,
this.appBar,
this.body,
this.floatingActionButton,
this.floatingActionButtonLocation,
this.floatingActionButtonAnimator,
this.persistentFooterButtons,
this.persistentFooterAlignment = AlignmentDirectional.centerEnd,
this.drawer,
this.onDrawerChanged,
this.endDrawer,
this.onEndDrawerChanged,
this.bottomNavigationBar,
this.bottomSheet,
this.backgroundColor,
this.resizeToAvoidBottomInset,
this.primary = true,
this.drawerDragStartBehavior = DragStartBehavior.start,
this.extendBody = false,
this.extendBodyBehindAppBar = false,
this.drawerScrimColor,
this.drawerEdgeDragWidth,
this.drawerEnableOpenDragGesture = true,
this.endDrawerEnableOpenDragGesture = true,
this.restorationId,
});
}
iOSライクなUI(クパチーノデザイン)での実装をしたい場合はCupertinoPageScaffold
Widgetを使用してください。
Scaffoldの使い方
Scaffold
は土台部分を構成するWidgetなので画面単位の土台として活用します。デフォルトのサンプルコードであるMyHomePage
(StatefulWidgetなので実装は_MyHomePageState
)にも実際に使われています。それぞれの部分を切り出しながら実装を確認していきます。
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
title: Text(widget.title),
),
body: 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.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}

appBar
コンストラクタの引数appBar
はアプリバー(上部バー)部分のUIを構築するWidgetを指定します。定義を確認すると指定できる型はPreferredSizeWidget?
になっています。PreferredSizeWidget
は高さが明示的に指定されているという特徴があります。
/// Scaffoldの上部に表示されるappBar
final PreferredSizeWidget? appBar;
AppBar
はPreferredSizeWidget
を継承しているので基本的にAppBar
を使用すればOKです。カスタムで実装したい場合はPreferredSizeWidget
を継承させる必要があります。
appBar: AppBar(
// 背景色
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// アプリバーに表示したい文字列
title: Text(widget.title),
),
アプリバーの右側にボタンを追加したい場合はactions
で指定します。この中に指定したWidgetがアプリバーの右側に配置されます。
AppBar(
title: Text("登録"),
actions: [
IconButton(
onPressed: () { add(context); },
icon: Icon(Icons.check)
),
],
)
body
コンストラクタの引数body
は画面に表示するメインコンテンツを指定します。Widget
型で指定できるので任意のUIを指定することが可能ですが、指定したコンテンツはappBarやfloatingActionButton、drawerなどの下側に表示されるようになります。
/// Scaffoldの主なコンテンツ。
///
/// [appBar] の下、アンビエント [MediaQuery] の [MediaQueryData.viewInsets] の下部の上、および [floatingActionButton] と [drawer] の後ろに表示されます。[resizeToAvoidBottomInset] が false の場合、オンスクリーン キーボードが表示されても本体のサイズは変更されません。つまり、`viewInsets.bottom` によってインセットされません。
///
/// Scaffoldの本体のWidgetは、AppBarとScaffoldの下部の間の使用可能なスペースの左上に配置されます。代わりにこのWidgetを中央に配置するには、[Center] Widgetに配置し、それを本体にすることを検討してください。代わりにこのWidgetを展開するには、[SizedBox.expand] に配置することを検討してください。
///
/// 通常は画面に収まるWidgetの列があるが、オーバーフローする可能性があり、その場合はスクロールする必要がある場合は、Scaffoldの本体として [ListView] を使用することを検討してください。これは、本体がスクロール可能なリストである場合にも適しています。
final Widget? body;
例えばサンプルコードではCenter
を使用して中央に配置し、Column
で要素を縦に並べ、その中にText
などを配置しています。
body: 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.headlineMedium,
),
],
),
),
floatingActionButton
コンストラクタの引数floatingActionButton
は画面の右下に表示されるフローティングボタンを指定します。フローティングボタンはアプリでよくあるUIの1つです。
/// [body] の上の右下隅に浮かんで表示されるボタン。
///
/// 通常は [FloatingActionButton]。
final Widget? floatingActionButton;
例えばサンプルコードではFloatingActionButton
を使用しており、onPressedで実行したい処理を指定し、childでアイコンを指定しています。
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
drawer
コンストラクタの引数drawer
は画面サイドからスライドして表示されるビューを指定します。Widget
型で指定できるので任意のUIを指定することが可能です。
/// [body] の横に表示されるパネルで、モバイル デバイスでは非表示になっていることがよくあります。左から右 ([TextDirection.ltr]) または右から左 ([TextDirection.rtl]) にスワイプします。
///
/// 通常は [Drawer] です。
///
/// ドロワーを開くには、[ScaffoldState.openDrawer] 関数を使用します。
///
/// ドロワーを閉じるには、[ScaffoldState.closeDrawer]、[Navigator.pop]
/// のいずれかを使用するか、キーボードの Esc キーを押します。
///
/// {@tool dartpad}
/// モバイルでドロワーの端のスワイプを無効にするには、[Scaffold.drawerEnableOpenDragGesture] を false に設定します。次に、[ScaffoldState.openDrawer] を使用してドロワーを開き、[Navigator.pop] を使用して閉じます。
///
/// ** examples/api/lib/material/scaffold/scaffold.drawer.0.dart のコードをご覧ください **
/// {@end-tool}
final Widget? drawer;
Drawer
Widgetを使用することで簡単にUIを実装することが可能です。AppBar
には自動でドロワー表示用のハンバーガーアイコンが表示され、開閉のスライドアニメーションもよしなに実装してくれます。

class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(title: const Text("ドロワーの例")),
drawer: Drawer(
child: ListView(
padding: EdgeInsets.zero,
children: [
const DrawerHeader(
decoration: BoxDecoration(color: Colors.blue),
child: Text(
"メニュー",
style: TextStyle(color: Colors.white, fontSize: 24),
),
),
ListTile(
leading: const Icon(Icons.home),
title: const Text("ホーム"),
onTap: () {
print("ホームが選択されました");
},
),
ListTile(
leading: const Icon(Icons.settings),
title: const Text("設定"),
onTap: () {
print("設定が選択されました");
},
),
],
),
),
body: const Center(child: Text("メイン画面")),
),
);
}
}
まだまだ勉強中ですので間違っている点や至らぬ点がありましたら教えていただけると助かります。
ご覧いただきありがとうございました。