最後の文字を入力して1秒経過したら検索をかける方法
ロジック
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
part 'home_screen_notifier.freezed.dart';
@freezed
class HomeScreenState with _$HomeScreenState {
const factory HomeScreenState({
@Default(<String>['Adam', 'Bob', 'Cody', 'Dave', 'Fred', 'Emma'])
List<String> nameList,
List<String>? searchedNames,
}) = _HomeScreenState;
}
final homeScreenProvider =
StateNotifierProvider.autoDispose<HomeScreenNotifier, HomeScreenState>(
(ref) => HomeScreenNotifier(),
);
class HomeScreenNotifier extends StateNotifier<HomeScreenState> {
HomeScreenNotifier() : super(const HomeScreenState());
final searchDelayMilliSeconds = 1000;
DateTime _lastChangedDate = DateTime.now();
void delayedSearch({required String text}) {
Future<void>.delayed(Duration(milliseconds: searchDelayMilliSeconds), () {
final now = DateTime.now();
if (now.difference(_lastChangedDate).inMilliseconds >
searchDelayMilliSeconds) {
_lastChangedDate = now;
search(text: text);
}
});
//キーワードが入力されるごとに、検索処理を待たずに_lastChangedDateを更新する
_lastChangedDate = DateTime.now();
}
void search({required String text}) {
if (text.trim().isEmpty) {
state = state.copyWith(searchedNames: null);
} else {
final searchedNames = state.nameList.where((element) {
final name = element.toLowerCase();
final input = text.toLowerCase();
return name.contains(input);
}).toList();
state = state.copyWith(searchedNames: searchedNames);
}
}
}
呼び出し側
TextField(
onChanged: (String? text) => ref
.read(homeScreenProvider.notifier)
.delayedSearch(text: text!),
),
キャプチャ
![](https://assets.st-note.com/img/1677058156173-5AJzB7UEaV.png?width=800)
全体コード
HomeScreenNotifier
import 'package:freezed_annotation/freezed_annotation.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
part 'home_screen_notifier.freezed.dart';
@freezed
class HomeScreenState with _$HomeScreenState {
const factory HomeScreenState({
@Default(<String>['Adam', 'Bob', 'Cody', 'Dave', 'Fred', 'Emma'])
List<String> nameList,
List<String>? searchedNames,
}) = _HomeScreenState;
}
final homeScreenProvider =
StateNotifierProvider.autoDispose<HomeScreenNotifier, HomeScreenState>(
(ref) => HomeScreenNotifier(),
);
class HomeScreenNotifier extends StateNotifier<HomeScreenState> {
HomeScreenNotifier() : super(const HomeScreenState());
final searchDelayMilliSeconds = 1000;
DateTime _lastChangedDate = DateTime.now();
void delayedSearch({required String text}) {
Future<void>.delayed(Duration(milliseconds: searchDelayMilliSeconds), () {
final now = DateTime.now();
if (now.difference(_lastChangedDate).inMilliseconds >
searchDelayMilliSeconds) {
_lastChangedDate = now;
search(text: text);
}
});
//キーワードが入力されるごとに、検索処理を待たずに_lastChangedDateを更新する
_lastChangedDate = DateTime.now();
}
void search({required String text}) {
if (text.trim().isEmpty) {
state = state.copyWith(searchedNames: null);
} else {
final searchedNames = state.nameList.where((element) {
final name = element.toLowerCase();
final input = text.toLowerCase();
return name.contains(input);
}).toList();
state = state.copyWith(searchedNames: searchedNames);
}
}
}
HomeScreen
import 'package:flutter/material.dart';
import 'package:flutter_hooks/flutter_hooks.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
class HomeScreen extends StatelessWidget {
const HomeScreen({super.key});
@override
Widget build(BuildContext context) {
return const Scaffold(
appBar: MyAppBar(title: 'Home'),
body: _Body(),
);
}
}
class _Body extends HookConsumerWidget {
const _Body();
@override
Widget build(BuildContext context, WidgetRef ref) {
final nameList =
ref.watch(homeScreenProvider.select((value) => value.nameList));
final searchedNames = ref.watch(
homeScreenProvider.select((value) => value.searchedNames),
);
return Padding(
padding: const EdgeInsets.symmetric(vertical: 20, horizontal: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const _Title(title: '検索フォーム'),
TextField(
onChanged: (String? text) => ref
.read(homeScreenProvider.notifier)
.delayedSearch(text: text!),
),
const SizedBox(height: 16),
const _Title(title: '検索結果'),
const SizedBox(height: 16),
if (searchedNames == null) _SearchResult(names: nameList),
if (searchedNames != null && searchedNames.isNotEmpty)
_SearchResult(names: searchedNames),
if (searchedNames != null && searchedNames.isEmpty) const _NotFound(),
],
),
);
}
}
class _Title extends StatelessWidget {
const _Title({required this.title});
final String title;
@override
Widget build(BuildContext context) {
return Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
);
}
}
class _SearchResult extends StatelessWidget {
const _SearchResult({required this.names});
final List<String> names;
@override
Widget build(BuildContext context) {
return Expanded(
child: ListView.builder(
itemCount: names.length,
itemBuilder: (context, index) {
final name = names[index];
return _ListTile(name: name);
},
),
);
}
}
class _ListTile extends StatelessWidget {
const _ListTile({required this.name});
final String name;
@override
Widget build(BuildContext context) {
return ListTile(
title: Text(name),
);
}
}
class _NotFound extends StatelessWidget {
const _NotFound();
@override
Widget build(BuildContext context) {
return const Expanded(
child: Center(
child: Text(
'Not found',
style: TextStyle(
fontSize: 30,
fontWeight: FontWeight.bold,
),
),
),
);
}
}
参考
この記事が気に入ったらサポートをしてみませんか?