kin_ui 1.0.1
kin_ui: ^1.0.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· Prefix:Kin· Flutter SDK:^3.11.0
Table of Contents #
- Installation
- Root Setup
- Theming
- Layout System
- Token System
- Primitives
- Components
- Shell & Navigation
- Templates
- Showcase Screens
1. Installation #
Add kin_ui to your app's pubspec.yaml:
dependencies:
kin_ui:
git:
url: <repo> # Visit pub.dev for direct access
ref: v1.0.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 — nothing is hardcoded. You must create your own theme.
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
})
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:
const myLightTheme = KinTheme(
primary: Color(0xFF6C63FF),
primaryContainer: Color(0xFFE8E6FF),
surface: Color(0xFFF5F5F0),
surfaceVariant: Color(0xFFFFFFFF),
onSurface: Color(0xFF1C1C1E),
onSurfaceVariant: Color(0xFF8E8E93),
outline: Color(0xFFE0E0E0),
destructive: Color(0xFFFF453A),
);
const myDarkTheme = KinTheme(
primary: Color(0xFF6C63FF),
primaryContainer: Color(0xFF3D35A3),
surface: Color(0xFF1C1C1E),
surfaceVariant: Color(0xFF2C2C2E),
onSurface: Color(0xFFFFFFFF),
onSurfaceVariant: Color(0xFF8E8E93),
outline: Color(0xFF3A3A3C),
destructive: Color(0xFFFF453A),
);
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.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
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 press behavior — scale animation + haptic + disabled state.
KinPress(
onTap: () {},
onLongPress: () {},
enabled: true, // default: true
scale: 0.97, // default: KinMotion.pressScale
child: myWidget,
)
KinSurface #
Themed container. Reads KinTheme automatically. Supports frosted glass in dark mode.
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 + scale animation
onLongPress: () {},
variant: KinCardVariant.standard, // .standard, .elevated, .outlined
padding: EdgeInsets.all(KinSpacing.md),
borderRadius: KinRadius.lgAll,
)
KinButton #
KinButton(
label: 'Save',
onTap: () {},
variant: KinButtonVariant.primary, // .primary, .secondary, .ghost, .destructive
isLoading: false,
enabled: true,
leading: Icon(Icons.save), // optional icon
)
KinSheet #
Bottom sheet with spring animation, drag-to-dismiss, and keyboard awareness.
KinSheet.show(
context: context,
child: mySheetContent,
showHandle: true, // drag handle at top
isDismissible: true,
enableDrag: true,
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,
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),
);
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(
initialIndex: 0,
onTabChanged: (i) {},
items: [Icon(Icons.home), Icon(Icons.search)],
labels: ['Home', 'Search'],
height: 58,
borderRadius: 32,
)
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'),
],
)
10. Showcase Screens #
10. Showcase Screens #
Every showcase screen is built only using Kin components — no raw Flutter widgets at screen level. Lives in lib/example/.
| Screen | File | Components Featured |
|---|---|---|
| Component Gallery | component_gallery_showcase.dart |
All Tier 1–5 components, pickers |
| Feed Screen | feed_showcase.dart |
KinFeedTemplate, KinCard, KinTopBar |
| Detail Screen | detail_showcase.dart |
KinDetailTemplate, KinButton, KinSheet |
| Settings Screen | settings_showcase.dart |
KinSettingsTemplate, KinListTile, KinToggle |
| Dashboard Screen | dashboard_showcase.dart |
KinDashboardTemplate, KinProgress, KinBadge |
| Responsive Preview | responsive_preview_showcase.dart |
All three layout modes side by side |
Run the app to browse all screens:
flutter run
For what's coming next, see ROADMAP.md