├── .gitignore ├── .metadata ├── .vscode └── launch.json ├── CHANGELOG.md ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── build.gradle ├── settings.gradle └── src │ └── main │ ├── AndroidManifest.xml │ ├── kotlin │ └── dev │ │ └── henryleunghk │ │ └── flutter_native_text_input │ │ ├── NativeTextInput.kt │ │ ├── NativeTextInputFactory.kt │ │ └── NativeTextInputPlugin.kt │ └── res │ └── drawable │ ├── edit_text_background.xml │ └── edit_text_cursor.xml ├── demo ├── flutter-textfield.gif ├── more-examples.gif └── native-textview.gif ├── example ├── .gitignore ├── .metadata ├── README.md ├── analysis_options.yaml ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── kotlin │ │ │ │ └── dev │ │ │ │ │ └── henryleunghk │ │ │ │ │ └── flutter_native_text_input_example │ │ │ │ │ └── MainActivity.kt │ │ │ └── res │ │ │ │ ├── drawable-v21 │ │ │ │ └── launch_background.xml │ │ │ │ ├── drawable │ │ │ │ └── launch_background.xml │ │ │ │ ├── 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 │ │ │ │ ├── values-night │ │ │ │ └── styles.xml │ │ │ │ └── values │ │ │ │ └── styles.xml │ │ │ └── profile │ │ │ └── AndroidManifest.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ └── gradle-wrapper.properties │ └── settings.gradle ├── ios │ ├── .gitignore │ ├── Flutter │ │ ├── AppFrameworkInfo.plist │ │ ├── Debug.xcconfig │ │ ├── Flutter.podspec │ │ └── Release.xcconfig │ ├── Podfile │ ├── Podfile.lock │ ├── Runner.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ │ └── WorkspaceSettings.xcsettings │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── Runner.xcscheme │ ├── Runner.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── Runner │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── Icon-App-1024x1024@1x.png │ │ │ ├── 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-83.5x83.5@2x.png │ │ └── LaunchImage.imageset │ │ │ ├── Contents.json │ │ │ ├── LaunchImage.png │ │ │ ├── LaunchImage@2x.png │ │ │ ├── LaunchImage@3x.png │ │ │ └── README.md │ │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ │ ├── Info.plist │ │ └── main.m ├── lib │ ├── demo_item.dart │ ├── main.dart │ └── more_use_case_listing_page.dart ├── pubspec.lock └── pubspec.yaml ├── flutter_native_text_input.iml ├── ios ├── .gitignore ├── Assets │ └── .gitkeep ├── Classes │ ├── NativeTextInput.h │ ├── NativeTextInput.m │ ├── NativeTextInputDelegate.h │ ├── NativeTextInputDelegate.m │ ├── NativeTextInputFactory.h │ ├── NativeTextInputFactory.m │ ├── NativeTextInputPlugin.h │ └── NativeTextInputPlugin.m └── flutter_native_text_input.podspec ├── lib └── flutter_native_text_input.dart ├── migration-to-v2.md └── pubspec.yaml /.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 | *.ipr 14 | *.iws 15 | .idea/ 16 | 17 | # The .vscode folder contains launch configuration and tasks you configure in 18 | # VS Code which you may wish to be included in version control, so this line 19 | # is commented out by default. 20 | #.vscode/ 21 | 22 | # Flutter/Dart/Pub related 23 | # Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. 24 | /pubspec.lock 25 | **/doc/api/ 26 | .dart_tool/ 27 | .packages 28 | build/ 29 | -------------------------------------------------------------------------------- /.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: 6ec059c99498f9ce4b445458b5f2ccfd20fcc95c 8 | channel: master 9 | 10 | project_type: plugin 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "example", 9 | "cwd": "example", 10 | "request": "launch", 11 | "type": "dart" 12 | }, 13 | { 14 | "name": "example (profile mode)", 15 | "cwd": "example", 16 | "request": "launch", 17 | "type": "dart", 18 | "flutterMode": "profile" 19 | }, 20 | { 21 | "name": "flutter-native-text-input", 22 | "request": "launch", 23 | "type": "dart" 24 | }, 25 | { 26 | "name": "flutter-native-text-input (profile mode)", 27 | "request": "launch", 28 | "type": "dart", 29 | "flutterMode": "profile" 30 | } 31 | ] 32 | } -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## 2.4.0 2 | 3 | * support custom font families on iOS 4 | 5 | ## 2.3.0 6 | 7 | * dispose `TextEditingController` and `FocusNode`, preventing memory leaks 8 | * support for detecting tap gestures on iOS 9 | 10 | ## 2.2.0 11 | 12 | * fix an iOS race condition that would cause errors when autofocusing 13 | * implement autocorrect for iOS 14 | * use `UITextView.attributedPlaceholder` to draw the iOS placeholder 15 | * ensure that `BoxConstraints` are valid 16 | 17 | ## 2.1.1 18 | 19 | * fix android text input background not transparent 20 | 21 | ## 2.1.0 22 | 23 | * add back `placeholderStyle` in `iosOptions` 24 | 25 | ## 2.0.0 26 | 27 | * add Android support 28 | * wrap `cursorColor` and `keyboardAppearance` in `iosOptions` 29 | * replace `placeholderStyle` with `placeholderColor` 30 | 31 | ## 1.3.1 32 | 33 | * add minHeightPadding configuration 34 | 35 | ## 1.3.0 36 | 37 | * add support of onEditingComplete 38 | 39 | ## 1.2.4 40 | 41 | * fix focus action not working sometimes 42 | 43 | ## 1.2.3 44 | 45 | * fix placeholder re-appeared upon widget rebuilt 46 | 47 | ## 1.2.2 48 | 49 | * fix focusNode usage 50 | 51 | ## 1.2.1 52 | 53 | * fix returnKeyType and onSubmitted usage 54 | 55 | ## 1.2.0 56 | 57 | * add support of cursorColor, returnKeyType and textCapitalization 58 | 59 | ## 1.1.1 60 | 61 | * fix maxLines usage and support dynamic content height 62 | 63 | ## 1.1.0 64 | 65 | * support customizations 66 | 67 | ## 1.0.0 68 | 69 | * support null-safety 70 | 71 | ## 0.1.3 72 | 73 | * fix Chinese typing issue 74 | 75 | ## 0.1.2 76 | 77 | * fix input height not returning default on clearing multi-line text 78 | 79 | ## 0.1.1 80 | 81 | * update examples 82 | 83 | ## 0.1.0 84 | 85 | * support dynamic widget height with plugin documentation added 86 | 87 | ## 0.0.2 88 | 89 | * update usage example 90 | 91 | ## 0.0.1 92 | 93 | * initial release with iOS support 94 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | If you enjoy using this package or it helps you or your team, you could also buy me a cup of coffee to show support :) 2 | 3 | https://PayPal.Me/hkhenryleung/50 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Henry Leung 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Native Text Input for Flutter 2 | 3 | A text input widget built using the native `UITextView` on iOS and `EditText` on Android. For text view, please refer to [flutter-native-text-view](https://github.com/henryleunghk/flutter-native-text-view). 4 | 5 | ``` 6 | Android support is added in v2 🎉🎉🎉 7 | ``` 8 | For existing project(s) using v1, please refer to [migration guide](migration-to-v2.md) here. 9 | 10 | ## Installation 11 | 12 | Follow the instructions from https://pub.dev/packages/flutter_native_text_input/install 13 | 14 | ## Why you should use this 15 | 16 | Many mobile app users are used to the subtle features provided by the native iOS or Android systems. Even though Flutter provides a lot of useful widgets, many Flutter developers will notice that `TextField` or `CupertinoTextField` provided by Flutter are [not on par with their native counterpart][1]. 17 | 18 | ![](demo/flutter-textfield.gif) 19 | ![](demo/native-textview.gif) 20 | 21 | The above shows just one of the missing text editing features in Flutter's `TextField`, when comparing to the native iOS `UITextView`. Features like these are especially important if you're building an app that involves a lot of text composition (messaging, editing document, and so on). 22 | 23 | To address this, this package simply wraps the native `UITextView` (and `EditText` for Android) as a Flutter widget. 24 | 25 | Hope you find it useful and happy coding! 🎉🎉🎉 26 | 27 | ## Plugin API 28 | 29 | | Name | Type | Description | Default | 30 | |:----------------|:--------------|:-------------------------------|:-------------------------| 31 | | `controller` | TextEditingController | Controlling the text being edited (https://api.flutter.dev/flutter/material/TextField/controller.html) | null | 32 | | `decoration` | BoxDecoration | Controls the BoxDecoration of the box behind the text input (https://api.flutter.dev/flutter/cupertino/CupertinoTextField/decoration.html) | null | 33 | | `focusNode` | FocusNode | Defines the keyboard focus for this widget (https://api.flutter.dev/flutter/material/TextField/focusNode.html) | null | 34 | | `iosOptions` | IosOptions | iOS only options (cursorColor, keyboardAppearance) | null | 35 | | `keyboardType` | KeyboardType | Type of keyboard to display for a given text-based view (https://developer.apple.com/documentation/uikit/uikeyboardtype) | KeyboardType.defaultType | 36 | | `maxLines` | int | The maximum number of lines to show at one time, wrapping if necessary (https://api.flutter.dev/flutter/material/TextField/maxLines.html) | 1 | 37 | | `minHeightPadding` | double | Extra vertical spacing added in addition to line height for iOS UITextView to fit well in single line mode | 18.0 | 38 | | `minLines` | int | Minimum number of lines of text input widget | 1 | 39 | | `placeholder` | String | Placeholder text when text entry is empty (https://api.flutter.dev/flutter/cupertino/CupertinoTextField/placeholder.html) | null | 40 | | `placeholderColor`| Color | The text color to use for the placeholder text | null | 41 | | `returnKeyType` | ReturnKeyType | Constants that specify the text string that displays in the Return key of a keyboard (https://developer.apple.com/documentation/uikit/uireturnkeytype) | ReturnKeyType.defaultAction | 42 | | `style` | TextStyle | The style to use for the text being edited [Only `fontSize`, `fontWeight`, `color` are supported] (https://api.flutter.dev/flutter/material/TextField/style.html) | null | 43 | | `textAlign` | TextAlign | How the text should be aligned horizontally (https://api.flutter.dev/flutter/material/TextField/textAlign.html) | TextAlign.start | 44 | | `textCapitalization` | TextCapitalization | Configures how the platform keyboard will select an uppercase or lowercase keyboard (https://api.flutter.dev/flutter/material/TextField/textCapitalization.html) | TextCapitalization.none | 45 | | `textContentType` | TextContentType | To identify the semantic meaning expected for a text-entry area (https://developer.apple.com/documentation/uikit/uitextcontenttype) | null | 46 | | `onChanged` | ValueChanged\ | Called when the user initiates a change to text entry (https://api.flutter.dev/flutter/material/TextField/onChanged.html) | null | 47 | | `onEditingComplete` | VoidCallback? | Called when the user submits editable content (e.g., user presses the "done" button on the keyboard) (https://api.flutter.dev/flutter/material/TextField/onEditingComplete.html) | null | 48 | | `onSubmitted` | ValueChanged\ | Called when the user indicates that they are done editing the text in the field (https://api.flutter.dev/flutter/material/TextField/onSubmitted.html) | null | 49 | 50 | ### **IosOptions** 51 | | Name | Type | Description | Default | 52 | |:----------------|:--------------|:-------------------------------|:-------------------------| 53 | | `cursorColor` | Color | The color of the cursor (https://api.flutter.dev/flutter/material/TextField/cursorColor.html) | null | 54 | | `keyboardAppearance` | Brightness | The appearance of the keyboard (https://api.flutter.dev/flutter/material/TextField/keyboardAppearance.html) | null | 55 | | `placeholderStyle`| TextStyle | The style to use for the placeholder text. [Only `fontSize`, `fontWeight` are supported] (https://api.flutter.dev/flutter/cupertino/CupertinoTextField/placeholderStyle.html) | null | 56 | 57 | 58 | ## More examples 59 | 60 | You may find more usage examples [here][2]. 61 | 62 | ![](demo/more-examples.gif) 63 | 64 | ## Contributing 65 | 66 | ### Found a bug? 67 | Please do not hestitate to report that. This cuuld help improve this package. 68 | 69 | ### Feature request? 70 | Please feel free to create an issue. 71 | 72 | ### Pull request? 73 | Contributors are welcome. Just create a PR and it would be reviewed and merged ASAP. 74 | 75 | If you enjoy using this package or it helps you or your team, you could also buy me a cup of coffee to show support :) 76 | 77 | https://PayPal.Me/hkhenryleung/50 78 | 79 | ## License 80 | 81 | This project is licensed under the [MIT License](https://opensource.org/licenses/mit-license.html). 82 | 83 | [1]: https://github.com/flutter/flutter/issues/12920 84 | [2]: https://github.com/henryleunghk/flutter-native-text-input/blob/master/example/lib/more_use_case_listing_page.dart 85 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | include: package:flutter_lints/flutter.yaml 2 | 3 | # Additional information about this file can be found at 4 | # https://dart.dev/guides/language/analysis-options 5 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | group 'dev.henryleunghk.flutter_native_text_input' 2 | version '1.0-SNAPSHOT' 3 | 4 | buildscript { 5 | ext.kotlin_version = '1.6.10' 6 | repositories { 7 | google() 8 | mavenCentral() 9 | } 10 | 11 | dependencies { 12 | classpath 'com.android.tools.build:gradle:4.1.0' 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 14 | } 15 | } 16 | 17 | rootProject.allprojects { 18 | repositories { 19 | google() 20 | mavenCentral() 21 | } 22 | } 23 | 24 | apply plugin: 'com.android.library' 25 | apply plugin: 'kotlin-android' 26 | 27 | android { 28 | compileSdkVersion 31 29 | 30 | compileOptions { 31 | sourceCompatibility JavaVersion.VERSION_1_8 32 | targetCompatibility JavaVersion.VERSION_1_8 33 | } 34 | 35 | kotlinOptions { 36 | jvmTarget = '1.8' 37 | } 38 | 39 | sourceSets { 40 | main.java.srcDirs += 'src/main/kotlin' 41 | } 42 | 43 | defaultConfig { 44 | minSdkVersion 16 45 | } 46 | } 47 | 48 | dependencies { 49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 50 | implementation 'androidx.core:core-ktx:1.3.0' 51 | } 52 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'flutter_native_text_input' 2 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/henryleunghk/flutter_native_text_input/NativeTextInput.kt: -------------------------------------------------------------------------------- 1 | package dev.henryleunghk.flutter_native_text_input 2 | 3 | import android.content.Context 4 | import android.graphics.Color 5 | import android.graphics.Typeface 6 | import android.os.Build 7 | import android.text.InputType 8 | import android.util.Log 9 | import android.util.TypedValue 10 | import android.view.Gravity 11 | import android.view.View 12 | import android.view.inputmethod.EditorInfo 13 | import android.view.inputmethod.InputMethodManager 14 | import android.widget.EditText 15 | import androidx.annotation.NonNull 16 | import androidx.core.widget.doOnTextChanged 17 | import io.flutter.plugin.common.MethodCall 18 | import io.flutter.plugin.common.MethodChannel 19 | import io.flutter.plugin.platform.PlatformView 20 | 21 | val TAG: String = "NativeTextInput" 22 | 23 | internal class NativeTextInput(context: Context, id: Int, creationParams: Map, channel: MethodChannel) : PlatformView, MethodChannel.MethodCallHandler { 24 | private val context: Context 25 | private val scaledDensity: Float 26 | private val editText: EditText 27 | 28 | override fun getView(): View { 29 | return editText 30 | } 31 | 32 | override fun dispose() {} 33 | 34 | init { 35 | this.context = context 36 | scaledDensity = context.resources.displayMetrics.scaledDensity 37 | 38 | editText = EditText(context) 39 | editText.setBackgroundResource(R.drawable.edit_text_background) 40 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { 41 | editText.setTextCursorDrawable(R.drawable.edit_text_cursor) 42 | } 43 | 44 | if (creationParams.get("fontColor") != null) { 45 | val rgbMap = creationParams.get("fontColor") as Map 46 | val color = Color.argb( 47 | rgbMap.get("alpha") as Int, 48 | rgbMap.get("red") as Int, 49 | rgbMap.get("green") as Int, 50 | rgbMap.get("blue") as Int) 51 | editText.setTextColor(color) 52 | } 53 | 54 | if (creationParams.get("fontSize") != null) { 55 | val fontSize = creationParams.get("fontSize") as Double 56 | Log.d(TAG, "fontSize:" + fontSize) 57 | editText.setTextSize(TypedValue.COMPLEX_UNIT_SP, fontSize.toFloat()) 58 | editText.textSize = fontSize.toFloat() 59 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 60 | editText.lineHeight = fontSize.toInt() 61 | } 62 | } 63 | 64 | if (creationParams.get("fontWeight") != null && 65 | android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.P) { 66 | val fontWeight = creationParams.get("fontWeight") as String 67 | if (fontWeight == "FontWeight.w100") { 68 | editText.typeface = Typeface.create(editText.typeface, 100, false) 69 | } else if (fontWeight == "FontWeight.w200") { 70 | editText.typeface = Typeface.create(editText.typeface, 200, false) 71 | } else if (fontWeight == "FontWeight.w300") { 72 | editText.typeface = Typeface.create(editText.typeface, 300, false) 73 | } else if (fontWeight == "FontWeight.w400") { 74 | editText.typeface = Typeface.create(editText.typeface, 400, false) 75 | } else if (fontWeight == "FontWeight.w500") { 76 | editText.typeface = Typeface.create(editText.typeface, 500, false) 77 | } else if (fontWeight == "FontWeight.w600") { 78 | editText.typeface = Typeface.create(editText.typeface, 600, false) 79 | } else if (fontWeight == "FontWeight.w700") { 80 | editText.typeface = Typeface.create(editText.typeface, 700, false) 81 | } else if (fontWeight == "FontWeight.w800") { 82 | editText.typeface = Typeface.create(editText.typeface, 800, false) 83 | } else if (fontWeight == "FontWeight.w900") { 84 | editText.typeface = Typeface.create(editText.typeface, 900, false) 85 | } 86 | } 87 | 88 | val minLines = creationParams.get("minLines") as Int 89 | editText.minLines = minLines 90 | editText.setLines(minLines) 91 | 92 | val maxLines = creationParams.get("maxLines") as Int 93 | if (maxLines > minLines) { 94 | editText.maxLines = maxLines 95 | } else { 96 | editText.maxLines = minLines 97 | } 98 | 99 | val minHeightPadding = creationParams.get("minHeightPadding") as Double 100 | editText.setPadding( 101 | 0, 102 | minHeightPadding.toInt() / 2, 103 | 0, 104 | minHeightPadding.toInt() / 2) 105 | 106 | editText.hint = creationParams.get("placeholder") as String 107 | 108 | if (creationParams.get("placeholderFontColor") != null) { 109 | val rgbMap = creationParams.get("placeholderFontColor") as Map 110 | val color = Color.argb( 111 | rgbMap.get("alpha") as Int, 112 | rgbMap.get("red") as Int, 113 | rgbMap.get("green") as Int, 114 | rgbMap.get("blue") as Int) 115 | editText.setHintTextColor(color) 116 | } 117 | 118 | if (creationParams.get("returnKeyType") != null) { 119 | val returnKeyType = creationParams.get("returnKeyType") as String 120 | if (returnKeyType == "ReturnKeyType.go") { 121 | editText.imeOptions = EditorInfo.IME_ACTION_GO 122 | } else if (returnKeyType == "ReturnKeyType.next") { 123 | editText.imeOptions = EditorInfo.IME_ACTION_NEXT 124 | } else if (returnKeyType == "ReturnKeyType.search") { 125 | editText.imeOptions = EditorInfo.IME_ACTION_SEARCH 126 | } else if (returnKeyType == "ReturnKeyType.send") { 127 | editText.imeOptions = EditorInfo.IME_ACTION_SEND 128 | } else if (returnKeyType == "ReturnKeyType.done") { 129 | editText.imeOptions = EditorInfo.IME_ACTION_DONE 130 | } 131 | } 132 | 133 | if (creationParams.get("text") != null) { 134 | val text = creationParams.get("text") as String 135 | editText.setText(text) 136 | } 137 | 138 | if (creationParams.get("textAlign") != null) { 139 | val textAlign = creationParams.get("textAlign") as String 140 | if (textAlign == "TextAlign.left") { 141 | editText.gravity = Gravity.LEFT 142 | } else if (textAlign == "TextAlign.right") { 143 | editText.gravity = Gravity.RIGHT 144 | } else if (textAlign == "TextAlign.center") { 145 | editText.gravity = Gravity.CENTER 146 | } else if (textAlign == "TextAlign.justify") { 147 | editText.gravity = Gravity.FILL 148 | } else if (textAlign == "TextAlign.start") { 149 | editText.gravity = Gravity.START 150 | } else if (textAlign == "TextAlign.end") { 151 | editText.gravity = Gravity.END 152 | } 153 | } 154 | 155 | if (creationParams.get("textCapitalization") != null) { 156 | val textCapitalization = creationParams.get("textCapitalization") as String 157 | if (textCapitalization == "TextCapitalization.none") { 158 | editText.inputType = InputType.TYPE_CLASS_TEXT 159 | } else if (textCapitalization == "TextCapitalization.characters") { 160 | editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_CHARACTERS 161 | } else if (textCapitalization == "TextCapitalization.sentences") { 162 | editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES 163 | } else if (textCapitalization == "TextCapitalization.words") { 164 | editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_CAP_WORDS 165 | } 166 | } 167 | 168 | if (creationParams.get("keyboardType") != null) { 169 | val keyboardType = creationParams.get("keyboardType") as String 170 | if (keyboardType == "KeyboardType.numbersAndPunctuation" || 171 | keyboardType == "KeyboardType.numberPad" || 172 | keyboardType == "KeyboardType.asciiCapableNumberPad") { 173 | editText.inputType = InputType.TYPE_CLASS_NUMBER 174 | } else if (keyboardType == "KeyboardType.phonePad") { 175 | editText.inputType = InputType.TYPE_CLASS_PHONE 176 | } else if (keyboardType == "KeyboardType.decimalPad") { 177 | editText.inputType = InputType.TYPE_CLASS_NUMBER or InputType.TYPE_NUMBER_FLAG_DECIMAL 178 | } else if (keyboardType == "KeyboardType.url" || 179 | keyboardType == "KeyboardType.webSearch") { 180 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_URI 181 | } else if (keyboardType == "KeyboardType.emailAddress") { 182 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS 183 | } 184 | } else if (creationParams.get("textContentType") != null) { 185 | val textContentType = creationParams.get("textContentType") as String 186 | if (textContentType == "TextContentType.username" || 187 | textContentType == "TextContentType.givenName" || 188 | textContentType == "TextContentType.middleName" || 189 | textContentType == "TextContentType.familyName" || 190 | textContentType == "TextContentType.nickname") { 191 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_PERSON_NAME 192 | } else if (textContentType == "TextContentType.password" || 193 | textContentType == "TextContentType.newPassword") { 194 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_PASSWORD 195 | } else if (textContentType == "TextContentType.fullStreetAddress" || 196 | textContentType == "TextContentType.streetAddressLine1" || 197 | textContentType == "TextContentType.streetAddressLine2" || 198 | textContentType == "TextContentType.addressCity" || 199 | textContentType == "TextContentType.addressState" || 200 | textContentType == "TextContentType.addressCityAndState") { 201 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_POSTAL_ADDRESS 202 | } else if (textContentType == "TextContentType.telephoneNumber") { 203 | editText.inputType = InputType.TYPE_CLASS_PHONE 204 | } else if (textContentType == "TextContentType.emailAddress") { 205 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_EMAIL_ADDRESS 206 | } else if (textContentType == "TextContentType.url") { 207 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_VARIATION_URI 208 | } 209 | } 210 | 211 | val width = creationParams.get("width") as Double 212 | editText.maxWidth = width.toInt() 213 | 214 | if (minLines > 1 || maxLines > 1) { 215 | editText.inputType = editText.inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE 216 | editText.setHorizontallyScrolling(false) 217 | } 218 | 219 | editText.setOnFocusChangeListener { v, hasFocus -> 220 | Log.d(TAG, "hasFocus:" + hasFocus) 221 | if (hasFocus) { 222 | channel.invokeMethod("inputStarted", null) 223 | } else { 224 | channel.invokeMethod("inputFinished", mapOf("text" to editText.text.toString())) 225 | } 226 | } 227 | 228 | editText.doOnTextChanged { text, start, before, count -> 229 | Log.d(TAG, "doOnTextChanged:text:"+text.toString()) 230 | Log.d(TAG, "doOnTextChanged:lineCount:"+editText.lineCount); 231 | channel.invokeMethod("inputValueChanged", mapOf("text" to text.toString())) 232 | } 233 | 234 | channel.setMethodCallHandler(this) 235 | } 236 | 237 | fun showKeyboard() { 238 | val inputMethodManager: InputMethodManager = 239 | context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 240 | inputMethodManager.showSoftInput(editText, 0) 241 | } 242 | 243 | fun hideKeyboard() { 244 | val inputMethodManager = 245 | context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 246 | inputMethodManager.hideSoftInputFromWindow(editText.windowToken, 0) 247 | } 248 | 249 | override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: MethodChannel.Result) { 250 | if (call.method == "getContentHeight") { 251 | var contentHeight = editText.lineHeight / scaledDensity * editText.lineCount 252 | Log.d(TAG, "getContentHeight:" + contentHeight) 253 | result.success(contentHeight.toDouble()) 254 | } else if (call.method == "getLineHeight") { 255 | val lineHeight = editText.textSize / scaledDensity 256 | Log.d(TAG, "getLineHeight:" + lineHeight) 257 | result.success(lineHeight.toDouble()) 258 | } else if (call.method == "focus") { 259 | editText.requestFocus() 260 | showKeyboard() 261 | } else if (call.method == "unfocus") { 262 | editText.clearFocus() 263 | hideKeyboard() 264 | } else if (call.method == "setText") { 265 | val text = call.argument("text") 266 | editText.setText(text) 267 | } 268 | } 269 | } 270 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/henryleunghk/flutter_native_text_input/NativeTextInputFactory.kt: -------------------------------------------------------------------------------- 1 | package dev.henryleunghk.flutter_native_text_input 2 | 3 | import android.content.Context 4 | import io.flutter.embedding.engine.plugins.FlutterPlugin 5 | import io.flutter.plugin.common.BinaryMessenger 6 | import io.flutter.plugin.common.MethodChannel 7 | import io.flutter.plugin.common.StandardMessageCodec 8 | import io.flutter.plugin.platform.PlatformView 9 | import io.flutter.plugin.platform.PlatformViewFactory 10 | 11 | class NativeTextInputFactory(binding: FlutterPlugin.FlutterPluginBinding): PlatformViewFactory(StandardMessageCodec.INSTANCE) { 12 | /// The MethodChannel that will the communication between Flutter and native Android 13 | /// 14 | /// This local reference serves to register the plugin with the Flutter Engine and unregister it 15 | /// when the Flutter Engine is detached from the Activity 16 | private lateinit var channel: MethodChannel 17 | private lateinit var nativeTextInput: NativeTextInput 18 | private var messenger: BinaryMessenger 19 | 20 | init { 21 | messenger = binding.binaryMessenger 22 | } 23 | 24 | override fun create(context: Context?, viewId: Int, args: Any?): PlatformView { 25 | val creationParams = args as Map 26 | val channelName = "flutter_native_text_input${viewId}" 27 | channel = MethodChannel(messenger, channelName) 28 | 29 | return NativeTextInput(context!!, viewId, creationParams, channel) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/src/main/kotlin/dev/henryleunghk/flutter_native_text_input/NativeTextInputPlugin.kt: -------------------------------------------------------------------------------- 1 | package dev.henryleunghk.flutter_native_text_input 2 | 3 | import androidx.annotation.NonNull 4 | 5 | import io.flutter.embedding.engine.plugins.FlutterPlugin 6 | 7 | /** NativeTextInputPlugin */ 8 | class NativeTextInputPlugin: FlutterPlugin { 9 | private lateinit var factory: NativeTextInputFactory 10 | 11 | override fun onAttachedToEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 12 | factory = NativeTextInputFactory(binding) 13 | binding.platformViewRegistry.registerViewFactory("flutter_native_text_input", factory) 14 | } 15 | 16 | override fun onDetachedFromEngine(@NonNull binding: FlutterPlugin.FlutterPluginBinding) { 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /android/src/main/res/drawable/edit_text_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /android/src/main/res/drawable/edit_text_cursor.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /demo/flutter-textfield.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/demo/flutter-textfield.gif -------------------------------------------------------------------------------- /demo/more-examples.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/demo/more-examples.gif -------------------------------------------------------------------------------- /demo/native-textview.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/demo/native-textview.gif -------------------------------------------------------------------------------- /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 | # Visual Studio Code related 19 | .vscode/ 20 | 21 | # Flutter/Dart/Pub related 22 | **/doc/api/ 23 | .dart_tool/ 24 | .flutter-plugins 25 | .packages 26 | .pub-cache/ 27 | .pub/ 28 | /build/ 29 | 30 | # Android related 31 | **/android/**/gradle-wrapper.jar 32 | **/android/.gradle 33 | **/android/captures/ 34 | **/android/gradlew 35 | **/android/gradlew.bat 36 | **/android/local.properties 37 | **/android/**/GeneratedPluginRegistrant.java 38 | 39 | # iOS/XCode related 40 | **/ios/**/*.mode1v3 41 | **/ios/**/*.mode2v3 42 | **/ios/**/*.moved-aside 43 | **/ios/**/*.pbxuser 44 | **/ios/**/*.perspectivev3 45 | **/ios/**/*sync/ 46 | **/ios/**/.sconsign.dblite 47 | **/ios/**/.tags* 48 | **/ios/**/.vagrant/ 49 | **/ios/**/DerivedData/ 50 | **/ios/**/Icon? 51 | **/ios/**/Pods/ 52 | **/ios/**/.symlinks/ 53 | **/ios/**/profile 54 | **/ios/**/xcuserdata 55 | **/ios/.generated/ 56 | **/ios/Flutter/App.framework 57 | **/ios/Flutter/Flutter.framework 58 | **/ios/Flutter/Generated.xcconfig 59 | **/ios/Flutter/app.flx 60 | **/ios/Flutter/app.zip 61 | **/ios/Flutter/flutter_assets/ 62 | **/ios/ServiceDefinitions.json 63 | **/ios/Runner/GeneratedPluginRegistrant.* 64 | .flutter-plugins-dependencies 65 | 66 | # Exceptions to above rules. 67 | !**/ios/**/default.mode1v3 68 | !**/ios/**/default.mode2v3 69 | !**/ios/**/default.pbxuser 70 | !**/ios/**/default.perspectivev3 71 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 72 | -------------------------------------------------------------------------------- /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: 6ec059c99498f9ce4b445458b5f2ccfd20fcc95c 8 | channel: master 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # flutter_native_text_input_example 2 | 3 | Demonstrates how to use the flutter_native_text_input plugin. 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 | -------------------------------------------------------------------------------- /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/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 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /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 flutter.compileSdkVersion 30 | 31 | compileOptions { 32 | sourceCompatibility JavaVersion.VERSION_1_8 33 | targetCompatibility JavaVersion.VERSION_1_8 34 | } 35 | 36 | kotlinOptions { 37 | jvmTarget = '1.8' 38 | } 39 | 40 | sourceSets { 41 | main.java.srcDirs += 'src/main/kotlin' 42 | } 43 | 44 | defaultConfig { 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 46 | applicationId "dev.henryleunghk.flutter_native_text_input_example" 47 | minSdkVersion flutter.minSdkVersion 48 | targetSdkVersion flutter.targetSdkVersion 49 | versionCode flutterVersionCode.toInteger() 50 | versionName flutterVersionName 51 | } 52 | 53 | buildTypes { 54 | release { 55 | // TODO: Add your own signing config for the release build. 56 | // Signing with the debug keys for now, so `flutter run --release` works. 57 | signingConfig signingConfigs.debug 58 | } 59 | } 60 | } 61 | 62 | flutter { 63 | source '../..' 64 | } 65 | 66 | dependencies { 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 68 | } 69 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /example/android/app/src/main/kotlin/dev/henryleunghk/flutter_native_text_input_example/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package dev.henryleunghk.flutter_native_text_input_example 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /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/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /example/android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:4.1.1' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 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 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /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-6.7-all.zip 7 | -------------------------------------------------------------------------------- /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/ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /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 | 9.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /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/Flutter.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # NOTE: This podspec is NOT to be published. It is only used as a local source! 3 | # This is a generated file; do not edit or check into version control. 4 | # 5 | 6 | Pod::Spec.new do |s| 7 | s.name = 'Flutter' 8 | s.version = '1.0.0' 9 | s.summary = 'High-performance, high-fidelity mobile apps.' 10 | s.homepage = 'https://flutter.io' 11 | s.license = { :type => 'MIT' } 12 | s.author = { 'Flutter Dev Team' => 'flutter-dev@googlegroups.com' } 13 | s.source = { :git => 'https://github.com/flutter/engine', :tag => s.version.to_s } 14 | s.ios.deployment_target = '9.0' 15 | # Framework linking is handled by Flutter tooling, not CocoaPods. 16 | # Add a placeholder to satisfy `s.dependency 'Flutter'` plugin podspecs. 17 | s.vendored_frameworks = 'path/to/nothing' 18 | end 19 | -------------------------------------------------------------------------------- /example/ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /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 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) 32 | end 33 | 34 | post_install do |installer| 35 | installer.pods_project.targets.each do |target| 36 | flutter_additional_ios_build_settings(target) 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /example/ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | - flutter_native_text_input (2.2.0): 4 | - Flutter 5 | 6 | DEPENDENCIES: 7 | - Flutter (from `Flutter`) 8 | - flutter_native_text_input (from `.symlinks/plugins/flutter_native_text_input/ios`) 9 | 10 | EXTERNAL SOURCES: 11 | Flutter: 12 | :path: Flutter 13 | flutter_native_text_input: 14 | :path: ".symlinks/plugins/flutter_native_text_input/ios" 15 | 16 | SPEC CHECKSUMS: 17 | Flutter: 50d75fe2f02b26cc09d224853bb45737f8b3214a 18 | flutter_native_text_input: 246da758e5a2e744774aa839a1a21902e0e84ceb 19 | 20 | PODFILE CHECKSUM: 8e679eca47255a8ca8067c4c67aab20e64cb974d 21 | 22 | COCOAPODS: 1.10.1 23 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 04AC526076737816B913148B /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 20317FD878180A696C0B139F /* libPods-Runner.a */; }; 11 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 13 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 14 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 15 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 16 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 17 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 18 | /* End PBXBuildFile section */ 19 | 20 | /* Begin PBXCopyFilesBuildPhase section */ 21 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 22 | isa = PBXCopyFilesBuildPhase; 23 | buildActionMask = 2147483647; 24 | dstPath = ""; 25 | dstSubfolderSpec = 10; 26 | files = ( 27 | ); 28 | name = "Embed Frameworks"; 29 | runOnlyForDeploymentPostprocessing = 0; 30 | }; 31 | /* End PBXCopyFilesBuildPhase section */ 32 | 33 | /* Begin PBXFileReference section */ 34 | 0AB42EF411C960DD1E82AB99 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 35 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 36 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 37 | 20317FD878180A696C0B139F /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 38 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 39 | 3B578188023EE5148B662334 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 40 | 63C84D3D8131657DDC13475E /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 41 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 42 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 43 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 44 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 45 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 46 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 48 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 49 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 50 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 51 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 52 | /* End PBXFileReference section */ 53 | 54 | /* Begin PBXFrameworksBuildPhase section */ 55 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 56 | isa = PBXFrameworksBuildPhase; 57 | buildActionMask = 2147483647; 58 | files = ( 59 | 04AC526076737816B913148B /* libPods-Runner.a in Frameworks */, 60 | ); 61 | runOnlyForDeploymentPostprocessing = 0; 62 | }; 63 | /* End PBXFrameworksBuildPhase section */ 64 | 65 | /* Begin PBXGroup section */ 66 | 13F3A92D4D90FBB2A69620D0 /* Frameworks */ = { 67 | isa = PBXGroup; 68 | children = ( 69 | 20317FD878180A696C0B139F /* libPods-Runner.a */, 70 | ); 71 | name = Frameworks; 72 | sourceTree = ""; 73 | }; 74 | 438ECF61B6C2712A8E3914FA /* Pods */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | 3B578188023EE5148B662334 /* Pods-Runner.debug.xcconfig */, 78 | 0AB42EF411C960DD1E82AB99 /* Pods-Runner.release.xcconfig */, 79 | 63C84D3D8131657DDC13475E /* Pods-Runner.profile.xcconfig */, 80 | ); 81 | name = Pods; 82 | path = Pods; 83 | sourceTree = ""; 84 | }; 85 | 9740EEB11CF90186004384FC /* Flutter */ = { 86 | isa = PBXGroup; 87 | children = ( 88 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 89 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 90 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 91 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 92 | ); 93 | name = Flutter; 94 | sourceTree = ""; 95 | }; 96 | 97C146E51CF9000F007C117D = { 97 | isa = PBXGroup; 98 | children = ( 99 | 9740EEB11CF90186004384FC /* Flutter */, 100 | 97C146F01CF9000F007C117D /* Runner */, 101 | 97C146EF1CF9000F007C117D /* Products */, 102 | 438ECF61B6C2712A8E3914FA /* Pods */, 103 | 13F3A92D4D90FBB2A69620D0 /* Frameworks */, 104 | ); 105 | sourceTree = ""; 106 | }; 107 | 97C146EF1CF9000F007C117D /* Products */ = { 108 | isa = PBXGroup; 109 | children = ( 110 | 97C146EE1CF9000F007C117D /* Runner.app */, 111 | ); 112 | name = Products; 113 | sourceTree = ""; 114 | }; 115 | 97C146F01CF9000F007C117D /* Runner */ = { 116 | isa = PBXGroup; 117 | children = ( 118 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 119 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 120 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 121 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 122 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 123 | 97C147021CF9000F007C117D /* Info.plist */, 124 | 97C146F11CF9000F007C117D /* Supporting Files */, 125 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 126 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 127 | ); 128 | path = Runner; 129 | sourceTree = ""; 130 | }; 131 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 132 | isa = PBXGroup; 133 | children = ( 134 | 97C146F21CF9000F007C117D /* main.m */, 135 | ); 136 | name = "Supporting Files"; 137 | sourceTree = ""; 138 | }; 139 | /* End PBXGroup section */ 140 | 141 | /* Begin PBXNativeTarget section */ 142 | 97C146ED1CF9000F007C117D /* Runner */ = { 143 | isa = PBXNativeTarget; 144 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 145 | buildPhases = ( 146 | C8ADB1B1B4FCCE23C1D146AC /* [CP] Check Pods Manifest.lock */, 147 | 9740EEB61CF901F6004384FC /* Run Script */, 148 | 97C146EA1CF9000F007C117D /* Sources */, 149 | 97C146EB1CF9000F007C117D /* Frameworks */, 150 | 97C146EC1CF9000F007C117D /* Resources */, 151 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 152 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 153 | ); 154 | buildRules = ( 155 | ); 156 | dependencies = ( 157 | ); 158 | name = Runner; 159 | productName = Runner; 160 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 161 | productType = "com.apple.product-type.application"; 162 | }; 163 | /* End PBXNativeTarget section */ 164 | 165 | /* Begin PBXProject section */ 166 | 97C146E61CF9000F007C117D /* Project object */ = { 167 | isa = PBXProject; 168 | attributes = { 169 | LastUpgradeCheck = 1300; 170 | ORGANIZATIONNAME = ""; 171 | TargetAttributes = { 172 | 97C146ED1CF9000F007C117D = { 173 | CreatedOnToolsVersion = 7.3.1; 174 | }; 175 | }; 176 | }; 177 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 178 | compatibilityVersion = "Xcode 9.3"; 179 | developmentRegion = en; 180 | hasScannedForEncodings = 0; 181 | knownRegions = ( 182 | en, 183 | Base, 184 | ); 185 | mainGroup = 97C146E51CF9000F007C117D; 186 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 187 | projectDirPath = ""; 188 | projectRoot = ""; 189 | targets = ( 190 | 97C146ED1CF9000F007C117D /* Runner */, 191 | ); 192 | }; 193 | /* End PBXProject section */ 194 | 195 | /* Begin PBXResourcesBuildPhase section */ 196 | 97C146EC1CF9000F007C117D /* Resources */ = { 197 | isa = PBXResourcesBuildPhase; 198 | buildActionMask = 2147483647; 199 | files = ( 200 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 201 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 202 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 203 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | /* End PBXResourcesBuildPhase section */ 208 | 209 | /* Begin PBXShellScriptBuildPhase section */ 210 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 211 | isa = PBXShellScriptBuildPhase; 212 | buildActionMask = 2147483647; 213 | files = ( 214 | ); 215 | inputPaths = ( 216 | ); 217 | name = "Thin Binary"; 218 | outputPaths = ( 219 | ); 220 | runOnlyForDeploymentPostprocessing = 0; 221 | shellPath = /bin/sh; 222 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 223 | }; 224 | 9740EEB61CF901F6004384FC /* Run Script */ = { 225 | isa = PBXShellScriptBuildPhase; 226 | buildActionMask = 2147483647; 227 | files = ( 228 | ); 229 | inputPaths = ( 230 | ); 231 | name = "Run Script"; 232 | outputPaths = ( 233 | ); 234 | runOnlyForDeploymentPostprocessing = 0; 235 | shellPath = /bin/sh; 236 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 237 | }; 238 | C8ADB1B1B4FCCE23C1D146AC /* [CP] Check Pods Manifest.lock */ = { 239 | isa = PBXShellScriptBuildPhase; 240 | buildActionMask = 2147483647; 241 | files = ( 242 | ); 243 | inputFileListPaths = ( 244 | ); 245 | inputPaths = ( 246 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 247 | "${PODS_ROOT}/Manifest.lock", 248 | ); 249 | name = "[CP] Check Pods Manifest.lock"; 250 | outputFileListPaths = ( 251 | ); 252 | outputPaths = ( 253 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 254 | ); 255 | runOnlyForDeploymentPostprocessing = 0; 256 | shellPath = /bin/sh; 257 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 258 | showEnvVarsInLog = 0; 259 | }; 260 | /* End PBXShellScriptBuildPhase section */ 261 | 262 | /* Begin PBXSourcesBuildPhase section */ 263 | 97C146EA1CF9000F007C117D /* Sources */ = { 264 | isa = PBXSourcesBuildPhase; 265 | buildActionMask = 2147483647; 266 | files = ( 267 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 268 | 97C146F31CF9000F007C117D /* main.m in Sources */, 269 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 270 | ); 271 | runOnlyForDeploymentPostprocessing = 0; 272 | }; 273 | /* End PBXSourcesBuildPhase section */ 274 | 275 | /* Begin PBXVariantGroup section */ 276 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 277 | isa = PBXVariantGroup; 278 | children = ( 279 | 97C146FB1CF9000F007C117D /* Base */, 280 | ); 281 | name = Main.storyboard; 282 | sourceTree = ""; 283 | }; 284 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 285 | isa = PBXVariantGroup; 286 | children = ( 287 | 97C147001CF9000F007C117D /* Base */, 288 | ); 289 | name = LaunchScreen.storyboard; 290 | sourceTree = ""; 291 | }; 292 | /* End PBXVariantGroup section */ 293 | 294 | /* Begin XCBuildConfiguration section */ 295 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 296 | isa = XCBuildConfiguration; 297 | buildSettings = { 298 | ALWAYS_SEARCH_USER_PATHS = NO; 299 | CLANG_ANALYZER_NONNULL = YES; 300 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 301 | CLANG_CXX_LIBRARY = "libc++"; 302 | CLANG_ENABLE_MODULES = YES; 303 | CLANG_ENABLE_OBJC_ARC = YES; 304 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 305 | CLANG_WARN_BOOL_CONVERSION = YES; 306 | CLANG_WARN_COMMA = YES; 307 | CLANG_WARN_CONSTANT_CONVERSION = YES; 308 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 309 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 310 | CLANG_WARN_EMPTY_BODY = YES; 311 | CLANG_WARN_ENUM_CONVERSION = YES; 312 | CLANG_WARN_INFINITE_RECURSION = YES; 313 | CLANG_WARN_INT_CONVERSION = YES; 314 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 315 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 316 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 317 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 318 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 319 | CLANG_WARN_STRICT_PROTOTYPES = YES; 320 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 321 | CLANG_WARN_UNREACHABLE_CODE = YES; 322 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 323 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 324 | COPY_PHASE_STRIP = NO; 325 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 326 | ENABLE_NS_ASSERTIONS = NO; 327 | ENABLE_STRICT_OBJC_MSGSEND = YES; 328 | GCC_C_LANGUAGE_STANDARD = gnu99; 329 | GCC_NO_COMMON_BLOCKS = YES; 330 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 331 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 332 | GCC_WARN_UNDECLARED_SELECTOR = YES; 333 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 334 | GCC_WARN_UNUSED_FUNCTION = YES; 335 | GCC_WARN_UNUSED_VARIABLE = YES; 336 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 337 | MTL_ENABLE_DEBUG_INFO = NO; 338 | SDKROOT = iphoneos; 339 | SUPPORTED_PLATFORMS = iphoneos; 340 | TARGETED_DEVICE_FAMILY = "1,2"; 341 | VALIDATE_PRODUCT = YES; 342 | }; 343 | name = Profile; 344 | }; 345 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 346 | isa = XCBuildConfiguration; 347 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 348 | buildSettings = { 349 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 350 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 351 | DEVELOPMENT_TEAM = Q7S7S477W7; 352 | ENABLE_BITCODE = NO; 353 | INFOPLIST_FILE = Runner/Info.plist; 354 | LD_RUNPATH_SEARCH_PATHS = ( 355 | "$(inherited)", 356 | "@executable_path/Frameworks", 357 | ); 358 | PRODUCT_BUNDLE_IDENTIFIER = dev.henryleunghk.flutterNativeTextInputExample; 359 | PRODUCT_NAME = "$(TARGET_NAME)"; 360 | VERSIONING_SYSTEM = "apple-generic"; 361 | }; 362 | name = Profile; 363 | }; 364 | 97C147031CF9000F007C117D /* Debug */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ALWAYS_SEARCH_USER_PATHS = NO; 368 | CLANG_ANALYZER_NONNULL = YES; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 388 | CLANG_WARN_STRICT_PROTOTYPES = YES; 389 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 390 | CLANG_WARN_UNREACHABLE_CODE = YES; 391 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 392 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 393 | COPY_PHASE_STRIP = NO; 394 | DEBUG_INFORMATION_FORMAT = dwarf; 395 | ENABLE_STRICT_OBJC_MSGSEND = YES; 396 | ENABLE_TESTABILITY = YES; 397 | GCC_C_LANGUAGE_STANDARD = gnu99; 398 | GCC_DYNAMIC_NO_PIC = NO; 399 | GCC_NO_COMMON_BLOCKS = YES; 400 | GCC_OPTIMIZATION_LEVEL = 0; 401 | GCC_PREPROCESSOR_DEFINITIONS = ( 402 | "DEBUG=1", 403 | "$(inherited)", 404 | ); 405 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 406 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 407 | GCC_WARN_UNDECLARED_SELECTOR = YES; 408 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 409 | GCC_WARN_UNUSED_FUNCTION = YES; 410 | GCC_WARN_UNUSED_VARIABLE = YES; 411 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 412 | MTL_ENABLE_DEBUG_INFO = YES; 413 | ONLY_ACTIVE_ARCH = YES; 414 | SDKROOT = iphoneos; 415 | TARGETED_DEVICE_FAMILY = "1,2"; 416 | }; 417 | name = Debug; 418 | }; 419 | 97C147041CF9000F007C117D /* Release */ = { 420 | isa = XCBuildConfiguration; 421 | buildSettings = { 422 | ALWAYS_SEARCH_USER_PATHS = NO; 423 | CLANG_ANALYZER_NONNULL = YES; 424 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 425 | CLANG_CXX_LIBRARY = "libc++"; 426 | CLANG_ENABLE_MODULES = YES; 427 | CLANG_ENABLE_OBJC_ARC = YES; 428 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 429 | CLANG_WARN_BOOL_CONVERSION = YES; 430 | CLANG_WARN_COMMA = YES; 431 | CLANG_WARN_CONSTANT_CONVERSION = YES; 432 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 433 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 434 | CLANG_WARN_EMPTY_BODY = YES; 435 | CLANG_WARN_ENUM_CONVERSION = YES; 436 | CLANG_WARN_INFINITE_RECURSION = YES; 437 | CLANG_WARN_INT_CONVERSION = YES; 438 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 439 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 440 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 441 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 442 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 443 | CLANG_WARN_STRICT_PROTOTYPES = YES; 444 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 445 | CLANG_WARN_UNREACHABLE_CODE = YES; 446 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 447 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 448 | COPY_PHASE_STRIP = NO; 449 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 450 | ENABLE_NS_ASSERTIONS = NO; 451 | ENABLE_STRICT_OBJC_MSGSEND = YES; 452 | GCC_C_LANGUAGE_STANDARD = gnu99; 453 | GCC_NO_COMMON_BLOCKS = YES; 454 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 455 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 456 | GCC_WARN_UNDECLARED_SELECTOR = YES; 457 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 458 | GCC_WARN_UNUSED_FUNCTION = YES; 459 | GCC_WARN_UNUSED_VARIABLE = YES; 460 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 461 | MTL_ENABLE_DEBUG_INFO = NO; 462 | SDKROOT = iphoneos; 463 | SUPPORTED_PLATFORMS = iphoneos; 464 | TARGETED_DEVICE_FAMILY = "1,2"; 465 | VALIDATE_PRODUCT = YES; 466 | }; 467 | name = Release; 468 | }; 469 | 97C147061CF9000F007C117D /* Debug */ = { 470 | isa = XCBuildConfiguration; 471 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 472 | buildSettings = { 473 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 474 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 475 | DEVELOPMENT_TEAM = Q7S7S477W7; 476 | ENABLE_BITCODE = NO; 477 | INFOPLIST_FILE = Runner/Info.plist; 478 | LD_RUNPATH_SEARCH_PATHS = ( 479 | "$(inherited)", 480 | "@executable_path/Frameworks", 481 | ); 482 | PRODUCT_BUNDLE_IDENTIFIER = dev.henryleunghk.flutterNativeTextInputExample; 483 | PRODUCT_NAME = "$(TARGET_NAME)"; 484 | VERSIONING_SYSTEM = "apple-generic"; 485 | }; 486 | name = Debug; 487 | }; 488 | 97C147071CF9000F007C117D /* Release */ = { 489 | isa = XCBuildConfiguration; 490 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 491 | buildSettings = { 492 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 493 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 494 | DEVELOPMENT_TEAM = Q7S7S477W7; 495 | ENABLE_BITCODE = NO; 496 | INFOPLIST_FILE = Runner/Info.plist; 497 | LD_RUNPATH_SEARCH_PATHS = ( 498 | "$(inherited)", 499 | "@executable_path/Frameworks", 500 | ); 501 | PRODUCT_BUNDLE_IDENTIFIER = dev.henryleunghk.flutterNativeTextInputExample; 502 | PRODUCT_NAME = "$(TARGET_NAME)"; 503 | VERSIONING_SYSTEM = "apple-generic"; 504 | }; 505 | name = Release; 506 | }; 507 | /* End XCBuildConfiguration section */ 508 | 509 | /* Begin XCConfigurationList section */ 510 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 511 | isa = XCConfigurationList; 512 | buildConfigurations = ( 513 | 97C147031CF9000F007C117D /* Debug */, 514 | 97C147041CF9000F007C117D /* Release */, 515 | 249021D3217E4FDB00AE95B9 /* Profile */, 516 | ); 517 | defaultConfigurationIsVisible = 0; 518 | defaultConfigurationName = Release; 519 | }; 520 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 521 | isa = XCConfigurationList; 522 | buildConfigurations = ( 523 | 97C147061CF9000F007C117D /* Debug */, 524 | 97C147071CF9000F007C117D /* Release */, 525 | 249021D4217E4FDB00AE95B9 /* Profile */, 526 | ); 527 | defaultConfigurationIsVisible = 0; 528 | defaultConfigurationName = Release; 529 | }; 530 | /* End XCConfigurationList section */ 531 | }; 532 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 533 | } 534 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/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.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 | -------------------------------------------------------------------------------- /example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /example/ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | #import "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /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/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/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/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /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/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /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/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/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 | CFBundleDisplayName 8 | Flutter Native Text Input 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | flutter_native_text_input_example 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /example/ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/lib/demo_item.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class DemoItem extends StatelessWidget { 4 | final String? title; 5 | final Widget? child; 6 | 7 | DemoItem({ 8 | this.title, 9 | this.child, 10 | }); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Card( 15 | elevation: 1, 16 | child: Column( 17 | crossAxisAlignment: CrossAxisAlignment.start, 18 | children: [ 19 | Container( 20 | color: Colors.black12, 21 | child: Row(children: [ 22 | Padding( 23 | padding: EdgeInsets.all(8), 24 | child: Text(title!), 25 | ) 26 | ])), 27 | Padding(padding: EdgeInsets.all(8), child: child), 28 | ], 29 | )); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /example/lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/cupertino.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_native_text_input/flutter_native_text_input.dart'; 4 | import 'package:flutter_native_text_input_example/demo_item.dart'; 5 | import 'package:flutter_native_text_input_example/more_use_case_listing_page.dart'; 6 | 7 | void main() => runApp(const MyApp()); 8 | 9 | class MyApp extends StatefulWidget { 10 | const MyApp({Key? key}) : super(key: key); 11 | 12 | @override 13 | _MyAppState createState() => _MyAppState(); 14 | } 15 | 16 | class _MyAppState extends State { 17 | @override 18 | Widget build(BuildContext context) { 19 | return MaterialApp(home: HomePage()); 20 | } 21 | } 22 | 23 | class HomePage extends StatelessWidget { 24 | final FocusNode _focusNode = FocusNode(); 25 | 26 | _onChangeText(value) => debugPrint("_onChangeText: $value"); 27 | _onSubmittedText(value) => debugPrint("_onSubmittedText: $value"); 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: const Text('Demo Page'), 34 | ), 35 | body: ListView( 36 | children: [ 37 | DemoItem( 38 | title: 'Flutter TextField Example Usage', 39 | child: TextField( 40 | onChanged: _onChangeText, 41 | onSubmitted: _onSubmittedText, 42 | autocorrect: true, 43 | decoration: const InputDecoration( 44 | hintText: 'placeholder', 45 | border: InputBorder.none, 46 | ), 47 | ), 48 | ), 49 | DemoItem( 50 | title: 'Flutter CupertinoTextField Example Usage', 51 | child: CupertinoTextField( 52 | autocorrect: true, 53 | cursorColor: Colors.black87, 54 | decoration: BoxDecoration( 55 | border: Border.all( 56 | color: Colors.black87, 57 | width: 2, 58 | ), 59 | ), 60 | style: const TextStyle( 61 | fontSize: 16, 62 | color: Colors.black54, 63 | ), 64 | textCapitalization: TextCapitalization.sentences, 65 | placeholderStyle: const TextStyle( 66 | fontSize: 16, 67 | fontWeight: FontWeight.bold, 68 | color: Colors.black12, 69 | ), 70 | placeholder: 'placeholder', 71 | onChanged: _onChangeText, 72 | onSubmitted: _onSubmittedText, 73 | ), 74 | ), 75 | DemoItem( 76 | title: 'NativeTextInput Example Usage', 77 | child: Container( 78 | height: 30, 79 | child: NativeTextInput( 80 | decoration: BoxDecoration( 81 | border: Border.all( 82 | color: Colors.black87, 83 | width: 2, 84 | ), 85 | ), 86 | style: const TextStyle( 87 | fontSize: 16, 88 | color: Colors.black54, 89 | ), 90 | minHeightPadding: 4, 91 | textCapitalization: TextCapitalization.sentences, 92 | placeholder: "placeholder", 93 | placeholderColor: Colors.black12, 94 | iosOptions: IosOptions( 95 | autocorrect: true, 96 | cursorColor: Colors.black87, 97 | keyboardAppearance: Brightness.dark, 98 | placeholderStyle: const TextStyle( 99 | fontWeight: FontWeight.bold, 100 | ), 101 | ), 102 | keyboardType: KeyboardType.defaultType, 103 | onChanged: _onChangeText, 104 | onSubmitted: _onSubmittedText, 105 | focusNode: _focusNode, 106 | ), 107 | ), 108 | ), 109 | Center( 110 | child: FlatButton( 111 | color: Colors.blue, 112 | colorBrightness: Brightness.dark, 113 | child: const Text("View More Use Cases"), 114 | onPressed: () { 115 | Navigator.of(context).push(MaterialPageRoute( 116 | builder: (_) => MoreUseCaseListingPage())); 117 | }), 118 | ), 119 | ], 120 | ), 121 | ); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /example/lib/more_use_case_listing_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_native_text_input/flutter_native_text_input.dart'; 3 | import 'package:flutter_native_text_input_example/demo_item.dart'; 4 | 5 | class MoreUseCaseListingPage extends StatefulWidget { 6 | const MoreUseCaseListingPage({Key? key}) : super(key: key); 7 | 8 | @override 9 | State createState() => _MoreUseCaseListingPageState(); 10 | } 11 | 12 | class _MoreUseCaseListingPageState extends State { 13 | final FocusNode _focusNode = FocusNode(); 14 | final TextEditingController _changeTextController = TextEditingController(); 15 | 16 | String _currentTextInput = ''; 17 | 18 | _onChangeText(value) => debugPrint("_onChangeText: $value"); 19 | _onSubmittedText(value) => debugPrint("_onSubmittedText: $value"); 20 | _onTap(BuildContext context) { 21 | const snackBar = SnackBar(content: Text('Tapped!')); 22 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 23 | } 24 | 25 | @override 26 | Widget build(BuildContext context) { 27 | return Scaffold( 28 | appBar: AppBar( 29 | title: const Text("More Use Cases"), 30 | ), 31 | body: SingleChildScrollView( 32 | child: Column( 33 | children: [ 34 | DemoItem( 35 | title: "No Placeholder", 36 | child: NativeTextInput( 37 | onChanged: _onChangeText, 38 | onSubmitted: _onSubmittedText, 39 | )), 40 | DemoItem( 41 | title: "Custom font", 42 | child: NativeTextInput( 43 | style: const TextStyle( 44 | fontFamily: 'Noteworthy', 45 | ), 46 | placeholder: 'Noteworthy', 47 | iosOptions: IosOptions( 48 | placeholderStyle: const TextStyle( 49 | fontFamily: 'Noteworthy', 50 | ), 51 | ), 52 | )), 53 | DemoItem( 54 | title: "Numeric Keyboard", 55 | child: Column( 56 | children: [ 57 | NativeTextInput( 58 | keyboardType: KeyboardType.decimalPad, 59 | onChanged: _onChangeText, 60 | onSubmitted: _onSubmittedText, 61 | ), 62 | ], 63 | )), 64 | DemoItem( 65 | title: "Pre-Filling Text", 66 | child: NativeTextInput( 67 | controller: TextEditingController(text: "Text"), 68 | onChanged: _onChangeText, 69 | onSubmitted: _onSubmittedText, 70 | )), 71 | DemoItem( 72 | title: "Aligning Text to End", 73 | child: NativeTextInput( 74 | textAlign: TextAlign.end, 75 | controller: TextEditingController(text: "Text"), 76 | onChanged: _onChangeText, 77 | onSubmitted: _onSubmittedText, 78 | ), 79 | ), 80 | DemoItem( 81 | title: "Multiline Text Input", 82 | child: NativeTextInput( 83 | minLines: 3, 84 | maxLines: 5, 85 | returnKeyType: ReturnKeyType.defaultAction, 86 | onChanged: _onChangeText, 87 | onSubmitted: _onSubmittedText, 88 | )), 89 | DemoItem( 90 | title: "Displaying Input Value", 91 | child: Column(children: [ 92 | NativeTextInput( 93 | placeholder: "Type something here", 94 | onChanged: (value) { 95 | _onChangeText(value); 96 | setState(() { 97 | _currentTextInput = value; 98 | }); 99 | }, 100 | onSubmitted: _onSubmittedText, 101 | ), 102 | Row( 103 | crossAxisAlignment: CrossAxisAlignment.start, 104 | children: [ 105 | const Text('Current Input: '), 106 | Expanded(child: Text(_currentTextInput)), 107 | ], 108 | ) 109 | ]), 110 | ), 111 | DemoItem( 112 | title: "Focusing or Unfocusing Text Input", 113 | child: Column( 114 | children: [ 115 | FlatButton( 116 | color: Colors.blue, 117 | colorBrightness: Brightness.dark, 118 | child: const Text("Tap Me!"), 119 | onPressed: () { 120 | if (_focusNode.hasFocus) { 121 | FocusScope.of(context).unfocus(); 122 | } else { 123 | FocusScope.of(context).requestFocus(_focusNode); 124 | } 125 | }), 126 | NativeTextInput( 127 | focusNode: _focusNode, 128 | onChanged: _onChangeText, 129 | onSubmitted: _onSubmittedText, 130 | ) 131 | ], 132 | )), 133 | DemoItem( 134 | title: "Filling Text Programmatically", 135 | child: Column( 136 | children: [ 137 | FlatButton( 138 | color: Colors.blue, 139 | colorBrightness: Brightness.dark, 140 | child: const Text("Tap Me!"), 141 | onPressed: () => _changeTextController.text = 142 | DateTime.now().toString(), 143 | ), 144 | NativeTextInput( 145 | controller: _changeTextController, 146 | onChanged: _onChangeText, 147 | onSubmitted: _onSubmittedText, 148 | ) 149 | ], 150 | )), 151 | DemoItem( 152 | title: "Recognizing Tap", 153 | child: NativeTextInput( 154 | onTap: () => _onTap(context), 155 | ), 156 | ), 157 | Padding( 158 | padding: const EdgeInsets.all(100), 159 | child: Center( 160 | child: Column(children: const [ 161 | Text( 162 | 'All done!', 163 | style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold), 164 | textAlign: TextAlign.center, 165 | ), 166 | Text( 167 | 'Enjoy using in your way!', 168 | style: TextStyle(fontSize: 17), 169 | textAlign: TextAlign.center, 170 | ) 171 | ])), 172 | ), 173 | ], 174 | ), 175 | )); 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /example/pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.8.2" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.2.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.3.1" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.16.0" 46 | cupertino_icons: 47 | dependency: "direct main" 48 | description: 49 | name: cupertino_icons 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.0.4" 53 | fake_async: 54 | dependency: transitive 55 | description: 56 | name: fake_async 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.3.0" 60 | flutter: 61 | dependency: "direct main" 62 | description: flutter 63 | source: sdk 64 | version: "0.0.0" 65 | flutter_lints: 66 | dependency: "direct dev" 67 | description: 68 | name: flutter_lints 69 | url: "https://pub.dartlang.org" 70 | source: hosted 71 | version: "1.0.4" 72 | flutter_native_text_input: 73 | dependency: "direct main" 74 | description: 75 | path: ".." 76 | relative: true 77 | source: path 78 | version: "2.3.0" 79 | flutter_test: 80 | dependency: "direct dev" 81 | description: flutter 82 | source: sdk 83 | version: "0.0.0" 84 | lints: 85 | dependency: transitive 86 | description: 87 | name: lints 88 | url: "https://pub.dartlang.org" 89 | source: hosted 90 | version: "1.0.1" 91 | matcher: 92 | dependency: transitive 93 | description: 94 | name: matcher 95 | url: "https://pub.dartlang.org" 96 | source: hosted 97 | version: "0.12.11" 98 | material_color_utilities: 99 | dependency: transitive 100 | description: 101 | name: material_color_utilities 102 | url: "https://pub.dartlang.org" 103 | source: hosted 104 | version: "0.1.4" 105 | meta: 106 | dependency: transitive 107 | description: 108 | name: meta 109 | url: "https://pub.dartlang.org" 110 | source: hosted 111 | version: "1.7.0" 112 | path: 113 | dependency: transitive 114 | description: 115 | name: path 116 | url: "https://pub.dartlang.org" 117 | source: hosted 118 | version: "1.8.1" 119 | sky_engine: 120 | dependency: transitive 121 | description: flutter 122 | source: sdk 123 | version: "0.0.99" 124 | source_span: 125 | dependency: transitive 126 | description: 127 | name: source_span 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "1.8.2" 131 | stack_trace: 132 | dependency: transitive 133 | description: 134 | name: stack_trace 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.10.0" 138 | stream_channel: 139 | dependency: transitive 140 | description: 141 | name: stream_channel 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.1.0" 145 | string_scanner: 146 | dependency: transitive 147 | description: 148 | name: string_scanner 149 | url: "https://pub.dartlang.org" 150 | source: hosted 151 | version: "1.1.0" 152 | term_glyph: 153 | dependency: transitive 154 | description: 155 | name: term_glyph 156 | url: "https://pub.dartlang.org" 157 | source: hosted 158 | version: "1.2.0" 159 | test_api: 160 | dependency: transitive 161 | description: 162 | name: test_api 163 | url: "https://pub.dartlang.org" 164 | source: hosted 165 | version: "0.4.9" 166 | vector_math: 167 | dependency: transitive 168 | description: 169 | name: vector_math 170 | url: "https://pub.dartlang.org" 171 | source: hosted 172 | version: "2.1.2" 173 | sdks: 174 | dart: ">=2.17.0-0 <3.0.0" 175 | flutter: ">=2.5.0" 176 | -------------------------------------------------------------------------------- /example/pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_native_text_input_example 2 | description: Demonstrates how to use the flutter_native_text_input plugin. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `flutter pub publish`. This is preferred for private packages. 6 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 7 | 8 | environment: 9 | sdk: ">=2.12.0 <3.0.0" 10 | 11 | # Dependencies specify other packages that your package needs in order to work. 12 | # To automatically upgrade your package dependencies to the latest versions 13 | # consider running `flutter pub upgrade --major-versions`. Alternatively, 14 | # dependencies can be manually updated by changing the version numbers below to 15 | # the latest version available on pub.dev. To see which dependencies have newer 16 | # versions available, run `flutter pub outdated`. 17 | dependencies: 18 | flutter: 19 | sdk: flutter 20 | 21 | flutter_native_text_input: 22 | # When depending on this package from a real application you should use: 23 | # flutter_native_text_input: ^x.y.z 24 | # See https://dart.dev/tools/pub/dependencies#version-constraints 25 | # The example app is bundled with the plugin so we use a path dependency on 26 | # the parent directory to use the current plugin's version. 27 | path: ../ 28 | 29 | # The following adds the Cupertino Icons font to your application. 30 | # Use with the CupertinoIcons class for iOS style icons. 31 | cupertino_icons: ^1.0.2 32 | 33 | dev_dependencies: 34 | flutter_test: 35 | sdk: flutter 36 | 37 | # The "flutter_lints" package below contains a set of recommended lints to 38 | # encourage good coding practices. The lint set provided by the package is 39 | # activated in the `analysis_options.yaml` file located at the root of your 40 | # package. See that file for information about deactivating specific lint 41 | # rules and activating additional ones. 42 | flutter_lints: ^1.0.0 43 | 44 | # For information on the generic Dart part of this file, see the 45 | # following page: https://dart.dev/tools/pub/pubspec 46 | 47 | # The following section is specific to Flutter. 48 | flutter: 49 | 50 | # The following line ensures that the Material Icons font is 51 | # included with your application, so that you can use the icons in 52 | # the material Icons class. 53 | uses-material-design: true 54 | 55 | # To add assets to your application, add an assets section, like this: 56 | # assets: 57 | # - images/a_dot_burr.jpeg 58 | # - images/a_dot_ham.jpeg 59 | 60 | # An image asset can refer to one or more resolution-specific "variants", see 61 | # https://flutter.dev/assets-and-images/#resolution-aware. 62 | 63 | # For details regarding adding assets from package dependencies, see 64 | # https://flutter.dev/assets-and-images/#from-packages 65 | 66 | # To add custom fonts to your application, add a fonts section here, 67 | # in this "flutter" section. Each entry in this list should have a 68 | # "family" key with the font family name, and a "fonts" key with a 69 | # list giving the asset and other descriptors for the font. For 70 | # example: 71 | # fonts: 72 | # - family: Schyler 73 | # fonts: 74 | # - asset: fonts/Schyler-Regular.ttf 75 | # - asset: fonts/Schyler-Italic.ttf 76 | # style: italic 77 | # - family: Trajan Pro 78 | # fonts: 79 | # - asset: fonts/TrajanPro.ttf 80 | # - asset: fonts/TrajanPro_Bold.ttf 81 | # weight: 700 82 | # 83 | # For details regarding fonts from package dependencies, 84 | # see https://flutter.dev/custom-fonts/#from-packages 85 | -------------------------------------------------------------------------------- /flutter_native_text_input.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | .vagrant/ 3 | .sconsign.dblite 4 | .svn/ 5 | 6 | .DS_Store 7 | *.swp 8 | profile 9 | 10 | DerivedData/ 11 | build/ 12 | GeneratedPluginRegistrant.h 13 | GeneratedPluginRegistrant.m 14 | 15 | .generated/ 16 | 17 | *.pbxuser 18 | *.mode1v3 19 | *.mode2v3 20 | *.perspectivev3 21 | 22 | !default.pbxuser 23 | !default.mode1v3 24 | !default.mode2v3 25 | !default.perspectivev3 26 | 27 | xcuserdata 28 | 29 | *.moved-aside 30 | 31 | *.pyc 32 | *sync/ 33 | Icon? 34 | .tags* 35 | 36 | /Flutter/Generated.xcconfig 37 | /Flutter/ephemeral/ 38 | /Flutter/flutter_export_environment.sh -------------------------------------------------------------------------------- /ios/Assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/henryleunghk/flutter-native-text-input/5edaefabb5bec0955bc01cc639a3f2531b82eb0f/ios/Assets/.gitkeep -------------------------------------------------------------------------------- /ios/Classes/NativeTextInput.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | NS_ASSUME_NONNULL_BEGIN 4 | 5 | @interface NativeInputField : NSObject 6 | 7 | - (instancetype)initWithFrame:(CGRect)frame 8 | viewIdentifier:(int64_t)viewId 9 | arguments:(id _Nullable)args 10 | binaryMessenger:(NSObject*)messenger; 11 | 12 | - (UIView*)view; 13 | @end 14 | 15 | NS_ASSUME_NONNULL_END 16 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInput.m: -------------------------------------------------------------------------------- 1 | #import "NativeTextInput.h" 2 | #import "NativeTextInputDelegate.h" 3 | 4 | @interface UITextView(Placeholder) 5 | @property(nullable, nonatomic, copy) NSAttributedString *attributedPlaceholder; 6 | @end 7 | 8 | @implementation NativeInputField { 9 | UITextView* _textView; 10 | 11 | int64_t _viewId; 12 | FlutterMethodChannel* _channel; 13 | NativeTextInputDelegate* _delegate; 14 | id _Nullable _args; 15 | 16 | float _containerWidth; 17 | } 18 | 19 | 20 | - (instancetype)initWithFrame:(CGRect)frame 21 | viewIdentifier:(int64_t)viewId 22 | arguments:(id _Nullable)args 23 | binaryMessenger:(NSObject*)messenger { 24 | 25 | if ([super init]) { 26 | NSString* channelName = [NSString stringWithFormat:@"flutter_native_text_input%lld", viewId]; 27 | _channel = [FlutterMethodChannel methodChannelWithName:channelName binaryMessenger:messenger]; 28 | 29 | _viewId = viewId; 30 | _args = args; 31 | 32 | _textView = [[UITextView alloc] initWithFrame:frame]; 33 | _textView.backgroundColor = UIColor.clearColor; 34 | _textView.keyboardAppearance = [self keyboardAppearanceFromString:args[@"keyboardAppearance"]]; 35 | _textView.keyboardType = [self keyboardTypeFromString:args[@"keyboardType"]]; 36 | _textView.returnKeyType = [self returnKeyTypeFromString:args[@"returnKeyType"]]; 37 | _textView.textAlignment = [self textAlignmentFromString:args[@"textAlign"]]; 38 | _textView.autocapitalizationType = [self textAutocapitalizationTypeFromString:args[@"textCapitalization"]]; 39 | _textView.textContainer.lineBreakMode = NSLineBreakByCharWrapping; 40 | 41 | if ([args[@"maxLines"] intValue] == 1) { 42 | _textView.textContainer.maximumNumberOfLines = 1; 43 | } 44 | float minHeightPadding = [args[@"minHeightPadding"] floatValue]; 45 | [_textView setTextContainerInset: UIEdgeInsetsMake(minHeightPadding / 2, 0, minHeightPadding / 2, 0)]; 46 | if (@available(iOS 10.0, *)) { 47 | _textView.textContentType = [self textContentTypeFromString:args[@"textContentType"]]; 48 | } 49 | if (args[@"cursorColor"] && ![args[@"cursorColor"] isKindOfClass:[NSNull class]]) { 50 | NSDictionary* fontColor = args[@"cursorColor"]; 51 | _textView.tintColor = [UIColor colorWithRed:[fontColor[@"red"] floatValue]/255.0 green:[fontColor[@"green"] floatValue]/255.0 blue:[fontColor[@"blue"] floatValue]/255.0 alpha:[fontColor[@"alpha"] floatValue]/255.0]; 52 | } 53 | if (args[@"autocorrect"] && ![args[@"autocorrect"] isKindOfClass:[NSNull class]]) { 54 | _textView.autocorrectionType = [args[@"autocorrect"] boolValue] 55 | ? UITextAutocorrectionTypeYes 56 | : UITextAutocorrectionTypeNo; 57 | } 58 | 59 | _delegate = [[NativeTextInputDelegate alloc] initWithChannel:_channel arguments:args ]; 60 | _textView.delegate = _delegate; 61 | 62 | _textView.text = args[@"text"]; 63 | _textView.textColor = _delegate.fontColor; 64 | _textView.font = _delegate.font; 65 | NSMutableDictionary *attributes = [NSMutableDictionary dictionary]; 66 | if (args[@"placeholder"] && ![args[@"placeholder"] isKindOfClass:[NSNull class]]) { 67 | if (_delegate.placeholderFont) { 68 | [attributes setObject:_delegate.placeholderFont forKey:NSFontAttributeName]; 69 | } 70 | if (_delegate.placeholderFontColor) { 71 | [attributes setObject:_delegate.placeholderFontColor forKey:NSForegroundColorAttributeName]; 72 | } 73 | _textView.attributedPlaceholder = [[NSAttributedString alloc] initWithString:args[@"placeholder"] 74 | attributes:attributes]; 75 | } 76 | 77 | if ([args[@"onTap"] boolValue]) { 78 | UITapGestureRecognizer *singleTap = [[UITapGestureRecognizer alloc] initWithTarget:_delegate 79 | action:@selector(singleTapRecognized:)]; 80 | singleTap.delegate = _delegate; 81 | singleTap.numberOfTapsRequired = 1; 82 | [_textView addGestureRecognizer:singleTap]; 83 | } 84 | 85 | _containerWidth = [args[@"width"] floatValue]; 86 | 87 | __weak __typeof__(self) weakSelf = self; 88 | [_channel setMethodCallHandler:^(FlutterMethodCall* call, FlutterResult result) { 89 | [weakSelf onMethodCall:call result:result]; 90 | }]; 91 | } 92 | return self; 93 | } 94 | 95 | - (void)onMethodCall:(FlutterMethodCall*)call result:(FlutterResult)result { 96 | if ([[call method] isEqualToString:@"getContentHeight"]) { 97 | CGSize boundSize = CGSizeMake(_containerWidth, MAXFLOAT); 98 | CGSize size = [_textView sizeThatFits: boundSize]; 99 | result([NSNumber numberWithFloat: size.height]); 100 | } else if ([[call method] isEqualToString:@"getLineHeight"]) { 101 | result([NSNumber numberWithFloat: _textView.font.lineHeight]); 102 | } else if ([[call method] isEqualToString:@"unfocus"]) { 103 | [self onUnFocus:call result:result]; 104 | } else if ([[call method] isEqualToString:@"focus"]) { 105 | [self onFocus:call result:result]; 106 | } else if ([[call method] isEqualToString:@"setText"]) { 107 | [self onSetText:call result:result]; 108 | } else { 109 | result(FlutterMethodNotImplemented); 110 | } 111 | } 112 | 113 | - (void)onFocus:(FlutterMethodCall*)call result:(FlutterResult)result { 114 | [_textView becomeFirstResponder]; 115 | result(nil); 116 | } 117 | 118 | - (void)onUnFocus:(FlutterMethodCall*)call result:(FlutterResult)result { 119 | [_textView resignFirstResponder]; 120 | 121 | result(nil); 122 | } 123 | 124 | - (void)onSetText:(FlutterMethodCall*)call result:(FlutterResult)result { 125 | _textView.text = call.arguments[@"text"]; 126 | _textView.textColor = _delegate.fontColor; 127 | _textView.font = _delegate.font; 128 | 129 | if (_textView.textContainer.maximumNumberOfLines == 1) { 130 | _textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail; 131 | } 132 | result(nil); 133 | } 134 | 135 | - (UIView*)view { 136 | return _textView; 137 | } 138 | 139 | - (UIKeyboardAppearance)keyboardAppearanceFromString:(NSString*)keyboardAppearance { 140 | if (!keyboardAppearance || [keyboardAppearance isKindOfClass:[NSNull class]]) { 141 | return UIKeyboardAppearanceDefault; 142 | } 143 | if ([keyboardAppearance isEqualToString:@"Brightness.dark"]) { 144 | return UIKeyboardAppearanceDark; 145 | } else if ([keyboardAppearance isEqualToString:@"Brightness.light"]) { 146 | return UIKeyboardAppearanceLight; 147 | } 148 | 149 | return UIKeyboardAppearanceDefault; 150 | } 151 | 152 | - (UIKeyboardType)keyboardTypeFromString:(NSString*)keyboardType { 153 | if (!keyboardType || [keyboardType isKindOfClass:[NSNull class]]) { 154 | return UIKeyboardTypeDefault; 155 | } 156 | 157 | if ([keyboardType isEqualToString:@"KeyboardType.asciiCapable"]) { 158 | return UIKeyboardTypeASCIICapable; 159 | } 160 | else if ([keyboardType isEqualToString:@"KeyboardType.numbersAndPunctuation"]) { 161 | return UIKeyboardTypeNumbersAndPunctuation; 162 | } 163 | else if ([keyboardType isEqualToString:@"KeyboardType.url"]) { 164 | return UIKeyboardTypeURL; 165 | } 166 | else if ([keyboardType isEqualToString:@"KeyboardType.numberPad"]) { 167 | return UIKeyboardTypeNumberPad; 168 | } 169 | else if ([keyboardType isEqualToString:@"KeyboardType.phonePad"]) { 170 | return UIKeyboardTypePhonePad; 171 | } 172 | else if ([keyboardType isEqualToString:@"KeyboardType.namePhonePad"]) { 173 | return UIKeyboardTypeNamePhonePad; 174 | } 175 | else if ([keyboardType isEqualToString:@"KeyboardType.emailAddress"]) { 176 | return UIKeyboardTypeEmailAddress; 177 | } 178 | else if ([keyboardType isEqualToString:@"KeyboardType.decimalPad"]) { 179 | return UIKeyboardTypeDecimalPad; 180 | } 181 | else if ([keyboardType isEqualToString:@"KeyboardType.twitter"]) { 182 | return UIKeyboardTypeTwitter; 183 | } 184 | else if ([keyboardType isEqualToString:@"KeyboardType.webSearch"]) { 185 | return UIKeyboardTypeWebSearch; 186 | } 187 | else if ([keyboardType isEqualToString:@"KeyboardType.asciiCapableNumberPad"]) { 188 | if (@available(iOS 10.0, *)) { 189 | return UIKeyboardTypeASCIICapableNumberPad; 190 | } else { 191 | return UIKeyboardTypeNumberPad; 192 | } 193 | } 194 | 195 | return UIKeyboardTypeDefault; 196 | } 197 | 198 | - (UIReturnKeyType)returnKeyTypeFromString:(NSString *)returnKeyType { 199 | if (!returnKeyType || [returnKeyType isKindOfClass:[NSNull class]]) { 200 | return UIReturnKeyDefault; 201 | } 202 | 203 | if ([returnKeyType isEqualToString:@"ReturnKeyType.defaultAction"]) { 204 | return UIReturnKeyDefault; 205 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.go"]) { 206 | return UIReturnKeyGo; 207 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.google"]) { 208 | return UIReturnKeyGoogle; 209 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.join"]) { 210 | return UIReturnKeyJoin; 211 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.next"]) { 212 | return UIReturnKeyNext; 213 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.route"]) { 214 | return UIReturnKeyRoute; 215 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.search"]) { 216 | return UIReturnKeySearch; 217 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.send"]) { 218 | return UIReturnKeySend; 219 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.yahoo"]) { 220 | return UIReturnKeyYahoo; 221 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.done"]) { 222 | return UIReturnKeyDone; 223 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.emergencyCall"]) { 224 | return UIReturnKeyEmergencyCall; 225 | } else if ([returnKeyType isEqualToString:@"ReturnKeyType.continueAction"]) { 226 | return UIReturnKeyContinue; 227 | } 228 | 229 | return UIReturnKeyDefault; 230 | } 231 | 232 | - (UITextAutocapitalizationType)textAutocapitalizationTypeFromString:(NSString *)textCapitalization { 233 | if (!textCapitalization || [textCapitalization isKindOfClass:[NSNull class]]) { 234 | return UITextAutocapitalizationTypeNone; 235 | } 236 | 237 | if ([textCapitalization isEqualToString:@"TextCapitalization.none"]) { 238 | return UITextAutocapitalizationTypeNone; 239 | } else if ([textCapitalization isEqualToString:@"TextCapitalization.characters"]) { 240 | return UITextAutocapitalizationTypeAllCharacters; 241 | } else if ([textCapitalization isEqualToString:@"TextCapitalization.sentences"]) { 242 | return UITextAutocapitalizationTypeSentences; 243 | } else if ([textCapitalization isEqualToString:@"TextCapitalization.words"]) { 244 | return UITextAutocapitalizationTypeWords; 245 | } 246 | 247 | return UITextAutocapitalizationTypeNone; 248 | } 249 | 250 | - (UITextContentType)textContentTypeFromString:(NSString *)contentType { 251 | if (!contentType || [contentType isKindOfClass:[NSNull class]]) { 252 | return nil; 253 | } 254 | 255 | if (@available(iOS 10.0, *)) { 256 | 257 | if ([contentType isEqualToString:@"TextContentType.username"]) { 258 | if (@available(iOS 11.0, *)) { 259 | return UITextContentTypeUsername; 260 | } else { 261 | return nil; 262 | } 263 | } 264 | 265 | if ([contentType isEqualToString:@"TextContentType.password"]) { 266 | if (@available(iOS 11.0, *)) { 267 | return UITextContentTypePassword; 268 | } else { 269 | return nil; 270 | } 271 | } 272 | 273 | if ([contentType isEqualToString:@"TextContentType.newPassword"]) { 274 | if (@available(iOS 12.0, *)) { 275 | return UITextContentTypeNewPassword; 276 | } else if (@available(iOS 11.0, *)) { 277 | return UITextContentTypePassword; 278 | } else { 279 | return nil; 280 | } 281 | } 282 | 283 | if ([contentType isEqualToString:@"TextContentType.oneTimeCode"]) { 284 | if (@available(iOS 12.0, *)) { 285 | return UITextContentTypeOneTimeCode; 286 | } else { 287 | return nil; 288 | } 289 | } 290 | 291 | NSDictionary *dict = 292 | @{ 293 | @"TextContentType.name": UITextContentTypeName, 294 | @"TextContentType.namePrefix": UITextContentTypeNamePrefix, 295 | @"TextContentType.givenName": UITextContentTypeGivenName, 296 | @"TextContentType.middleName": UITextContentTypeMiddleName, 297 | @"TextContentType.familyName": UITextContentTypeFamilyName, 298 | @"TextContentType.nameSuffix": UITextContentTypeNameSuffix, 299 | @"TextContentType.nickname": UITextContentTypeNickname, 300 | @"TextContentType.jobTitle": UITextContentTypeJobTitle, 301 | @"TextContentType.organizationName": UITextContentTypeOrganizationName, 302 | @"TextContentType.location": UITextContentTypeLocation, 303 | @"TextContentType.fullStreetAddress": UITextContentTypeFullStreetAddress, 304 | @"TextContentType.streetAddressLine1": UITextContentTypeStreetAddressLine1, 305 | @"TextContentType.streetAddressLine2": UITextContentTypeStreetAddressLine2, 306 | @"TextContentType.city": UITextContentTypeAddressCity, 307 | @"TextContentType.addressState": UITextContentTypeAddressState, 308 | @"TextContentType.addressCityAndState": UITextContentTypeAddressCityAndState, 309 | @"TextContentType.sublocality": UITextContentTypeSublocality, 310 | @"TextContentType.countryName": UITextContentTypeCountryName, 311 | @"TextContentType.postalCode": UITextContentTypePostalCode, 312 | @"TextContentType.telephoneNumber": UITextContentTypeTelephoneNumber, 313 | @"TextContentType.emailAddress": UITextContentTypeEmailAddress, 314 | @"TextContentType.url": UITextContentTypeURL, 315 | @"TextContentType.creditCardNumber": UITextContentTypeCreditCardNumber 316 | }; 317 | 318 | return dict[contentType]; 319 | } else { 320 | return nil; 321 | } 322 | } 323 | 324 | - (NSTextAlignment)textAlignmentFromString:(NSString*)textAlignment { 325 | if (!textAlignment || [textAlignment isKindOfClass:[NSNull class]]) { 326 | return NSTextAlignmentNatural; 327 | } 328 | 329 | if ([textAlignment isEqualToString:@"TextAlign.left"]) { 330 | return NSTextAlignmentLeft; 331 | } else if ([textAlignment isEqualToString:@"TextAlign.right"]) { 332 | return NSTextAlignmentRight; 333 | } else if ([textAlignment isEqualToString:@"TextAlign.center"]) { 334 | return NSTextAlignmentCenter; 335 | } else if ([textAlignment isEqualToString:@"TextAlign.justify"]) { 336 | return NSTextAlignmentJustified; 337 | } else if ([textAlignment isEqualToString:@"TextAlign.end"]) { 338 | return ([self layoutDirection] == UIUserInterfaceLayoutDirectionLeftToRight) 339 | ? NSTextAlignmentRight 340 | : NSTextAlignmentLeft; 341 | } 342 | 343 | // TextAlign.start 344 | return NSTextAlignmentNatural; 345 | } 346 | 347 | - (UIUserInterfaceLayoutDirection)layoutDirection { 348 | if (@available(iOS 9.0, *)) { 349 | return [UIView userInterfaceLayoutDirectionForSemanticContentAttribute:_textView.semanticContentAttribute]; 350 | } 351 | 352 | return UIApplication.sharedApplication.userInterfaceLayoutDirection; 353 | } 354 | 355 | @end 356 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInputDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | @interface NativeTextInputDelegate : NSObject 7 | 8 | - (instancetype)initWithChannel:(FlutterMethodChannel*)channel arguments:(id _Nullable)args; 9 | - (UIColor *)fontColor; 10 | - (UIFont *)font; 11 | - (UIColor *)placeholderFontColor; 12 | - (UIFont *)placeholderFont; 13 | 14 | @end 15 | 16 | NS_ASSUME_NONNULL_END 17 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInputDelegate.m: -------------------------------------------------------------------------------- 1 | #import "NativeTextInputDelegate.h" 2 | 3 | @implementation NativeTextInputDelegate { 4 | FlutterMethodChannel* _channel; 5 | id _Nullable _args; 6 | 7 | float _fontSize; 8 | NSString *_fontFamily; 9 | UIFontWeight _fontWeight; 10 | UIColor* _fontColor; 11 | 12 | float _placeholderFontSize; 13 | NSString *_placeholderFontFamily; 14 | UIFontWeight _placeholderFontWeight; 15 | UIColor* _placeholderFontColor; 16 | } 17 | 18 | - (instancetype)initWithChannel:(FlutterMethodChannel*)channel arguments:(id _Nullable)args { 19 | self = [super init]; 20 | 21 | _fontSize = 16.0; 22 | _fontWeight = UIFontWeightRegular; 23 | _fontColor = UIColor.blackColor; 24 | 25 | _placeholderFontSize = 16.0; 26 | _placeholderFontWeight = UIFontWeightRegular; 27 | _placeholderFontColor = UIColor.lightGrayColor; 28 | 29 | if (args[@"fontSize"] && ![args[@"fontSize"] isKindOfClass:[NSNull class]]) { 30 | NSNumber* fontSize = args[@"fontSize"]; 31 | _fontSize = [fontSize floatValue]; 32 | _placeholderFontSize = _fontSize; 33 | } 34 | if (args[@"fontFamily"] && ![args[@"fontFamily"] isKindOfClass:[NSNull class]]) { 35 | _fontFamily = args[@"fontFamily"]; 36 | } 37 | if (args[@"fontWeight"] && ![args[@"fontWeight"] isKindOfClass:[NSNull class]]) { 38 | _fontWeight = [self fontWeightFromString:args[@"fontWeight"]]; 39 | } 40 | if (args[@"fontColor"] && ![args[@"fontColor"] isKindOfClass:[NSNull class]]) { 41 | NSDictionary* fontColor = args[@"fontColor"]; 42 | _fontColor = [UIColor colorWithRed:[fontColor[@"red"] floatValue]/255.0 green:[fontColor[@"green"] floatValue]/255.0 blue:[fontColor[@"blue"] floatValue]/255.0 alpha:[fontColor[@"alpha"] floatValue]/255.0]; 43 | } 44 | if (args[@"placeholderFontSize"] && ![args[@"placeholderFontSize"] isKindOfClass:[NSNull class]]) { 45 | NSNumber* placeholderFontSize = args[@"placeholderFontSize"]; 46 | _placeholderFontSize = [placeholderFontSize floatValue]; 47 | } 48 | if (args[@"placeholderFontFamily"] && ![args[@"placeholderFontFamily"] isKindOfClass:[NSNull class]]) { 49 | _placeholderFontFamily = args[@"placeholderFontFamily"]; 50 | } 51 | if (args[@"placeholderFontWeight"] && ![args[@"placeholderFontWeight"] isKindOfClass:[NSNull class]]) { 52 | _placeholderFontWeight = [self fontWeightFromString:args[@"placeholderFontWeight"]]; 53 | } 54 | if (args[@"placeholderFontColor"] && ![args[@"placeholderFontColor"] isKindOfClass:[NSNull class]]) { 55 | NSDictionary* placeholderFontColor = args[@"placeholderFontColor"]; 56 | _placeholderFontColor = [UIColor colorWithRed:[placeholderFontColor[@"red"] floatValue]/255.0 green:[placeholderFontColor[@"green"] floatValue]/255.0 blue:[placeholderFontColor[@"blue"] floatValue]/255.0 alpha:[placeholderFontColor[@"alpha"] floatValue]/255.0]; 57 | } 58 | 59 | if (self) { 60 | _channel = channel; 61 | _args = args; 62 | } 63 | return self; 64 | } 65 | 66 | - (UIColor *)fontColor { 67 | return _fontColor; 68 | } 69 | 70 | - (UIFont *)font { 71 | if (_fontFamily) { 72 | return [UIFont fontWithName:_fontFamily size:_fontSize]; 73 | } else { 74 | return [UIFont systemFontOfSize:_fontSize weight:_fontWeight]; 75 | } 76 | } 77 | 78 | - (UIColor *)placeholderFontColor { 79 | return _placeholderFontColor; 80 | } 81 | 82 | - (UIFont *)placeholderFont { 83 | if (_placeholderFontFamily) { 84 | return [UIFont fontWithName:_placeholderFontFamily size:_placeholderFontSize]; 85 | } else { 86 | return [UIFont systemFontOfSize:_placeholderFontSize weight:_placeholderFontWeight]; 87 | } 88 | } 89 | 90 | - (UIFontWeight)fontWeightFromString:(NSString*)fontWeight { 91 | if (!fontWeight || [fontWeight isKindOfClass:[NSNull class]]) { 92 | return UIFontWeightRegular; 93 | } 94 | if ([fontWeight isEqualToString:@"FontWeight.w100"]) { 95 | return UIFontWeightUltraLight; 96 | } else if ([fontWeight isEqualToString:@"FontWeight.w200"]) { 97 | return UIFontWeightThin; 98 | } else if ([fontWeight isEqualToString:@"FontWeight.w300"]) { 99 | return UIFontWeightLight; 100 | } else if ([fontWeight isEqualToString:@"FontWeight.w400"]) { 101 | return UIFontWeightRegular; 102 | } else if ([fontWeight isEqualToString:@"FontWeight.w500"]) { 103 | return UIFontWeightMedium; 104 | } else if ([fontWeight isEqualToString:@"FontWeight.w600"]) { 105 | return UIFontWeightSemibold; 106 | } else if ([fontWeight isEqualToString:@"FontWeight.w700"]) { 107 | return UIFontWeightBold; 108 | } else if ([fontWeight isEqualToString:@"FontWeight.w800"]) { 109 | return UIFontWeightHeavy; 110 | } else if ([fontWeight isEqualToString:@"FontWeight.w900"]) { 111 | return UIFontWeightBlack; 112 | } 113 | 114 | return UIFontWeightRegular; 115 | } 116 | 117 | - (void)textViewDidBeginEditing:(UITextView *)textView { 118 | if (textView.textContainer.maximumNumberOfLines == 1) { 119 | textView.textContainer.lineBreakMode = NSLineBreakByCharWrapping; 120 | } 121 | 122 | [_channel invokeMethod:@"inputStarted" 123 | arguments:nil]; 124 | } 125 | 126 | - (void)textViewDidChange:(UITextView *)textView { 127 | [_channel invokeMethod:@"inputValueChanged" arguments:@{ @"text": textView.text }]; 128 | } 129 | 130 | - (void)textViewDidEndEditing:(UITextView *)textView { 131 | if (textView.textContainer.maximumNumberOfLines == 1) { 132 | textView.textContainer.lineBreakMode = NSLineBreakByTruncatingTail; 133 | } 134 | } 135 | 136 | - (BOOL)textView:(UITextView *)textView shouldChangeTextInRange:(NSRange)range replacementText:(NSString *)text { 137 | if ((textView.returnKeyType != UIReturnKeyDefault || 138 | textView.textContainer.maximumNumberOfLines == 1) && 139 | [text isEqualToString:@"\n"] 140 | ) { 141 | [_channel invokeMethod:@"inputFinished" 142 | arguments:@{ @"text": textView.text }]; 143 | return false; 144 | } 145 | return true; 146 | } 147 | 148 | - (void)singleTapRecognized:(UIGestureRecognizer *)gestureRecognizer { 149 | [_channel invokeMethod:@"singleTapRecognized" arguments:@{}]; 150 | } 151 | 152 | #pragma mark - Gesture recognizer delegate 153 | 154 | - (BOOL)gestureRecognizer:(UIGestureRecognizer *)gestureRecognizer shouldRecognizeSimultaneouslyWithGestureRecognizer:(UIGestureRecognizer *)otherGestureRecognizer { 155 | return YES; 156 | } 157 | 158 | @end 159 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInputFactory.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | NS_ASSUME_NONNULL_BEGIN 5 | 6 | @interface NativeTextInputFactory : NSObject 7 | - (instancetype)initWithMessenger:(NSObject*)messenger; 8 | @end 9 | 10 | NS_ASSUME_NONNULL_END 11 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInputFactory.m: -------------------------------------------------------------------------------- 1 | #import "NativeTextInputFactory.h" 2 | #import "NativeTextInput.h" 3 | 4 | @implementation NativeTextInputFactory { 5 | NSObject* _messenger; 6 | } 7 | 8 | - (instancetype)initWithMessenger:(NSObject*)messenger { 9 | self = [super init]; 10 | if (self) { 11 | _messenger = messenger; 12 | } 13 | return self; 14 | } 15 | 16 | - (NSObject*)createArgsCodec { 17 | return [FlutterStandardMessageCodec sharedInstance]; 18 | } 19 | 20 | - (NSObject*)createWithFrame:(CGRect)frame 21 | viewIdentifier:(int64_t)viewId 22 | arguments:(id _Nullable)args { 23 | NativeInputField* textField = [[NativeInputField alloc] initWithFrame:frame 24 | viewIdentifier:viewId 25 | arguments:args 26 | binaryMessenger:_messenger]; 27 | return textField; 28 | } 29 | 30 | @end 31 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInputPlugin.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface NativeTextInputPlugin : NSObject 4 | @end 5 | -------------------------------------------------------------------------------- /ios/Classes/NativeTextInputPlugin.m: -------------------------------------------------------------------------------- 1 | #import "NativeTextInputPlugin.h" 2 | #import "NativeTextInputFactory.h" 3 | 4 | @implementation NativeTextInputPlugin 5 | 6 | + (void)registerWithRegistrar:(NSObject*)registrar { 7 | NativeTextInputFactory* textFieldFactory = 8 | [[NativeTextInputFactory alloc] initWithMessenger:registrar.messenger]; 9 | 10 | [registrar registerViewFactory:textFieldFactory 11 | withId:@"flutter_native_text_input"]; 12 | } 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ios/flutter_native_text_input.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 3 | # 4 | Pod::Spec.new do |s| 5 | s.name = 'flutter_native_text_input' 6 | s.version = '2.2.0' 7 | s.summary = 'Native text input for Flutter' 8 | s.description = <<-DESC 9 | Native text input for Flutter 10 | DESC 11 | s.homepage = 'https://github.com/henryleunghk/flutter-native-text-input' 12 | s.license = { :file => '../LICENSE' } 13 | s.author = { 'Henry Leung' => 'hk.eleven@gmail.com' } 14 | s.source = { :path => '.' } 15 | s.source_files = 'Classes/**/*' 16 | s.public_header_files = 'Classes/**/*.h' 17 | s.dependency 'Flutter' 18 | 19 | s.ios.deployment_target = '9.0' 20 | end 21 | 22 | -------------------------------------------------------------------------------- /lib/flutter_native_text_input.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/cupertino.dart'; 4 | import 'package:flutter/foundation.dart'; 5 | import 'package:flutter/gestures.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/rendering.dart'; 8 | import 'package:flutter/scheduler.dart'; 9 | import 'package:flutter/services.dart'; 10 | 11 | enum ReturnKeyType { 12 | defaultAction, 13 | go, 14 | google, 15 | join, 16 | next, 17 | route, 18 | search, 19 | send, 20 | yahoo, 21 | done, 22 | emergencyCall, 23 | continueAction, 24 | } 25 | 26 | enum TextContentType { 27 | name, 28 | namePrefix, 29 | givenName, 30 | middleName, 31 | familyName, 32 | nameSuffix, 33 | nickname, 34 | jobTitle, 35 | organizationName, 36 | location, 37 | fullStreetAddress, 38 | streetAddressLine1, 39 | streetAddressLine2, 40 | addressCity, 41 | addressState, 42 | addressCityAndState, 43 | sublocality, 44 | countryName, 45 | postalCode, 46 | telephoneNumber, 47 | emailAddress, 48 | url, 49 | creditCardNumber, 50 | username, 51 | password, 52 | newPassword, // iOS12+ 53 | oneTimeCode // iOS12+ 54 | } 55 | 56 | enum KeyboardType { 57 | /// Default type for the current input method. 58 | defaultType, 59 | 60 | /// Displays a keyboard which can enter ASCII characters 61 | asciiCapable, 62 | 63 | /// Numbers and assorted punctuation. 64 | numbersAndPunctuation, 65 | 66 | /// A type optimized for URL entry (shows . / .com prominently). 67 | url, 68 | 69 | /// A number pad with locale-appropriate digits (0-9, ۰-۹, ०-९, etc.). Suitable for PIN 70 | numberPad, 71 | 72 | /// A phone pad (1-9, *, 0, #, with letters under the numbers). 73 | phonePad, 74 | 75 | /// A type optimized for entering a person's name or phone number. 76 | namePhonePad, 77 | 78 | /// A type optimized for multiple email address entry (shows space @ . prominently). 79 | emailAddress, 80 | 81 | /// A number pad with a decimal point. iOS 4.1+. 82 | decimalPad, 83 | 84 | /// A type optimized for twitter text entry (easy access to @ #). 85 | twitter, 86 | 87 | /// A default keyboard type with URL-oriented addition (shows space . prominently). 88 | webSearch, 89 | 90 | // A number pad (0-9) that will always be ASCII digits. Falls back to KeyboardType.numberPad below iOS 10. 91 | asciiCapableNumberPad 92 | } 93 | 94 | class NativeTextInput extends StatefulWidget { 95 | static const viewType = 'flutter_native_text_input'; 96 | 97 | const NativeTextInput({ 98 | Key? key, 99 | this.controller, 100 | this.decoration, 101 | this.focusNode, 102 | this.iosOptions, 103 | this.keyboardType = KeyboardType.defaultType, 104 | this.maxLines = 1, 105 | this.minHeightPadding = 18, 106 | this.minLines = 1, 107 | this.placeholder, 108 | this.placeholderColor, 109 | this.returnKeyType = ReturnKeyType.done, 110 | this.style, 111 | this.textAlign = TextAlign.start, 112 | this.textCapitalization = TextCapitalization.none, 113 | this.textContentType, 114 | this.onChanged, 115 | this.onEditingComplete, 116 | this.onSubmitted, 117 | this.onTap, 118 | }) : super(key: key); 119 | 120 | /// Controlling the text being edited 121 | /// (https://api.flutter.dev/flutter/material/TextField/controller.html) 122 | /// 123 | /// Default: null, this widget will create its own [TextEditingController]. 124 | final TextEditingController? controller; 125 | 126 | /// Controls the BoxDecoration of the box behind the text input 127 | /// (https://api.flutter.dev/flutter/cupertino/CupertinoTextField/decoration.html) 128 | /// 129 | /// Default: null 130 | final BoxDecoration? decoration; 131 | 132 | /// Defines the keyboard focus for this widget 133 | /// (https://api.flutter.dev/flutter/material/TextField/focusNode.html) 134 | /// 135 | /// Default: null 136 | final FocusNode? focusNode; 137 | 138 | /// iOS only options (cursorColor, keyboardAppearance) 139 | /// 140 | /// Default: null 141 | final IosOptions? iosOptions; 142 | 143 | /// Type of keyboard to display for a given text-based view 144 | /// (https://developer.apple.com/documentation/uikit/uikeyboardtype) 145 | /// 146 | /// Default: KeyboardType.defaultType 147 | final KeyboardType keyboardType; 148 | 149 | /// Extra vertical spacing added in addition to line height for iOS UITextView to fit well in single line mode 150 | /// Note: Text content height would be used instead if it is greater than this value 151 | /// 152 | /// Default: 18.0 153 | final double minHeightPadding; 154 | 155 | /// The maximum number of lines to show at one time, wrapping if necessary 156 | /// (https://api.flutter.dev/flutter/material/TextField/maxLines.html) 157 | /// 158 | /// Default: 1 159 | final int maxLines; 160 | 161 | /// Minimum number of lines of text input widget 162 | /// 163 | /// Default: 1 164 | final int minLines; 165 | 166 | /// Placeholder text when text entry is empty 167 | /// (https://api.flutter.dev/flutter/cupertino/CupertinoTextField/placeholder.html) 168 | /// 169 | /// Default: null 170 | final String? placeholder; 171 | 172 | /// The text color to use for the placeholder text 173 | /// 174 | /// Default: null 175 | final Color? placeholderColor; 176 | 177 | /// Constants that specify the text string that displays in the Return key of a keyboard 178 | /// (https://developer.apple.com/documentation/uikit/uireturnkeytype) 179 | /// 180 | /// Default: ReturnKeyType.defaultAction 181 | final ReturnKeyType returnKeyType; 182 | 183 | /// The style to use for the text being edited [Only `fontSize`, `fontWeight`, `color` are supported] 184 | /// (https://api.flutter.dev/flutter/material/TextField/style.html) 185 | /// 186 | /// Default: null 187 | final TextStyle? style; 188 | 189 | /// How the text should be aligned horizontally 190 | /// (https://api.flutter.dev/flutter/material/TextField/textAlign.html) 191 | /// 192 | /// Default: TextAlign.start 193 | final TextAlign textAlign; 194 | 195 | /// Configures how the platform keyboard will select an uppercase or lowercase keyboard 196 | /// (https://api.flutter.dev/flutter/material/TextField/textCapitalization.html) 197 | /// 198 | /// Default: TextCapitalization.none 199 | final TextCapitalization textCapitalization; 200 | 201 | /// To identify the semantic meaning expected for a text-entry area 202 | /// (https://developer.apple.com/documentation/uikit/uitextcontenttype) 203 | /// 204 | /// Default: null 205 | final TextContentType? textContentType; 206 | 207 | /// Called when the user initiates a change to text entry 208 | /// (https://api.flutter.dev/flutter/material/TextField/onChanged.html) 209 | /// 210 | /// Default: null 211 | final ValueChanged? onChanged; 212 | 213 | /// Called when the user submits editable content (e.g., user presses the "done" button on the keyboard). 214 | /// (https://api.flutter.dev/flutter/material/TextField/onEditingComplete.html) 215 | /// 216 | /// Default: null 217 | final VoidCallback? onEditingComplete; 218 | 219 | /// Called when the user indicates that they are done editing the text in the field 220 | /// (https://api.flutter.dev/flutter/material/TextField/onSubmitted.html) 221 | /// 222 | /// Default: null 223 | final ValueChanged? onSubmitted; 224 | 225 | /// Called when the user taps the field. 226 | /// 227 | /// Not implemented yet on Android. 228 | /// 229 | /// Default: null 230 | final VoidCallback? onTap; 231 | 232 | @override 233 | State createState() => _NativeTextInputState(); 234 | } 235 | 236 | class IosOptions { 237 | /// Whether autocorrect is enabled. 238 | /// 239 | /// Default: null 240 | final bool? autocorrect; 241 | 242 | /// The color of the cursor 243 | /// (https://api.flutter.dev/flutter/material/TextField/cursorColor.html) 244 | /// 245 | /// Default: null 246 | final Color? cursorColor; 247 | 248 | /// The appearance of the keyboard 249 | /// (https://api.flutter.dev/flutter/material/TextField/keyboardAppearance.html) 250 | /// 251 | /// Default: null 252 | final Brightness? keyboardAppearance; 253 | 254 | /// The style to use for the placeholder text. [Only `fontSize`, `fontWeight` are supported] 255 | /// (https://api.flutter.dev/flutter/cupertino/CupertinoTextField/placeholderStyle.html) 256 | /// 257 | /// Default: null 258 | final TextStyle? placeholderStyle; 259 | 260 | IosOptions({ 261 | this.autocorrect, 262 | this.cursorColor, 263 | this.keyboardAppearance, 264 | this.placeholderStyle, 265 | }); 266 | } 267 | 268 | class _NativeTextInputState extends State { 269 | final Completer _channel = Completer(); 270 | 271 | TextEditingController? _controller; 272 | TextEditingController get _effectiveController => 273 | widget.controller ?? (_controller ??= TextEditingController()); 274 | 275 | FocusNode? _focusNode; 276 | FocusNode get _effectiveFocusNode => 277 | widget.focusNode ?? (_focusNode ??= FocusNode()); 278 | 279 | bool get _isMultiline => widget.maxLines == 0 || widget.maxLines > 1; 280 | double _lineHeight = 22.0; 281 | double _contentHeight = 22.0; 282 | 283 | @override 284 | void initState() { 285 | super.initState(); 286 | 287 | _effectiveFocusNode.addListener(_focusNodeListener); 288 | widget.controller?.addListener(_controllerListener); 289 | } 290 | 291 | @override 292 | void dispose() { 293 | _effectiveFocusNode.removeListener(_focusNodeListener); 294 | widget.controller?.removeListener(_controllerListener); 295 | 296 | _controller?.dispose(); 297 | _focusNode?.dispose(); 298 | 299 | super.dispose(); 300 | } 301 | 302 | Future _focusNodeListener() async { 303 | final MethodChannel channel = await _channel.future; 304 | if (mounted) { 305 | channel.invokeMethod(_effectiveFocusNode.hasFocus ? "focus" : "unfocus"); 306 | } 307 | } 308 | 309 | Future _controllerListener() async { 310 | final MethodChannel channel = await _channel.future; 311 | channel.invokeMethod( 312 | "setText", 313 | {"text": widget.controller?.text ?? ''}, 314 | ); 315 | channel.invokeMethod("getContentHeight").then((value) { 316 | if (value != null && value != _contentHeight) { 317 | setState(() { 318 | _contentHeight = value; 319 | }); 320 | } 321 | }); 322 | } 323 | 324 | Widget _platformView(BoxConstraints layout) { 325 | switch (defaultTargetPlatform) { 326 | case TargetPlatform.android: 327 | return PlatformViewLink( 328 | viewType: NativeTextInput.viewType, 329 | surfaceFactory: (context, controller) => AndroidViewSurface( 330 | controller: controller as AndroidViewController, 331 | hitTestBehavior: PlatformViewHitTestBehavior.opaque, 332 | gestureRecognizers: const >{}, 333 | ), 334 | onCreatePlatformView: (PlatformViewCreationParams params) { 335 | return PlatformViewsService.initSurfaceAndroidView( 336 | id: params.id, 337 | viewType: NativeTextInput.viewType, 338 | layoutDirection: TextDirection.ltr, 339 | creationParams: _buildCreationParams(layout), 340 | creationParamsCodec: const StandardMessageCodec(), 341 | ) 342 | ..addOnPlatformViewCreatedListener((_) { 343 | params.onPlatformViewCreated(_); 344 | _createMethodChannel(_); 345 | }) 346 | ..create(); 347 | }, 348 | ); 349 | case TargetPlatform.iOS: 350 | return UiKitView( 351 | viewType: NativeTextInput.viewType, 352 | creationParamsCodec: const StandardMessageCodec(), 353 | creationParams: _buildCreationParams(layout), 354 | onPlatformViewCreated: _createMethodChannel, 355 | ); 356 | default: 357 | return CupertinoTextField( 358 | controller: widget.controller, 359 | cursorColor: widget.iosOptions?.cursorColor, 360 | decoration: BoxDecoration( 361 | border: Border.all(width: 0, color: Colors.transparent), 362 | ), 363 | focusNode: widget.focusNode, 364 | keyboardAppearance: widget.iosOptions?.keyboardAppearance, 365 | maxLines: widget.maxLines, 366 | minLines: widget.minLines, 367 | placeholder: widget.placeholder, 368 | placeholderStyle: TextStyle(color: widget.placeholderColor), 369 | textAlign: widget.textAlign, 370 | textCapitalization: widget.textCapitalization, 371 | onChanged: widget.onChanged, 372 | onSubmitted: widget.onSubmitted, 373 | ); 374 | } 375 | } 376 | 377 | @override 378 | Widget build(BuildContext context) { 379 | return ConstrainedBox( 380 | constraints: BoxConstraints( 381 | minHeight: _minHeight, 382 | maxHeight: _maxHeight > _minHeight ? _maxHeight : _minHeight, 383 | ), 384 | child: LayoutBuilder( 385 | builder: (context, layout) => Container( 386 | decoration: widget.decoration, 387 | child: _platformView(layout), 388 | ), 389 | ), 390 | ); 391 | } 392 | 393 | void _createMethodChannel(int nativeViewId) { 394 | MethodChannel channel = 395 | MethodChannel("flutter_native_text_input$nativeViewId") 396 | ..setMethodCallHandler(_onMethodCall); 397 | channel.invokeMethod("getLineHeight").then((value) { 398 | if (value != null) { 399 | setState(() { 400 | _lineHeight = value; 401 | }); 402 | } 403 | }); 404 | channel.invokeMethod("getContentHeight").then((value) { 405 | if (value != null) { 406 | setState(() { 407 | _contentHeight = value; 408 | }); 409 | } 410 | }); 411 | _channel.complete(channel); 412 | } 413 | 414 | Map _buildCreationParams(BoxConstraints constraints) { 415 | Map params = { 416 | "maxLines": widget.maxLines, 417 | "minHeightPadding": widget.minHeightPadding, 418 | "minLines": widget.minLines, 419 | "placeholder": widget.placeholder ?? "", 420 | "returnKeyType": widget.returnKeyType.toString(), 421 | "text": _effectiveController.text, 422 | "textAlign": widget.textAlign.toString(), 423 | "textCapitalization": widget.textCapitalization.toString(), 424 | "textContentType": widget.textContentType?.toString(), 425 | "keyboardAppearance": widget.iosOptions?.keyboardAppearance.toString(), 426 | "keyboardType": widget.keyboardType.toString(), 427 | "width": constraints.maxWidth, 428 | }; 429 | 430 | if (widget.style != null && widget.style?.fontSize != null) { 431 | params = { 432 | ...params, 433 | "fontSize": widget.style?.fontSize, 434 | }; 435 | } 436 | 437 | if (widget.style != null && widget.style?.fontWeight != null) { 438 | params = { 439 | ...params, 440 | "fontWeight": widget.style?.fontWeight.toString(), 441 | }; 442 | } 443 | 444 | if (widget.style != null && widget.style?.fontFamily != null) { 445 | params = { 446 | ...params, 447 | "fontFamily": widget.style?.fontFamily.toString(), 448 | }; 449 | } 450 | 451 | if (widget.style != null && widget.style?.color != null) { 452 | params = { 453 | ...params, 454 | "fontColor": { 455 | "red": widget.style?.color?.red, 456 | "green": widget.style?.color?.green, 457 | "blue": widget.style?.color?.blue, 458 | "alpha": widget.style?.color?.alpha, 459 | } 460 | }; 461 | } 462 | 463 | if (widget.iosOptions?.placeholderStyle != null && 464 | widget.iosOptions?.placeholderStyle?.fontSize != null) { 465 | params = { 466 | ...params, 467 | "placeholderFontSize": widget.iosOptions?.placeholderStyle?.fontSize, 468 | }; 469 | } 470 | 471 | if (widget.iosOptions?.placeholderStyle != null && 472 | widget.iosOptions?.placeholderStyle?.fontWeight != null) { 473 | params = { 474 | ...params, 475 | "placeholderFontWeight": 476 | widget.iosOptions?.placeholderStyle?.fontWeight.toString(), 477 | }; 478 | } 479 | 480 | if (widget.iosOptions?.placeholderStyle != null && 481 | widget.iosOptions?.placeholderStyle?.fontFamily != null) { 482 | params = { 483 | ...params, 484 | "placeholderFontFamily": 485 | widget.iosOptions?.placeholderStyle?.fontFamily.toString(), 486 | }; 487 | } 488 | 489 | if (widget.placeholderColor != null) { 490 | params = { 491 | ...params, 492 | "placeholderFontColor": { 493 | "red": widget.placeholderColor?.red, 494 | "green": widget.placeholderColor?.green, 495 | "blue": widget.placeholderColor?.blue, 496 | "alpha": widget.placeholderColor?.alpha, 497 | }, 498 | }; 499 | } 500 | 501 | if (widget.iosOptions?.cursorColor != null) { 502 | params = { 503 | ...params, 504 | "cursorColor": { 505 | "red": widget.iosOptions!.cursorColor?.red, 506 | "green": widget.iosOptions!.cursorColor?.green, 507 | "blue": widget.iosOptions!.cursorColor?.blue, 508 | "alpha": widget.iosOptions!.cursorColor?.alpha, 509 | }, 510 | }; 511 | } 512 | 513 | if (widget.iosOptions?.autocorrect != null) { 514 | params = { 515 | ...params, 516 | "autocorrect": widget.iosOptions!.autocorrect, 517 | }; 518 | } 519 | 520 | if (widget.onTap != null) { 521 | params = { 522 | ...params, 523 | "onTap": true, 524 | }; 525 | } 526 | 527 | return params; 528 | } 529 | 530 | Future _onMethodCall(MethodCall call) async { 531 | switch (call.method) { 532 | case "inputValueChanged": 533 | final String? text = call.arguments["text"]; 534 | final int? lineIndex = call.arguments["currentLine"]; 535 | _inputValueChanged(text, lineIndex); 536 | return null; 537 | 538 | case "inputStarted": 539 | _inputStarted(); 540 | return null; 541 | 542 | case "inputFinished": 543 | final String? text = call.arguments["text"]; 544 | _inputFinished(text); 545 | return null; 546 | 547 | case "singleTapRecognized": 548 | _singleTapRecognized(); 549 | } 550 | 551 | throw MissingPluginException( 552 | "NativeTextInput._onMethodCall: No handler for ${call.method}"); 553 | } 554 | 555 | double get _minHeight => 556 | (widget.minLines * _lineHeight) + widget.minHeightPadding; 557 | 558 | double get _maxHeight { 559 | if (!_isMultiline) return _minHeight; 560 | if (_contentHeight > _minHeight && widget.maxLines > 0) { 561 | double maxLineHeight = widget.maxLines * _lineHeight; 562 | if (defaultTargetPlatform == TargetPlatform.android) { 563 | maxLineHeight += widget.minHeightPadding; 564 | } 565 | return _contentHeight > maxLineHeight ? maxLineHeight : _contentHeight; 566 | } 567 | if (_contentHeight > _minHeight) return _contentHeight; 568 | return _minHeight; 569 | } 570 | 571 | // input control methods 572 | void _inputStarted() { 573 | _scrollIntoView(); 574 | if (!_effectiveFocusNode.hasFocus) { 575 | FocusScope.of(context).requestFocus(_effectiveFocusNode); 576 | } 577 | } 578 | 579 | void _inputFinished(String? text) async { 580 | if (widget.onEditingComplete != null) { 581 | widget.onEditingComplete!(); 582 | } else { 583 | final channel = await _channel.future; 584 | channel.invokeMethod("unfocus"); 585 | if (_effectiveFocusNode.hasFocus) FocusScope.of(context).unfocus(); 586 | } 587 | if (widget.onSubmitted != null) { 588 | await Future.delayed(const Duration(milliseconds: 100)); 589 | widget.onSubmitted!(text); 590 | } 591 | } 592 | 593 | void _inputValueChanged(String? text, int? lineIndex) async { 594 | if (text != null) { 595 | _effectiveController.text = text; 596 | if (widget.onChanged != null) widget.onChanged!(text); 597 | 598 | final channel = await _channel.future; 599 | final value = await channel.invokeMethod("getContentHeight"); 600 | if (mounted && value != null && value != _contentHeight) { 601 | _contentHeight = value; 602 | setState(() {}); 603 | } 604 | } 605 | } 606 | 607 | void _singleTapRecognized() => widget.onTap?.call(); 608 | 609 | static const Duration _caretAnimationDuration = Duration(milliseconds: 100); 610 | static const Curve _caretAnimationCurve = Curves.fastOutSlowIn; 611 | 612 | void _scrollIntoView() { 613 | SchedulerBinding.instance.addPostFrameCallback((_) { 614 | context.findRenderObject()!.showOnScreen( 615 | duration: _caretAnimationDuration, 616 | curve: _caretAnimationCurve, 617 | ); 618 | }); 619 | } 620 | } 621 | -------------------------------------------------------------------------------- /migration-to-v2.md: -------------------------------------------------------------------------------- 1 | # Migrating to V2 2 | 3 | Thanks for using this plugin and I hope you are happy using it so far. 4 | 5 | ## What's New 6 | 7 | - Android support is added. 8 | 9 | ## How to Migrate 10 | 11 | 1. If you are using `cursorColor` and/or `keyboardAppearance`, they are wrapped in `iosOptions` now as these params are effective on iOS platform only 12 | ``` 13 | NativeTextInput( 14 | ..._otherConfigs, 15 | iosOptions: IosOptions( 16 | cursorColor: _cursoorColor, 17 | keyboardAppearance: _keyboardAppearance, 18 | ), 19 | ) 20 | ``` 21 | 22 | 2. If you are using `placeholderStyle`, `placeholderColor` is extracted and the rest are moved inside `iosOptions` 23 | ``` 24 | NativeTextInput( 25 | ..._otherConfigs, 26 | placeholderColor: _placeholderColor, 27 | iosOptions: IosOptions( 28 | ..._otherOptions, 29 | placeholderStyle: TextStyle( 30 | fontSize: _placeholderFontSize, 31 | fontWeight: _placeholderFontWeight, 32 | ), 33 | ), 34 | ) 35 | ``` 36 | 37 | That's all. Enjoy! -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_native_text_input 2 | description: Native text input for Flutter. Currently iOS-only with the use of UITextView. 3 | 4 | version: 2.4.0 5 | homepage: https://github.com/henryleunghk/flutter-native-text-input 6 | 7 | environment: 8 | sdk: ">=2.12.0 <3.0.0" 9 | flutter: ">=2.5.0" 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | flutter_lints: ^1.0.0 19 | 20 | # For information on the generic Dart part of this file, see the 21 | # following page: https://dart.dev/tools/pub/pubspec 22 | 23 | # The following section is specific to Flutter. 24 | flutter: 25 | # This section identifies this Flutter project as a plugin project. 26 | # The 'pluginClass' and Android 'package' identifiers should not ordinarily 27 | # be modified. They are used by the tooling to maintain consistency when 28 | # adding or updating assets for this project. 29 | plugin: 30 | platforms: 31 | android: 32 | package: dev.henryleunghk.flutter_native_text_input 33 | pluginClass: NativeTextInputPlugin 34 | ios: 35 | pluginClass: NativeTextInputPlugin 36 | 37 | # To add assets to your plugin package, add an assets section, like this: 38 | # assets: 39 | # - images/a_dot_burr.jpeg 40 | # - images/a_dot_ham.jpeg 41 | # 42 | # For details regarding assets in packages, see 43 | # https://flutter.dev/assets-and-images/#from-packages 44 | # 45 | # An image asset can refer to one or more resolution-specific "variants", see 46 | # https://flutter.dev/assets-and-images/#resolution-aware. 47 | 48 | # To add custom fonts to your plugin package, add a fonts section here, 49 | # in this "flutter" section. Each entry in this list should have a 50 | # "family" key with the font family name, and a "fonts" key with a 51 | # list giving the asset and other descriptors for the font. For 52 | # example: 53 | # fonts: 54 | # - family: Schyler 55 | # fonts: 56 | # - asset: fonts/Schyler-Regular.ttf 57 | # - asset: fonts/Schyler-Italic.ttf 58 | # style: italic 59 | # - family: Trajan Pro 60 | # fonts: 61 | # - asset: fonts/TrajanPro.ttf 62 | # - asset: fonts/TrajanPro_Bold.ttf 63 | # weight: 700 64 | # 65 | # For details regarding fonts in packages, see 66 | # https://flutter.dev/custom-fonts/#from-packages 67 | --------------------------------------------------------------------------------