🎉
【Flutter】ドロップダウンはExpansionPanelではなくExpansionTileを使おう!
はじめに
以下のドロップダウンはモバイルアプリではよく見る動きだと思います。(アコーディオンメニューと呼ばれる動き)
ExpansionTileというWidgetを用いることでこの動きを実装することができます。
ExpansionTileと似たExpansionPanelListもあるのですが、こちらはかなり使いづらいです。こちらの不便さについても紹介します。
ExpansionTile
ExpansionTile(
onExpansionChanged: (bool changed) {
//開いた時の処理を書ける
},
title: Text('title'),
children: <Widget>[
//ここにドロップダウンで出したい部分を書く
],
),
ExpansionTileで囲むだけでシンプルにドロップダウンの動きを実装できます。
他にもcolorやiconをカスタマイズすることができます。最後にサンプルコードを置いておきますのでよろしければお使いください。
詳しくは以下のドキュメントを参照してみてください!
ExpansionTileサンプルコード
import 'package:flutter/material.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
bool cute = false;
bool beautiful = false;
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text('ExpansionTile'),
),
body: Column(
children: <Widget>[
ExpansionTile(
title: Text('好きなタイプは?'),
onExpansionChanged: (bool changed) {
setState(() {
cute = false;
beautiful = changed;
});
},
children: <Widget>[
CheckboxListTile(
value: cute,
onChanged: (checked) {
setState(() {
cute = checked!;
});
},
title: Text('可愛い系'),
),
CheckboxListTile(
value: beautiful,
onChanged: (checked) {
setState(() {
beautiful = checked!;
});
},
title: Text('美人系'),
),
],
),
ExpansionTile(
title: Text('説明'),
children: <Widget>[
Container(
height: 50,
child: Text('好きなタイプにチェックを入れよう!'),
),
],
),
],
),
);
}
}
ExpansionPanelList
こちらのドキュメントによるとExpansionPanelとは”ExpansionPanelListの子として使用される拡張パネルを作成”するものだそうです。ExpansionPanelListの子要素でしか使われないものと思われる。
ExpansionPanelListの動きはこのようになります。
一見良い感じに動いていますが、1番と2番の間に空白ができてしまい、空白を調整することができないため使い勝手悪いなという印象です。
最小構成が以下のコードになりそうです。(もっと良い方法あるかもしれないですが、ExpansionTileを使う方がシンプルにわかりやすく実装できると思います!)
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'expansionPanelList',
home: Scaffold(
appBar: AppBar(),
body: const MyStatefulWidget(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({Key? key}) : super(key: key);
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
//拡大されているかどうかのbool値を持つクラスを作るのが良いっぽい
class Item {
Item({
required this.index,
this.isExpanded = false,
});
String index;
bool isExpanded;
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
final List<Item> _data = [
Item(index: '1番'),
Item(index: '2番'),
];
Widget build(BuildContext context) {
return SingleChildScrollView(
child: Container(
child: ExpansionPanelList(
expansionCallback: (int index, bool isExpanded) {
//タップした時の挙動を書く
//どのタイルを開いたか書く
setState(() {
_data[index].isExpanded = !isExpanded;
});
},
//childrenの中はExpansionPanelのみ
children: _data.map<ExpansionPanel>(
(Item item) {
return ExpansionPanel(
headerBuilder: (BuildContext context, bool isExpanded) {
return ListTile(title: Text(item.index));
},
body: ListTile(title: Text(item.index)),
//isExpandedがtrueだとpanelが開いた状態になる
isExpanded: item.isExpanded,
);
},
).toList(),
),
),
);
}
}
う〜む、わかりづらい!
比較
ExpansionTile | ExpansionPanel |
---|---|
わかりやすさ、カスタマイズのしやすさでExpansionTileが圧倒的おすすめ!
Discussion