Files
monisuo/.agents/skills/shadcn-ui-flutter/SKILL.md
2026-03-23 00:08:19 +08:00

878 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Shadcn UI for Flutter - 技能文档
> Flutter 版本的 shadcn/ui 组件库,提供美观、可定制的 UI 组件
## 📦 安装
```yaml
# pubspec.yaml
dependencies:
shadcn_ui: ^0.2.4 # 使用最新版本
```
```bash
flutter pub add shadcn_ui
```
---
## 🚀 快速开始
### 纯 Shadcn UI
```dart
import 'package:shadcn_ui/shadcn_ui.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ShadApp(
home: MyHomePage(),
);
}
}
```
### Shadcn + Material推荐
```dart
import 'package:shadcn_ui/shadcn_ui.dart';
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return ShadApp.custom(
themeMode: ThemeMode.dark,
darkTheme: ShadThemeData(
brightness: Brightness.dark,
colorScheme: const ShadSlateColorScheme.dark(),
),
appBuilder: (context) {
return MaterialApp(
theme: Theme.of(context),
localizationsDelegates: const [
GlobalShadLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
builder: (context, child) {
return ShadAppBuilder(child: child!);
},
home: MyHomePage(),
);
},
);
}
}
```
---
## 🎨 主题配置
### 颜色方案
支持的颜色方案:
- `blue` - 蓝色
- `gray` - 灰色
- `green` - 绿色
- `neutral` - 中性色
- `orange` - 橙色
- `red` - 红色
- `rose` - 玫瑰色
- `slate` - 石板色(推荐深色模式)
- `stone` - 石头色
- `violet` - 紫罗兰色
- `yellow` - 黄色
- `zinc` - 锌色
```dart
ShadApp(
darkTheme: ShadThemeData(
brightness: Brightness.dark,
colorScheme: const ShadSlateColorScheme.dark(),
),
theme: ShadThemeData(
brightness: Brightness.light,
colorScheme: const ShadZincColorScheme.light(),
),
)
```
### 动态切换主题
```dart
final lightColorScheme = ShadColorScheme.fromName('blue');
final darkColorScheme = ShadColorScheme.fromName('slate', brightness: Brightness.dark);
```
### 自定义颜色
```dart
ShadThemeData(
colorScheme: const ShadZincColorScheme.light(
custom: {
'brand': Color(0xFF00D4AA),
},
),
)
// 访问自定义颜色
ShadTheme.of(context).colorScheme.custom['brand']!
```
---
## 📝 文本样式
```dart
// 标题
Text('Heading 1 Large', style: ShadTheme.of(context).textTheme.h1Large)
Text('Heading 1', style: ShadTheme.of(context).textTheme.h1)
Text('Heading 2', style: ShadTheme.of(context).textTheme.h2)
Text('Heading 3', style: ShadTheme.of(context).textTheme.h3)
Text('Heading 4', style: ShadTheme.of(context).textTheme.h4)
// 正文
Text('Paragraph', style: ShadTheme.of(context).textTheme.p)
Text('Lead', style: ShadTheme.of(context).textTheme.lead)
Text('Large', style: ShadTheme.of(context).textTheme.large)
Text('Small', style: ShadTheme.of(context).textTheme.small)
Text('Muted', style: ShadTheme.of(context).textTheme.muted)
// 其他
Text('Blockquote', style: ShadTheme.of(context).textTheme.blockquote)
Text('Table', style: ShadTheme.of(context).textTheme.table)
Text('List', style: ShadTheme.of(context).textTheme.list)
```
### 自定义字体
```dart
// pubspec.yaml
flutter:
fonts:
- family: CustomFont
fonts:
- asset: fonts/CustomFont-Regular.ttf
// 使用
ShadThemeData(
textTheme: ShadTextTheme(
family: 'CustomFont',
),
)
// 或使用 Google Fonts
ShadThemeData(
textTheme: ShadTextTheme.fromGoogleFont(GoogleFonts.poppins),
)
```
---
## 🧩 核心组件
### Button 按钮
```dart
// 主要按钮
ShadButton(
child: const Text('Primary'),
onPressed: () {},
)
// 次要按钮
ShadButton.secondary(
child: const Text('Secondary'),
onPressed: () {},
)
// 危险按钮
ShadButton.destructive(
child: const Text('Delete'),
onPressed: () {},
)
// 边框按钮
ShadButton.outline(
child: const Text('Outline'),
onPressed: () {},
)
// 幽灵按钮
ShadButton.ghost(
child: const Text('Ghost'),
onPressed: () {},
)
// 链接按钮
ShadButton.link(
child: const Text('Link'),
onPressed: () {},
)
// 带图标
ShadButton(
leading: const Icon(LucideIcons.mail),
child: const Text('Login with Email'),
onPressed: () {},
)
// 加载状态
ShadButton(
leading: SizedBox.square(
dimension: 16,
child: CircularProgressIndicator(
strokeWidth: 2,
color: ShadTheme.of(context).colorScheme.primaryForeground,
),
),
child: const Text('Please wait'),
)
// 渐变和阴影
ShadButton(
gradient: const LinearGradient(colors: [Colors.cyan, Colors.indigo]),
shadows: [
BoxShadow(
color: Colors.blue.withOpacity(.4),
spreadRadius: 4,
blurRadius: 10,
offset: const Offset(0, 2),
),
],
child: const Text('Gradient'),
)
```
### IconButton 图标按钮
```dart
ShadIconButton(
icon: const Icon(LucideIcons.rocket),
onPressed: () {},
)
ShadIconButton.secondary(
icon: const Icon(LucideIcons.settings),
onPressed: () {},
)
ShadIconButton.destructive(
icon: const Icon(LucideIcons.trash),
onPressed: () {},
)
```
### Input 输入框
```dart
// 基础输入框
ShadInput(
placeholder: const Text('Enter your email'),
)
// 表单输入框
ShadInputFormField(
id: 'email',
label: const Text('Email'),
placeholder: const Text('Enter your email'),
description: const Text('We will never share your email.'),
validator: (value) {
if (value == null || value.isEmpty) {
return 'Email is required';
}
if (!value.contains('@')) {
return 'Invalid email format';
}
return null;
},
)
```
### Card 卡片
```dart
ShadCard(
width: 350,
title: Text('Create project', style: ShadTheme.of(context).textTheme.h4),
description: const Text('Deploy your new project in one-click.'),
footer: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
ShadButton.outline(
child: const Text('Cancel'),
onPressed: () {},
),
ShadButton(
child: const Text('Deploy'),
onPressed: () {},
),
],
),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 16),
child: Column(
children: [
const Text('Name'),
const SizedBox(height: 6),
const ShadInput(placeholder: Text('Project name')),
],
),
),
)
```
### Alert 警告
```dart
// 普通警告
ShadAlert(
icon: Icon(LucideIcons.terminal),
title: Text('Heads up!'),
description: Text('You can add components using the cli.'),
)
// 错误警告
ShadAlert.destructive(
icon: Icon(LucideIcons.circleAlert),
title: Text('Error'),
description: Text('Your session has expired.'),
)
```
### Badge 徽章
```dart
ShadBadge(child: const Text('Primary'))
ShadBadge.secondary(child: const Text('Secondary'))
ShadBadge.destructive(child: const Text('Destructive'))
ShadBadge.outline(child: const Text('Outline'))
```
### Checkbox 复选框
```dart
bool checked = false;
ShadCheckbox(
value: checked,
onChanged: (v) => setState(() => checked = v),
label: const Text('Accept terms and conditions'),
sublabel: const Text('You agree to our Terms of Service.'),
)
// 表单中使用
ShadCheckboxFormField(
id: 'terms',
initialValue: false,
inputLabel: const Text('I accept the terms'),
validator: (v) {
if (!v) return 'You must accept the terms';
return null;
},
)
```
### Switch 开关
```dart
bool enabled = false;
ShadSwitch(
value: enabled,
onChanged: (v) => setState(() => enabled = v),
)
// 表单中使用
ShadSwitchFormField(
id: 'notifications',
initialValue: false,
inputLabel: const Text('Enable notifications'),
)
```
### Select 选择器
```dart
final options = ['Option 1', 'Option 2', 'Option 3'];
String? selected;
ShadSelect<String>(
placeholder: const Text('Select an option'),
options: options
.map((e) => ShadOption(value: e, child: Text(e)))
.toList(),
selectedOptionBuilder: (context, value) {
return Text(value);
},
onChanged: (value) {
setState(() => selected = value);
},
)
// 表单中使用
ShadSelectFormField<String>(
id: 'framework',
label: const Text('Framework'),
placeholder: const Text('Select'),
options: [
ShadOption(value: 'flutter', child: Text('Flutter')),
ShadOption(value: 'react', child: Text('React')),
],
selectedOptionBuilder: (context, value) => Text(value),
)
```
### Dialog 对话框
```dart
// 显示对话框
showShadDialog(
context: context,
builder: (context) => ShadDialog(
title: const Text('Edit Profile'),
description: const Text('Make changes to your profile.'),
actions: [
ShadButton.outline(
child: const Text('Cancel'),
onPressed: () => Navigator.of(context).pop(),
),
ShadButton(
child: const Text('Save'),
onPressed: () => Navigator.of(context).pop(),
),
],
child: Container(
width: 375,
padding: const EdgeInsets.all(20),
child: Column(
children: [
ShadInput(placeholder: Text('Name')),
],
),
),
),
)
// 警告对话框
showShadDialog(
context: context,
builder: (context) => ShadDialog.alert(
title: const Text('Are you sure?'),
description: const Text('This action cannot be undone.'),
actions: [
ShadButton.outline(
child: const Text('Cancel'),
onPressed: () => Navigator.of(context).pop(false),
),
ShadButton.destructive(
child: const Text('Delete'),
onPressed: () => Navigator.of(context).pop(true),
),
],
),
)
```
### DatePicker 日期选择器
```dart
DateTime? selected;
ShadDatePicker(
selected: selected,
onChanged: (date) {
setState(() => selected = date);
},
)
// 日期范围选择器
ShadDatePicker.range()
// 表单中使用
ShadDatePickerFormField(
id: 'birthday',
label: const Text('Date of birth'),
description: const Text('Your date of birth.'),
validator: (v) {
if (v == null) return 'Date of birth is required';
return null;
},
)
```
### Calendar 日历
```dart
DateTime selected = DateTime.now();
ShadCalendar(
selected: selected,
fromMonth: DateTime(2024, 1),
toMonth: DateTime(2024, 12),
)
// 多选
ShadCalendar.multiple(
numberOfMonths: 2,
min: 5,
max: 10,
)
// 范围选择
ShadCalendar.range(
min: 2,
max: 5,
)
```
### Avatar 头像
```dart
ShadAvatar(
'https://example.com/avatar.jpg',
placeholder: Text('CN'),
)
```
### Accordion 手风琴
```dart
final details = [
(title: 'Is it acceptable?', content: 'Yes.'),
(title: 'Is it styled?', content: 'Yes.'),
];
ShadAccordion<({String content, String title})>(
children: details.map(
(detail) => ShadAccordionItem(
value: detail,
title: Text(detail.title),
child: Text(detail.content),
),
),
)
// 多开模式
ShadAccordion<({String content, String title})>.multiple(
children: details.map(...),
)
```
### Breadcrumb 面包屑
```dart
ShadBreadcrumb(
children: [
ShadBreadcrumbLink(
onPressed: () => print('Home'),
child: const Text('Home'),
),
ShadBreadcrumbLink(
onPressed: () => print('Components'),
child: const Text('Components'),
),
Text('Breadcrumb'),
],
)
// 带下拉菜单
ShadBreadcrumb(
children: [
ShadBreadcrumbLink(
onPressed: () {},
child: const Text('Home'),
),
ShadBreadcrumbDropdown(
items: [
ShadBreadcrumbDropMenuItem(
onPressed: () {},
child: const Text('Documentation'),
),
ShadBreadcrumbDropMenuItem(
onPressed: () {},
child: const Text('Themes'),
),
],
child: const Text('Components'),
),
Text('Breadcrumb'),
],
)
```
### Context Menu 右键菜单
```dart
ShadContextMenuRegion(
items: [
const ShadContextMenuItem(child: Text('Copy')),
const ShadContextMenuItem(child: Text('Cut')),
const ShadContextMenuItem(child: Text('Paste')),
const Divider(height: 8),
const ShadContextMenuItem(child: Text('Delete')),
],
child: Container(
width: 300,
height: 200,
child: const Text('Right click here'),
),
)
```
---
## 📋 表单管理
### 基础表单
```dart
final formKey = GlobalKey<ShadFormState>();
ShadForm(
key: formKey,
child: Column(
children: [
ShadInputFormField(
id: 'username',
label: const Text('Username'),
validator: (v) {
if (v == null || v.isEmpty) return 'Username is required';
if (v.length < 3) return 'Username must be at least 3 characters';
return null;
},
),
ShadInputFormField(
id: 'email',
label: const Text('Email'),
validator: (v) {
if (v == null || !v.contains('@')) return 'Invalid email';
return null;
},
),
ShadButton(
child: const Text('Submit'),
onPressed: () {
if (formKey.currentState!.saveAndValidate()) {
print('Form value: ${formKey.currentState!.value}');
}
},
),
],
),
)
```
### 初始值
```dart
ShadForm(
initialValue: {
'username': 'john_doe',
'email': 'john@example.com',
},
child: Column(...),
)
```
### 嵌套表单(点号表示法)
```dart
ShadForm(
child: Column(
children: [
ShadInputFormField(
id: 'user.name',
label: const Text('Name'),
),
ShadInputFormField(
id: 'user.email',
label: const Text('Email'),
),
],
),
)
// 输出: {'user': {'name': '...', 'email': '...'}}
```
---
## 🎯 图标
使用 Lucide Icons
```dart
Icon(LucideIcons.home)
Icon(LucideIcons.settings)
Icon(LucideIcons.user)
Icon(LucideIcons.search)
Icon(LucideIcons.mail)
Icon(LucideIcons.heart)
Icon(LucideIcons.star)
```
浏览所有图标https://lucide.dev/icons/
---
## 🎬 动画
所有组件都支持 `flutter_animate` 动画:
```dart
ShadButton(
child: const Text('Animated Button'),
onPressed: () {},
// 添加动画效果
).animate().fadeIn().slideX()
```
---
## 📦 依赖库
shadcn_ui 包含以下优秀的库:
1. **flutter_animate** - 动画库
2. **lucide_icons_flutter** - 图标库
3. **two_dimensional_scrollables** - 表格组件
4. **intl** - 国际化
5. **universal_image** - 图片加载
---
## 💡 最佳实践
### 1. 与 Material 混用
```dart
// 推荐:使用 ShadApp.custom 包装 MaterialApp
ShadApp.custom(
appBuilder: (context) => MaterialApp(
theme: Theme.of(context),
builder: (context, child) => ShadAppBuilder(child: child!),
home: MyPage(),
),
)
```
### 2. 主题定制
```dart
ShadThemeData(
colorScheme: const ShadZincColorScheme.dark(),
primaryButtonTheme: const ShadButtonTheme(
backgroundColor: Color(0xFF00D4AA),
),
textTheme: ShadTextTheme(
family: 'CustomFont',
),
)
```
### 3. 表单验证
```dart
ShadInputFormField(
id: 'password',
label: const Text('Password'),
obscureText: true,
validator: (v) {
if (v == null || v.isEmpty) return 'Password is required';
if (v.length < 8) return 'Password must be at least 8 characters';
return null;
},
)
```
---
## 🚨 注意事项
1. **必须使用 ShadApp**:不要直接使用 MaterialApp要使用 ShadApp 或 ShadApp.custom
2. **主题访问**:使用 `ShadTheme.of(context)` 访问主题
3. **图标**:使用 `LucideIcons` 而不是 Material Icons
4. **表单**:推荐使用 ShadForm 配合各种 FormField 组件
---
## 📚 参考资源
- 官方文档https://mariuti.com/flutter-shadcn-ui/
- LLM 友好文档https://mariuti.com/flutter-shadcn-ui/llms.txt
- GitHubhttps://github.com/nank1ro/flutter-shadcn-ui
- Lucide 图标https://lucide.dev/icons/
---
## 🎨 示例主题配置
### 深色主题(推荐)
```dart
ShadApp(
themeMode: ThemeMode.dark,
darkTheme: ShadThemeData(
brightness: Brightness.dark,
colorScheme: const ShadSlateColorScheme.dark(),
),
home: MyApp(),
)
```
### 浅色主题
```dart
ShadApp(
themeMode: ThemeMode.light,
theme: ShadThemeData(
brightness: Brightness.light,
colorScheme: const ShadZincColorScheme.light(),
),
home: MyApp(),
)
```
### 自定义品牌色
```dart
ShadThemeData(
colorScheme: const ShadZincColorScheme.dark(
custom: {
'brand': Color(0xFF00D4AA),
'success': Color(0xFF10B981),
'warning': Color(0xFFF59E0B),
'error': Color(0xFFEF4444),
},
),
)
// 使用
Container(
color: ShadTheme.of(context).colorScheme.custom['brand'],
)
```
---
**文档版本**: 0.2.4
**最后更新**: 2026-03-22