[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"i-ri:search-line":3,"i-ri:moon-line":8,"i-ri:sun-line":10,"i-ri:menu-3-line":12,"i-ri:close-line":14,"notes-module-items-im":16,"notes-page-im-04-client-release\u002F01-flutter-build":66,"i-ri:arrow-left-s-line":460,"i-ri:menu-2-line":462,"i-ri:arrow-right-s-line":464,"i-ep:copy-document":466},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"currentColor\" d=\"m18.031 16.617l4.283 4.282l-1.415 1.415l-4.282-4.283A8.96 8.96 0 0 1 11 20c-4.968 0-9-4.032-9-9s4.032-9 9-9s9 4.032 9 9a8.96 8.96 0 0 1-1.969 5.617m-2.006-.742A6.98 6.98 0 0 0 18 11c0-3.867-3.133-7-7-7s-7 3.133-7 7s3.133 7 7 7a6.98 6.98 0 0 0 4.875-1.975z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":9},"\u003Cpath fill=\"currentColor\" d=\"M10 7a7 7 0 0 0 12 4.9v.1c0 5.523-4.477 10-10 10S2 17.523 2 12S6.477 2 12 2h.1A6.98 6.98 0 0 0 10 7m-6 5a8 8 0 0 0 15.062 3.762A9 9 0 0 1 8.238 4.938A8 8 0 0 0 4 12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":11},"\u003Cpath fill=\"currentColor\" d=\"M12 18a6 6 0 1 1 0-12a6 6 0 0 1 0 12m0-2a4 4 0 1 0 0-8a4 4 0 0 0 0 8M11 1h2v3h-2zm0 19h2v3h-2zM3.515 4.929l1.414-1.414L7.05 5.636L5.636 7.05zM16.95 18.364l1.414-1.414l2.121 2.121l-1.414 1.414zm2.121-14.85l1.414 1.415l-2.121 2.121l-1.414-1.414zM5.636 16.95l1.414 1.414l-2.121 2.121l-1.414-1.414zM23 11v2h-3v-2zM4 11v2H1v-2z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":13},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm6 7h12v2H9zm-6 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":15},"\u003Cpath fill=\"currentColor\" d=\"m12 10.587l4.95-4.95l1.414 1.414l-4.95 4.95l4.95 4.95l-1.415 1.414l-4.95-4.95l-4.949 4.95l-1.414-1.415l4.95-4.95l-4.95-4.95L7.05 5.638z\"\u002F>",[17,22,26,30,34,38,42,46,50,54,58,62],{"path":18,"title":19,"meta":20},"\u002Fnotes\u002Fim\u002F01-message-state\u002F01-offline-sync","连接恢复了，离线消息为什么还没补齐？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},"2026-11-05T09:47:15.000Z",{"path":23,"title":24,"meta":25},"\u002Fnotes\u002Fim\u002F01-message-state\u002F02-read-cursor","手机读过了，电脑为何又显示未读？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":27,"title":28,"meta":29},"\u002Fnotes\u002Fim\u002F01-message-state\u002F03-pagination-search","聊天搜索跳回旧消息后，怎样继续翻页？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":31,"title":32,"meta":33},"\u002Fnotes\u002Fim\u002F01-message-state\u002F04-unread-refresh","消息没停，未读角标就不更新？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":35,"title":36,"meta":37},"\u002Fnotes\u002Fim\u002F02-connection-protocol\u002F01-stream-parsing","一次接收回调，为什么不是一条消息？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":39,"title":40,"meta":41},"\u002Fnotes\u002Fim\u002F02-connection-protocol\u002F02-request-response","长连接上的请求，怎样等到自己的响应？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":43,"title":44,"meta":45},"\u002Fnotes\u002Fim\u002F02-connection-protocol\u002F03-connection-lifecycle","登录一直在连接中，到底在等什么？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":47,"title":48,"meta":49},"\u002Fnotes\u002Fim\u002F03-message-content\u002F01-file-download","附件下载完了，为什么还是打不开？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":51,"title":52,"meta":53},"\u002Fnotes\u002Fim\u002F03-message-content\u002F02-image-send","图片上传到 100%，为什么还没发出去？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":55,"title":56,"meta":57},"\u002Fnotes\u002Fim\u002F03-message-content\u002F03-mentions","聊天 @提及怎样保存身份和位置？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":59,"title":60,"meta":61},"\u002Fnotes\u002Fim\u002F04-client-release\u002F01-flutter-build","Release 包为什么仍连着测试环境？",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"path":63,"title":64,"meta":65},"\u002Fnotes\u002Fim","即时通信",{"date":-1,"tags":-1,"summary":-1,"lastUpdated":21},{"id":67,"type":68,"sourcePath":69,"routePath":59,"path":59,"title":60,"description":70,"updatedAt":21,"meta":71,"body":80,"toc":454},"bm90ZTpub3Rlcy9pbS8wNC1jbGllbnQtcmVsZWFzZS8wMS1mbHV0dGVyLWJ1aWxkLm1k","note","notes\u002Fim\u002F04-client-release\u002F01-flutter-build.md","一个 App 同时有测试和生产服务，打正式包时加了 --release，并不意味着它一定连接生产后端。Release 决定构建模式，服务器地址仍由应用自己的配置选择。",{"id":67,"type":68,"sourcePath":69,"slug":72,"title":60,"updatedAt":21,"description":70,"tags":73,"date":77,"comment":78,"summary":79,"lastUpdated":21},"im\u002F04-client-release\u002F01-flutter-build",[74,75,76],"flutter","build","engineering","2026-09-14",true,"发布模式不决定后端地址。构建时显式选择环境，再检查 Xcode 残留参数与最终 IPA，最后安装实际产物核对。",{"type":81,"children":82},"root",[83,91,106,111,118,141,154,223,237,243,248,253,360,365,371,384,397,402,408,413,443,448],{"type":84,"tag":85,"props":86,"children":88},"element","h1",{"id":87},"release-包为什么仍连着测试环境",[89],{"type":90,"value":60},"text",{"type":84,"tag":92,"props":93,"children":94},"p",{},[95,97,104],{"type":90,"value":96},"一个 App 同时有测试和生产服务，打正式包时加了 ",{"type":84,"tag":98,"props":99,"children":101},"code",{"className":100},[],[102],{"type":90,"value":103},"--release",{"type":90,"value":105},"，并不意味着它一定连接生产后端。Release 决定构建模式，服务器地址仍由应用自己的配置选择。",{"type":84,"tag":92,"props":107,"children":108},{},[109],{"type":90,"value":110},"这个 Flutter 项目使用构建参数选择后端，Android 和 iOS 脚本显式传入生产值。还要处理一种常见路径：命令行联调用过测试环境，随后直接从 Xcode 归档，生成配置中仍可能保留旧参数。",{"type":84,"tag":112,"props":113,"children":115},"h2",{"id":114},"构建模式与后端环境为什么要分别配置",[116],{"type":90,"value":117},"构建模式与后端环境，为什么要分别配置？",{"type":84,"tag":92,"props":119,"children":120},{},[121,130,132,139],{"type":84,"tag":122,"props":123,"children":127},"a",{"href":124,"rel":125},"https:\u002F\u002Fdocs.flutter.dev\u002Ftesting\u002Fbuild-modes",[126],"nofollow",[128],{"type":90,"value":129},"Flutter 构建模式",{"type":90,"value":131}," 区分 Debug、Profile 和 Release，分别用于开发、性能分析与发布。移动端开发中的 JIT、增量编译和发布时的 AOT，也是在决定编译执行方式，参见 ",{"type":84,"tag":122,"props":133,"children":136},{"href":134,"rel":135},"https:\u002F\u002Fdart.dev\u002Foverview#platform",[126],[137],{"type":90,"value":138},"Dart 平台说明",{"type":90,"value":140},"。这些都不替项目选择服务器。",{"type":84,"tag":92,"props":142,"children":143},{},[144,146,152],{"type":90,"value":145},"环境可以通过构建配置注入，避免打包前手动改请求地址、之后再改回来。项目用 ",{"type":84,"tag":98,"props":147,"children":149},{"className":148},[],[150],{"type":90,"value":151},"APP_BACKEND_ENV",{"type":90,"value":153},"，正式脚本分别执行：",{"type":84,"tag":155,"props":156,"children":161},"pre",{"className":157,"code":158,"language":159,"meta":160,"style":160},"language-bash shiki shiki-themes github-light github-dark","flutter build apk --release --dart-define=APP_BACKEND_ENV=prod\nflutter build ipa --release --dart-define=APP_BACKEND_ENV=prod\n","bash","",[162],{"type":84,"tag":98,"props":163,"children":164},{"__ignoreMap":160},[165,198],{"type":84,"tag":166,"props":167,"children":170},"span",{"class":168,"line":169},"line",1,[171,176,182,187,193],{"type":84,"tag":166,"props":172,"children":174},{"style":173},"--shiki-default:#6F42C1;--shiki-dark:#B392F0",[175],{"type":90,"value":74},{"type":84,"tag":166,"props":177,"children":179},{"style":178},"--shiki-default:#032F62;--shiki-dark:#9ECBFF",[180],{"type":90,"value":181}," build",{"type":84,"tag":166,"props":183,"children":184},{"style":178},[185],{"type":90,"value":186}," apk",{"type":84,"tag":166,"props":188,"children":190},{"style":189},"--shiki-default:#005CC5;--shiki-dark:#79B8FF",[191],{"type":90,"value":192}," --release",{"type":84,"tag":166,"props":194,"children":195},{"style":189},[196],{"type":90,"value":197}," --dart-define=APP_BACKEND_ENV=prod\n",{"type":84,"tag":166,"props":199,"children":201},{"class":168,"line":200},2,[202,206,210,215,219],{"type":84,"tag":166,"props":203,"children":204},{"style":173},[205],{"type":90,"value":74},{"type":84,"tag":166,"props":207,"children":208},{"style":178},[209],{"type":90,"value":181},{"type":84,"tag":166,"props":211,"children":212},{"style":178},[213],{"type":90,"value":214}," ipa",{"type":84,"tag":166,"props":216,"children":217},{"style":189},[218],{"type":90,"value":192},{"type":84,"tag":166,"props":220,"children":221},{"style":189},[222],{"type":90,"value":197},{"type":84,"tag":92,"props":224,"children":225},{},[226,228,235],{"type":90,"value":227},"参数在 Dart 中由编译环境声明读取，不是运行时系统环境变量，见 ",{"type":84,"tag":122,"props":229,"children":232},{"href":230,"rel":231},"https:\u002F\u002Fdart.dev\u002Flibraries\u002Fcore\u002Fenvironment-declarations",[126],[233],{"type":90,"value":234},"Dart 编译环境声明",{"type":90,"value":236},"。它会进入客户端产物，适合环境标识，不适合保存秘密。平台打包仍需要原生宿主、插件、资源、依赖和签名，Dart 编译完成只是其中一步。",{"type":84,"tag":112,"props":238,"children":240},{"id":239},"忘传或拼错参数应该失败还是用默认值",[241],{"type":90,"value":242},"忘传或拼错参数，应该失败还是用默认值？",{"type":84,"tag":92,"props":244,"children":245},{},[246],{"type":90,"value":247},"两种策略都可选择。拒绝未知值能尽早暴露错误，默认值则能减少遗漏参数造成的中断，但也可能让拼写错误继续生成产物。",{"type":84,"tag":92,"props":249,"children":250},{},[251],{"type":90,"value":252},"项目当前使用下面的选择规则：",{"type":84,"tag":254,"props":255,"children":256},"table",{},[257,281],{"type":84,"tag":258,"props":259,"children":260},"thead",{},[261],{"type":84,"tag":262,"props":263,"children":264},"tr",{},[265,271,276],{"type":84,"tag":266,"props":267,"children":268},"th",{},[269],{"type":90,"value":270},"输入",{"type":84,"tag":266,"props":272,"children":273},{},[274],{"type":90,"value":275},"Debug 后端",{"type":84,"tag":266,"props":277,"children":278},{},[279],{"type":90,"value":280},"Release\u002FProfile 后端",{"type":84,"tag":282,"props":283,"children":284},"tbody",{},[285,304,324,344],{"type":84,"tag":262,"props":286,"children":287},{},[288,294,299],{"type":84,"tag":289,"props":290,"children":291},"td",{},[292],{"type":90,"value":293},"未注入",{"type":84,"tag":289,"props":295,"children":296},{},[297],{"type":90,"value":298},"测试",{"type":84,"tag":289,"props":300,"children":301},{},[302],{"type":90,"value":303},"生产",{"type":84,"tag":262,"props":305,"children":306},{},[307,316,320],{"type":84,"tag":289,"props":308,"children":309},{},[310],{"type":84,"tag":98,"props":311,"children":313},{"className":312},[],[314],{"type":90,"value":315},"prod",{"type":84,"tag":289,"props":317,"children":318},{},[319],{"type":90,"value":303},{"type":84,"tag":289,"props":321,"children":322},{},[323],{"type":90,"value":303},{"type":84,"tag":262,"props":325,"children":326},{},[327,336,340],{"type":84,"tag":289,"props":328,"children":329},{},[330],{"type":84,"tag":98,"props":331,"children":333},{"className":332},[],[334],{"type":90,"value":335},"dev",{"type":84,"tag":289,"props":337,"children":338},{},[339],{"type":90,"value":298},{"type":84,"tag":289,"props":341,"children":342},{},[343],{"type":90,"value":298},{"type":84,"tag":262,"props":345,"children":346},{},[347,352,356],{"type":84,"tag":289,"props":348,"children":349},{},[350],{"type":90,"value":351},"其他值或空串",{"type":84,"tag":289,"props":353,"children":354},{},[355],{"type":90,"value":298},{"type":84,"tag":289,"props":357,"children":358},{},[359],{"type":90,"value":303},{"type":84,"tag":92,"props":361,"children":362},{},[363],{"type":90,"value":364},"因此未知值没有被全面拒绝。若以后改为构建失败，Dart 默认逻辑和平台检查要一起调整，构建记录也应能说明最终选中了哪个环境。",{"type":84,"tag":112,"props":366,"children":368},{"id":367},"直接从-xcode-归档怎样拦住旧的测试参数",[369],{"type":90,"value":370},"直接从 Xcode 归档，怎样拦住旧的测试参数？",{"type":84,"tag":92,"props":372,"children":373},{},[374,376,382],{"type":90,"value":375},"Flutter 联调后会生成 iOS 构建配置，其中可能残留 ",{"type":84,"tag":98,"props":377,"children":379},{"className":378},[],[380],{"type":90,"value":381},"DART_DEFINES",{"type":90,"value":383},"。仓库里的代码没有改地址，不等于本机生成配置没有测试值。",{"type":84,"tag":92,"props":385,"children":386},{},[387,389,395],{"type":90,"value":388},"项目在 Flutter 构建脚本前增加检查阶段，解码逗号分隔的值，在精确的 Release、Profile 配置下发现 ",{"type":84,"tag":98,"props":390,"children":392},{"className":391},[],[393],{"type":90,"value":394},"APP_BACKEND_ENV=dev",{"type":90,"value":396}," 就失败退出。",{"type":84,"tag":92,"props":398,"children":399},{},[400],{"type":90,"value":401},"这只覆盖指定配置名和 dev 值。新增自定义配置名称时要一起调整；参数缺失或未知值仍允许继续，由应用默认逻辑处理。它是针对残留测试参数的一道检查，尚未成为全面的环境参数校验。",{"type":84,"tag":112,"props":403,"children":405},{"id":404},"clean-过命令成功了能直接交付吗",[406],{"type":90,"value":407},"clean 过、命令成功了，能直接交付吗？",{"type":84,"tag":92,"props":409,"children":410},{},[411],{"type":90,"value":412},"两个平台脚本会清理生成状态再取依赖，Android 还清 Gradle，iOS 执行 pod install。这能减少旧构建状态干扰，但没有锁住全部 SDK、工具链、依赖和签名条件，因此不代表已经实现可复现构建。",{"type":84,"tag":92,"props":414,"children":415},{},[416,418,424,426,432,434,441],{"type":90,"value":417},"签名或导出配置出错，也不是重新编译 Dart 就能解决。iOS 归档与 IPA 要分别检查：归档在 ",{"type":84,"tag":98,"props":419,"children":421},{"className":420},[],[422],{"type":90,"value":423},"build\u002Fios\u002Farchive\u002F",{"type":90,"value":425},"，IPA 在 ",{"type":84,"tag":98,"props":427,"children":429},{"className":428},[],[430],{"type":90,"value":431},"build\u002Fios\u002Fipa\u002F",{"type":90,"value":433},"，见 ",{"type":84,"tag":122,"props":435,"children":438},{"href":436,"rel":437},"https:\u002F\u002Fdocs.flutter.dev\u002Fdeployment\u002Fios#create-an-app-bundle",[126],[439],{"type":90,"value":440},"iOS 构建与发布",{"type":90,"value":442},"。项目脚本缺分发证书时先提示，构建后找不到 IPA 则按失败处理。",{"type":84,"tag":92,"props":444,"children":445},{},[446],{"type":90,"value":447},"文件存在还没有验证安装、签名、版本、调试符号或实际后端。交付前应安装这次生成的包，核对版本与连接环境，并保存代码版本、模式、环境参数、工具版本和产物位置，保证后来排查的是同一份包。",{"type":84,"tag":449,"props":450,"children":451},"style",{},[452],{"type":90,"value":453},"html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":160,"searchDepth":200,"depth":200,"links":455},[456,457,458,459],{"id":114,"depth":200,"text":117},{"id":239,"depth":200,"text":242},{"id":367,"depth":200,"text":370},{"id":404,"depth":200,"text":407},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":461},"\u003Cpath fill=\"currentColor\" d=\"m10.828 12l4.95 4.95l-1.414 1.415L8 12l6.364-6.364l1.414 1.414z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":463},"\u003Cpath fill=\"currentColor\" d=\"M3 4h18v2H3zm0 7h12v2H3zm0 7h18v2H3z\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":465},"\u003Cpath fill=\"currentColor\" d=\"m13.172 12l-4.95-4.95l1.414-1.413L16 12l-6.364 6.364l-1.414-1.415z\"\u002F>",{"left":4,"top":4,"width":467,"height":467,"rotate":4,"vFlip":6,"hFlip":6,"body":468},1024,"\u003Cpath fill=\"currentColor\" d=\"M768 832a128 128 0 0 1-128 128H192A128 128 0 0 1 64 832V384a128 128 0 0 1 128-128v64a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64z\"\u002F>\u003Cpath fill=\"currentColor\" d=\"M384 128a64 64 0 0 0-64 64v448a64 64 0 0 0 64 64h448a64 64 0 0 0 64-64V192a64 64 0 0 0-64-64zm0-64h448a128 128 0 0 1 128 128v448a128 128 0 0 1-128 128H384a128 128 0 0 1-128-128V192A128 128 0 0 1 384 64\"\u002F>"]