1
+ import 'dart:math' ;
2
+ import 'dart:ui' ;
3
+
1
4
import 'package:flutter/material.dart' ;
2
5
import 'dart:async' ;
3
6
4
- import 'package:flutter/services.dart' ;
5
7
import 'package:super_editor_spellcheck/super_editor_spellcheck.dart' ;
6
8
7
9
void main () {
@@ -16,48 +18,214 @@ class MyApp extends StatefulWidget {
16
18
}
17
19
18
20
class _MyAppState extends State <MyApp > {
19
- String _platformVersion = 'Unknown' ;
20
- final _superEditorSpellcheckPlugin = SuperEditorSpellcheck ();
21
+ final _superEditorSpellcheckPlugin = SuperEditorSpellCheckerPlugin ();
22
+ final _textController = TextEditingController (text: 'She go to the store everys day.' );
23
+
24
+ List <TextSuggestion > _suggestions = [];
25
+
26
+ TextRange _firstMispelledWord = TextRange .empty;
27
+ List <String > _firstMispelledWordSuggestions = [];
28
+ CheckGrammarResult ? _grammarAnalysis;
29
+ int ? _wordCount;
30
+ int ? _documentTag;
31
+ Map <String , String > _userReplacementsDictionary = < String , String > {};
32
+ List <String > _completionsForLastWord = [];
33
+ Timer ? _searchTimer;
21
34
22
35
@override
23
36
void initState () {
24
37
super .initState ();
25
- initPlatformState ();
38
+
39
+ _textController.addListener (_onTextChanged);
40
+ _fetchSuggestions ();
26
41
}
27
42
28
- // Platform messages are asynchronous, so we initialize in an async method.
29
- Future <void > initPlatformState () async {
30
- String platformVersion;
31
- // Platform messages may fail, so we use a try/catch PlatformException.
32
- // We also handle the message potentially returning null.
33
- try {
34
- platformVersion =
35
- await _superEditorSpellcheckPlugin.getPlatformVersion () ?? 'Unknown platform version' ;
36
- } on PlatformException {
37
- platformVersion = 'Failed to get platform version.' ;
43
+ @override
44
+ void dispose () {
45
+ _textController.removeListener (_onTextChanged);
46
+ _textController.dispose ();
47
+ if (_documentTag != null ) {
48
+ _superEditorSpellcheckPlugin.macSpellChecker.closeSpellDocumentWithTag (_documentTag! );
38
49
}
50
+ super .dispose ();
51
+ }
52
+
53
+ void _onTextChanged () {
54
+ _searchTimer? .cancel ();
55
+ _searchTimer = Timer (
56
+ const Duration (milliseconds: 300 ),
57
+ _fetchSuggestions,
58
+ );
59
+ }
60
+
61
+ Future <void > _fetchSuggestions () async {
62
+ final textToSearch = _textController.text;
63
+ final locale = PlatformDispatcher .instance.locale;
64
+
65
+ int ? tag = _documentTag;
66
+ tag ?? = await _superEditorSpellcheckPlugin.macSpellChecker.uniqueSpellDocumentTag ();
39
67
40
- // If the widget was removed from the tree while the asynchronous platform
41
- // message was in flight, we want to discard the reply rather than calling
42
- // setState to update our non-existent appearance.
43
- if (! mounted) return ;
68
+ final language = _superEditorSpellcheckPlugin.macSpellChecker.convertDartLocaleToMacLanguageCode (locale)! ;
69
+
70
+ final suggestions = await _superEditorSpellcheckPlugin.fetchSuggestions (
71
+ locale,
72
+ textToSearch,
73
+ );
74
+
75
+ if (_shouldAbortCurrentSearch (textToSearch)) {
76
+ return ;
77
+ }
78
+
79
+ final firstMisspelled = await _superEditorSpellcheckPlugin.macSpellChecker.checkSpelling (
80
+ stringToCheck: textToSearch,
81
+ startingOffset: 0 ,
82
+ language: language,
83
+ );
84
+
85
+ if (_shouldAbortCurrentSearch (textToSearch)) {
86
+ return ;
87
+ }
88
+
89
+ final firstSuggestions = firstMisspelled.isValid
90
+ ? await _superEditorSpellcheckPlugin.macSpellChecker.guesses (
91
+ range: firstMisspelled,
92
+ text: textToSearch,
93
+ language: language,
94
+ )
95
+ : < String > [];
96
+
97
+ if (_shouldAbortCurrentSearch (textToSearch)) {
98
+ return ;
99
+ }
100
+
101
+ final grammarAnalysis = await _superEditorSpellcheckPlugin.macSpellChecker.checkGrammar (
102
+ stringToCheck: textToSearch,
103
+ startingOffset: 0 ,
104
+ language: language,
105
+ );
106
+
107
+ if (_shouldAbortCurrentSearch (textToSearch)) {
108
+ return ;
109
+ }
110
+
111
+ final wordCount = await _superEditorSpellcheckPlugin.macSpellChecker.countWords (
112
+ text: textToSearch,
113
+ language: language,
114
+ );
115
+
116
+ if (_shouldAbortCurrentSearch (textToSearch)) {
117
+ return ;
118
+ }
119
+
120
+ final replacements = await _superEditorSpellcheckPlugin.macSpellChecker.userReplacementsDictionary ();
121
+
122
+ if (_shouldAbortCurrentSearch (textToSearch)) {
123
+ return ;
124
+ }
125
+
126
+ final completionOffset = max (textToSearch.lastIndexOf (' ' ), 0 );
127
+
128
+ final completions = await _superEditorSpellcheckPlugin.macSpellChecker.completions (
129
+ partialWordRange: TextRange (start: completionOffset, end: textToSearch.length),
130
+ text: textToSearch,
131
+ language: language,
132
+ );
133
+
134
+ if (_shouldAbortCurrentSearch (textToSearch)) {
135
+ return ;
136
+ }
44
137
45
138
setState (() {
46
- _platformVersion = platformVersion;
139
+ _documentTag = tag;
140
+ _suggestions = suggestions;
141
+ _firstMispelledWord = firstMisspelled;
142
+ _firstMispelledWordSuggestions = firstSuggestions;
143
+ _grammarAnalysis = grammarAnalysis;
144
+ _wordCount = wordCount;
145
+ _userReplacementsDictionary = replacements;
146
+ _completionsForLastWord = completions;
47
147
});
48
148
}
49
149
150
+ bool _shouldAbortCurrentSearch (String textToSearch) {
151
+ if (! mounted) {
152
+ return true ;
153
+ }
154
+
155
+ if (textToSearch != _textController.text) {
156
+ // The user changed the text while the search was happening. Ignore the results,
157
+ // because a new search will happen.
158
+ return true ;
159
+ }
160
+
161
+ return false ;
162
+ }
163
+
50
164
@override
51
165
Widget build (BuildContext context) {
52
166
return MaterialApp (
53
167
home: Scaffold (
54
168
appBar: AppBar (
55
169
title: const Text ('Plugin example app' ),
56
170
),
57
- body: Center (
58
- child: Text ('Running on: $_platformVersion \n ' ),
171
+ body: Padding (
172
+ padding: const EdgeInsets .all (8.0 ),
173
+ child: Column (
174
+ children: [
175
+ TextField (
176
+ controller: _textController,
177
+ ),
178
+ if (_documentTag != null ) //
179
+ Text ('Document tag: $_documentTag ' ),
180
+ if (_wordCount != null ) //
181
+ Text ('Word count: $_wordCount ' ),
182
+ if (_firstMispelledWord.isValid)
183
+ Text ('First misspelled word: ${_textController .text .substring (
184
+ _firstMispelledWord .start ,
185
+ _firstMispelledWord .end ,
186
+ )}' ),
187
+ if (_firstMispelledWordSuggestions.isNotEmpty)
188
+ Text ('Suggestions for first misspelled word: ${_firstMispelledWordSuggestions .join (', ' )}' ),
189
+ const SizedBox (height: 10 ),
190
+ _suggestions.isEmpty
191
+ ? const Text ('No spelling errors found.' )
192
+ : Column (
193
+ crossAxisAlignment: CrossAxisAlignment .start,
194
+ children: _suggestions.map (_buildSuggestions).toList (),
195
+ ),
196
+ if (_grammarAnalysis != null )
197
+ Column (
198
+ crossAxisAlignment: CrossAxisAlignment .start,
199
+ children: _grammarAnalysis! .details.map (_buildGrammarAnalysis).toList (),
200
+ ),
201
+ Column (
202
+ crossAxisAlignment: CrossAxisAlignment .start,
203
+ children: _userReplacementsDictionary.entries.map (_buildReplacement).toList (),
204
+ ),
205
+ if (_completionsForLastWord.isNotEmpty)
206
+ Text ('Completions for last word: ${_completionsForLastWord .join (', ' )}' ),
207
+ ],
208
+ ),
59
209
),
60
210
),
61
211
);
62
212
}
213
+
214
+ Widget _buildSuggestions (TextSuggestion span) {
215
+ return Text (
216
+ '${_textController .text .substring (span .range .start , span .range .end )}: ${span .suggestions .join (', ' )}' ,
217
+ );
218
+ }
219
+
220
+ Widget _buildGrammarAnalysis (GrammaticalAnalysisDetail ? detail) {
221
+ return Text (
222
+ '${_textController .text .substring (detail !.range .start , detail .range .end )}: ${detail .userDescription }' ,
223
+ );
224
+ }
225
+
226
+ Widget _buildReplacement (MapEntry <String , String > entry) {
227
+ return Text (
228
+ 'Replace ${entry .key } with ${entry .value }' ,
229
+ );
230
+ }
63
231
}
0 commit comments