This commit is contained in:
2026-03-23 00:08:19 +08:00
parent ca33e0d3c5
commit 2fdd842b89
11 changed files with 1196 additions and 380 deletions

View File

@@ -0,0 +1,516 @@
---
name: clean-code
description: Clean Code 代码质量审查和重构指南。用于代码审查、重构、提升代码质量、减少技术债务。触发词代码审查、clean code、重构、代码质量、技术债务、代码规范。
---
# Clean Code 技能
帮助编写高质量、可维护、可读性强的代码。
## 核心原则
### 1. 有意义的命名
```typescript
// ❌ 坏
const d = new Date();
const ymd = date.split('-');
// ✅ 好
const currentDate = new Date();
const [year, month, day] = date.split('-');
```
**规则:**
- 使用描述性名称,避免缩写
- 名称应该表达意图
- 避免误导性名称
- 做有意义的区分(不是 a1, a2
- 使用可搜索的名称
- 类名用名词,方法名用动词
### 2. 函数
```typescript
// ❌ 坏 - 做太多事
function processUser(user: User) {
// 验证
if (!user.email) throw new Error('Email required');
if (!user.name) throw new Error('Name required');
// 保存
database.save(user);
// 发送邮件
emailService.send(user.email, 'Welcome!');
// 记录日志
logger.log(`User ${user.name} created`);
}
// ✅ 好 - 单一职责
function validateUser(user: User): void {
if (!user.email) throw new Error('Email required');
if (!user.name) throw new Error('Name required');
}
function saveUser(user: User): void {
database.save(user);
}
function sendWelcomeEmail(user: User): void {
emailService.send(user.email, 'Welcome!');
}
function logUserCreation(user: User): void {
logger.log(`User ${user.name} created`);
}
function processUser(user: User) {
validateUser(user);
saveUser(user);
sendWelcomeEmail(user);
logUserCreation(user);
}
```
**规则:**
- 函数应该小(<20行
- 只做一件事
- 每个函数一个抽象层级
- 使用描述性名称
- 函数参数越少越好理想0-3个
- 避免副作用
- 分隔指令与询问
### 3. 注释
```typescript
// ❌ 坏 - 多余的注释
// 检查用户是否已激活
if (user.isActive) { ... }
// ✅ 好 - 代码自解释
if (user.isActive) { ... }
// ✅ 好 - 解释为什么
// 使用 setTimeout 而不是 setInterval 避免重叠执行
setTimeout(processQueue, 1000);
```
**规则:**
- 好的代码是自解释的
- 注释不能弥补糟糕的代码
- 用代码表达意图
- 好的注释法律信息、解释意图、警示、TODO
- 坏的注释:喃喃自语、多余的、误导的、注释掉的代码
### 4. 格式
```typescript
// ❌ 坏
export class User{constructor(private name:string,private age:number){}
getName(){return this.name;}}
// ✅ 好
export class User {
constructor(
private name: string,
private age: number
) {}
getName(): string {
return this.name;
}
}
```
**规则:**
- 垂直格式:概念之间用空行分隔
- 水平格式:行宽<120字符
- 缩进统一使用2或4空格
- 团队规则:遵循项目既定风格
### 5. 对象和数据结构
```typescript
// ❌ 坏 - 暴露内部结构
class User {
public name: string;
public age: number;
}
// ✅ 好 - 隐藏实现
class User {
private _name: string;
private _age: number;
get name(): string {
return this._name;
}
set age(value: number) {
if (value < 0) throw new Error('Invalid age');
this._age = value;
}
}
```
**规则:**
- 数据抽象:隐藏实现
- 数据、对象的反对称性
- 得墨忒耳定律:模块不应知道它操作对象的内部细节
### 6. 错误处理
```typescript
// ❌ 坏 - 返回 null
function getUser(id: string): User | null {
return database.find(id);
}
// ✅ 好 - 抛出异常
function getUser(id: string): User {
const user = database.find(id);
if (!user) throw new UserNotFoundError(id);
return user;
}
// ✅ 好 - 使用 Special Case 模式
class NullUser implements User {
name = 'Guest';
age = 0;
}
```
**规则:**
- 使用异常而非返回码
- 先写 Try-Catch-Finally
- 给出异常的上下文
- 别返回 null 值
- 别传递 null 值
### 7. 边界
```typescript
// ❌ 坏 - 直接依赖第三方类
import { Map } from 'third-party-lib';
class UserCollection {
private users: Map<string, User>;
}
// ✅ 好 - 使用适配器模式
interface UserMap {
get(key: string): User;
set(key: string, user: User): void;
}
class ThirdPartyUserMap implements UserMap {
private map: Map<string, User>;
get(key: string): User {
return this.map.get(key);
}
set(key: string, user: User): void {
this.map.set(key, user);
}
}
```
**规则:**
- 隐藏第三方代码
- 使用适配器模式
- 边界处的代码需要清晰的分割和测试
### 8. 单元测试
```typescript
// ❌ 坏 - 不清晰的测试
test('user', () => {
const u = new User('John', 25);
expect(u.getName()).toBe('John');
expect(u.getAge()).toBe(25);
});
// ✅ 好 - BUILD-OPERATE-CHECK 模式
test('shouldReturnUserName', () => {
// BUILD
const user = new User('John', 25);
// OPERATE
const name = user.getName();
// CHECK
expect(name).toBe('John');
});
// ✅ 好 - Given-When-Then 模式
test('shouldReturnUserName', () => {
// Given
const user = new User('John', 25);
// When
const name = user.getName();
// Then
expect(name).toBe('John');
});
```
**规则:**
- F.I.R.S.T 原则:
- Fast快速
- Independent独立
- Repeatable可重复
- Self-Validating自验证
- Timely及时
- 每个测试一个断言
- 单一概念
- 测试代码和生产代码一样重要
### 9. 类
```typescript
// ❌ 坏 - 大类
class UserManager {
createUser() {}
deleteUser() {}
updateUser() {}
sendEmail() {}
validateEmail() {}
generateReport() {}
}
// ✅ 好 - 单一职责
class UserService {
create(user: User) {}
delete(id: string) {}
update(user: User) {}
}
class EmailService {
send(email: string, content: string) {}
validate(email: string): boolean {}
}
class ReportService {
generate(userId: string): Report {}
}
```
**规则:**
- 类应该小
- 单一职责原则SRP
- 内聚性:方法和数据互相依赖
- 保持内聚性就会得到许多短小的类
### 10. 系统
```typescript
// ❌ 坏 - 硬编码依赖
class UserService {
private db = new Database(); // 硬编码
}
// ✅ 好 - 依赖注入
class UserService {
constructor(private db: Database) {}
}
// ✅ 好 - 工厂模式
class ServiceFactory {
static createUserService(): UserService {
return new UserService(new Database());
}
}
```
**规则:**
- 分离构造和使用
- 依赖注入
- 扩充AOP面向切面编程
- 测试驱动系统架构
## 代码审查清单
### 命名
- [ ] 变量名是否描述了其用途?
- [ ] 函数名是否描述了其行为?
- [ ] 类名是否描述了其职责?
- [ ] 名称是否一致?
### 函数
- [ ] 函数是否小于20行
- [ ] 函数是否只做一件事?
- [ ] 函数参数是否<=3个
- [ ] 函数是否有副作用?
- [ ] 函数名是否描述性?
### 结构
- [ ] 代码是否有清晰的层次结构?
- [ ] 类是否遵循单一职责原则?
- [ ] 是否有重复代码?
- [ ] 依赖是否清晰?
### 测试
- [ ] 是否有足够的测试覆盖?
- [ ] 测试是否快速?
- [ ] 测试是否独立?
- [ ] 测试是否清晰?
### 错误处理
- [ ] 是否处理了所有可能的错误?
- [ ] 错误信息是否清晰?
- [ ] 是否避免了返回 null
## 重构技巧
### 提取方法
```typescript
// Before
function printOwing(invoice: Invoice) {
let outstanding = 0;
// 打印横幅
console.log('***********************');
console.log('*** Customer Owes ***');
console.log('***********************');
// 计算未付款
for (const order of invoice.orders) {
outstanding += order.amount;
}
// 打印详情
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
// After
function printOwing(invoice: Invoice) {
printBanner();
const outstanding = calculateOutstanding(invoice);
printDetails(invoice, outstanding);
}
function printBanner() {
console.log('***********************');
console.log('*** Customer Owes ***');
console.log('***********************');
}
function calculateOutstanding(invoice: Invoice): number {
return invoice.orders.reduce((sum, order) => sum + order.amount, 0);
}
function printDetails(invoice: Invoice, outstanding: number) {
console.log(`name: ${invoice.customer}`);
console.log(`amount: ${outstanding}`);
}
```
### 内联方法
```typescript
// Before
function getRating(driver: Driver): number {
return moreThanFiveLateDeliveries(driver) ? 2 : 1;
}
function moreThanFiveLateDeliveries(driver: Driver): boolean {
return driver.numberOfLateDeliveries > 5;
}
// After
function getRating(driver: Driver): number {
return driver.numberOfLateDeliveries > 5 ? 2 : 1;
}
```
### 提取变量
```typescript
// Before
if (platform.toUpperCase().indexOf('MAC') > -1 &&
browser.toUpperCase().indexOf('IE') > -1 &&
wasInitialized() && resize > 0) {
// do something
}
// After
const isMacOs = platform.toUpperCase().indexOf('MAC') > -1;
const isIE = browser.toUpperCase().indexOf('IE') > -1;
const wasResized = resize > 0;
if (isMacOs && isIE && wasInitialized() && wasResized) {
// do something
}
```
### 分解条件
```typescript
// Before
if (date.before(SUMMER_START) || date.after(SUMMER_END)) {
charge = quantity * winterRate + winterServiceCharge;
} else {
charge = quantity * summerRate;
}
// After
if (isSummer(date)) {
charge = summerCharge(quantity);
} else {
charge = winterCharge(quantity);
}
function isSummer(date: Date): boolean {
return !date.before(SUMMER_START) && !date.after(SUMMER_END);
}
function summerCharge(quantity: number): number {
return quantity * summerRate;
}
function winterCharge(quantity: number): number {
return quantity * winterRate + winterServiceCharge;
}
```
### 以多态取代条件
```typescript
// Before
function calculatePay(employee: Employee): number {
switch (employee.type) {
case 'ENGINEER':
return employee.monthlySalary;
case 'SALESMAN':
return employee.monthlySalary + employee.commission;
case 'MANAGER':
return employee.monthlySalary + employee.bonus;
default:
throw new Error('Invalid employee type');
}
}
// After
abstract class Employee {
abstract calculatePay(): number;
}
class Engineer extends Employee {
calculatePay(): number {
return this.monthlySalary;
}
}
class Salesman extends Employee {
calculatePay(): number {
return this.monthlySalary + this.commission;
}
}
class Manager extends Employee {
calculatePay(): number {
return this.monthlySalary + this.bonus;
}
}
```
## 使用方法
1. **代码审查时**:参考清单逐项检查
2. **重构时**:应用重构技巧
3. **新功能开发时**:遵循核心原则
4. **代码坏味道识别**:参考常见问题
告诉我需要审查的代码,我会帮你识别问题并提供改进建议!

View File

@@ -0,0 +1,160 @@
---
name: ralph-loop
description: Ralph Loop 自动化编码循环 - 让 AI 代理持续工作直到完成任务。支持 PLANNING规划和 BUILDING构建两种模式使用 exec + process 工具监控进度。
allowed-tools: Read, Write, Edit, Exec, Process
---
# Ralph Loop (Agent Mode)
自动化 AI 编码代理循环工作流。
## 工作流程
```
需求定义 → PLANNING Loop → BUILDING Loop → 完成
```
1. **PLANNING Loop** - 创建/更新 IMPLEMENTATION_PLAN.md不实现
2. **BUILDING Loop** - 实现任务、运行测试、更新计划、提交
## 支持的 CLI
| CLI | 命令模式 | TTY 需求 |
|-----|---------|---------|
| OpenCode | `opencode run --model <MODEL> "$(cat PROMPT.md)"` | ✅ |
| Codex | `codex exec --full-auto "$(cat PROMPT.md)"` | ✅ |
| Claude Code | `claude --dangerously-skip-permissions "$(cat PROMPT.md)"` | ✅ |
| Pi | `pi --provider <PROVIDER> -p "$(cat PROMPT.md)"` | ✅ |
| Goose | `goose run "$(cat PROMPT.md)"` | ✅ |
## 使用方法
### 1. 准备项目文件
创建 `PROMPT.md`:
```markdown
# 目标
<你的任务描述>
## 参考文件
- specs/*.md
- IMPLEMENTATION_PLAN.md
```
创建 `AGENTS.md`:
```markdown
# 项目说明
## 测试命令
npm test
## 构建命令
npm run build
```
### 2. 启动循环
**PLANNING 模式**:
```bash
# 我会执行
exec(
command: 'opencode run --model claude-opus-4 "$(cat PROMPT.md)"',
workdir: "/path/to/project",
background: true,
pty: true,
timeout: 3600
)
```
**BUILDING 模式**:
```bash
# 同上,但 PROMPT.md 内容不同
```
### 3. 监控进度
```bash
# 轮询状态
process(action: "poll", sessionId: "xxx")
# 查看日志
process(action: "log", sessionId: "xxx", offset: -30)
```
### 4. 检测完成
检查 `IMPLEMENTATION_PLAN.md`:
- `STATUS: PLANNING_COMPLETE` - 规划完成
- `STATUS: COMPLETE` - 构建完成
## 提示词模板
### PLANNING 模式
```markdown
You are running a Ralph PLANNING loop for this goal: <goal>.
Read specs/* and the current codebase. Only update IMPLEMENTATION_PLAN.md.
Rules:
- Do not implement
- Do not commit
- Create a prioritized task list
- Write down questions if unclear
Completion:
When plan is ready, add: STATUS: PLANNING_COMPLETE
```
### BUILDING 模式
```markdown
You are running a Ralph BUILDING loop for this goal: <goal>.
Context: specs/*, IMPLEMENTATION_PLAN.md, AGENTS.md
Tasks:
1) Pick the most important task
2) Investigate code
3) Implement
4) Run backpressure commands from AGENTS.md
5) Update IMPLEMENTATION_PLAN.md
6) Update AGENTS.md with learnings
7) Commit with clear message
Completion:
When all done, add: STATUS: COMPLETE
```
## 安全注意事项
- 使用沙盒环境
- 设置合理超时
- 重要项目先备份
- 监控进度,不要过早终止
## 故障排除
| 问题 | 解决方案 |
|------|---------|
| CLI 卡住 | 确保 pty: true |
| 无法启动 | 检查 CLI 路径和 git 仓库 |
| 未检测到完成 | 验证 IMPLEMENTATION_PLAN.md 中的格式 |
| 超时 | 增加 timeout 参数 |
## 示例
### 完整工作流
1. 用户: "帮我实现用户认证模块"
2. 我创建 specs/auth.md, PROMPT.md, AGENTS.md
3. 我启动 PLANNING 循环
4. AI 生成 IMPLEMENTATION_PLAN.md
5. 我启动 BUILDING 循环
6. AI 逐个实现任务
7. 检测到 STATUS: COMPLETE
8. 完成!
## License
MIT

View File

@@ -0,0 +1,877 @@
# 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

View File

@@ -0,0 +1,23 @@
# Shadcn UI Flutter 技能
## 描述
Flutter 版本的 shadcn/ui 组件库技能。提供美观、可定制的 UI 组件支持深色模式、Material 混用、表单管理等功能。当用户需要在 Flutter 项目中使用 shadcn 风格的 UI 组件时使用此技能。
## 使用场景
- 创建新的 Flutter 项目需要现代化 UI
- 将现有 Flutter 项目迁移到 shadcn 风格
- 需要深色模式支持
- 需要表单验证和管理
- 需要与 Material Design 混用
## 快速链接
- [官方文档](https://mariuti.com/flutter-shadcn-ui/)
- [GitHub](https://github.com/nank1ro/flutter-shadcn-ui)
- [Lucide 图标](https://lucide.dev/icons/)
## 关键文件
- `SKILL.md` - 完整技能文档