FLutter的Cursor Skills 实战分享
聊聊 Cursor 的 Skills 功能,用 Flutter 项目举例
最近网上一直在炒 Agent Skills 功能, 年底不忙了摸索了一下 Cursor 的Agent Skills 这个功能。结果用了几天之后... 哇偶真香。
今天就来聊聊这玩意儿怎么用,顺便分享下我踩的坑。
这东西到底是干嘛的?
其实说白了,就是让你"调教" AI 的一种方式。
你有没有遇到过这种情况:让 AI 帮你写个 Flutter Widget,它写出来的代码风格跟你项目里的完全不一样?你说"加个 const",它下次又忘了。你说"用 GetX",它给你整个 Provider...
Skills 就是解决这个问题的。你写一个配置文件,告诉 AI "我们项目是这么干的",它就会照着来。
文件放哪儿?
两个地方可以放:
个人目录:~/.cursor/skills/你的技能名/SKILL.md
- 所有项目都能用
- 比如你的代码风格偏好、常用的工具链配置
项目目录:项目根目录/.cursor/skills/你的技能名/SKILL.md
- 只有当前项目用
- 可以提交 Git,团队共享
因为公司只有我一个移动端开发, 所以我一般是通用的放个人目录,项目特定的放项目里。
入门:写个最简单的
然后来个最简单的感受一下。
在 ~/.cursor/skills/flutter-widget/ 下面建个 SKILL.md:
---
name: flutter-widget
description: 写 Flutter Widget 的时候用这个。创建组件、写页面、搞 UI 的时候会自动触发。
---
# 我的 Widget 写法
别废话,直接上规矩:
1. 能用 StatelessWidget 就别用 StatefulWidget
2. 构造函数必须加 const
3. 参数必填的用 required
就这样写:
```dart
class UserCard extends StatelessWidget {
const UserCard({
super.key,
required this.name,
});
final String name;
@override
Widget build(BuildContext context) {
return Text(name);
}
}
别给我整什么 Key? key 老写法,直接 super.key 完事。
保存。这就完事了???
现在你让 AI "帮我写个用户卡片组件",它就会按这个风格来。
---
## 进阶:description 很重要!
这里有个坑我踩过——description 写得太随意,AI 根本不触发你的技能。
**错误示范**:
```yaml
description: Flutter 开发
这太模糊了,AI 不知道什么时候该用。
正确示范:
description: 写 Flutter Widget 的时候用。当用户说"创建组件"、"写页面"、"搞个 UI"、"弄个卡片"的时候触发。
关键是要写清楚什么时候用。AI 是根据这个 description 来判断要不要加载你的技能的。
中级玩法:加点料
用了一阵之后,我发现光写 Widget 规范不够。导入顺序、文件命名这些也得管,不然代码还是乱。
---
name: flutter-dev
description: Flutter 开发全套规范。写代码、建文件、搞页面的时候用。
---
# Flutter 开发规范(我的版本)
## 文件怎么命名
我习惯这样:
- Widget 文件:`user_card.dart`(小写下划线)
- 页面文件:`user_page.dart` 或 `user_view.dart`
- 服务类:`user_service.dart`
类名当然还是大驼峰 `UserCard`。
## import 顺序
这个我有强迫症,必须这么排:
```dart
// 1. dart 自带的
import 'dart:async';
// 2. flutter 的
import 'package:flutter/material.dart';
// 3. 第三方包
import 'package:get/get.dart';
// 4. 自己项目的
import '../services/api.dart';
中间空一行分隔,看着舒服。
GetX Controller 怎么写
我们项目用 GetX,controller 这么写:
class UserController extends GetxController {
final _loading = false.obs;
bool get loading => _loading.value;
final _user = Rxn<User>();
User? get user => _user.value;
Future<void> loadUser() async {
_loading.value = true;
try {
_user.value = await api.getUser();
} finally {
_loading.value = false;
}
}
}
注意几点:
- 私有变量用下划线
- 对外暴露 getter
- loading 状态别忘了 finally 里关掉
页面结构
class UserPage extends StatelessWidget {
const UserPage({super.key});
@override
Widget build(BuildContext context) {
return GetBuilder<UserController>(
init: UserController(),
builder: (c) => Scaffold(
appBar: AppBar(title: const Text('用户')),
body: Obx(() {
if (c.loading) {
return const Center(child: CircularProgressIndicator());
}
return _buildContent(c);
}),
),
);
}
Widget _buildContent(UserController c) {
// 内容
}
}
---
## 高级玩法:文件拆分
技能写多了之后,一个文件塞不下了。而且每次 AI 都要读整个文件,Token 哗哗的烧。
这时候就要拆分。
**目录结构**:
~/.cursor/skills/flutter-pro/
├── SKILL.md # 主文件,放最常用的
├── getx.md # GetX 详细用法
├── api.md # 网络请求规范
└── widgets.md # 组件规范
**SKILL.md 里这么写**:
```markdown
---
name: flutter-pro
description: Flutter 开发完整规范。写代码、搞状态管理、调接口的时候用。
---
# Flutter 开发
## 快速参考
### 状态管理
用 GetX。详细的看 [getx.md](getx.md)
### 网络请求
用 Dio + 拦截器。详细的看 [api.md](api.md)
### 组件开发
const 构造、StatelessWidget 优先。详细的看 [widgets.md](widgets.md)
## 最常用的代码
(这里放几个最常用的模板)
这样 AI 平时只读主文件,需要详细信息的时候才去读子文件。省 Token。
省 Token 的一些心得
用了一段时间,总结了几个省钱技巧:
1. 别写废话
AI 比你想象的聪明。什么是 StatelessWidget、什么是 const,它都知道。你只需要告诉它"用 const"就行了,不用解释为什么。
别这样:
## 关于 const 关键字
const 是 Dart 中的编译时常量关键字。使用 const 可以让 Flutter 在编译时就确定 Widget 的值,从而优化性能。当 Widget 的所有属性都是编译时常量时,应该使用 const...
这样就够了:
## 规矩
- 构造函数加 const
- 能 const 的 Widget 都 const
2. 用模板代替解释
与其写一大段"表单验证应该怎么做",不如直接贴个模板:
TextFormField(
validator: (v) => v!.isEmpty ? '必填' : null,
decoration: const InputDecoration(labelText: '用户名'),
)
代码就是最好的文档。
3. 渐进式披露
主文件放最常用的 20%,剩下 80% 拆到子文件。AI 需要的时候自己会去读。
我现在的配置
分享一下我自己的配置,给你参考:
个人目录(~/.cursor/skills/):
-
dart-style/- Dart 代码风格(命名、注释这些) -
git-msg/- Git commit 信息格式
项目目录(.cursor/skills/):
-
project-api/- 项目的 API 接口规范 -
project-db/- 本地数据库操作规范
这样分的好处是:换个项目,个人偏好还在,项目相关的配置跟着项目走。
一些坑
技能没生效?
检查一下:
-
description写得够不够具体 - 文件名是不是
SKILL.md(大小写敏感) - yaml 格式有没有写错(冒号后面要有空格)
Skills 和 Rules 有啥区别?
Rules 是每次对话都会加载的,Skills 是按需加载的。
所以:
- 简单的、每次都要用的偏好 → 放 Rules
- 复杂的、特定场景才用的 → 放 Skills
最后
这功能我觉得挺值得花时间配置的。一次配置,后面省很多事。
刚开始别贪多,先搞一个最简单的,比如 Widget 规范。用顺了再慢慢加。
有问题欢迎交流。