SendTrain/lib/widgets/generic/elements/form_search_input.dart

106 lines
3.4 KiB
Dart

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, this.optionalPayload});
final TextEditingController sessionController;
final dynamic optionalPayload;
@override
State<FormSearchInput> createState() => _FormSearchInputState();
}
class _FormSearchInputState extends State<FormSearchInput> {
String? _currentQuery;
final service = GooglePlacesService();
// The most recent suggestions received from the API.
late Iterable<Widget> _lastOptions = <Widget>[];
late final Debouncer debouncer;
// Calls the "remote" API to search with the given query. Returns null when
// the call has been made obsolete.
Future<Iterable<Suggestion>?> _search(String query) async {
_currentQuery = query;
// In a real application, there should be some error handling here.
// final Iterable<String> options = await _FakeAPI.search(_currentQuery!);
if (query.isNotEmpty) {
final List<Suggestion>? 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<Suggestion>? options =
(await debouncer.process(controller.text))?.toList();
if (options == null) {
return _lastOptions;
}
_lastOptions = List<ListTile>.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<String, dynamic> 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;
});
}
}