import 'dart:async'; import 'package:flutter/material.dart'; import 'package:sendtrain/services/functional/debouncer.dart'; import 'package:sendtrain/widgets/generic/elements/form_text_input.dart'; class Suggestion { T content; Suggestion(this.content); Widget resultWidget() { return ListTile( title: Text('test'), ); } } // controller: manages the selected content // service: manages the requests for the specific data to search against // title: the title of the text input // callback: the fuction called when a selection is made class FormSearchInput extends StatefulWidget { const FormSearchInput( {super.key, required this.controller, required this.service, required this.resultHandler, this.title}); final String? title; final TextEditingController controller; final dynamic service; final Function resultHandler; @override State createState() => _FormSearchInputState(); } class _FormSearchInputState extends State { String? _currentQuery; late final service = widget.service; late final resultHandler = widget.resultHandler; // The most recent suggestions received from the API. late Iterable _lastOptions = []; late final Debouncer debouncer; // @override // initState() { // service = widget.service; // } // Calls the "remote" API to search with the given query. Returns null when // the call has been made obsolete. Future?> _search(String query) async { _currentQuery = query; // In a real application, there should be some error handling here. if (query.isNotEmpty && query.length > 3) { final List? suggestions = await service.fetchSuggestions(_currentQuery!); // If another search happened after this one, throw away these options. if (_currentQuery != query) { return null; } _currentQuery = null; return suggestions?.map((suggestion) => suggestion); } else { return null; } } @override void initState() { super.initState(); debouncer = Debouncer(Duration(milliseconds: 50), _search); } @override Widget build(BuildContext context) { return SearchAnchor( isFullScreen: false, builder: (BuildContext context, SearchController controller) { return FormTextInput( controller: widget.controller, title: widget.title ?? "", icon: Icon(Icons.search_rounded), maxLines: 2, requiresValidation: false, onTap: () { controller.openView(); }); }, suggestionsBuilder: (BuildContext context, SearchController controller) async { final List? options = (await debouncer.process(controller.text))?.toList(); if (options == null) { return _lastOptions; } _lastOptions = List.generate(options.length, (int index) { final Suggestion item = options[index]; final dynamic content = item.content; return service.resultWidget(content, () { resultHandler(content, service); controller.closeView(null); }); }); return _lastOptions; }); } }