Membangun Fitur Pencarian di Flutter
Fitur pencarian adalah komponen esensial dalam banyak aplikasi modern, memungkinkan pengguna untuk dengan cepat menemukan informasi yang mereka butuhkan dari kumpulan data yang besar. Implementasi fitur ini di Flutter dapat dilakukan dengan berbagai cara, mulai dari yang sederhana hingga yang kompleks dengan integrasi API dan algoritma pencarian lanjutan. Artikel ini akan memandu Anda melalui proses membangun fitur pencarian dasar di aplikasi Flutter Anda, fokus pada manajemen state dan pembaruan UI.
1. Persiapan Awal Proyek
Pertama, pastikan Anda memiliki proyek Flutter yang berfungsi. Jika belum, Anda bisa membuatnya dengan perintah berikut:
flutter create flutter_search_app
cd flutter_search_app
Kemudian, buka file lib/main.dart. Untuk contoh ini, kita akan mengimplementasikan semua kode dalam file ini.
2. Model Data
Kita memerlukan model data sederhana untuk dijadikan target pencarian. Mari kita definisikan kelas Item dan beberapa data sampel yang akan kita tampilkan dan cari.
class Item {
final int id;
final String name;
Item({required this.id, required this.name});
}
List- allItems = [
Item(id: 1, name: 'Apple'),
Item(id: 2, name: 'Banana'),
Item(id: 3, name: 'Orange'),
Item(id: 4, name: 'Grape'),
Item(id: 5, name: 'Strawberry'),
Item(id: 6, name: 'Pineapple'),
Item(id: 7, name: 'Mango'),
Item(id: 8, name: 'Blueberry'),
Item(id: 9, name: 'Raspberry'),
Item(id: 10, name: 'Watermelon'),
];
3. Struktur UI Dasar
Kita akan menggunakan Scaffold dengan AppBar dan ListView.builder untuk menampilkan hasil. Kunci dari fitur pencarian adalah bagaimana AppBar dapat berubah dari judul biasa menjadi kolom pencarian (TextField) dan bagaimana ListView memperbarui itemnya berdasarkan kueri pencarian.
4. Logika Pencarian dan Manajemen State
Kita akan menggunakan StatefulWidget untuk mengelola status pencarian. State yang perlu kita kelola meliputi:
_isSearching: Boolean untuk menunjukkan apakah mode pencarian aktif._searchQuery: String yang menampung input pencarian pengguna._filteredItems: DaftarItemyang ditampilkan setelah difilter._searchController:TextEditingControlleruntuk mengelola inputTextField.
Fungsi utama adalah _filterItems(String query) yang akan memproses daftar allItems dan mengembalikan daftar item yang namanya cocok dengan kueri pencarian (tanpa memedulikan huruf besar/kecil).
5. Implementasi Kode Lengkap
Berikut adalah implementasi lengkap untuk file lib/main.dart, yang mencakup model data, UI, dan logika pencarian:
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
// Model Data
class Item {
final int id;
final String name;
Item({required this.id, required this.name});
}
// Data Sampel
List- allItems = [
Item(id: 1, name: 'Apple'),
Item(id: 2, name: 'Banana'),
Item(id: 3, name: 'Orange'),
Item(id: 4, name: 'Grape'),
Item(id: 5, name: 'Strawberry'),
Item(id: 6, name: 'Pineapple'),
Item(id: 7, name: 'Mango'),
Item(id: 8, name: 'Blueberry'),
Item(id: 9, name: 'Raspberry'),
Item(id: 10, name: 'Watermelon'),
Item(id: 11, name: 'Lemon'),
Item(id: 12, name: 'Peach'),
Item(id: 13, name: 'Pear'),
Item(id: 14, name: 'Cherry'),
Item(id: 15, name: 'Kiwi'),
];
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Search Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
visualDensity: VisualDensity.adaptivePlatformDensity,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State
createState() => _MyHomePageState();
}
class _MyHomePageState extends State {
bool _isSearching = false;
String _searchQuery = '';
final TextEditingController _searchController = TextEditingController();
List- _filteredItems = [];
@override
void initState() {
super.initState();
_filteredItems = allItems; // Tampilkan semua item di awal
// Tambahkan listener ke controller untuk memfilter setiap kali teks berubah
_searchController.addListener(_onSearchChanged);
}
@override
void dispose() {
_searchController.removeListener(_onSearchChanged);
_searchController.dispose();
super.dispose();
}
// Dipanggil setiap kali teks di TextField berubah
void _onSearchChanged() {
_filterItems(_searchController.text);
}
// Logika untuk memfilter item berdasarkan kueri
void _filterItems(String query) {
setState(() {
_searchQuery = query;
if (query.isEmpty) {
_filteredItems = allItems;
} else {
_filteredItems = allItems
.where((item) =>
item.name.toLowerCase().contains(query.toLowerCase()))
.toList();
}
});
}
// Mengaktifkan mode pencarian
void _startSearch() {
setState(() {
_isSearching = true;
});
}
// Menonaktifkan mode pencarian dan mereset kueri
void _stopSearch() {
setState(() {
_isSearching = false;
_searchController.clear();
_filterItems(''); // Reset filter untuk menampilkan semua item
});
}
// Widget untuk TextField pencarian di AppBar
Widget _buildSearchField() {
return TextField(
controller: _searchController,
autofocus: true, // Fokuskan secara otomatis saat mode pencarian aktif
decoration: const InputDecoration(
hintText: 'Cari...',
border: InputBorder.none,
hintStyle: TextStyle(color: Colors.white70),
),
style: const TextStyle(color: Colors.white, fontSize: 16.0),
// onChanged tidak perlu diatur di sini karena kita sudah menggunakan addListener
);
}
// Aksi untuk AppBar (ikon pencarian atau ikon hapus)
List
_buildAppBarActions() {
if (_isSearching) {
return [
IconButton(
icon: const Icon(Icons.clear),
onPressed: _stopSearch,
),
];
} else {
return [
IconButton(
icon: const Icon(Icons.search),
onPressed: _startSearch,
),
];
}
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: _isSearching ? _buildSearchField() : const Text('Daftar Item'),
actions: _buildAppBarActions(),
),
body: _filteredItems.isEmpty
? const Center(child: Text('Tidak ada item ditemukan.'))
: ListView.builder(
itemCount: _filteredItems.length,
itemBuilder: (context, index) {
return Card(
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
elevation: 2,
child: ListTile(
title: Text(_filteredItems[index].name),
subtitle: Text('ID: ${_filteredItems[index].id}'),
onTap: () {
// Contoh: Menampilkan SnackBar saat item diklik
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Anda memilih ${_filteredItems[index].name}')),
);
},
),
);
},
),
);
}
}
6. Pertimbangan Lanjutan
Implementasi di atas menyediakan fondasi yang kokoh, tetapi ada beberapa area di mana Anda dapat memperluas dan mengoptimalkan fitur pencarian:
- Debouncing Input: Untuk aplikasi dengan data yang sangat besar atau pencarian yang memakan sumber daya (misalnya, memanggil API), Anda mungkin ingin menambahkan debouncing ke input pencarian. Ini mencegah fungsi pencarian dipanggil terlalu sering, menunggu sebentar setelah pengguna berhenti mengetik sebelum memicu pencarian. Paket seperti
rxdartatau implementasi manual denganTimerdapat membantu. - Pencarian Asinkron: Jika data Anda berasal dari API atau database, Anda perlu mengimplementasikan pencarian secara asinkron (misalnya, menggunakan
FutureBuilder,StreamBuilder, atau state management yang lebih canggih seperti Provider/BLoC/Riverpod) untuk menangani status loading, error, dan hasil. - Algoritma Pencarian Lebih Lanjut: Contoh ini menggunakan pencarian berbasis substring sederhana. Untuk pengalaman pengguna yang lebih baik, pertimbangkan algoritma seperti pencarian fuzzy (misalnya, menggunakan paket
fuzzy) yang dapat menemukan kecocokan bahkan jika ada kesalahan ketik kecil. - Performa pada Dataset Besar: Untuk dataset yang sangat besar, memfilter seluruh daftar di memori mungkin tidak efisien. Pertimbangkan untuk memuat data secara paginasi atau mendelegasikan pemfilteran ke backend untuk meningkatkan performa.
- Manajemen State Lanjutan: Untuk aplikasi yang lebih kompleks, mengelola state pencarian dengan
setStatemungkin menjadi rumit. Pola manajemen state seperti Provider, BLoC, atau Riverpod dapat memberikan solusi yang lebih terstruktur dan dapat diskalakan, memisahkan logika bisnis dari UI. - Riwayat Pencarian: Menyimpan riwayat pencarian pengguna dan menampilkannya saat
TextFieldkosong dapat meningkatkan kegunaan.
Kesimpulan
Membangun fitur pencarian di Flutter melibatkan kombinasi UI yang responsif dan logika pemfilteran data yang efisien. Dengan memahami bagaimana mengelola state dan memperbarui UI berdasarkan input pengguna, Anda dapat menciptakan pengalaman pencarian yang kuat dan intuitif untuk aplikasi Anda. Contoh dasar ini memberikan fondasi yang kokoh, yang dapat Anda kembangkan lebih lanjut dengan fitur-fitur dan optimasi yang lebih canggih sesuai kebutuhan proyek Anda.