Skip to main content

Theme the widgets

koel_widgets ships themeable chat building blocks — MessageBubble, ChatInput, FollowUpList — in both Material 3 and Cupertino styles. One KoelTheme, attached as a ThemeExtension, drives all of them.

import 'package:flutter/material.dart';
import 'package:koel_widgets/koel_widgets.dart';

MaterialApp(
// The single hook that themes every koel widget below.
theme: ThemeData(extensions: [KoelTheme.light()]),
darkTheme: ThemeData.dark().copyWith(extensions: [KoelTheme.dark()]),
home: const ChatScreen(),
);

KoelTheme.light() / KoelTheme.dark() are ready-made palettes; copyWith lets you override individual slots (colors, text styles, spacing). The widgets read the theme from the ambient ThemeExtension, with a brightness-appropriate fallback if none is attached — so they render even under a bare CupertinoApp.

The widgets

// A rendered message — pass a koel_core Message; style is auto-picked from the
// platform, or forced with BubbleStyle.material / BubbleStyle.cupertino.
MessageBubble(message);
MessageBubble(message, style: BubbleStyle.cupertino);

// The composer. Enter submits (Shift+Enter on hardware keyboards inserts a
// newline); the trailing slot is yours (e.g. an attachment button).
ChatInput(
onSubmit: (text) => controller.send(text), // text is trimmed for you
placeholder: 'Message koel…',
attachmentSlot: const Icon(Icons.attach_file),
);

// Tappable follow-up suggestions.
FollowUpList(
suggestions: const ['Tell me a joke', 'Show me code', 'Summarize this'],
onSelected: (suggestion) => controller.send(suggestion),
);

Wiring it to a controller

Drive the message list from a KoelChatController (see Quickstart and the example/ app) and feed each Message to a MessageBubble. ChatInput.onSubmit and FollowUpList.onSelected both call controller.send, so the composer and the suggestions share one path into the session.