ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

用Trae从0到1开发Flutter Web小游戏2048的实战指南

用Trae从0到1开发Flutter Web小游戏2048的实战指南 用 Trae 从 0 到 1 开发 Flutter Web 小游戏 2048这个项目我用两个晚上做完第一晚搭环境、写核心算法第二晚让 Trae 把 UI 和交互补全最后部署到 nginx 上手机浏览器打开就能直接玩。整个过程里 Trae 帮我省了不少力气但也确实给我挖了几个坑。这篇文章不打算只贴一堆代码我会把当初为什么选 Trae、为什么用 Flutter Web、算法怎么设计、AI 辅助开发时该让它干什么活、部署踩了什么坑一条龙讲清楚。如果你手头有个 Flutter 项目想试试 AI 辅助开发或者想用一个小而完整的项目练手这篇应该对你有点用。1. 项目概述与整体思路1.1 2048 为什么值得用 Flutter Web 写一遍2048 是一个 4x4 棋盘、通过方向键滑动合并数字的小游戏。规则简单到三句话能讲完但实现起来牵扯的核心点并不少棋盘数据结构怎么设计、每行每列的合并算法怎么写、随机生成方块的概率、胜负怎么判定、手势和键盘两套交互怎么同时兼容、手机和桌面响应式怎么适配。这个复杂度对练手来说刚刚好——太小了体现不出工程分工太大了又容易把注意力浪费在业务逻辑而不是技术实现上。Flutter Web 又是另一个值得尝试的点。很多人对 Flutter 的印象还停留在移动 App实际上从 3.x 版本开始Flutter Web 已经能把同一套 Dart 代码编译成网页应用而且 CanvasKit 渲染模式下的性能和动画表现都相当能打。2048 这种游戏本身对渲染就有要求数字方块移动、合并、缩放这些动画正好用来检验 Flutter Web 在真实项目里的实际体验而不是停留在Hello World层面。1.2 Trae 在这个项目里扮演什么角色Trae 是我最近常用的 AI IDE本质是一个深度集成 AI 能力的编辑器内置了 Chat、Build 和 Agent 几种工作模式。和单纯用 Copilot 做代码补全不同Trae 的 Agent 模式可以自己读项目上下文、直接改文件、在终端里执行命令这意味着我只需要把需求描述清楚它能帮我把整个功能文件生成出来而不是一行一行地等我触发提示。我选择 Trae 来做这个小游戏核心原因是写 UI 代码这种活正好是 AI 的舒适区。2048 的游戏界面无非是一个 4x4 的格子、分数面板、状态提示、开始按钮用 Flutter 写这些布局代码量不小但套路固定交给 AI 生成能省很多敲键盘的时间。真正需要我亲自把关的是合并算法和状态管理这些逻辑部分因为这里的边界条件非常多AI 写的代码表面看没问题跑起来就是不对。1.3 项目结构与技术选型总览整个项目我按功能分了三层lib/models/game_model.dart2048 核心逻辑棋盘数据、移动合并、随机生成、胜负判断。lib/screens/game_screen.dart游戏界面包括网格渲染、分数展示、手势和键盘事件。lib/main.dart应用入口和基础主题配置。状态管理只用了 Flutter 自带的ChangeNotifier没有上 Provider 或者 Riverpod。原因很简单这个游戏只有一个全局状态也就是棋盘数据没有任何页面需要跨模块共享这份状态引入重型状态管理反而增加理解成本。如果你以后要加排行榜、多用户存档再迁移到 Riverpod 也不迟小步快跑在这个项目里更合适。技术选型上还有两个决定值得说明一下。第一Web 端我没有用任何第三方游戏框架纯 Flutter 控件实现因为 2048 的交互足够简单不需要 Flame 这类引擎引入额外依赖只会增加首屏加载体积。第二棋盘渲染用的是GridView.builder而不是自定义CustomPainter虽然在动画表现上CustomPainter上限更高但 GridView 方案代码量少、容易维护方块的入场动画交给AnimatedScale就够用了。2. 从零准备开发环境2.1 安装 Trae 并完成基础配置Trae 的安装没什么好说的去官网下载对应系统的安装包一路下一步就行。第一次打开的时候有两件事需要立刻做。第一确认 AI 模型配置。Trae 底层支持多个模型不同模型在写 Dart 时候的表现差异挺大。我的实际体验是写 Flutter 这种强类型语言代码理解能力更强的模型对类型系统、BuildContext生命周期这些概念的把握明显更好不至于生成一堆编译不过的代码。打开设置找到模型切换的入口选择一个你自己顺手且对代码比较友好的模型即可。第二熟悉一下内置终端。Trae 自带终端面板我后面所有flutter命令都在编辑器里直接执行不需要切窗口。在开始写代码之前建议先在终端执行flutter --version确认 SDK 已经能被正确识别。注意很多新人容易忽略模型切换这一步拿默认模型硬写。如果你生成的代码频繁出现类型错误、API 过期、构造函数少传参数这类低级问题先检查是不是模型选得不对再考虑其它方向的优化这会帮你省下大量无意义的调试时间。2.2 Flutter SDK 安装与 FVM 多版本管理Flutter SDK 下载并不复杂去官方渠道下载压缩包解压即可官方文档写得很清楚。但我强烈建议装一个 FVMFlutter Version Management。原因很现实我电脑上同时有老项目用 Flutter 2.x新项目用 Flutter 3.x如果 PATH 里只配一个版本老项目分分钟编译挂掉。FVM 可以按项目目录切换 Flutter 版本配置好后在每个项目里执行fvm use 3.27.4就能锁定版本互不干扰。安装 FVM 很直接dart pub global activate fvm装完把 FVM 的缓存 SDK 路径配置好再通过 FVM 安装一个稳定版 Flutterfvm install stable fvm use stable之后在 Trae 里打开项目时把 SDK 路径指向 FVM 的实际缓存目录比如~/fvm/versions/stable。这样 IDE 的智能提示和终端里的flutter命令会用同一个版本不会出现编辑器里能编译、命令行却报版本不一致的诡异问题。这类问题特别隐蔽我之前在其它项目里被折腾过一次所以这次从最开始就统一好了环境。2.3 开启 Flutter Web 调试比较新的 Flutter SDK 默认已经开启 Web 支持不需要像老教程里那样手动执行flutter config --enable-web。创建项目的命令是flutter create game2048创建完在项目目录里执行flutter devices如果列出的设备里出现了 Chrome 或者 Edge说明 Web 支持没问题。直接跑起来flutter run -d chrome会打开一个 Chrome 窗口里面是 Flutter 默认的计数器 Demo。确认这一步跑通环境就算齐了。我第一次在这个环节磨了半小时后来发现是 FVM 的 PATH 配置没生效终端里执行的是系统自带的旧 Flutter和 IDE 里完全不是同一个版本——这类问题在搭环境时特别容易遇到排查思路就是先确认你每次执行的flutter到底是哪个路径下的可执行文件。3. 2048 核心算法与完整代码实现3.1 棋盘模型与状态管理2048 的棋盘我用二维数组ListListint表示0表示空格数字 2、4、8……表示对应的方块值。为什么不压成一维数组虽然一维数组在内存上更紧凑但 2048 的合并逻辑天然是按行和按列操作的二维结构能直接通过行列索引遍历代码可读性好太多。小游戏里那一点点性能差异根本不值得牺牲可维护性正确的做法是把代码写得让下一个人一眼看懂。状态管理选择了ChangeNotifier。Game2048继承ChangeNotifier在移动、重置时调用notifyListeners()UI 层用ListenableBuilder监听它数据变化后自动重建界面。这是 Flutter 官方自带的最小状态方案不需要额外依赖也没有 context 传递、依赖注入这些学习成本。3.2 合并算法只写一个方向其他靠旋转2048 的核心难点是移动合并规则。以向左移动为例每一行要做的事是剔除所有 0把相邻相同的数字合并一次再把 0 补回右侧。关键点在于合并只能进行一次比如一行是[4,4,4,0]向左合并后应该是[8,4,0,0]而不是[8,8,0,0]或者[4,8,0,0]这个细节很容易在实现时搞错。如果四个方向都分别去写合并逻辑代码会非常冗余且容易遗漏边界。我的做法是固定只写向左合并另外三个方向通过旋转棋盘来复用。具体思路是这样向上移动等价于把棋盘顺时针旋转 90 度后执行向左合并再逆时针旋转回去向右移动等价于旋转 180 度执行向左合并再转回向下移动等价于逆时针旋转 90 度执行向左合并再转回。这样整个移动逻辑只有一个_moveLeft其余方向只是棋盘变换的排列组合代码量直接少了一大半而且对方向的处理是统一的不容易出现某个方向行为不一致的 bug。3.3 随机生成、胜负判断与完整 GameModel每次有效移动后要在一个随机空白格子里生成新方块。新方块是 2 还是 4我按 9:1 的概率生成90% 是 210% 是 4。这个比例参考了原版 2048太高的 4 出现概率会让游戏难度上升过快太低又缺少惊喜。如果你是想做困难模式把 4 的概率调高就是一个最直接的调节杠杆。胜负判断分两步。先检查棋盘里有没有出现 2048 的方块有就标记胜利再检查是否还能移动。不能移动的条件是棋盘已满且任意相邻的格子水平或垂直没有相同数字。这两个条件都不满足游戏结束。这里要注意判断顺序应该先检查胜利再检查失败避免玩家同时达到 2048 和死局时界面先弹出 Game Over 的尴尬情况。下面是完整的Game2048模型代码这也是整个项目的核心文件import dart:math; import package:flutter/foundation.dart; enum Direction { left, right, up, down } class Game2048 extends ChangeNotifier { static const int size 4; static const int winScore 2048; final Random _random Random(); late ListListint _grid; int _score 0; bool _gameOver false; bool _win false; ListListint get grid _grid; int get score _score; bool get gameOver _gameOver; bool get win _win; Game2048() { reset(); } void reset() { _score 0; _gameOver false; _win false; _grid List.generate(size, (_) List.filled(size, 0)); _addRandomTile(); _addRandomTile(); notifyListeners(); } bool canMove() { for (int i 0; i size; i) { for (int j 0; j size; j) { if (_grid[i][j] 0) return true; if (j size - 1 _grid[i][j] _grid[i][j 1]) return true; if (i size - 1 _grid[i][j] _grid[i 1][j]) return true; } } return false; } void _checkStatus() { if (_grid.any((row) row.any((v) v winScore))) { _win true; } else if (!canMove()) { _gameOver true; } } void _addRandomTile() { final emptyTiles Pointint[]; for (int i 0; i size; i) { for (int j 0; j size; j) { if (_grid[i][j] 0) emptyTiles.add(Point(i, j)); } } if (emptyTiles.isEmpty) return; final pos emptyTiles[_random.nextInt(emptyTiles.length)]; _grid[pos.x][pos.y] _random.nextInt(10) 0 ? 4 : 2; } Listint _mergeLine(Listint line) { final tiles line.where((v) v ! 0).toList(); final result int[]; var i 0; while (i tiles.length) { if (i 1 tiles.length tiles[i] tiles[i 1]) { final merged tiles[i] * 2; result.add(merged); _score merged; i 2; } else { result.add(tiles[i]); i 1; } } while (result.length size) { result.add(0); } return result; } ListListint _rotate(ListListint matrix, int times) { var result matrix; for (var t 0; t times; t) { final rotated List.generate(size, (_) List.filled(size, 0)); for (var i 0; i size; i) { for (var j 0; j size; j) { rotated[j][size - 1 - i] result[i][j]; } } result rotated; } return result; } bool _moveLeft() { var changed false; for (var i 0; i size; i) { final oldRow Listint.from(_grid[i]); _grid[i] _mergeLine(_grid[i]); if (!listEquals(oldRow, _grid[i])) changed true; } return changed; } void move(Direction dir) { if (_gameOver || _win) return; final rotateTimes switch (dir) { Direction.left 0, Direction.up 1, Direction.right 2, Direction.down 3, }; _grid _rotate(_grid, rotateTimes); final changed _moveLeft(); _grid _rotate(_grid, (4 - rotateTimes) % 4); if (changed) { _addRandomTile(); _checkStatus(); notifyListeners(); } } }这段代码包含了前面讲的所有要点_mergeLine里的i 2保证了合并只能进行一次_rotate通过次数参数统一处理四个方向的转换move方法只有在棋盘真正发生变化时才生成新方块和触发通知避免无效操作导致界面频繁刷新。3.4 为什么移动后必须判断 changed这里有个很容易被忽略的细节用户按了方向键但棋盘根本动不了比如所有方块都堆在左边界再按左此时不应该生成新方块。所以_moveLeft会逐行比较合并前后的数组只要有任意一行发生变化才判定为一次有效移动。我第一次让 Trae 生成这个逻辑时它写出来的版本是无论棋盘有没有变化都会生成新方块结果出现了一个诡异现象朝墙壁方向连续按方向键棋盘里的 4 和 2 越冒越多玩家还没开始认真玩棋盘就被新方块塞满了。这个问题用单元测试很容易暴露但在 AI 生成的代码里却非常常见。后面我会专门讲一讲这类 AI 写代码的坑以及我怎么用测试来给 AI 的产出兜底。3.5 给核心算法补一组测试用例验证 2048 算法最直接的方式就是写测试。我建了一个test/game_model_test.dart按照几个典型边界场景来测测试场景输入期望结果空行合并[0,0,0,0][0,0,0,0]无变化单次合并[4,4,4,0][8,4,0,0]不允许连锁合并四连合并[2,2,2,2][4,4,0,0]而非[8,0,0,0]末尾合并[2,0,0,2][4,0,0,0]无变化判断全 0 行执行左移changed false不生成新方块由于_mergeLine是私有方法我通过暴露一个公开的move(Direction.left)来间接测试或者直接构造好_grid后调用移动方法。测试用例跑起来之后只要 AI 改坏了合并逻辑flutter test立刻就能报红这也让我在后面用 AI 扩展功能时心里更有底。4. 用 Trae 辅助开发 UI 与交互4.1 Chat 模式如何帮我写 UI 骨架核心算法写完剩下的 UI 部分我直接用 Trae 的 Chat 模式来生成。我给它提的需求是这样的帮我写一个 Flutter 游戏界面文件game_screen.dart展示 4x4 棋盘、当前分数和最高分、游戏状态提示进行中/胜利/结束用 GridView 渲染方块不同数值显示不同背景色支持滑动手势和键盘方向键。Trae 生成的第一版基本能用但有两个问题一是方块颜色用的是写死的 Color 数组后面我想调色板得改代码二是不支持键盘操作只实现了手势。我继续在 Chat 里追问补上键盘监听它把Focus和KeyEvent的代码片段补了进来还顺带处理了 Web 端方向键滚动页面的问题。AI 生成 UI 代码最大的优势是快但需要你有一个明确的需求清单。如果你自己都不清楚界面要展示哪些元素、操作方式是左滑还是点按钮、状态怎么呈现AI 产出的结果大概率是看起来哪都有细看都不对最后还得你自己返工。4.2 滑动手势与键盘方向键的具体实现手势部分我用Listener组件通过记录指针按下和抬起的位置计算滑动的方向向量再映射成Direction调用模型Listener( onPointerDown: (event) _start event.localPosition, onPointerUp: (event) { final offset event.localPosition - _start; if (offset.distance 20) return; if (offset.dx.abs() offset.dy.abs()) { model.move(offset.dx 0 ? Direction.right : Direction.left); } else { model.move(offset.dy 0 ? Direction.down : Direction.up); } }, child: ..., )这里有个细节20 像素以内的位移会被忽略防止用户点击按钮时误触发移动。Web 端桌面访问时鼠标拖拽也会触发这个手势这在 2048 里反而是个好事因为桌面用户既可以用方向键也可以用鼠标拖动。键盘监听用了Focus组件配合onKeyEvent处理四个方向键。需要注意 Flutter Web 在浏览器里默认会把方向键行为交给页面导致画面跟着滚动。解决方案是让处理方向键的焦点节点获取焦点并返回KeyEventResult.handled阻止事件冒泡这样浏览器就不知道方向键被用过了自然也就不会去滚动页面。4.3 方块动画与视觉反馈其实 Flutter 做 2048 的动画比我想象中简单。网格里每个方块我都用AnimatedScale包裹新方块生成时从 0.6 倍缩放到 1 倍合并后达到 2048 时做闪烁效果背景色则通过AnimatedContainer做渐变过渡。移动过程本身我暂时没有做完整的滑动插值而是直接刷新网格布局因为 Flutter Web 在 CanvasKit 下的性能足够直接跳变也能接受。这些动画代码也是让 Trae 生成的我只需要描述新生成的方块放大出现、合并时闪一下、不同数字颜色不一样。AI 把AnimationController和AnimatedBuilder组合起来实现了比较流畅的效果。不过这里有一个坑如果每个格子都各自维护一个AnimationController性能会很差Web 端低配机器直接卡。后来我让 Trae 重构了方案用TweenAnimationBuilder做统一处理效果立刻流畅很多。所以在让 AI 写动画时一定要提醒它考虑 widget 数量不要写出每个方块一个 controller的写法。4.4 AI 写代码的坑必须自己 review 边界Trae 帮我省了很多时间但它犯错误也更隐蔽。我遇到两个典型问题。第一合并算法里合并一次的规则它一开始写错了。它给出的版本是遍历当前行如果当前值和下一个值相等就直接合并但没有跳过被合并的下一个数。结果[4,4,4,0]变成了[8,8,0,0]。这个 bug 在不专门测试时很难发现因为 2048 的棋盘长期处于数字混杂状态偶尔出现一次错误合并玩家只会觉得分数怎么涨得这么快是不是哪里不对。第二它生成的随机生成方块代码会在一局游戏中反复使用同一个Random实例来生成随机位置但某些情况下忘记重置状态导致新方块会出现在同一个位置。这种随机性问题在调试时尤其折磨人因为它是时好时坏的复现不了就很难定位。我后来在 GameModel 里显式地把Random实例作为类成员维护问题才稳定消失。所以我的建议是AI 生成的游戏逻辑代码至少给它写一组单元测试。测试用例就是一些边界输入比如全 0 行、[4,4,4,0]、[2,2,2,2]、[0,0,0,2]这些跑一遍就知道合并逻辑对不对。这也是我用 Trae 这类工具之后养成的习惯——AI 负责写我负责验收验收不通过就打回让它重新改。5. Flutter Web 渲染与响应式适配5.1 渲染器选择默认 CanvasKit 就够了Flutter Web 支持多种渲染后端其中 CanvasKit 是当前的主流选择。它本质上是在浏览器里用 WebAssembly 跑一套 Skia 渲染引擎视觉效果和原生 App 几乎一致动画流畅度也高。老的 HTML Renderer 在兼容性上有一定优势但性能和视觉效果明显弱一截而且新版本 Flutter 已经逐步移除了 HTML Renderer新项目直接用默认的 CanvasKit 渲染即可。2048 这种对动画要求不算苛刻的游戏根本不挑渲染后端但 CanvasKit 首次加载确实有个痛点它需要下载几 MB 的 wasm 文件在弱网环境下会让用户看到白屏。第一次部署后我打开游戏白屏了将近三秒才出现界面后来排查发现是 wasm 文件没有被正确压缩nginx 加上 gzip 并配置合适的缓存头之后加载速度明显改善。这个放到下一章节详细说。5.2 响应式布局手机、平板、桌面一版搞定2048 的布局简单但想同时适配手机竖屏、手机横屏、桌面浏览器还是得做适配。我用了LayoutBuilder拿到父容器尺寸然后取宽高中较小的一方作为棋盘边长剩下的高度留给分数面板和状态栏LayoutBuilder( builder: (context, constraints) { final size constraints.biggest; final boardSize min(size.width, size.height - 160); return Column( children: [ ScorePanel(score: model.score), SizedBox( width: boardSize, height: boardSize, child: board, ), ], ); }, )棋盘内部用GridView.builder渲染 4x4 网格每个格子的宽高通过childAspectRatio设成 1 保证正方形。方块内的字体大小也是动态算的棋盘宽度除以 8大概就是方块能容纳的数字字号上限。这样无论屏幕多大棋盘永远保持合理比例不会出现数字溢出格子的情况。手机上还要处理安全区和浏览器地址栏高度。我在页面最外层套了SafeArea横屏时左右两端不会被刘海遮挡。这些小细节单独看都不难但堆在一起就是能玩和好用的区别。5.3 性能优化减少无谓 rebuildFlutter 的notifyListeners一触发监听它的整个 widget 树都会重建。2048 棋盘是 16 个格子一次重建量其实不大但如果你在build方法里做了列表拷贝、字符串拼接、随机数生成这类操作每次都重跑还是会浪费而且 Web 端的重建成本比移动端高。我的优化思路是给棋盘和分数面板分别包上RepaintBoundary把它们隔离成独立的绘制层。分数变化时只重绘分数区域移动方块时只重绘画布区域互不干扰。Web 端这种优化效果尤其明显因为浏览器渲染成本更高RepaintBoundary能直接把重绘范围限定在一个合成层内避免整棵页面树都跟着标记 dirty。另外一个容易忽略的点不要在build里直接调用Random()或者DateTime.now()这会让每次重建都产生不同的结果导致界面闪烁。这些非纯函数操作尽量放进模型层UI 层只负责根据状态渲染这是性能优化里成本最低、收益最高的一条原则。6. 打包部署与常见问题排查实录6.1 构建生产包与 nginx 子路径部署开发调试跑通后打包生产版本flutter build web产物在build/web目录下。如果游戏要部署在站点子目录而不是域名根路径需要在构建时指定 base hrefflutter build web --base-href/2048/这样就可以用 nginx 的location块把它挂到子路径下location /2048/ { alias /var/www/game2048/; index index.html; try_files $uri $uri/ /2048/index.html; }这里有个关键点Flutter Web 构建产物里包含很多资源文件路径如果 base href 写错页面会加载不出 CanvasKit 的 wasm表现就是白屏。最简单的方式是把--base-href配成和 nginx location 一致部署完在浏览器开发者工具的网络面板里检查资源请求路径一看便知。6.2 Service Worker 缓存引发的血泪坑Flutter Web 默认会在构建产物里注册一个 Service Worker把静态资源缓存到浏览器本地让二次访问实现秒开。但这个机制在开发迭代时很坑你发布了新版本用户的浏览器显示的还是旧版本。我第一次部署后修改了游戏配色再发布不管怎么刷新都是旧界面最后发现 Service Worker 把旧的main.dart.js缓存了。解决方法是给 nginx 配置缓存策略让版本化的资源文件强制不走缓存同时给index.html设置短缓存避免它被 Service Worker 长期持有location /2048/ { alias /var/www/game2048/; try_files $uri $uri/ /2048/index.html; } location ~* \.(js|wasm|css|png|ttf)$ { expires 30d; add_header Cache-Control public, immutable; }注意index.html本身不要加上面的强缓存否则 Service Worker 更新逻辑会被浏览器缓存干扰。发布新版本后如果担心用户侧还是旧包最简单的兜底方案是在代码里写一个版本号检测后台可以控制前端弹窗提示刷新。6.3 踩坑速查表我把这几次部署遇到的高频问题整理成一张表方便排查现象常见原因解决方案打开页面白屏资源路径 404 或 base href 不匹配检查网络面板资源请求确认--base-href与 nginx 路径一致刷新后仍是旧版本Service Worker 缓存了旧资源配置强缓存给带版本号的静态资源管理好index.html的缓存策略首次加载很慢CanvasKit 的 wasm 未压缩nginx 开启 gzip并对 wasm/js 文件做静态压缩手机端横屏棋盘被裁切未处理安全区域外层包SafeArea动态计算棋盘尺寸方向键触发页面滚动键盘事件未被拦截使用KeyEventResult.handled阻止事件冒泡连续按方向键出现大量新方块未判断棋盘是否真的变化移动后先比较前后棋盘有变化才生成新方块6.4 还能怎么扩展游戏能正常玩之后我在想接下来往哪个方向加东西。2048 这个项目再往下走有几个不错的方向一是给方块移动加滑动插值动画而不是直接跳变观感会好很多二是存档机制用shared_preferences保存当前棋盘和分数刷新页面后能继续玩三是排行榜如果自己搭后端太重可以考虑 Firebase 的匿名登录加实时数据库Web 端接入也很方便。还有一个我很感兴趣的方向是反向操作给移动加一个撤销按钮每一步都保存操作快照出错了可以回退。这个功能对提升交互体验很有帮助实现也不复杂只需要在move之前把棋盘状态和分数压栈撤销时弹栈即可。如果以后想把它做成完整的小游戏产品这些都是必须考虑的功能。最后说一点我个人的体会。用 Trae 完成 2048 这个项目我的分工是算法逻辑我先把思路理清楚AI 负责生成初版代码界面布局这种繁琐但清晰明了的工作全部丢给 AI等到编译报错或者逻辑不对让 AI 先自查一轮再由我来判对错。这个流程跑下来最大的感受是AI 编程工具确实能帮你把项目从 0 带到 1但它不能替你做判断。合并逻辑错了、随机数出问题、缓存不更新这些坑没有一个能靠提示词解决最终还是靠你理解游戏规则、理解浏览器缓存机制、理解 Flutter 的渲染原理。如果你也想上手玩一玩 Trae别一上来就让它生成整个项目。我的建议是先把核心算法写成测试用例再让 AI 填实现最后跑一遍flutter test验收。一开始 Trae 生成的代码经常在测试面前露馅改几轮之后你就会发现你对这个项目的理解深度决定了 AI 能替你省多少事。
返回列表