恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
AI辅助从零搭建宠物生命周期管理App:Flutter与Claude Code实战
首页
资讯中心
/
AI辅助从零搭建宠物生命周期管理App:Flutter与Claude Code实战
AI辅助从零搭建宠物生命周期管理App:Flutter与Claude Code实战
发布时间:2026/10/12 10:49:28
1. 为什么我选择用AI辅助从零搭建一个宠物生命周期管理App宠物生命周期管理这个方向我在两年前就想过要做。当时家里养了一只猫从接回家到日常疫苗、驱虫、体检、换粮、体重记录信息散落在备忘录、相册、微信聊天记录里想查个上次驱虫时间得翻半天。市面上宠物类App不少但大多数要么偏社交、要么偏电商真正围绕“一只宠物从进家门到老去”这条完整时间线做管理的工具体验都差点意思。这次我决定不再拖了直接动手。但和以往不同的是我这次全程用Claude Code作为主力开发工具从零开始搭建整个项目。为什么这么做一方面是想验证一下当前AI辅助编程在真实项目里的边界到底在哪另一方面也是想给自己一个约束——不追求大而全先把核心链路跑通。这篇文章我会把整个搭建过程拆开讲清楚项目怎么设计、数据模型怎么定、Claude Code在每个环节怎么用、哪些地方它帮了大忙、哪些地方它反而添乱、我最后怎么绕过去的。如果你也在考虑用AI工具做一个完整的小项目或者你本身就想做一个宠物管理类应用这篇内容应该能让你少走不少弯路。需要提前说明的是我不是要教你“如何用一句话让AI生成一个App”那种内容你看完还是不知道怎么做。我要讲的是真实的工程流程需求拆解、技术选型、数据建模、页面实现、联调测试以及AI在每一步里扮演的角色。2. 项目整体设计与技术选型思路2.1 核心需求拆解一只宠物的完整时间线我先把这个App要解决的问题定义清楚。宠物生命周期管理核心是围绕“时间线”做记录和提醒。具体拆成四个模块基础档案品种、生日、性别、绝育状态、芯片号、领养日期。这些是静态信息但决定了后续所有提醒的基准。健康记录疫苗、驱虫、体检、就诊、用药、过敏史。每一条都带时间戳且部分记录会触发下一次提醒。日常记录体重、喂食、排便、精神状态。这部分是高频但轻量的输入重点是快速记录和趋势展示。提醒系统基于健康记录和用户设定的周期自动计算下一次疫苗、驱虫、体检的时间并推送通知。我特意没有做社交、商城、宠物百科这些模块。原因很简单一个独立开发者做全功能必死先把单点做到极致。宠物生命周期管理的核心价值就是“不让主人忘记重要的事”这个点打透了用户自然留下来。2.2 技术选型为什么选这套组合技术栈的选择我考虑了三个维度开发速度、跨平台能力、长期维护成本。维度方案A方案B最终选择前端框架FlutterReact NativeFlutter后端方案自建服务云开发云开发数据库MySQL文档型数据库文档型数据库状态管理ProviderRiverpodRiverpod本地存储SQLite键值存储SQLite选Flutter的原因很直接一套代码跑双端UI一致性有保障而且Claude Code对Dart和Flutter的代码生成质量相当高我实测下来它写Flutter页面的准确率比写React Native要高出一截。后端用云开发是因为我不想在项目初期就折腾服务器运维云开发的数据库和云函数能覆盖90%的需求等用户量起来再迁移也不迟。数据库选文档型是因为宠物记录的数据结构不固定——不同宠物的健康记录字段可能不一样用关系型数据库反而要频繁改表。状态管理用Riverpod是因为它的编译期安全性和测试友好度比Provider好虽然学习曲线稍陡但项目一旦复杂起来Riverpod的优势就体现出来了。提示如果你也是独立开发技术选型的第一原则不是“哪个最强”而是“哪个能让你最快跑通闭环”。我见过太多项目死在选型阶段纠结三个月最后什么都没做出来。2.3 用Claude Code做项目脚手架的正确姿势很多人用Claude Code的方式是打开对话框输入“帮我做一个宠物管理App”然后期待它吐出一个完整项目。我试过结果是一堆看起来能跑但结构混乱的代码改起来比从头写还累。我的做法是把Claude Code当成一个“执行力极强但需要明确指令的初级工程师”。具体来说我先把项目骨架手动搭好包括目录结构、依赖配置、路由框架然后让Claude Code在既定框架内填充具体实现。项目目录结构是这样的lib/ core/ constants/ theme/ utils/ data/ models/ repositories/ datasources/ features/ pet_profile/ health_record/ daily_log/ reminder/ shared/ widgets/ providers/这个结构是我根据经验定的Claude Code在这个框架下生成代码时会自动把文件放到对应目录不会乱。如果你不给它结构它就会把所有东西堆在一个文件里后期维护是灾难。3. 核心数据模型与AI辅助建模实操3.1 宠物档案模型的设计细节数据模型是整个项目的地基这部分我花了最多时间和Claude Code反复讨论。宠物档案模型看起来简单但有几个字段的设计直接影响后续功能。class PetProfile { final String id; final String name; final String species; // 猫、狗、兔等 final String breed; final DateTime birthday; final String gender; final bool isNeutered; final String? chipNumber; final DateTime adoptionDate; final String? avatarPath; final double? currentWeight; final DateTime createdAt; final DateTime updatedAt; }这里有几个设计决策值得展开说。birthday用DateTime而不是字符串是因为后续计算年龄、疫苗周期都要基于日期运算。isNeutered用bool而不是枚举是因为绝育状态只有两种枚举反而增加复杂度。currentWeight冗余存储最新体重是为了在列表页快速展示不用每次去查体重记录表。我把这个模型描述给Claude Code时它一开始建议加一个age字段直接存年龄。我否掉了因为年龄是随时间变化的派生数据存下来就要定时更新不如每次从birthday计算。这个判断需要人来把关AI在数据建模上容易犯“冗余存储派生数据”的错误。3.2 健康记录模型与提醒触发逻辑健康记录模型是整个App最复杂的部分因为它要同时承担“记录”和“触发提醒”两个职责。class HealthRecord { final String id; final String petId; final HealthRecordType type; // vaccine, deworming, checkup, medication final DateTime recordDate; final DateTime? nextDueDate; final String? note; final String? vetName; final ListString attachments; final MapString, dynamic extra; }nextDueDate是提醒系统的核心。比如打了一针疫苗记录时填入下次接种日期系统就会在那个日期前三天推送提醒。extra字段用Map存储类型特有的数据比如疫苗记录存疫苗名称和批号驱虫记录存药品名称和剂量。这样不用为每种记录类型建一张表扩展性也好。Claude Code在这里帮了我大忙。我让它根据这个模型生成提醒计算的逻辑它给出了一个相当完整的实现DateTime? calculateNextDueDate(HealthRecord record) { if (record.nextDueDate ! null) return record.nextDueDate; switch (record.type) { case HealthRecordType.vaccine: return record.recordDate.add(const Duration(days: 365)); case HealthRecordType.deworming: return record.recordDate.add(const Duration(days: 90)); case HealthRecordType.checkup: return record.recordDate.add(const Duration(days: 180)); default: return null; } }这段逻辑我检查了一遍默认周期设置合理但有个问题不同疫苗的接种周期不一样狂犬疫苗是一年猫三联可能是三年。所以我在extra里加了cycleDays字段优先读取用户填写的周期没有才用默认值。这个修正是我基于领域知识做的AI不知道这些细节。3.3 用AI生成模型代码的注意事项用Claude Code生成数据模型代码时我总结了几个经验先定字段再让AI写代码不要让它自由发挥字段设计你先把字段列表列出来让它只负责写序列化和反序列化逻辑。要求生成fromJson和toJson文档型数据库存的是JSON这两个方法必须要有而且要处理null值。让它生成copyWith方法Flutter里更新模型对象时copyWith比重新构造方便得多。检查日期字段的序列化AI经常忘记把DateTime转成ISO字符串直接存DateTime对象会导致数据库报错。注意AI生成的模型代码一定要手动检查字段类型和序列化逻辑这部分出错后期排查成本极高。我踩过一次坑体重字段被生成为int结果用户输入62.5kg直接崩溃。4. 核心功能模块的实现过程4.1 宠物档案页从表单到图片上传宠物档案页是用户第一次打开App就要用的页面体验好坏直接决定留存。我让Claude Code先生成一个基础表单然后在此基础上改。基础表单生成得很快但有几个问题需要手动调整。第一品种选择用下拉框还是输入框AI默认给了输入框但宠物品种是有限集合输入框会导致数据混乱。我改成了“常用品种快捷选择自定义输入”的组合。第二生日选择器AI用了普通日期选择但宠物生日可能不精确我加了“大概年龄”的选项允许用户只填年份。图片上传部分Claude Code给的方案是直接用image_picker插件但没处理图片压缩。宠物头像原图可能好几MB直接上传浪费流量也拖慢加载。我加了压缩逻辑FutureFile compressImage(File file) async { final result await FlutterImageCompress.compressAndGetFile( file.absolute.path, file.absolute.path _compressed.jpg, quality: 70, minWidth: 800, minHeight: 800, ); return File(result!.path); }质量70、最小宽高800这个参数是我实测下来的平衡点。再低头像会糊再高文件还是太大。4.2 健康记录录入快速记录的设计哲学健康记录录入的频率不高但每次录入的信息量大。我的设计原则是常用记录三步内完成复杂记录不超过一屏。Claude Code帮我生成了记录类型选择器、日期选择器、备注输入框这些基础组件。但它在布局上给了一个很长的表单所有字段平铺。我改成了分步式第一步选类型第二步填核心信息日期关键字段第三步选填备注和附件。这里有个细节值得说疫苗记录的“下次接种日期”我做了自动计算。用户选完疫苗类型和接种日期后系统根据预设周期自动填入下次日期用户可以直接确认或手动修改。这个逻辑Claude Code写得很顺因为它就是简单的日期加法。void onVaccineTypeChanged(String vaccineType, DateTime recordDate) { final cycleDays vaccineCycles[vaccineType] ?? 365; final nextDate recordDate.add(Duration(days: cycleDays)); setState(() { nextDueDate nextDate; }); }vaccineCycles是一个预设的Map狂犬365天、猫三联1095天、犬瘟热365天等等。这些数据是我查资料整理的AI不知道具体数值但它能帮你把逻辑框架搭好。4.3 提醒系统的实现与本地通知配置提醒系统是宠物生命周期管理的灵魂。我用的方案是本地通知服务端兜底。本地通知负责准时触发服务端负责在用户换设备后同步提醒。本地通知用flutter_local_notifications插件Claude Code生成的初始化代码基本可用但有几个坑我踩过时区问题默认用的是UTC必须显式设置本地时区否则提醒时间会差8小时。权限申请时机不要在App启动就申请通知权限用户还没理解App价值就弹权限框拒绝率极高。我改成用户创建第一条提醒时才申请。通知ID冲突每条提醒要用唯一的ID我用的是记录ID的hashCode但要注意hashCode可能为负需要取绝对值。Futurevoid scheduleReminder(HealthRecord record) async { if (record.nextDueDate null) return; final scheduledDate record.nextDueDate!.subtract(const Duration(days: 3)); if (scheduledDate.isBefore(DateTime.now())) return; await flutterLocalNotificationsPlugin.zonedSchedule( record.id.hashCode.abs(), 宠物健康提醒, ${record.petName}的${record.type.label}即将到期, tz.TZDateTime.from(scheduledDate, tz.local), const NotificationDetails( android: AndroidNotificationDetails( pet_health_channel, 宠物健康提醒, importance: Importance.high, priority: Priority.high, ), ), androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, ); }提前三天提醒这个设计也是我基于经验定的。太早提醒用户会忘太晚提醒来不及预约。三天是个比较舒服的窗口。4.4 体重趋势图数据可视化的轻量实现体重记录是高频操作趋势图是用户最常看的内容。我用fl_chart插件做折线图Claude Code生成的图表代码结构没问题但默认样式太丑坐标轴、网格线、数据点都需要调。调样式这部分我花了大概一个小时主要是让图表看起来干净。我的做法是去掉网格线、只保留横向参考线、数据点用小圆点、折线用平滑曲线。颜色用主题色和App整体风格统一。LineChartData( gridData: FlGridData( show: true, drawVerticalLine: false, horizontalInterval: 1, getDrawingHorizontalLine: (value) FlLine( color: Colors.grey.withOpacity(0.2), strokeWidth: 1, ), ), titlesData: FlTitlesData( leftTitles: AxisTitles( sideTitles: SideTitles(showTitles: true, reservedSize: 40), ), bottomTitles: AxisTitles( sideTitles: SideTitles( showTitles: true, interval: 7, getTitlesWidget: (value, meta) Text( DateFormat(MM/dd).format(DateTime.fromMillisecondsSinceEpoch(value.toInt())), style: const TextStyle(fontSize: 10), ), ), ), ), lineBarsData: [ LineChartBarData( spots: weightRecords.map((r) FlSpot( r.date.millisecondsSinceEpoch.toDouble(), r.weight, )).toList(), isCurved: true, curveSmoothness: 0.3, dotData: FlDotData(show: true, dotSize: 4), belowBarData: BarAreaData(show: true, color: themeColor.withOpacity(0.1)), ), ], )curveSmoothness设0.3是我试出来的太高曲线会失真太低看起来像折线。belowBarData加一层淡色填充视觉上更有层次。5. 用Claude Code开发时踩过的坑与应对策略5.1 AI代码的“看起来对”陷阱Claude Code生成的代码有个特点语法正确、逻辑通顺、看起来完全能用但跑起来就是有问题。我遇到最典型的一次是状态管理。它给我生成了一段Riverpod的Provider代码用StateNotifierProvider管理宠物列表。代码本身没问题但它没有处理异步加载状态。用户打开列表页时数据还在加载页面直接显示空列表然后数据到了才刷新。体验上就是闪一下。我改成用AsyncNotifierProvider配合when方法处理loading、error、data三种状态。这个修正需要理解Riverpod的状态模型AI知道怎么写代码但它不理解“用户体验上不能闪”这个隐含需求。提示AI生成的代码一定要在真机上跑一遍模拟器有时候看不出体验问题。我养成了每完成一个模块就真机测试的习惯虽然麻烦但能提前发现大量问题。5.2 依赖版本冲突的排查方法Flutter项目依赖多版本冲突是家常便饭。Claude Code生成pubspec.yaml时经常给的是它训练数据里的版本号和你项目里其他依赖不兼容。我的应对方法是不让AI写版本号只让它写包名版本号我自己用flutter pub add命令加让pub自动解析兼容版本。如果已经冲突了用flutter pub deps看依赖树找到冲突的包手动降级或升级。有一次image_picker和另一个插件冲突我排查了半小时。最后发现是image_picker的某个版本依赖了旧版的http包和项目里的http版本不兼容。解决办法是在pubspec.yaml里用dependency_overrides强制指定http版本。这个技巧AI不会主动告诉你得自己踩过才知道。5.3 数据库查询性能的优化实录项目初期数据量小查询怎么写都快。但我模拟了1000条健康记录后列表页明显卡顿。排查发现是每次查询都全量拉取然后在内存里过滤。优化方案是查询时加条件、加索引、分页加载。文档型数据库的查询条件要提前设计好比如按petId查询、按recordDate排序这些字段都要建索引。FutureListHealthRecord getRecordsByPet( String petId, { int limit 20, DateTime? startAfter, }) async { var query db.collection(health_records) .where(petId, isEqualTo: petId) .orderBy(recordDate, descending: true) .limit(limit); if (startAfter ! null) { query query.startAfter([startAfter]); } final snapshot await query.get(); return snapshot.docs.map((doc) HealthRecord.fromJson(doc.data())).toList(); }分页加载配合滚动监听用户滑到底部自动加载下一页。这个改动之后1000条记录的列表页滑动流畅内存占用也降下来了。5.4 常见问题速查表问题现象可能原因排查方法解决方案通知不触发时区未设置打印tz.local初始化时设置本地时区图片上传失败文件过大打印文件大小加压缩逻辑列表页闪空未处理loading状态检查Provider类型改用AsyncNotifierProvider依赖冲突版本不兼容flutter pub depsdependency_overrides查询卡顿未建索引查看查询计划给查询字段建索引日期计算错误时区偏移打印DateTime统一用本地时间这张表是我在实际开发中遇到问题后整理的每个问题都真实发生过。建议你开发时也维护一张自己的问题表下次遇到类似问题直接查表效率高很多。6. 项目收尾与后续扩展方向6.1 测试与上线前的检查清单项目功能跑通后我没有急着上线而是花了两天做测试。测试清单包括新用户从注册到创建第一只宠物的完整流程添加健康记录后提醒是否正确触发体重记录连续添加后趋势图是否正常多宠物切换是否流畅断网状态下App是否崩溃后台运行超过24小时后通知是否正常其中断网测试发现了三个问题图片上传没做失败重试、数据库查询没做超时处理、提醒列表没做本地缓存。这三个问题在联网状态下完全看不出来但用户在地铁、电梯里就会遇到。修复方案分别是上传加指数退避重试、查询加5秒超时、提醒列表本地缓存一份。这些改动不大但体验提升明显。6.2 后续可以扩展的功能当前版本是MVP核心链路跑通了但还有很多可以加的东西。我列几个优先级比较高的多宠物家庭共享一个家庭多个成员共同管理宠物需要做权限和数据同步。健康报告导出把宠物的健康记录导出成PDF看病时直接给医生看。智能提醒优化根据宠物的年龄、品种、病史动态调整提醒周期。宠物成长相册按时间线展示宠物照片和健康记录关联。这些功能我不会一次性做完而是根据用户反馈逐个迭代。独立开发最忌讳的就是闭门造车做一堆没人用的功能。6.3 关于AI辅助开发的个人体会用Claude Code做完这个项目我最大的感受是AI确实能极大提升开发效率但它替代不了人的判断。它能在你明确需求后快速生成代码能在你遇到报错时给出排查方向能在你写文档时帮你整理结构。但它不知道“用户打开App时不能闪”不知道“疫苗周期因种类而异”不知道“断网时不能崩溃”。这些判断来自经验来自对用户的理解来自对领域的熟悉。AI是放大器它放大你的能力也放大你的盲区。你越清楚自己要做什么它越好用你越模糊它越容易带你跑偏。所以我的建议是用AI做项目先把需求想清楚把架构定下来把关键决策做了然后让AI去执行。执行过程中保持怀疑每段代码都跑一遍每个体验都自己试一遍。这样下来AI就是你的超级助手反过来你就是AI的调试员累且低效。这个宠物生命周期管理App我还在持续迭代后面有新的经验和踩坑我会继续整理出来。如果你也在做类似的项目或者对某个环节有疑问欢迎交流。