1. 项目概述:一个为AI学习者打造的“一站式大学”
如果你最近也在关注AI领域,大概率会和我有同样的感受:这个领域的变化速度,已经快到让人喘不过气。今天OpenAI发布了新模型,明天Anthropic更新了上下文窗口,后天又冒出一个你没听过的初创公司,推出了某个垂直领域的专用API。对于开发者、产品经理,甚至是想要入行的学习者来说,光是搞清楚“现在有哪些AI服务”、“它们各自能做什么”、“我应该怎么用”,就已经是一项浩大的工程。
这不再是一个可以靠几篇博客文章就能跟上的时代了。信息是碎片化的,散落在官方文档、技术博客、社区帖子和新闻快讯里。于是,我萌生了一个想法:能不能建一个“AI领域的大学”,把所有这些分散的知识系统化、结构化地整理起来,让任何一个想学习的人,都能在一个地方,按图索骥地掌握主流AI服务商的核心信息?
这就是“AI University”项目的起点。它的核心目标很简单:成为一个覆盖主流AI服务商的、动态更新的、内置学习与考核机制的应用平台。更具体一点,我为自己设定了几个关键指标:第一,内容要全,至少要覆盖50家以上的AI提供商;第二,架构要灵活,新厂商的加入不能导致应用重构;第三,要有用户粘性,通过游戏化的方式激励持续学习;第四,开发要快,用最小可行产品(MVP)验证想法。
最终,我使用Flutter和Supabase,在3天时间内,构建了一个覆盖54家AI提供商、具备完整学习-测验-排名-分享功能的应用。这篇文章,我会详细拆解整个项目的架构设计、实现细节,以及那些让我在极短时间内完成开发的关键决策与工具链。无论你是想了解如何高效整合Flutter与Supabase,还是对构建数据驱动的动态应用感兴趣,亦或是想学习如何管理一个内容快速更迭的项目,相信都能从中找到参考。
2. 技术选型与架构设计思路
在项目启动前,技术栈的选择直接决定了开发效率和后期的可维护性。我的核心诉求是:前端要能快速构建美观、跨平台的UI;后端要能极简地处理数据存储、用户认证和实时更新,最好无需自己维护服务器。
2.1 为什么是Flutter + Supabase?
这个组合几乎是当前小型团队或个人开发者快速验证想法的“黄金搭档”。
Flutter的选择基于三点考虑:
- 跨平台一致性:我需要应用能同时覆盖Web、iOS和Android。Flutter一份代码多处部署的特性,完美契合了快速启动、广泛触达的需求。特别是对于学习类应用,用户可能在电脑浏览器上浏览,也可能在手机碎片时间学习,跨平台支持至关重要。
- 开发效率与热重载:Flutter的热重载功能对于UI调试效率的提升是颠覆性的。在构建包含动态标签页、卡片式内容、交互式测验等复杂UI时,能够实时看到改动效果,极大加快了开发节奏。
- 丰富的生态系统:
provider或riverpod用于状态管理,http或dio用于网络请求,还有大量成熟的UI组件库。这意味着我不需要从零开始造轮子,可以把精力集中在业务逻辑上。
Supabase的选择则更偏向于后端服务的“开箱即用”:
- 真正的后端即服务(BaaS):它提供了PostgreSQL数据库、即时API、身份认证、存储和实时订阅功能。对于这个项目,我主要用到前三项。最关键的是,它的数据库是真正的PostgreSQL,这意味着我可以使用所有熟悉的SQL功能,包括视图、函数、行级安全策略(RLS),这对于实现排行榜、分数更新等复杂逻辑至关重要。
- 无缝的客户端集成:Supabase为Flutter提供了官方且维护良好的SDK(
supabase_flutter)。通过几行配置,我就能在应用中进行用户登录、注册,并直接通过生成的API与数据库交互,完全绕过了传统后端API的编写工作。 - 行级安全策略(RLS):这是Supabase的一个杀手级功能。我可以在数据库层面定义“哪些用户能访问或修改哪些数据”,而不是在应用代码中写一堆权限判断。例如,在
ai_university_scores表上,我可以设置一条策略:用户只能插入或更新自己的分数记录。这样,客户端直接upsert分数时,无需担心安全问题,也省去了编写专用API端点的麻烦。
注意:虽然Supabase的RLS非常强大,但在设计策略时务必谨慎。错误的策略可能导致数据泄露或写入失败。我的经验是,为每张需要权限控制的表,先明确“匿名用户”、“已认证用户”、“管理员”等角色,再针对
SELECT、INSERT、UPDATE、DELETE操作分别编写策略,并在开发初期进行充分测试。
2.2 核心架构:以数据库为中心的动态驱动
传统的内容型应用,内容(如提供商的介绍、模型列表)往往是硬编码在应用内的,或者通过一个需要发版才能更新的配置文件来管理。这对于AI这个日新月异的领域来说,是完全不可接受的。我的目标是:增加一个新的AI提供商,只需要在数据库里插入数据,应用界面自动随之变化,无需更新客户端。
为此,我设计了完全以数据库为中心的架构:
- 单一事实来源:所有静态学习内容(提供商概述、模型列表、API指南)和动态内容(最新新闻)都存储在Supabase的一张核心表
ai_university_content中。这张表的结构决定了应用的形态。 - 动态UI生成:Flutter应用启动时,首先查询数据库,获取所有唯一的
provider(提供商)列表。然后,根据这个列表动态生成顶部的标签页(Tab)和对应的内容页面。这意味着,只要我在数据库里为“Sakana AI”这个新提供商插入了数据,下次用户打开应用或刷新时,界面上就会自动出现“Sakana AI”的标签页。 - 内容与逻辑分离:测验题目、分数、用户学习进度等,也都存储在各自的表中。应用UI只负责展示和交互,所有业务逻辑(如计算总分、更新连续学习天数)尽可能通过数据库的视图(View)和函数(Function)来完成。
这种架构的最大优势是解耦和可维护性。内容运营者(甚至是我自己通过脚本)可以专注于维护数据库里的内容,而应用开发者可以专注于优化用户体验和性能,两者通过定义清晰的数据库Schema进行协作,互不干扰。
3. 数据库设计与内容管理策略
数据库是整个平台的基石,设计的好坏直接影响到功能实现的复杂度、性能以及未来的扩展性。
3.1 核心内容表的设计
我创建了主表ai_university_content,其设计思路如下:
CREATE TABLE ai_university_content ( id uuid DEFAULT gen_random_uuid() PRIMARY KEY, provider text NOT NULL, -- 例如:'openai', 'anthropic', 'gemini' category text NOT NULL, -- 例如:'overview', 'models', 'api', 'news' title text NOT NULL, content text NOT NULL, -- 存储Markdown格式的文本 published_at date, UNIQUE (provider, category) -- 唯一约束,确保每个提供商每个类别只有一份内容 );字段解析与设计理由:
provider和category:这两个字段构成了内容的“坐标”。provider标识这是哪家AI公司,category定义内容类型。UNIQUE约束确保了一个提供商在一个类别下只有一份最新内容,避免了数据混乱。content使用text类型存储Markdown:这是关键决策。Markdown格式轻量、易读易写,并且Flutter端有非常成熟的Markdown渲染库(如flutter_markdown)。这让我可以在后端用最简单的方式维护富文本内容,前端也能获得不错的排版效果,无需引入复杂的富文本编辑器。published_at:这是一个可选的日期字段,对于news类别的文章特别有用,可以用于排序和筛选。
实操心得:关于内容结构化最初我考虑过为models(模型列表)设计更复杂的JSONB字段来存储结构化数据,但最终选择了统一的Markdown。原因在于,早期阶段速度优先,Markdown足以清晰展示模型名称、参数、特点等信息。如果未来需要更复杂的查询(如“找出所有支持函数调用的模型”),我可以再通过数据库迁移,将这部分内容拆分到专门的子表或转换为JSONB。在MVP阶段,避免过度设计,用最简单的方式跑通流程是第一要务。
3.2 自动化内容更新:双保险机制
静态内容(如公司概述、API基础用法)可以手动维护,但“新闻”(news)类别必须自动化。AI领域的新闻更新太快,手动更新不现实。我设计了一个两层级的“双保险”自动更新系统:
第一层:快速抓取(GitHub Actions, 每2小时一次)我编写了一个Python脚本,部署在GitHub Actions上。这个脚本会轮询我预先收集好的几十个AI相关博客、官网公告的RSS源。每次运行,它会抓取每个源最新的5篇文章标题和摘要,然后通过Supabase的API,更新到ai_university_content表中category='news'的记录里。这保证了信息的时效性,用户在白天几乎能看到小时级的最新动态。
第二层:深度加工(Claude Code + NotebookLM, 每4小时一次)RSS摘要通常比较简短。为了提供更有价值的“新闻解读”,我设置了另一个更慢但更智能的流程。同样使用定时任务(如Cron Job或另一个GitHub Actions),调用Claude Code实例。这个实例会读取第一层抓取到的新闻摘要,然后利用NotebookLM等工具进行深度研究和分析,生成一段包含背景、技术要点、潜在影响的、更丰富的解释性内容。最后,它用这段更优质的内容去覆盖(overwrite)第一层生成的简单摘要。
“后来者胜”的冲突解决策略两个系统都会更新同一条记录(同一个provider的news)。我采用了简单的“时间戳最新覆盖”策略。Supabase的UPDATE操作会自然更新记录。这样,清晨用户看到的是快速的RSS摘要,下午看到的就是经过Claude深度加工后的解读版。这种设计既保证了速度,又提升了内容质量。
注意事项:自动化更新一定要处理好错误和异常。我的脚本里包含了重试机制、网络超时设置,并且会记录详细的日志。如果Supabase API调用失败,脚本会发送通知到我的Slack或邮箱。对于内容抓取,务必遵守网站的
robots.txt协议,控制请求频率,避免给对方服务器造成压力。
4. Flutter前端实现详解
前端应用是用户直接交互的界面,需要做到清晰、流畅,并能完美适配从数据库动态获取的数据结构。
4.1 动态UI构建:从数据到界面
应用的核心界面是一个DefaultTabController,包含顶部的标签栏和对应的内容页面。关键点在于,标签和内容都不是硬编码的。
第一步:获取提供商列表应用启动后,首先向Supabase发送一个查询,获取所有不重复的provider值:
final List<Map<String, dynamic>> providerData = await _supabase .from('ai_university_content') .select('provider') .execute(); // 去重并排序 List<String> providers = providerData.map((e) => e['provider'] as String).toSet().toList()..sort();第二步:动态生成Tab根据获取到的providers列表,动态生成Tab组件。为了更友好,我维护了一个本地的Map,将provider的代码(如openai)映射为显示名称和表情符号。
final Map<String, Map<String, String>> _providerMeta = { 'openai': {'name': 'OpenAI', 'emoji': '🤖'}, 'anthropic': {'name': 'Anthropic', 'emoji': '🔬'}, 'gemini': {'name': 'Google Gemini', 'emoji': '🌐'}, // ... 其他53家提供商 }; List<Tab> buildTabs(List<String> providers) { return providers.map((providerCode) { final meta = _providerMeta[providerCode] ?? {'name': providerCode, 'emoji': '📄'}; return Tab( child: Row( mainAxisSize: MainAxisSize.min, children: [ Text(meta['emoji']!), SizedBox(width: 4), Text(meta['name']!), ], ), ); }).toList(); }这样,即使数据库里新增了一个provider为sakana_ai的记录,前端也会自动生成一个显示为“Sakana AI 🐟”的标签页(只要我在_providerMeta里添加了映射)。
第三步:按需加载内容每个标签页对应的内容页面是一个FutureBuilder或StreamBuilder,它根据当前选中的provider和category(如‘overview’),去Supabase查询对应的content(Markdown文本),然后使用flutter_markdown包进行渲染。通过TabBarView的physics属性设置为NeverScrollableScrollPhysics(),可以防止用户快速滑动时触发过多的并行网络请求,改为根据索引懒加载。
4.2 游戏化功能实现:积分、连续记录与排行榜
学习需要正向反馈。我设计了三个核心的游戏化功能:测验积分、连续学习记录和排行榜。
1. 测验与积分系统每个提供商的api类别内容后,会附带一个简单的测验(例如选择题)。用户提交答案后,前端会计算得分,并直接向ai_university_scores表执行upsert操作。
-- 该表通过RLS确保用户只能操作自己的记录 CREATE TABLE ai_university_scores ( user_id uuid REFERENCES auth.users NOT NULL, provider text NOT NULL, score int DEFAULT 0, last_updated_at timestamptz DEFAULT now(), UNIQUE (user_id, provider) );在Flutter中,操作非常简单:
await _supabase.from('ai_university_scores').upsert({ 'user_id': _currentUser.id, 'provider': currentProvider, 'score': newScore, // 计算出的新分数 }).execute();得益于RLS策略,这个直接来自客户端的操作是安全的。upsert确保了如果用户第一次测验该提供商就插入记录,后续再次测验则更新分数。
2. 连续学习记录系统这个功能旨在鼓励用户每日登录。逻辑是:记录用户最后一次学习(浏览任何提供商内容或进行测验)的日期。如果本次学习日期与上次记录日期不是同一天,则连续天数加1。 我选择在Supabase中创建一个存储过程(Stored Procedure)来处理这个逻辑,因为它涉及读取、判断和更新,在数据库端完成更原子、更高效。
CREATE OR REPLACE FUNCTION public.increment_streak(user_uuid uuid) RETURNS void LANGUAGE plpgsql SECURITY DEFINER -- 以函数定义者的权限运行,绕过RLS进行特定更新 AS $$ DECLARE last_study_date date; current_streak int; BEGIN -- 从用户配置表中获取上次学习日期和当前连续天数 SELECT last_studied, streak INTO last_study_date, current_streak FROM user_profiles WHERE id = user_uuid; -- 如果上次学习不是今天,则增加连续天数 IF last_study_date IS NULL OR last_study_date < CURRENT_DATE THEN UPDATE user_profiles SET streak = COALESCE(current_streak, 0) + 1, last_studied = CURRENT_DATE WHERE id = user_uuid; END IF; END; $$;在Flutter端,用户完成一次学习行为后,调用这个远程过程调用(RPC)即可:
await _supabase.rpc('increment_streak', params: {'user_uuid': _currentUser.id}).execute();3. 排行榜的实现排行榜需要聚合所有用户的分数。直接在客户端查询ai_university_scores表然后排序计算,在用户量多时会非常低效。最佳实践是在数据库端创建一个视图(View)。
CREATE VIEW ai_university_leaderboard AS SELECT user_id, SUM(score) AS total_score, COUNT(DISTINCT provider) AS providers_studied, RANK() OVER (ORDER BY SUM(score) DESC) AS rank FROM ai_university_scores GROUP BY user_id;这个视图实时计算每个用户的总分、学习过的提供商数量,并使用窗口函数RANK()进行排名。Flutter端只需要像查询普通表一样查询这个视图,结果就是已经排好序的排行榜数据,性能极佳。
final leaderboardData = await _supabase .from('ai_university_leaderboard') .select() .order('rank') .execute();4.3 分享功能:将成就可视化
为了让用户有更强的分享动力,我实现了“学习成就卡片”生成功能。思路是:在Flutter中,将一个包含用户头像、昵称、总分、排名等信息的Widget,渲染成一张图片。
关键技术点:RepaintBoundary与Web下载
- 包装Widget:将需要分享的卡片Widget用
RepaintBoundary包裹,并赋予一个GlobalKey。RepaintBoundary( key: _shareCardKey, child: ShareCardWidget(userData: userData), // 自定义的卡片组件 ) - 转换为图片:使用
GlobalKey获取RenderRepaintBoundary对象,并将其渲染为ui.Image。final RenderRepaintBoundary boundary = _shareCardKey.currentContext!.findRenderObject() as RenderRepaintBoundary; final ui.Image image = await boundary.toImage(pixelRatio: 2.0); // pixelRatio提高分辨率 final ByteData? byteData = await image.toByteData(format: ui.ImageByteFormat.png); final Uint8List pngBytes = byteData!.buffer.asUint8List(); - 触发下载(Web端):在Flutter Web环境中,可以将图片字节转换为Data URL,并通过模拟点击一个隐藏的
<a>标签来触发下载。
对于移动端(iOS/Android),则需要使用import 'dart:html' as web; // 仅在Web端导入 final base64 = base64Encode(pngBytes); final anchor = web.AnchorElement() ..href = 'data:image/png;base64,$base64' ..download = 'ai_university_progress.png'; anchor.click();path_provider和image_gallery_saver等包,将图片保存到相册。
这个功能虽然代码量不大,但极大地提升了产品的传播属性和用户的成就感。
5. 部署、自动化与效率提升心法
一个完整的项目离不开部署和运维。同时,如何在3天内完成54个提供商的内容填充,是另一个值得分享的效率故事。
5.1 部署与自动化提醒
应用部署:Flutter Web应用构建后,可以非常方便地部署到任何静态托管服务,如Firebase Hosting、Vercel、Netlify或Cloudflare Pages。我选择了Firebase Hosting,因为它与Flutter生态集成良好,部署命令简单(flutter build web然后firebase deploy)。
学习提醒自动化:为了提升用户留存,我实现了一个“学习提醒”系统。通过GitHub Actions的定时任务(Cron)功能,每天在目标用户群体的活跃时间(例如,东京时间上午9点)触发一个工作流。
# .github/workflows/daily-reminder.yml name: Daily Study Reminder on: schedule: - cron: '0 0 * * *' # UTC时间每天00:00,即东京时间09:00 jobs: send-reminders: runs-on: ubuntu-latest steps: - name: Call Reminder Endpoint run: | curl -X POST https://your-project.supabase.co/functions/v1/send_study_reminders \ -H "Authorization: Bearer ${{ secrets.SUPABASE_SERVICE_ROLE_KEY }}" \ -H "Content-Type: application/json"这个工作流会调用一个部署在Supabase Edge Functions(边缘函数)中的send_study_reminders函数。该函数内部逻辑是:
- 查询数据库,找出最近3到30天内没有学习记录的用户。
- 根据每个用户的历史进度和连续天数,生成个性化的提醒消息(例如:“你的连续学习天数已达5天,再学习2天即可获得周常勋章!今天来看看Groq的API吧?”)。
- 通过集成推送通知服务(如OneSignal)或发送邮件,将提醒触达用户。
实操心得:Edge Functions的选择Supabase Edge Functions基于Deno,非常适合这种轻量、事件驱动的后台任务。相比于自己维护一个服务器或使用复杂的云函数服务,它的配置更简单,与Supabase数据库的交互也更直接。对于发送通知这种可能耗时的操作,放在Edge Function中异步执行,不会阻塞主应用。
5.2 “3天54提供商”的效率秘诀
这可能是整个项目中最“硬核”的部分。内容创作是耗时的,尤其是要保证54家提供商,每家都有overview、models、api三个板块的优质内容。我的秘诀是:高度结构化的模板 + AI辅助生成 + 自动化流水线。
- 创建内容模板:我为每个提供商的三个板块设计了固定的Markdown模板。
overview.md:公司背景、核心方向、主要产品。models.md:以表格形式列出主要模型名称、上下文长度、关键特点、发布日期。api.md:API快速入门指南,包括认证方式、一个简单的代码示例(如cURL或Python)。
- 利用Claude Code进行批量生产:我没有手动编写54份内容。而是准备了种子信息(公司官网、文档链接),然后启动多个Claude Code实例。每个实例负责一个提供商。我给它指令:“请根据[官网链接],按照以下模板生成三个Markdown文件。” Claude Code能够自动浏览网页、提取信息,并格式化成我需要的模板。
- 严格的文件与流程管理:我使用一个本地目录
supabase/migrations/来管理所有的SQL种子文件。每个提供商对应一个SQL文件(如2024032001_insert_openai_content.sql)。Claude Code在生成内容后,会直接写入对应的SQL文件。然后,我通过Supabase CLI一键运行所有迁移(supabase db reset),数据就完整地进入了数据库。 - 并行化工作流:我同时在3个Claude Code实例上工作,每个实例负责不同的提供商集合。一个实例在生成A公司的内容时,另一个实例已经在审核B公司生成的内容,第三个实例则在编写C公司的API示例。这种“流水线”作业,将我的角色从“创作者”转变为“审核与调度员”,极大提升了效率。
核心避坑技巧:AI生成的内容一定要人工复核,特别是技术细节和代码示例。我的流程是:Claude生成 -> 我快速浏览关键数据(如模型参数是否准确)-> 运行SQL迁移 -> 在开发中的Flutter应用里实时查看效果。发现问题立即回退修改。永远不要完全信任AI的输出,尤其是涉及具体数字和代码时。
6. 遇到的问题、解决方案与未来规划
在高速开发中,不可避免地会遇到各种问题。以下是几个典型问题及我的解决思路。
6.1 性能与用户体验优化
问题1:首次加载白屏时间过长由于应用启动时需要从Supabase查询提供商列表和用户数据,在网络不佳时会有明显的等待。
解决方案:
- 数据预取与缓存:使用
supabase_flutter包自带的本地缓存功能,或集成hive、shared_preferences,将提供商列表等不常变的数据在首次加载后缓存到本地。下次启动时先显示缓存数据,再在后台静默更新。 - 骨架屏(Skeleton Screen):在内容加载区域使用骨架屏动画,给用户即时反馈,降低等待的焦虑感。
- 分页与懒加载:对于未来可能非常长的新闻列表,实现分页查询,而不是一次性拉取所有数据。
问题2:Web端图片分享功能在移动端不兼容如前所述,Web端使用dart:html实现下载,但这在移动端编译时会报错。
解决方案:使用条件导入(Conditional Import)和平台接口(Platform Interface)。
// 定义一个抽象类 abstract class ShareService { Future<void> saveImage(Uint8List pngBytes); } // 在Web端的实现 // share_service_web.dart class ShareServiceWeb implements ShareService { Future<void> saveImage(Uint8List pngBytes) async { // ... 使用dart:html的实现 } } // 在移动端的实现(通过MethodChannel调用原生代码) // share_service_mobile.dart class ShareServiceMobile implements ShareService { static const platform = MethodChannel('com.example.app/share'); Future<void> saveImage(Uint8List pngBytes) async { try { await platform.invokeMethod('saveImage', {'imageBytes': pngBytes}); } catch (e) { print('保存图片失败: $e'); } } } // 主文件中通过条件导出决定使用哪个实现这样,代码就能根据编译平台自动选择正确的实现。
6.2 数据一致性与安全考量
问题:并发更新下的数据竞争在“连续学习记录”场景中,如果用户非常快速地在两个设备上同时进行学习,可能会触发两次increment_streak函数调用,导致连续天数被错误地增加多次。
解决方案:利用数据库的事务和更精确的条件判断。在存储过程中,可以基于last_studied日期进行更原子化的检查与更新。PostgreSQL的UPDATE ... WHERE语句本身在事务中就是原子的。更稳健的做法是,将检查逻辑完全放在数据库函数中,就像我之前示例的那样,它在一个事务内完成读取、判断和更新,可以有效避免竞争条件。
安全提醒:虽然Supabase的RLS极大地简化了权限管理,但必须充分测试每条策略。特别是对于INSERT和UPDATE操作,要确保用户不能修改user_id来操作他人数据。对于像“排行榜”这种需要聚合所有用户数据的视图,要为其设置单独的RLS策略,通常允许已认证用户SELECT,但禁止INSERT/UPDATE/DELETE。
6.3 项目演进与未来可能的方向
这个MVP在3天内验证了核心想法。如果项目继续发展,我会优先考虑以下几个方向:
内容深度与互动性增强:
- 交互式代码沙盒:在
api板块集成一个可运行的代码编辑器(如使用Zipper或Replit的嵌入服务),让用户可以直接在浏览器里修改和运行调用AI API的代码,看到实时结果。 - 视频与图文教程:为复杂的API或模型特性制作简短的视频讲解或图文指南。
- 社区贡献:开放内容贡献渠道,让社区专家可以提交内容更新或新的提供商介绍,通过Pull Request流程进行审核合并。
- 交互式代码沙盒:在
学习路径个性化:
- 智能推荐:根据用户已学习的提供商和测验分数,推荐下一个最适合学习的内容。例如,学完了OpenAI GPT-4,系统可以推荐学习Anthropic Claude,并进行对比分析。
- 技能图谱:将AI能力标签化(如“文本生成”、“图像识别”、“语音合成”),为用户生成可视化的技能掌握图谱。
技术架构升级:
- 离线支持:利用Flutter的
isar或sembast等本地数据库,实现核心学习内容的离线阅读和测验,同步则在有网络时进行。 - 实时协作:如果引入社交功能(如好友一起学习),可以利用Supabase的Realtime功能,实现学习状态的实时同步。
- 微服务化:当用户量和功能复杂度增长到一定程度,可以将内容抓取、通知发送、数据分析等后台任务拆分为独立的微服务,提高系统的可伸缩性和可维护性。
- 离线支持:利用Flutter的
这个项目的核心收获,不仅仅是快速构建了一个可用的产品,更验证了“Flutter + Supabase”这套技术栈对于个人开发者或小团队在追求速度与灵活性时的强大威力。它将我从繁琐的后端开发中解放出来,让我能专注于产品逻辑和用户体验。而通过AI工具链的辅助,又将内容生产的效率提升了一个数量级。对于有志于快速实现想法的开发者来说,这套方法论或许值得一试。