點燈坊

失くすものさえない今が強くなるチャンスよ

ListView 使用 Event 動態 Filter

Sam Xiao's Avatar 2024-12-18

要實現 ListView 動態 filter,ListView 可綁定另外一個 filter 的 List,並在 TextFieldonChanged event 及時改變要 filter 的 Keyword。

Version

Flutter 3.24.5

Flutter

filter01

  • Android 與 iOS 都成功使用 ListViewEvent 實現動態 Filter

ListView

import 'package:flutter/material.dart';

class Home extends StatefulWidget {
  const Home({super.key});

  
  State<Home> createState() => _Home();
}

class _Home extends State<Home> {
  final List<String> _allItems = [
    'Apple',
    'Banana',
    'Cherry',
    'Date',
    'Elderberry',
    'Fig',
    'Grapes'
  ];

  List<String> _filteredItems = [];

  
  void initState() {
    super.initState();
    _filteredItems = _allItems;
  }

  void onChanged(String value) {
    final keyword = value.trim().toLowerCase();

    final filteredItems = _allItems
        .where((item) => item.toLowerCase().contains(keyword))
        .toList();

    setState(() => _filteredItems = filteredItems);
  }

  
  Widget build(BuildContext context) {
    var appBar = AppBar(
      title: Text(
        'Filter List',
      ),
    );

    var textField = Padding(
      padding: const EdgeInsets.all(8.0),
      child: TextField(
        decoration: InputDecoration(
          hintText: 'Enter Keyword to Filter',
          border: OutlineInputBorder(
            borderRadius: BorderRadius.circular(8.0),
          ),
        ),
        onChanged: onChanged,
      ),
    );

    var listView = Expanded(
      child: ListView.builder(
        itemCount: _filteredItems.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(
              _filteredItems[index],
            ),
          );
        },
      ),
    );

    var body = Column(
      children: [
        textField,
        listView,
      ],
    );

    return Scaffold(
      appBar: appBar,
      body: body,
    );
  }
}

Line 11

final List<String> _allItems = [
  'Apple',
  'Banana',
  'Cherry',
  'Date',
  'Elderberry',
  'Fig',
  'Grapes'
];
  • _allItemsListView 的原始資料,實務上會來自 API

Line 21

List<String> _filteredItems = [];
  • filteredItems:動態 filter 過的資料,會綁定到 ListView

Line 23


void initState() {
  super.initState();
  _filteredItems = _allItems;
}
  • initState():一開始先將所有原始資料給 filter 過的資料

Line 60

var listView = Expanded(
  child: ListView.builder(
    itemCount: _filteredItems.length,
    itemBuilder: (context, index) {
      return ListTile(
        title: Text(
          _filteredItems[index],
        ),
      );
    },
  ),
);
  • ListView:顯示動態 filter 的資掉
  • itemCount:將 filter 過的資料綁定到 ListView
  • itemBuilder():由 filter 過的資料建立 ListTile

Line 47

var textField = Padding(
  padding: const EdgeInsets.all(8.0),
  child: TextField(
    decoration: InputDecoration(
      hintText: 'Enter Keyword to Filter',
      border: OutlineInputBorder(
        borderRadius: BorderRadius.circular(8.0),
      ),
    ),
    onChanged: onChanged,
  ),
);
  • TextField:輸入 keyword 供 filter
  • onChanged():為了讓邊輸入邊 filter,所以寫在 onChanged event

Line 29

void onChanged(String value) {
  final keyword = value.trim().toLowerCase();

  final filteredItems = _allItems
      .where((item) => item.toLowerCase().contains(keyword))
      .toList();

  setState(() => _filteredItems = filteredItems);
}
  • where():Dart 版的 filter(),當 callback 回傳為 true 時為篩選條件
  • contains():只要包含一部分 keyword 即可,相當於 SQL 的 like
  • 將 filter 結果寫入 state

Conclusion

  • 要實現 ListView 動態 filter,這是最基本使用 event 的寫法,亦可使用更像 Vue computedgetter 實現