Dartでリストをフィルタリングする方法!(where)【Flutter】

Dart(Flutter)でリストをフィルタリングする方法について書いています。
下記のような場合に、使用すると便利です。

  • リスト内の欲しいデータを一括で取り出したい
  • ListViewに表示するデータをフィルタリングしたい

この記事では、Dartのwhereメソッドを使ったリストのフィルタリング方法を解説します。

※ 載せているコードはFlutter 3.29.3 / Dart 3.7.2で動作を確認しています。

Listのwhereメソッドとは?

whereメソッドは、リストの中から条件に合う要素だけを抽出するための便利なメソッドです。
元のリストは変更せず、新しいリストを返すため、安全に使用できます。

基本的な使い方

void main() {
  List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];

  // 偶数だけを取り出す
  var evenNumbers = numbers.where((number) => number % 2 == 0);

  print(evenNumbers.toList()); // [2, 4, 6, 8, 10]
}

numbersリストを作成しています。
その次の処理で、numbersリストからwhereメソッドを呼び出しています。

whereメソッド内で書いた条件のtrueのデータだけがリストになって返ってきます。
number % 2 == 0」でリストのそれぞれの数値が偶数のものだけ取得できます。

※ whereメソッドはIterableを返すため、リストとして使いたい場合は.toList()を呼び出す必要があります。

リストのフィルタリングの使用例

リストをいろいろフィルタリングしている使用例です。

1. 数値のフィルタリング

数値のリストから特定のスコア以上のものを取得して出力しています。

void main() {
  List<int> scores = [45, 67, 89, 23, 95, 78, 56];

  // 60点以上の成績だけを抽出
  var passingScores = scores.where((score) => score >= 60).toList();

  print(passingScores); // [67, 89, 95, 78]
}

scoresリストを作成して、whereメソッドを呼び出しています。
数値比較で「score >= 60」としているので、「60」以上のデータが取得できます。

2. 文字列のフィルタリング

文字列のリストから、aで始まる文字列のみや6文字以上のリストを抽出しています。

void main() {
  List<String> fruits = ['apple', 'banana', 'avocado', 'cherry', 'apricot'];

  // "a"で始まる果物を抽出
  var aFruits = fruits.where((fruit) => fruit.startsWith('a')).toList();

  print(aFruits); // [apple, avocado, apricot]

  // 6文字以上の果物を抽出
  var longNames = fruits.where((fruit) => fruit.length >= 6).toList();

  print(longNames); // [banana, avocado, cherry, apricot]
}

fruitsリストを作成して、次の処理で”a”で始まる文字列だけをリストから抽出しています。
その次の処理では、文字数が6文字以上のデータを抽出して取得しました。

3. オブジェクトのフィルタリング

オブジェクトのリストについても、プロパティにアクセスすることでフィルタリング可能です。

class User {
  String name;
  int age;
  bool isPremium;

  User(this.name, this.age, this.isPremium);
}

void main() {
  List<User> users = [
    User('太郎', 25, true),
    User('花子', 30, false),
    User('次郎', 22, true),
    User('美咲', 28, false),
  ];

  // プレミアム会員だけを抽出
  var premiumUsers = users.where((user) => user.isPremium).toList();

  print('プレミアム会員: ${premiumUsers.map((u) => u.name).join(', ')}');
  // プレミアム会員: 太郎, 次郎

  // 25歳以上のユーザーを抽出
  var adults = users.where((user) => user.age >= 25).toList();

  print('25歳以上: ${adults.map((u) => u.name).join(', ')}');
  // 25歳以上: 太郎, 花子, 美咲
}

usersというUserオブジェクトを4つ持っているリストを作成しています。
Userオブジェクトは名前・年齢・プレミアム会員かを保持しています。

最初にwhereメソッドでプレミアム会員のみを抽出しています。
その次の処理で、25歳以上のユーザーを抽出しています。

このようにオブジェクトのプロパティにアクセスすることで、オブジェクトのリストもフィルタリングできます。

Flutter開発での実践例

実際にFlutterでリストを作って、フィルタリングしてみます。

リストビューでのフィルタリング

この例では、Todoリストを完了状態でフィルタリングして表示する処理を行っています。

class TodoApp extends StatefulWidget {
  @override
  _TodoAppState createState() => _TodoAppState();
}

class _TodoAppState extends State<TodoApp> {
  List<Map<String, dynamic>> todos = [
    {'title': '買い物', 'completed': false},
    {'title': '掃除', 'completed': true},
    {'title': '勉強', 'completed': false},
    {'title': '運動', 'completed': true},
  ];

  bool showCompletedOnly = false;

  @override
  Widget build(BuildContext context) {
    // 表示するTodoをフィルタリング
    var displayTodos = showCompletedOnly
        ? todos.where((todo) => todo['completed'] == true).toList()
        : todos;

    return Scaffold(
      appBar: AppBar(
        title: const Text('Todo App'),
      ),
      body: ListView.builder(
        itemCount: displayTodos.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(displayTodos[index]['title']),
          );
        },
      ),
    );
  }
}

まず、todosにはタイトルと完了状態(completed)を持つMapのリストを用意しています。
showCompletedOnlyは「完了したTodoだけを表示するかどうか」を制御するフラグです。

buildメソッド内では、このフラグの状態に応じて表示用のリストを切り替えています。

 var displayTodos = showCompletedOnly
        ? todos.where((todo) => todo['completed'] == true).toList()
        : todos;

このようにUIに表示する直前でリストをフィルタリングすることで、元データを変更せずに表示内容だけを切り替えられます。
今回のようにListViewの表示を切り替えるのは、下記のようなケースで使用されます。

  • 完了 / 未完了の切り替え
  • カテゴリ別表示
  • お気に入りのみ表示

検索機能の実装

class _SearchableListState extends State<SearchableList> {
  List<String> allItems = ['りんご', 'バナナ', 'オレンジ', 'ぶどう', 'いちご'];
  String searchQuery = '';

  @override
  Widget build(BuildContext context) {
    // 検索クエリでフィルタリング
    var filteredItems = allItems
        .where((item) => item.contains(searchQuery))
        .toList();

    return Scaffold(
      appBar: AppBar(
        title: const Text('Search App'),
      ),
      body: Padding(padding: EdgeInsets.all(16),
      child: Column(
          children: [
            TextField(
              onChanged: (value) {
                setState(() {
                  searchQuery = value;
                });
              },
              decoration: InputDecoration(hintText: '検索...'),
            ),
            Expanded(
              child: ListView.builder(
                itemCount: filteredItems.length,
                itemBuilder: (context, index) {
                  return ListTile(title: Text(filteredItems[index]));
                },
              ),
            ),
          ],
        )
      )
    );
  }
}

こちらの例では、入力された検索文字列に応じてリストを動的にフィルタリングしています。
allItemsは元となる全データのリストで、searchQueryにはユーザーが入力した検索文字列を保持します。

buildメソッド内で、whereを使って検索条件に一致する要素だけを抽出しています。

var filteredItems = allItems
    .where((item) => item.contains(searchQuery))
    .toList();

containsメソッドを使うことで、入力した文字列を含むアイテムだけが抽出されて、表示されます。

TextFieldonChangedでは、入力が変わるたびにsearchQueryを更新し、setStateによって画面を再描画しています。
この実装をすることで、リアルタイムに検索できます。

複数条件でのフィルタリング

ANDORを合わせることで、複雑な条件でフィルタリングできます。

AND条件

ANDを使用した例です。

void main() {
  List<int> numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];

  // 偶数かつ5より大きい数
  var filtered = numbers.where((n) => n % 2 == 0 && n > 5).toList();

  print(filtered); // [6, 8, 10, 12]
}

リストの中の数値が2で割ったときに余りが0で、5以上のものを抽出しています。

OR条件

ORを使用した例です。

void main() {
  List<String> words = ['cat', 'dog', 'car', 'dart', 'door'];

  // "c"で始まるか"d"で始まる単語
  var filtered = words.where((word) => 
    word.startsWith('c') || word.startsWith('d')
  ).toList();

  print(filtered); // [cat, dog, car, dart, door]
}

cもしくはdから始まる文字列をリストから抽出しています。

複雑な条件

オブジェクトのリストに対して、2つの条件でフィルタリングしています。

class Product {
  String name;
  double price;
  String category;

  Product(this.name, this.price, this.category);
}

void main() {
  List<Product> products = [
    Product('ノートPC', 80000, '電子機器'),
    Product('マウス', 2000, '電子機器'),
    Product('デスク', 15000, '家具'),
    Product('チェア', 12000, '家具'),
  ];

  // 電子機器カテゴリで10000円以下の商品
  var affordableElectronics = products.where((product) =>
    product.category == '電子機器' && product.price <= 10000
  ).toList();

  print(affordableElectronics.map((p) => p.name).join(', ')); // マウス
}

Productオブジェクトのリストを作成してフィルタリングしています。
categoryが電子機器で、priceが10000以下に絞っています。

結果、マウスのみが抽出されます。

まとめ

Dartのwhereメソッドは、リストをフィルタリングするための強力で柔軟なツールでした。
主なポイントは以下の通りです。

  • whereメソッドは条件に合う要素だけを抽出できる
  • 戻り値はIterableなので、リストとして使う場合は.toList()が必要
  • 複数の条件を組み合わせたり、他のメソッドと連鎖させることができる
  • Flutter開発では検索機能やフィルタリング機能の実装に非常に便利

この記事で紹介したテクニックを使えば、Dart(Flutter)でのリスト操作がより簡単で効率的になります。
リストを使う際に活用していきましょう。

コメント