auto_suggest_box 0.1.2 copy "auto_suggest_box: ^0.1.2" to clipboard
auto_suggest_box: ^0.1.2 copied to clipboard

A highly customizable, performance-optimized auto-suggest/autocomplete widget for Flutter with Fluent UI design. Features debounced search, LRU caching, keyboard navigation, form validation, advanced [...]

Fluent Auto Suggest Box #

pub package License: MIT Flutter

A highly customizable, performance-optimized auto-suggest/autocomplete widget for Flutter with Fluent UI design. Features include debounced search, LRU caching, keyboard navigation, form validation, advanced search dialog, and BLoC/Cubit state management (similar to smart_pagination).

Features #

  • Debounced Search - Configurable delay to reduce API calls
  • LRU Caching - Intelligent caching with TTL (Time To Live) expiration
  • Keyboard Navigation - Full support for Arrow keys, Tab, Escape, and Enter
  • Form Validation - Built-in validator support with AutovalidateMode
  • Server-Side Search - Async search with onNoResultsFound callback
  • Loading & Error States - Customizable loading and error handling
  • Custom Builders - Full control over item rendering
  • Accessibility - Semantic labels and screen reader support
  • Recent Searches - Track and display search history
  • Advanced Search Dialog - Full-featured search with filters, pagination, and multiple view modes
  • BLoC/Cubit Support - State management similar to smart_pagination
  • Performance Optimized - Reduced rebuilds, efficient memory usage

Installation #

Add this to your pubspec.yaml:

dependencies:
  auto_suggest_box: ^0.1.2

Then run:

flutter pub get

Quick Start #

Basic Usage #

import 'package:auto_suggest_box/auto_suggest_box.dart';

FluentAutoSuggestBox<String>(
  items: [
    FluentAutoSuggestBoxItem(value: '1', label: 'Apple'),
    FluentAutoSuggestBoxItem(value: '2', label: 'Banana'),
    FluentAutoSuggestBoxItem(value: '3', label: 'Cherry'),
  ],
  onSelected: (item) {
    print('Selected: ${item?.label}');
  },
)
FluentAutoSuggestBox<Product>(
  items: localProducts,
  enableCache: true,
  cacheMaxSize: 100,
  cacheDuration: Duration(minutes: 30),
  debounceDelay: Duration(milliseconds: 300),
  onNoResultsFound: (query) async {
    // Fetch from server
    final response = await api.searchProducts(query);
    return response.map((p) => FluentAutoSuggestBoxItem(
      value: p,
      label: p.name,
      subtitle: Text(p.description),
    )).toList();
  },
  onSelected: (item) {
    if (item != null) {
      navigateToProduct(item.value);
    }
  },
)

Form Validation #

FluentAutoSuggestBox<String>.form(
  items: countries,
  validator: (value) {
    if (value == null || value.isEmpty) {
      return 'Please select a country';
    }
    return null;
  },
  autovalidateMode: AutovalidateMode.onUserInteraction,
  onSelected: (item) => setState(() => selectedCountry = item?.value),
)

Cubit/BLoC State Management #

This package provides FluentAutoSuggestBoxCubit to manage the state of FluentAutoSuggestBox widget.

Creating a Cubit #

final cubit = FluentAutoSuggestBoxCubit<Product>();

Managing Items #

// Set items
cubit.setItems([
  FluentAutoSuggestBoxItem(value: product1, label: 'iPhone'),
  FluentAutoSuggestBoxItem(value: product2, label: 'Samsung'),
  FluentAutoSuggestBoxItem(value: product3, label: 'Pixel'),
]);

// Add item
cubit.addItem(FluentAutoSuggestBoxItem(value: product4, label: 'OnePlus'));

// Remove item
cubit.removeItem(item);
cubit.removeItemByValue(product1);

// Clear all items
cubit.clearItems();

Selection #

// Select an item
cubit.selectItem(item);
cubit.selectByValue(product1);
cubit.selectByIndex(0);

// Clear selection
cubit.clearSelection();

// Get selected item
final selected = cubit.state.selectedItem;

Loading State #

// Show loading
cubit.setLoading(true);

// Load from server
final products = await api.getProducts();
cubit.setItems(products.map((p) =>
  FluentAutoSuggestBoxItem(value: p, label: p.name)
).toList());

// Hide loading
cubit.setLoading(false);

Error Handling #

try {
  cubit.setLoading(true);
  final products = await api.getProducts();
  cubit.setItems(products);
} catch (e) {
  cubit.setError(e);
} finally {
  cubit.setLoading(false);
}

Using with FluentAutoSuggestBox #

BlocBuilder<FluentAutoSuggestBoxCubit<Product>, FluentAutoSuggestBoxState<Product>>(
  bloc: cubit,
  builder: (context, state) {
    return FluentAutoSuggestBox<Product>(
      items: state.items,
      enabled: state.isEnabled && !state.isLoading,
      onSelected: (item) {
        if (item != null) {
          cubit.selectItem(item);
        }
      },
      loadingBuilder: state.isLoading
        ? (context) => const ProgressRing()
        : null,
    );
  },
)

State Properties #

final state = cubit.state;

state.items           // List of items
state.selectedItem    // Currently selected item
state.text            // Current text in input
state.isLoading       // Loading state
state.error           // Error object (if any)
state.hasError        // Has error
state.hasSelection    // Has selected item
state.isEmpty         // Items list is empty
state.itemCount       // Number of items
state.isEnabled       // Is widget enabled
state.isReadOnly      // Is widget read-only

Available Methods #

Method Description
setItems(items) Set all items
addItem(item) Add single item
addItems(items) Add multiple items
removeItem(item) Remove item
removeItemByValue(value) Remove by value
clearItems() Clear all items
selectItem(item) Select item
selectByValue(value) Select by value
selectByIndex(index) Select by index
clearSelection() Clear selection
setText(text) Set input text
clearText() Clear input text
setLoading(bool) Set loading state
setError(error) Set error
clearError() Clear error
setEnabled(bool) Enable/disable
setReadOnly(bool) Set read-only
reset() Reset to initial state
clear() Clear selection, text, error
search(query) Search in local items

Using BlocBuilder Directly #

BlocBuilder<AutoSuggestCubit<Product>, AutoSuggestState<Product>>(
  bloc: productsCubit,
  builder: (context, state) {
    return switch (state) {
      AutoSuggestInitial() => Text('Start typing to search...'),
      AutoSuggestLoading(:final query) => CircularProgressIndicator(),
      AutoSuggestLoaded(:final items) => ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) => Text(items[index].name),
      ),
      AutoSuggestEmpty(:final query) => Text('No results for "$query"'),
      AutoSuggestError(:final error) => Text('Error: $error'),
    };
  },
)

AutoSuggestBlocBuilder (Convenience Widget) #

AutoSuggestBlocBuilder<Product>(
  cubit: productsCubit,
  onInitial: (context) => Text('Ready to search'),
  onLoading: (context, query, previousItems) => ProgressRing(),
  onLoaded: (context, items, query) => ProductList(items: items),
  onEmpty: (context, query) => Text('No products found'),
  onError: (context, error, query, previousItems) => ErrorWidget(error),
)

State Classes #

State Properties Description
AutoSuggestInitial - Initial state before any search
AutoSuggestLoading query, previousItems, isLoadingMore Loading state
AutoSuggestLoaded items, query, fetchedAt, dataExpiredAt Success with data
AutoSuggestEmpty query, searchedAt No results found
AutoSuggestError error, query, previousItems Error state

Data Expiration (like smart_pagination) #

// Check if data is expired
if (cubit.isDataExpired) {
  await cubit.refresh();
}

// Or use automatic check
await cubit.checkAndRefreshIfExpired();

// Access data age
final age = cubit.dataAge;  // Duration since last fetch

Statistics #

final stats = cubit.stats;
print('Cache hits: ${stats['cacheHits']}');
print('Cache misses: ${stats['cacheMisses']}');
print('Hit rate: ${(stats['cacheHitRate'] * 100).toStringAsFixed(1)}%');

API Reference #

FluentAutoSuggestBox #

The main widget for creating auto-suggest input fields.

Parameter Type Default Description
items List<FluentAutoSuggestBoxItem<T>> required List of suggestion items
controller TextEditingController? null Text controller for the input field
autoSuggestController AutoSuggestController<T>? null Controller for managing state
onChanged OnTextChanged<T>? null Callback when text changes
onSelected ValueChanged<FluentAutoSuggestBoxItem<T>?>? null Callback when item is selected
itemBuilder ItemBuilder<T>? null Custom widget builder for items
noResultsFoundBuilder WidgetBuilder? null Widget shown when no results
loadingBuilder WidgetBuilder? null Widget shown while loading
sorter ItemSorter<T>? defaultItemSorter Custom sorting function
enableCache bool true Enable result caching
cacheMaxSize int 100 Maximum cache entries
cacheDuration Duration 30 minutes Cache TTL
debounceDelay Duration 300ms Debounce delay for search
minSearchLength int 2 Minimum characters to trigger search
maxPopupHeight double 380.0 Maximum height of suggestion popup
direction AutoSuggestBoxDirection below Popup direction (above/below)
validator FormFieldValidator<String>? null Form validation function
autovalidateMode AutovalidateMode disabled Validation mode

FluentAutoSuggestBoxItem #

Represents a single suggestion item.

FluentAutoSuggestBoxItem<T>({
  required T value,        // The actual data value
  required String label,   // Display text
  Widget? child,           // Custom widget (overrides label)
  Widget? subtitle,        // Secondary text/widget
  bool enabled = true,     // Whether item is selectable
  String? semanticLabel,   // Accessibility label
})

AutoSuggestController #

Controller for managing search state and metrics.

final controller = AutoSuggestController<String>(
  debounceDelay: Duration(milliseconds: 300),
  minSearchLength: 2,
  maxRecentSearches: 10,
  enableRecentSearches: true,
);

// Access state
print(controller.searchQuery);
print(controller.isLoading);
print(controller.recentSearches);

// Get statistics
final stats = controller.getStats();
print('Success rate: ${stats['successRate']}');

// Clean up
controller.dispose();

SearchResultsCache #

LRU cache with automatic expiration.

final cache = SearchResultsCache<Product>(
  maxSize: 100,
  maxAge: Duration(minutes: 30),
  enablePrefixMatching: true,
);

// Cache operations
cache.set('query', results);
final cached = cache.get('query');
cache.clear();

// Get statistics
final stats = cache.getStats();
print('Hit rate: ${(stats.hitRate * 100).toStringAsFixed(1)}%');

Advanced Search Dialog #

For complex search requirements, use the AdvancedSearchDialog:

// Single selection
final result = await AdvancedSearchDialog.show<Product>(
  context: context,
  items: products,
  onSearch: (query, filters) async {
    return await api.search(query, filters: filters);
  },
  config: AdvancedSearchConfig(
    title: 'Find Product',
    searchHint: 'Search by name or SKU...',
    showFilters: true,
    showStats: true,
    viewMode: AdvancedSearchViewMode.grid,
    keyboardShortcut: SingleActivator(LogicalKeyboardKey.f3),
  ),
);

// Multi-selection
final results = await AdvancedSearchDialog.showMultiSelect<Product>(
  context: context,
  items: products,
  onSearch: (query, filters) async => await api.search(query),
  maxSelections: 5,
);

View Modes #

Mode Description
list Traditional list view with details
grid Card-based grid layout
compact Condensed single-line items

Configuration Options #

AdvancedSearchConfig(
  title: 'Search',                    // Dialog title
  searchHint: 'Type to search...',    // Placeholder text
  keyboardShortcut: SingleActivator(LogicalKeyboardKey.f3),
  showFilters: true,                  // Show filter panel
  showStats: true,                    // Show result statistics
  viewMode: AdvancedSearchViewMode.list,
  enableViewModeSwitch: true,         // Allow mode switching
  resultsPerPage: 20,                 // Pagination size
  enablePagination: false,            // Enable pagination
  enableAnimations: true,             // Enable animations
)

Custom Builders #

Custom Item Builder #

FluentAutoSuggestBox<Product>(
  items: products,
  itemBuilder: (context, item) {
    return ListTile(
      leading: Image.network(item.value.imageUrl),
      title: Text(item.label),
      subtitle: Text('\$${item.value.price}'),
      trailing: Icon(Icons.chevron_right),
    );
  },
)

Custom Sorter #

FluentAutoSuggestBox<Product>(
  items: products,
  sorter: (query, items) {
    return items.where((item) {
      // Custom matching logic
      return item.label.toLowerCase().contains(query.toLowerCase()) ||
             item.value.sku.contains(query);
    }).toSet();
  },
)

Custom Loading Builder #

FluentAutoSuggestBox<String>(
  items: items,
  loadingBuilder: (context) {
    return Padding(
      padding: EdgeInsets.all(16),
      child: Row(
        children: [
          CircularProgressIndicator(),
          SizedBox(width: 16),
          Text('Searching...'),
        ],
      ),
    );
  },
)

Keyboard Navigation #

Key Action
Arrow Down Select next item
Arrow Up Select previous item
Enter Confirm selection
Tab Move to next field
Shift+Tab Move to previous field
Escape Close suggestions
F3 Open advanced search (if enabled)

Performance Tips #

  1. Enable Caching - Set enableCache: true for repeated searches
  2. Adjust Debounce - Increase debounceDelay for slow APIs
  3. Set Min Length - Use minSearchLength to prevent unnecessary searches
  4. Use Prefix Matching - Cache uses prefix matching by default
  5. Limit Results - Return limited results from onNoResultsFound
FluentAutoSuggestBox<String>(
  items: items,
  enableCache: true,
  cacheMaxSize: 50,
  cacheDuration: Duration(minutes: 15),
  debounceDelay: Duration(milliseconds: 500),
  minSearchLength: 3,
)

Theming #

Custom Theme #

AdvancedSearchConfig(
  theme: AdvancedSearchTheme(
    primaryColor: Colors.blue,
    backgroundColor: Colors.white,
    cardColor: Colors.grey[50],
    selectedItemColor: Colors.blue.withOpacity(0.1),
    borderRadius: BorderRadius.circular(12),
    spacing: 16.0,
    elevation: 8.0,
  ),
)

Custom Icons #

AdvancedSearchConfig(
  icons: AdvancedSearchIcons(
    search: FluentIcons.search,
    clear: FluentIcons.clear,
    filter: FluentIcons.filter,
    viewList: FluentIcons.view_list,
    viewGrid: FluentIcons.grid_view_medium,
  ),
)

Example App #

See the /example folder for a complete sample application demonstrating:

  • Basic autocomplete
  • Server-side search with caching
  • Form validation
  • Custom builders
  • Advanced search dialog
  • Multiple view modes
  • Keyboard shortcuts

Run the example:

cd example
flutter run

Requirements #

  • Flutter >= 1.17.0
  • Dart SDK >= 3.10.3

Dependencies #

Roadmap #

  • ✅ Cubit/BLoC state management integration (similar to smart_pagination)
  • ❌ RTL language support improvements
  • ❌ More animation options
  • ❌ Voice search support
  • ❌ Grouped suggestions
  • ❌ Inline suggestions (ghost text)
  • ❌ Pagination support for large datasets

Contributing #

Contributions are welcome! Please feel free to submit a Pull Request.

License #

This project is licensed under the MIT License - see the LICENSE file for details.

Support #

If you find this package helpful, please give it a star on GitHub!

1
likes
0
points
239
downloads

Publisher

unverified uploader

Weekly Downloads

A highly customizable, performance-optimized auto-suggest/autocomplete widget for Flutter with Fluent UI design. Features debounced search, LRU caching, keyboard navigation, form validation, advanced search dialog, and BLoC/Cubit state management.

Repository (GitHub)
View/report issues

Topics

#autocomplete #autosuggest #search #fluent-ui #bloc

License

unknown (license)

Dependencies

equatable, fluent_ui, flutter, flutter_bloc, gap

More

Packages that depend on auto_suggest_box