AVATARS & CHIPS

Chip

A single, reusable Flutter chip widget driven by enums. Covers four kinds — basic (filled or outlined), input (leading avatar or deletable ×), choice (with a lavender selected state), and status (success, warning, danger, info via a semantic palette) — all pill-shaped with hairline borders. Built on the FlutterKit house theme. No external packages, fully DartPad-ready.

chiptagfilter
Preview11 variants

Filled

Design

Outlined

Design

Leading icon

Flutter

What's Included

  • Basic chips — filled or outlined with an optional leading icon
  • Input chips — leading avatar with initials, or a deletable trailing ×
  • Choice chips — tappable with a lavender-tinted selected state
  • Status chips — success, warning, danger, info from a semantic triplet palette
  • Pill shape with hairline borders, sized for touch
  • One AppChip widget driven entirely by ChipKind and ChipStatus enums

Use Cases

  • Filter bars — tappable choice chips that toggle a selected lavender state
  • Tag clouds — read-only basic chips for categories or skills
  • Token / recipient fields — input chips with avatars and a removable ×
  • Inline status — success / warning / danger / info badges next to a row

PRO TIP

Keep a single AppChip in your design system and switch its look with the kind and status enums instead of hand-rolling Chip, InputChip, ChoiceChip, and FilterChip separately. Wrap groups of chips in a Wrap widget with spacing: 8 and runSpacing: 8 so they reflow cleanly across screen widths, and drive the selected flag from your own state so a filter bar stays a single source of truth.

Complete Dart Code

Copy this into your Flutter project. No external packages required.

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

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        backgroundColor: const Color(0xFFFAFAFB),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(24),
            child: Wrap(
              spacing: 8,
              runSpacing: 8,
              children: [
                const AppChip(label: 'Basic'),
                const AppChip(label: 'Outlined', outlined: true),
                const AppChip(label: 'Flutter', icon: Icons.bolt),
                const AppChip(label: 'Ada Lovelace', avatarText: 'AL'),
                AppChip(label: 'Removable', onDeleted: () {}),
                AppChip(
                  label: 'Selected',
                  kind: ChipKind.choice,
                  selected: true,
                  onTap: () {},
                ),
                const AppChip(
                  label: 'Success',
                  kind: ChipKind.status,
                  status: ChipStatus.success,
                ),
                const AppChip(
                  label: 'Warning',
                  kind: ChipKind.status,
                  status: ChipStatus.warning,
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

/// FlutterKit Chip — house design system (Linear lavender, light theme).
/// One enum-driven chip: basic, input, choice, and status kinds.
/// rohansurve.in/flutterkit/chip

enum ChipKind { basic, input, choice, status }

enum ChipStatus { success, warning, danger, info }

class AppChip extends StatelessWidget {
  const AppChip({
    super.key,
    required this.label,
    this.kind = ChipKind.basic,
    this.outlined = false,
    this.selected = false,
    this.status = ChipStatus.info,
    this.icon,
    this.avatarText,
    this.onTap,
    this.onDeleted,
  });

  /// Chip text.
  final String label;

  /// basic · input · choice · status.
  final ChipKind kind;

  /// Basic/choice: transparent fill + hairline border instead of a muted fill.
  final bool outlined;

  /// Choice chip: lavender-tinted selected state.
  final bool selected;

  /// Status chip color (uses the semantic triplet).
  final ChipStatus status;

  /// Optional leading icon.
  final IconData? icon;

  /// Input chip: leading avatar with initials (e.g. 'AL').
  final String? avatarText;

  /// Makes the whole chip tappable (choice / filter chips).
  final VoidCallback? onTap;

  /// Shows a trailing × that calls this (input chips).
  final VoidCallback? onDeleted;

  // ── House tokens — docs/flutterkit-design.md §1 ──
  static const Color _accent = Color(0xFF5E6AD2);
  static const Color _accentTint = Color(0xFFEEF0FB);
  static const Color _accentBorder = Color(0xFFD5D9F4);
  static const Color _surfaceMuted = Color(0xFFF4F5F7);
  static const Color _hairline = Color(0xFFE6E8EB);
  static const Color _hairlineStrong = Color(0xFFD5D8DD);
  static const Color _ink = Color(0xFF1C1E26);

  // Semantic triplets: [fg, bg, border].
  static const Map<ChipStatus, List<Color>> _semantic = {
    ChipStatus.success: [
      Color(0xFF1F9D55),
      Color(0xFFE7F6EE),
      Color(0xFFBCE6CE),
    ],
    ChipStatus.warning: [
      Color(0xFFB7791F),
      Color(0xFFFCF3E3),
      Color(0xFFEBD9AE),
    ],
    ChipStatus.danger: [
      Color(0xFFD92D20),
      Color(0xFFFDECEA),
      Color(0xFFF4C7C1),
    ],
    ChipStatus.info: [
      Color(0xFF3E63DD),
      Color(0xFFEAEFFC),
      Color(0xFFC4D2F7),
    ],
  };

  @override
  Widget build(BuildContext context) {
    // Resolve fill / text / border from kind + state.
    late final Color fill;
    late final Color fg;
    late final Color border;

    switch (kind) {
      case ChipKind.status:
        final t = _semantic[status]!;
        fg = t[0];
        fill = t[1];
        border = t[2];
        break;
      case ChipKind.choice:
        if (selected) {
          fg = _accent;
          fill = _accentTint;
          border = _accentBorder;
          break;
        }
        fg = _ink;
        fill = outlined ? Colors.transparent : _surfaceMuted;
        border = outlined ? _hairlineStrong : _hairline;
        break;
      case ChipKind.basic:
      case ChipKind.input:
        fg = _ink;
        fill = outlined ? Colors.transparent : _surfaceMuted;
        border = outlined ? _hairlineStrong : _hairline;
        break;
    }

    final children = <Widget>[];

    if (avatarText != null) {
      children
        ..add(Container(
          width: 20,
          height: 20,
          alignment: Alignment.center,
          decoration: const BoxDecoration(
            color: _accent,
            shape: BoxShape.circle,
          ),
          child: Text(
            avatarText!,
            style: const TextStyle(
              color: Colors.white,
              fontSize: 10,
              fontWeight: FontWeight.w600,
            ),
          ),
        ))
        ..add(const SizedBox(width: 6));
    } else if (icon != null) {
      children
        ..add(Icon(icon, size: 16, color: fg))
        ..add(const SizedBox(width: 6));
    }

    children.add(Text(
      label,
      style: TextStyle(
        color: fg,
        fontSize: 13,
        fontWeight: FontWeight.w500,
        height: 1.2,
      ),
    ));

    if (onDeleted != null) {
      children
        ..add(const SizedBox(width: 6))
        ..add(GestureDetector(
          onTap: onDeleted,
          child: Icon(Icons.close, size: 14, color: fg.withOpacity(0.7)),
        ));
    }

    final chip = Container(
      padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
      decoration: BoxDecoration(
        color: fill,
        borderRadius: BorderRadius.circular(999), // pill
        border: Border.all(color: border),
      ),
      child: Row(mainAxisSize: MainAxisSize.min, children: children),
    );

    if (onTap == null) return chip;

    return Material(
      color: Colors.transparent,
      borderRadius: BorderRadius.circular(999),
      child: InkWell(
        onTap: onTap,
        borderRadius: BorderRadius.circular(999),
        child: chip,
      ),
    );
  }
}

// ── Usage ──
// const AppChip(label: 'Design')                         // basic filled
// const AppChip(label: 'Design', outlined: true)         // basic outlined
// const AppChip(label: 'Flutter', icon: Icons.bolt)      // leading icon
// const AppChip(label: 'Ada Lovelace', avatarText: 'AL') // input avatar
// AppChip(label: 'Removable', onDeleted: () {})          // deletable ×
// AppChip(label: 'All', kind: ChipKind.choice, selected: true, onTap: () {})
// const AppChip(label: 'Live', kind: ChipKind.status, status: ChipStatus.success)

How to use this widget

  1. 1Copy the complete Dart code above
  2. 2Create a new file — app_chip.dart in your Flutter project
  3. 3Use AppChip(label: 'Design') — switch the look with the kind, status, selected, and outlined parameters

Related components

Buttons & Actions

Button Widget

One enum-driven button — 5 types, solid/pill/outline styles, 4 sizes, icon + loading

View component →
Feedback & Alerts

Alert Widget

Dismissible alert banners in 5 styles — Info, Success, Warning, and Danger

View component →
Data Display

Stat Card

Dashboard metric cards in 4 styles — Simple, Icon, Trend, and Progress

View component →