Televerseteleverse.

Keyboards

Create interactive bot experiences with Reply and Inline Keyboards.

Telegram bots support two types of keyboards that enhance user interaction: Reply Keyboards (custom keyboards that appear below the chat) and Inline Keyboards (buttons attached directly to messages).

Reply Keyboard

Appears below the message input. When tapped, sends the button text as a regular message.

  • Replaces the default keyboard
  • Great for menu navigation
  • Can request contact/location
Inline Keyboard

Attached to messages. Triggers a callback query without sending a message.

  • Stays with the message
  • Perfect for actions & navigation
  • Can open URLs & web apps

Reply Keyboard

The Keyboard class provides a fluent API for building reply keyboards. Instead of manually constructing ReplyKeyboardMarkup with nested arrays, you can chain methods to build keyboards intuitively.

Reply Keyboard Example

Basic Usage

Use .text() to add buttons and .row() to start a new row:

main.dart
final keyboard = Keyboard()
    .text("📱 Account")
    .row()
    .text("⚙️ Settings")
    .text("🤌 Nevermind");

await ctx.reply("Please choose an option:", replyMarkup: keyboard);

Factory Constructors

For common patterns, Keyboard provides convenient factory constructors:

main.dart
// Create keyboard from 2D string array
final keyboard = Keyboard.from([
  ["Yes", "No"],
  ["Maybe", "Cancel"]
]);

// Create a grid layout (3 columns)
final numpad = Keyboard.grid(
  ["1", "2", "3", "4", "5", "6", "7", "8", "9", "*", "0", "#"],
  columns: 3,
);

// Single row of buttons
final quick = Keyboard.singleRow(["Option A", "Option B", "Option C"]);

// Single column of buttons
final menu = Keyboard.column(["Start", "Help", "Settings"]);

Configuration Options

Customize keyboard behavior with configuration methods:

main.dart
final keyboard = Keyboard()
    .text("Submit")
    .row()
    .text("Cancel")
    .resized()           // Fit keyboard to screen
    .oneTime()           // Hide after button tap
    .persistent()        // Always show keyboard
    .placeholder("Select an option...");
MethodDescription
resized()Resize keyboard vertically for optimal fit
oneTime()Hide keyboard after a button is tapped
persistent()Always show keyboard when regular keyboard is hidden
placeholder(text)Show placeholder text in input field
makeSelective()Show keyboard to specific users only

Special Buttons

Reply keyboards support special button types for requesting user data:

main.dart
final keyboard = Keyboard()
    .requestContact("📞 Share Contact")
    .requestLocation("📍 Share Location")
    .row()
    .users(
      text: "👤 Select User",
      requestId: 1,
      userIsBot: false,
    )
    .chat(
      text: "💬 Select Chat",
      requestId: 2,
    )
    .row()
    .poll("📊 Create Poll", type: PollType.quiz)
    .webApp("🌐 Open App", "https://example.com");

Handling Keyboard Taps

When a button is tapped, its text is sent as a regular message. Use bot.text() to handle it:

main.dart
// When the user taps a button, its text is sent as a message
bot.text("⚙️ Settings", (ctx) async {
  await ctx.reply("Here are your settings.");
});

// Handle multiple options
bot.text("📱 Account", (ctx) => ctx.reply("Account details..."));
bot.text("🤌 Nevermind", (ctx) => ctx.reply("Okay, no problem!"));

Removing the Keyboard

To remove the custom keyboard and show the default system keyboard:

main.dart
// Remove the keyboard from the user's screen
await ctx.reply(
  "Keyboard removed!",
  replyMarkup: Keyboard.remove(),
);

Inline Keyboard

The InlineKeyboard class provides a fluent API for building inline keyboards. Inline buttons appear directly on messages and trigger callback queries instead of sending messages.

Inline Keyboard Example

Basic Usage

Use .text(label, callbackData) to add callback buttons:

main.dart
final keyboard = InlineKeyboard()
    .text("👍 Like", "like")
    .text("👎 Dislike", "dislike")
    .row()
    .url("🌐 Visit Website", "https://televerse.weaverlabs.ca");

await ctx.reply("Rate this bot:", replyMarkup: keyboard);

Pagination Example

Inline keyboards are perfect for pagination and navigation controls:

main.dart
final keyboard = InlineKeyboard()
    .text("<< 1", "page_first")
    .text("< 3", "page_prev")
    .text("• 4 •", "page_current")
    .text("5 >", "page_next")
    .text("10 >>", "page_last")
    .row()
    .url("🌐 Search on Web", "https://google.com/search?q=...");

await ctx.reply("Results Page 4.", replyMarkup: keyboard);

Factory Constructors

Build keyboards from arrays using factory constructors:

main.dart
// Create from (text, callbackData) records
final keyboard = InlineKeyboard.from([
  [("Yes", "answer_yes"), ("No", "answer_no")],
  [("Maybe", "answer_maybe")],
]);

// Grid layout with 3 columns
final options = InlineKeyboard.grid([
  ["A", "opt_a"], ["B", "opt_b"], ["C", "opt_c"],
  ["D", "opt_d"], ["E", "opt_e"], ["F", "opt_f"],
], columns: 3);

Button Types

Inline keyboards support various button types for different actions:

main.dart
final keyboard = InlineKeyboard()
    .text("Callback Button", "data")
    .url("Open URL", "https://example.com")
    .row()
    .webApp("Launch App", "https://app.example.com")
    .login("Auth Login", "https://auth.example.com")
    .row()
    .switchInline("Share", "check this out")
    .switchInlineCurrentChat("Search Here", "query")
    .row()
    .pay("💳 Pay Now")
    .copyText("📋 Copy", copyText: "ABC123");
MethodDescription
text(label, data)Callback button with custom payload
url(label, url)Opens a URL in browser
webApp(label, url)Opens a Telegram Web App
login(label, url)Telegram Login button
switchInline(label, query)Switch to inline mode in another chat
switchInlineCurrentChat()Switch to inline mode in current chat
pay(label)Payment button (invoices only)
copyText(label, copyText:)Copy text to clipboard
game(label)Launch a Telegram game

Handling Callback Queries

When an inline button is pressed, a callback query is sent. Use bot.callbackQuery() to handle it:

main.dart
// Handle specific callback data
bot.callbackQuery("like", (ctx) async {
  await ctx.answerCallbackQuery(text: "Thanks for liking! ❤️");
  await ctx.editMessageText("You liked this message!");
});

// Handle with pattern matching
bot.callbackQuery(RegExp(r'^page_'), (ctx) async {
  final action = ctx.callbackQuery!.data;
  // Parse action and update pagination...
  await ctx.editMessageText("Page updated!");
});

Tip: Always call ctx.answerCallbackQuery() to acknowledge the callback. This removes the loading indicator from the button.

See Also

  • Context - Learn about reply methods and context properties.
  • Bot Class - Understand how to handle updates with the Bot class.