kin_ui 1.7.1
kin_ui: ^1.7.1 copied to clipboard
A Native inspired Flutter design system. Tokens, primitives, components, adaptive shell, and templates out of the box.
Kin UI — Setup & Guide #
Package:
kin_ui· Version:1.7.0· Prefix:Kin· Flutter SDK:^3.11.0
Table of Contents #
- Installation
- Root Setup
- Theming
- Layout System
- Token System
- Primitives
- Components
- Shell & Navigation
- Templates
1. Installation #
Add kin_ui to your app's pubspec.yaml:
dependencies:
kin_ui: ^1.7.0
Then run:
flutter pub get
Import the barrel file:
import 'package:kin_ui/kin_ui.dart';
This single import gives you every token, primitive, component, shell widget, and template.
2. Root Setup #
Wrap your MaterialApp home with KinApp — this sets up layout detection and BouncingScrollPhysics globally. You must provide your own KinTheme as a ThemeExtension.
import 'package:flutter/material.dart';
import 'package:kin_ui/kin_ui.dart';
void main() => runApp(const MainApp());
class MainApp extends StatelessWidget {
const MainApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
theme: ThemeData(
useMaterial3: true,
brightness: Brightness.light,
scaffoldBackgroundColor: const Color(0xFFF5F5F0),
colorScheme: const ColorScheme.light(
primary: Color(0xFF6C63FF),
surface: Color(0xFFF5F5F0),
onSurface: Color(0xFF1C1C1E),
error: Color(0xFFFF453A),
outline: Color(0xFFE0E0E0),
),
extensions: const [myLightTheme],
),
darkTheme: ThemeData(
useMaterial3: true,
brightness: Brightness.dark,
scaffoldBackgroundColor: const Color(0xFF1C1C1E),
colorScheme: const ColorScheme.dark(
primary: Color(0xFF6C63FF),
surface: Color(0xFF1C1C1E),
onSurface: Color(0xFFFFFFFF),
error: Color(0xFFFF453A),
outline: Color(0xFF3A3A3C),
),
extensions: const [myDarkTheme],
),
home: const KinApp(child: MyShellScreen()),
);
}
}
KinAppinjectsKinLayoutand setsBouncingScrollPhysicsglobally. No other config needed.
3. Theming #
KinTheme is a Flutter ThemeExtension. Every component reads colors from it, and the package now ships with ready-to-use Ink presets for light and dark apps.
Color Tokens #
const KinTheme({
required this.primary, // Brand accent, active states
required this.primaryContainer, // Lighter accent container
required this.surface, // Page / scaffold background
required this.surfaceVariant, // Cards, elevated surfaces
required this.onSurface, // Primary text
required this.onSurfaceVariant, // Secondary / hint text
required this.outline, // Borders, dividers
required this.destructive, // Delete, error, warning
this.success, // Success states (default: iOS green)
this.warning, // Warning states (default: iOS amber)
})
Reading the Theme #
final theme = KinTheme.of(context);
// theme.primary, theme.surface, theme.onSurface, etc.
Creating Your Theme #
Define a light and dark variant that matches your brand:
final myLightTheme = KinTheme.inkLight;
final myDarkTheme = KinTheme.inkDark;
Then pass them to your MaterialApp:
MaterialApp(
theme: ThemeData(
extensions: const [myLightTheme],
// ... your colorScheme, scaffoldBackgroundColor, etc.
),
darkTheme: ThemeData(
extensions: const [myDarkTheme],
// ...
),
);
4. Layout System #
KinLayout is an InheritedWidget injected by KinApp. Components read it — never use MediaQuery directly inside components.
final layout = KinLayout.of(context);
layout.type // LayoutType.compact / medium / expanded
layout.columns // 4 / 8 / 12
layout.margins // EdgeInsets (auto-calculated)
layout.isCompact // bool
layout.isMedium // bool
layout.isExpanded // bool
Breakpoints #
| Type | Width | Columns | Nav Style |
|---|---|---|---|
compact |
< 600dp | 4 | KinBottomBar |
medium |
600–1200dp | 8 | KinNavRail |
expanded |
≥ 1200dp | 12 | KinNavDrawer |
5. Token System #
All design values live in token classes. Never hardcode values in components.
Spacing #
KinSpacing.xs // 4.0
KinSpacing.sm // 8.0
KinSpacing.screen // 12.0 - shell edges, floating chrome
KinSpacing.md // 16.0
KinSpacing.lg // 24.0
KinSpacing.xl // 40.0
Radius #
// Raw values
KinRadius.sm // 12.0 — inputs, chips
KinRadius.md // 16.0 — general
KinRadius.lg // 26.0 — cards
KinRadius.xl // 28.0 — sheets, dialogs
KinRadius.pill // 999.0 — bottom nav, badges
// Pre-built BorderRadius
KinRadius.smAll, KinRadius.mdAll, KinRadius.lgAll,
KinRadius.xlAll, KinRadius.pillAll
Motion #
// Durations
KinMotion.fast // 150ms
KinMotion.normal // 300ms
KinMotion.slow // 500ms
// Curves
KinMotion.enter // Curves.easeOutCubic
KinMotion.exit // Curves.easeInCubic
// Physics
KinMotion.spring // SpringDescription(mass: 1, stiffness: 400, damping: 30)
KinMotion.pressScale // 0.97
// Fluid Springs
KinMotion.fluidSnap // stiffness: 300, damping: 22 — tab switches, interactive
KinMotion.fluidBounce // stiffness: 220, damping: 18 — carousel, reveals
KinMotion.fluidGentle // stiffness: 170, damping: 20 — subtle morphing
Material #
Surface material presets for glass and tinted effects.
// Default — solid background (existing behavior)
KinMaterial.opaque()
// Apple-style frosted glass with backdrop blur
KinMaterial.glass(thickness: KinGlassThickness.regular)
// Thickness: ultraThin, thin, regular, thick, ultraThick
// Semi-transparent tinted surface
KinMaterial.tinted(tintColor: Colors.blue, tintOpacity: 0.12)
Apply to any supporting component:
KinCard(material: KinMaterial.glass(), child: myContent)
KinSheet.show(context: context, material: KinMaterial.glass(), child: myContent)
KinSurface(material: KinMaterial.tinted(), child: myContent)
Typography #
KinTypography.displayLarge // 34px, w700 — page titles
KinTypography.titleLarge // 22px, w600 — section headers
KinTypography.titleMedium // 18px, w600 — sub-headers
KinTypography.bodyLarge // 16px, w400 — content
KinTypography.bodyMedium // 14px, w400 — secondary content
KinTypography.labelLarge // 13px, w500 — nav labels, chips
KinTypography.labelSmall // 11px, w500 — captions
6. Primitives #
Primitives are the atoms. Compose them to build components — never use raw Flutter widgets at component level.
KinPress #
Wraps any widget with spring-driven press animation + haptic + disabled state.
KinPress(
onTap: () {},
onLongPress: () {},
enabled: true, // default: true
scale: 0.97, // default: KinMotion.pressScale
child: myWidget,
)
Uses dual springs: tight press-down (500/30) and bouncy release (300/18) for fluid feel.
### KinSurface
Themed container. Reads `KinTheme` automatically. Supports frosted glass in dark mode.
```dart
KinSurface(
variant: SurfaceVariant.card, // .page, .card, .elevated
borderRadius: KinRadius.lgAll,
padding: EdgeInsets.all(KinSpacing.md),
frosted: false, // true → backdrop blur in dark mode
child: myContent,
)
KinText #
Typography-aware text with named constructors.
KinText('Hello', style: KinTypography.titleLarge, color: theme.onSurface)
// Named constructors
KinText.titleLarge('Title', color: theme.onSurface)
KinText.bodyMedium('Body text', color: theme.onSurfaceVariant)
7. Components #
All components read KinTheme from context — no explicit theming needed.
KinCard #
KinCard(
child: myContent,
onTap: () {}, // enables ink tap + spring press animation
onLongPress: () {},
variant: KinCardVariant.standard, // .standard, .elevated, .outlined
padding: EdgeInsets.all(KinSpacing.md),
borderRadius: KinRadius.lgAll,
heroTag: 'my-hero', // wraps in Hero for shared element transitions
material: KinMaterial.glass(), // optional — glass or tinted surface
)
KinButton #
KinButton(
label: 'Save',
onTap: () {},
variant: KinButtonVariant.primary, // .primary, .secondary, .ghost, .destructive
size: KinButtonSize.medium, // .small, .medium, .large
isLoading: false,
enabled: true,
leading: Icon(Icons.save), // optional icon
)
KinSheet #
Bottom sheet with spring animation, drag-to-dismiss, named detents, and keyboard awareness.
KinSheet.show(
context: context,
child: mySheetContent,
showHandle: true, // drag handle at top
isDismissible: true,
enableDrag: true,
detents: [KinSheetDetent.medium, KinSheetDetent.large], // .small (25%), .medium (50%), .large (90%)
material: KinMaterial.glass(), // optional glass/tinted material
padding: null, // auto-calculated with safe area
);
KinDialog #
Bottom-aligned dialog with slide-up transition and frosted glass.
final confirmed = await KinDialog.show(
context: context,
title: 'Delete item?',
message: 'This cannot be undone.',
confirmLabel: 'Delete',
cancelLabel: 'Cancel',
isDestructive: true,
);
KinTextField #
KinTextField(
hint: 'Enter email',
label: 'Email',
errorText: null, // shows error state when set
onChanged: (val) {},
onSubmitted: (val) {},
controller: myController,
obscureText: false,
enabled: true,
keyboardType: TextInputType.emailAddress,
maxLines: 1,
maxLength: 100, // character limit
showCharCounter: true, // shows "42/100" below field
inputFormatters: [], // TextInputFormatter list
prefix: Icon(Icons.email),
suffix: Icon(Icons.clear),
focusNode: myFocusNode,
)
KinChip #
KinChip(
label: 'Filter',
isSelected: true,
onTap: () {},
leading: Icon(Icons.filter),
)
KinToggle #
Compact animated switch (42×26).
KinToggle(
value: isOn,
onChanged: (val) {},
enabled: true,
)
KinSearchBar #
Pill-shaped search input with frosted glass in dark mode.
KinSearchBar(
hint: 'Search',
onChanged: (val) {},
onSubmitted: (val) {},
controller: myController,
leading: Icon(Icons.search),
trailing: Icon(Icons.mic),
focusNode: myFocusNode,
)
KinSegmentedControl #
KinSegmentedControl(
segments: ['All', 'Active', 'Done'],
selectedIndex: 0,
onChanged: (index) {},
)
KinExpansionCard #
KinExpansionCard(
title: 'Advanced Options',
subtitle: 'Tap to expand',
leading: Icon(Icons.settings),
initiallyExpanded: false,
child: myExpandedContent,
)
KinSlider #
KinSlider(
value: 0.5,
onChanged: (val) {},
min: 0.0,
max: 1.0,
divisions: 10,
enabled: true,
)
KinToast #
KinToast.show(
context,
message: 'Saved successfully',
duration: Duration(seconds: 3),
leading: Icon(Icons.check),
);
KinSkeleton #
// Inline placeholder
KinSkeleton(width: 200, height: 16)
// Presets
KinSkeleton.card() // height: 120, card-shaped
KinSkeleton.circle(size: 40) // circular avatar placeholder
// List tile placeholder (separate class)
KinSkeletonListTile(showAvatar: true)
KinProgress #
// Linear
KinProgress.linear(value: 0.6, strokeWidth: 4)
KinProgress.linear() // indeterminate (value: null)
// Circular
KinProgress.circular(value: 0.6, strokeWidth: 3)
KinProgress.circular() // indeterminate
KinBadge #
KinBadge(
count: 3, // null → dot-only badge
show: true,
color: Colors.red, // optional override
child: Icon(Icons.mail),
)
KinPullToRefresh #
KinPullToRefresh(
onRefresh: () async { await fetchData(); },
child: myScrollView,
)
KinColorPicker #
Bottom-aligned color picker with HSV selection. Frosted glass in dark mode.
final color = await KinColorPicker.show(
context: context,
initialColor: Color(0xFFE84141),
);
KinDatePicker #
Bottom-aligned calendar date picker. Frosted glass in dark mode.
final date = await KinDatePicker.show(
context: context,
initialDate: DateTime.now(),
firstDate: DateTime(2000),
lastDate: DateTime(2100),
);
KinTimePicker #
Scroll-wheel time picker with flat columns and edge-fading text. Supports 12-hour (scrollable AM/PM) and 24-hour formats.
final time = await KinTimePicker.show(
context: context,
initialTime: TimeOfDay.now(),
use24HourFormat: false,
);
KinImagePicker #
Native-style bottom-sheet source selector for picking an image from camera or gallery. You supply the actual picking logic via callback.
final bytes = await KinImagePicker.show(
context: context,
onPickImage: (source) async {
// source is KinImageSource.camera or .gallery
// Use image_picker, file_picker, etc. and return Uint8List? bytes
return null;
},
);
KinPhotoPicker #
Dialog-style expandable photo grid. Starts compact and swipe-expands to 75 % of screen height. Great for in-app gallery selection.
final bytes = await KinPhotoPicker.show(
context: context,
onLoadPhotos: () async {
// Return List<KinPhotoItem> with image bytes
return myPhotos.map((p) => KinPhotoItem(bytes: p)).toList();
},
);
KinSnackbar #
Overlay-based snackbar with variant colors, optional action button, and swipe-to-dismiss.
KinSnackbar.show(
context,
message: 'File saved',
variant: KinSnackbarVariant.success, // .standard, .success, .warning, .error
actionLabel: 'Undo',
onAction: () {},
);
KinFloatingButton #
Themed floating action button with size and variant options.
KinFloatingButton(
icon: Icon(Icons.add),
onTap: () {},
variant: KinFloatingButtonVariant.primary, // .primary, .secondary, .surface, .destructive
size: KinFloatingButtonSize.regular, // .small, .regular
)
// Extended variant with label
KinFloatingButton.extended(
icon: Icon(Icons.add),
label: 'Create',
onTap: () {},
)
KinPopupMenu #
Contextual popup menu anchored to a trigger widget. Frosted glass in dark mode.
KinPopupMenu(
items: [
KinPopupMenuItem(label: 'Edit', leading: Icon(Icons.edit_outlined), onTap: () {}),
KinPopupMenuItem(label: 'Duplicate', leading: Icon(Icons.copy_outlined), onTap: () {}),
const KinPopupMenuDivider(),
KinPopupMenuItem(label: 'Delete', isDestructive: true, onTap: () {}),
],
child: KinButton(label: 'Actions', variant: KinButtonVariant.secondary),
)
KinDrawer #
Push-body drawer that slides content aside with scale and rounded-corner animation.
final controller = KinDrawerController();
KinDrawer(
controller: controller,
header: KinText.titleMedium('Menu'),
edge: KinDrawerEdge.left, // .left or .right
body: myPageContent,
children: [
KinDrawerSection(title: 'Navigation'),
KinDrawerItem(label: 'Home', leading: Icon(Icons.home_outlined), onTap: () {}),
KinDrawerItem(label: 'Settings', leading: Icon(Icons.settings_outlined), onTap: () {}),
],
)
// Open / close / toggle
controller.open();
controller.close();
controller.toggle();
KinCarousel #
Auto-playing page carousel with three layout variants and two indicator styles.
KinCarousel.card(
height: 180,
autoPlay: true,
indicator: KinCarouselIndicator.dash, // .dot, .dash
children: [page1, page2, page3],
)
// Also: KinCarousel.stacked(...)
KinStepper #
Multi-step progress indicator supporting horizontal and vertical layouts.
KinStepper(
steps: [
KinStep(title: 'Cart'),
KinStep(title: 'Address'),
KinStep(title: 'Payment'),
KinStep(title: 'Done'),
],
currentStep: 1,
orientation: KinStepperOrientation.horizontal, // .horizontal, .vertical
variant: KinStepperVariant.numbered, // .numbered, .dot, .progress
onStepTapped: (index) {},
)
KinChart #
Lightweight CustomPaint chart with bar, line, and pie types.
KinChart.bar(
entries: [
KinChartEntry(label: 'Mon', value: 40),
KinChartEntry(label: 'Tue', value: 70),
KinChartEntry(label: 'Wed', value: 55),
],
showValues: true,
)
// Also: KinChart.line(...), KinChart.pie(...)
KinPageView #
Page view with multiple transition effects and indicator styles.
KinPageView.parallax(
indicator: KinPageIndicator.dash, // .dot, .dash, .numbered, .none
height: 200,
children: [page1, page2, page3],
)
// Also: KinPageView.stack(...)
KinDataTable #
Themed data table with sorting, selection, and horizontal scroll.
KinDataTable(
columns: [
KinDataColumn(label: 'Name', width: 120, sortable: true),
KinDataColumn(label: 'Role', width: 100),
],
rows: [
KinDataRow(cells: [KinDataCell.text('Alice'), KinDataCell.text('Designer')]),
KinDataRow(cells: [KinDataCell.text('Bob'), KinDataCell.text('Engineer')]),
],
)
KinAccessibility #
Wraps a subtree with accessibility enhancements: colour-blindness simulation, greyscale, high contrast, reduced motion, and text scaling.
KinAccessibility(
filter: KinAccessibilityFilter.deuteranopia, // .none, .greyscale, .highContrast, .reducedMotion, .inverted, .deuteranopia, .protanopia
textScaleFactor: 1.2,
boldText: true,
child: myApp,
)
KinEmptyState #
Centered placeholder for empty lists and zero-data screens.
KinEmptyState(
icon: Icons.inbox_outlined,
title: 'No messages',
subtitle: 'Your inbox is empty',
actionLabel: 'Compose',
onAction: () {},
compact: false, // reduces spacing for constrained layouts
)
KinOtpField #
Auto-advancing OTP code input.
KinOtpField(
length: 6,
onCompleted: (code) => print('OTP: $code'),
onChanged: (value) {},
)
KinNumberPad #
On-screen numeric keypad with haptic feedback.
KinNumberPad(
onDigit: (digit) {},
onDelete: () {},
onSubmit: () {}, // optional — shows submit key
)
KinTagInput #
Chip-based tag input with inline text field.
KinTagInput(
tags: ['flutter', 'dart'],
onTagsChanged: (tags) {},
hint: 'Add tag...',
)
KinTimeline #
Vertical timeline with themed nodes and custom content.
KinTimeline(
items: [
KinTimelineItem(
title: 'Order placed',
subtitle: 'Apr 20, 2026',
icon: Icons.shopping_cart,
),
KinTimelineItem(
title: 'Shipped',
subtitle: 'Apr 21, 2026',
),
],
connectorStyle: KinTimelineConnectorStyle.solid, // .solid, .dashed
)
8. Shell & Navigation #
KinScaffold #
The adaptive scaffold. Automatically switches bottom nav / rail / drawer based on KinLayout.
KinScaffold(
navItems: [
KinNavItem(icon: Icons.home_rounded, label: 'Home'),
KinNavItem(icon: Icons.search_rounded, label: 'Search'),
KinNavItem(icon: Icons.settings_rounded, label: 'Settings'),
],
currentIndex: _tab,
onIndexChanged: (i) => setState(() => _tab = i),
body: _pages[_tab],
drawerHeader: 'My App', // optional — shown in expanded drawer
searchBar: KinSearchBar(), // optional — floats above bottom bar
)
Compact →
KinBottomBar. Medium →KinNavRail. Expanded →KinNavDrawer. Zero extra code.
KinNavItem #
const KinNavItem(
icon: Icons.home_rounded,
label: 'Home',
activeIcon: Icons.home, // optional distinct icon for selected state
)
KinTopBar #
Used inside CustomScrollView as a sliver. Large title collapses on scroll. Leading and actions get frosted glass containers on collapse.
CustomScrollView(
slivers: [
KinTopBar(
title: 'Feed',
expandedHeight: 120,
leading: IconButton(
icon: Icon(Icons.arrow_back_rounded),
onPressed: () {},
),
actions: [
IconButton(icon: Icon(Icons.search), onPressed: () {}),
IconButton(icon: Icon(Icons.more_vert), onPressed: () {}),
],
),
// your sliver content...
],
)
Leading → circular frosted container on collapse. Actions → circular (1) or pill (2+).
KinListTile #
Includes grey ink ripple on tap alongside scale animation.
KinListTile(
title: 'Notifications',
subtitle: 'Manage your alerts',
leading: Icon(Icons.notifications),
trailing: KinToggle(value: true, onChanged: (_) {}),
onTap: () {},
)
KinBottomBar (standalone) #
Can be used independently outside KinScaffold:
KinBottomBar(
currentIndex: 0,
onTabChanged: (i) {},
items: [Icon(Icons.home), Icon(Icons.search)],
labels: ['Home', 'Search'],
height: 58,
borderRadius: 32,
)
Defaults:
- translucent glass material instead of a solid slab
- 12px compact-screen horizontal shell padding
- labels auto-hide in tighter landscape widths to keep the bar balanced
9. Templates #
Templates are pre-wired KinTopBar + scroll view + layout logic. Provide data, not layout.
KinFeedTemplate #
Scrollable card feed with optional pull-to-refresh.
KinFeedTemplate(
title: 'News',
leading: IconButton(icon: Icon(Icons.menu), onPressed: () {}),
actions: [IconButton(icon: Icon(Icons.filter), onPressed: () {})],
onRefresh: () async { await loadFeed(); },
bottomPadding: 160,
children: [
KinCard(child: Text('Post 1')),
KinCard(child: Text('Post 2')),
],
)
KinDetailTemplate #
Article / product detail with optional hero and sticky action bar.
KinDetailTemplate(
title: 'Article',
leading: IconButton(icon: Icon(Icons.arrow_back), onPressed: () {}),
actions: [IconButton(icon: Icon(Icons.share), onPressed: () {})],
hero: Image.asset('assets/hero.png'),
heroHeight: 260,
stickyAction: KinButton(label: 'Buy Now', onTap: () {}),
children: [
KinText.bodyLarge('Article body...'),
],
)
KinSettingsTemplate #
Grouped settings with card sections.
KinSettingsTemplate(
title: 'Settings',
header: myProfileCard,
footer: KinText.labelSmall('v1.0.0'),
sections: [
KinSettingsSection(
header: 'General',
children: [
KinListTile(
title: 'Notifications',
leading: Icon(Icons.notifications),
trailing: KinToggle(value: true, onChanged: (_) {}),
),
],
),
],
)
KinDashboardTemplate #
Stat cards grid + activity feed.
KinDashboardTemplate(
title: 'Dashboard',
actions: [IconButton(icon: Icon(Icons.refresh), onPressed: () {})],
stats: [
KinCard(child: myStatWidget),
KinCard(child: myStatWidget),
],
feed: [
KinListTile(title: 'New user signed up', subtitle: '2m ago'),
KinListTile(title: 'Order received', subtitle: '5m ago'),
],
)
License #
MIT — see LICENSE for details.