Open2
Flutter keyword search

Flutter keyword search
タイトルをキーワード検索できるようにしたい。 setState()
の中で、takeを呼ぶ。where, containsも使う。
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: TodoListScreen(),
);
}
}
class Todo {
final int userId;
final int id;
final String title;
final bool completed;
Todo({required this.userId, required this.id, required this.title, required this.completed});
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
userId: json['userId'],
id: json['id'],
title: json['title'],
completed: json['completed'],
);
}
}
class TodoListScreen extends StatefulWidget {
_TodoListScreenState createState() => _TodoListScreenState();
}
class _TodoListScreenState extends State<TodoListScreen> {
List<Todo> todos = [];
List<Todo> filteredTodos = [];
TextEditingController searchController = TextEditingController();
void initState() {
super.initState();
fetchTodos();
}
void dispose() {
searchController.dispose();
super.dispose();
}
Future<void> fetchTodos() async {
try {
final response = await Dio().get('https://jsonplaceholder.typicode.com/todos');
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
setState(() {
todos = data.map((item) => Todo.fromJson(item)).take(10).toList();
filteredTodos = todos;
});
}
} catch (e) {
print('Error fetching todos: $e');
}
}
void searchTodos() {
String query = searchController.text;
setState(() {
if (query.isEmpty) {
filteredTodos = todos;
} else {
filteredTodos = todos.where((todo) {
bool matchesUserId = false;
try {
int queryInt = int.parse(query);
matchesUserId = todo.userId == queryInt;
} catch (e) {
// query is not a number, ignore this part
}
return matchesUserId || todo.title.toLowerCase().contains(query.toLowerCase());
}).toList();
}
});
}
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: searchController,
decoration: const InputDecoration(
labelText: 'Search by User ID or Title',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
),
),
),
const SizedBox(width: 10),
ElevatedButton(
onPressed: searchTodos,
child: const Text('Search'),
),
],
),
),
Expanded(
child: ListView.builder(
itemCount: filteredTodos.length,
itemBuilder: (context, index) {
final todo = filteredTodos[index];
return ListTile(
title: Text(todo.title),
subtitle: Text('User ID: ${todo.userId}'),
trailing: todo.completed ? const Icon(Icons.check_box) : const Icon(Icons.check_box_outline_blank),
);
},
),
),
],
),
);
}
}

Controllerを使う
addListener, removeListenerを使ってみる。
import 'package:flutter/material.dart';
import 'package:dio/dio.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
Widget build(BuildContext context) {
return MaterialApp(
title: 'Todo App',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const TodoListScreen(),
);
}
}
class Todo {
final int userId;
final int id;
final String title;
final bool completed;
const Todo({required this.userId, required this.id, required this.title, required this.completed});
factory Todo.fromJson(Map<String, dynamic> json) {
return Todo(
userId: json['userId'],
id: json['id'],
title: json['title'],
completed: json['completed'],
);
}
}
class TodoListScreen extends StatefulWidget {
const TodoListScreen({Key? key}) : super(key: key);
_TodoListScreenState createState() => _TodoListScreenState();
}
class _TodoListScreenState extends State<TodoListScreen> {
List<Todo> todos = [];
final TextEditingController searchController = TextEditingController();
String searchQuery = '';
void initState() {
super.initState();
fetchTodos();
searchController.addListener(_onSearchChanged);
}
void dispose() {
searchController.removeListener(_onSearchChanged);
searchController.dispose();
super.dispose();
}
void _onSearchChanged() {
setState(() {
searchQuery = searchController.text;
});
}
Future<void> fetchTodos() async {
try {
final response = await Dio().get('https://jsonplaceholder.typicode.com/todos');
if (response.statusCode == 200) {
final List<dynamic> data = response.data;
setState(() {
todos = data.map((item) => Todo.fromJson(item)).toList();
});
}
} catch (e) {
print('Error fetching todos: $e');
}
}
bool _filterTodo(Todo todo) {
if (searchQuery.isEmpty) return true;
final lowercaseQuery = searchQuery.toLowerCase();
return todo.title.toLowerCase().contains(lowercaseQuery) ||
todo.userId.toString() == searchQuery;
}
Widget build(BuildContext context) {
final filteredTodos = todos.where(_filterTodo).toList();
return Scaffold(
appBar: AppBar(
title: const Text('Todo List'),
),
body: Column(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: TextField(
controller: searchController,
decoration: const InputDecoration(
labelText: 'Search by User ID or Title',
prefixIcon: Icon(Icons.search),
border: OutlineInputBorder(),
),
),
),
Expanded(
child: ListView.builder(
key: ValueKey(searchQuery), // Add this line
itemCount: filteredTodos.length,
itemBuilder: (context, index) {
final todo = filteredTodos[index];
return ListTile(
key: ValueKey(todo.id), // Add this line
title: Text(todo.title),
subtitle: Text('User ID: ${todo.userId}'),
trailing: todo.completed
? const Icon(Icons.check_box)
: const Icon(Icons.check_box_outline_blank),
);
},
),
),
],
),
);
}
}