Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions lib/widgets/autocomplete.dart
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ abstract class AutocompleteField<QueryT extends AutocompleteQuery, ResultT exten

class _AutocompleteFieldState<QueryT extends AutocompleteQuery, ResultT extends AutocompleteResult> extends State<AutocompleteField<QueryT, ResultT>> with PerAccountStoreAwareStateMixin<AutocompleteField<QueryT, ResultT>> {
AutocompleteView<QueryT, ResultT>? _viewModel;
final ScrollController _scrollController = ScrollController();

void _initViewModel(QueryT query) {
_viewModel = widget.initViewModel(context, query)
Expand All @@ -63,6 +64,10 @@ class _AutocompleteFieldState<QueryT extends AutocompleteQuery, ResultT extends
} else {
assert(_viewModel!.acceptsQuery(newQuery));
_viewModel!.query = newQuery;
// Reset scroll when query content changes
if (_scrollController.hasClients) {
_scrollController.jumpTo(0);
}
}
}
}
Expand Down Expand Up @@ -96,6 +101,7 @@ class _AutocompleteFieldState<QueryT extends AutocompleteQuery, ResultT extends
void dispose() {
widget.controller.removeListener(_handleControllerChange);
_viewModel?.dispose(); // removes our listener
_scrollController.dispose();
super.dispose();
}

Expand Down Expand Up @@ -138,6 +144,7 @@ class _AutocompleteFieldState<QueryT extends AutocompleteQuery, ResultT extends
child: ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 300), // TODO not hard-coded
child: ListView.builder(
controller: _scrollController,
padding: EdgeInsets.zero,
shrinkWrap: true,
itemCount: _resultsToDisplay.length,
Expand Down
66 changes: 66 additions & 0 deletions test/widgets/autocomplete_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -503,6 +503,72 @@ void main() {

debugNetworkImageHttpClientProvider = null;
});

testWidgets('scroll position resets when query changes', (tester) async {
final composeInputFinder = await setupToComposeInput(tester);
final store = await testBinding.globalStore.perAccount(eg.selfAccount.id);
// Add many emoji starting with 's' to ensure the list is scrollable.
// The autocomplete box has maxHeight of 300, so we need enough items.
store.setServerEmojiData(ServerEmojiData(codeToNames: {
'1f600': ['sa_first_item'],
'1f601': ['sb_second'],
'1f602': ['sc_third'],
'1f603': ['sd_fourth'],
'1f604': ['se_fifth'],
'1f605': ['sf_sixth'],
'1f606': ['sg_seventh'],
'1f607': ['sh_eighth'],
'1f608': ['si_ninth'],
'1f609': ['sj_tenth'],
'1f60a': ['sk_eleventh'],
'1f60b': ['sl_twelfth'],
'1f60c': ['sm_thirteenth'],
'1f60d': ['sn_fourteenth'],
'1f60e': ['so_fifteenth'],
'1f60f': ['sp_sixteenth'],
'1f610': ['sq_seventeenth'],
'1f611': ['sr_eighteenth'],
'1f612': ['ss_nineteenth'],
'1f613': ['sz_last_item'],
}));

// Enter an emoji query to show many options.
// TODO(#226): Remove this extra edit when this bug is fixed.
await tester.enterText(composeInputFinder, 'hi :');
await tester.enterText(composeInputFinder, 'hi :s');
await tester.pump();
await tester.pump();

// Verify the first item is visible and the last is not (needs scrolling).
check(find.text('sa_first_item')).findsOne();

// Find the autocomplete ListView's Scrollable.
final listViewFinder = find.descendant(
of: find.byType(Material),
matching: find.byType(ListView),
);
final scrollableFinder = find.descendant(
of: listViewFinder,
matching: find.byType(Scrollable));

// Scroll until the last item is visible.
await tester.scrollUntilVisible(find.text('sz_last_item'), 50,
scrollable: scrollableFinder);
await tester.pump();

// The last item should now be visible.
check(find.text('sz_last_item')).findsOne();

// Change the query; scroll should reset to top.
await tester.enterText(composeInputFinder, 'hi :sa');
await tester.pump();
await tester.pump();

// After query change, the first matching item should be visible at top.
check(find.text('sa_first_item')).findsOne();

debugNetworkImageHttpClientProvider = null;
});
});

group('TopicAutocomplete', () {
Expand Down
Loading