スポンサーリンク
grouped_list を使用する
Flutterを使って、グループ単位でListViewを表示するにはどうすればいいのかという壁にぶつかったのですが、「grouped_list」 というパッケージを作ってくださっている方がいました。
これを使うと非常に簡単に実装することができます。
grouped_listについて使用方法を解説します。
使用手順
pubspec.yamlに追記
dependencies:
grouped_list: ^3.7.1
パッケージをimportする
import 'package:grouped_list/grouped_list.dart';
Widgetを作成する
まずはpub.devのサンプルコードを試してみます。
List<Map<String, String>> _elements = createElements();
return GroupedListView<dynamic, String>(
elements: _elements,
groupBy: (element) => element['key'],
groupSeparatorBuilder: (String groupByValue) => Text(groupByValue),
itemBuilder: (context, dynamic element) => Text(element['text']),
itemComparator: (item1, item2) => item1['text'].compareTo(item2['text']), // optional
useStickyGroupSeparators: true, // optional
floatingHeader: true, // optional
order: GroupedListOrder.ASC, // optional
);
List<Map<String, String>> createElements() {
List<Map<String, String>> res = List();
Map<String, String> map1 = {'key': 'key1', 'text': 'text1-1'};
Map<String, String> map2 = {'key': 'key2', 'text': 'text2-3'};
Map<String, String> map3 = {'key': 'key2', 'text': 'text2-2'};
Map<String, String> map4 = {'key': 'key2', 'text': 'text2-1'};
Map<String, String> map5 = {'key': 'key3', 'text': 'text3-1'};
res.add(map1);
res.add(map2);
res.add(map3);
res.add(map4);
res.add(map5);
return res;
}
各引数の説明
引数 | 型 | 説明 |
elements | List<T> | 表示したいリストを設定します。Tにはグルーピングのキーを設定できる値を持たせておく必要があります。 |
groupBy | E | elementsに設定した値のTの一つが引数のFunctionです。渡されたTからグルーピングに使用する値を返却してください。 |
groupSeparatorBuilder | Widget | グルーピングごとのセパレーターを設定します。サンプルだとgroupByで返却している値をそのまま使用しています。 |
itemBuilder | Widget | 1つのデータごとの表示するWidgetを設定します。 |
itemComparator | int | グループ内のソート条件を設定します。設定は任意です。 |
useStickyGroupSeparators | bool | グルーピングごとのセパレーターを固定するかどうかの設定です。trueにした場合はスクロールしたときにセパレーターが固定されます。おそらく多くの人が求めているものはこれをtrueにした際の挙動です。 |
floatingHeader | bool | useStickyHeaderをtrueに設定している場合に、固定されたセパレーターの挙動を制御します。trueに設定すると、セパレーターが浮き上がりデータに被らなくります。falseにするとヘッダーの領域にデータが入ってくるとデータが見えなくなります。 |
order | GroupedListOrder | 昇順、降順を設定できます。 |
groupSeparatorBuilderとitemBuilderの設定値がキレイな画面を作るポイントですね。
私のサンプルだと文字を返却しているので見た目がかなりしょぼいです。
チャット画面を作成してみる
pub.devに画像だけサンプルで載っているチャット画面を再現してみました。
すごくいい感じ!
チャットの機能を導入しようと思ったらこんな感じで簡単に作れちゃいますね!
grouped_list おすすめです。
ソースはこんな感じです。
List<Map<String, String>> _elements = createElements();
return GroupedListView<dynamic, String>(
elements: _elements,
groupBy: (element) => element['key'],
groupSeparatorBuilder: (String groupByValue) =>
_createHeader(groupByValue),
itemBuilder: (context, dynamic element) =>
_createItem(context, element),
itemComparator: (item1, item2) =>
item1['time'].compareTo(item2['time']),
useStickyGroupSeparators: true,
floatingHeader: false,
stickyHeaderBackgroundColor: Colors.white.withOpacity(0.5),
order: GroupedListOrder.ASC, // optional
);
List<Map<String, String>> createElements() {
List<Map<String, String>> res = List();
Map<String, String> map1 = {
'key': '2020年12月2日',
'text': '明日、夜ご飯食べに行かない?',
'lr': 'l',
'time': '18:12'
};
Map<String, String> map2 = {
'key': '2020年12月3日',
'text': 'すまん、返事遅れた!',
'lr': 'r',
'time': '09:24'
};
Map<String, String> map3 = {
'key': '2020年12月3日',
'text': 'いいね!行こうぜ!',
'lr': 'r',
'time': '09:26'
};
Map<String, String> map4 = {
'key': '2020年12月3日',
'text': 'じゃあ、19時に新橋駅前に集合で',
'lr': 'l',
'time': '09:35'
};
Map<String, String> map5 = {
'key': '2020年12月5日',
'text': '頭いてええええええ',
'lr': 'r',
'time': '14:12'
};
Map<String, String> map6 = {
'key': '2020年12月5日',
'text': '二日酔いじゃん。えっ昨日酒飲んでなくない',
'lr': 'l',
'time': '14:22'
};
Map<String, String> map7 = {
'key': '2020年12月5日',
'text': 'ぐわあああああああああああああああ',
'lr': 'r',
'time': '14:25'
};
Map<String, String> map8 = {
'key': '2020年12月5日',
'text': 'あかん、救急車呼ばな',
'lr': 'l',
'time': '14:29'
};
res.add(map1);
res.add(map2);
res.add(map3);
res.add(map4);
res.add(map5);
res.add(map6);
res.add(map7);
res.add(map8);
return res;
}
Widget _createHeader(String groupByValue) {
return Padding(
padding: const EdgeInsets.only(
top: 8.0, bottom: 8.0, left: 120.0, right: 120.0),
child: ClipRRect(
borderRadius: BorderRadius.all(Radius.circular(12.0)),
child: Container(
color: Colors.lightBlue,
child: Text(
groupByValue,
style:
TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
textAlign: TextAlign.center,
),
)),
);
}
Widget _createItem(BuildContext context, element) {
return Padding(
padding: element['lr'] == 'l'
? const EdgeInsets.only(top: 4.0, bottom: 4.0, right: 45.0, left: 8.0)
: const EdgeInsets.only(top: 4.0, bottom: 4.0, left: 45.0, right: 8.0),
child: Card(
elevation: 10.0,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8.0)),
color: Colors.white,
//shadowColor: Colors.white24,
child: ListTile(
leading: element['lr'] == 'r'
? Padding(
padding: const EdgeInsets.only(top: 4.0),
child: Text(element['time'],),
)
: Icon(Icons.person_outline_rounded),
title: Text(element['text']),
trailing: element['lr'] == 'l'
? Text(element['time'])
: Icon(Icons.person_rounded),
),
),
);
}
スポンサーリンク
スポンサーリンク