youhua
This commit is contained in:
516
.agents/skills/clean-code/SKILL.md
Normal file
516
.agents/skills/clean-code/SKILL.md
Normal 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. **代码坏味道识别**:参考常见问题
|
||||
|
||||
告诉我需要审查的代码,我会帮你识别问题并提供改进建议!
|
||||
160
.agents/skills/ralph-loop/SKILL.md
Normal file
160
.agents/skills/ralph-loop/SKILL.md
Normal 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
|
||||
877
.agents/skills/shadcn-ui-flutter/SKILL.md
Normal file
877
.agents/skills/shadcn-ui-flutter/SKILL.md
Normal 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
|
||||
- GitHub:https://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
|
||||
23
.agents/skills/shadcn-ui-flutter/skill.yaml
Normal file
23
.agents/skills/shadcn-ui-flutter/skill.yaml
Normal 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` - 完整技能文档
|
||||
Reference in New Issue
Block a user