flow_ui 0.1.0 copy "flow_ui: ^0.1.0" to clipboard
flow_ui: ^0.1.0 copied to clipboard

Flow UI is an open-source Flutter UI library to build production-grade Chat & AI assistant interfaces.

Flow UI logo

flow_ui

pub package pub points github stars license: MIT

πŸ“š Documentation Β· 🧩 Playground Β· πŸ€“ API Reference Β· πŸ—ΊοΈ Roadmap

Flow UI is an open-source Flutter UI library to build production-grade Chat & AI assistant interfaces.

The flow_ui chat surface

Important

flow_ui is pre-1.0. The API is still settling, and minor releases may carry breaking changes β€” pin a minor version and read the changelog when upgrading.

What's in the box #

Component What it does
FlowChatScreen The full chat surface: bounded thread over a composer, centred at a readable width, with a zero state (greeting, lifted composer, starters) and a jump-to-latest button
FlowThread Scrollable conversation anchored to the newest message
FlowMessage One turn β€” ink-wash user bubble, plain assistant, error bubble, typed content parts
FlowStreamingText Animated text reveal while a reply arrives
FlowThinkingIndicator Turning, breathing asterisk with a shimmering label
FlowShimmerText Sweeping text highlight, static once settled
FlowMessageActions Copy / regenerate / edit / feedback row under a message
FlowComposer Multiline input with send/stop, attachments strip, and leading/trailing action slots
FlowMenu Icon-triggered menu with groups, submenus, and toggles β€” anchored card on desktop, bottom sheet on phones
FlowModelSelector Model picker with effort and overflow submenus, sheet on phones
FlowAttachmentGroup Image and file tiles with a type pill
FlowAttachmentPreview Full-screen image viewer with zoom and paging
FlowSuggestion / FlowSuggestionGroup Prompt starters β€” plain or outlined; scroll, wrap, or column layouts
FlowGreeting Zero-state headline
FlowTheme Design tokens (colors and typography) as a ThemeExtension, with light and dark presets

Getting started #

dependencies:
  flow_ui: ^0.1.0

Install the theme once (optional β€” without it, components fall back to a preset matching the ambient brightness):

MaterialApp(
  theme: ThemeData(extensions: [FlowTheme.light()]),
  darkTheme: ThemeData(
    brightness: Brightness.dark,
    extensions: [FlowTheme.dark()],
  ),
)

The default typography ships with the package β€” Figtree, bundled under the SIL Open Font License β€” so the theme renders as designed with no font setup.

Build a chat screen #

Messages are pure view models. Your app maps its own transport into FlowMessageData, and streaming is data, not streams: while a reply arrives, rebuild with copyWith carrying the grown text.

class ChatPage extends StatefulWidget {
  const ChatPage({super.key});

  @override
  State<ChatPage> createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  final ScrollController _scroll = ScrollController();
  List<FlowMessageData> _messages = const [];
  bool _generating = false;

  void _send(String text) async {
    final id = DateTime.now().microsecondsSinceEpoch.toString();
    setState(() {
      _messages = [
        ..._messages,
        FlowMessageData.text(id: id, role: FlowMessageRole.user, text: text),
        // An empty pending reply renders the thinking indicator.
        FlowMessageData(
          id: '$id-reply',
          role: FlowMessageRole.assistant,
          status: FlowMessageStatus.pending,
        ),
      ];
      _generating = true;
    });

    // Feed chunks from your backend as they arrive.
    var streamed = '';
    await for (final chunk in myBackend.reply(text)) {
      streamed += chunk;
      setState(() {
        _messages = [
          ..._messages.sublist(0, _messages.length - 1),
          _messages.last.copyWith(
            parts: [FlowTextPart(streamed)],
            status: FlowMessageStatus.streaming,
          ),
        ];
      });
    }

    setState(() {
      _messages = [
        ..._messages.sublist(0, _messages.length - 1),
        _messages.last.copyWith(status: FlowMessageStatus.complete),
      ];
      _generating = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: FlowChatScreen(
        empty: _messages.isEmpty,
        greeting: const FlowGreeting(
          icon: Icons.wb_twilight,
          text: 'Good afternoon',
        ),
        suggestions: FlowSuggestionGroup(
          layout: FlowSuggestionLayout.column,
          suggestions: [
            FlowSuggestion(
              label: 'Write an essay about life and enjoyment',
              icon: Icons.edit_note,
              onTap: () => _send('Write an essay about life and enjoyment'),
            ),
            FlowSuggestion(
              label: 'Create a Monday briefing from my tasks',
              icon: Icons.event_available,
              onTap: () => _send('Create a Monday briefing from my tasks'),
            ),
          ],
        ),
        thread: FlowThread(
          messages: _messages,
          controller: _scroll,
          thinkingLabel: 'Thinking…',
        ),
        threadController: _scroll,
        jumpToLatestTooltip: 'Jump to latest',
        composer: FlowComposer(
          placeholder: 'How can I help you today?',
          isStreaming: _generating,
          onSend: _send,
          onStop: myBackend.stop,
        ),
      ),
    );
  }
}

FlowChatScreen is body-only β€” it builds no Scaffold and no app bar, so your app keeps the chrome, the background, and the keyboard inset. See example/lib/main.dart for a complete runnable version of this page, and the live playground for a demo of every component with variants and code snippets.

Composer accessories #

The composer takes leading and trailing action slots. Drop in a FlowMenu (attachments, toggles) and a FlowModelSelector β€” both render an anchored card on wide layouts and a bottom sheet on phones:

FlowComposer(
  onSend: _send,
  leadingActions: [
    FlowMenu(
      icon: Icons.add,
      tooltip: 'Add to chat',
      entries: const [
        FlowMenuOption(id: 'files', icon: Icons.attach_file, label: 'Add files'),
        FlowMenuDivider(),
        FlowMenuOption(id: 'web', icon: Icons.public, label: 'Web search', selected: true),
      ],
      onSelected: _handleMenu,
    ),
  ],
  trailingActions: [
    FlowModelSelector(
      models: const [
        FlowModelOption(id: 'fast', label: 'Fast', description: 'Quick answers'),
        FlowModelOption(id: 'smart', label: 'Smart', description: 'Hard problems'),
      ],
      selectedId: _modelId,
      onSelected: (id) => setState(() => _modelId = id),
    ),
  ],
)

Message content is typed parts #

A message holds an ordered list of sealed FlowMessageParts β€” FlowTextPart, FlowAttachmentPart, and FlowCustomPart for anything the package doesn't know about. Custom parts render through a builder you supply, so hosts can inject arbitrary widgets (tool cards, citations, charts) without forking the message renderer:

FlowThread(
  messages: _messages,
  customPartBuilder: (context, message, part) {
    return switch (part.type) {
      'order-card' => OrderCard(order: part.data as Order),
      _ => null, // unknown parts are skipped
    };
  },
)

Attachments carry an ImageProvider, so network, file, memory, and asset images all work β€” the package never loads anything itself:

FlowMessageData(
  id: 'm1',
  role: FlowMessageRole.user,
  parts: [
    FlowAttachmentPart([
      FlowAttachment(id: 'a1', thumbnail: NetworkImage(url), kind: 'JPG', label: 'sunset.jpg'),
    ]),
    FlowTextPart('What do you think of this shot?'),
  ],
)

Theming #

FlowTheme carries two token sets β€” colors and typography. Role names follow Material 3's ColorScheme, so an existing scheme maps across, with one addition: the design draws content at three ink levels (onSurface, onSurfaceVariant, onSurfaceMuted) where M3 names two. Start from a preset and override what your brand needs:

FlowTheme(
  colors: FlowColors.dark.copyWith(primary: const Color(0xFF6C5CE7)),
  typography: FlowTypography.standard,
)

Spacing and corner radii are deliberately not tokens. Following Material's structure, each component bakes its own metrics from the Flow UI design file and exposes per-widget overrides (padding:, borderRadius:) where hosts retheme. Strings shown to the user (tooltips, placeholders, labels) are host-supplied, so localization stays in your app β€” the one exception, the model selector's effortLabel and moreModelsLabel English defaults, is overridable the same way.

Docs & playground #

Full documentation lives at flowui.stac.dev, and every component has a stage in the live playground β€” variant pills and code snippets included. The playground is also in the repo to run locally:

cd playground && flutter run -d chrome

License #

Code is released under the MIT License. The bundled Figtree font is licensed separately under the SIL Open Font License.

3
likes
160
points
79
downloads

Documentation

Documentation
API reference

Publisher

verified publisherstac.dev

Weekly Downloads

Flow UI is an open-source Flutter UI library to build production-grade Chat & AI assistant interfaces.

Homepage
Repository (GitHub)
View/report issues

Topics

#ai #chat #chatbot #assistant #ui

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flow_ui