在 Flutter 项目里装 Skills:从 0 搭建 AI 辅助开发体系

在 Flutter 项目里装 Skills:从 0 搭建 AI 辅助开发体系

使用假设

文章类型:教程干货;目标读者:Flutter 开发者、独立开发者、技术团队负责人;核心目标:知识沉淀 + 涨粉;作者调性:独立开发者。

备选标题

  1. 在 Flutter 项目里装 Skills:让 AI 写代码时自动遵守你的规范(推荐)
  2. Flutter + Skills:从零搭建 AI 辅助开发的完整工作流
  3. 给 Flutter 项目装上 AI 大脑:Skills 落地实战指南
  4. 让 AI 成为你的 Flutter 专家:Skills 体系搭建全流程
  5. Flutter 开发效率翻倍:用 Skills 驯服 AI 编码助手

摘要

在 Flutter 项目中使用 AI 编码助手时,最常见的问题是"AI 写的代码不符合项目规范"。本文从零开始,教你如何在 Flutter 项目中搭建 Skills 体系,让 AI 编码助手自动理解并遵守你的项目架构、代码风格和最佳实践。包含完整的操作步骤、可复用的配置模板和真实坑点处理。


你可能正在经历的事

你用 Cursor 或 Claude Code 写 Flutter,AI 确实能帮你快速生成代码。但每次生成后,你都要花时间修正:

  • 状态管理混用了 Provider 和 Riverpod
  • 路由配置没有用 GoRouter
  • 代码风格和项目现有规范不一致
  • Widget 嵌套层级不合理

本质上,AI 编码助手不知道你的项目"游戏规则"。 它只知道通用的 Flutter 知识,但不知道你的项目用什么架构、遵循什么规范、哪些文件该放哪里。

Skills 就是解决这个问题的钥匙。它不是新的编程语言或框架,而是一套给 AI 编码助手看的"项目规则手册"。


Skills 是什么,为什么 Flutter 项目需要它

什么是 Skills

Skills 是一组由指令文件、脚本和资源组成的文件夹,专门给 AI 编码助手"充电"用的。你可以把它理解为:

  • AI 的专业教材:让 AI 助手在特定领域的开发任务中更加精准高效
  • 开放标准:遵循统一的技能规范,可以在不同的 AI Agent 之间自由迁移
  • 即插即用:一条命令安装到项目中,AI 助手自动识别并应用

简单来说,装上 Skills 后,你的 AI 编码助手就从"通用选手"升级为"你的项目专属专家"了。

为什么 Flutter 项目特别需要 Skills

Flutter 有几个特性让 AI 辅助开发特别容易出问题:

  1. 强类型系统:Dart 的类型系统很严格,但 AI 容易生成类型不匹配的代码
  2. 布局约束复杂:Flutter 的布局系统和 Web 差异很大,AI 对约束传递理解不足
  3. 状态管理方案多样:Provider、Riverpod、Bloc、GetX 等,AI 需要知道你的项目用哪个
  4. 代码组织规范:Widget 拆分、文件目录结构、命名约定等,AI 不会自动遵守

从零搭建:Flutter 项目的 Skills 体系

前置条件

  • 一个已有的 Flutter 项目(或新建一个)
  • 安装了支持 Skills 的 AI 编码助手(Cursor、Claude Code、Trae 等)
  • 了解基本的 Flutter 项目结构和架构

步骤一:初始化 Skills 目录

在项目根目录创建 .skills/ 文件夹,这是 AI 编码助手自动识别 Skills 的标准位置。

bash 复制代码
mkdir -p .skills

目录结构推荐:

perl 复制代码
.skills/
├── AGENTS.md              # 项目级 AI 指令(全局规则)
├── skills/                # 技能模块目录
│   ├── flutter-architecture/     # 架构规范
│   ├── flutter-code-style/       # 代码风格
│   ├── flutter-state-management/ # 状态管理
│   ├── flutter-routing/          # 路由配置
│   └── flutter-widget-patterns/  # Widget 模式
└── workflows/             # 工作流(可选)

步骤二:编写 AGENTS.md(全局规则)

这是最重要的文件,相当于项目的"宪法"。AI 编码助手会优先读取这里的规则。

markdown 复制代码
# 项目 AI 开发规范

## 技术栈

- 框架:Flutter 3.x + Dart
- 状态管理:Riverpod 3.x
- 路由:GoRouter
- 网络请求:Dio
- 代码生成:build_runner + freezed

## 全局红线(不可违反)

1. **状态管理统一使用 Riverpod**,禁止混用其他方案
2. **路由必须通过 GoRouter 配置**,禁止手写 Navigator.push
3. **所有 Widget 必须使用 const 构造函数**(除非有理由)
4. **文件命名使用 snake_case**,类名使用 PascalCase
5. **Widget 拆分原则**:单个文件不超过 200 行,超过必须拆分

## 目录结构约定

lib/ ├── main.dart # 入口文件 ├── app/ # 应用核心 │ ├── app.dart # 根 Widget │ └── routes.dart # 路由配置 ├── features/ # 业务功能模块 │ └── auth/ # 认证模块示例 │ ├── presentation/ # UI 层 │ ├── domain/ # 领域层 │ └── data/ # 数据层 ├── shared/ # 共享组件和工具 └── providers/ # Riverpod Providers

perl 复制代码
## Skills 索引

| Skill | 用途 | 触发场景 |
|-------|------|----------|
| flutter-architecture | 架构规范和分层原则 | 新建模块、修改架构 |
| flutter-code-style | 代码风格和命名约定 | 生成代码、代码审查 |
| flutter-state-management | Riverpod 使用规范 | 状态管理相关操作 |
| flutter-routing | GoRouter 路由配置 | 路由相关操作 |
| flutter-widget-patterns | 常用 Widget 模式 | UI 开发 |

步骤三:编写第一个 Skill --- 状态管理规范

.skills/skills/flutter-state-management/ 目录下创建 SKILL.md

markdown 复制代码
# Flutter State Management Skill

## 核心规则

1. **优先使用 NotifierProvider** 管理可变状态
2. **使用 Provider** 管理只读状态
3. **使用 Family** 处理需要参数的 Provider
4. **状态变更必须在 Notifier 内部完成**,禁止在 UI 层直接修改状态

## 代码模板

### 创建一个简单的 Counter Provider

```dart
// lib/providers/counter_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';

final counterProvider = NotifierProvider<CounterNotifier, int>(
  CounterNotifier.new,
);

class CounterNotifier extends Notifier<int> {
  @override
  int build() {
    return 0;
  }

  void increment() {
    state++;
  }

  void decrement() {
    state--;
  }

  void reset() {
    state = 0;
  }
}

在 Widget 中使用

dart 复制代码
// lib/features/home/presentation/home_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

class HomeScreen extends ConsumerWidget {
  const HomeScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final count = ref.watch(counterProvider);

    return Scaffold(
      body: Center(
        child: Text('Count: $count'),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => ref.read(counterProvider.notifier).increment(),
        child: const Icon(Icons.add),
      ),
    );
  }
}

常见错误与修复

错误:在 UI 层直接修改状态

dart 复制代码
// ❌ 错误
ref.watch(counterProvider).notifier.state++;

// ✅ 正确
ref.read(counterProvider.notifier).increment();

错误:使用 StateProvider 管理复杂状态

dart 复制代码
// ❌ 错误:复杂状态应该用 NotifierProvider
final userProvider = StateProvider<User>((ref) => User());

// ✅ 正确
final userProvider = NotifierProvider<UserNotifier, User>(
  UserNotifier.new,
);
php 复制代码
### 步骤四:编写路由配置 Skill

在 `.skills/skills/flutter-routing/` 目录下创建 `SKILL.md`:

```markdown
# Flutter Routing Skill

## 核心规则

1. **所有路由必须在 app/routes.dart 中统一配置**
2. **使用命名路由**,禁止使用路径字符串字面量
3. **路由参数必须通过 GoRouter 的 extra 或 pathParameters 传递**
4. **页面 Widget 必须是 ConsumerWidget**(除非不需要状态)

## 代码模板

### 路由配置

```dart
// lib/app/routes.dart
import 'package:go_router/go_router.dart';
import 'package:flutter/material.dart';

import '../features/home/presentation/home_screen.dart';
import '../features/auth/presentation/login_screen.dart';

final router = GoRouter(
  initialLocation: '/',
  routes: [
    GoRoute(
      path: '/',
      name: 'home',
      builder: (context, state) => const HomeScreen(),
    ),
    GoRoute(
      path: '/login',
      name: 'login',
      builder: (context, state) => const LoginScreen(),
    ),
    GoRoute(
      path: '/profile/:userId',
      name: 'profile',
      builder: (context, state) {
        final userId = state.pathParameters['userId']!;
        return ProfileScreen(userId: userId);
      },
    ),
  ],
);

导航跳转

dart 复制代码
// ✅ 正确:使用命名路由
context.pushNamed('login');
context.pushNamed('profile', pathParameters: {'userId': '123'});

// ❌ 错误:使用路径字符串
context.push('/login');

路由命名规范

  • 全部小写,单词之间用 - 分隔
  • 使用语义化名称,如 homeloginprofile
  • 避免使用缩写或简写
shell 复制代码
### 步骤五:测试效果

现在打开你的 AI 编码助手,让它帮你写一个新页面:

请帮我创建一个用户资料页面,包含用户名、头像和简介。

less 复制代码
如果 Skills 配置正确,AI 应该会:

1. 在 `lib/features/profile/presentation/` 目录下创建页面
2. 使用 Riverpod 管理状态(如果需要)
3. 使用 GoRouter 配置路由
4. 遵循代码风格规范

---

## 实战:让 AI 按规范写代码

### 场景:创建一个 Todo 功能模块

让我们用一个完整的例子来验证 Skills 的效果。

**需求**:创建一个 Todo 功能,包含列表展示、添加、删除和状态切换。

**AI 编码助手的输出(Skills 生效后)**:

```dart
// lib/features/todo/presentation/todo_screen.dart
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';

import '../domain/todo.dart';

class TodoScreen extends ConsumerWidget {
  const TodoScreen({super.key});

  @override
  Widget build(BuildContext context, WidgetRef ref) {
    final todos = ref.watch(todoListProvider);

    return Scaffold(
      appBar: AppBar(title: const Text('Todos')),
      body: ListView.builder(
        itemCount: todos.length,
        itemBuilder: (context, index) {
          final todo = todos[index];
          return ListTile(
            title: Text(todo.title),
            leading: Checkbox(
              value: todo.completed,
              onChanged: (value) {
                ref.read(todoListProvider.notifier).toggle(todo.id);
              },
            ),
            trailing: IconButton(
              icon: const Icon(Icons.delete),
              onPressed: () {
                ref.read(todoListProvider.notifier).remove(todo.id);
              },
            ),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () => _showAddTodoDialog(context, ref),
        child: const Icon(Icons.add),
      ),
    );
  }

  void _showAddTodoDialog(BuildContext context, WidgetRef ref) {
    final controller = TextEditingController();

    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('Add Todo'),
        content: TextField(controller: controller),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Cancel'),
          ),
          TextButton(
            onPressed: () {
              if (controller.text.isNotEmpty) {
                ref.read(todoListProvider.notifier).add(controller.text);
              }
              Navigator.pop(context);
            },
            child: const Text('Add'),
          ),
        ],
      ),
    );
  }
}
dart 复制代码
// lib/features/todo/domain/todo.dart
class Todo {
  const Todo({
    required this.id,
    required this.title,
    required this.completed,
  });

  final String id;
  final String title;
  final bool completed;

  Todo copyWith({
    String? id,
    String? title,
    bool? completed,
  }) {
    return Todo(
      id: id ?? this.id,
      title: title ?? this.title,
      completed: completed ?? this.completed,
    );
  }
}
dart 复制代码
// lib/providers/todo_provider.dart
import 'package:flutter_riverpod/flutter_riverpod.dart';
import '../features/todo/domain/todo.dart';

final todoListProvider = NotifierProvider<TodoListNotifier, List<Todo>>(
  TodoListNotifier.new,
);

class TodoListNotifier extends Notifier<List<Todo>> {
  @override
  List<Todo> build() {
    return [];
  }

  void add(String title) {
    state = [
      ...state,
      Todo(
        id: DateTime.now().toString(),
        title: title,
        completed: false,
      ),
    ];
  }

  void remove(String id) {
    state = state.where((todo) => todo.id != id).toList();
  }

  void toggle(String id) {
    state = state.map((todo) {
      return todo.id == id ? todo.copyWith(completed: !todo.completed) : todo;
    }).toList();
  }
}

效果验证

  • ✅ 使用了 Riverpod 的 NotifierProvider
  • ✅ 使用了 ConsumerWidget
  • ✅ 文件目录符合约定(features/todo/presentation/)
  • ✅ 状态变更在 Notifier 内部完成
  • ✅ 使用了 const 构造函数

常见坑点与解决方案

坑点一:AI 忽略 Skills 的规则

触发场景:AI 编码助手生成的代码不符合 Skills 中定义的规则。

看到的现象:AI 生成了 Provider 代码,但项目规范要求使用 Riverpod。

根本原因:Skills 文件的格式或位置不正确,AI 无法识别。

实际处理

  1. 检查 .skills/ 目录是否在项目根目录
  2. 检查 SKILL.md 文件是否存在且格式正确
  3. 检查 AI 编码助手是否支持 Skills 规范
  4. AGENTS.md 中用更明确的语言重写规则

验证结果:让 AI 重新生成代码,确认是否遵守了规则。

怎么避免:定期验证 Skills 的效果,确保 AI 确实在使用这些规则。

坑点二:Skills 规则之间冲突

触发场景:多个 Skills 文件中定义的规则互相矛盾。

看到的现象:一个 Skill 要求使用 Riverpod,另一个 Skill 要求使用 Provider。

根本原因:Skills 模块之间没有明确的优先级和互斥声明。

实际处理:在 AGENTS.md 中添加"规则优先级"声明:

markdown 复制代码
## 规则优先级

1. AGENTS.md 中的全局规则优先级最高
2. 特定 Skill 的规则只在该 Skill 生效的场景中适用
3. 规则冲突时,以 AGENTS.md 中的全局规则为准

验证结果:检查所有 Skills 文件,确保没有与全局规则冲突的内容。

怎么避免:编写每个 Skill 时,先阅读 AGENTS.md 的全局规则。

坑点三:Skills 过于复杂导致 AI 理解困难

触发场景:Skills 文件太长或太复杂,AI 无法正确理解。

看到的现象:AI 只遵循了部分规则,忽略了其他规则。

根本原因:单个 Skills 文件超过了 AI 的上下文窗口,或者规则表述不够清晰。

实际处理

  1. 拆分过长的 Skills 文件
  2. 使用更简洁的语言表述规则
  3. 提供更多的代码示例
  4. 删除冗余或重复的规则

验证结果:简化后重新测试,确认 AI 是否能完整遵守规则。

怎么避免:保持每个 Skills 文件不超过 200 行,规则不超过 10 条。


可收藏清单:Flutter Skills 体系搭建检查

在完成 Skills 体系搭建后,用这份清单自检:

  1. 目录结构.skills/ 目录是否在项目根目录?
  2. AGENTS.md:是否定义了技术栈、全局红线和目录结构约定?
  3. Skills 索引AGENTS.md 中是否有完整的 Skills 索引表?
  4. 核心 Skills:是否至少包含架构、代码风格、状态管理和路由四个 Skill?
  5. 代码示例:每个 Skill 是否有完整的代码示例和错误对比?
  6. 规则优先级:是否定义了规则冲突时的处理策略?
  7. 验证测试:是否用一个真实需求测试过 Skills 的效果?
  8. 团队共享.skills/ 目录是否已加入版本控制?

写在最后

Skills 不是魔法,它只是把你的项目知识系统化地告诉 AI。搭建一套好的 Skills 体系,需要你先梳理清楚自己的项目规则------这本身就是一次有价值的架构反思。

当 AI 编码助手开始自动遵守你的规范时,你会发现:你不再是"AI 的校对员",而是"AI 的指挥官"。你负责定义方向和规则,AI 负责执行具体的编码工作。

下一步:如果你想让 AI 参与更复杂的流程,可以学习如何编写 Workflow,把多个 Skills 串联成完整的开发流水线。

你在 Flutter 项目中使用 AI 编码助手时遇到过什么问题?欢迎在评论区讨论。


标签建议

Flutter、Skills、AI编程、Riverpod、GoRouter

相关推荐
孤狼GPT3 小时前
Codex免费版够用吗?Free、Plus和Pro怎么选
chatgpt·ai编程·codex·chatgpt plus·chatgpt pro
袋鼠云数栈UED团队4 小时前
Agent Loop:让 Agent 真正跑起来的核心机制
ai编程
宅小年5 小时前
AI 记忆是怎么实现的?下篇:向量检索、知识图谱和长期记忆系统
aigc·openai·ai编程
xianrenli385 小时前
DS5900作业之“氛围(Vibe)”背后的伦理
学习·ai编程·msai
hqk6 小时前
我给 AI 造了一个原型工具:AIWireframeViewer V1
前端·ai编程·vibecoding
小四的小六7 小时前
MCP Server 从0到1实战:手写一个让AI调用本地数据库的Tool工具
后端·openai·ai编程
_codeOH7 小时前
读完这两份代码,我终于搞懂了 Agent 的 Skills 到底是什么
人工智能·ai编程
一心只读圣贤书7 小时前
如何在项目中落地Agent?从选型到上线的完整指南
aigc·ai编程
AI大模型-小雄8 小时前
ChatGPT 上传文件失败怎么办?从格式、大小到账号状态逐项排查
gpt·chatgpt·ai编程·开发工具·codex·ai办公·chatgpt pro