1. 为什么我选择用对话式AI从零搭建一个宠物生命周期管理App
宠物生命周期管理这个需求,乍一听好像挺小众,但真正养过宠物的人都知道,从接回家第一天到最终告别,中间涉及的事情琐碎到令人崩溃。疫苗什么时候打、驱虫多久一次、体重变化趋势、发情期记录、体检报告归档、不同阶段的饮食调整……这些信息如果全靠脑子记或者散落在各种备忘录里,基本等于没记。我身边至少有三位养宠的朋友,在宠物生病需要向兽医描述历史情况时,翻遍手机找不到完整的记录。
市面上确实有不少宠物管理类App,但用下来总有几个问题:要么功能太重,什么社交、商城、直播全塞进来,打开就卡;要么数据存在云端,我总担心哪天服务停了记录全丢;要么就是界面花哨但核心的提醒功能做得稀烂,疫苗到期不提醒,驱虫时间算错。所以我决定自己动手做一个,核心诉求就三条:数据本地优先、提醒准确可靠、界面干净够用。
那为什么标题里强调“Claude Code从零搭建”和“全流程AI实现”?因为这次我刻意尝试了一种新的开发方式——不写一行手写代码,全程通过对话式AI来完成从需求梳理、技术选型、架构设计到具体编码、调试、优化的所有环节。我想验证一件事:在2025年这个时间点,一个有一定开发经验但不想从零敲代码的人,能不能靠AI把一个完整可用的App做出来。结论先放在这里:能,但前提是你得知道怎么跟AI对话,以及哪些地方必须自己把关。
这篇文章我会把整个过程的每一个关键决策、每一次踩坑、每一段实际可用的对话策略都拆开讲清楚。不管你是想做一个宠物管理App,还是想用同样的方式做别的项目,这套方法论都能直接复用。文章会比较长,因为我会把“为什么这么做”和“具体怎么做”都讲透,建议先收藏再慢慢看。
2. 动手之前先把需求拆到AI能理解的粒度
2.1 宠物生命周期到底包含哪些阶段和事件
很多人做项目失败,不是因为技术不行,而是因为一开始需求就没想清楚。你直接跟AI说“帮我做一个宠物管理App”,它给你的东西大概率是一个四不像。我的做法是先把“宠物生命周期”这个模糊概念拆成AI能理解的结构化描述。
宠物的生命周期,从时间维度上可以分成几个阶段:幼年期(0-1岁)、成年期(1-7岁)、老年期(7岁以上)。不同阶段关注的重点完全不同。幼年期核心是疫苗和驱虫的密集安排,成年期重点是体重管理和定期体检,老年期则要关注慢性病筛查和饮食调整。
从事件维度上,我梳理了以下几类必须记录的内容:
- 医疗类事件:疫苗接种、体内外驱虫、体检、就诊记录、用药记录、过敏史
- 生理类事件:体重记录、发情周期、绝育时间、食欲变化
- 日常类事件:洗澡、剪指甲、驱虫药投喂、特殊饮食调整
- 纪念类事件:生日、领养日、第一次学会某个技能
把这些梳理清楚之后,我写了一段结构化的需求描述给AI。这里有个关键技巧:不要用自然语言长篇大论,而是用类似数据库表结构的格式来描述。比如我会这样写:
实体:宠物 字段:id, 名字, 品种, 性别, 出生日期, 领养日期, 绝育状态, 当前体重, 头像路径 实体:事件记录 字段:id, 宠物id, 事件类型(枚举), 事件名称, 计划时间, 实际完成时间, 备注, 附件路径 实体:提醒规则 字段:id, 宠物id, 事件类型, 触发条件(时间间隔/固定日期), 提前提醒天数, 是否重复这样AI理解起来几乎没有歧义,后面生成的数据模型和代码结构也会更合理。我试过用纯自然语言描述,AI给出的方案里经常把“事件”和“提醒”混在一起,后期改起来很痛苦。
2.2 哪些功能必须做,哪些坚决不做
需求拆解完之后,下一步是砍需求。这一步比加需求更重要。我给自己定了一个原则:第一版只做“记录+提醒”两个核心功能,其他全部砍掉。
具体来说,必须做的功能包括:
- 添加/编辑/删除宠物档案
- 按时间线查看某只宠物的所有事件记录
- 创建基于时间间隔的提醒规则(比如每30天驱虫一次)
- 本地推送通知
- 数据导出为JSON文件(防止数据丢失)
坚决不做的功能包括:
- 用户注册登录(本地App不需要)
- 云端同步(第一版不做,降低复杂度)
- 社交分享(完全没必要)
- 宠物商城(跟核心需求无关)
- 多设备同步(技术复杂度高,收益低)
这个取舍逻辑很重要。我见过太多个人项目死在“什么都想要”上。你跟AI说“帮我做一个宠物App”,它可能会给你生成一个包含登录、支付、社交的完整方案,看起来很美好,但你根本做不完。主动砍需求,是让AI帮你聚焦的关键。
2.3 把需求翻译成AI能直接执行的提示词结构
需求想清楚之后,最后一步是把它翻译成AI能直接执行的提示词。我的经验是,一个好的提示词应该包含四个部分:
- 角色设定:告诉AI它是什么角色,比如“你是一个资深移动端开发工程师”
- 上下文:项目背景、技术栈、已有代码结构
- 具体任务:这次要完成什么,输入是什么,输出是什么
- 约束条件:代码风格、命名规范、不能用的库、性能要求
举个例子,我让AI生成数据模型时的提示词是这样的:
你是一个资深Flutter开发工程师,正在帮我开发一个宠物生命周期管理App。 技术栈:Flutter 3.x + Dart 3.x + SQLite(sqflite包)+ Provider状态管理。 当前项目结构:lib/目录下已有main.dart和models/文件夹。 任务:在models/文件夹下创建pet.dart和event.dart两个数据模型文件。 要求: - 使用不可变类(immutable class) - 包含toMap()和fromMap()方法用于SQLite序列化 - 字段命名使用小驼峰 - 每个字段加注释说明用途 - 不要引入除sqflite之外的任何第三方包这种提示词结构,AI几乎不会跑偏。我对比过,用这种结构化提示词生成的代码,一次通过率大概在80%以上,而用“帮我写个宠物模型”这种模糊提示词,基本都要返工。
3. 技术选型:为什么我最终选了Flutter加SQLite这套组合
3.1 跨平台方案对比:Flutter、React Native和原生开发
技术选型这一步,我让AI帮我做了一次完整的对比分析。我给它的提示词是:“列出Flutter、React Native、Kotlin Multiplatform、原生Android/iOS五种方案在个人开发宠物管理App场景下的优劣对比,用表格输出,重点考虑开发效率、性能、本地数据库支持、学习成本四个维度。”
AI给出的对比表格我整理如下:
| 方案 | 开发效率 | 性能 | 本地数据库支持 | 学习成本 | 适合场景 |
|---|---|---|---|---|---|
| Flutter | 高 | 接近原生 | sqflite成熟 | 中等 | 个人跨平台项目首选 |
| React Native | 高 | 中等 | 需要额外配置 | 低(有前端基础) | 前端开发者快速上手 |
| Kotlin Multiplatform | 中 | 高 | 原生支持好 | 高 | 有Kotlin经验的开发者 |
| 原生Android | 低 | 最高 | Room非常成熟 | 中等 | 只做Android平台 |
| 原生iOS | 低 | 最高 | CoreData成熟 | 高 | 只做iOS平台 |
我最终选Flutter的理由有三条:第一,我只需要写一套代码就能同时覆盖Android和iOS,个人项目维护成本最低;第二,sqflite这个包对SQLite的封装非常成熟,增删改查写起来很顺手;第三,Flutter的热重载在调试UI时效率极高,改一行代码秒级看到效果。
React Native其实也是不错的选择,但我之前踩过一个坑:RN的本地数据库方案在不同版本之间API变动比较大,而Flutter的sqflite接口相对稳定。对于个人项目来说,稳定性比先进性更重要。
3.2 本地数据存储:SQLite为什么比JSON文件更靠谱
一开始我想过直接用JSON文件存数据,简单粗暴。但跟AI讨论之后发现几个致命问题:
- 并发写入问题:如果同时有多个提醒触发,JSON文件写入会冲突
- 查询效率低:想查“所有宠物下周需要驱虫的记录”,JSON需要全量加载再过滤
- 数据完整性难保证:JSON没有事务概念,写到一半崩溃就全丢了
SQLite完美解决这些问题。它支持事务、支持复杂查询、单文件存储方便备份、而且sqflite包让Flutter操作SQLite变得非常简单。我让AI帮我设计了数据库表结构,核心就三张表:
CREATE TABLE pets ( id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL, species TEXT NOT NULL, breed TEXT, gender TEXT, birth_date TEXT, adopt_date TEXT, neutered INTEGER DEFAULT 0, current_weight REAL, avatar_path TEXT, created_at TEXT NOT NULL ); CREATE TABLE events ( id INTEGER PRIMARY KEY AUTOINCREMENT, pet_id INTEGER NOT NULL, event_type TEXT NOT NULL, event_name TEXT NOT NULL, planned_time TEXT, actual_time TEXT, notes TEXT, attachment_path TEXT, created_at TEXT NOT NULL, FOREIGN KEY (pet_id) REFERENCES pets(id) ON DELETE CASCADE ); CREATE TABLE reminders ( id INTEGER PRIMARY KEY AUTOINCREMENT, pet_id INTEGER NOT NULL, event_type TEXT NOT NULL, interval_days INTEGER, fixed_date TEXT, advance_days INTEGER DEFAULT 1, repeat INTEGER DEFAULT 1, last_triggered TEXT, FOREIGN KEY (pet_id) REFERENCES pets(id) ON DELETE CASCADE );这里有个细节值得说:ON DELETE CASCADE这个外键约束非常重要。当你删除一只宠物时,它关联的所有事件和提醒会自动删除,不需要手动清理。我一开始没加这个,结果测试时删了宠物之后数据库里留了一堆孤儿记录,查起来各种报错。
3.3 状态管理:Provider够用,别过度设计
状态管理是Flutter开发里最容易过度设计的地方。我见过有人用BLoC写一个只有三个页面的App,代码量翻了三倍。对于宠物管理App这种规模,Provider完全够用。
我让AI帮我设计了三个Provider:
PetProvider:管理宠物列表的增删改查EventProvider:管理事件记录的增删改查ReminderProvider:管理提醒规则的增删改查和触发逻辑
每个Provider内部持有对应的Repository,Repository负责跟SQLite交互。这种分层的好处是,如果以后想换成云端存储,只需要改Repository层,Provider和UI层完全不用动。
AI一开始给我生成的方案里用了Riverpod,功能确实更强,但学习曲线也更陡。我跟AI说“换成Provider,理由是这个项目只有我一个人开发,不需要团队协作级别的状态管理方案”,它很快就调整过来了。跟AI沟通时,给出明确的约束理由,它会更精准地执行。
4. 用对话式AI写代码的完整实操流程
4.1 项目初始化:从flutter create到第一个可运行页面
项目初始化这一步,我全程让AI指导我操作。提示词是:“我是一个Flutter初学者,请一步一步指导我创建一个新的Flutter项目,项目名叫pet_lifecycle,然后创建一个最简单的首页,显示‘宠物生命周期管理’标题和一个空状态提示。”
AI给出的步骤非常清晰:
- 在终端执行
flutter create pet_lifecycle - 进入项目目录
cd pet_lifecycle - 打开
pubspec.yaml,添加依赖:sqflite、path、provider、flutter_local_notifications - 执行
flutter pub get - 修改
lib/main.dart,替换为AI生成的首页代码
这里我踩了第一个坑:flutter_local_notifications这个包在Android上需要额外配置权限和通知渠道,AI第一次生成的代码没有包含这部分,导致通知功能在Android上完全不工作。我后来把报错信息直接贴给AI,它才补充了AndroidManifest.xml的配置和初始化代码。
提示:跟AI协作时,遇到报错不要自己瞎猜,直接把完整的错误日志贴给它,让它分析原因并给出修复方案。这比你自己查文档快十倍。
4.2 数据模型和数据库层:让AI生成可测试的代码
数据模型和数据库层是整个项目的地基,这部分代码必须保证质量。我的做法是让AI先生成代码,然后我写单元测试来验证。
AI生成的Pet模型大致是这样的:
class Pet { final int? id; final String name; final String species; final String? breed; final String? gender; final String? birthDate; final String? adoptDate; final bool neutered; final double? currentWeight; final String? avatarPath; final String createdAt; const Pet({ this.id, required this.name, required this.species, this.breed, this.gender, this.birthDate, this.adoptDate, this.neutered = false, this.currentWeight, this.avatarPath, required this.createdAt, }); Map<String, dynamic> toMap() { return { 'id': id, 'name': name, 'species': species, 'breed': breed, 'gender': gender, 'birth_date': birthDate, 'adopt_date': adoptDate, 'neutered': neutered ? 1 : 0, 'current_weight': currentWeight, 'avatar_path': avatarPath, 'created_at': createdAt, }; } factory Pet.fromMap(Map<String, dynamic> map) { return Pet( id: map['id'] as int?, name: map['name'] as String, species: map['species'] as String, breed: map['breed'] as String?, gender: map['gender'] as String?, birthDate: map['birth_date'] as String?, adoptDate: map['adopt_date'] as String?, neutered: (map['neutered'] as int) == 1, currentWeight: map['current_weight'] as double?, avatarPath: map['avatar_path'] as String?, createdAt: map['created_at'] as String, ); } }这段代码质量不错,但有一个隐藏问题:current_weight在SQLite里存的是REAL类型,但Dart的as double?转换在某些情况下会失败,因为SQLite可能返回int。我让AI修复这个问题,它改成了(map['current_weight'] as num?)?.toDouble()。这种细节问题,只有实际跑起来才会发现。
数据库层的DatabaseHelper类,AI生成得也比较完整,包含了单例模式、版本管理、onCreate和onUpgrade回调。我额外让AI加了一个close()方法,用于在App退出时关闭数据库连接,避免资源泄漏。
4.3 界面搭建:从时间线视图到提醒设置页
界面部分是我跟AI协作最多的环节。宠物管理App的核心界面是一个时间线视图,按时间倒序展示某只宠物的所有事件记录。我让AI用ListView.builder实现,每个列表项显示事件图标、事件名称、计划时间和实际完成时间。
AI第一次生成的代码用了ListTile,功能没问题但视觉效果太单调。我给了它更具体的描述:“每个列表项左侧显示一个圆形图标,图标颜色根据事件类型区分(医疗类用红色、生理类用蓝色、日常类用绿色),右侧显示事件名称和时间,已完成的事件加删除线效果。”AI很快就生成了符合要求的代码。
提醒设置页稍微复杂一些,需要处理几种不同的提醒类型:
- 间隔重复提醒:比如每30天驱虫一次,需要计算下次触发时间
- 固定日期提醒:比如每年生日提醒,需要处理闰年和月份天数差异
- 一次性提醒:比如某次体检预约,完成后不再重复
这里我让AI帮我写了一个ReminderCalculator工具类,核心逻辑是:
DateTime? calculateNextTrigger(Reminder reminder, DateTime lastTriggered) { if (reminder.repeat == 0) { return null; // 一次性提醒,触发后不再重复 } if (reminder.intervalDays != null) { return lastTriggered.add(Duration(days: reminder.intervalDays!)); } if (reminder.fixedDate != null) { final fixed = DateTime.parse(reminder.fixedDate!); final now = DateTime.now(); var next = DateTime(now.year, fixed.month, fixed.day); if (next.isBefore(now)) { next = DateTime(now.year + 1, fixed.month, fixed.day); } return next; } return null; }这段逻辑看起来简单,但实际测试时发现一个问题:如果固定日期是2月29日,在非闰年时DateTime(now.year, 2, 29)会自动变成3月1日。我让AI处理这个边界情况,它加了一个判断:如果月份是2且日期是29,在非闰年时改为2月28日。
4.4 本地通知集成:Android和iOS的差异化处理
本地通知是宠物管理App的核心功能,也是最容易出问题的部分。flutter_local_notifications这个包在Android和iOS上的配置差异很大。
Android端需要:
- 在
AndroidManifest.xml中添加POST_NOTIFICATIONS权限(Android 13+) - 创建通知渠道(NotificationChannel)
- 处理精确闹钟权限(Android 12+需要
SCHEDULE_EXACT_ALARM)
iOS端需要:
- 在
Info.plist中添加通知权限描述 - 请求用户授权
- 处理前台通知展示
我让AI分别生成了Android和iOS的配置代码,然后自己手动在真机上测试。这里踩了一个大坑:Android 13+的POST_NOTIFICATIONS权限是运行时权限,必须在代码中动态请求,光在Manifest里声明是不够的。AI第一次生成的代码漏了这一步,导致通知在Android 13设备上完全不显示。
修复方案是在App启动时调用权限请求:
final androidImpl = flutterLocalNotificationsPlugin .resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>(); await androidImpl?.requestNotificationsPermission();注意:通知权限请求的时机很重要。不要在App一启动就弹窗,用户还没理解App是干什么的就拒绝授权,后面很难再让用户手动开启。我的做法是在用户创建第一个提醒规则时再请求权限,并附上一句说明:“开启通知权限,确保不会错过宠物的疫苗和驱虫时间。”
5. 调试和优化阶段:AI帮我解决了哪些棘手问题
5.1 数据库迁移:当表结构需要变更时怎么办
项目做到一半,我发现需要给events表加一个reminder_id字段,用来关联事件和提醒规则。这就涉及到数据库迁移。
SQLite的迁移逻辑是在onUpgrade回调里处理的。我让AI帮我写迁移代码,它给出的方案是:
@override Future<void> onUpgrade(Database db, int oldVersion, int newVersion) async { if (oldVersion < 2) { await db.execute('ALTER TABLE events ADD COLUMN reminder_id INTEGER'); } }看起来很简单,但这里有个隐患:如果用户是从版本1直接升级到版本3,oldVersion < 2这个条件会执行,但版本2到版本3的迁移逻辑也需要执行。正确的写法应该是用连续的if判断,而不是else if。AI第一次写的是else if,我指出问题后它改成了独立的if。
另外,ALTER TABLE ADD COLUMN在SQLite里不支持添加外键约束。如果需要加外键,只能重建表。这个限制AI一开始没提到,是我查文档后发现的。对于个人项目来说,不加外键约束问题不大,但需要在代码层面保证数据一致性。
5.2 性能优化:列表卡顿和数据库查询慢的排查过程
App做到后期,宠物事件记录积累到几百条之后,时间线列表开始出现明显卡顿。我让AI帮我分析可能的原因,它给出了几个排查方向:
- 列表项构建开销大:每个列表项都重新解析日期、重新加载图标
- 数据库查询没有索引:
events表的pet_id字段没有索引,查询慢 - 图片加载没有缓存:宠物头像每次都从磁盘重新读取
我逐一验证:
- 列表项问题:把日期格式化和图标选择逻辑移到数据层,列表项只负责渲染
- 索引问题:给
events.pet_id和events.planned_time加了索引,查询速度从200ms降到20ms - 图片问题:用了
cached_network_image的思路,但因为是本地图片,改用了一个简单的内存缓存Map
优化之后,列表滚动流畅度明显提升。这里有个经验:Flutter的ListView.builder本身性能很好,卡顿通常是因为itemBuilder里做了太多事情。把计算逻辑前置到数据准备阶段,是解决列表卡顿最有效的方法。
5.3 数据导出和备份:防止用户数据丢失的最后一道防线
本地存储最大的风险是数据丢失。用户换手机、卸载重装、手机损坏,数据就没了。所以我做了一个数据导出功能,把所有数据导出为JSON文件,用户可以通过系统分享保存到网盘或发送给自己。
导出逻辑不复杂,核心是把三张表的数据查出来,组装成JSON:
Future<String> exportData() async { final db = await database; final pets = await db.query('pets'); final events = await db.query('events'); final reminders = await db.query('reminders'); final data = { 'version': 1, 'exported_at': DateTime.now().toIso8601String(), 'pets': pets, 'events': events, 'reminders': reminders, }; return jsonEncode(data); }导入逻辑稍微复杂一些,需要处理ID冲突和数据校验。我的做法是导入时重新生成所有ID,用旧ID到新ID的映射表来更新关联关系。这样即使用户多次导入同一份数据,也不会产生冲突。
AI在生成导入逻辑时,一开始没有处理事务。如果导入到一半失败,数据库会处于不一致状态。我让AI加上事务包裹,确保要么全部成功,要么全部回滚。
6. 跟AI协作开发这件事,我踩过的坑和总结的经验
6.1 AI生成代码的常见问题模式
用了几个月AI辅助开发,我总结出AI生成代码的几个典型问题:
第一,边界条件处理不完整。比如日期计算不考虑闰年、数组操作不考虑空数组、字符串处理不考虑空字符串。这类问题在正常流程下不会暴露,但用户一用就会出问题。
第二,错误处理过于简单。AI倾向于用try-catch包住所有代码然后打印日志,但实际项目中需要区分不同类型的错误,给用户不同的提示。比如数据库打开失败和查询失败,处理方式完全不同。
第三,依赖版本不匹配。AI训练数据里的包版本可能比较旧,生成的代码用了新版本已经废弃的API。我的做法是在提示词里明确指定版本号,比如“使用sqflite 2.3.0版本的API”。
第四,代码风格不统一。同一个项目里,AI可能一会儿用final一会儿用var,一会儿用命名参数一会儿用位置参数。我后来在提示词里加了一句“遵循Effective Dart规范”,情况好了很多。
6.2 哪些环节必须人工把关
虽然AI能写大部分代码,但有几个环节我坚持人工把关:
- 数据库表结构设计:这是地基,一旦设计不合理后期改起来成本极高
- 数据迁移逻辑:涉及用户数据安全,必须逐行审查
- 权限和隐私相关代码:通知权限、文件读写权限,必须确保符合平台规范
- 核心业务逻辑:比如提醒触发时间的计算,必须写单元测试验证
我的做法是:AI生成代码,我审查关键逻辑,然后写测试用例验证。测试通过之后才合并到主分支。这套流程虽然比纯手写快很多,但绝不是“无脑复制粘贴”。
6.3 提示词工程的几个实用技巧
最后分享几个我在这个项目里总结的提示词技巧:
技巧一:给AI一个“角色”和“约束”。不要只说“帮我写代码”,要说“你是一个有五年Flutter经验的开发工程师,请遵循以下约束:不使用任何状态管理库、所有异步操作必须有错误处理、代码必须通过flutter analyze检查”。
技巧二:分步骤执行,不要一次性要太多。我从来不会说“帮我做一个完整的宠物管理App”,而是拆成“先设计数据库表结构”“再生成数据模型”“然后写数据库操作层”“最后做界面”。每一步都验证通过再进行下一步。
技巧三:把报错信息完整贴给AI。不要只贴“报错了”三个字,要贴完整的错误堆栈、相关代码片段、你期望的行为和实际行为。信息越完整,AI修复越精准。
技巧四:让AI解释它的方案。每次AI给出代码后,我会追问一句“解释一下这段代码的关键逻辑和可能的边界问题”。这不仅能帮我理解代码,还能让AI自己发现潜在问题。
技巧五:保留对话历史。AI在长对话中能记住之前的上下文,所以尽量在同一个对话里完成一个模块的开发。如果开新对话,记得把关键上下文(技术栈、项目结构、命名规范)重新贴一遍。
这个宠物生命周期管理App目前已经在我自己的手机上稳定运行了几个月,记录了我家两只猫的所有疫苗、驱虫和体检数据。整个开发过程大概用了三周左右的业余时间,其中跟AI对话的时间占了大头,真正手写代码的时间不到20%。如果你也想用类似的方式做一个自己的项目,我的建议是:先从最小的核心功能开始,跑通之后再逐步扩展。不要一上来就追求完美,AI能帮你快速迭代,但方向得你自己把控。