import 'dart:async'; import 'dart:math'; import 'package:flutter/material.dart'; import 'package:sendtrain/services/apis/google_places_service.dart'; import 'package:sendtrain/services/functional/debouncer.dart'; import 'package:sendtrain/widgets/generic/elements/form_text_input.dart'; class FormSearchInput extends StatefulWidget { const FormSearchInput( {super.key, required this.sessionController, required this.service, this.requestCallback, this.optionalPayload}); final TextEditingController sessionController; final dynamic service; final Function? requestCallback; final dynamic optionalPayload; @override State createState() => _FormSearchInputState(); } class _FormSearchInputState extends State { String? _currentQuery; late final service = widget.service; late final requestCallback = widget.requestCallback; // 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. // final Iterable options = await _FakeAPI.search(_currentQuery!); if (query.isNotEmpty) { final List? suggestions = await service.fetchSuggestions(_currentQuery!, 'en'); // 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( builder: (BuildContext context, SearchController controller) { return FormTextInput( controller: widget.sessionController, title: 'Location (optional)', 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]; return ListTile( title: Text(item.description), onTap: () async { // widget.optionalPayload = service.fetchPhoto(json.decode(item.image)); if (item.imageReferences != null) { // get a random photo item from the returned result Map photo = item.imageReferences![ Random().nextInt(item.imageReferences!.length)]; await service.fetchPhoto(photo['name']).then((photoMap) { widget.optionalPayload.photoUri = photoMap['photoUri']; }); } widget.optionalPayload.address = item.address; widget.sessionController.text = item.description; service.finish(); controller.closeView(item.description); }, ); }); return _lastOptions; }); } }