Keyboard handling without TextField

In the “key” event it has Gotcha that if the event is handled by TextField/TextBox/Webview there are certain things that we have to keep in mind.
I want to use the keyboard for a custom control I want to design. Here I want to catch all the keys.
I am trying to see how to put the keyboard on the screen. I see that the only function is setKeyboardFocus and that function requires a textfield to enable the keyboard. This seemed weird because of the Gotcha because it said “if” the event is handled by these. Now with this function requirment of passing a textField, it seems key events are always handled by these!
Is there any other way to show the keyboard and handle the events from the keyboard without a textfield?

I realize that I can put a textField off the screen and use that to show the keyboard. But I think in that case the textfield will be handling the key events first and I will not get all the key events.

I say this because I see that even though the documentation says “back” key is not handled by textField I am not able to to catch it in my handler when I run it on Android 15. So I don’t want to go through the textField and deal with these complex cases.

I would ideally like to directly handle the keyboard and get all the events directly. Would this be possible in any way?

I always create a virtual keyboard from Solar2D’s display api instead of using native TextField.

Like this:

You can try this Keyboard module.

Usage:

--main.lua
require("onScreenKeyboard")

local textField = display.newText("", 0, 0, display.contentWidth, 100, native.systemFont, 50)
textField:setTextColor(255, 255, 255)

local keyboard = onScreenKeyboard:new()
--create a listener function that receives the events of the keyboard
local listener = function(event)
    if (event.phase == "ended") then
        textField.text = keyboard:getText()

        textField.anchorX = 0
        textField.anchorY = 0
        textField.x = 0
        textField.y = 0

        if (event.target.inputCompleted == true) then
            print("Input of data complete...")
            keyboard:destroy()
        end
    end
end

keyboard:setListener(listener)

keyboard:drawKeyBoard(keyboard.keyBoardMode.letters_small)

Thanks for the custome keyboard suggestion. This will be a issue for me since I want to use different language keyboards as well.

Hello @aryajur , I use a simple and effective approach for capturing user input in Solar2D. Here’s how I do it:

  1. Create an Input Field
    I create a native.newTextBox, which acts as the input field where the user can type anything they want.
  2. Activate the Android Keyboard
    By listening to the "userInput" event, I trigger the Android keyboard when the user taps on the text box.
    Now you have a fully functional input area with a working keyboard.
  3. Handle the Submitted Text
    I add a simple “Send” button. When tapped, it grabs the text that was typed and allows me to process it however I need.

It’s a straightforward solution that works well on mobile devices.
here is the code:

local baseWidth = display.contentWidth
local baseHeight = display.contentHeight

local hPercent = (baseHeight / 100) 
local wPercent = (baseWidth / 100) 

local center_x = display.contentCenterX
local center_y = display.contentCenterY

local correntText = ""
local textBox = native.newTextBox(center_x, center_y - hPercent * 35, wPercent * 88, wPercent * 40) 
textBox.text = ""
textBox.font = native.newFont( "assets/fonts/arial.ttf", 18)
textBox.size = wPercent * 5.5
textBox.isEditable = true


-- Capture the text
local function textListener(event)

    if event.phase == "began" then
        native.setKeyboardFocus( textBox )
        --User begins editing.
        print("Step 1", event.text)
    elseif event.phase == "ended" or event.phase == "submitted" then
        --
    elseif event.phase == "editing"  then
        correntText = correntText .. event.newCharacters 
        --print("Step 2" , correntText)
    end
end

textBox:addEventListener("userInput", textListener)

-- Create send button (simple)
local sendButton = display.newRect(textBox.x, textBox.y + wPercent * 54, wPercent* 30, hPercent * 8)
sendButton:setFillColor(0.2, 0.6, 1)

local buttonTex = display.newText("Enviar", sendButton.x, sendButton.y, native.systemFontBold, wPercent * 6)
sendButton:addEventListener("tap", function(event)
    -- here you can handle the typed content!
    sendMessage(correntText)
    native.setKeyboardFocus(nil) -- remove the focus
    textBox.text = ""
end)

Thank you cmaiia for sharing this approach. I am trying to fully understand the flow. So suppose you have a textField at a certain position in your screen, say at the bottom end of the screen. Now here is the flow I understood:

  1. The user taps on the textfield and the keyboard comes up
  2. Now you create a textbox and set the focus to it to get the user input events there
  3. After all the data is is entered and the user presses the Send button you can take that from the text box and process it and put the data in the textfield

Is that right?

One question I have here is that I see you position the textbox at the center of the screen. How do you make sure the Keyboard does not overlap it the textbox or the send button?

Actually, the code I shared is specifically designed to avoid using native.newTextField, which by default brings up the Android keyboard.

Why avoid using textField?
Because it behaves inconsistently:

  • When the user types, the text may look correct on screen, but internally it’s not updated properly.
  • For example, if the user types “A”, the textField doesn’t actually reflect that in its internal content. When they type “B”, the value you access is just “A”.
  • So if the user tries to type something like “CMaiia”, what gets saved is only “CMaii” — the last letter is always missing.

In addition, textField does not clear the input properly, and often you can’t reliably capture the user’s full input.

That’s why the flow of my code works like this:

  1. The player taps on a native.newTextBox, which serves as the input field.
  2. This triggers the Android keyboard using the "userInput" event, and the textBox correctly receives the typed input.
  3. When the player taps the “Send” or “Save” button, you simply retrieve the text from the correntText variable, which is accurately updated during typing.

This approach gives me much more control and works reliably.

As for the position of the textBox:

It’s centered on the X-axis, but it’s shifted upward on the Y-axis. Take a look:

The part center_y - hPercent * 35 offsets the textBox upward by 35% of the screen height. Which creates space for the keyboard
The other values define its width and height → wPercent * 88, wPercent * 40.