1
- import 'dart:async' ;
2
1
import 'dart:math' ;
3
2
4
3
import 'package:flutter/material.dart' ;
@@ -181,17 +180,21 @@ class _CodeFieldState extends State<CodeField> {
181
180
ScrollController ? _horizontalCodeScroll;
182
181
final _codeFieldKey = GlobalKey ();
183
182
183
+ OverlayEntry ? _suggestionsPopup;
184
184
Offset _normalPopupOffset = Offset .zero;
185
185
Offset _flippedPopupOffset = Offset .zero;
186
186
double painterWidth = 0 ;
187
187
double painterHeight = 0 ;
188
188
189
- StreamSubscription <bool >? _keyboardVisibilitySubscription;
190
189
FocusNode ? _focusNode;
191
190
String ? lines;
192
191
String longestLine = '' ;
193
192
Size ? windowSize;
194
193
late TextStyle textStyle;
194
+ Color ? _backgroundCol;
195
+
196
+ final _editorKey = GlobalKey ();
197
+ Offset ? _editorOffset;
195
198
196
199
@override
197
200
void initState () {
@@ -202,6 +205,7 @@ class _CodeFieldState extends State<CodeField> {
202
205
203
206
widget.controller.addListener (_onTextChanged);
204
207
widget.controller.addListener (_updatePopupOffset);
208
+ widget.controller.popupController.addListener (_onPopupStateChanged);
205
209
_horizontalCodeScroll = ScrollController ();
206
210
_focusNode = widget.focusNode ?? FocusNode ();
207
211
_focusNode! .attach (context, onKeyEvent: _onKeyEvent);
@@ -226,13 +230,25 @@ class _CodeFieldState extends State<CodeField> {
226
230
void dispose () {
227
231
widget.controller.removeListener (_onTextChanged);
228
232
widget.controller.removeListener (_updatePopupOffset);
233
+ widget.controller.popupController.removeListener (_onPopupStateChanged);
229
234
_numberScroll? .dispose ();
230
235
_codeScroll? .dispose ();
231
236
_horizontalCodeScroll? .dispose ();
232
- unawaited (_keyboardVisibilitySubscription? .cancel ());
233
237
super .dispose ();
234
238
}
235
239
240
+ @override
241
+ void didUpdateWidget (covariant CodeField oldWidget) {
242
+ super .didUpdateWidget (oldWidget);
243
+ widget.controller.removeListener (_onTextChanged);
244
+ widget.controller.removeListener (_updatePopupOffset);
245
+ widget.controller.popupController.removeListener (_onPopupStateChanged);
246
+
247
+ widget.controller.addListener (_onTextChanged);
248
+ widget.controller.addListener (_updatePopupOffset);
249
+ widget.controller.popupController.addListener (_onPopupStateChanged);
250
+ }
251
+
236
252
void rebuild () {
237
253
setState (() {
238
254
WidgetsBinding .instance.addPostFrameCallback ((_) {
@@ -263,6 +279,16 @@ class _CodeFieldState extends State<CodeField> {
263
279
if (line.length > longestLine.length) longestLine = line;
264
280
});
265
281
282
+ if (_codeScroll != null && _editorKey.currentContext != null ) {
283
+ final box = _editorKey.currentContext! .findRenderObject () as RenderBox ? ;
284
+ _editorOffset = box? .localToGlobal (Offset .zero);
285
+ if (_editorOffset != null ) {
286
+ var fixedOffset = _editorOffset! ;
287
+ fixedOffset += Offset (0 , _codeScroll! .offset);
288
+ _editorOffset = fixedOffset;
289
+ }
290
+ }
291
+
266
292
rebuild ();
267
293
}
268
294
@@ -287,7 +313,6 @@ class _CodeFieldState extends State<CodeField> {
287
313
child: Text (longestLine, style: textStyle),
288
314
), // Add extra padding
289
315
),
290
- // ignore: prefer_if_elements_to_conditional_expressions
291
316
widget.expands ? Expanded (child: codeField) : codeField,
292
317
],
293
318
),
@@ -310,12 +335,12 @@ class _CodeFieldState extends State<CodeField> {
310
335
311
336
final themeData = Theme .of (context);
312
337
final styles = CodeTheme .of (context)? .styles;
313
- Color ? backgroundCol = widget.background ??
338
+ _backgroundCol = widget.background ??
314
339
styles? [rootKey]? .backgroundColor ??
315
340
DefaultStyles .backgroundColor;
316
341
317
342
if (widget.decoration != null ) {
318
- backgroundCol = null ;
343
+ _backgroundCol = null ;
319
344
}
320
345
321
346
final defaultTextStyle = TextStyle (
@@ -325,35 +350,6 @@ class _CodeFieldState extends State<CodeField> {
325
350
326
351
textStyle = defaultTextStyle.merge (widget.textStyle);
327
352
328
- final lineNumberSize = textStyle.fontSize;
329
- final lineNumberColor =
330
- widget.gutterStyle.textStyle? .color ?? textStyle.color? .withOpacity (.5 );
331
-
332
- final lineNumberTextStyle =
333
- (widget.gutterStyle.textStyle ?? textStyle).copyWith (
334
- color: lineNumberColor,
335
- fontFamily: textStyle.fontFamily,
336
- fontSize: lineNumberSize,
337
- );
338
-
339
- final gutterStyle = widget.gutterStyle.copyWith (
340
- textStyle: lineNumberTextStyle,
341
- errorPopupTextStyle: widget.gutterStyle.errorPopupTextStyle ??
342
- textStyle.copyWith (
343
- fontSize: DefaultStyles .errorPopupTextSize,
344
- backgroundColor: DefaultStyles .backgroundColor,
345
- fontStyle: DefaultStyles .fontStyle,
346
- ),
347
- );
348
-
349
- Widget ? gutter;
350
- if (gutterStyle.showGutter) {
351
- gutter = GutterWidget (
352
- codeController: widget.controller,
353
- style: gutterStyle,
354
- );
355
- }
356
-
357
353
final codeField = TextField (
358
354
focusNode: _focusNode,
359
355
scrollPadding: widget.padding,
@@ -395,37 +391,48 @@ class _CodeFieldState extends State<CodeField> {
395
391
shortcuts: _shortcuts,
396
392
child: Container (
397
393
decoration: widget.decoration,
398
- color: backgroundCol ,
394
+ color: _backgroundCol ,
399
395
key: _codeFieldKey,
400
396
padding: const EdgeInsets .only (left: 8 ),
401
397
child: Row (
402
398
crossAxisAlignment: CrossAxisAlignment .start,
403
399
children: [
404
- if (gutter != null ) gutter,
405
- Expanded (
406
- child: Stack (
407
- children: [
408
- editingField,
409
- if (widget.controller.popupController.isPopupShown &&
410
- windowSize != null )
411
- Popup (
412
- normalOffset: _normalPopupOffset,
413
- flippedOffset: _flippedPopupOffset,
414
- controller: widget.controller.popupController,
415
- editingWindowSize: windowSize! ,
416
- style: textStyle,
417
- backgroundColor: backgroundCol,
418
- parentFocusNode: _focusNode! ,
419
- ),
420
- ],
421
- ),
422
- ),
400
+ if (widget.gutterStyle.showGutter) _buildGutter (),
401
+ Expanded (key: _editorKey, child: editingField),
423
402
],
424
403
),
425
404
),
426
405
);
427
406
}
428
407
408
+ Widget _buildGutter () {
409
+ final lineNumberSize = textStyle.fontSize;
410
+ final lineNumberColor =
411
+ widget.gutterStyle.textStyle? .color ?? textStyle.color? .withOpacity (.5 );
412
+
413
+ final lineNumberTextStyle =
414
+ (widget.gutterStyle.textStyle ?? textStyle).copyWith (
415
+ color: lineNumberColor,
416
+ fontFamily: textStyle.fontFamily,
417
+ fontSize: lineNumberSize,
418
+ );
419
+
420
+ final gutterStyle = widget.gutterStyle.copyWith (
421
+ textStyle: lineNumberTextStyle,
422
+ errorPopupTextStyle: widget.gutterStyle.errorPopupTextStyle ??
423
+ textStyle.copyWith (
424
+ fontSize: DefaultStyles .errorPopupTextSize,
425
+ backgroundColor: DefaultStyles .backgroundColor,
426
+ fontStyle: DefaultStyles .fontStyle,
427
+ ),
428
+ );
429
+
430
+ return GutterWidget (
431
+ codeController: widget.controller,
432
+ style: gutterStyle,
433
+ );
434
+ }
435
+
429
436
void _updatePopupOffset () {
430
437
final textPainter = _getTextPainter (widget.controller.text);
431
438
final caretHeight = _getCaretHeight (textPainter);
@@ -467,7 +474,8 @@ class _CodeFieldState extends State<CodeField> {
467
474
return max (
468
475
_getCaretOffset (textPainter).dx +
469
476
widget.padding.left -
470
- _horizontalCodeScroll! .offset,
477
+ _horizontalCodeScroll! .offset +
478
+ (_editorOffset? .dx ?? 0 ),
471
479
0 ,
472
480
);
473
481
}
@@ -478,8 +486,43 @@ class _CodeFieldState extends State<CodeField> {
478
486
caretHeight +
479
487
16 +
480
488
widget.padding.top -
481
- _codeScroll! .offset,
489
+ _codeScroll! .offset +
490
+ (_editorOffset? .dy ?? 0 ),
482
491
0 ,
483
492
);
484
493
}
494
+
495
+ void _onPopupStateChanged () {
496
+ final shouldShow =
497
+ widget.controller.popupController.shouldShow && windowSize != null ;
498
+ if (! shouldShow) {
499
+ _suggestionsPopup? .remove ();
500
+ _suggestionsPopup = null ;
501
+ return ;
502
+ }
503
+
504
+ if (_suggestionsPopup == null ) {
505
+ _suggestionsPopup = _buildSuggestionOverlay ();
506
+ Overlay .of (context).insert (_suggestionsPopup! );
507
+ }
508
+
509
+ _suggestionsPopup! .markNeedsBuild ();
510
+ }
511
+
512
+ OverlayEntry _buildSuggestionOverlay () {
513
+ return OverlayEntry (
514
+ builder: (context) {
515
+ return Popup (
516
+ normalOffset: _normalPopupOffset,
517
+ flippedOffset: _flippedPopupOffset,
518
+ controller: widget.controller.popupController,
519
+ editingWindowSize: windowSize! ,
520
+ style: textStyle,
521
+ backgroundColor: _backgroundCol,
522
+ parentFocusNode: _focusNode! ,
523
+ editorOffset: _editorOffset,
524
+ );
525
+ },
526
+ );
527
+ }
485
528
}
0 commit comments