├── test ├── code_field_test.dart └── code_auto_complete_test.dart ├── example ├── ios │ ├── Runner │ │ ├── Runner-Bridging-Header.h │ │ ├── Assets.xcassets │ │ │ ├── LaunchImage.imageset │ │ │ │ ├── LaunchImage.png │ │ │ │ ├── LaunchImage@2x.png │ │ │ │ ├── LaunchImage@3x.png │ │ │ │ ├── README.md │ │ │ │ └── Contents.json │ │ │ └── AppIcon.appiconset │ │ │ │ ├── Icon-App-20x20@1x.png │ │ │ │ ├── Icon-App-20x20@2x.png │ │ │ │ ├── Icon-App-20x20@3x.png │ │ │ │ ├── Icon-App-29x29@1x.png │ │ │ │ ├── Icon-App-29x29@2x.png │ │ │ │ ├── Icon-App-29x29@3x.png │ │ │ │ ├── Icon-App-40x40@1x.png │ │ │ │ ├── Icon-App-40x40@2x.png │ │ │ │ ├── Icon-App-40x40@3x.png │ │ │ │ ├── Icon-App-60x60@2x.png │ │ │ │ ├── Icon-App-60x60@3x.png │ │ │ │ ├── Icon-App-76x76@1x.png │ │ │ │ ├── Icon-App-76x76@2x.png │ │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ │ ├── Icon-App-83.5x83.5@2x.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Base.lproj │ │ │ ├── Main.storyboard │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Flutter │ │ ├── Debug.xcconfig │ │ ├── Release.xcconfig │ │ └── AppFrameworkInfo.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ │ └── IDEWorkspaceChecks.plist │ │ ├── xcshareddata │ │ │ └── xcschemes │ │ │ │ └── Runner.xcscheme │ │ └── project.pbxproj │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── WorkspaceSettings.xcsettings │ │ │ └── IDEWorkspaceChecks.plist │ ├── .gitignore │ └── Podfile ├── web │ ├── favicon.png │ ├── icons │ │ ├── Icon-192.png │ │ ├── Icon-512.png │ │ ├── Icon-maskable-192.png │ │ └── Icon-maskable-512.png │ ├── manifest.json │ └── index.html ├── android │ ├── gradle.properties │ ├── app │ │ ├── src │ │ │ ├── main │ │ │ │ ├── res │ │ │ │ │ ├── mipmap-hdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-mdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ │ │ └── ic_launcher.png │ │ │ │ │ ├── drawable │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── drawable-v21 │ │ │ │ │ │ └── launch_background.xml │ │ │ │ │ ├── values │ │ │ │ │ │ └── styles.xml │ │ │ │ │ └── values-night │ │ │ │ │ │ └── styles.xml │ │ │ │ ├── kotlin │ │ │ │ │ └── com │ │ │ │ │ │ └── example │ │ │ │ │ │ └── example │ │ │ │ │ │ └── MainActivity.kt │ │ │ │ └── AndroidManifest.xml │ │ │ ├── debug │ │ │ │ └── AndroidManifest.xml │ │ │ └── profile │ │ │ │ └── AndroidManifest.xml │ │ └── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ ├── .gitignore │ ├── settings.gradle │ └── build.gradle ├── macos │ ├── Runner │ │ ├── Configs │ │ │ ├── Debug.xcconfig │ │ │ ├── Release.xcconfig │ │ │ ├── Warnings.xcconfig │ │ │ └── AppInfo.xcconfig │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ ├── app_icon_128.png │ │ │ │ ├── app_icon_16.png │ │ │ │ ├── app_icon_256.png │ │ │ │ ├── app_icon_32.png │ │ │ │ ├── app_icon_512.png │ │ │ │ ├── app_icon_64.png │ │ │ │ ├── app_icon_1024.png │ │ │ │ └── Contents.json │ │ ├── AppDelegate.swift │ │ ├── Release.entitlements │ │ ├── DebugProfile.entitlements │ │ ├── MainFlutterWindow.swift │ │ ├── Info.plist │ │ └── Base.lproj │ │ │ └── MainMenu.xib │ ├── .gitignore │ ├── Flutter │ │ ├── Flutter-Debug.xcconfig │ │ ├── Flutter-Release.xcconfig │ │ └── GeneratedPluginRegistrant.swift │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── Runner.xcodeproj │ │ ├── project.xcworkspace │ │ │ └── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Podfile.lock │ └── Podfile ├── assets │ └── fonts │ │ └── sourceCode │ │ ├── SourceCodePro-Black.ttf │ │ ├── SourceCodePro-Bold.ttf │ │ ├── SourceCodePro-Light.ttf │ │ ├── SourceCodePro-Italic.ttf │ │ ├── SourceCodePro-Medium.ttf │ │ ├── SourceCodePro-Regular.ttf │ │ ├── SourceCodePro-BoldItalic.ttf │ │ ├── SourceCodePro-ExtraLight.ttf │ │ ├── SourceCodePro-SemiBold.ttf │ │ ├── SourceCodePro-BlackItalic.ttf │ │ ├── SourceCodePro-LightItalic.ttf │ │ ├── SourceCodePro-MediumItalic.ttf │ │ ├── SourceCodePro-SemiBoldItalic.ttf │ │ └── SourceCodePro-ExtraLightItalic.ttf ├── lib │ ├── readme │ │ ├── fib.dart │ │ └── readme_examples.dart │ ├── main.dart │ ├── custom_code_box.dart │ └── themes.dart ├── .metadata ├── deploy.sh ├── README.md ├── .gitignore ├── test │ └── widget_test.dart ├── analysis_options.yaml └── pubspec.yaml ├── doc └── images │ ├── top.gif │ ├── android.png │ ├── typing.gif │ ├── example_0.png │ ├── example_1.png │ ├── example_2.png │ ├── example_3.png │ └── long_line.gif ├── code_field.code-workspace ├── lib ├── src │ ├── code_field │ │ ├── editor_params.dart │ │ ├── code_auto_complete.dart │ │ ├── code_controller.dart │ │ └── code_field.dart │ ├── code_theme │ │ ├── code_theme_data.dart │ │ └── code_theme.dart │ ├── code_modifiers │ │ ├── tab_code_modifier.dart │ │ ├── code_modifier.dart │ │ ├── close_block_code_modifier.dart │ │ └── indent_code_modifier.dart │ └── line_numbers │ │ ├── line_number_style.dart │ │ └── line_number_controller.dart └── code_text_field.dart ├── .metadata ├── pubspec.yaml ├── LICENSE ├── CHANGELOG.md ├── .gitignore ├── README.md └── analysis_options.yaml /test/code_field_test.dart: -------------------------------------------------------------------------------- 1 | void main() {} 2 | -------------------------------------------------------------------------------- /example/ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /doc/images/top.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/top.gif -------------------------------------------------------------------------------- /doc/images/android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/android.png -------------------------------------------------------------------------------- /doc/images/typing.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/typing.gif -------------------------------------------------------------------------------- /doc/images/example_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/example_0.png -------------------------------------------------------------------------------- /doc/images/example_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/example_1.png -------------------------------------------------------------------------------- /doc/images/example_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/example_2.png -------------------------------------------------------------------------------- /doc/images/example_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/example_3.png -------------------------------------------------------------------------------- /doc/images/long_line.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/doc/images/long_line.gif -------------------------------------------------------------------------------- /example/web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/web/favicon.png -------------------------------------------------------------------------------- /example/web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/web/icons/Icon-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/web/icons/Icon-512.png -------------------------------------------------------------------------------- /code_field.code-workspace: -------------------------------------------------------------------------------- 1 | { 2 | "folders": [ 3 | { 4 | "path": "." 5 | } 6 | ], 7 | "settings": {} 8 | } 9 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /example/web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /example/macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /example/ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-Black.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-Black.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-Bold.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-Light.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-Light.ttf -------------------------------------------------------------------------------- /lib/src/code_field/editor_params.dart: -------------------------------------------------------------------------------- 1 | class EditorParams { 2 | final int tabSpaces; 3 | 4 | const EditorParams({ 5 | this.tabSpaces = 2, 6 | }); 7 | } 8 | -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-Italic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-Italic.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-Medium.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-Regular.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-BoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-BoldItalic.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-ExtraLight.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-ExtraLight.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-SemiBold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-SemiBold.ttf -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-BlackItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-BlackItalic.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-LightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-LightItalic.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-MediumItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-MediumItalic.ttf -------------------------------------------------------------------------------- /example/macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "ephemeral/Flutter-Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-SemiBoldItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-SemiBoldItalic.ttf -------------------------------------------------------------------------------- /example/assets/fonts/sourceCode/SourceCodePro-ExtraLightItalic.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/assets/fonts/sourceCode/SourceCodePro-ExtraLightItalic.ttf -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/BertrandBev/code_field/HEAD/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/com/example/example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 7 | -------------------------------------------------------------------------------- /example/macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /test/code_auto_complete_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:code_text_field/code_text_field.dart'; 2 | import 'package:flutter_test/flutter_test.dart'; 3 | 4 | void main() { 5 | test('test write back pre function.', () { 6 | var c = CodeAutoComplete.repeatCount('MA', 'MATCH'); 7 | expect(c, 2); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/lib/readme/fib.dart: -------------------------------------------------------------------------------- 1 | // An expensive but pretty 2 | // recursive implementation 3 | int fibonacci(int n) { 4 | if (n <= 1) return n; 5 | return fibonacci(n - 1) 6 | + fibonacci(n - 2); 7 | } 8 | 9 | void main() { 10 | print("Fibonacci sequence:"); 11 | for (var n = 0; n < 10; n++) 12 | print(fibonacci(n)); 13 | } 14 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c5a4b4029c0798f37c4a39b479d7cb75daa7b05c 8 | channel: stable 9 | 10 | project_type: package 11 | -------------------------------------------------------------------------------- /example/.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: c5a4b4029c0798f37c4a39b479d7cb75daa7b05c 8 | channel: stable 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | import url_launcher_macos 9 | 10 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 11 | UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) 12 | } 13 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /example/macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /example/deploy.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | # package.json "deploy": "yarn build; push-dir --dir=dist --branch=gh-pages --cleanup" 3 | 4 | # abort on errors 5 | set -e 6 | 7 | # build 8 | flutter build web --release --no-sound-null-safety 9 | 10 | # navigate into the build output directory 11 | cd build/web 12 | 13 | # Commit repo 14 | git init 15 | git add -A 16 | git commit -m 'deploy' 17 | git push -f git@github.com:BertrandBev/code_field.git master:gh-pages 18 | 19 | # Nav back 20 | cd - -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/src/code_theme/code_theme_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | @immutable 4 | class CodeThemeData { 5 | final Map styles; 6 | 7 | const CodeThemeData({ 8 | required this.styles, 9 | }); 10 | 11 | @override 12 | int get hashCode => styles.hashCode; 13 | 14 | @override 15 | bool operator ==(Object other) { 16 | return identical(this, other) || 17 | other is CodeThemeData && styles == other.styles; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /example/macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /lib/src/code_modifiers/tab_code_modifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import '../code_field/editor_params.dart'; 4 | import 'code_modifier.dart'; 5 | 6 | class TabModifier extends CodeModifier { 7 | const TabModifier() : super('\t'); 8 | 9 | @override 10 | TextEditingValue? updateString( 11 | String text, 12 | TextSelection sel, 13 | EditorParams params, 14 | ) { 15 | final tmp = replace(text, sel.start, sel.end, ' ' * params.tabSpaces); 16 | return tmp; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: code_text_field 2 | description: A customizable code field supporting syntax highlighting, bi-directionnal scrolling and code modifiers 3 | version: 1.1.1 4 | homepage: https://github.com/BertrandBev/code_field 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | flutter: ">=1.26.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | linked_scroll_controller: ^0.2.0 14 | highlight: ^0.7.0 15 | flutter_highlight: ^0.7.0 16 | 17 | dev_dependencies: 18 | flutter_test: 19 | sdk: flutter 20 | 21 | flutter: 22 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # example 2 | 3 | A new Flutter project. 4 | 5 | ## Getting Started 6 | 7 | This project is a starting point for a Flutter application. 8 | 9 | A few resources to get you started if this is your first Flutter project: 10 | 11 | - [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) 12 | - [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) 13 | 14 | For help getting started with Flutter, view our 15 | [online documentation](https://flutter.dev/docs), which offers tutorials, 16 | samples, guidance on mobile development, and a full API reference. 17 | -------------------------------------------------------------------------------- /lib/src/code_theme/code_theme.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | import 'code_theme_data.dart'; 4 | 5 | class CodeTheme extends InheritedWidget { 6 | const CodeTheme({ 7 | required this.data, 8 | Key? key, 9 | required Widget child, 10 | }) : super(key: key, child: child); 11 | 12 | final CodeThemeData? data; 13 | 14 | static CodeThemeData? of(BuildContext context) { 15 | final widget = context.dependOnInheritedWidgetOfExactType(); 16 | return widget?.data; 17 | } 18 | 19 | @override 20 | bool updateShouldNotify(covariant CodeTheme oldWidget) { 21 | return oldWidget.data != data; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /lib/code_text_field.dart: -------------------------------------------------------------------------------- 1 | export 'src/code_field/code_auto_complete.dart'; 2 | export 'src/code_field/code_controller.dart'; 3 | export 'src/code_field/code_field.dart'; 4 | export 'src/code_field/editor_params.dart'; 5 | 6 | export 'src/code_modifiers/close_block_code_modifier.dart'; 7 | export 'src/code_modifiers/code_modifier.dart'; 8 | export 'src/code_modifiers/indent_code_modifier.dart'; 9 | export 'src/code_modifiers/tab_code_modifier.dart'; 10 | 11 | export 'src/code_theme/code_theme.dart'; 12 | export 'src/code_theme/code_theme_data.dart'; 13 | 14 | export 'src/line_numbers/line_number_controller.dart'; 15 | export 'src/line_numbers/line_number_style.dart'; 16 | -------------------------------------------------------------------------------- /example/web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "short_name": "example", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ] 23 | } 24 | -------------------------------------------------------------------------------- /example/macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = example 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2022 com.example. All rights reserved. 15 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /example/macos/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - FlutterMacOS (1.0.0) 3 | - url_launcher_macos (0.0.1): 4 | - FlutterMacOS 5 | 6 | DEPENDENCIES: 7 | - FlutterMacOS (from `Flutter/ephemeral`) 8 | - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) 9 | 10 | EXTERNAL SOURCES: 11 | FlutterMacOS: 12 | :path: Flutter/ephemeral 13 | url_launcher_macos: 14 | :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos 15 | 16 | SPEC CHECKSUMS: 17 | FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424 18 | url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4 19 | 20 | PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c 21 | 22 | COCOAPODS: 1.11.3 23 | -------------------------------------------------------------------------------- /lib/src/line_numbers/line_number_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class LineNumberStyle { 4 | /// Width of the line number column 5 | final double width; 6 | 7 | /// Alignment of the numbers in the column 8 | final TextAlign textAlign; 9 | 10 | /// Style of the numbers 11 | final TextStyle? textStyle; 12 | 13 | /// Background of the line number column 14 | final Color? background; 15 | 16 | /// Central horizontal margin between the numbers and the code 17 | final double margin; 18 | 19 | const LineNumberStyle({ 20 | this.width = 42.0, 21 | this.textAlign = TextAlign.right, 22 | this.margin = 10.0, 23 | this.textStyle, 24 | this.background, 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.7.0' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /lib/src/code_modifiers/code_modifier.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../code_field/editor_params.dart'; 4 | 5 | abstract class CodeModifier { 6 | final String char; 7 | 8 | const CodeModifier(this.char); 9 | 10 | // Helper to insert [str] in [text] between [start] and [end] 11 | TextEditingValue replace(String text, int start, int end, String str) { 12 | final len = str.length; 13 | return TextEditingValue( 14 | text: text.replaceRange(start, end, str), 15 | selection: TextSelection( 16 | baseOffset: start + len, 17 | extentOffset: start + len, 18 | ), 19 | ); 20 | } 21 | 22 | TextEditingValue? updateString( 23 | String text, 24 | TextSelection sel, 25 | EditorParams params, 26 | ); 27 | } 28 | -------------------------------------------------------------------------------- /example/ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | **/ios/Flutter/.last_build_id 26 | .dart_tool/ 27 | .flutter-plugins 28 | .flutter-plugins-dependencies 29 | .packages 30 | .pub-cache/ 31 | .pub/ 32 | /build/ 33 | 34 | # Web related 35 | lib/generated_plugin_registrant.dart 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | -------------------------------------------------------------------------------- /lib/src/code_modifiers/close_block_code_modifier.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | import '../code_field/editor_params.dart'; 6 | import 'code_modifier.dart'; 7 | 8 | class CloseBlockModifier extends CodeModifier { 9 | const CloseBlockModifier() : super('}'); 10 | 11 | @override 12 | TextEditingValue? updateString( 13 | String text, 14 | TextSelection sel, 15 | EditorParams params, 16 | ) { 17 | int spaceCount = 0; 18 | 19 | for (var k = min(sel.start, text.length) - 1; k >= 0; k--) { 20 | if (text[k] == '\n') { 21 | break; 22 | } 23 | 24 | if (text[k] != ' ') { 25 | spaceCount = 0; 26 | break; 27 | } 28 | 29 | spaceCount += 1; 30 | } 31 | 32 | if (spaceCount >= params.tabSpaces) { 33 | return replace(text, sel.start - params.tabSpaces, sel.end, '}'); 34 | } 35 | 36 | return null; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/src/line_numbers/line_number_controller.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class LineNumberController extends TextEditingController { 4 | final TextSpan Function(int, TextStyle?)? lineNumberBuilder; 5 | 6 | LineNumberController( 7 | this.lineNumberBuilder, 8 | ); 9 | 10 | @override 11 | TextSpan buildTextSpan({ 12 | required BuildContext context, 13 | TextStyle? style, 14 | bool? withComposing, 15 | }) { 16 | final children = []; 17 | final list = text.split('\n'); 18 | 19 | for (int k = 0; k < list.length; k++) { 20 | final el = list[k]; 21 | final number = int.parse(el); 22 | var textSpan = TextSpan(text: el, style: style); 23 | 24 | if (lineNumberBuilder != null) { 25 | textSpan = lineNumberBuilder!(number, style); 26 | } 27 | 28 | children.add(textSpan); 29 | if (k < list.length - 1) { 30 | children.add(const TextSpan(text: '\n')); 31 | } 32 | } 33 | 34 | return TextSpan(children: children, style: style); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Bertrand Bevillard 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /example/macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib/src/code_modifiers/indent_code_modifier.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | import '../code_field/editor_params.dart'; 6 | import 'code_modifier.dart'; 7 | 8 | class IndentModifier extends CodeModifier { 9 | final bool handleBrackets; 10 | 11 | const IndentModifier({ 12 | this.handleBrackets = true, 13 | }) : super('\n'); 14 | 15 | @override 16 | TextEditingValue? updateString( 17 | String text, 18 | TextSelection sel, 19 | EditorParams params, 20 | ) { 21 | var spacesCount = 0; 22 | var braceCount = 0; 23 | 24 | for (var k = min(sel.start, text.length) - 1; k >= 0; k--) { 25 | if (text[k] == '\n') { 26 | break; 27 | } 28 | 29 | if (text[k] == ' ') { 30 | spacesCount += 1; 31 | } else { 32 | spacesCount = 0; 33 | } 34 | 35 | if (text[k] == '{') { 36 | braceCount += 1; 37 | } else if (text[k] == '}') { 38 | braceCount -= 1; 39 | } 40 | } 41 | 42 | if (braceCount > 0) { 43 | spacesCount += params.tabSpaces; 44 | } 45 | 46 | final insert = '\n${' ' * spacesCount}'; 47 | return replace(text, sel.start, sel.end, insert); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /example/test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // // This is a basic Flutter widget test. 2 | // // 3 | // // To perform an interaction with a widget in your test, use the WidgetTester 4 | // // utility that Flutter provides. For example, you can send tap and scroll 5 | // // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // // tree, read text, and verify that the values of widget properties are correct. 7 | // 8 | // import 'package:flutter/material.dart'; 9 | // import 'package:flutter_test/flutter_test.dart'; 10 | // 11 | // import 'package:example/main.dart'; 12 | // 13 | // void main() { 14 | // testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // // Build our app and trigger a frame. 16 | // await tester.pumpWidget(const MyApp()); 17 | // 18 | // // Verify that our counter starts at 0. 19 | // expect(find.text('0'), findsOneWidget); 20 | // expect(find.text('1'), findsNothing); 21 | // 22 | // // Tap the '+' icon and trigger a frame. 23 | // await tester.tap(find.byIcon(Icons.add)); 24 | // await tester.pump(); 25 | // 26 | // // Verify that our counter has incremented. 27 | // expect(find.text('0'), findsNothing); 28 | // expect(find.text('1'), findsOneWidget); 29 | // }); 30 | // } 31 | -------------------------------------------------------------------------------- /example/macos/Podfile: -------------------------------------------------------------------------------- 1 | platform :osx, '10.11' 2 | 3 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 4 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 5 | 6 | project 'Runner', { 7 | 'Debug' => :debug, 8 | 'Profile' => :release, 9 | 'Release' => :release, 10 | } 11 | 12 | def flutter_root 13 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__) 14 | unless File.exist?(generated_xcode_build_settings_path) 15 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \"flutter pub get\" is executed first" 16 | end 17 | 18 | File.foreach(generated_xcode_build_settings_path) do |line| 19 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 20 | return matches[1].strip if matches 21 | end 22 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \"flutter pub get\"" 23 | end 24 | 25 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 26 | 27 | flutter_macos_podfile_setup 28 | 29 | target 'Runner' do 30 | use_frameworks! 31 | use_modular_headers! 32 | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) 34 | end 35 | 36 | post_install do |installer| 37 | installer.pods_project.targets.each do |target| 38 | flutter_additional_macos_build_settings(target) 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def flutter_root 14 | generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__) 15 | unless File.exist?(generated_xcode_build_settings_path) 16 | raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first" 17 | end 18 | 19 | File.foreach(generated_xcode_build_settings_path) do |line| 20 | matches = line.match(/FLUTTER_ROOT\=(.*)/) 21 | return matches[1].strip if matches 22 | end 23 | raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get" 24 | end 25 | 26 | require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root) 27 | 28 | flutter_ios_podfile_setup 29 | 30 | target 'Runner' do 31 | use_frameworks! 32 | use_modular_headers! 33 | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 35 | end 36 | 37 | post_install do |installer| 38 | installer.pods_project.targets.each do |target| 39 | flutter_additional_ios_build_settings(target) 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /example/analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /example/web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | example 30 | 31 | 32 | 33 | 36 | 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /example/ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | example 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## [1.0.0-1] - 2021-03-06 2 | 3 | * Initial release 4 | 5 | ## [1.0.0-4] - 2021-03-11 6 | 7 | * Added horizontal scrolling support 8 | * Added code modifiers 9 | * Cleaner padding API 10 | 11 | ## [1.0.0-6] - 2021-03-12 12 | 13 | * Added a temporary fix for https://github.com/flutter/flutter/issues/77929 14 | 15 | ## [1.0.0-7] - 2021-03-12 16 | 17 | * Added a rawText getter to CodeController 18 | 19 | ## [1.0.0-9] - 2021-04-21 20 | 21 | * Removed dependency on flutter_keyboard_visibility 22 | 23 | ## [1.0.1-0] - 2021-05-22 24 | 25 | * TextEditingController.buildTextSpan breaking change migration for flutter 2.2.0 26 | 27 | ## [1.0.1-1] - 2021-06-04 28 | 29 | * Added wrap paramerter to disable horizontal scrolling 30 | 31 | ## [1.0.1-2] - 2021-07-23 32 | 33 | * Fixed highlight parsing on web (issue #11) 34 | 35 | ## [1.0.2] - 2021-07-23 36 | 37 | * removeChar & removeSelection methods added 38 | * added onChange callback 39 | * added enabled flag 40 | * fixed middle dot issue 41 | 42 | ## [1.0.3] - 2022-05-02 43 | 44 | * added onTap to CodeField API 45 | * fixed tab behavior in read-only mode 46 | * added setCursor method to CodeController 47 | 48 | ## [1.0.4] - 2022-06-22 49 | 50 | * added isDense to CodeField API as optional parameter 51 | * added smartQuotesType to CodeField API as optional parameter 52 | * added keyboardType to CodeField API as optional parameter 53 | * solves 'enter' linebreak bug 54 | * adds a macos desktop example app 55 | 56 | ## [1.1.0] - 2022-11-19 57 | 58 | * removed webSpaceFix (https://github.com/flutter/flutter/issues/77929) 59 | * fixed #43, #58, #59, #60, #62, #64 60 | * updated dependencies 61 | 62 | ## [1.1.1] - 2023-07-06 63 | 64 | * added hintText and hintStyle to CodeField API as optional parameters -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | build/ 32 | 33 | # Android related 34 | **/android/**/gradle-wrapper.jar 35 | **/android/.gradle 36 | **/android/captures/ 37 | **/android/gradlew 38 | **/android/gradlew.bat 39 | **/android/local.properties 40 | **/android/**/GeneratedPluginRegistrant.java 41 | 42 | # iOS/XCode related 43 | **/ios/**/*.mode1v3 44 | **/ios/**/*.mode2v3 45 | **/ios/**/*.moved-aside 46 | **/ios/**/*.pbxuser 47 | **/ios/**/*.perspectivev3 48 | **/ios/**/*sync/ 49 | **/ios/**/.sconsign.dblite 50 | **/ios/**/.tags* 51 | **/ios/**/.vagrant/ 52 | **/ios/**/DerivedData/ 53 | **/ios/**/Icon? 54 | **/ios/**/Pods/ 55 | **/ios/**/.symlinks/ 56 | **/ios/**/profile 57 | **/ios/**/xcuserdata 58 | **/ios/.generated/ 59 | **/ios/Flutter/App.framework 60 | **/ios/Flutter/Flutter.framework 61 | **/ios/Flutter/Flutter.podspec 62 | **/ios/Flutter/Generated.xcconfig 63 | **/ios/Flutter/app.flx 64 | **/ios/Flutter/app.zip 65 | **/ios/Flutter/flutter_assets/ 66 | **/ios/Flutter/flutter_export_environment.sh 67 | **/ios/ServiceDefinitions.json 68 | **/ios/Runner/GeneratedPluginRegistrant.* 69 | 70 | # Exceptions to above rules. 71 | !**/ios/**/default.mode1v3 72 | !**/ios/**/default.mode2v3 73 | !**/ios/**/default.pbxuser 74 | !**/ios/**/default.perspectivev3 75 | 76 | pubspec.lock 77 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 31 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | defaultConfig { 36 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 37 | applicationId "com.example.example" 38 | minSdkVersion 16 39 | targetSdkVersion 30 40 | versionCode flutterVersionCode.toInteger() 41 | versionName flutterVersionName 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 59 | } 60 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: example 2 | description: Example 3 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.12.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | cupertino_icons: ^1.0.3 13 | font_awesome_flutter: ^9.0.0 14 | url_launcher: ^6.0.4 15 | 16 | dev_dependencies: 17 | flutter_test: 18 | sdk: flutter 19 | code_text_field: 20 | path: ../ 21 | 22 | flutter: 23 | uses-material-design: true 24 | fonts: 25 | - family: SourceCode 26 | fonts: 27 | - asset: assets/fonts/sourceCode/SourceCodePro-Black.ttf 28 | weight: 900 29 | - asset: assets/fonts/sourceCode/SourceCodePro-BlackItalic.ttf 30 | style: italic 31 | weight: 900 32 | - asset: assets/fonts/sourceCode/SourceCodePro-Bold.ttf 33 | weight: 700 34 | - asset: assets/fonts/sourceCode/SourceCodePro-BoldItalic.ttf 35 | style: italic 36 | weight: 700 37 | - asset: assets/fonts/sourceCode/SourceCodePro-SemiBold.ttf 38 | weight: 600 39 | - asset: assets/fonts/sourceCode/SourceCodePro-SemiBoldItalic.ttf 40 | style: italic 41 | weight: 600 42 | - asset: assets/fonts/sourceCode/SourceCodePro-Medium.ttf 43 | weight: 500 44 | - asset: assets/fonts/sourceCode/SourceCodePro-MediumItalic.ttf 45 | style: italic 46 | weight: 500 47 | - asset: assets/fonts/sourceCode/SourceCodePro-Regular.ttf 48 | weight: 400 49 | - asset: assets/fonts/sourceCode/SourceCodePro-Italic.ttf 50 | style: italic 51 | weight: 400 52 | - asset: assets/fonts/sourceCode/SourceCodePro-Light.ttf 53 | weight: 300 54 | - asset: assets/fonts/sourceCode/SourceCodePro-LightItalic.ttf 55 | style: italic 56 | weight: 300 57 | - asset: assets/fonts/sourceCode/SourceCodePro-ExtraLight.ttf 58 | weight: 200 59 | - asset: assets/fonts/sourceCode/SourceCodePro-ExtraLightItalic.ttf 60 | style: italic 61 | weight: 200 62 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 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 | -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/custom_code_box.dart'; 2 | // import 'package:example/readme/readme_examples.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:url_launcher/url_launcher.dart'; 5 | // ignore: import_of_legacy_library_into_null_safe 6 | import 'package:font_awesome_flutter/font_awesome_flutter.dart'; 7 | 8 | void main() { 9 | runApp(MyApp()); 10 | } 11 | 12 | class MyApp extends StatelessWidget { 13 | @override 14 | Widget build(BuildContext context) { 15 | return MaterialApp( 16 | debugShowCheckedModeBanner: false, 17 | title: 'Code field', 18 | theme: ThemeData( 19 | primarySwatch: Colors.blueGrey, 20 | ), 21 | home: HomePage(), 22 | ); 23 | } 24 | } 25 | 26 | class HomePage extends StatefulWidget { 27 | HomePage({Key? key}) : super(key: key); 28 | 29 | @override 30 | _HomePageState createState() => _HomePageState(); 31 | } 32 | 33 | class _HomePageState extends State { 34 | @override 35 | void initState() { 36 | super.initState(); 37 | } 38 | 39 | @override 40 | void dispose() { 41 | super.dispose(); 42 | } 43 | 44 | Future _launchInBrowser(String url) async { 45 | if (await canLaunch(url)) { 46 | await launch( 47 | url, 48 | forceSafariVC: false, 49 | forceWebView: false, 50 | headers: {'my_header_key': 'my_header_value'}, 51 | ); 52 | } else { 53 | throw 'Could not launch $url'; 54 | } 55 | } 56 | 57 | @override 58 | Widget build(BuildContext context) { 59 | final preset = [ 60 | "dart|monokai-sublime", 61 | "python|atom-one-dark", 62 | "cpp|an-old-hope", 63 | "java|a11y-dark", 64 | "javascript|vs", 65 | ]; 66 | List children = preset.map((e) { 67 | final parts = e.split('|'); 68 | print(parts); 69 | final box = CustomCodeBox( 70 | language: parts[0], 71 | theme: parts[1], 72 | ); 73 | return Padding( 74 | padding: EdgeInsets.only(bottom: 32.0), 75 | child: box, 76 | ); 77 | }).toList(); 78 | final page = Center( 79 | child: Container( 80 | constraints: BoxConstraints(maxWidth: 900), 81 | padding: EdgeInsets.symmetric(vertical: 32.0), 82 | child: Column(children: children), 83 | ), 84 | ); 85 | 86 | // return Scaffold( 87 | // appBar: AppBar(), 88 | // body: CodeEditor(), 89 | // ); 90 | 91 | return Scaffold( 92 | backgroundColor: Color(0xFF363636), 93 | appBar: AppBar( 94 | backgroundColor: Color(0xff23241f), 95 | title: Text("CodeField demo"), 96 | // title: Text("Recursive Fibonacci"), 97 | centerTitle: false, 98 | actions: [ 99 | TextButton.icon( 100 | style: TextButton.styleFrom( 101 | padding: EdgeInsets.symmetric(horizontal: 8.0), 102 | primary: Colors.white, 103 | ), 104 | icon: Icon(FontAwesomeIcons.github), 105 | onPressed: () => 106 | _launchInBrowser("https://github.com/BertrandBev/code_field"), 107 | label: Text("GITHUB"), 108 | ), 109 | SizedBox(width: 8.0), 110 | ], 111 | ), 112 | body: SingleChildScrollView(child: page), 113 | // body: CodeEditor5(), 114 | ); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /example/lib/custom_code_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:example/code_snippets.dart'; 2 | import 'package:example/themes.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:code_text_field/code_text_field.dart'; 5 | import 'package:highlight/languages/all.dart'; 6 | 7 | class CustomCodeBox extends StatefulWidget { 8 | final String language; 9 | final String theme; 10 | 11 | const CustomCodeBox({Key? key, required this.language, required this.theme}) 12 | : super(key: key); 13 | 14 | @override 15 | _CustomCodeBoxState createState() => _CustomCodeBoxState(); 16 | } 17 | 18 | class _CustomCodeBoxState extends State { 19 | String? language; 20 | String? theme; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | language = widget.language; 26 | theme = widget.theme; 27 | } 28 | 29 | List get languageList { 30 | const TOP = { 31 | "java", 32 | "cpp", 33 | "python", 34 | "javascript", 35 | "cs", 36 | "dart", 37 | "haskell", 38 | "ruby", 39 | }; 40 | return [ 41 | ...TOP, 42 | null, // Divider 43 | ...CODE_SNIPPETS.keys.where((el) => !TOP.contains(el)) 44 | ]; 45 | } 46 | 47 | List get themeList { 48 | const TOP = { 49 | "monokai-sublime", 50 | "a11y-dark", 51 | "an-old-hope", 52 | "vs2015", 53 | "vs", 54 | "atom-one-dark", 55 | }; 56 | return [ 57 | ...TOP, 58 | null, // Divider 59 | ...THEMES.keys.where((el) => !TOP.contains(el)) 60 | ]; 61 | } 62 | 63 | Widget buildDropdown(Iterable choices, String value, IconData icon, 64 | Function(String?) onChanged) { 65 | return DropdownButton( 66 | value: value, 67 | items: choices.map((String? value) { 68 | return new DropdownMenuItem( 69 | value: value, 70 | child: value == null 71 | ? Divider() 72 | : Text(value, style: TextStyle(color: Colors.white)), 73 | ); 74 | }).toList(), 75 | icon: Icon(icon, color: Colors.white), 76 | onChanged: onChanged, 77 | dropdownColor: Colors.black87, 78 | ); 79 | } 80 | 81 | @override 82 | Widget build(BuildContext context) { 83 | final codeDropdown = 84 | buildDropdown(languageList, language!, Icons.code, (val) { 85 | if (val == null) return; 86 | setState(() => language = val); 87 | }); 88 | final themeDropdown = 89 | buildDropdown(themeList, theme!, Icons.color_lens, (val) { 90 | if (val == null) return; 91 | setState(() => theme = val); 92 | }); 93 | final dropdowns = Row(children: [ 94 | SizedBox(width: 12.0), 95 | codeDropdown, 96 | SizedBox(width: 12.0), 97 | themeDropdown, 98 | ]); 99 | final codeField = InnerField( 100 | key: ValueKey("$language - $theme"), 101 | language: language!, 102 | theme: theme!, 103 | ); 104 | return Column(children: [ 105 | dropdowns, 106 | codeField, 107 | ]); 108 | } 109 | } 110 | 111 | class InnerField extends StatefulWidget { 112 | final String language; 113 | final String theme; 114 | 115 | const InnerField({Key? key, required this.language, required this.theme}) 116 | : super(key: key); 117 | 118 | @override 119 | _InnerFieldState createState() => _InnerFieldState(); 120 | } 121 | 122 | class _InnerFieldState extends State { 123 | CodeController? _codeController; 124 | 125 | @override 126 | void initState() { 127 | super.initState(); 128 | _codeController = CodeController( 129 | text: CODE_SNIPPETS[widget.language], 130 | patternMap: { 131 | r"\B#[a-zA-Z0-9]+\b": TextStyle(color: Colors.red), 132 | r"\B@[a-zA-Z0-9]+\b": TextStyle( 133 | fontWeight: FontWeight.w800, 134 | color: Colors.blue, 135 | ), 136 | r"\B![a-zA-Z0-9]+\b": 137 | TextStyle(color: Colors.yellow, fontStyle: FontStyle.italic), 138 | }, 139 | stringMap: { 140 | "bev": TextStyle(color: Colors.indigo), 141 | }, 142 | language: allLanguages[widget.language], 143 | ); 144 | } 145 | 146 | @override 147 | void dispose() { 148 | _codeController?.dispose(); 149 | super.dispose(); 150 | } 151 | 152 | @override 153 | Widget build(BuildContext context) { 154 | final styles = THEMES[widget.theme]; 155 | 156 | if (styles == null) { 157 | return _buildCodeField(); 158 | } 159 | 160 | return CodeTheme( 161 | data: CodeThemeData(styles: styles), 162 | child: _buildCodeField(), 163 | ); 164 | } 165 | 166 | Widget _buildCodeField() { 167 | return CodeField( 168 | controller: _codeController!, 169 | textStyle: TextStyle(fontFamily: 'SourceCode'), 170 | ); 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lib/src/code_field/code_auto_complete.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:highlight/highlight.dart'; 5 | import '../../code_text_field.dart'; 6 | 7 | /// config auto complete 8 | class CodeAutoComplete { 9 | /// can input your options which created through editor text and language. 10 | List Function(String, int cursorIndex, Mode?) optionsBuilder; 11 | 12 | /// depends on your options, you can create your own item widget. 13 | final Widget Function(BuildContext, T, bool, Function(String) onTap) 14 | itemBuilder; 15 | 16 | /// set the tip panel size. 17 | final BoxConstraints constraints; 18 | 19 | /// set the tip panel background color. 20 | final Color? backgroundColor; 21 | 22 | /// the tip panel display status. 23 | bool isShowing = false; 24 | 25 | /// the tip panel current index of items. 26 | int current = 0; 27 | 28 | /// the tip panel set state function. 29 | void Function(void Function())? panelSetState; 30 | 31 | /// the code field widget. 32 | late CodeField widget; 33 | 34 | /// a getter function to get the text value from option, default to toString 35 | String Function(T)? optionValue; 36 | 37 | /// the options list. 38 | List options = []; 39 | 40 | /// the panel offset. 41 | Offset? offset; 42 | final StreamController streamController = StreamController.broadcast(); 43 | Stream get stream => streamController.stream; 44 | 45 | CodeAutoComplete({ 46 | required this.optionsBuilder, 47 | required this.itemBuilder, 48 | this.offset, 49 | this.constraints = const BoxConstraints(maxHeight: 300, maxWidth: 240), 50 | this.backgroundColor, 51 | this.optionValue, 52 | }); 53 | 54 | OverlayEntry? panelOverlay; 55 | 56 | /// remove the tip panel. 57 | void remove() { 58 | if (panelOverlay != null) panelOverlay?.remove(); 59 | panelOverlay = null; 60 | } 61 | 62 | /// hide the tip panel. 63 | void hide() { 64 | streamController.add(null); 65 | } 66 | 67 | /// create and show the tip panel. 68 | void show(BuildContext codeFieldContext, CodeField wdg, FocusNode focusNode) { 69 | widget = wdg; 70 | OverlayEntry overlayEntry = OverlayEntry(builder: (context) { 71 | return StreamBuilder( 72 | stream: stream, 73 | builder: (context, snapshot) { 74 | isShowing = false; 75 | current = 0; 76 | options = optionsBuilder( 77 | widget.controller.text, 78 | widget.controller.selection.baseOffset, 79 | widget.controller.language, 80 | ); 81 | if (!focusNode.hasFocus || options.isEmpty) return const Offstage(); 82 | if (snapshot.hasData && 83 | snapshot.data != true && 84 | snapshot.data != null && 85 | '${snapshot.data}'.isNotEmpty) { 86 | isShowing = true; 87 | return panelWrap(codeFieldContext, wdg, focusNode); 88 | } else { 89 | return const Offstage(); 90 | } 91 | }, 92 | ); 93 | }); 94 | 95 | panelOverlay = overlayEntry; 96 | Overlay.of(codeFieldContext).insert(overlayEntry); 97 | } 98 | 99 | /// the core widget of tip panel. 100 | Widget buildPanel(BuildContext context) { 101 | return SingleChildScrollView( 102 | child: Column( 103 | mainAxisSize: MainAxisSize.min, 104 | children: options 105 | .map((tip) => itemBuilder( 106 | context, tip, current == options.indexOf(tip), write)) 107 | .toList(), 108 | ), 109 | ); 110 | } 111 | 112 | /// write the text to code field. 113 | void write(String text) { 114 | var offset = widget.controller.selection.baseOffset; 115 | int start = repeatCount(widget.controller.text.substring(0, offset), text); 116 | widget.controller 117 | ..text = widget.controller.text.replaceRange( 118 | widget.controller.selection.baseOffset - start, 119 | widget.controller.selection.baseOffset, 120 | text) 121 | ..selection = TextSelection.fromPosition( 122 | TextPosition(offset: offset + text.length - start)); 123 | widget.onChanged?.call(widget.controller.text); 124 | hide(); 125 | } 126 | 127 | /// write the current item text to code field. 128 | void writeCurrent() { 129 | if (options.isNotEmpty) { 130 | write(optionValue?.call(options[current]) ?? options[current].toString()); 131 | } 132 | } 133 | 134 | /// get the repeat count of pre word and tip word. 135 | static int repeatCount(String text, String text2) { 136 | text = text.toLowerCase(); 137 | text2 = text2.toLowerCase(); 138 | var same = 0; 139 | while (text2.isNotEmpty) { 140 | if (text.endsWith(text2)) { 141 | return same += text2.length; 142 | } 143 | text2 = text2.substring(0, text2.length - 1); 144 | } 145 | return same; 146 | } 147 | 148 | /// get the panel offset through the cursor offset. 149 | Offset cursorOffset( 150 | BuildContext context, CodeField widget, FocusNode focusNode) { 151 | var s = widget.controller.text; 152 | TextStyle textStyle = widget.textStyle ?? const TextStyle(); 153 | textStyle = textStyle.copyWith( 154 | fontSize: textStyle.fontSize ?? 16.0, 155 | ); 156 | TextPainter painter = TextPainter( 157 | textDirection: TextDirection.ltr, 158 | text: TextSpan( 159 | style: textStyle, 160 | text: s.substring(0, widget.controller.selection.baseOffset), 161 | ), 162 | )..layout(); 163 | var cursorBefore = s.substring(0, widget.controller.selection.baseOffset); 164 | TextPainter hpainter = TextPainter( 165 | textDirection: TextDirection.ltr, 166 | text: TextSpan( 167 | style: textStyle, 168 | text: cursorBefore.substring(max(cursorBefore.lastIndexOf('\n'), 0)), 169 | ), 170 | )..layout(); 171 | 172 | return Offset(focusNode.offset.dx + hpainter.width, 173 | focusNode.offset.dy + painter.height) + 174 | (offset ?? Offset.zero); 175 | } 176 | 177 | /// the style widget of tip panel. 178 | Widget panelWrap(BuildContext context, CodeField wdg, FocusNode focusNode) { 179 | Offset offset = cursorOffset(context, widget, focusNode); 180 | return Positioned( 181 | top: offset.dy, 182 | left: offset.dx, 183 | child: Material( 184 | elevation: 8, 185 | child: StatefulBuilder(builder: (context, setState) { 186 | panelSetState = setState; 187 | return background(ConstrainedBox( 188 | constraints: constraints, 189 | child: ConstrainedBox( 190 | constraints: constraints, 191 | child: buildPanel(context), 192 | ), 193 | )); 194 | }), 195 | )); 196 | } 197 | 198 | /// the style widget of tip panel. 199 | Widget background(Widget content) { 200 | if (backgroundColor != null) { 201 | return ColoredBox(color: backgroundColor!, child: content); 202 | } 203 | return content; 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /example/lib/readme/readme_examples.dart: -------------------------------------------------------------------------------- 1 | /* 2 | * Simple example 3 | */ 4 | 5 | import 'package:flutter/material.dart'; 6 | import 'package:code_text_field/code_text_field.dart'; 7 | // Import the language & theme 8 | import 'package:highlight/languages/dart.dart'; 9 | import 'package:flutter_highlight/themes/monokai-sublime.dart'; 10 | import 'package:flutter_highlight/themes/a11y-dark.dart'; 11 | 12 | class CodeEditor extends StatefulWidget { 13 | @override 14 | _CodeEditorState createState() => _CodeEditorState(); 15 | } 16 | 17 | class _CodeEditorState extends State { 18 | CodeController? _codeController; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | final source = ""; 24 | // Instantiate the CodeController 25 | _codeController = CodeController( 26 | text: source, 27 | language: dart, 28 | ); 29 | } 30 | 31 | @override 32 | void dispose() { 33 | _codeController?.dispose(); 34 | super.dispose(); 35 | } 36 | 37 | @override 38 | Widget build(BuildContext context) { 39 | return CodeTheme( 40 | data: CodeThemeData(styles: monokaiSublimeTheme), 41 | child: CodeField( 42 | controller: _codeController!, 43 | textStyle: TextStyle(fontFamily: 'SourceCode'), 44 | expands: true, 45 | ), 46 | ); 47 | } 48 | } 49 | 50 | class CodeEditor1 extends StatefulWidget { 51 | @override 52 | _CodeEditor1State createState() => _CodeEditor1State(); 53 | } 54 | 55 | class _CodeEditor1State extends State { 56 | CodeController? _codeController; 57 | 58 | @override 59 | void initState() { 60 | super.initState(); 61 | final source = "void main() {\n print(\"Hello, world!\");\n}"; 62 | // Instantiate the CodeController 63 | _codeController = CodeController( 64 | text: source, 65 | language: dart, 66 | ); 67 | } 68 | 69 | @override 70 | void dispose() { 71 | _codeController?.dispose(); 72 | super.dispose(); 73 | } 74 | 75 | @override 76 | Widget build(BuildContext context) { 77 | return CodeTheme( 78 | data: CodeThemeData(styles: monokaiSublimeTheme), 79 | child: CodeField( 80 | controller: _codeController!, 81 | textStyle: TextStyle(fontFamily: 'SourceCode'), 82 | ), 83 | ); 84 | } 85 | } 86 | 87 | /* 88 | * Custom map example 89 | */ 90 | 91 | class CodeEditor2 extends StatefulWidget { 92 | @override 93 | _CodeEditor2State createState() => _CodeEditor2State(); 94 | } 95 | 96 | class _CodeEditor2State extends State { 97 | CodeController? _codeController; 98 | 99 | @override 100 | void initState() { 101 | super.initState(); 102 | final source = "void main() {\n print(\"Hello, world!\");\n}"; 103 | // Instantiate the CodeController 104 | _codeController = CodeController( 105 | text: source, 106 | language: dart, 107 | stringMap: { 108 | "Hello": TextStyle(fontWeight: FontWeight.bold, color: Colors.red), 109 | "world": TextStyle(fontStyle: FontStyle.italic, color: Colors.green), 110 | }, 111 | ); 112 | } 113 | 114 | @override 115 | void dispose() { 116 | _codeController?.dispose(); 117 | super.dispose(); 118 | } 119 | 120 | @override 121 | Widget build(BuildContext context) { 122 | return CodeTheme( 123 | data: CodeThemeData(styles: monokaiSublimeTheme), 124 | child: CodeField( 125 | controller: _codeController!, 126 | textStyle: TextStyle(fontFamily: 'SourceCode'), 127 | ), 128 | ); 129 | } 130 | } 131 | 132 | /* 133 | * Custom patterns 134 | */ 135 | 136 | class CodeEditor3 extends StatefulWidget { 137 | @override 138 | _CodeEditor3State createState() => _CodeEditor3State(); 139 | } 140 | 141 | class _CodeEditor3State extends State { 142 | CodeController? _codeController; 143 | 144 | @override 145 | void initState() { 146 | super.initState(); 147 | final source = "void main() {\n print(\"#Hello, #world!\");\n}"; 148 | // Instantiate the CodeController 149 | _codeController = CodeController( 150 | text: source, 151 | language: dart, 152 | patternMap: { 153 | r"\B#[a-zA-Z0-9]+\b": 154 | TextStyle(fontWeight: FontWeight.bold, color: Colors.purpleAccent), 155 | }, 156 | ); 157 | } 158 | 159 | @override 160 | void dispose() { 161 | _codeController?.dispose(); 162 | super.dispose(); 163 | } 164 | 165 | @override 166 | Widget build(BuildContext context) { 167 | return CodeTheme( 168 | data: CodeThemeData(styles: monokaiSublimeTheme), 169 | child: CodeField( 170 | controller: _codeController!, 171 | textStyle: TextStyle(fontFamily: 'SourceCode'), 172 | ), 173 | ); 174 | } 175 | } 176 | 177 | /* 178 | * Custom map 179 | */ 180 | 181 | class CodeEditor4 extends StatefulWidget { 182 | @override 183 | _CodeEditor4State createState() => _CodeEditor4State(); 184 | } 185 | 186 | class _CodeEditor4State extends State { 187 | CodeController? _codeController; 188 | 189 | @override 190 | void initState() { 191 | super.initState(); 192 | final source = "void main() {\n print(\"#Hello, #world!\");\n}"; 193 | // Instantiate the CodeController 194 | _codeController = CodeController( 195 | text: source, 196 | patternMap: { 197 | r'".*"': TextStyle(color: Colors.yellow), 198 | r'[a-zA-Z0-9]+\(.*\)': TextStyle(color: Colors.green), 199 | }, 200 | stringMap: { 201 | "void": TextStyle(fontWeight: FontWeight.bold, color: Colors.red), 202 | "print": TextStyle(fontWeight: FontWeight.bold, color: Colors.blue), 203 | }, 204 | ); 205 | } 206 | 207 | @override 208 | void dispose() { 209 | _codeController?.dispose(); 210 | super.dispose(); 211 | } 212 | 213 | @override 214 | Widget build(BuildContext context) { 215 | return CodeField( 216 | controller: _codeController!, 217 | textStyle: TextStyle( 218 | fontFamily: 'SourceCode', 219 | // color: Colors.grey.shade100, 220 | ), 221 | lineNumberStyle: LineNumberStyle( 222 | textStyle: TextStyle( 223 | // color: Colors.grey.shade500, 224 | )), 225 | ); 226 | } 227 | } 228 | 229 | /* 230 | * Android screen 231 | */ 232 | 233 | class CodeEditor5 extends StatefulWidget { 234 | @override 235 | _CodeEditor5State createState() => _CodeEditor5State(); 236 | } 237 | 238 | class _CodeEditor5State extends State { 239 | CodeController? _codeController; 240 | 241 | @override 242 | void initState() { 243 | super.initState(); 244 | final source = """// An expensive but pretty 245 | // recursive implementation 246 | int fibonacci(int n) { 247 | if (n <= 1) return n; 248 | return fibonacci(n - 1) 249 | + fibonacci(n - 2); 250 | } 251 | 252 | void main() { 253 | print("Fibonacci sequence:"); 254 | for (var n = 0; n < 10; n++) 255 | print(fibonacci(n)); 256 | } 257 | """; 258 | // Instantiate the CodeController 259 | _codeController = CodeController( 260 | text: source, 261 | language: dart, 262 | ); 263 | } 264 | 265 | @override 266 | void dispose() { 267 | _codeController?.dispose(); 268 | super.dispose(); 269 | } 270 | 271 | @override 272 | Widget build(BuildContext context) { 273 | return CodeTheme( 274 | data: CodeThemeData(styles: a11yDarkTheme), 275 | child: CodeField( 276 | controller: _codeController!, 277 | textStyle: TextStyle(fontFamily: 'SourceCode'), 278 | expands: true, 279 | ), 280 | ); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CodeField 2 | 3 | A customizable code text field supporting syntax highlighting 4 | 5 | [![Pub](https://img.shields.io/pub/v/code_text_field.svg)](https://pub.dev/packages/code_text_field) 6 | [![Website shields.io](https://img.shields.io/website-up-down-green-red/http/shields.io.svg)](https://bertrandbev.github.io/code_field/) 7 | [![GitHub license](https://img.shields.io/github/license/Naereen/StrapDown.js.svg)](https://raw.githubusercontent.com/BertrandBev/code_field/master/LICENSE) 8 | [![Awesome Flutter](https://img.shields.io/badge/Awesome-Flutter-blue.svg?longCache=true&style=flat-square)](https://github.com/Solido/awesome-flutter) 9 | 10 | 11 | 12 | ## Live demo 13 | 14 | A [live demo](https://bertrandbev.github.io/code_field/#/) showcasing a few language / theme combinations 15 | 16 | ## Showcase 17 | 18 | The experimental VM [dlox](https://github.com/BertrandBev/dlox) uses **CodeField** in its [online editor](https://bertrandbev.github.io/dlox/#/) 19 | 20 | 21 | ## Features 22 | 23 | - Code highlight for 189 built-in languages with 90 themes thanks to [flutter_highlight](https://pub.dev/packages/flutter_highlight) 24 | - Easy language highlight customization through the use of theme maps 25 | - Fully customizable code field style through a TextField like API 26 | - Handles horizontal/vertical scrolling and vertical expansion 27 | - Supports code modifiers 28 | - Works on Android, iOS, Web, MacOS, Windows, and Linux 29 | 30 | Code modifiers help manage indents automatically 31 | 32 | 33 | 34 | 35 | The editor is wrapped in a horizontal scrollable container to handle long lines 36 | 37 | 38 | 39 | 40 | 41 | ## Installing 42 | 43 | In the `pubspec.yaml` of your flutter project, add the following dependency: 44 | 45 | ```yaml 46 | dependencies: 47 | ... 48 | code_text_field: 49 | ``` 50 | 51 | [latest version](https://pub.dev/packages/code_text_field/install) 52 | 53 | In your library add the following import: 54 | 55 | ```dart 56 | import 'package:code_text_field/code_text_field.dart'; 57 | ``` 58 | 59 | 60 | ## Simple example 61 | 62 | A CodeField widget works with a **CodeController** which dynamically parses the text input according to a language and renders it with a theme map 63 | 64 | ```dart 65 | import 'package:flutter/material.dart'; 66 | import 'package:code_text_field/code_text_field.dart'; 67 | // Import the language & theme 68 | import 'package:highlight/languages/dart.dart'; 69 | import 'package:flutter_highlight/themes/monokai-sublime.dart'; 70 | 71 | class CodeEditor extends StatefulWidget { 72 | @override 73 | _CodeEditorState createState() => _CodeEditorState(); 74 | } 75 | 76 | class _CodeEditorState extends State { 77 | CodeController? _codeController; 78 | 79 | @override 80 | void initState() { 81 | super.initState(); 82 | final source = "void main() {\n print(\"Hello, world!\");\n}"; 83 | // Instantiate the CodeController 84 | _codeController = CodeController( 85 | text: source, 86 | language: dart, 87 | ); 88 | } 89 | 90 | @override 91 | void dispose() { 92 | _codeController?.dispose(); 93 | super.dispose(); 94 | } 95 | 96 | @override 97 | Widget build(BuildContext context) { 98 | return CodeTheme( 99 | data: const CodeThemeData(styles: monokaiSublimeTheme), 100 | child: CodeField( 101 | controller: _codeController!, 102 | textStyle: const TextStyle(fontFamily: 'SourceCode'), 103 | ), 104 | ); 105 | } 106 | } 107 | ``` 108 | 109 | 110 | 111 | Here, the monospace font [Source Code Pro](https://fonts.google.com/specimen/Source+Code+Pro?preview.text_type=custom) has been added to the assets folder and to the [pubspec.yaml](https://github.com/BertrandBev/code_field/blob/master/example/pubspec.yaml) file. 112 | 113 | 114 | ## Parser options 115 | 116 | On top of a language definition, word-wise styling can be specified in the **stringMap** field 117 | 118 | ```dart 119 | _codeController = CodeController( 120 | //... 121 | stringMap: { 122 | "Hello": TextStyle(fontWeight: FontWeight.bold, color: Colors.red), 123 | "world": TextStyle(fontStyle: FontStyle.italic, color: Colors.green), 124 | }, 125 | ); 126 | ``` 127 | 128 | 129 | 130 | More complex regexes may also be used with the **patternMap**. When a language is used though, its regexes patterns take precedence over **patternMap** and **stringMap**. 131 | 132 | ```dart 133 | _codeController = CodeController( 134 | //... 135 | patternMap: { 136 | r"\B#[a-zA-Z0-9]+\b": 137 | TextStyle(fontWeight: FontWeight.bold, color: Colors.purpleAccent), 138 | }, 139 | ); 140 | ``` 141 | 142 | 143 | 144 | Both **patternMap** and **stringMap** can be used without specifying a language. 145 | 146 | ```dart 147 | _codeController = CodeController( 148 | text: source, 149 | patternMap: { 150 | r'".*"': TextStyle(color: Colors.yellow), 151 | r'[a-zA-Z0-9]+\(.*\)': TextStyle(color: Colors.green), 152 | }, 153 | stringMap: { 154 | "void": TextStyle(fontWeight: FontWeight.bold, color: Colors.red), 155 | "print": TextStyle(fontWeight: FontWeight.bold, color: Colors.blue), 156 | }, 157 | ); 158 | ``` 159 | 160 | 161 | 162 | 163 | ## Code Modifiers 164 | 165 | Code modifiers can be created to react to special keystrokes. 166 | The default modifiers handle tab to space & automatic indentation. Here's the implementation of the default **TabModifier** 167 | 168 | ```dart 169 | class TabModifier extends CodeModifier { 170 | const TabModifier() : super('\t'); 171 | 172 | @override 173 | TextEditingValue? updateString( 174 | String text, TextSelection sel, EditorParams params) { 175 | final tmp = replace(text, sel.start, sel.end, " " * params.tabSpaces); 176 | return tmp; 177 | } 178 | } 179 | ``` 180 | 181 | ## API 182 | 183 | ### CodeField 184 | 185 | ```dart 186 | const CodeField({ 187 | Key? key, 188 | required this.controller, 189 | this.minLines, 190 | this.maxLines, 191 | this.expands = false, 192 | this.wrap = false, 193 | this.background, 194 | this.decoration, 195 | this.textStyle, 196 | this.padding = EdgeInsets.zero, 197 | this.lineNumberStyle = const LineNumberStyle(), 198 | this.enabled, 199 | this.onTap, 200 | this.readOnly = false, 201 | this.cursorColor, 202 | this.textSelectionTheme, 203 | this.lineNumberBuilder, 204 | this.focusNode, 205 | this.onChanged, 206 | this.isDense = false, 207 | this.smartQuotesType, 208 | this.keyboardType, 209 | this.lineNumbers = true, 210 | this.horizontalScroll = true, 211 | this.selectionControls, 212 | this.hintText, 213 | this.hintStyle, 214 | }) 215 | ``` 216 | ### LineNumberStyle 217 | 218 | ```dart 219 | const LineNumberStyle({ 220 | this.width = 42.0, 221 | this.textAlign = TextAlign.right, 222 | this.margin = 10.0, 223 | this.textStyle, 224 | this.background, 225 | }); 226 | ``` 227 | 228 | ### CodeController 229 | 230 | ```dart 231 | CodeController({ 232 | String? text, 233 | Mode? language, 234 | this.patternMap, 235 | this.stringMap, 236 | this.params = const EditorParams(), 237 | this.modifiers = const [ 238 | IndentModifier(), 239 | CloseBlockModifier(), 240 | TabModifier(), 241 | ], 242 | }) 243 | ``` 244 | 245 | ## Limitations 246 | 247 | - Autocomplete disabling on android [doesn't work yet](https://github.com/flutter/flutter/issues/71679) 248 | 249 | ## Notes 250 | 251 | A [breaking change](https://flutter.dev/docs/release/breaking-changes/buildtextspan-buildcontext) to the `TextEditingController` was introduced in flutter beta, dev & master channels. The branch [beta](https://github.com/BertrandBev/code_field/tree/beta) should comply with those changes. 252 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | ## This file is copied from 2 | ## https://raw.githubusercontent.com/mockturtl/tidy/master/lib/analysis_options.2.17.0.yaml 3 | ## Differences are then marked with '##'. 4 | 5 | analyzer: 6 | errors: 7 | missing_required_param: error 8 | 9 | linter: 10 | rules: 11 | - always_declare_return_types 12 | # - always_put_control_body_on_new_line # conflicts: curly_braces_in_flow_control_structures 13 | # - always_put_required_named_parameters_first # conflicts: use_key_in_widget_constructors, sort_child_properties_last 14 | - always_require_non_null_named_parameters 15 | # - always_specify_types # conflicts: omit_local_variable_types 16 | # - always_use_package_imports # conflicts: prefer_relative_imports 17 | - annotate_overrides 18 | - avoid_annotating_with_dynamic 19 | - avoid_bool_literals_in_conditional_expressions 20 | - avoid_catches_without_on_clauses 21 | - avoid_catching_errors 22 | - avoid_classes_with_only_static_members 23 | - avoid_double_and_int_checks 24 | - avoid_dynamic_calls 25 | - avoid_empty_else 26 | - avoid_equals_and_hash_code_on_mutable_classes 27 | # - avoid_escaping_inner_quotes # note: escape characters are fine 28 | - avoid_field_initializers_in_const_classes 29 | - avoid_final_parameters # conflicts: prefer_final_parameters 30 | - avoid_function_literals_in_foreach_calls 31 | - avoid_implementing_value_types 32 | - avoid_init_to_null 33 | - avoid_js_rounded_ints 34 | - avoid_multiple_declarations_per_line 35 | - avoid_null_checks_in_equality_operators 36 | # - avoid_positional_boolean_parameters # note: one is fine. Do use named params otherwise. 37 | - avoid_print 38 | - avoid_private_typedef_functions 39 | - avoid_redundant_argument_values 40 | - avoid_relative_lib_imports 41 | - avoid_renaming_method_parameters 42 | - avoid_return_types_on_setters 43 | - avoid_returning_null 44 | - avoid_returning_null_for_future 45 | - avoid_returning_null_for_void 46 | - avoid_returning_this 47 | - avoid_setters_without_getters 48 | - avoid_shadowing_type_parameters 49 | - avoid_single_cascade_in_expression_statements 50 | - avoid_slow_async_io 51 | - avoid_type_to_string 52 | - avoid_types_as_parameter_names 53 | ## - avoid_types_on_closure_parameters 54 | - avoid_unnecessary_containers 55 | - avoid_unused_constructor_parameters 56 | - avoid_void_async 57 | - avoid_web_libraries_in_flutter 58 | - await_only_futures 59 | - camel_case_extensions 60 | - camel_case_types 61 | - cancel_subscriptions 62 | - cascade_invocations 63 | - cast_nullable_to_non_nullable 64 | - close_sinks 65 | - comment_references 66 | - conditional_uri_does_not_exist 67 | - constant_identifier_names 68 | - control_flow_in_finally 69 | - curly_braces_in_flow_control_structures 70 | - depend_on_referenced_packages 71 | - deprecated_consistency 72 | # - diagnostic_describe_all_properties # note: for Flutter 73 | - directives_ordering 74 | - do_not_use_environment 75 | - empty_catches 76 | - empty_constructor_bodies 77 | - empty_statements 78 | - eol_at_end_of_file 79 | - exhaustive_cases 80 | - file_names 81 | ## - flutter_style_todos 82 | - hash_and_equals 83 | - implementation_imports 84 | - invariant_booleans 85 | - iterable_contains_unrelated_type 86 | - join_return_with_assignment 87 | # - leading_newlines_in_multiline_strings # note: optional 88 | - library_names 89 | - library_prefixes 90 | - library_private_types_in_public_api 91 | # - lines_longer_than_80_chars # note: false positive with long doc comments 92 | - list_remove_unrelated_type 93 | - literal_only_boolean_expressions 94 | - missing_whitespace_between_adjacent_strings 95 | - no_adjacent_strings_in_list 96 | - no_default_cases 97 | - no_duplicate_case_values 98 | - no_leading_underscores_for_library_prefixes 99 | - no_leading_underscores_for_local_identifiers 100 | - no_logic_in_create_state 101 | - no_runtimeType_toString 102 | - non_constant_identifier_names 103 | - noop_primitive_operations 104 | - null_check_on_nullable_type_parameter 105 | - null_closures 106 | ## - omit_local_variable_types 107 | - one_member_abstracts 108 | - only_throw_errors 109 | - overridden_fields 110 | - package_api_docs 111 | - package_names 112 | - package_prefixed_library_names 113 | ## - parameter_assignments 114 | - prefer_adjacent_string_concatenation 115 | - prefer_asserts_in_initializer_lists 116 | - prefer_asserts_with_message 117 | - prefer_collection_literals 118 | - prefer_conditional_assignment 119 | - prefer_const_constructors 120 | - prefer_const_constructors_in_immutables 121 | - prefer_const_declarations 122 | - prefer_const_literals_to_create_immutables 123 | - prefer_constructors_over_static_methods 124 | - prefer_contains 125 | # - prefer_double_quotes # conflicts: prefer_single_quotes 126 | - prefer_equal_for_default_values 127 | ## - prefer_expression_function_bodies 128 | - prefer_final_fields 129 | # - prefer_final_in_for_each # conflicts: unnecessary_final 130 | # - prefer_final_locals # conflicts: unnecessary_final 131 | # - prefer_final_parameters # conflicts: unnecessary_final, avoid_final_parameters 132 | - prefer_for_elements_to_map_fromIterable 133 | - prefer_foreach 134 | - prefer_function_declarations_over_variables 135 | - prefer_generic_function_type_aliases 136 | ## - prefer_if_elements_to_conditional_expressions 137 | - prefer_if_null_operators 138 | - prefer_initializing_formals 139 | - prefer_inlined_adds 140 | - prefer_int_literals 141 | - prefer_interpolation_to_compose_strings 142 | - prefer_is_empty 143 | - prefer_is_not_empty 144 | - prefer_is_not_operator 145 | - prefer_iterable_whereType 146 | - prefer_mixin 147 | - prefer_null_aware_method_calls 148 | - prefer_null_aware_operators 149 | - prefer_relative_imports 150 | - prefer_single_quotes 151 | - prefer_spread_collections 152 | - prefer_typing_uninitialized_variables 153 | - prefer_void_to_null 154 | - provide_deprecation_message 155 | # - public_member_api_docs # note: consider overriding as API stabilizes 156 | - recursive_getters 157 | # - require_trailing_commas # note: let `dart format` wrap closing brackets 158 | - secure_pubspec_urls 159 | - sized_box_for_whitespace 160 | - sized_box_shrink_expand 161 | - slash_for_doc_comments 162 | - sort_child_properties_last 163 | # - sort_constructors_first # conflicts with auto-sorting all members, per [Fuschia style guide](https://fuchsia.dev/fuchsia-src/development/languages/dart/style#do_order_members_using_the_dart_analyzer) 164 | # - sort_pub_dependencies # conflicts with grouping local packages separately. Do keep groups sorted. 165 | - sort_unnamed_constructors_first 166 | - test_types_in_equals 167 | - throw_in_finally 168 | - tighten_type_of_initializing_formals 169 | - type_annotate_public_apis # note: use Object not to conflict with avoid_annotating_with_dynamic 170 | - type_init_formals 171 | - unawaited_futures 172 | - unnecessary_await_in_return 173 | - unnecessary_brace_in_string_interps 174 | - unnecessary_const 175 | - unnecessary_constructor_name 176 | ## - unnecessary_final 177 | - unnecessary_getters_setters 178 | - unnecessary_lambdas 179 | - unnecessary_late 180 | - unnecessary_new 181 | - unnecessary_null_aware_assignments 182 | - unnecessary_null_checks 183 | - unnecessary_null_in_if_null_operators 184 | - unnecessary_nullable_for_final_variable_declarations 185 | - unnecessary_overrides 186 | - unnecessary_parenthesis 187 | - unnecessary_raw_strings 188 | - unnecessary_statements 189 | - unnecessary_string_escapes 190 | - unnecessary_string_interpolations 191 | - unnecessary_this 192 | - unrelated_type_equality_checks 193 | - unsafe_html 194 | - use_build_context_synchronously 195 | - use_colored_box 196 | - use_decorated_box 197 | - use_enums 198 | - use_full_hex_values_for_flutter_colors 199 | - use_function_type_syntax_for_parameters 200 | - use_if_null_to_convert_nulls_to_bools 201 | - use_is_even_rather_than_modulo 202 | # - use_key_in_widget_constructors # note: consider enabling for published libraries 203 | - use_late_for_private_fields_and_variables 204 | - use_named_constants 205 | - use_raw_strings 206 | - use_rethrow_when_possible 207 | - use_setters_to_change_properties 208 | - use_string_buffers 209 | - use_super_parameters 210 | - use_test_throws_matchers 211 | - use_to_and_as_if_applicable 212 | - valid_regexps 213 | - void_checks 214 | -------------------------------------------------------------------------------- /example/lib/themes.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter_highlight/themes/a11y-dark.dart'; 2 | import 'package:flutter_highlight/themes/a11y-light.dart'; 3 | import 'package:flutter_highlight/themes/agate.dart'; 4 | import 'package:flutter_highlight/themes/an-old-hope.dart'; 5 | import 'package:flutter_highlight/themes/androidstudio.dart'; 6 | import 'package:flutter_highlight/themes/arduino-light.dart'; 7 | import 'package:flutter_highlight/themes/arta.dart'; 8 | import 'package:flutter_highlight/themes/ascetic.dart'; 9 | import 'package:flutter_highlight/themes/atelier-cave-dark.dart'; 10 | import 'package:flutter_highlight/themes/atelier-cave-light.dart'; 11 | import 'package:flutter_highlight/themes/atelier-dune-dark.dart'; 12 | import 'package:flutter_highlight/themes/atelier-dune-light.dart'; 13 | import 'package:flutter_highlight/themes/atelier-estuary-dark.dart'; 14 | import 'package:flutter_highlight/themes/atelier-estuary-light.dart'; 15 | import 'package:flutter_highlight/themes/atelier-forest-dark.dart'; 16 | import 'package:flutter_highlight/themes/atelier-forest-light.dart'; 17 | import 'package:flutter_highlight/themes/atelier-heath-dark.dart'; 18 | import 'package:flutter_highlight/themes/atelier-heath-light.dart'; 19 | import 'package:flutter_highlight/themes/atelier-lakeside-dark.dart'; 20 | import 'package:flutter_highlight/themes/atelier-lakeside-light.dart'; 21 | import 'package:flutter_highlight/themes/atelier-plateau-dark.dart'; 22 | import 'package:flutter_highlight/themes/atelier-plateau-light.dart'; 23 | import 'package:flutter_highlight/themes/atelier-savanna-dark.dart'; 24 | import 'package:flutter_highlight/themes/atelier-savanna-light.dart'; 25 | import 'package:flutter_highlight/themes/atelier-seaside-dark.dart'; 26 | import 'package:flutter_highlight/themes/atelier-seaside-light.dart'; 27 | import 'package:flutter_highlight/themes/atelier-sulphurpool-dark.dart'; 28 | import 'package:flutter_highlight/themes/atelier-sulphurpool-light.dart'; 29 | import 'package:flutter_highlight/themes/atom-one-dark-reasonable.dart'; 30 | import 'package:flutter_highlight/themes/atom-one-dark.dart'; 31 | import 'package:flutter_highlight/themes/atom-one-light.dart'; 32 | import 'package:flutter_highlight/themes/brown-paper.dart'; 33 | import 'package:flutter_highlight/themes/codepen-embed.dart'; 34 | import 'package:flutter_highlight/themes/color-brewer.dart'; 35 | import 'package:flutter_highlight/themes/darcula.dart'; 36 | import 'package:flutter_highlight/themes/dark.dart'; 37 | import 'package:flutter_highlight/themes/default.dart'; 38 | import 'package:flutter_highlight/themes/docco.dart'; 39 | import 'package:flutter_highlight/themes/dracula.dart'; 40 | import 'package:flutter_highlight/themes/far.dart'; 41 | import 'package:flutter_highlight/themes/foundation.dart'; 42 | import 'package:flutter_highlight/themes/github-gist.dart'; 43 | import 'package:flutter_highlight/themes/github.dart'; 44 | import 'package:flutter_highlight/themes/gml.dart'; 45 | import 'package:flutter_highlight/themes/googlecode.dart'; 46 | import 'package:flutter_highlight/themes/gradient-dark.dart'; 47 | import 'package:flutter_highlight/themes/grayscale.dart'; 48 | import 'package:flutter_highlight/themes/gruvbox-dark.dart'; 49 | import 'package:flutter_highlight/themes/gruvbox-light.dart'; 50 | import 'package:flutter_highlight/themes/hopscotch.dart'; 51 | import 'package:flutter_highlight/themes/hybrid.dart'; 52 | import 'package:flutter_highlight/themes/idea.dart'; 53 | import 'package:flutter_highlight/themes/ir-black.dart'; 54 | import 'package:flutter_highlight/themes/isbl-editor-dark.dart'; 55 | import 'package:flutter_highlight/themes/isbl-editor-light.dart'; 56 | import 'package:flutter_highlight/themes/kimbie.dark.dart'; 57 | import 'package:flutter_highlight/themes/kimbie.light.dart'; 58 | import 'package:flutter_highlight/themes/lightfair.dart'; 59 | import 'package:flutter_highlight/themes/magula.dart'; 60 | import 'package:flutter_highlight/themes/mono-blue.dart'; 61 | import 'package:flutter_highlight/themes/monokai-sublime.dart'; 62 | import 'package:flutter_highlight/themes/monokai.dart'; 63 | import 'package:flutter_highlight/themes/night-owl.dart'; 64 | import 'package:flutter_highlight/themes/nord.dart'; 65 | import 'package:flutter_highlight/themes/obsidian.dart'; 66 | import 'package:flutter_highlight/themes/ocean.dart'; 67 | import 'package:flutter_highlight/themes/paraiso-dark.dart'; 68 | import 'package:flutter_highlight/themes/paraiso-light.dart'; 69 | import 'package:flutter_highlight/themes/pojoaque.dart'; 70 | import 'package:flutter_highlight/themes/purebasic.dart'; 71 | import 'package:flutter_highlight/themes/qtcreator_dark.dart'; 72 | import 'package:flutter_highlight/themes/qtcreator_light.dart'; 73 | import 'package:flutter_highlight/themes/railscasts.dart'; 74 | import 'package:flutter_highlight/themes/rainbow.dart'; 75 | import 'package:flutter_highlight/themes/routeros.dart'; 76 | import 'package:flutter_highlight/themes/school-book.dart'; 77 | import 'package:flutter_highlight/themes/shades-of-purple.dart'; 78 | import 'package:flutter_highlight/themes/solarized-dark.dart'; 79 | import 'package:flutter_highlight/themes/solarized-light.dart'; 80 | import 'package:flutter_highlight/themes/sunburst.dart'; 81 | import 'package:flutter_highlight/themes/tomorrow-night-blue.dart'; 82 | import 'package:flutter_highlight/themes/tomorrow-night-bright.dart'; 83 | import 'package:flutter_highlight/themes/tomorrow-night-eighties.dart'; 84 | import 'package:flutter_highlight/themes/tomorrow-night.dart'; 85 | import 'package:flutter_highlight/themes/tomorrow.dart'; 86 | import 'package:flutter_highlight/themes/vs.dart'; 87 | import 'package:flutter_highlight/themes/vs2015.dart'; 88 | import 'package:flutter_highlight/themes/xcode.dart'; 89 | import 'package:flutter_highlight/themes/xt256.dart'; 90 | import 'package:flutter_highlight/themes/zenburn.dart'; 91 | 92 | const THEMES = { 93 | 'a11y-dark': a11yDarkTheme, 94 | 'a11y-light': a11yLightTheme, 95 | 'agate': agateTheme, 96 | 'an-old-hope': anOldHopeTheme, 97 | 'androidstudio': androidstudioTheme, 98 | 'arduino-light': arduinoLightTheme, 99 | 'arta': artaTheme, 100 | 'ascetic': asceticTheme, 101 | 'atelier-cave-dark': atelierCaveDarkTheme, 102 | 'atelier-cave-light': atelierCaveLightTheme, 103 | 'atelier-dune-dark': atelierDuneDarkTheme, 104 | 'atelier-dune-light': atelierDuneLightTheme, 105 | 'atelier-estuary-dark': atelierEstuaryDarkTheme, 106 | 'atelier-estuary-light': atelierEstuaryLightTheme, 107 | 'atelier-forest-dark': atelierForestDarkTheme, 108 | 'atelier-forest-light': atelierForestLightTheme, 109 | 'atelier-heath-dark': atelierHeathDarkTheme, 110 | 'atelier-heath-light': atelierHeathLightTheme, 111 | 'atelier-lakeside-dark': atelierLakesideDarkTheme, 112 | 'atelier-lakeside-light': atelierLakesideLightTheme, 113 | 'atelier-plateau-dark': atelierPlateauDarkTheme, 114 | 'atelier-plateau-light': atelierPlateauLightTheme, 115 | 'atelier-savanna-dark': atelierSavannaDarkTheme, 116 | 'atelier-savanna-light': atelierSavannaLightTheme, 117 | 'atelier-seaside-dark': atelierSeasideDarkTheme, 118 | 'atelier-seaside-light': atelierSeasideLightTheme, 119 | 'atelier-sulphurpool-dark': atelierSulphurpoolDarkTheme, 120 | 'atelier-sulphurpool-light': atelierSulphurpoolLightTheme, 121 | 'atom-one-dark-reasonable': atomOneDarkReasonableTheme, 122 | 'atom-one-dark': atomOneDarkTheme, 123 | 'atom-one-light': atomOneLightTheme, 124 | 'brown-paper': brownPaperTheme, 125 | 'codepen-embed': codepenEmbedTheme, 126 | 'color-brewer': colorBrewerTheme, 127 | 'darcula': darculaTheme, 128 | 'dark': darkTheme, 129 | 'default': defaultTheme, 130 | 'docco': doccoTheme, 131 | 'dracula': draculaTheme, 132 | 'far': farTheme, 133 | 'foundation': foundationTheme, 134 | 'github-gist': githubGistTheme, 135 | 'github': githubTheme, 136 | 'gml': gmlTheme, 137 | 'googlecode': googlecodeTheme, 138 | 'gradient-dark': gradientDarkTheme, 139 | 'grayscale': grayscaleTheme, 140 | 'gruvbox-dark': gruvboxDarkTheme, 141 | 'gruvbox-light': gruvboxLightTheme, 142 | 'hopscotch': hopscotchTheme, 143 | 'hybrid': hybridTheme, 144 | 'idea': ideaTheme, 145 | 'ir-black': irBlackTheme, 146 | 'isbl-editor-dark': isblEditorDarkTheme, 147 | 'isbl-editor-light': isblEditorLightTheme, 148 | 'kimbie.dark': kimbieDarkTheme, 149 | 'kimbie.light': kimbieLightTheme, 150 | 'lightfair': lightfairTheme, 151 | 'magula': magulaTheme, 152 | 'mono-blue': monoBlueTheme, 153 | 'monokai-sublime': monokaiSublimeTheme, 154 | 'monokai': monokaiTheme, 155 | 'night-owl': nightOwlTheme, 156 | 'nord': nordTheme, 157 | 'obsidian': obsidianTheme, 158 | 'ocean': oceanTheme, 159 | 'paraiso-dark': paraisoDarkTheme, 160 | 'paraiso-light': paraisoLightTheme, 161 | 'pojoaque': pojoaqueTheme, 162 | 'purebasic': purebasicTheme, 163 | 'qtcreator_dark': qtcreatorDarkTheme, 164 | 'qtcreator_light': qtcreatorLightTheme, 165 | 'railscasts': railscastsTheme, 166 | 'rainbow': rainbowTheme, 167 | 'routeros': routerosTheme, 168 | 'school-book': schoolBookTheme, 169 | 'shades-of-purple': shadesOfPurpleTheme, 170 | 'solarized-dark': solarizedDarkTheme, 171 | 'solarized-light': solarizedLightTheme, 172 | 'sunburst': sunburstTheme, 173 | 'tomorrow-night-blue': tomorrowNightBlueTheme, 174 | 'tomorrow-night-bright': tomorrowNightBrightTheme, 175 | 'tomorrow-night-eighties': tomorrowNightEightiesTheme, 176 | 'tomorrow-night': tomorrowNightTheme, 177 | 'tomorrow': tomorrowTheme, 178 | 'vs': vsTheme, 179 | 'vs2015': vs2015Theme, 180 | 'xcode': xcodeTheme, 181 | 'xt256': xt256Theme, 182 | 'zenburn': zenburnTheme, 183 | }; 184 | -------------------------------------------------------------------------------- /lib/src/code_field/code_controller.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: public_member_api_docs, sort_constructors_first 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/services.dart'; 4 | import 'package:highlight/highlight_core.dart'; 5 | 6 | import '../code_modifiers/close_block_code_modifier.dart'; 7 | import '../code_modifiers/code_modifier.dart'; 8 | import '../code_modifiers/indent_code_modifier.dart'; 9 | import '../code_modifiers/tab_code_modifier.dart'; 10 | import '../code_theme/code_theme.dart'; 11 | import '../code_theme/code_theme_data.dart'; 12 | import 'code_auto_complete.dart'; 13 | import 'editor_params.dart'; 14 | 15 | class CodeController extends TextEditingController { 16 | Mode? _language; 17 | CodeAutoComplete? autoComplete; 18 | 19 | /// A highlight language to parse the text with 20 | Mode? get language => _language; 21 | 22 | set language(Mode? language) { 23 | if (language == _language) { 24 | return; 25 | } 26 | 27 | if (language != null) { 28 | _languageId = language.hashCode.toString(); 29 | highlight.registerLanguage(_languageId, language); 30 | } 31 | 32 | _language = language; 33 | notifyListeners(); 34 | } 35 | 36 | /// A map of specific regexes to style 37 | final Map? patternMap; 38 | 39 | /// A map of specific keywords to style 40 | final Map? stringMap; 41 | 42 | /// Common editor params such as the size of a tab in spaces 43 | /// 44 | /// Will be exposed to all [modifiers] 45 | final EditorParams params; 46 | 47 | /// A list of code modifiers to dynamically update the code upon certain keystrokes 48 | final List modifiers; 49 | 50 | /* Computed members */ 51 | String _languageId = ''; 52 | final _modifierMap = {}; 53 | final _styleList = []; 54 | RegExp? _styleRegExp; 55 | 56 | String get languageId => _languageId; 57 | 58 | CodeController({ 59 | String? text, 60 | Mode? language, 61 | // @Deprecated('Use CodeTheme widget to provide theme to CodeField.') 62 | // Map? theme, 63 | this.patternMap, 64 | this.stringMap, 65 | this.params = const EditorParams(), 66 | this.modifiers = const [ 67 | IndentModifier(), 68 | CloseBlockModifier(), 69 | TabModifier(), 70 | ], 71 | }) : super(text: text) { 72 | this.language = language; 73 | 74 | // Create modifier map 75 | for (final el in modifiers) { 76 | _modifierMap[el.char] = el; 77 | } 78 | 79 | // Build styleRegExp 80 | final patternList = []; 81 | if (stringMap != null) { 82 | patternList.addAll(stringMap!.keys.map((e) => r'(\b' + e + r'\b)')); 83 | _styleList.addAll(stringMap!.values); 84 | } 85 | if (patternMap != null) { 86 | patternList.addAll(patternMap!.keys.map((e) => '($e)')); 87 | _styleList.addAll(patternMap!.values); 88 | } 89 | _styleRegExp = RegExp(patternList.join('|'), multiLine: true, unicode: true); 90 | } 91 | 92 | /// Sets a specific cursor position in the text 93 | void setCursor(int offset) { 94 | selection = TextSelection.collapsed(offset: offset); 95 | } 96 | 97 | /// Replaces the current [selection] by [str] 98 | void insertStr(String str) { 99 | final sel = selection; 100 | text = text.replaceRange(selection.start, selection.end, str); 101 | final len = str.length; 102 | 103 | selection = sel.copyWith( 104 | baseOffset: sel.start + len, 105 | extentOffset: sel.start + len, 106 | ); 107 | } 108 | 109 | /// Remove the char just before the cursor or the selection 110 | void removeChar() { 111 | if (selection.start < 1) { 112 | return; 113 | } 114 | 115 | final sel = selection; 116 | text = text.replaceRange(selection.start - 1, selection.start, ''); 117 | 118 | selection = sel.copyWith( 119 | baseOffset: sel.start - 1, 120 | extentOffset: sel.start - 1, 121 | ); 122 | } 123 | 124 | /// Remove the selected text 125 | void removeSelection() { 126 | final sel = selection; 127 | text = text.replaceRange(selection.start, selection.end, ''); 128 | 129 | selection = sel.copyWith( 130 | baseOffset: sel.start, 131 | extentOffset: sel.start, 132 | ); 133 | } 134 | 135 | /// Remove the selection or last char if the selection is empty 136 | void backspace() { 137 | if (selection.start < selection.end) { 138 | removeSelection(); 139 | } else { 140 | removeChar(); 141 | } 142 | } 143 | 144 | KeyEventResult onKey(RawKeyEvent event) { 145 | if (event.isKeyPressed(LogicalKeyboardKey.tab)) { 146 | text = text.replaceRange(selection.start, selection.end, '\t'); 147 | return KeyEventResult.handled; 148 | } 149 | 150 | if (autoComplete?.isShowing ?? false) { 151 | if (event.isKeyPressed(LogicalKeyboardKey.arrowDown)) { 152 | autoComplete!.current = 153 | (autoComplete!.current + 1) % autoComplete!.options.length; 154 | autoComplete!.panelSetState?.call(() {}); 155 | return KeyEventResult.handled; 156 | } 157 | if (event.isKeyPressed(LogicalKeyboardKey.arrowUp)) { 158 | autoComplete!.current = 159 | (autoComplete!.current - 1) % autoComplete!.options.length; 160 | autoComplete!.panelSetState?.call(() {}); 161 | return KeyEventResult.handled; 162 | } 163 | if (event.isKeyPressed(LogicalKeyboardKey.enter)) { 164 | autoComplete!.writeCurrent(); 165 | return KeyEventResult.handled; 166 | } 167 | if (event.isKeyPressed(LogicalKeyboardKey.escape)) { 168 | autoComplete!.hide(); 169 | return KeyEventResult.handled; 170 | } 171 | } 172 | 173 | return KeyEventResult.ignored; 174 | } 175 | 176 | int? _insertedLoc(String a, String b) { 177 | final sel = selection; 178 | 179 | if (a.length + 1 != b.length || sel.start != sel.end || sel.start < 0) { 180 | return null; 181 | } 182 | 183 | return sel.start; 184 | } 185 | 186 | @override 187 | set value(TextEditingValue newValue) { 188 | final loc = _insertedLoc(text, newValue.text); 189 | 190 | if (loc != null) { 191 | final char = newValue.text[loc]; 192 | final modifier = _modifierMap[char]; 193 | final val = modifier?.updateString(super.text, selection, params); 194 | 195 | if (val != null) { 196 | // Update newValue 197 | newValue = newValue.copyWith( 198 | text: val.text, 199 | selection: val.selection, 200 | ); 201 | } 202 | } 203 | super.value = newValue; 204 | } 205 | 206 | TextSpan _processPatterns(String text, TextStyle? style) { 207 | final children = []; 208 | 209 | text.splitMapJoin( 210 | _styleRegExp!, 211 | onMatch: (Match m) { 212 | if (_styleList.isEmpty) { 213 | return ''; 214 | } 215 | 216 | int idx; 217 | for (idx = 1; 218 | idx < m.groupCount && 219 | idx <= _styleList.length && 220 | m.group(idx) == null; 221 | idx++) {} 222 | 223 | children.add(TextSpan( 224 | text: m[0], 225 | style: _styleList[idx - 1], 226 | )); 227 | return ''; 228 | }, 229 | onNonMatch: (String span) { 230 | children.add(TextSpan(text: span, style: style)); 231 | return ''; 232 | }, 233 | ); 234 | 235 | return TextSpan(style: style, children: children); 236 | } 237 | 238 | TextSpan _processLanguage( 239 | String text, 240 | CodeThemeData? widgetTheme, 241 | TextStyle? style, 242 | ) { 243 | final result = highlight.parse(text, language: _languageId); 244 | 245 | final nodes = result.nodes; 246 | 247 | final children = []; 248 | var currentSpans = children; 249 | final stack = >[]; 250 | 251 | void traverse(Node node) { 252 | var val = node.value; 253 | final nodeChildren = node.children; 254 | final nodeStyle = widgetTheme?.styles[node.className]; 255 | 256 | if (val != null) { 257 | var child = TextSpan(text: val, style: nodeStyle); 258 | 259 | if (_styleRegExp != null) { 260 | child = _processPatterns(val, nodeStyle); 261 | } 262 | 263 | currentSpans.add(child); 264 | } else if (nodeChildren != null) { 265 | List tmp = []; 266 | 267 | currentSpans.add(TextSpan( 268 | children: tmp, 269 | style: nodeStyle, 270 | )); 271 | 272 | stack.add(currentSpans); 273 | currentSpans = tmp; 274 | 275 | for (final n in nodeChildren) { 276 | traverse(n); 277 | if (n == nodeChildren.last) { 278 | currentSpans = stack.isEmpty ? children : stack.removeLast(); 279 | } 280 | } 281 | } 282 | } 283 | 284 | if (nodes != null) { 285 | nodes.forEach(traverse); 286 | } 287 | 288 | return TextSpan(style: style, children: children); 289 | } 290 | 291 | @override 292 | TextSpan buildTextSpan({ 293 | required BuildContext context, 294 | TextStyle? style, 295 | bool? withComposing, 296 | }) { 297 | // Return parsing 298 | if (_language != null) { 299 | return _processLanguage(text, CodeTheme.of(context), style); 300 | } 301 | if (_styleRegExp != null) { 302 | return _processPatterns(text, style); 303 | } 304 | return TextSpan(text: text, style: style); 305 | } 306 | 307 | CodeController copyWith({ 308 | Mode? _language, 309 | CodeAutoComplete? autoComplete, 310 | Map? patternMap, 311 | Map? stringMap, 312 | EditorParams? params, 313 | List? modifiers, 314 | String? _languageId, 315 | RegExp? _styleRegExp, 316 | }) { 317 | return CodeController( 318 | _language: _language ?? this._language, 319 | autoComplete: autoComplete ?? this.autoComplete, 320 | patternMap: patternMap ?? this.patternMap, 321 | stringMap: stringMap ?? this.stringMap, 322 | params: params ?? this.params, 323 | modifiers: modifiers ?? this.modifiers, 324 | _languageId: _languageId ?? this._languageId, 325 | _styleRegExp: _styleRegExp ?? this._styleRegExp, 326 | ); 327 | } 328 | } 329 | -------------------------------------------------------------------------------- /lib/src/code_field/code_field.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:io'; 3 | import 'dart:math'; 4 | 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:flutter/gestures.dart'; 7 | import 'package:flutter/material.dart'; 8 | import 'package:linked_scroll_controller/linked_scroll_controller.dart'; 9 | 10 | import '../code_theme/code_theme.dart'; 11 | import '../line_numbers/line_number_controller.dart'; 12 | import '../line_numbers/line_number_style.dart'; 13 | import 'code_auto_complete.dart'; 14 | import 'code_controller.dart'; 15 | 16 | class CodeField extends StatefulWidget { 17 | /// {@macro flutter.widgets.textField.smartQuotesType} 18 | final SmartQuotesType? smartQuotesType; 19 | 20 | /// {@macro flutter.widgets.textField.smartDashesType} 21 | final SmartDashesType? smartDashesType; 22 | 23 | /// {@macro flutter.widgets.textField.keyboardType} 24 | final TextInputType? keyboardType; 25 | 26 | /// {@macro flutter.widgets.textField.minLines} 27 | final int? minLines; 28 | 29 | /// {@macro flutter.widgets.textField.maxLInes} 30 | final int? maxLines; 31 | 32 | /// {@macro flutter.widgets.textField.expands} 33 | final bool expands; 34 | 35 | /// Whether overflowing lines should wrap around or make the field scrollable horizontally 36 | final bool wrap; 37 | 38 | /// A CodeController instance to apply language highlight, themeing and modifiers 39 | final CodeController controller; 40 | 41 | /// A LineNumberStyle instance to tweak the line number column styling 42 | final LineNumberStyle lineNumberStyle; 43 | 44 | /// {@macro flutter.widgets.textField.cursorColor} 45 | final Color? cursorColor; 46 | 47 | /// {@macro flutter.widgets.textField.textStyle} 48 | final TextStyle? textStyle; 49 | 50 | /// A way to replace specific line numbers by a custom TextSpan 51 | final TextSpan Function(int, TextStyle?)? lineNumberBuilder; 52 | 53 | /// {@macro flutter.widgets.textField.enabled} 54 | final bool? enabled; 55 | 56 | /// {@macro flutter.widgets.editableText.onChanged} 57 | final void Function(String)? onChanged; 58 | 59 | /// {@macro flutter.widgets.editableText.readOnly} 60 | final bool readOnly; 61 | 62 | /// {@macro flutter.widgets.textField.isDense} 63 | final bool isDense; 64 | 65 | /// {@macro flutter.widgets.textField.selectionControls} 66 | final TextSelectionControls? selectionControls; 67 | 68 | /// {@macro flutter.widgets.textField.textInputAction} 69 | final TextInputAction? textInputAction; 70 | 71 | /// {@macro flutter.services.TextInputConfiguration.enableSuggestions} 72 | final bool enableSuggestions; 73 | 74 | final Color? background; 75 | final EdgeInsets padding; 76 | final Decoration? decoration; 77 | final TextSelectionThemeData? textSelectionTheme; 78 | final FocusNode? focusNode; 79 | final void Function()? onTap; 80 | final bool lineNumbers; 81 | final bool horizontalScroll; 82 | final String? hintText; 83 | final TextStyle? hintStyle; 84 | final CodeAutoComplete? autoComplete; 85 | 86 | const CodeField({ 87 | Key? key, 88 | required this.controller, 89 | this.minLines, 90 | this.maxLines, 91 | this.expands = false, 92 | this.wrap = false, 93 | this.background, 94 | this.decoration, 95 | this.textStyle, 96 | this.padding = EdgeInsets.zero, 97 | this.lineNumberStyle = const LineNumberStyle(), 98 | this.enabled, 99 | this.onTap, 100 | this.readOnly = false, 101 | this.cursorColor, 102 | this.textSelectionTheme, 103 | this.lineNumberBuilder, 104 | this.focusNode, 105 | this.onChanged, 106 | this.isDense = false, 107 | this.smartQuotesType, 108 | this.smartDashesType, 109 | this.keyboardType, 110 | this.lineNumbers = true, 111 | this.horizontalScroll = true, 112 | this.selectionControls, 113 | this.hintText, 114 | this.hintStyle, 115 | this.autoComplete, 116 | this.textInputAction, 117 | this.enableSuggestions = false, 118 | }) : super(key: key); 119 | 120 | @override 121 | State createState() => _CodeFieldState(); 122 | } 123 | 124 | class _CodeFieldState extends State { 125 | // Add a controller 126 | LinkedScrollControllerGroup? _controllers; 127 | ScrollController? _numberScroll; 128 | ScrollController? _codeScroll; 129 | LineNumberController? _numberController; 130 | 131 | StreamSubscription? _keyboardVisibilitySubscription; 132 | FocusNode? _focusNode; 133 | String? lines; 134 | String longestLine = ''; 135 | 136 | @override 137 | void initState() { 138 | super.initState(); 139 | _controllers = LinkedScrollControllerGroup(); 140 | _numberScroll = _controllers?.addAndGet(); 141 | _codeScroll = _controllers?.addAndGet(); 142 | _numberController = LineNumberController(widget.lineNumberBuilder); 143 | widget.controller.addListener(_onTextChanged); 144 | _focusNode = widget.focusNode ?? FocusNode(); 145 | _focusNode!.onKey = _onKey; 146 | _focusNode!.attach(context, onKey: _onKey); 147 | 148 | WidgetsBinding.instance.addPostFrameCallback((_) { 149 | createAutoComplate(); 150 | }); 151 | 152 | _onTextChanged(); 153 | } 154 | 155 | void createAutoComplate() { 156 | widget.autoComplete?.show(context, widget, _focusNode!); 157 | widget.controller.autoComplete = widget.autoComplete; 158 | _codeScroll?.addListener(hideAutoComplete); 159 | } 160 | 161 | KeyEventResult _onKey(FocusNode node, RawKeyEvent event) { 162 | if (widget.readOnly) { 163 | return KeyEventResult.ignored; 164 | } 165 | 166 | return widget.controller.onKey(event); 167 | } 168 | 169 | @override 170 | void dispose() { 171 | widget.controller.removeListener(_onTextChanged); 172 | _numberScroll?.dispose(); 173 | _codeScroll?.dispose(); 174 | _numberController?.dispose(); 175 | _keyboardVisibilitySubscription?.cancel(); 176 | widget.autoComplete?.remove(); 177 | super.dispose(); 178 | } 179 | 180 | void rebuild() { 181 | setState(() {}); 182 | } 183 | 184 | void _onTextChanged() { 185 | // Rebuild line number 186 | final str = widget.controller.text.split('\n'); 187 | final buf = []; 188 | 189 | for (var k = 0; k < str.length; k++) { 190 | buf.add((k + 1).toString()); 191 | } 192 | 193 | _numberController?.text = buf.join('\n'); 194 | 195 | // Find longest line 196 | longestLine = ''; 197 | widget.controller.text.split('\n').forEach((line) { 198 | if (line.length > longestLine.length) longestLine = line; 199 | }); 200 | 201 | setState(() {}); 202 | } 203 | 204 | // Wrap the codeField in a horizontal scrollView 205 | Widget _wrapInScrollView( 206 | Widget codeField, 207 | TextStyle textStyle, 208 | double minWidth, 209 | ) { 210 | final leftPad = widget.lineNumberStyle.margin / 2; 211 | final intrinsic = IntrinsicWidth( 212 | child: Column( 213 | mainAxisSize: MainAxisSize.min, 214 | crossAxisAlignment: CrossAxisAlignment.stretch, 215 | children: [ 216 | ConstrainedBox( 217 | constraints: BoxConstraints( 218 | maxHeight: 0, 219 | minWidth: max(minWidth - leftPad, 0), 220 | ), 221 | child: Padding( 222 | padding: const EdgeInsets.only(right: 16), 223 | child: Text(longestLine, style: textStyle), 224 | ), // Add extra padding 225 | ), 226 | widget.expands ? Expanded(child: codeField) : codeField, 227 | ], 228 | ), 229 | ); 230 | 231 | return MediaQuery( 232 | // TODO: Temporary fix: https://github.com/flutter/flutter/issues/127017 233 | data: !kIsWeb && Platform.isIOS 234 | ? const MediaQueryData( 235 | gestureSettings: DeviceGestureSettings(touchSlop: 8), 236 | ) 237 | : MediaQuery.of(context), 238 | child: SingleChildScrollView( 239 | padding: EdgeInsets.only( 240 | left: leftPad, 241 | right: widget.padding.right, 242 | ), 243 | scrollDirection: Axis.horizontal, 244 | child: intrinsic, 245 | ), 246 | ); 247 | } 248 | 249 | void removeAutoComplete() { 250 | widget.autoComplete?.remove(); 251 | } 252 | 253 | void hideAutoComplete() { 254 | widget.autoComplete?.hide(); 255 | } 256 | 257 | @override 258 | Widget build(BuildContext context) { 259 | // Default color scheme 260 | const rootKey = 'root'; 261 | final defaultBg = Colors.grey.shade900; 262 | final defaultText = Colors.grey.shade200; 263 | 264 | final styles = CodeTheme.of(context)?.styles; 265 | Color? backgroundCol = 266 | widget.background ?? styles?[rootKey]?.backgroundColor ?? defaultBg; 267 | 268 | if (widget.decoration != null) { 269 | backgroundCol = null; 270 | } 271 | 272 | TextStyle textStyle = widget.textStyle ?? const TextStyle(); 273 | textStyle = textStyle.copyWith( 274 | color: textStyle.color ?? styles?[rootKey]?.color ?? defaultText, 275 | fontSize: textStyle.fontSize ?? 16.0, 276 | ); 277 | 278 | TextStyle numberTextStyle = 279 | widget.lineNumberStyle.textStyle ?? const TextStyle(); 280 | final numberColor = 281 | (styles?[rootKey]?.color ?? defaultText).withOpacity(0.7); 282 | 283 | // Copy important attributes 284 | numberTextStyle = numberTextStyle.copyWith( 285 | color: numberTextStyle.color ?? numberColor, 286 | fontSize: textStyle.fontSize, 287 | fontFamily: textStyle.fontFamily, 288 | ); 289 | 290 | final cursorColor = 291 | widget.cursorColor ?? styles?[rootKey]?.color ?? defaultText; 292 | 293 | TextField? lineNumberCol; 294 | Container? numberCol; 295 | 296 | if (widget.lineNumbers) { 297 | lineNumberCol = TextField( 298 | smartQuotesType: widget.smartQuotesType, 299 | smartDashesType: widget.smartDashesType, 300 | scrollPadding: widget.padding, 301 | style: numberTextStyle, 302 | controller: _numberController, 303 | enabled: false, 304 | minLines: widget.minLines, 305 | maxLines: widget.maxLines, 306 | selectionControls: widget.selectionControls, 307 | expands: widget.expands, 308 | scrollController: _numberScroll, 309 | decoration: InputDecoration( 310 | disabledBorder: InputBorder.none, 311 | isDense: widget.isDense, 312 | ), 313 | textAlign: widget.lineNumberStyle.textAlign, 314 | ); 315 | 316 | numberCol = Container( 317 | width: widget.lineNumberStyle.width, 318 | padding: EdgeInsets.only( 319 | left: widget.padding.left, 320 | right: widget.lineNumberStyle.margin / 2, 321 | ), 322 | color: widget.lineNumberStyle.background, 323 | child: lineNumberCol, 324 | ); 325 | } 326 | 327 | final codeField = TextField( 328 | keyboardType: widget.keyboardType, 329 | smartQuotesType: widget.smartQuotesType, 330 | smartDashesType: widget.smartDashesType, 331 | focusNode: _focusNode, 332 | onTap: () { 333 | widget.autoComplete?.hide(); 334 | widget.onTap?.call(); 335 | }, 336 | scrollPadding: widget.padding, 337 | style: textStyle, 338 | controller: widget.controller, 339 | minLines: widget.minLines, 340 | selectionControls: widget.selectionControls, 341 | maxLines: widget.maxLines, 342 | expands: widget.expands, 343 | scrollController: _codeScroll, 344 | decoration: InputDecoration( 345 | disabledBorder: InputBorder.none, 346 | border: InputBorder.none, 347 | focusedBorder: InputBorder.none, 348 | isDense: widget.isDense, 349 | hintText: widget.hintText, 350 | hintStyle: widget.hintStyle, 351 | ), 352 | onTapOutside: (e) { 353 | Future.delayed(const Duration(milliseconds: 300), hideAutoComplete); 354 | }, 355 | cursorColor: cursorColor, 356 | autocorrect: false, 357 | enableSuggestions: widget.enableSuggestions, 358 | enabled: widget.enabled, 359 | onChanged: (text) { 360 | widget.onChanged?.call(text); 361 | widget.autoComplete?.streamController.add(text); 362 | }, 363 | readOnly: widget.readOnly, 364 | textInputAction: widget.textInputAction, 365 | ); 366 | 367 | final codeCol = Theme( 368 | data: Theme.of(context).copyWith( 369 | textSelectionTheme: widget.textSelectionTheme, 370 | ), 371 | child: LayoutBuilder( 372 | builder: (BuildContext context, BoxConstraints constraints) { 373 | // Control horizontal scrolling 374 | return widget.wrap 375 | ? codeField 376 | : _wrapInScrollView(codeField, textStyle, constraints.maxWidth); 377 | }, 378 | ), 379 | ); 380 | 381 | return Container( 382 | decoration: widget.decoration, 383 | color: backgroundCol, 384 | padding: !widget.lineNumbers ? const EdgeInsets.only(left: 8) : null, 385 | child: Row( 386 | crossAxisAlignment: CrossAxisAlignment.start, 387 | children: [ 388 | if (widget.lineNumbers && numberCol != null) numberCol, 389 | Expanded(child: codeCol), 390 | ], 391 | ), 392 | ); 393 | } 394 | } 395 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1020; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | buildActionMask = 2147483647; 175 | files = ( 176 | ); 177 | inputPaths = ( 178 | ); 179 | name = "Thin Binary"; 180 | outputPaths = ( 181 | ); 182 | runOnlyForDeploymentPostprocessing = 0; 183 | shellPath = /bin/sh; 184 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 185 | }; 186 | 9740EEB61CF901F6004384FC /* Run Script */ = { 187 | isa = PBXShellScriptBuildPhase; 188 | buildActionMask = 2147483647; 189 | files = ( 190 | ); 191 | inputPaths = ( 192 | ); 193 | name = "Run Script"; 194 | outputPaths = ( 195 | ); 196 | runOnlyForDeploymentPostprocessing = 0; 197 | shellPath = /bin/sh; 198 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 199 | }; 200 | /* End PBXShellScriptBuildPhase section */ 201 | 202 | /* Begin PBXSourcesBuildPhase section */ 203 | 97C146EA1CF9000F007C117D /* Sources */ = { 204 | isa = PBXSourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 208 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 209 | ); 210 | runOnlyForDeploymentPostprocessing = 0; 211 | }; 212 | /* End PBXSourcesBuildPhase section */ 213 | 214 | /* Begin PBXVariantGroup section */ 215 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 216 | isa = PBXVariantGroup; 217 | children = ( 218 | 97C146FB1CF9000F007C117D /* Base */, 219 | ); 220 | name = Main.storyboard; 221 | sourceTree = ""; 222 | }; 223 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 224 | isa = PBXVariantGroup; 225 | children = ( 226 | 97C147001CF9000F007C117D /* Base */, 227 | ); 228 | name = LaunchScreen.storyboard; 229 | sourceTree = ""; 230 | }; 231 | /* End PBXVariantGroup section */ 232 | 233 | /* Begin XCBuildConfiguration section */ 234 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 235 | isa = XCBuildConfiguration; 236 | buildSettings = { 237 | ALWAYS_SEARCH_USER_PATHS = NO; 238 | CLANG_ANALYZER_NONNULL = YES; 239 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 240 | CLANG_CXX_LIBRARY = "libc++"; 241 | CLANG_ENABLE_MODULES = YES; 242 | CLANG_ENABLE_OBJC_ARC = YES; 243 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 244 | CLANG_WARN_BOOL_CONVERSION = YES; 245 | CLANG_WARN_COMMA = YES; 246 | CLANG_WARN_CONSTANT_CONVERSION = YES; 247 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 248 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 249 | CLANG_WARN_EMPTY_BODY = YES; 250 | CLANG_WARN_ENUM_CONVERSION = YES; 251 | CLANG_WARN_INFINITE_RECURSION = YES; 252 | CLANG_WARN_INT_CONVERSION = YES; 253 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 254 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 255 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 256 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 257 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 258 | CLANG_WARN_STRICT_PROTOTYPES = YES; 259 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 260 | CLANG_WARN_UNREACHABLE_CODE = YES; 261 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 262 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 263 | COPY_PHASE_STRIP = NO; 264 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 265 | ENABLE_NS_ASSERTIONS = NO; 266 | ENABLE_STRICT_OBJC_MSGSEND = YES; 267 | GCC_C_LANGUAGE_STANDARD = gnu99; 268 | GCC_NO_COMMON_BLOCKS = YES; 269 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 270 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 271 | GCC_WARN_UNDECLARED_SELECTOR = YES; 272 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 273 | GCC_WARN_UNUSED_FUNCTION = YES; 274 | GCC_WARN_UNUSED_VARIABLE = YES; 275 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 276 | MTL_ENABLE_DEBUG_INFO = NO; 277 | SDKROOT = iphoneos; 278 | SUPPORTED_PLATFORMS = iphoneos; 279 | TARGETED_DEVICE_FAMILY = "1,2"; 280 | VALIDATE_PRODUCT = YES; 281 | }; 282 | name = Profile; 283 | }; 284 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 285 | isa = XCBuildConfiguration; 286 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 287 | buildSettings = { 288 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 289 | CLANG_ENABLE_MODULES = YES; 290 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 291 | ENABLE_BITCODE = NO; 292 | INFOPLIST_FILE = Runner/Info.plist; 293 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 294 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 295 | PRODUCT_NAME = "$(TARGET_NAME)"; 296 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 297 | SWIFT_VERSION = 5.0; 298 | VERSIONING_SYSTEM = "apple-generic"; 299 | }; 300 | name = Profile; 301 | }; 302 | 97C147031CF9000F007C117D /* Debug */ = { 303 | isa = XCBuildConfiguration; 304 | buildSettings = { 305 | ALWAYS_SEARCH_USER_PATHS = NO; 306 | CLANG_ANALYZER_NONNULL = YES; 307 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 308 | CLANG_CXX_LIBRARY = "libc++"; 309 | CLANG_ENABLE_MODULES = YES; 310 | CLANG_ENABLE_OBJC_ARC = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_EMPTY_BODY = YES; 318 | CLANG_WARN_ENUM_CONVERSION = YES; 319 | CLANG_WARN_INFINITE_RECURSION = YES; 320 | CLANG_WARN_INT_CONVERSION = YES; 321 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 322 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 323 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 324 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 325 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 326 | CLANG_WARN_STRICT_PROTOTYPES = YES; 327 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 328 | CLANG_WARN_UNREACHABLE_CODE = YES; 329 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 330 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 331 | COPY_PHASE_STRIP = NO; 332 | DEBUG_INFORMATION_FORMAT = dwarf; 333 | ENABLE_STRICT_OBJC_MSGSEND = YES; 334 | ENABLE_TESTABILITY = YES; 335 | GCC_C_LANGUAGE_STANDARD = gnu99; 336 | GCC_DYNAMIC_NO_PIC = NO; 337 | GCC_NO_COMMON_BLOCKS = YES; 338 | GCC_OPTIMIZATION_LEVEL = 0; 339 | GCC_PREPROCESSOR_DEFINITIONS = ( 340 | "DEBUG=1", 341 | "$(inherited)", 342 | ); 343 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 344 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 345 | GCC_WARN_UNDECLARED_SELECTOR = YES; 346 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 347 | GCC_WARN_UNUSED_FUNCTION = YES; 348 | GCC_WARN_UNUSED_VARIABLE = YES; 349 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 350 | MTL_ENABLE_DEBUG_INFO = YES; 351 | ONLY_ACTIVE_ARCH = YES; 352 | SDKROOT = iphoneos; 353 | TARGETED_DEVICE_FAMILY = "1,2"; 354 | }; 355 | name = Debug; 356 | }; 357 | 97C147041CF9000F007C117D /* Release */ = { 358 | isa = XCBuildConfiguration; 359 | buildSettings = { 360 | ALWAYS_SEARCH_USER_PATHS = NO; 361 | CLANG_ANALYZER_NONNULL = YES; 362 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 363 | CLANG_CXX_LIBRARY = "libc++"; 364 | CLANG_ENABLE_MODULES = YES; 365 | CLANG_ENABLE_OBJC_ARC = YES; 366 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 367 | CLANG_WARN_BOOL_CONVERSION = YES; 368 | CLANG_WARN_COMMA = YES; 369 | CLANG_WARN_CONSTANT_CONVERSION = YES; 370 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 371 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 372 | CLANG_WARN_EMPTY_BODY = YES; 373 | CLANG_WARN_ENUM_CONVERSION = YES; 374 | CLANG_WARN_INFINITE_RECURSION = YES; 375 | CLANG_WARN_INT_CONVERSION = YES; 376 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 377 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 378 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 380 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 381 | CLANG_WARN_STRICT_PROTOTYPES = YES; 382 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 383 | CLANG_WARN_UNREACHABLE_CODE = YES; 384 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 385 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 386 | COPY_PHASE_STRIP = NO; 387 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 388 | ENABLE_NS_ASSERTIONS = NO; 389 | ENABLE_STRICT_OBJC_MSGSEND = YES; 390 | GCC_C_LANGUAGE_STANDARD = gnu99; 391 | GCC_NO_COMMON_BLOCKS = YES; 392 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 393 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 394 | GCC_WARN_UNDECLARED_SELECTOR = YES; 395 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 396 | GCC_WARN_UNUSED_FUNCTION = YES; 397 | GCC_WARN_UNUSED_VARIABLE = YES; 398 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 399 | MTL_ENABLE_DEBUG_INFO = NO; 400 | SDKROOT = iphoneos; 401 | SUPPORTED_PLATFORMS = iphoneos; 402 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 403 | TARGETED_DEVICE_FAMILY = "1,2"; 404 | VALIDATE_PRODUCT = YES; 405 | }; 406 | name = Release; 407 | }; 408 | 97C147061CF9000F007C117D /* Debug */ = { 409 | isa = XCBuildConfiguration; 410 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 411 | buildSettings = { 412 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 413 | CLANG_ENABLE_MODULES = YES; 414 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 415 | ENABLE_BITCODE = NO; 416 | INFOPLIST_FILE = Runner/Info.plist; 417 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 418 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 419 | PRODUCT_NAME = "$(TARGET_NAME)"; 420 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 421 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 422 | SWIFT_VERSION = 5.0; 423 | VERSIONING_SYSTEM = "apple-generic"; 424 | }; 425 | name = Debug; 426 | }; 427 | 97C147071CF9000F007C117D /* Release */ = { 428 | isa = XCBuildConfiguration; 429 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 430 | buildSettings = { 431 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 432 | CLANG_ENABLE_MODULES = YES; 433 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 434 | ENABLE_BITCODE = NO; 435 | INFOPLIST_FILE = Runner/Info.plist; 436 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 437 | PRODUCT_BUNDLE_IDENTIFIER = com.example.example; 438 | PRODUCT_NAME = "$(TARGET_NAME)"; 439 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 440 | SWIFT_VERSION = 5.0; 441 | VERSIONING_SYSTEM = "apple-generic"; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 97C147031CF9000F007C117D /* Debug */, 452 | 97C147041CF9000F007C117D /* Release */, 453 | 249021D3217E4FDB00AE95B9 /* Profile */, 454 | ); 455 | defaultConfigurationIsVisible = 0; 456 | defaultConfigurationName = Release; 457 | }; 458 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 459 | isa = XCConfigurationList; 460 | buildConfigurations = ( 461 | 97C147061CF9000F007C117D /* Debug */, 462 | 97C147071CF9000F007C117D /* Release */, 463 | 249021D4217E4FDB00AE95B9 /* Profile */, 464 | ); 465 | defaultConfigurationIsVisible = 0; 466 | defaultConfigurationName = Release; 467 | }; 468 | /* End XCConfigurationList section */ 469 | }; 470 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 471 | } 472 | -------------------------------------------------------------------------------- /example/macos/Runner/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 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 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | --------------------------------------------------------------------------------