Repository: akvelon/flutter-code-editor (dart)

Issue:
[Bug] Suggestions popup is not closed after view change
Demonstration:
https://github.com/akvelon/flutter-code-editor/assets/31556582/327c4050-9021-469a-8b58-940127eef2fc

Reproducible example:
```
import 'package:flutter/material.dart';
import 'package:flutter_code_editor/flutter_code_editor.dart';
import 'package:flutter_highlight/themes/monokai-sublime.dart';
import 'package:highlight/languages/java.dart';

void main() {
  runApp(const CodeEditor());
}

final controller1 = CodeController(
  text: '',
  language: java,
);

final controller2 = CodeController(
  text: '',
  language: java,
);

final controller3 = CodeController(
  text: '',
  language: java,
);

class CodeEditor extends StatelessWidget {
  const CodeEditor({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: CodeTheme(
          data: CodeThemeData(styles: monokaiSublimeTheme),
          child: DefaultTabController(
            length: 3,
            child: Scaffold(
              appBar: AppBar(
                bottom: const TabBar(
                  tabs: [
                    Tab(icon: Icon(Icons.directions_car)),
                    Tab(icon: Icon(Icons.directions_transit)),
                    Tab(icon: Icon(Icons.directions_bike)),
                  ],
                ),
                title: const Text('Popup bug'),
              ),
              body: TabBarView(
                children: [
                  SingleChildScrollView(
                    child: CodeField(
                      controller: controller1,
                    ),
                  ),
                  SingleChildScrollView(
                    child: CodeField(
                      controller: controller2,
                    ),
                  ),
                  SingleChildScrollView(
                    child: CodeField(
                      controller: controller3,
                    ),
                  ),
                ],
              ),
            ),
          ),
        ),
      ),
    );
  }
}
```

Pull request description:
Resolves #241

Patch:
diff --git a/.github/workflows/dart.yaml b/.github/workflows/dart.yaml
index 5f127eea..c860f932 100644
--- a/.github/workflows/dart.yaml
+++ b/.github/workflows/dart.yaml
@@ -7,7 +7,7 @@ on:
     branches: [ '**' ]
 
 env:
-  flutter_version: 3.7.1
+  flutter_version: 3.10.2
 
 jobs:
   build:
diff --git a/lib/src/code_field/code_controller.dart b/lib/src/code_field/code_controller.dart
index 8d2d166a..5d1864c7 100644
--- a/lib/src/code_field/code_controller.dart
+++ b/lib/src/code_field/code_controller.dart
@@ -144,7 +144,7 @@ class CodeController extends TextEditingController {
     Set<String> readOnlySectionNames = const {},
     Set<String> visibleSectionNames = const {},
     @Deprecated('Use CodeTheme widget to provide theme to CodeField.')
-        Map<String, TextStyle>? theme,
+    Map<String, TextStyle>? theme,
     this.analysisResult = const AnalysisResult(issues: []),
     this.patternMap,
     this.readOnly = false,
diff --git a/lib/src/code_field/code_field.dart b/lib/src/code_field/code_field.dart
index 15146eaf..51dfb0cc 100644
--- a/lib/src/code_field/code_field.dart
+++ b/lib/src/code_field/code_field.dart
@@ -186,7 +186,7 @@ class CodeField extends StatefulWidget {
     this.onChanged,
     @Deprecated('Use gutterStyle instead') this.lineNumbers,
     @Deprecated('Use gutterStyle instead')
-        this.lineNumberStyle = const GutterStyle(),
+    this.lineNumberStyle = const GutterStyle(),
   })  : assert(
             gutterStyle == null || lineNumbers == null,
             'Can not provide gutterStyle and lineNumbers at the same time. '
@@ -264,6 +264,7 @@ class _CodeFieldState extends State<CodeField> {
     widget.controller.removeListener(_onTextChanged);
     widget.controller.removeListener(_updatePopupOffset);
     widget.controller.popupController.removeListener(_onPopupStateChanged);
+    _suggestionsPopup?.remove();
     widget.controller.searchController.removeListener(
       _onSearchControllerChange,
     );
diff --git a/lib/src/gutter/gutter.dart b/lib/src/gutter/gutter.dart
index c417a9f8..88c7424e 100644
--- a/lib/src/gutter/gutter.dart
+++ b/lib/src/gutter/gutter.dart
@@ -1,3 +1,6 @@
+// TODO(alexeyinkin): Remove when dropping support for Flutter < 3.10, https://github.com/akvelon/flutter-code-editor/issues/245
+// ignore_for_file: unnecessary_non_null_assertion
+
 import 'package:flutter/material.dart';
 
 import '../code_field/code_controller.dart';


Test patch:
diff --git a/test/src/code_field/code_controller_readonly_test.dart b/test/src/code_field/code_controller_readonly_test.dart
index 7ffe1df7..e89586e2 100644
--- a/test/src/code_field/code_controller_readonly_test.dart
+++ b/test/src/code_field/code_controller_readonly_test.dart
@@ -121,7 +121,6 @@ void main() {
             //               cursor /
             selection: TextSelection.collapsed(
               offset: 14,
-              affinity: TextAffinity.upstream,
             ),
           ),
           reason: 'Delete - no effect',
@@ -460,7 +459,6 @@ void main() {
             //          cursor /
             selection: TextSelection.collapsed(
               offset: 10,
-              affinity: TextAffinity.upstream,
             ),
           ),
           reason: 'Backspace Delete Type at last empty line - No effect',
diff --git a/test/src/code_field/code_controller_shortcut_test.dart b/test/src/code_field/code_controller_shortcut_test.dart
index 3d63c241..01303fac 100644
--- a/test/src/code_field/code_controller_shortcut_test.dart
+++ b/test/src/code_field/code_controller_shortcut_test.dart
@@ -18,12 +18,16 @@ class MyClass {
 void main() {
   final calls = <MethodCall>[];
 
-  void mockClipboardHandler() {
+  void mockClipboardHandler(WidgetTester wt) {
     calls.clear();
 
-    SystemChannels.platform.setMockMethodCallHandler((MethodCall call) async {
-      calls.add(call);
-    });
+    wt.binding.defaultBinaryMessenger.setMockMethodCallHandler(
+      SystemChannels.platform,
+      (MethodCall call) async {
+        calls.add(call);
+        return null;
+      },
+    );
   }
 
   group('CodeController. Shortcuts.', () {
@@ -97,7 +101,7 @@ void main() {
         controller.value = const TextEditingValue(text: MethodSnippet.full);
         controller.foldAt(1);
         await wt.selectFromHome(18, offset: 16);
-        mockClipboardHandler();
+        mockClipboardHandler(wt);
 
         await example.act();
 
diff --git a/test/src/history/code_history_controller_test.dart b/test/src/history/code_history_controller_test.dart
index f0609a39..f5faec4a 100644
--- a/test/src/history/code_history_controller_test.dart
+++ b/test/src/history/code_history_controller_test.dart
@@ -258,7 +258,6 @@ void main() {
           controller.selection,
           const TextSelection.collapsed(
             offset: MethodSnippet.visible.length,
-            affinity: TextAffinity.upstream,
           ),
         );
 
@@ -269,7 +268,6 @@ void main() {
           controller.selection,
           const TextSelection.collapsed(
             offset: MethodSnippet.visible.length,
-            affinity: TextAffinity.upstream,
           ),
         );
       });