はじめに
FlutterでPopupMenuButtonを使ってページ遷移機能を実装します。
次図のように、PopupMenuButtonで選択することでページ画面を遷移させるようにします。
実装
-
lib配下のディレクトリ構成
次図のような構成で実装しています。
|-- lib
| |-- main.dart
| |-- app_home.dart
| |-- pages
| |-- page1.dart
| |-- page2.dart -
lib/main.dart
StatefulWidgetのAppHomeを呼び出しているだけです。
import 'package:flutter/material.dart';
import 'package:app_1/app_home.dart'; // プロジェクト名はapp_1としています
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
// StatefulWidgetであるAppHomeの呼び出し
home: const AppHome(title: 'Demo: Page Transition'),
);
}
}
-
lib/app_home.dart
_AppStateクラスにおいて、PopupMenuButtonのクリック時の動作を定義するonSelectedを利用して_valueを切り替えています。
具体的には、onSelectedに指定した_select関数でsetState関数を呼び出し、_valueを切り替えるとともに状態(State)をリビルドしています。(参考: Stateのライフサイクルについて)
これにより、Scaffoldのbodyが切り替わります。
また、PopupMenuButtonに付しているページのアイコンはこちらから選んでいます。
import 'package:flutter/material.dart';
import 'package:app_1/pages/page1.dart';
import 'package:app_1/pages/page2.dart';
// ページ情報(ページ番号、ページ名、アイコン)を定義するクラス
class MenuItem {
int value;
String name;
IconData icon;
MenuItem(
this.value,
this.name,
this.icon
);
}
// ページ情報のリストを定義
List<MenuItem> _list = <MenuItem>[
MenuItem(1, 'Page 1', Icons.filter_1),
MenuItem(2, 'Page 2', Icons.filter_2),
];
// AppHomeクラス(StatefulWidget)
class AppHome extends StatefulWidget {
const AppHome({super.key, required this.title});
final String title;
@override
State<AppHome> createState() => _AppState();
}
// _AppStateクラス(AppHomeの内部State)
class _AppState extends State<AppHome> {
MenuItem _value = _list[0];
bool favorite = false;
void _select(MenuItem value) {
// setStateを契機に状態がリビルドされる
setState(() {
_value = value;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
appBar: AppBar(
title: Text(widget.title),
actions: <Widget>[
PopupMenuButton(
initialValue: _value,
icon: Icon(Icons.more_vert),
enabled: true,
// onSelectedで選択時の挙動を定義する
onSelected: _select,
itemBuilder: (context) {
return _list.map((MenuItem choice) {
return PopupMenuItem(
value: choice,
child: ListTile(
leading: Icon(choice.icon),
title: Text(choice.name),
),
);
}).toList();
}
)
],
),
body: PageWidget(),
),
);
}
PageWidget() {
if(_value.value == _list[0].value) {
return Container(child: Page1(title: widget.title));
}
if(_value.value == _list[1].value) {
return Container(child: Page2(title: widget.title));
}
}
}
-
lib/pages/page1.dart
ページ1をStatelessWidgetで作成。
import 'package:flutter/material.dart';
class Page1 extends StatelessWidget {
const Page1({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: this.title,
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
body: Center(
child: Text(
'Page 1',
style: TextStyle(
fontSize: 24,
fontStyle: FontStyle.italic,
color: Colors.blue,
),
textAlign: TextAlign.center
),
),
),
);
}
}
-
lib/pages/page2.dart
ページ2をStatelessWidgetで作成。
import 'package:flutter/material.dart';
class Page2 extends StatelessWidget {
const Page2({super.key, required this.title});
final String title;
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: this.title,
theme: ThemeData(primarySwatch: Colors.blue),
home: Scaffold(
body: Center(
child: Text(
'Page 2',
style: TextStyle(
fontSize: 24,
fontStyle: FontStyle.italic,
color: Colors.red
),
textAlign: TextAlign.center
),
),
),
);
}
}
動作環境
- WSL2(Ubuntu20.04 LTS)
- Flutter: 2.13.0
- Dart: 2.18.0
- DevTools: 2.12.2
おわりに
Flutterは社内ハッカソンに参加した際のデモに使用し、興味を持ちました。
今後も勉強していきたいと思っています。