searchable_spinner 1.0.0
searchable_spinner: ^1.0.0 copied to clipboard
A modern, generic, searchable dropdown/spinner for Flutter with local and async search, debounce, pagination, theming, validation, and responsive UI.
example/lib/main.dart
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:searchable_spinner/searchable_spinner.dart';
void main() {
runApp(const ExampleApp());
}
class Customer {
const Customer(this.id, this.name, this.email);
final int id;
final String name;
final String email;
}
class ExampleApp extends StatefulWidget {
const ExampleApp({super.key});
@override
State<ExampleApp> createState() => _ExampleAppState();
}
class _ExampleAppState extends State<ExampleApp> {
final customers = List.generate(
30,
(i) => Customer(i + 1, 'Customer ${i + 1}', 'customer${i + 1}@example.com'),
);
Customer? selectedLocal;
Customer? selectedAsync;
Future<List<Customer>> searchCustomers(String query) async {
await Future<void>.delayed(const Duration(milliseconds: 500));
final q = query.toLowerCase().trim();
if (q.isEmpty) return customers.take(10).toList();
return customers
.where(
(e) =>
e.name.toLowerCase().contains(q) ||
e.email.toLowerCase().contains(q),
)
.take(10)
.toList();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(useMaterial3: true, colorSchemeSeed: Colors.indigo),
home: Scaffold(
appBar: AppBar(title: const Text('Searchable Spinner')),
body: ListView(
padding: const EdgeInsets.all(20),
children: [
SearchableSpinner<Customer>(
items: customers,
value: selectedLocal,
label: 'Local Customer',
hintText: 'Choose customer',
prefixIcon: Icons.person_outline,
itemLabel: (e) => e.name,
itemBuilder: (context, customer, selected) {
return ListTile(
leading: CircleAvatar(
child: Text(customer.name.split(' ').last),
),
title: Text(customer.name),
subtitle: Text(customer.email),
trailing: selected
? const Icon(Icons.check_circle_rounded)
: null,
);
},
onChanged: (value) {
setState(() => selectedLocal = value);
},
validator: (value) =>
value == null ? 'Customer is required' : null,
),
const SizedBox(height: 24),
SearchableSpinner<Customer>.async(
label: 'API Customer',
hintText: 'Search customer',
prefixIcon: Icons.cloud_outlined,
itemLabel: (e) => e.name,
search: searchCustomers,
onChanged: (value) {
setState(() => selectedAsync = value);
},
),
const SizedBox(height: 24),
Text(
'Selected: ${selectedAsync?.name ?? 'None'}',
style: Theme.of(context).textTheme.titleMedium,
),
],
),
),
);
}
}