# 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
