Skip to main content

paginated_typeahead 1.1.0 copy "paginated_typeahead: ^1.1.0" to clipboard
paginated_typeahead: ^1.1.0 copied to clipboard

A customizable typeahead (autocomplete) text field for Flutter with built-in pagination, infinite scrolling, and debouncing.

Paginated TypeAhead #

A TypeAhead (autocomplete) widget for Flutter with built-in infinite scroll pagination, debouncing, and modern declarative overlay management.

Pub Version License: MIT

[Paginated TypeAhead Demo]

Features #

  • Infinite Scroll Pagination: Automatically loads more suggestions as the user scrolls to the bottom of the dropdown.
  • Modern Dart 3 Records: Clean (List<T>? items, bool hasMore) response contract for pagination callbacks.
  • Declarative Overlay Architecture: Powered by Flutter's OverlayPortal for smooth rendering without manual overlay lifecycle issues.
  • Deep Customization:
    • Custom search text field builder (builder)
    • Item builder (itemBuilder) & item separators (separatorBuilder)
    • Initial loading (loadingBuilder) & load-more progress (loadMoreLoadingBuilder)
    • Empty results (emptyBuilder)
    • Error state (errorBuilder) & load-more retry (loadMoreErrorBuilder)
  • Smart Auto-Flipping: Automatically switches dropdown direction (down / up) when space below is constrained or keyboard appears.
  • Debounce & Threshold: Configurable debounce duration and minimum characters threshold.
  • Programmatic Controller: Open, close, toggle, or select items programmatically via SuggestionsController.
  • Form Support: Seamlessly integrates with Flutter forms (validator, autovalidateMode, inputFormatters).

Getting Started #

Add paginated_typeahead to your pubspec.yaml:

dependencies:
  paginated_typeahead: ^1.1.0

Or install it via terminal:

flutter pub add paginated_typeahead

Import the package:

import 'package:paginated_typeahead/paginated_typeahead.dart';

Usage #

Basic Example #

PaginatedTypeAhead<User>(
  initialPage: 1,
  suggestionsCallback: (query, page) async {
    final response = await apiService.getUsers(query: query, page: page);
    return (response.users, response.hasMorePages);
  },
  itemBuilder: (user) => ListTile(
    title: Text(user.name),
    subtitle: Text(user.email),
  ),
  onSelected: (user) {
    print('Selected user: ${user.name}');
  },
)

Custom Text Field Builder #

Use the builder parameter to supply your own custom TextFormField, input decoration, or custom styling:

PaginatedTypeAhead<User>(
  initialPage: 1,
  builder: (context, controller, focusNode) {
    return TextFormField(
      controller: controller,
      focusNode: focusNode,
      decoration: InputDecoration(
        hintText: 'Search users...',
        prefixIcon: const Icon(Icons.search),
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(12),
        ),
      ),
    );
  },
  suggestionsCallback: (query, page) async {
    final response = await apiService.getUsers(query: query, page: page);
    return (response.users, response.hasMorePages);
  },
  itemBuilder: (user) => ListTile(
    title: Text(user.name),
  ),
  onSelected: (user) => print(user.name),
)

Note: Both controller and focusNode provided by the builder must be passed to your text field for overlay positioning and typing events to work properly.


Customizing Status & Pagination Builders #

You can customize each state of the search and pagination lifecycle:

PaginatedTypeAhead<Repository>(
  initialPage: 1,
  suggestionsCallback: (query, page) => repository.search(query, page),
  itemBuilder: (repo) => ListTile(title: Text(repo.name)),
  onSelected: (repo) => selectRepo(repo),
  // Separator between items
  separatorBuilder: (context, index) => const Divider(height: 1),
  // Initial loading indicator
  loadingBuilder: (context) => const Padding(
    padding: EdgeInsets.all(16),
    child: Center(child: CircularProgressIndicator()),
  ),
  // Indicator at the bottom while loading the next page
  loadMoreLoadingBuilder: (context) => const Padding(
    padding: EdgeInsets.all(12),
    child: Center(
      child: SizedBox(
        width: 20,
        height: 20,
        child: CircularProgressIndicator(strokeWidth: 2),
      ),
    ),
  ),
  // Widget displayed when no items match the query
  emptyBuilder: (context) => const Padding(
    padding: EdgeInsets.all(16),
    child: Center(child: Text('No repositories found')),
  ),
  // Widget displayed when initial fetch fails
  errorBuilder: (context, error) => Padding(
    padding: const EdgeInsets.all(16),
    child: Center(
      child: Text(
        'Failed to load: $error',
        style: TextStyle(color: Theme.of(context).colorScheme.error),
      ),
    ),
  ),
  // Retry widget displayed when loading more fails
  loadMoreErrorBuilder: (context, retry) => Padding(
    padding: const EdgeInsets.all(12),
    child: Center(
      child: TextButton.icon(
        onPressed: retry,
        icon: const Icon(Icons.refresh, size: 18),
        label: const Text('Retry loading more'),
      ),
    ),
  ),
)

Programmatic Control via SuggestionsController #

Use SuggestionsController to programmatically open, close, toggle, or select items:

final suggestionsController = SuggestionsController<User>();

// Open dropdown
suggestionsController.open();

// Close dropdown
suggestionsController.close();

// Toggle dropdown visibility
suggestionsController.toggle();

// Select an item programmatically
suggestionsController.select(user);

// Check if open
final isOpen = suggestionsController.isOpen;

// Pass to the widget:
PaginatedTypeAhead<User>(
  suggestionsController: suggestionsController,
  initialPage: 1,
  suggestionsCallback: (query, page) => fetchUsers(query, page),
  itemBuilder: (user) => ListTile(title: Text(user.name)),
  onSelected: (user) => handleSelect(user),
)

API Reference #

PaginatedTypeAhead<T> Properties #

Property Type Description Default
initialPage int The starting page number passed to suggestionsCallback. Required
suggestionsCallback PaginatedSuggestionsCallback<T> Fetches suggestions for (query, page). Returns (List<T>? items, bool hasMore). Required
itemBuilder SuggestionsItemBuilder<T> Builds the widget for each suggestion item. Required
onSelected SuggestionSelectionCallback<T>? Called when a suggestion item is selected. null
builder SuggestionsFieldBuilder? Custom builder for the search text field (context, controller, focusNode). null
decorationBuilder SuggestionsDecorationBuilder? Custom builder for the dropdown box decoration (context, child). Default Material card
separatorBuilder IndexedWidgetBuilder? Builds a separator between suggestion items. null
loadingBuilder WidgetBuilder? Custom builder shown while initial suggestions are loading. Built-in spinner
loadMoreLoadingBuilder WidgetBuilder? Custom builder shown at bottom while loading the next page. Built-in spinner
emptyBuilder WidgetBuilder? Custom builder shown when no items match. Built-in message
errorBuilder SuggestionsErrorBuilder? Custom builder shown when initial fetch fails (context, error). Built-in message
loadMoreErrorBuilder LoadMoreErrorBuilder? Custom builder shown when next page fetch fails (context, retry). Built-in retry button
controller TextEditingController? Controller for the search field text. Auto-managed
focusNode FocusNode? Focus node for the search field. Auto-managed
suggestionsController SuggestionsController<T>? Controller to programmatically control the dropdown. null
hintText String Placeholder text for default search field. 'Search...'
debounceDuration Duration Delay to wait after user stops typing before triggering search. 300ms
minCharsForSuggestions int Minimum characters required to trigger suggestions. 0
direction VerticalDirection Preferred vertical direction (down or up). VerticalDirection.down
autoFlipDirection bool Whether to automatically flip direction when space is constrained. true
autoFlipMinHeight double Minimum vertical height below field before flipping upwards. 64.0
hideOnUnfocus bool Whether dropdown closes when the text field loses focus. true
hideOnSelect bool Whether dropdown closes when an item is selected. true
clearOnSelect bool Whether to clear the search text field when an item is selected. false
constrainWidth bool Whether the dropdown width matches the text field width. true
dropdownConstraints BoxConstraints Additional size constraints for the suggestions dropdown overlay. BoxConstraints()
offset Offset? Offset applied to dropdown overlay position. null
inputDecoration InputDecoration? Decoration for the default text field. Styled default
validator FormFieldValidator<String>? Form validation logic. null
autovalidateMode AutovalidateMode? Auto-validation mode for form validation. null
keyboardType TextInputType? Keyboard type for the text field. null
inputFormatters List<TextInputFormatter>? Formatters applied to the text field input. null

Example Application #

Check out the example directory for a complete demo application featuring:

  • Live GitHub repository search with Dio and pagination
  • Custom search field styling
  • Interactive settings to toggle debounce, direction, dividers, constrainWidth, darkMode, and more.

License #

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

3
likes
0
points
446
downloads

Publisher

unverified uploader

Weekly Downloads

A customizable typeahead (autocomplete) text field for Flutter with built-in pagination, infinite scrolling, and debouncing.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flutter

More

Packages that depend on paginated_typeahead