Repository navigation
Description
Steps to reproduce
- Add "http" package in pubspec
- Run the sample code
Expected results
When you type a keyword into the RawAutocomplete field, the dropdown results should represent the api output from the latest text inside the search field.
Actual results
When you start typing into the RawAutocomplete field the dropdown results show the output of the slowest call resolved by the options builder amongst all the calls in progress instead of the last call made to the optionsBuilder.
For Instance in the sample code i am using the google books api, i have added delay in the function which resolves the http call (As the google books api responds a bit too fast), that reduces with the keyword length.
You can see in the video attached that when i type keywords into the field, the results of the dropdown represent the slowest call resolved amongst all the active calls at the time when i stopped typing, instead of showing the the results from the last call made to the optionsBuilder.
Code sample
Code sample
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
debugShowCheckedModeBanner: false,
home: RawAutoCompleteExample(),
);
}
}
class RawAutoCompleteExample extends StatefulWidget {
const RawAutoCompleteExample({super.key});
@override
State<RawAutoCompleteExample> createState() => _RawAutoCompleteExampleState();
}
class _RawAutoCompleteExampleState extends State<RawAutoCompleteExample> {
final _controller = TextEditingController();
final _focusNode = FocusNode();
@override
void dispose() {
_controller.dispose();
_focusNode.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: SizedBox(
width: 350,
child: RawAutocomplete<String>(
textEditingController: _controller,
focusNode: _focusNode,
fieldViewBuilder: _fieldViewBuilder,
optionsViewBuilder: _optionsViewBuilder,
optionsBuilder: _searchLocation,
),
),
),
);
}
Widget _fieldViewBuilder(
BuildContext context, controller, focusNode, onSubmit) {
return AnimatedContainer(
duration: const Duration(milliseconds: 150),
height: 40,
alignment: Alignment.center,
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 8),
constraints: const BoxConstraints(minHeight: 40),
decoration: BoxDecoration(
color: Colors.transparent,
borderRadius: BorderRadius.circular(8),
border: Border.all(
width: 1,
color: Colors.grey,
),
),
child: TextField(
controller: controller,
focusNode: focusNode,
maxLines: 1,
decoration: const InputDecoration(
isCollapsed: true,
floatingLabelBehavior: FloatingLabelBehavior.never,
alignLabelWithHint: true,
border: InputBorder.none,
),
),
);
}
Widget _optionsViewBuilder(BuildContext context,
void Function(String) onSelect, Iterable<String> options) {
return Align(
alignment: Alignment.topLeft,
child: Material(
child: Container(
margin: const EdgeInsets.only(top: 7),
height: (40.0 * (options.length > 4 ? 4 : options.length)) + 16,
width: 350,
padding: const EdgeInsets.all(8),
clipBehavior: Clip.antiAlias,
decoration: ShapeDecoration(
color: Colors.white,
shape: RoundedRectangleBorder(
side: const BorderSide(
width: 1,
color: Colors.grey,
),
borderRadius: BorderRadius.circular(12),
),
shadows: const [
BoxShadow(
color: Color(0x19000000),
blurRadius: 8,
offset: Offset(0, 4),
spreadRadius: 0,
)
],
),
child: ListView(
children: options
.map((option) => GestureDetector(
onTap: () => onSelect(option),
child: AnimatedContainer(
height: 40,
alignment: Alignment.centerLeft,
decoration: BoxDecoration(
color: Colors.white.withOpacity(0),
borderRadius: BorderRadius.circular(24)),
duration: const Duration(milliseconds: 150),
padding: const EdgeInsets.symmetric(horizontal: 16),
child: Text(
option.toString(),
),
),
))
.toList(),
),
),
),
);
}
Future<List<String>> _searchLocation(TextEditingValue keyword) async {
final response = await http.get(Uri.parse(
'https://dev-gateway.digitalhire.com/v1/recruitment/static-listing/locations?search=${keyword.text}&page=1&pageSize=5000'));
if (response.statusCode == 200) {
List<dynamic> data = json.decode(response.body);
return [
keyword.text,
...data
.map((e) => '${e["city"] as String?}, ${e["state"] as String?}')
.toList()
];
} else {
throw Exception('Failed to load data');
}
}
}
Screenshots or Video
Screenshots / Video demonstration
Screen.Recording.2024-06-25.at.4.33.27.PM.mov
Logs
Logs
[Paste your logs here]Flutter Doctor output
Doctor output
[✓] Flutter (Channel stable, 3.22.2, on macOS 14.0 23A344 darwin-arm64, locale
en-PK)
• Flutter version 3.22.2 on channel stable at
/Users/mohsinraza/Documents/Core/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision 761747bfc5 (3 weeks ago), 2024-06-05 22:15:13 +0200
• Engine revision edd8546116
• Dart version 3.4.3
• DevTools version 2.34.3
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.1)
• Android SDK at /Users/mohsinraza/Library/Android/sdk
• Platform android-34, build-tools 33.0.1
• Java binary at: /Applications/Android
Studio.app/Contents/jbr/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build
17.0.6+0-17.0.6b802.4-9586694)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 15.4)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 15F31d
• CocoaPods version 1.15.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2022.2)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build
17.0.6+0-17.0.6b802.4-9586694)
[✓] IntelliJ IDEA Ultimate Edition (version 2023.1.2)
• IntelliJ at /Applications/IntelliJ IDEA.app
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
[✓] VS Code (version 1.90.0)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.90.0
[✓] Connected device (3 available)
• macOS (desktop) • macos • darwin-arm64 •
macOS 14.0 23A344 darwin-arm64
• Mac Designed for iPad (desktop) • mac-designed-for-ipad • darwin •
macOS 14.0 23A344 darwin-arm64
• Chrome (web) • chrome • web-javascript •
Google Chrome 126.0.6478.63
[✓] Network resources
• All expected network resources are available.
• No issues found!
Activity
danagbemava-nc commentedon Jun 25, 2024
Hi @mohsinraza-fdev, I think this might be an issue with your debouncing logic. You could at certain points have the text show what you have typed but that would not be the request that is made. You can see what I mean by running your app in the web browser and checking the network requests.
How did you come up with the logic for the debouncing? I think using a fixed delay might yield a more consistent result.
mohsinraza-fdev commentedon Jun 25, 2024
A fixed delay would yield a more consistent result if the api responds fast, the purpose of adding a decrementing delay was to mock the behaviour of an api that responds slower, let me give you another example.
If you replace the _searchBooks function with this _searchLocation one you might be able to understand it better,
in the video i have attached i typed 4 letters "m", "o", "n", "a", 4 requests were triggered in the network tab, i have set the search keyword as the first dropdown value so that we are able to tell which keywords request is currently in the dropdown values.
if you watch carefully you will notice that the result from the whole word "mona" resolved fast and was updated in the dropdown values for a split second, but just cause the request from keyword "m" was resolved last, the dropdown values updated to the results from the keyword "m" in the end.
Screen.Recording.2024-06-25.at.10.56.44.PM.mov
victorsanni commentedon Jun 25, 2024
I am working on a PR related to this. Another PR I am working on requires this fix to land.
danagbemava-nc commentedon Jun 26, 2024
Thanks for this demonstration, I do see a similar behavior. Labeling.
complete sample
flutter doctor -v
mohsinraza-fdev commentedon Jun 26, 2024
I have created a pull request #150776 which solves this issue, currently working on adding a test for the changes I have made.
Behaviour after the changes:
Screen.Recording.2024-06-26.at.12.33.01.PM.mov
set onChangedField function to only update options with new results (#…
github-actions commentedon Oct 21, 2024
This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of
flutter doctor -vand a minimal reproduction of the issue.