super_auto_suggestion_box 1.5.0
super_auto_suggestion_box: ^1.5.0 copied to clipboard
Super Auto Suggestion Box is a GeniusLink Flutter package providing a typeahead/combobox with local and remote sources, fuzzy matching, single or multi-select, free-text entry, progressive fallback, i [...]
super_auto_suggestion_box #
super_auto_suggestion_box provides the GeniusLink SuperAutoSuggestionsBox<T>:
a themed typeahead / combobox with local and remote sources, fuzzy matching,
single- and multi-select, free-text entry, progressive remote fallback,
server-side paging, recents, inline create, shadow-hint completion, record
binding, read-only/fixable states, advanced search, validation, and bare
embedding.
Version 1.5.0 keeps raw T values as the public data model, consolidates
suggestion row construction into SuperAutoSuggestionsItem<T>(...), and
finishes the Super prefix migration for public presentation/widget classes.
The validator still receives the selected raw T?, and onSelectionChanged
remains the selection callback for both select and de-select operations.
Every SuperAutoSuggestionsBox<T> requires a
SuperAutoSuggestionsSource<T>. Use SuperAutoSuggestionSources.list<T>(values) for a
local collection. Initial multi-select values belong to
SuperAutoSuggestionsController.initialSelected; query, recents, and
multi-select configuration belong to the widget.
SuperAutoSuggestionsItem<T> suggestionBuilder(
List<T> items,
int index,
T element,
)
SuperAutoSuggestionsItem<T> remains public because builders return it, but
callers no longer wrap every collection item, fetch result, selected item,
recent item, or created item in SuperAutoSuggestionsItem<T>.
Setup #
dependencies:
super_auto_suggestion_box: ^1.5.0
import 'package:super_auto_suggestion_box/super_auto_suggestion_box.dart';
final typography = SuperTextTheme();
MaterialApp(
theme: SuperMaterialThemeData.light(
textTheme: typography,
primaryTextTheme: typography,
),
darkTheme: SuperMaterialThemeData.dark(
textTheme: typography,
primaryTextTheme: typography,
),
localizationsDelegates: const [
// ...
SuperAutoSuggestionsTranslation.delegate,
],
supportedLocales:
SuperAutoSuggestionsTranslation.delegate.supportedLocales,
);
Basic Usage #
final units = ['each', 'box', 'carton'];
SuperAutoSuggestionsItem<String> unitSuggestion(
List<String> items,
int index,
String unit,
) => SuperAutoSuggestionsItem<String>(
value: unit,
titleText: unit,
);
final box = SuperAutoSuggestionsController<String>(
allowFreeText: true,
);
SuperAutoSuggestionsBox<String>(
controller: box,
source: SuperAutoSuggestionSources.list<String>(units),
suggestionBuilder: unitSuggestion,
hintText: 'Type or pick...',
onSelectionChanged: (selected) {
final unit = selected.isEmpty ? null : selected.last;
// unit is the selected raw String?, or null after de-selection.
},
);
You can omit the controller, but the source remains required:
SuperAutoSuggestionsBox<String>(
source: SuperAutoSuggestionSources.list<String>(units),
suggestionBuilder: unitSuggestion,
onSelectionChanged: (selected) {},
);
Rich Rows #
Keep domain data raw and derive row metadata in the builder:
final accounts = ['1010', '1020', '4000'];
SuperAutoSuggestionsItem<String> accountSuggestion(
List<String> items,
int index,
String code,
) => SuperAutoSuggestionsItem<String>(
value: code,
titleText: switch (code) {
'1010' => 'Cash on Hand',
'1020' => 'Bank - Operating',
'4000' => 'Sales Revenue',
_ => code,
},
descriptionText: 'Account $code',
trailingText: code == '1020' ? '285,120.50' : null,
group: code.startsWith('1') ? 'Assets' : 'Income',
iconData: Icons.account_balance_outlined,
keywords: [code],
);
Custom rows receive both the raw item and the built suggestion:
SuperAutoSuggestionsBox<String>(
source: SuperAutoSuggestionSources.list<String>(accounts),
suggestionBuilder: accountSuggestion,
itemBuilder: (context, code, suggestion, highlighted) {
return Text('${suggestion.displayText} ($code)');
},
);
Custom supporting widgets now use the same constructor. titleText remains the
canonical searchable and committed title:
SuperAutoSuggestionsItem<String>(
value: '1020',
titleText: 'Bank - Operating',
description: const Text('1020 - Current Assets'),
trailing: const Chip(label: Text('Active')),
icon: const Icon(Icons.account_balance_outlined),
);
descriptionText, trailingText, and iconData remain available when custom
widgets are not needed. Suggestions can also carry an optional
Stream? enabledSnapshot alongside the immediate enabled boolean.
Suggestion Sources #
All built-in sources accept raw values and source-specific matching or fetch
configuration only. Pass suggestionBuilder to SuperAutoSuggestionsBox; it owns
the conversion to SuperAutoSuggestionsItem<T> for both widget-created and external
controllers.
final staticSource = SuperAutoSuggestionSources.list<String>(accounts);
final fuzzySource = SuperAutoSuggestionSources.fuzzy<String>(accounts);
final asyncSource = SuperAutoSuggestionSources.async<String>(
(query) => api.searchAccounts(query), // Future<List<String>>
initialItems: accounts.take(5).toList(),
);
final hybridSource = SuperAutoSuggestionSources.hybrid<String>(
initialItems: accounts,
fetch: (query) => api.searchAccounts(query), // Future<List<String>>
remoteThreshold: 1,
remoteMinChars: 2,
);
final remoteFallbackSource = SuperAutoSuggestionSources.remoteFallback<String>(
initialItems: accounts,
fetch: (query) => api.searchAccounts(query), // Future<List<String>>
remoteThreshold: 5,
remoteMinChars: 1,
);
final pagedSource = SuperAutoSuggestionSources.paged<String>(
(query, page) async {
final response = await api.searchAccountsPage(query, page);
return SuperSuggestionsPage<String>(
items: response.codes,
hasMore: response.hasMore,
);
},
resolveFrom: accounts,
);
SuperAutoSuggestionSources.strings(values) is still available for the simple
label-equals-value case. The source itself does not take a builder; the widget
owns the suggestionBuilder.
Concrete Source Classes #
The factory methods above return these public implementations:
| Factory | Concrete source |
|---|---|
list / strings / fuzzy |
SuperAutoListSuggestionsSource<T> |
async |
SuperAutoAsyncSuggestionsSource<T> |
hybrid |
SuperAutoHybridSuggestionsSource<T> |
remoteFallback |
SuperAutoRemoteFallbackSuggestionsSource<T> |
paged |
SuperAutoPagedSuggestionsSource<T> |
Prefer SuperAutoSuggestionSources for normal construction. Instantiate a
concrete source directly only when its public source-specific API is needed.
The pre-1.2.0 concrete class names are no longer canonical.
Presentation Widget Names #
The canonical public presentation/widget types now all use the Super prefix:
SuperAutoSuggestionsBoxThemeData, SuperAutoSuggestionsBoxFocusedStyle,
SuperAutoSuggestionsHighlight, and SuperAutoSuggestionsPanel<T>.
Deprecated typedefs preserve the 1.2.x names during migration.
Controller API #
Controller selections, result lists, recents, and callbacks use raw values:
final controller = SuperAutoSuggestionsController<String>(
initialValue: '1020',
initialSelected: const ['1010'],
);
controller.selected; // String?
controller.results; // List<String>
controller.selectedItems; // List<String>
controller.selectedValues; // List<String>, compatibility alias
controller.recents; // List<String>
controller.select('1010');
controller.toggleSelected('4000');
controller.setSelectedItems(['1010', '4000']);
controller.setRecents(['1020']);
controller.selectByValue('4000');
When using an external controller, provide the source and builder on the widget. The controller owns interaction state, not suggestion data or row presentation:
SuperAutoSuggestionsBox<String>(
controller: controller,
source: staticSource,
suggestionBuilder: accountSuggestion,
showRecents: true,
initialRecents: const ['4000'],
onRecentsChanged: (recentCodes) {},
);
After the controller is attached to an SuperAutoSuggestionsBox, UI metadata is
available through the render-facing accessors:
controller.suggestions; // List<SuperAutoSuggestionsItem<String>>
controller.suggestionAt(0); // SuperAutoSuggestionsItem<String>
controller.highlightedSuggestion; // SuperAutoSuggestionsItem<String>?
controller.selectedSuggestion; // SuperAutoSuggestionsItem<String>?
Selection Callback #
onSelectionChanged fires after every selection mutation. Single-select emits
[item] on selection and [] on de-selection; multi-select emits the complete
selected list.
SuperAutoSuggestionsBox<String>(
source: SuperAutoSuggestionSources.list<String>(accounts),
suggestionBuilder: accountSuggestion,
multiSelect: true,
onSelectionChanged: (codes) {},
);
Inline create returns a raw value:
SuperAutoSuggestionsBox<String>(
source: SuperAutoSuggestionSources.list<String>(vendors),
suggestionBuilder: vendorSuggestion,
onCreate: (query) async {
final vendor = await api.createVendor(query);
return vendor.id; // raw String
},
onSelectionChanged: (vendorIds) {},
);
ERP Input And Validation #
SuperAutoSuggestionsBox<T> participates in an enclosing Form through
FormField<T>. Its validator receives the selected raw T?, not the query
text. Keep a controller when form submission needs to read the selected value.
When autovalidateMode is omitted, the box inherits the nearest
Form.autovalidateMode before falling back to AutovalidateMode.disabled.
final documentController = SuperAutoSuggestionsController<String>();
Form(
key: formKey,
autovalidateMode: AutovalidateMode.onUserInteraction,
child: SuperAutoSuggestionsBox<String>(
controller: documentController,
source: SuperAutoSuggestionSources.list<String>(documentReferences),
suggestionBuilder: documentSuggestion,
decoration: const InputDecoration(
labelText: 'Document Reference',
helperText: 'Pick a document reference',
),
keyboardType: TextInputType.text,
inputFormatters: [
FilteringTextInputFormatter.allow(RegExp(r'[A-Za-z0-9-]')),
LengthLimitingTextInputFormatter(16),
],
textDirection: TextDirection.ltr,
textInputAction: TextInputAction.done,
showShadowHint: true,
completeShadowHintOnTab: true,
required: true,
validator: (value) {
if (value == null) return null; // `required` handles the empty selection.
return documentReferences.contains(value)
? null
: 'Pick a document from the list';
},
onSelectionChanged: (selected) {},
),
);
if (formKey.currentState!.validate()) {
final savedDocumentReference = documentController.selected;
}
Validation errors surface through the suffix error badge tooltip, matching the
GeniusLink form-field convention. For direct form-field access, controller
formFieldKey is now GlobalKey<FormFieldState<T>>?.
Use validationPosition to choose where validation appears:
ValidationPosition.suffixIcon shows the error badge in the field suffix,
ValidationPosition.underBox shows error text under the box, and
ValidationPosition.labelTrailing shows the error badge at the end of the
label row. When omitted, the box uses SuperFormField.validationPosition; when
that is also null, mobile defaults to under-box text and larger screens default
to label-trailing badges.
Use helpIcon to add a custom help affordance at the end of the label row:
SuperAutoSuggestionsBox<String>(
source: SuperAutoSuggestionSources.list<String>(accounts),
suggestionBuilder: accountSuggestion,
decoration: const InputDecoration(labelText: 'Posting account'),
helpIcon: const Tooltip(
message: 'Used by journal posting and reports.',
child: Icon(Icons.help_outline_rounded, size: 18),
),
);
For keyboard traversal, a single-select field with
textInputAction: TextInputAction.next moves focus to the next focusable field
immediately after an item is selected. Multi-select fields keep focus in the
current suggestions field.
States And Embedding #
disabled: dims and blocks interaction.readOnly: blocks interaction but keeps full contrast for posted/review states.allowFixed: shows a lock/unlock action backed bycontroller.isFixed.advancedSearch: opens a larger search surface withCtrl/Cmd+F.bare: removes outer chrome for table cells and compact host surfaces.restoreOnBlur: restores the last committed raw value when the user leaves without picking.
Migration #
See migration_1.1.0_to_1.2.0.md for the generic
validator, FormField<T> integration, removed callbacks, source-name
migrations, localization, TextInputAction.next, and
onSelectionChanged behavior. For earlier migrations, see
migration_1.0.0_to_1.1.0.md and
migration_0.14.0_to_1.0.0.md.
Localization #
The package ships English and Arabic translations using flutter_localizations,
intl, and generated intl_utils delegates. Register the package helpers on
your app:
MaterialApp(
localizationsDelegates: const [
// ...
SuperAutoSuggestionsTranslation.delegate,
],
supportedLocales:
SuperAutoSuggestionsTranslation.delegate.supportedLocales,
)
Built-in package strings such as the required-field message, loading/search
states, Recent group label, inline-create text, fixed/unfixed tooltips, and
Advanced Search chrome follow the active locale. Explicit custom strings passed
to the widget continue to take precedence. Registration is optional: when no
SuperAutoSuggestionsTranslation is available in the widget tree, package
widgets fall back to the built-in English localization.