All checks were successful
Flutter Schmutter / build (push) Successful in 3m37s
- screaming_possum.gif
186 lines
5.3 KiB
Dart
186 lines
5.3 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
|
|
import 'package:flutter/material.dart';
|
|
|
|
import 'package:get/get.dart';
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'package:f0ckapp/services/api_service.dart';
|
|
import 'package:f0ckapp/models/suggestion_model.dart';
|
|
|
|
class CustomSearchDelegate extends SearchDelegate<String> {
|
|
final ApiService apiService = Get.find<ApiService>();
|
|
Timer? _debounceTimer;
|
|
List<Suggestion>? _suggestions;
|
|
bool _isLoading = false;
|
|
String? _error;
|
|
String _lastFetchedQuery = "";
|
|
|
|
@override
|
|
List<Widget> buildActions(BuildContext context) {
|
|
return [
|
|
IconButton(
|
|
icon: const Icon(Icons.clear),
|
|
onPressed: () {
|
|
query = '';
|
|
_clearResults();
|
|
showSuggestions(context);
|
|
},
|
|
),
|
|
];
|
|
}
|
|
|
|
@override
|
|
Widget buildLeading(BuildContext context) {
|
|
return IconButton(
|
|
icon: const Icon(Icons.arrow_back),
|
|
onPressed: () {
|
|
_debounceTimer?.cancel();
|
|
close(context, 'null');
|
|
},
|
|
);
|
|
}
|
|
|
|
@override
|
|
Widget buildResults(BuildContext context) {
|
|
return Center(child: Text('Suchergebnisse für: "$query"'));
|
|
}
|
|
|
|
Future<List<Suggestion>> fetchSuggestions(String query) async {
|
|
final Uri uri = Uri.parse('https://api.f0ck.me/search/?q=$query');
|
|
try {
|
|
final http.Response response = await http
|
|
.get(uri)
|
|
.timeout(const Duration(seconds: 5));
|
|
|
|
if (response.statusCode == 200) {
|
|
final dynamic decoded = jsonDecode(response.body);
|
|
if (decoded is List) {
|
|
final suggestions = decoded
|
|
.map((item) => Suggestion.fromJson(item as Map<String, dynamic>))
|
|
.toList();
|
|
suggestions.sort((a, b) => b.score.compareTo(a.score));
|
|
return suggestions;
|
|
} else {
|
|
throw Exception('Unerwartetes Format: Es wurde eine Liste erwartet.');
|
|
}
|
|
} else if (response.statusCode == 400) {
|
|
final dynamic error = jsonDecode(response.body);
|
|
final String message = error is Map<String, dynamic>
|
|
? error['detail']?.toString() ?? 'Unbekannter Fehler.'
|
|
: 'Unbekannter Fehler.';
|
|
throw Exception('Client-Fehler 400: $message');
|
|
} else {
|
|
throw Exception(
|
|
'Fehler beim Abrufen der Vorschläge: ${response.statusCode}',
|
|
);
|
|
}
|
|
} on TimeoutException {
|
|
throw Exception('Anfrage an die API hat zu lange gedauert.');
|
|
} catch (e) {
|
|
throw Exception('Fehler bei der Verarbeitung der Anfrage: $e');
|
|
}
|
|
}
|
|
|
|
@override
|
|
Widget buildSuggestions(BuildContext context) {
|
|
return StatefulBuilder(
|
|
builder: (BuildContext context, void Function(void Function()) setState) {
|
|
if (query.isEmpty) {
|
|
_debounceTimer?.cancel();
|
|
return Container(
|
|
padding: const EdgeInsets.all(16.0),
|
|
child: const Text(''),
|
|
);
|
|
}
|
|
|
|
if (query != _lastFetchedQuery) {
|
|
_debounceTimer?.cancel();
|
|
_isLoading = true;
|
|
_error = null;
|
|
_suggestions = null;
|
|
|
|
_debounceTimer = Timer(Duration(milliseconds: 500), () async {
|
|
try {
|
|
final List<Suggestion> results = await fetchSuggestions(query);
|
|
_lastFetchedQuery = query;
|
|
setState(() {
|
|
_suggestions = results;
|
|
_isLoading = false;
|
|
});
|
|
} catch (e) {
|
|
_lastFetchedQuery = query;
|
|
setState(() {
|
|
_error = e.toString();
|
|
_suggestions = [];
|
|
_isLoading = false;
|
|
});
|
|
}
|
|
});
|
|
|
|
return Center(child: _buildLoadingIndicator());
|
|
}
|
|
|
|
if (_isLoading) {
|
|
return Center(child: _buildLoadingIndicator());
|
|
}
|
|
|
|
if (_error != null) {
|
|
return Center(child: Text("Fehler: $_error"));
|
|
}
|
|
|
|
if (_suggestions == null || _suggestions!.isEmpty) {
|
|
return Center(child: const Text("Keine Ergebnisse gefunden."));
|
|
}
|
|
|
|
return ListView.builder(
|
|
itemCount: _suggestions!.length,
|
|
itemBuilder: (BuildContext context, int index) {
|
|
final Suggestion suggestion = _suggestions![index];
|
|
return ListTile(
|
|
title: Text(suggestion.tag),
|
|
subtitle: Text(
|
|
'Getaggt: ${suggestion.tagged}x • Score: ${suggestion.score.toStringAsFixed(2)}',
|
|
style: TextStyle(fontSize: 12),
|
|
),
|
|
onTap: () async {
|
|
await apiService.setTag(suggestion.tag);
|
|
close(context, suggestion.tag);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
Widget _buildLoadingIndicator() {
|
|
return Column(
|
|
mainAxisAlignment: MainAxisAlignment.center,
|
|
children: [
|
|
const CircularProgressIndicator(strokeWidth: 3.0),
|
|
const SizedBox(height: 12),
|
|
const Text(
|
|
'Vorschläge werden geladen...',
|
|
style: TextStyle(fontStyle: FontStyle.italic),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
void _clearResults() {
|
|
_debounceTimer?.cancel();
|
|
_suggestions = null;
|
|
_isLoading = false;
|
|
_error = null;
|
|
_lastFetchedQuery = "";
|
|
}
|
|
|
|
@override
|
|
void close(BuildContext context, String result) {
|
|
_debounceTimer?.cancel();
|
|
super.close(context, result);
|
|
}
|
|
}
|