Skip to main content
Back to Blog

Building a Flutter custom keyboard with Fleksy | A step-by-step guide

Building a better cross-platform keyboard doesn’t have to be complicated.

The keyboard is one of the apps a user interacts with every day. Almost all apps accept text input and allow a user to enter text. Therefore, a good keyboard can make or break the User Experience. However, even for the two most popular platforms, Android and iOS, building a keyboard app is by no means easy. The existing guides require a high-level app and system knowledge, extended time, and investment, while on the other hand, the sample apps are broken and don’t even compile out of the box.

We will look at the Fleksy Keyboard SDK developed to address these significant issues while building a keyboard app. As the SDK natively supports Android and iOS platforms, creating a keyboard using the Flutter framework is very straightforward. The SDK is also well documented and available here.

This article assumes that you have:

To use the SDK, note that you first need a license key that can be requested in the developer’s dashboard. A free license is enough to follow this guide.

Android-related Setup

The Android dependency for the Fleksy Keyboard SDK can be synced and set up via Gradle. For a smoother integration, consider opening the android directory in the Android Studio.

Step 1

Add Fleksy SDK repository to the android/build.gradle file.

allprojects {
    repositories {
        maven {
            url = ""

Step 2

Add the dependency on the Flesky Keyboard SDK in the android/app/build.gradle file.

dependencies {
    // Keyboard Dependencies
    implementation "co.thingthing.fleksycore:fleksycore-release:3.5.16"

Step 3

Disable compression for JSON and wav files in android/app/build.gradle file. The SDK uses these file types for theming and customization purposes.

android {
  aaptOptions {
    noCompress '.json', '.wav'

Step 4

Add a new tools namespace and tag in your app module’s AndroidManifest.xml file to replace the application label when manifests merge during compilation. It is required to resolve a manifest merge error during compilation.


Step 5

Increase the minimum target SDK version of the app to 21. To do this, add a new property flutter.minSdkVersion in the android/ file.


Then, load and set this property in the android/app/build.gradle file afterward.

def flutterMinSdkVersion = localProperties.getProperty('flutter.minSdkVersion')
if (flutterMinSdkVersion == null) {
    flutterMinSdkVersion = 21

android {
    defaultConfig {
        minSdkVersion flutterMinSdkVersion

Step 6

  • Sync project with Gradle Files to load the keyboard SDK dependencies.
  • Create a Kotlin class for the keyboard service and inherit it from the KeyboardService class in your app’s module.
  • Override the method createConfiguration to return a configuration containing your license and the secret keys.

It is recommended NOT to commit your license keys in the code but to store them as variables in the environment and access them during the compilation.

import co.thingthing.fleksy.core.keyboard.KeyboardConfiguration
import co.thingthing.fleksy.core.keyboard.KeyboardService

class SampleKeyboardService : KeyboardService() {

    override fun createConfiguration(): KeyboardConfiguration {
        return KeyboardConfiguration(
            license = KeyboardConfiguration.LicenseConfiguration(
                licenseKey = "<your-license-key>",
                licenseSecret = "<your-license-secret>"


Step 7

Create a new input-method file (e.g., sample_input_method.xml) in your app module’s res/xml folder. The Android system will use this to obtain information about the input service we created above and display it to the users in the Settings.

<?xml version="1.0" encoding="utf-8"?>
<input-method xmlns:android=""
        android:overridesImplicitlyEnabledSubtype="true" />
        android:isAsciiCapable="true" />

Step 8

Add the newly created service and input method in your app module’s AndroidManifest.xml file.

<manifest xmlns:android=""
            <action android:name="android.view.InputMethod" />
            android:resource="@xml/sample_input_method" />

Step 9

Finally, download & copy the English language pack resourceArchive-en-US.jet to the assets/encrypted folder of the main app module. This will be used by the Fleksy Keyboard SDK as the default starting language. We can download other languages later using the SDK as required.

The assets/encrypted directory is absent by default on new projects unless created manually.

For example:

With a terminal (bash) open at the root of the android project, and with the provided language pack stored in the ~/Downloads folder:

  • Create the encrypted folder inside the app module’s assets folder:
mkdir -p app/src/main/assets/encrypted
  • Copy the language pack to the assets/encrypted folder:
cp ~/Downloads/resourceArchive-en-US.jet app/src/main/assets/encrypted

With the above steps, the Android part of our Flutter project is now ready to be built and run on a device of choice.

iOS-related Setup

The iOS package for the Fleksy Keyboard SDK can be synced using Swift Package Manager. For the best experience, consider opening the ios directory in Xcode.

Step 1

Xcode provides support to add a new Custom Keyboard Extension target to your project. It can be used to quickly generate required files and schemes in order to build your custom keyboard.

  1. Select your Project in the Project Navigator,
  2. Navigate to File > New > Target,
  3. Select the Custom Keyboard Extension from the Application Extension group and click next,
  4. Specify a Product Name (e.g. SampleKeyboard) and click finish.

Xcode will ask you if you want to activate the scheme for the newly created extension. Choose Activate.

Step 2

Add the FleksySDK package as a dependency using its repository URL, as follows:

  1. Navigate to Project’s General Pane > Package Dependencies,
  2. Click on the + button and enter the repository URL and click Add Package button.

Step 3

Locate your Custom Keyboard Extension created in step 1 in the Project Navigatior.

  1. Modify the KeyboardViewController class present in it by removing the existing content in it.
  2. Import the FleksyKeyboardSDK package and inherit from FKKeyboardViewController.
  3. Override the method createConfiguration to return a configuration containing your license and the secret keys.

It is recommended NOT to commit your license keys in the code but to store them as variables in the environment and access them during the compilation.

import FleksyKeyboardSDK

class KeyboardViewController: FleksyKeyboardSDK.FKKeyboardViewController {

    override func createConfiguration() -> KeyboardConfiguration {
        let style = StyleConfiguration()

        let appPopup = AppearancePopup()
        let appLongpress = AppearanceLongPress()
        let appearance = AppearanceConfiguration(
            objPopup: appPopup,
            objTouch: nil,
            objLongpress: appLongpress

        let typing = TypingConfiguration()
        let panelConfig = PanelConfiguration()
        let debugConfig = DebugConfiguration(debug: ())

        let licenseConfig = LicenseConfiguration(
            licenseKey: "<your-license-key>",
            licenseSecret: "<your-license-secret>"

        return KeyboardConfiguration(
            panel: panelConfig,
            capture: nil,
            style: style,
            appearance: appearance,
            typing: typing,
            specialKeys: nil,
            license: licenseConfig,
            debug: debugConfig

Step 4

If you are using Xcode 14 or higher, bitcode has been deprecated and disabled by default making this step non-required.

Fleksy Keyboard SDK doesn’t support building with bitcode so it must be disabled to avoid compilation failures. 

  1. Select your Custom Keyboard Extension’s General Pane > Build Settings,
  2. Under the Build Options find the option Enable Bitcode and set it to No.

Step 5

Fleksy Keyboard SDK also needs access to the network to validate API keys, download language and theme packs, and much more. For that, add the required RequestsOpenAccess key.

  1. Navigate to Custom Keyboard Extension’s General Pane > Info > NSExtension,
  2. Under NSExtensionAttributes, find RequestsOpenAccess key of type Booleanand set its value to 1

That’s all. With the steps above, the iOS part of our Flutter project is also ready to be built.

Best practices for Devs

1. Organizing and structuring custom keyboard code:

Organizing and structuring the code of a Flutter custom keyboard is crucial for efficient development and maintenance. Following best practices such as separating logic from UI, using modularization, and adhering to clean code principles like SOLID can greatly improve the quality and readability of the code. Utilizing a well-defined architecture pattern like MVVM or Provider can also help in separating concerns and making the codebase more maintainable. Additionally, documenting the code and using version control systems like Git can aid in collaboration among team members and ensure a streamlined development process.

2. Creating visually appealing and user-friendly custom keyboards:

The visual appearance and user-friendliness of a custom keyboard play a vital role in its acceptance among users. Best practices include designing an intuitive layout with well-spaced keys, choosing visually appealing fonts and colors, and providing clear visual feedback for user input. Incorporating accessibility features like adjustable font sizes and high contrast options can also enhance the usability of the custom keyboard. It’s essential to conduct thorough user testing and gather feedback to iterate and improve the visual aspects of the keyboard, keeping in mind the preferences and needs of the target audience.

3. Providing an enhanced user experience:

Flutter custom keyboards can go beyond basic input handling and offer advanced features to enhance the typing experience. This can include implementing auto-suggest and autocorrect functionalities to provide accurate word predictions and reduce typing errors. Supporting multiple languages, special characters, and different input methods like voice input and emoji suggestions can also add versatility to the custom keyboard. Integrating with external APIs or libraries for additional functionalities such as spell-checking or language translation can further enhance the capabilities of the custom keyboard, providing a more comprehensive typing experience to users.

4. Testing and Debugging:

Thorough testing and debugging are crucial for ensuring the reliability and stability of a Flutter custom keyboard. Best practices include conducting comprehensive unit testing, integration testing, and UI testing to catch any potential issues or bugs. Testing the custom keyboard in different scenarios, such as different device sizes, orientations, and language settings, can help identify and fix any compatibility issues. Proper error handling and logging mechanisms should also be implemented to aid in debugging and troubleshooting. Regularly reviewing logs, analyzing crash reports, and gathering feedback from users can help in continuously improving the quality and performance of the custom keyboard.

5. Deploying and Publishing 

Deploying and publishing a Flutter custom keyboard involves various steps, including app signing, app store guidelines compliance, and the submission process. Following the guidelines of the target app stores, such as Google Play Store and Apple App Store, is essential for a successful deployment. This includes adhering to security standards, providing necessary metadata and descriptions, and following the design and functionality guidelines. Properly preparing the custom keyboard for deployment, including thorough testing and localization, can ensure a smooth publishing process and increase the chances of acceptance by the app stores.

6. Further best practices for businesses:

For businesses interested in developing and using Flutter custom keyboards, some tips, and best practices include conducting thorough market research to identify the target audience and their needs, evaluating the feasibility and cost-effectiveness of developing a custom keyboard, and considering potential monetization strategies. Collaborating with experienced Flutter developers or development teams can ensure a smooth and efficient development process. Regularly gathering feedback from users, analyzing usage metrics, and continuously iterating and improving the custom keyboard can help businesses stay competitive in the market. Additionally, actively promoting and marketing the custom keyboard through various channels, such as social media, app store optimization, and targeted advertising, can increase its visibility and adoption among potential users.


1. How do I handle input events and manage the state in a Flutter Custom Keyboard?

Handling input events and managing state is a critical aspect of developing a Flutter Custom Keyboard. To handle input events, developers can use event listeners, such as onPressed or onChanged, to capture user input from the keyboard keys. Managing state can be done using Flutter’s built-in state management options like setState, Provider, or other third-party state management libraries. It’s important to carefully manage the state to ensure accurate input handling and synchronization between UI and logic.

2. How can I customize the appearance and layout of my Flutter Custom Keyboard?

Customizing the appearance and layout of a Flutter Custom Keyboard involves modifying the UI elements, such as keys, fonts, colors, and layout arrangements. Developers can use Flutter’s built-in widget customization options like TextStyle, Color, Padding, and Container to customize the appearance of the keys and overall layout. Additionally, utilizing third-party libraries for custom UI components or implementing custom painting techniques can further enhance the visual appeal of the custom keyboard.

3. How can I optimize performance and ensure a smooth user experience in my Flutter Custom Keyboard?

Optimizing performance and ensuring a smooth user experience is crucial for a successful Flutter Custom Keyboard. Some best practices include optimizing widget trees by minimizing unnecessary widget rebuilds, using const constructors where applicable, and optimizing layout and painting using LayoutBuilder or CustomPaint. Additionally, efficiently managing resources like memory and CPU usage, implementing efficient input event handling, and properly handling focus and accessibility can also contribute to a smooth user experience. Regular profiling and testing can help identify and resolve performance bottlenecks to ensure optimal performance of the custom keyboard.

If you have any questions or feedback, please don’t hesitate to chat with us!

Interested to know how Fleksy can help you? Sign up!

    Thanks, we'll be in touch with you soon!

    Did you like it? Spread the word:

    ✭ If you like Fleksy, give it a star on GitHub ✭