Skip to main content

prism_kit 1.1.0 copy "prism_kit: ^1.1.0" to clipboard
prism_kit: ^1.1.0 copied to clipboard

A unified Flutter component library with a shared design system: adaptive toasts, banners, sheets, data tables, steppers, skeletons, avatars, OTP and chip inputs, timelines, a command palette, search, [...]

Prism

πŸ”· Prism

One dependency. 27 adaptive Flutter components. Native feel on phone, desktop & web.

pub.dev MIT Flutter tests


Prism is a component library for Flutter that removes the most repetitive parts of app building. Instead of assembling a dozen fragmented pub.dev packages β€” each with its own style, API and quirks β€” you install one package and get a coherent, cross-platform toolkit with a shared design system:

  • Adaptive by default β€” every component responds to window size and target platform. A toast is bottom-centered on a phone and stacked top-right on a desktop window. A settings page becomes a two-pane layout on tablets. A dialog renders Material on Android and Cupertino-style behavior on macOS β€” from a single call.
  • Local-first & privacy-friendly β€” persistence uses shared_preferences on-device; no accounts, no telemetry, no network calls unless you explicitly opt in (the AI client and updater are the only networked parts, and you own the endpoints).
  • A real design system β€” PrismTokens (spacing, radii, motion) and PrismColors (semantic roles) so every component shares one visual language and follows light/dark and Material You automatically.
  • Zero heavy dependencies β€” only http, markdown, shared_preferences, url_launcher, meta and material_color_utilities.
  • Battle-tested β€” 385 unit & widget tests, analyzed clean.

πŸ“¦ Install #

dependencies:
  prism_kit: ^1.0.0
flutter pub add prism_kit

One import gives you everything:

import 'package:prism_kit/prism.dart';

🧩 Components #

# Component One-liner
1 AdaptiveScaffold Bottom nav bar β†’ navigation rail β†’ expanded rail, by window width
2 AdaptiveDialog Platform-adaptive dialogs from a single call
3 PrismAi Provider-agnostic streaming AI chat: OpenAI, Ollama, Groq, DeepSeek, Gemini, Anthropic β€” with typed events, tool-calling loop and a ready chat view
4 Banner Dismissible announcement banners; dismissals persist across restarts
5 Changelog Parse a Keep-a-Changelog markdown file into collapsible "What's new" cards
6 CodeView Syntax-highlighted, copyable code viewer with pluggable tokenizers
7 Form Declarative forms: fields, validators, submit lifecycle states
8 Image Retrying network image provider with configurable backoff strategy
9 Markdown Fast, plugin-free CommonMark + GFM renderer with syntax highlighting
10 MasterDetail Responsive two-pane layouts (list β†’ detail) that push on compact windows
11 Pagination Infinite-scroll list with page loader, empty/error/retry states
12 Palette Extract dominant/vibrant/muted color palettes from images
13 Search Adaptive search: local datasets or debounced async backends
14 Settings iOS-style grouped settings pages with searchable tiles
15 StateViews Loading / empty / error / success state widgets driven by one notifier
16 Toast Adaptive toasts: bottom-centered on mobile, stacked top-right on desktop
17 Updater In-app update flow: version gates, rollouts, skip-version, download progress
18 Skeleton Shimmering placeholders: text, card, tile list, circle β€” reduced-motion aware
19 Avatar Image with deterministic initials fallback, presence dots and badges
20 ChipInput Tag/token entry with paste splitting, duplicates, limits and suggestions
21 OtpField OTP/PIN entry with paste, auto-submit, SMS autofill and grouping
22 Timeline Vertical event timeline with connectors, semantic roles and rich nodes
23 Stepper Wizard progress: horizontal on wide, compact "Step X of Y" on phones
24 DataTable Sortable table that becomes labelled cards on narrow windows
25 CommandPalette ⌘K launcher with fuzzy search, grouping and keyboard navigation
26 AdaptiveSheet Bottom sheet on phones, centred dialog on desktop β€” one call
27 ConnectionBanner Offline/online banner with debounce, retry and live-region announcements

Design system #

PrismTokens Spacing scale, corner radii, motion durations and breakpoints, installable via ThemeData.extensions
PrismColors Semantic roles (success / warning / error / info) plus raised and sunken surfaces, derived from any ColorScheme

πŸš€ Usage #

Every snippet below is copy-pasteable and matches the shipped API.

AdaptiveScaffold β€” responsive navigation #

Renders a NavigationBar under 600 dp, a NavigationRail up to 839 dp and an expanded labeled rail beyond:

PrismAdaptiveScaffold(
  destinations: const [
    AdaptiveDestination(icon: Icons.home_outlined, selectedIcon: Icons.home, label: 'Home'),
    AdaptiveDestination(icon: Icons.search_outlined, selectedIcon: Icons.search, label: 'Search'),
    AdaptiveDestination(icon: Icons.settings_outlined, label: 'Settings'),
  ],
  bodyBuilder: (index) => const Center(child: Text('Page')),
)

Toast β€” adaptive notifications #

PrismToast.success(context, 'Saved successfully');
PrismToast.error(context, 'Something went wrong');
PrismToast.info(context, 'Heads up!');
PrismToast.warning(context, 'Low battery');

// Full control
PrismToast.show(
  context,
  const PrismToastMessage(
    message: 'Report synced',
    title: 'Done',
    duration: Duration(seconds: 5),
    actionLabel: 'View',
    onAction: openReport,
  ),
);

Position adapts automatically: bottom-centered on phones, stacked top-right where there's room.

Column(
  children: [
    PrismBanner(
      announcements: const [
        PrismAnnouncement(
          id: 'release-2-1',            // reuse the id + bump version to re-show
          version: 2,
          title: 'New in 2.1',
          message: 'Dark mode and faster sync!',
          actionLabel: "See what's new",
          severity: PrismSeverity.info,
        ),
      ],
    ),
    // …the rest of your page…
  ],
)

Dismissals persist via shared_preferences. Banners can be scheduled (startsAt/endsAt) and prioritized (newest, oldest, severity).

Search β€” local or backend #

// Local dataset
PrismSearch<String>(
  items: const ['Dart', 'Flutter', 'Firebase'],
  itemLabel: (item) => item,
  onSelected: (item) => print('picked $item'),
)

// Debounced async backend
PrismSearch<City>(
  onSearch: (query) => api.searchCities(query),
  itemLabel: (city) => city.name,
  onSelected: (city) => go(city),
)

Settings β€” grouped, searchable pages #

final controller = PrismSettingsController();

PrismSettingsPage(
  controller: controller,
  searchable: true,
  sections: const [
    PrismSettingsSection(
      title: 'Appearance',
      tiles: [
        PrismSwitchTile(
          common: PrismTileCommon(
            key: 'dark-mode',
            title: 'Dark mode',
            subtitle: 'Follow the system theme',
            leading: Icons.dark_mode,
          ),
        ),
        PrismActionTile(
          common: PrismTileCommon(
            key: 'language',
            title: 'Language',
            subtitle: 'English',
            leading: Icons.language,
          ),
          onTap: openLanguagePicker,
        ),
      ],
    ),
  ],
)

Read & write values through the controller: controller.set('dark-mode', true) / controller.get<bool>('dark-mode').

Markdown + CodeView β€” rich content #

PrismMarkdown('# Hello\n\n- **Rich** markdown\n- `inline code`')

PrismCodeView(code: "void main() => print('Hello');")

The markdown renderer is plugin-free (pure Dart CommonMark + GFM) and pairs perfectly with ChangelogView bullets.

Form β€” declarative & validated #

final controller = PrismFormController();

PrismForm(
  controller: controller,
  child: Column(children: [
    PrismTextField.email(name: 'email', label: 'Email'),
    PrismPasswordField(name: 'password', common: PrismFieldCommon(
      name: 'password', label: 'Password',
      validators: [Validators.required('Enter a password')],
    )),
    PrismDropdownField<String>(
      common: PrismFieldCommon(name: 'plan', label: 'Plan'),
      items: const ['Free', 'Pro'],
      itemBuilder: (plan) => plan,
    ),
    FilledButton(
      onPressed: () async {
        if (await controller.submit((values) => api.register(values))) {
          // success β€” status transitions surfaced via controller
        }
      },
      child: const Text('Create account'),
    ),
  ]),
)

Validators come from Validators.required(), Validators.email() etc., or you can pass any (value) => messageOrNull function. Submit runs all validations, then your action, and exposes typed statuses (pristine, editing, submitting, success, failure).

StateViews β€” one widget, four states #

final notifier = PrismViewStateNotifier<List<Note>>(const PrismViewLoading());

PrismStateView(
  state: notifier.value,
  successBuilder: (notes) => NotesList(notes),
  emptyMessage: 'No notes yet',
)

Transition with notifier.value = PrismViewSuccess(data) / PrismViewError(error) / PrismViewEmpty().

Pagination β€” infinite scroll #

PrismPaginatedList<Msg>(
  pageLoader: (page) => api.fetchMessages(page),  // return [] to signal end
  itemBuilder: (context, msg, index) => MsgTile(msg),
)

First-load spinner, error state with retry, and "no more items" footer are handled for you.

MasterDetail β€” two-pane layouts #

PrismMasterDetail<Conversation>(
  items: conversations,
  masterBuilder: (context, conv, selected) => ConvTile(conv, selected: selected),
  detailBuilder: (context, conv) => ChatScreen(conv),
)

On compact windows it pushes a detail route (PrismCompactBehavior.push); on wide windows it becomes a side-by-side split with a configurable pane width.

Palette β€” extract colors from images #

final palette = await PrismPalette.fromImage(image);
final themeColor = palette.vibrantColor ?? palette.dominantColor;

AI β€” streaming chat with any provider #

final controller = AiChatController(
  client: OpenAiStreamClient(
    baseUrl: 'https://api.openai.com/v1',   // or http://localhost:11434/v1 (Ollama)
    apiKey: 'sk-…',
  ),
  model: 'gpt-4o-mini',
);

AiChatView(controller: controller)   // full streaming chat UI

Typed events (AiDelta, AiToolCall, AiDone, AiError), SSE parsing, and an automatic tool-calling agent loop (AiAgent) are included. Swap in GeminiStreamClient, AnthropicStreamClient, or implement AiStreamClient for anything else.

Updater β€” in-app updates #

PrismUpdateController(
  source: RemoteUpdateSource(url: 'https://example.com/updates.json'),
  // updates.json: {"versions": {"1.1.0": {"notes": "...", "url": "..."}}}
)

Handles version comparison, staged rollouts, skip-version persistence and download progress events.

ChangelogView β€” What's new #

ChangelogView(
  changelog: await rootBundle.loadString('CHANGELOG.md'),
  highlightVersion: '2.1.0',
)

Parses Keep-a-Changelog format into collapsible version cards; WhatsNewSheet shows it as a modal.

Skeleton β€” loading that keeps the page shape #

if (loading)
  const PrismSkeletonList.cards(count: 3)
else
  NoteList(notes)

// Or compose primitives
PrismSkeletonCard(mediaHeight: 140, lines: 3)
PrismSkeletonText(lines: 2, lastLineFraction: 0.4)
PrismSkeleton.circle(diameter: 44)

The shimmer stops automatically when the platform requests reduced motion, so you never ship a strobe to a user who asked for calm.

Avatar β€” never a broken image #

PrismAvatar(name: 'Ayesha Khan')
PrismAvatar(name: 'Ayesha Khan', imageUrl: user.photoUrl, size: 56)
PrismAvatar(name: 'Ayesha Khan', presence: PrismPresence.online)
PrismAvatar(name: 'Acme Inc', shape: PrismAvatarShape.rounded, size: 48)

Initials come from PrismAvatar.initialsFor(name); the fallback colour is picked deterministically from the name, so a person looks the same on every device. Failed image loads silently fall back to initials.

ChipInput β€” tags without the pain #

PrismChipInput(
  label: 'Tags',
  value: tags,
  suggestions: const ['dart', 'flutter', 'ui'],
  maxChips: 5,
  helperText: 'Paste a comma-separated list to add several at once.',
  onChanged: (next) => setState(() => tags = next),
)

Handles comma/newline/semicolon paste, duplicate rejection (case-insensitive), backspace-to-delete, custom validators and an optional suggestion overlay.

OtpField β€” paste, autofill, auto-submit #

PrismOtpField(
  length: 6,
  groupSeparatorAfter: 3,     // renders 123 456
  errorText: error,
  onCompleted: (code) => verify(code),
)

PrismOtpField(length: 4, obscure: true)   // PIN entry

A single transparent field sits behind the digit boxes, so IME, paste, selection, autofill (oneTimeCode) and screen readers all behave natively.

Timeline β€” order tracking and activity feeds #

PrismTimeline(
  entries: [
    PrismTimelineEntry(
      title: 'Order placed',
      timestamp: '09:12',
      semantic: PrismSemantic.success,
    ),
    PrismTimelineEntry(
      title: 'Out for delivery',
      subtitle: 'Driver Sam Β· 2 stops away',
      isActive: true,
      child: TrackLiveCard(),
    ),
    PrismTimelineEntry(
      title: 'Delivery failed',
      semantic: PrismSemantic.error,
    ),
  ],
  onEntryTap: (index, entry) => open(index),
)

Stepper β€” wizards that stay readable #

PrismStepper(
  steps: const [
    PrismStep(title: 'Account'),
    PrismStep(title: 'Profile'),
    PrismStep(title: 'Preferences', isOptional: true),
  ],
  currentIndex: step,
  erroredIndices: {1},
  onStepTapped: (i) => setState(() => step = i),
)

Horizontal with connectors on wide windows; a compact Step 2 of 3 header with a progress bar on phones.

DataTable β€” one widget, two layouts #

PrismDataTable<Invoice>(
  rows: invoices,
  onRowTap: (row, i) => open(row),
  columns: [
    PrismColumn(label: 'Invoice', alwaysVisible: true,
      cell: (c, r, i) => Text(r.id),
      sortComparator: (a, b) => a.id.compareTo(b.id)),
    PrismColumn(label: 'Client', cell: (c, r, i) => Text(r.client)),
    PrismColumn(label: 'Total', numeric: true, cell: (c, r, i) => Text('\$${r.total}')),
  ],
)

Desktop gets a sortable table with a header row; phones get the same data as cards with field labels. No horizontal scrolling, no hidden columns.

CommandPalette β€” a ⌘K launcher #

final chosen = await PrismCommandPalette.show(
  context: context,
  commands: [
    PrismCommand(label: 'New note', icon: Icons.add, group: 'File',
      shortcut: '⌘N', onSelect: createNote),
    PrismCommand(label: 'Toggle theme', group: 'View',
      keywords: const ['dark', 'appearance'], onSelect: toggleTheme),
  ],
);

Fuzzy subsequence search (so tt finds Toggle theme), grouping, arrow/Enter/Escape navigation, and a full-screen route on phones.

AdaptiveSheet β€” bottom sheet or dialog, automatically #

final confirmed = await PrismAdaptiveSheet.show<bool>(
  context: context,
  title: 'Delete this note?',
  subtitle: 'It will move to Trash for 30 days.',
  child: const Text('You can restore it any time.'),
  actions: [
    const PrismSheetAction(label: 'Cancel', value: false),
    PrismSheetAction(label: 'Delete', value: true, isDestructive: true),
  ],
);

ConnectionBanner β€” offline UX in one line #

PrismConnectionBanner(
  source: () => Connectivity().onConnectivityChanged.map((r) => r != ConnectivityResult.none),
  onlineMessage: 'Back online β€” syncing…',
  onRetry: refetch,
)

Dampens short blips with a settle delay, flashes a reconnected message, and announces changes through a polite live region.

Design tokens β€” one visual language #

MaterialApp(
  theme: ThemeData(
    colorSchemeSeed: Colors.teal,
    extensions: const [
      PrismTokens(spaceMd: 20, radiusMd: 16, durationMedium: Duration(milliseconds: 200)),
    ],
  ),
)

// Anywhere:
Padding(padding: EdgeInsets.all(context.prismTokens.spaceMd), child: …)
Container(color: context.prismColors.surfaceRaised)
Container(color: context.prismColors.forRole(PrismSemantic.success).background)

Every component reads these tokens, so one override restyles the whole app consistently.


πŸ–₯ Platform support #

Component Android iOS macOS Windows Linux Web
AdaptiveScaffold / MasterDetail / Dialog βœ… βœ… βœ… βœ… βœ… βœ…
Toast / Banner / StateViews / Pagination βœ… βœ… βœ… βœ… βœ… βœ…
Search / Settings / Form βœ… βœ… βœ… βœ… βœ… βœ…
Markdown / CodeView / Changelog / Palette βœ… βœ… βœ… βœ… βœ… βœ…
Image (NetworkImageWithRetry) βœ… βœ… βœ… βœ… βœ… βœ…
AI client βœ… βœ… βœ… βœ… βœ… βœ…
Updater βœ… βœ… βœ… βœ… βœ… βœ…
Skeleton / Avatar / ChipInput / OtpField βœ… βœ… βœ… βœ… βœ… βœ…
Timeline / Stepper / DataTable βœ… βœ… βœ… βœ… βœ… βœ…
CommandPalette / AdaptiveSheet / ConnectionBanner βœ… βœ… βœ… βœ… βœ… βœ…
PrismTokens / PrismColors βœ… βœ… βœ… βœ… βœ… βœ…

πŸ§ͺ Quality #

  • 385 tests (unit + widget) across all components
  • Zero analyzer issues; flutter_lints enforced
  • Every public API documented

🧭 Migrating from the standalone packages #

If you previously used an individual prism_* package, swap the dependency and change one import:

- prism_toast: ^0.1.0
+ prism_kit: ^1.0.0

- import 'package:prism_toast/prism_toast.dart';
+ import 'package:prism_kit/prism.dart';

All class names stay identical (PrismToast, PrismBanner, PrismSearch, …).

πŸ“– Full example #

The example/ folder contains a gallery app with an interactive demo for every component β€” run it with:

cd example && flutter run

🀝 Contributing #

Issues and PRs welcome β€” pick a component and improve it: github.com/awaisdev5765/Prism.

β˜• Support #

If Prism helps you ship faster, please consider supporting my work:

Buy Me A Coffee

πŸ“„ License #

MIT β€” see LICENSE.

0
likes
0
points
1.19k
downloads

Publisher

unverified uploader

Weekly Downloads

A unified Flutter component library with a shared design system: adaptive toasts, banners, sheets, data tables, steppers, skeletons, avatars, OTP and chip inputs, timelines, a command palette, search, settings, forms, pagination, markdown, a code viewer and an AI chat client β€” responsive on phone, desktop and web.

Repository (GitHub)
View/report issues

Topics

#widget #components #ui #adaptive #responsive

License

unknown (license)

Dependencies

flutter, http, markdown, material_color_utilities, meta, shared_preferences, url_launcher

More

Packages that depend on prism_kit