-
Notifications
You must be signed in to change notification settings - Fork 19
/
Copy pathautocomplete_trigger.dart
113 lines (95 loc) · 4.09 KB
/
autocomplete_trigger.dart
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
import 'package:flutter/material.dart';
import 'package:multi_trigger_autocomplete/src/autocomplete_query.dart';
/// The type of the [AutocompleteTrigger] callback which returns a [Widget] that
/// displays the specified [options].
typedef AutocompleteTriggerOptionsViewBuilder = Widget Function(
BuildContext context,
AutocompleteQuery autocompleteQuery,
TextEditingController textEditingController,
);
class AutocompleteTrigger {
/// Creates a [AutocompleteTrigger] which can be used to trigger
/// autocomplete suggestions.
const AutocompleteTrigger({
required this.trigger,
required this.optionsViewBuilder,
this.triggerOnlyAtStart = false,
this.triggerOnlyAfterSpace = true,
this.minimumRequiredCharacters = 0,
});
/// The trigger character.
///
/// eg. '@', '#', ':'
final String trigger;
/// Whether the [trigger] should only be recognised at the start of the input.
final bool triggerOnlyAtStart;
/// Whether the [trigger] should only be recognised after a space.
final bool triggerOnlyAfterSpace;
/// The minimum required characters for the [trigger] to start recognising
/// a autocomplete options.
final int minimumRequiredCharacters;
/// Builds the selectable options widgets from a list of options objects.
///
/// The options are displayed floating above or below the field using a
/// [PortalTarget] inside of an [Portal].
final AutocompleteTriggerOptionsViewBuilder optionsViewBuilder;
@override
bool operator ==(Object other) =>
identical(this, other) ||
other is AutocompleteTrigger &&
runtimeType == other.runtimeType &&
trigger == other.trigger &&
triggerOnlyAtStart == other.triggerOnlyAtStart &&
triggerOnlyAfterSpace == other.triggerOnlyAfterSpace &&
minimumRequiredCharacters == other.minimumRequiredCharacters;
@override
int get hashCode =>
trigger.hashCode ^
triggerOnlyAtStart.hashCode ^
triggerOnlyAfterSpace.hashCode ^
minimumRequiredCharacters.hashCode;
/// Checks if the user is invoking the recognising [trigger] and returns
/// the autocomplete query if so.
AutocompleteQuery? invokingTrigger(TextEditingValue textEditingValue) {
final text = textEditingValue.text;
final cursorPosition = textEditingValue.selection.baseOffset;
// Find the first [trigger] location before the input cursor.
final firstTriggerIndexBeforeCursor =
text.substring(0, cursorPosition).lastIndexOf(trigger);
// If the [trigger] is not found before the cursor, then it's not a trigger.
if (firstTriggerIndexBeforeCursor == -1) return null;
// If the [trigger] is found before the cursor, but the [trigger] is only
// recognised at the start of the input, then it's not a trigger.
if (triggerOnlyAtStart && firstTriggerIndexBeforeCursor != 0) {
return null;
}
// Only show typing suggestions after a space, or at the start of the input
// valid examples: "@user", "Hello @user"
// invalid examples: "Hello@user"
final textBeforeTrigger = text.substring(0, firstTriggerIndexBeforeCursor);
if (triggerOnlyAfterSpace &&
textBeforeTrigger.isNotEmpty &&
!textBeforeTrigger.endsWith(' ')) {
return null;
}
// The suggestion range. Protect against invalid ranges.
final suggestionStart = firstTriggerIndexBeforeCursor + trigger.length;
final suggestionEnd = cursorPosition;
if (suggestionStart > suggestionEnd) return null;
// Fetch the suggestion text. The suggestions can't have spaces.
// valid example: "@luke_skywa..."
// invalid example: "@luke skywa..."
final suggestionText = text.substring(suggestionStart, suggestionEnd);
if (suggestionText.contains(' ')) return null;
// A minimum number of characters can be provided to only show
// suggestions after the customer has input enough characters.
if (suggestionText.length < minimumRequiredCharacters) return null;
return AutocompleteQuery(
query: suggestionText,
selection: TextSelection(
baseOffset: suggestionStart,
extentOffset: suggestionEnd,
),
);
}
}