Building a keyboard with Fleksy SDK using Flutter

Building a keyboard with Fleksy SDK using Flutter

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.

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 = "https://maven.fleksy.com/maven"
        }
    }
}

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.

<manifest
    ...
    xmlns:tools="http://schemas.android.com/tools">
   <application
        ...
        tools:replace="android:label">
        ...
    </application>
</manifest>

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/local.properties file.

...
flutter.minSdkVersion=21

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="http://schemas.android.com/apk/res/android"
    android:icon="@mipmap/ic_launcher"
    android:isDefault="true">
    <subtype
        android:imeSubtypeExtraValue="EmojiCapable,AsciiCapable,TrySuppressingImeSwitcher"
        android:imeSubtypeMode="keyboard"
        android:label="%s"
        android:overridesImplicitlyEnabledSubtype="true" />
    <subtype
        android:imeSubtypeExtraValue="AsciiCapable"
        android:imeSubtypeLocale="en_US"
        android:imeSubtypeMode="keyboard"
        android:isAsciiCapable="true" />
</input-method>

Step 8

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
  package="co.thingthing.samplesdk">
   <application
    ...
    <service
        android:name=".SampleKeyboardService"
        android:directBootAware="true"
        android:exported="true"
        android:permission="android.permission.BIND_INPUT_METHOD">
        <intent-filter>
            <action android:name="android.view.InputMethod" />
        </intent-filter>
        <meta-data
            android:name="android.view.im"
            android:resource="@xml/sample_input_method" />
     </service>
  </application>
</manifest>

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.

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 supports adding a new Custom Keyboard Extension target to your project. It can be used to quickly generate required files and schemes 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, enter the repository URL, and click the Add Package button.
https://github.com/FleksySDK/FleksySDK-iOS

Step 3

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

  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, 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 the RequestsOpenAccess key of type Boolean and 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.

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

Did you find this article valuable?

Support Fleksy's Engineering Blog by becoming a sponsor. Any amount is appreciated!