[Flutter/dart]Mapの要素の並び替えや抽出


概要


Flutterでのアプリ作成において、僕はユーザデータなどをMapで保持することが多いです(listだとデータを削除したときのindexのずれへの対応が面倒なため)。その場合、データを画面上に表示する際に、順番を規定したり特定の条件のデータだけを取り出したりするには一工夫必要です。(C#のdictionaryはlistと同じように扱えるのにな、、、)



方法


MapをMapEntryのlistに変換すればwhereやsortなどのメソッドが使えます。あとは使う側(基本的にはWidgetのbuildのはず)で引数をList<MapEntry>に取ればよい。


並び替え


以下では複数の並び替え方法に対応できるように比較方法をデリゲートで渡すメソッドにしています。

class UserData{
  String name;
  int score;

  UserData({this.name, this.score});
}


class MyHomePageState extends State<MyHomePage> {

  Map<int, UserData> data;

  @override
  void initState() {
    data={
      0: UserData(name: "Alice", score: 90),
      1: UserData(name: "Bob", score: 87),
      2: UserData(name: "Charlie", score: 98),
    };
  }

  @override
  Widget build(BuildContext context) {

    //scoreの低い順に並び替え
    var _entries=_orderData(data, (a, b) => (a.value as 
    UserData).score.compareTo((b.value as UserData).score));

    return Scaffold(
        appBar: AppBar(
          title: Text('demo'),
        ),
        body: Container(
          padding: const EdgeInsets.all(16),
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: _entries.map((e) => ListTile(
              title: Text(
                "${e.value.name}: ${e.value.score}",
                style: TextStyle(
                  fontSize: 16
                ),
              ),
            )).toList(),
          )
        )
    );
  }

  //これ!
  List<MapEntry> _orderData(Map<int, dynamic>maps, int 
  Function(MapEntry<int, dynamic>, MapEntry<int, dynamic>) sorter){
    return maps.entries.toList()..sort((a, b)=> sorter(a, b));
  }
}



抽出


同じく条件を表すデリゲートを引数にしたメソッドを作ります。

List<MapEntry> _selectData(Map maps, bool Function(MapEntry) predicate){
  return maps.entries.toList().where((element) => 
      predicate(element)).toList();
}

@override
Widget build(BuildContext context) {

  //scoreが90以上のデータを抽出
  var _entries=_selectData(data, (e) => e.value.score>90);

  return Scaffold(
      appBar: AppBar(
        title: Text('demo'),
      ),
      body: Container(
        padding: const EdgeInsets.all(16),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: _entries.map((e) => ListTile(
            title: Text(
              "${e.value.name}: ${e.value.score}",
              style: TextStyle(
                fontSize: 16
              ),
            ),
          )).toList(),
        )
      )
  );
}

最後に


個人としては、データ保持は検索や削除のしやすさを考えてMap、widgetに一覧表示する場合は並び替えや条件抽出のしやすさを考えてList<MapEntry>を使っています(あくまで個人的な手法ですが)。

最新記事

すべて表示

現象 flutter_local_notificationを使って通知を作成。通知タップ時の動作も登録。 通知が来た時にアプリが立ち上がっている(foregroundでもbackgroundでも)と、期待通りの動作となる。 しかし、通知が来た時にアプリが終了していると、通知をタップしても登録した動作とならない。 原因と解決策 アプリが終了すると登録した動作は消えてしまうらしい(参考)。 Note:

概要 Firebase Messagingでメッセージを送信し、通知がタップされたら何らかの動作を実行する(例:特定のページに移動する)という場合は多いかと思います。 通知タップ時にアプリがforeground状態(つまりアプリを開いている)ならば簡単なのですが、アプリがbackground状態にある場合は少し工夫が必要です。 方法 FirebaseMessaging.onBackgroundMe

現象 FirebaseMessaging.onBackgroundMessage()でタイトルのエラーが発生。 原因と解決策 上記メソッドの引数に渡すデリゲートはトップレベル関数でなければならない。自分の場合はインスタンスメソッドを渡していたのでエラーとなった。 渡すメソッドをトップレベル関数にするとエラーは解消した。 @main.dart void main() async{ //略 F

靴を大切にしよう!靴管理アプリ SHOES_KEEP

納品:iPhone6.5①.png

靴の履いた回数、お手入れ回数を管理するアプリです。

google-play-badge.png
Download_on_the_App_Store_Badge_JP_RGB_blk_100317.png

テーマ日記:テーマを決めてジャンルごとに記録

訂正①2040×1152.jpg

ジャンルごとにテーマ、サブテーマをつけて投稿、記録できる日記アプリです。

google-play-badge.png