今回は、Flutterで検索機能を実装する方法をご紹介します。
配列の中から、キーワードが含まれる要素だけを表示したいケースがよくありますよね。
そんな時にサクッとコピペで実装できる、テキスト検索の方法をご紹介します。
この記事では、
['Bob', 'John', 'Fred', 'Emma','Charlotte']
という名前の配列を使って、検索機能を説明していきます。
まずは、名前が入っている配列(データの配列)と検索結果が入る配列の2つを用意します。
static const nameList = ['Bob', 'John', 'Fred', 'Emma', 'Charlotte'];
List<String> searchedNames = [];
今回の例では、以下のような実装をしています。
TextField
のonChanged
部分のsearch
の処理によって、検索結果searchedNames
が変化します。
build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('検索'), centerTitle: true),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('検索フォーム', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
TextField(
onChanged: search,
decoration: InputDecoration(contentPadding: EdgeInsets.symmetric(horizontal: 16)),
),
const SizedBox(height: 16),
Text('検索結果', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
Text('Result : ${searchedName.toString()}')
],
),
),
);
}
Widget
setState
が入っています) void search(String text) {
setState(() {
if (text.trim().isEmpty) {
searchedNames = [];
} else {
searchedNames = nameList.where((element) => element == text).toList();
}
});
}
void search(String text) {
setState(() {
if (text.trim().isEmpty) {
searchedNames = [];
} else {
searchedNames = nameList.where((element) => element.contains(text)).toList();
}
});
}
検索処理にAPIを利用する場合は、TextField
のonChanged
のイベントが走るたびに、APIを叩くことになります。
結果的に動作が重くなったり、検索自体がうまくいかない場合があります。
そんな時には、ユーザーが入力し終えたら検索処理をする
という実装をすると良いと思います。
「ユーザーが最後の文字を打ってから、1秒経過したら検索をかける」という場合の例をご紹介します。
まずは、遅らせる秒数を定義します。(各自で調整してください)
そして、最後に文字を入力した日時をStatefulWidgetのStateに保持します。
static const searchDelayMillSec = 1000;
DateTime _lastChangedDate = DateTime.now();
検索を遅らせる処理は以下の通りです。
_lastChangedDate
が更新されるFuture.delayed
の中の検索処理が走る
という流れです。 void delayedSearch(String text) {
Future.delayed(const Duration(milliseconds: searchDelayMillSec), () {
final nowDate = DateTime.now();
if (nowDate.difference(_lastChangedDate).inMilliseconds > searchDelayMillSec) {
_lastChangedDate = nowDate;
search(text);
}
});
//キーワードが入力されるごとに、検索処理を待たずに_lastChangedDateを更新する
_lastChangedDate = DateTime.now();
}
Future.delayed
の中の検索処理が走る前に次の文字を入力すると、_lastChangedDate
が再度更新され、
if (nowDate.difference(_lastChangedDate).inMilliseconds > searchDelayMillSec)
という条件はfalse
になります。
したがって、1秒以内に次の文字が入力された場合には、検索処理は走りません。
以下にコード全文をコピペ用に置いておきます。
少し難しいので、実際に動かしてみるとイメージがつきやすいかもしれません。
import 'package:flutter/material.dart';
void main() => runApp(const SearchApp());
class SearchApp extends StatelessWidget {
const SearchApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return const MaterialApp(
home: SearchScreen(),
);
}
}
class SearchScreen extends StatefulWidget {
const SearchScreen({Key? key}) : super(key: key);
State<SearchScreen> createState() => _SearchScreenState();
}
class _SearchScreenState extends State<SearchScreen> {
static const searchDelayMillSec = 1000;
static const nameList = ['Bob', 'John', 'Fred', 'Emma', 'Charlotte'];
List<String> searchedNames = [];
DateTime _lastChangedDate = DateTime.now();
void search(String text) {
setState(() {
if (text.trim().isEmpty) {
searchedNames = [];
} else {
searchedNames = nameList.where((element) => element.contains(text)).toList();
}
});
}
void delayedSearch(String text) {
Future.delayed(const Duration(milliseconds: searchDelayMillSec), () {
final nowDate = DateTime.now();
if (nowDate.difference(_lastChangedDate).inMilliseconds > searchDelayMillSec) {
_lastChangedDate = nowDate;
search(text);
}
});
//キーワードが入力されるごとに、検索処理を待たずに_lastChangedDateを更新する
_lastChangedDate = DateTime.now();
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text('テキスト検索'), centerTitle: true),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('検索フォーム', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
TextField(
onChanged: delayedSearch,
decoration: InputDecoration(contentPadding: EdgeInsets.symmetric(horizontal: 16)),
),
const SizedBox(height: 16),
Text('検索結果', style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
const SizedBox(height: 16),
Text('Result : ${searchedNames.toString()}')
],
),
),
);
}
}
いかがでしたか。
StatefulWidgetを用いて、原始的な方法でFlutterでのテキスト検索を実装してみました。
こちらの例を参考にカスタマイズしていただけたら幸いです。
テキストフィールドを実装するに当たってのコツは、こちらで紹介しています。 https://www.kamo-it.org/blog/textfield-focus/
可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。
Read More可茂IT塾ではFlutterインターンを募集しています!可茂IT塾のエンジニアの判断で、一定以上のスキルをを習得した方には有給でのインターンも受け入れています。
Read More