Flutter Autocomplete匹配列表不显示问题求助
Hey there! Let's troubleshoot why your Flutter Autocomplete component isn't showing suggestions after fetching data from your API. I've looked through your code and spotted a few key issues that are causing this problem.
What's Going Wrong?
Initializing the Autocomplete Field in
build()
Flutter calls thebuild()method frequently (like when you callsetState, rotate the screen, etc.). Every time it runs, you're creating a new instance ofAutoCompleteTextField—this resets the component's state, so it can't pick up the updateduserslist after your API call.Static
usersList
Makingusersa static variable breaks Flutter's state management. When you update this list, the widget doesn't recognize the change, even when you callsetState().Missing Loading State Sync
Your loading state wasn't being updated when the API call starts, and the component wasn't reacting properly to the newusersdata once it arrived.
Fixed Code for autocompleter.dart
Here's the revised code that fixes these issues:
import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; import 'user.dart'; import 'dart:convert'; import 'package:autocomplete_textfield/autocomplete_textfield.dart'; class AutoCompleteDemo extends StatefulWidget { AutoCompleteDemo() : super(); final String title = "AutoComplete Demo"; @override _AutoCompleteDemoState createState() => _AutoCompleteDemoState(); } class _AutoCompleteDemoState extends State<AutoCompleteDemo> { AutoCompleteTextField? searchTextField; GlobalKey<AutoCompleteTextFieldState<User>> key = GlobalKey(); List<User> users = []; // Removed static keyword to make it an instance variable bool loading = false; void getUsers(String name) async { // Clear suggestions if input is empty if (name.isEmpty) { setState(() { users = []; loading = false; }); return; } // Show loading state before making API call setState(() => loading = true); try { final response = await http.get(Uri.parse("https://pptndt.com/api/unitsDropDown?name=$name")); if (response.statusCode == 200) { final fetchedUsers = loadUsers(response.body); setState(() { users = fetchedUsers; loading = false; }); print('Fetched ${users.length} users'); } else { print('Failed to fetch users: Status code ${response.statusCode}'); setState(() => loading = false); } } catch (e) { print('Error fetching users: $e'); setState(() => loading = false); } } static List<User> loadUsers(String jsonString) { final parsed = json.decode(jsonString).cast<Map<String, dynamic>>(); return parsed.map<User>((json) => User.fromJson(json)).toList(); } @override void initState() { super.initState(); // Initialize the autocomplete field once in initState searchTextField = AutoCompleteTextField<User>( key: key, clearOnSubmit: false, suggestions: users, style: const TextStyle(color: Colors.black, fontSize: 16.0), decoration: const InputDecoration( contentPadding: EdgeInsets.fromLTRB(10.0, 30.0, 10.0, 20.0), hintText: "Search Name", hintStyle: TextStyle(color: Colors.black), ), textChanged: (query) => getUsers(query), itemFilter: (item, query) { return item.name.toLowerCase().startsWith(query.toLowerCase()); }, itemSorter: (a, b) { return a.name.compareTo(b.name); }, itemSubmitted: (item) { setState(() { searchTextField?.textField.controller.text = item.name; }); }, itemBuilder: (context, item) { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( item.name, style: const TextStyle(fontSize: 20.0), ), const SizedBox(width: 10.0), ], ); }, ); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: Text(widget.title)), body: Column( mainAxisAlignment: MainAxisAlignment.start, children: [ // Show loading indicator while fetching data if (loading) const Padding( padding: EdgeInsets.all(8.0), child: CircularProgressIndicator(), ), // Render the pre-initialized autocomplete field if (searchTextField != null) searchTextField!, ], ), ); } }
Extra Tips to Improve Your Implementation
- Add Debouncing: To avoid spamming your API with a request for every keystroke, add a small delay before triggering the API call. Here's how:
Timer? _debounceTimer; void getUsers(String name) { _debounceTimer?.cancel(); _debounceTimer = Timer(const Duration(milliseconds: 300), () async { // Your existing API logic goes here }); } @override void dispose() { _debounceTimer?.cancel(); super.dispose(); } - Error UI Feedback: Instead of just printing errors to the console, show a snackbar or error message to the user so they know something went wrong.
- Null Safety: I added some null safety checks (like
AutoCompleteTextField?andsearchTextField!) to align with modern Flutter practices.
内容的提问来源于stack exchange,提问作者pri

