I am working on my first full scale mobile application and I am using Flutter and supabase. I am trying to create a search function on search page from which a user can search for a location and it has a List view with autocomplete suggestions that appears as the user types in the search field. I am using flutter and supabase, both of which I would say I am still at novice level.
Below is my search_sale_tab.dart which is where the search field is:
import 'package:flutter/material.dart';
import 'package:property_bazaar/views/search/search_sale_results_page.dart';
class SearchSalePage extends StatefulWidget {
const SearchSalePage({Key? key}) : super(key: key);
@override
State<SearchSalePage> createState() => _SearchSalePageState();
}
class _SearchSalePageState extends State<SearchSalePage> {
final key = GlobalKey<ScaffoldState>();
final TextEditingController _searchQuery = TextEditingController();
List<String> _list = [];
List<String> _resultForSale = [];
bool isSearching = false;
String _searchText = "";
_SearchSalePageState() {
_searchQuery.addListener(() {
if (_searchQuery.text.isEmpty) {
setState(() {
isSearching = false;
_searchText = "";
_resultForSale = [];
});
} else {
setState(() {
isSearching = true;
_searchText = _searchQuery.text;
_resultForSale = _buildSearchSalePage(_searchText);
});
}
});
}
@override
void initState() {
super.initState();
createSearchResultList();
}
void createSearchResultList() {
_list = [
'Nkana East, Kitwe',
'Riverside, Kitwe',
'Parklands, Kitwe',
'Nkana West, Kitwe',
'Chimwemwe, Kitwe',
'Kwacha East, Kitwe',
'Chelstone, Lusaka',
'Rhodespark, Lusaka',
'Leopards Hill, Lusaka',
'Ibex Hill, Lusaka',
'New Kasama, Lusaka',
'Kansenshi, Ndola',
];
}
List<String> _buildSearchSalePage(String query) {
if (query.isEmpty) {
return [];
} else {
List<String> searchResults = [];
for (String item in _list) {
if (item.toLowerCase().contains(query.toLowerCase())) {
searchResults.add(item);
}
}
return searchResults;
}
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(top: 25.0),
child: Scaffold(
key: key,
body: Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 10),
child: TextFormField(
controller: _searchQuery,
style: const TextStyle(
fontSize: 15,
color: Colors.grey,
fontWeight: FontWeight.normal,
),
decoration: InputDecoration(
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(15),
),
hintText: "Search locations",
hintStyle: const TextStyle(
fontSize: 15,
color: Colors.grey,
fontWeight: FontWeight.normal,
),
),
),
),
Expanded(
child: displaySearchResults(),
),
],
),
),
);
}
Widget displaySearchResults() {
if (isSearching) {
return searchSalePage();
} else {
return Center(
child: _searchQuery.text.isEmpty
? const Text("No results for ''", style: TextStyle(color: Colors.grey))
: Text("No results for '$_searchText'", style: TextStyle(color: Colors.grey)),
);
}
}
ListView searchSalePage() {
return ListView.builder(
itemCount: _resultForSale.length,
itemBuilder: (context, int index) {
return Container(
decoration: BoxDecoration(
color: Colors.grey[100],
border: const Border(
bottom: BorderSide(
color: Colors.grey,
width: 0.5,
),
),
),
child: ListTile(
onTap: () {
// Navigate to search results page and pass the value
Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => SaleSearchResultScreen(result: _resultForSale[index]),
),
);
},
title: Text(
_resultForSale.elementAt(index),
style: const TextStyle(fontSize: 18.0),
),
),
);
},
);
}
void _handleSearchStart() {
setState(() {
isSearching = true;
});
}
}
and this is the search_sale_results.dart file responsible for the page that produces the results of the listings that would be returned from the supabase backend after being queried against the result value passed on from the search_sale_tab.dart:
// ignore_for_file: unnecessary_cast, unnecessary_null_comparison, non_constant_identifier_names
import 'package:flutter/material.dart';
import 'package:property_bazaar/components/sale_listing_card.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:property_bazaar/views/listing_details/sale_listing_details_page.dart';
class SaleSearchResultScreen extends StatelessWidget {
final String result;
const SaleSearchResultScreen({Key? key, required this.result}) : super(key: key);
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(result),
),
body: FutureBuilder<List<dynamic>>(
future: _fetchData(result),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
if (snapshot.hasError) {
return Center(child: Text('Error: ${snapshot.error}'));
}
final propertyForSale = snapshot.data ?? [];
return ListView.builder(
itemCount: propertyForSale.length,
scrollDirection: Axis.vertical,
itemBuilder: (context, index) {
final item = propertyForSale[index];
return InkWell(
onTap: () =>
Navigator.push(context,
MaterialPageRoute(builder: (context)
=> SaleListingDetailsPage(itemDetail: propertyForSale[index])
),
),
child: SaleListingCard(
city_town: item['city_town'],
suburb: item['suburb'],
type: item['type'],
price: item['price'],
beds: item['beds'],
baths: item['baths'],
sqft: item['sqft'],
parking: item['parking'],
yearbuilt: item['yearbuilt'],
presented_by: item['presented_by'],
email: item['email'],
phone: item['phone'],
whatsapp: item['whatsapp'],
description: item['description'],
image: item['image'],
air_condition: item['air_conditioning'],
outdoor: item['outdoor'],
num_of_rooms: item['num_of_rooms'],
types_of_rooms: item['types_of_rooms'],
fireplace: item['fireplace'],
yard_fencing: item['yard_fencing'],
garden: item['garden'],
address: item['address'])
);
}
);
}
),
);
}
Future<List<dynamic>> _fetchData(String searchTerm) async {
final response = await Supabase.instance.client
.from('property_for_sale')
.select()
.ilike('search_text', '%$searchTerm%');
if (response == null) {
throw Exception('Failed to load data');
}
return response as List<dynamic>;
}
}
I’ve been trying to figure out where the problem could be but i can’t hack it. Any help would be appreciated.
Chanda Musonda is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.