<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Flutter on Website of SauceWu</title><link>https://saucewu.github.io/tags/flutter/</link><description>Recent content in Flutter on Website of SauceWu</description><generator>Hugo -- gohugo.io</generator><language>zh</language><lastBuildDate>Fri, 24 Apr 2026 12:00:00 +0900</lastBuildDate><atom:link href="https://saucewu.github.io/tags/flutter/index.xml" rel="self" type="application/rss+xml"/><item><title>Mac Mini 作为 GitLab Runner：iOS 和 Flutter 打包节点配置指南</title><link>https://saucewu.github.io/posts/mac-mini-%E4%BD%9C%E4%B8%BA-gitlab-runnerios-%E5%92%8C-flutter-%E6%89%93%E5%8C%85%E8%8A%82%E7%82%B9%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</link><pubDate>Fri, 24 Apr 2026 12:00:00 +0900</pubDate><guid>https://saucewu.github.io/posts/mac-mini-%E4%BD%9C%E4%B8%BA-gitlab-runnerios-%E5%92%8C-flutter-%E6%89%93%E5%8C%85%E8%8A%82%E7%82%B9%E9%85%8D%E7%BD%AE%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="mac-mini-作为-gitlab-runnerios-和-flutter-打包节点配置指南"&gt;Mac Mini 作为 GitLab Runner：iOS 和 Flutter 打包节点配置指南&lt;/h1&gt;
&lt;p&gt;iOS 和 Flutter 项目的 CI 有一个绕不开的限制：&lt;strong&gt;必须在 macOS 上构建&lt;/strong&gt;。云端 macOS 实例贵，自建 Mac Mini 作为 GitLab Runner 是最常见的低成本方案。&lt;/p&gt;
&lt;p&gt;这篇文章记录从零把一台 Mac Mini 配置成 GitLab CI 打包节点的完整过程，覆盖环境安装、Runner 注册、&lt;code&gt;.gitlab-ci.yml&lt;/code&gt; 配置、常见坑和维护建议。&lt;/p&gt;</description></item><item><title>flutter-patrol-pilot：让 AI Agent 自主跑通 Flutter iOS 测试</title><link>https://saucewu.github.io/posts/flutter-patrol-pilot%E8%AE%A9-ai-agent-%E8%87%AA%E4%B8%BB%E8%B7%91%E9%80%9A-flutter-ios-%E6%B5%8B%E8%AF%95/</link><pubDate>Thu, 23 Apr 2026 16:00:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-patrol-pilot%E8%AE%A9-ai-agent-%E8%87%AA%E4%B8%BB%E8%B7%91%E9%80%9A-flutter-ios-%E6%B5%8B%E8%AF%95/</guid><description>&lt;h1 id="flutter-patrol-pilot让-ai-agent-自主跑通-flutter-ios-测试"&gt;flutter-patrol-pilot：让 AI Agent 自主跑通 Flutter iOS 测试&lt;/h1&gt;
&lt;p&gt;Flutter iOS 的集成测试一直是个麻烦事：Xcode 环境脆、xcresult 日志难读、Patrol 框架有自己的坑、失败原因五花八门。让人类来修还好，让 AI Agent 来修——很容易陷入死循环。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/sauce-wu/flutter-patrol-pilot"&gt;flutter-patrol-pilot&lt;/a&gt;（简称 fpp）是一个专门解决这个问题的 Claude Code Agent Skill：它接管整个 Flutter iOS 测试生命周期，从编译到运行，从失败诊断到代码修复，带着硬性规则和停止条件，不进死循环。&lt;/p&gt;</description></item><item><title>Flutter 状态管理选型：Provider、Riverpod、GetIt 对比</title><link>https://saucewu.github.io/posts/flutter-%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86%E9%80%89%E5%9E%8Bproviderriverpodgetit-%E5%AF%B9%E6%AF%94/</link><pubDate>Mon, 20 Apr 2026 13:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E7%8A%B6%E6%80%81%E7%AE%A1%E7%90%86%E9%80%89%E5%9E%8Bproviderriverpodgetit-%E5%AF%B9%E6%AF%94/</guid><description>&lt;h1 id="flutter-状态管理选型providerriverpodgetit-对比"&gt;Flutter 状态管理选型：Provider、Riverpod、GetIt 对比&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、先说结论：它们并不是同一类东西&lt;/li&gt;
&lt;li&gt;二、Provider：最贴近 Flutter 原生心智&lt;/li&gt;
&lt;li&gt;三、Riverpod：更现代的响应式状态管理&lt;/li&gt;
&lt;li&gt;四、GetIt：更像依赖注入容器，不是完整状态管理方案&lt;/li&gt;
&lt;li&gt;五、核心维度横向对比&lt;/li&gt;
&lt;li&gt;六、实际项目怎么选&lt;/li&gt;
&lt;li&gt;七、常见误区&lt;/li&gt;
&lt;li&gt;八、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一先说结论它们并不是同一类东西"&gt;一、先说结论：它们并不是同一类东西&lt;/h2&gt;
&lt;p&gt;很多人在 Flutter 里做技术选型时，会把 &lt;strong&gt;Provider / Riverpod / GetIt&lt;/strong&gt; 放在一起比较，但它们其实不完全是同一层的东西。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Provider&lt;/strong&gt;：基于 &lt;code&gt;InheritedWidget&lt;/code&gt; 的状态注入与订阅方案，和 Flutter widget tree 绑定很深&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Riverpod&lt;/strong&gt;：从 Provider 演化出来的独立响应式状态管理框架，不依赖 &lt;code&gt;BuildContext&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;GetIt&lt;/strong&gt;：本质是 &lt;strong&gt;Service Locator / 依赖注入容器&lt;/strong&gt;，负责“拿对象”，不天然负责“驱动 UI 刷新”&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以如果一句话概括：&lt;/p&gt;</description></item><item><title>Flutter 性能优化实战</title><link>https://saucewu.github.io/posts/flutter-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98/</link><pubDate>Mon, 20 Apr 2026 12:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E5%AE%9E%E6%88%98/</guid><description>&lt;h1 id="flutter-性能优化实战"&gt;Flutter 性能优化实战&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、性能问题的三个来源&lt;/li&gt;
&lt;li&gt;二、Build 阶段优化&lt;/li&gt;
&lt;li&gt;三、Layout / Paint 阶段优化&lt;/li&gt;
&lt;li&gt;四、内存与图片优化&lt;/li&gt;
&lt;li&gt;五、列表性能&lt;/li&gt;
&lt;li&gt;六、用 DevTools 定位问题&lt;/li&gt;
&lt;li&gt;七、速查清单&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="一性能问题的三个来源"&gt;一、性能问题的三个来源&lt;/h2&gt;
&lt;p&gt;Flutter 渲染一帧的流程是：&lt;strong&gt;Build → Layout → Paint → Composite → Rasterize&lt;/strong&gt;。性能问题几乎都出在前三个阶段：&lt;/p&gt;</description></item><item><title>Flutter 交易所架构实战：DDD + Clean 落地指南</title><link>https://saucewu.github.io/posts/flutter-%E4%BA%A4%E6%98%93%E6%89%80%E6%9E%B6%E6%9E%84%E5%AE%9E%E6%88%98ddd-+-clean-%E8%90%BD%E5%9C%B0%E6%8C%87%E5%8D%97/</link><pubDate>Mon, 20 Apr 2026 20:30:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-%E4%BA%A4%E6%98%93%E6%89%80%E6%9E%B6%E6%9E%84%E5%AE%9E%E6%88%98ddd-+-clean-%E8%90%BD%E5%9C%B0%E6%8C%87%E5%8D%97/</guid><description>&lt;h1 id="flutter-交易所架构实战ddd--clean-落地指南"&gt;Flutter 交易所架构实战：DDD + Clean 落地指南&lt;/h1&gt;
&lt;p&gt;交易所项目做到一定规模，就会开始还之前欠下的债——页面里写 API、规则到处复制、改一处牵十处。&lt;/p&gt;
&lt;p&gt;用 &lt;code&gt;DDD + Clean&lt;/code&gt; 不是为了架构好看，是因为不用它的话，这类项目很难不失控。&lt;/p&gt;
&lt;p&gt;这篇不讲大而空的概念，只讲三个问题：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;为什么交易所需要 &lt;code&gt;DDD + Clean&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在 Flutter 里到底怎么分层&lt;/li&gt;
&lt;li&gt;一笔下单请求如何在系统里流转（含时序图）&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id="一为什么交易所适合-ddd--clean"&gt;一、为什么交易所适合 DDD + Clean&lt;/h2&gt;
&lt;p&gt;交易所天然是“复杂业务系统”，不是普通内容 App。你会长期面对：&lt;/p&gt;</description></item><item><title>Flutter FFI 插件的 GitHub CI 实践：以 ceres-wallet-core 为例</title><link>https://saucewu.github.io/posts/flutter-ffi-%E6%8F%92%E4%BB%B6%E7%9A%84-github-ci-%E5%AE%9E%E8%B7%B5%E4%BB%A5-ceres-wallet-core-%E4%B8%BA%E4%BE%8B/</link><pubDate>Fri, 17 Apr 2026 18:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-ffi-%E6%8F%92%E4%BB%B6%E7%9A%84-github-ci-%E5%AE%9E%E8%B7%B5%E4%BB%A5-ceres-wallet-core-%E4%B8%BA%E4%BE%8B/</guid><description>&lt;h1 id="flutter-ffi-插件的-github-ci-实践以-ceres-wallet-core-为例"&gt;Flutter FFI 插件的 GitHub CI 实践：以 ceres-wallet-core 为例&lt;/h1&gt;
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;普通 Flutter 插件的 CI 很简单：跑一下 &lt;code&gt;flutter test&lt;/code&gt; 就够了。但 Flutter FFI 插件夹着 C++/Rust native 库，问题就复杂得多——&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;native 库需要在 macOS 上编译 iOS，在 Linux 上编译 Android，不能混&lt;/li&gt;
&lt;li&gt;编译一次要十几分钟，开发者 &lt;code&gt;pub get&lt;/code&gt; 的时候不能让他们自己编&lt;/li&gt;
&lt;li&gt;Trust Wallet Core 有几十万行 C++ 代码，子模块更新需要自动化&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;ceres-wallet-core 的解法是两条 workflow：&lt;/p&gt;</description></item><item><title>ceres-wallet-core：用 Flutter 封装 Trust Wallet Core</title><link>https://saucewu.github.io/posts/ceres-wallet-core%E7%94%A8-flutter-%E5%B0%81%E8%A3%85-trust-wallet-core/</link><pubDate>Thu, 16 Apr 2026 16:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/ceres-wallet-core%E7%94%A8-flutter-%E5%B0%81%E8%A3%85-trust-wallet-core/</guid><description>&lt;h1 id="ceres-wallet-core用-flutter-封装-trust-wallet-core"&gt;ceres-wallet-core：用 Flutter 封装 Trust Wallet Core&lt;/h1&gt;
&lt;h2 id="背景"&gt;背景&lt;/h2&gt;
&lt;p&gt;在做 Flutter 钱包应用时，密钥管理和交易签名是绕不开的核心模块。社区里能用的方案要么只支持 EVM 单链、要么 Dart 原生实现存在安全隐患、要么直接调 Web3 RPC 把私钥暴露在网络层。&lt;/p&gt;
&lt;p&gt;&lt;a href="https://github.com/trustwallet/wallet-core"&gt;Trust Wallet Core&lt;/a&gt; 是目前业界覆盖链最广、经过生产验证的开源密钥库，用 C++ 实现，已经支持 60+ 条链。问题是它没有官方的 Flutter 绑定。&lt;/p&gt;</description></item><item><title>ceres-mpc：基于 DKLs23 的 Flutter 两方 MPC 钱包 SDK</title><link>https://saucewu.github.io/posts/ceres-mpc%E5%9F%BA%E4%BA%8E-dkls23-%E7%9A%84-flutter-%E4%B8%A4%E6%96%B9-mpc-%E9%92%B1%E5%8C%85-sdk/</link><pubDate>Sun, 12 Apr 2026 17:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/ceres-mpc%E5%9F%BA%E4%BA%8E-dkls23-%E7%9A%84-flutter-%E4%B8%A4%E6%96%B9-mpc-%E9%92%B1%E5%8C%85-sdk/</guid><description>&lt;h1 id="ceres-mpc基于-dkls23-的-flutter-两方-mpc-钱包-sdk"&gt;ceres-mpc：基于 DKLs23 的 Flutter 两方 MPC 钱包 SDK&lt;/h1&gt;
&lt;h2 id="为什么需要-mpc-钱包"&gt;为什么需要 MPC 钱包&lt;/h2&gt;
&lt;p&gt;传统 HD 钱包的私钥完整存储在用户设备上，一旦设备丢失或被攻击，资产就没了。助记词备份虽然能恢复，但本身也是攻击面。&lt;/p&gt;
&lt;p&gt;MPC（Multi-Party Computation）钱包的思路是：&lt;strong&gt;私钥从始至终都不存在于任何单一设备&lt;/strong&gt;，签名由多方协作计算完成。两方 ECDSA 的典型部署是客户端持有一份密钥分片，服务端持有另一份，任意一方单独都无法完成签名。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;ceres-mpc&lt;/strong&gt; 基于 &lt;a href="https://eprint.iacr.org/2023/765"&gt;DKLs23 协议&lt;/a&gt; 实现了一套完整的 Flutter MPC 钱包 SDK，密码学核心用 Rust 编写，通过 flutter_rust_bridge 暴露给 Dart 调度层。&lt;/p&gt;</description></item><item><title>Flutter 手势系统与竞技场机制详解</title><link>https://saucewu.github.io/posts/flutter-%E6%89%8B%E5%8A%BF%E7%B3%BB%E7%BB%9F%E4%B8%8E%E7%AB%9E%E6%8A%80%E5%9C%BA%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</link><pubDate>Fri, 10 Apr 2026 10:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E6%89%8B%E5%8A%BF%E7%B3%BB%E7%BB%9F%E4%B8%8E%E7%AB%9E%E6%8A%80%E5%9C%BA%E6%9C%BA%E5%88%B6%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h1 id="flutter-手势系统与竞技场机制详解"&gt;Flutter 手势系统与竞技场机制详解&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、Gesture Arena（手势竞技场）运行原理&lt;/li&gt;
&lt;li&gt;二、与 Android 触摸分发机制的核心区别&lt;/li&gt;
&lt;li&gt;三、OneSequenceGestureRecognizer 生命周期详解&lt;/li&gt;
&lt;li&gt;四、MultiTapGestureRecognizer 使用场景与区别&lt;/li&gt;
&lt;li&gt;五、GestureArenaTeam 实际应用场景&lt;/li&gt;
&lt;li&gt;六、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h1 id="一gesture-arena手势竞技场运行原理"&gt;一、Gesture Arena（手势竞技场）运行原理&lt;/h1&gt;
&lt;h2 id="1-两层触摸模型"&gt;1. 两层触摸模型&lt;/h2&gt;
&lt;p&gt;Flutter 的触摸处理分为两层：&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;层级&lt;/th&gt;
&lt;th&gt;说明&lt;/th&gt;
&lt;th&gt;对应类&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Pointer&lt;/td&gt;
&lt;td&gt;原始触摸事件（down / move / up）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;PointerEvent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gesture&lt;/td&gt;
&lt;td&gt;语义手势（tap、drag、scale 等）&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GestureRecognizer&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Pointer 层不做任何裁决，&lt;strong&gt;所有命中节点都会收到完整的 PointerEvent 序列&lt;/strong&gt;。Gesture Arena 工作在 Gesture 层，负责在多个识别器之间做出唯一裁决。&lt;/p&gt;</description></item><item><title>Flutter 渲染流程与三棵树详解</title><link>https://saucewu.github.io/posts/flutter-%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B%E4%B8%8E%E4%B8%89%E6%A3%B5%E6%A0%91%E8%AF%A6%E8%A7%A3/</link><pubDate>Tue, 20 Jan 2026 10:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E6%B8%B2%E6%9F%93%E6%B5%81%E7%A8%8B%E4%B8%8E%E4%B8%89%E6%A3%B5%E6%A0%91%E8%AF%A6%E8%A7%A3/</guid><description>&lt;h1 id="flutter-渲染流程与三棵树详解"&gt;Flutter 渲染流程与三棵树详解&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、Flutter 渲染架构全览&lt;/li&gt;
&lt;li&gt;二、三棵树是什么&lt;/li&gt;
&lt;li&gt;三、三棵树的协作流程&lt;/li&gt;
&lt;li&gt;四、各层职责与修改时机&lt;/li&gt;
&lt;li&gt;五、常见场景：该动哪一层？&lt;/li&gt;
&lt;li&gt;六、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="一flutter-渲染架构全览"&gt;一、Flutter 渲染架构全览&lt;/h2&gt;
&lt;p&gt;Flutter 的渲染流程从 Dart 代码到屏幕像素，经过了多个层次的处理：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;你写的 Widget
↓
Widget Tree（配置描述）
↓
Element Tree（生命周期 + 状态管理）
↓
RenderObject Tree（布局 + 绘制）
↓
Layer Tree（合成）
↓
Skia / Impeller（光栅化）
↓
GPU → 屏幕
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;这套架构的核心设计目标是&lt;strong&gt;把&amp;quot;描述&amp;quot;和&amp;quot;执行&amp;quot;分离&lt;/strong&gt;，让 Widget 可以廉价重建，而真正昂贵的布局和绘制操作尽量复用。&lt;/p&gt;</description></item><item><title>StatefulShellRoute 踩坑：为什么 Loading 只遮半屏，pop 还会误关页面</title><link>https://saucewu.github.io/posts/statefulshellroute-%E8%B8%A9%E5%9D%91%E4%B8%BA%E4%BB%80%E4%B9%88-loading-%E5%8F%AA%E9%81%AE%E5%8D%8A%E5%B1%8Fpop-%E8%BF%98%E4%BC%9A%E8%AF%AF%E5%85%B3%E9%A1%B5%E9%9D%A2/</link><pubDate>Sat, 20 Dec 2025 16:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/statefulshellroute-%E8%B8%A9%E5%9D%91%E4%B8%BA%E4%BB%80%E4%B9%88-loading-%E5%8F%AA%E9%81%AE%E5%8D%8A%E5%B1%8Fpop-%E8%BF%98%E4%BC%9A%E8%AF%AF%E5%85%B3%E9%A1%B5%E9%9D%A2/</guid><description>&lt;h1 id="statefulshellroute-踩坑为什么-loading-只遮半屏pop-还会误关页面"&gt;StatefulShellRoute 踩坑：为什么 Loading 只遮半屏，pop 还会误关页面&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、问题现象&lt;/li&gt;
&lt;li&gt;二、根因不是 Dialog，而是 Navigator 变多了&lt;/li&gt;
&lt;li&gt;三、为什么只遮住半屏&lt;/li&gt;
&lt;li&gt;四、为什么 pop 之后页面被关了&lt;/li&gt;
&lt;li&gt;五、正确做法：全局浮层挂到 root navigator&lt;/li&gt;
&lt;li&gt;六、一个更稳的封装思路&lt;/li&gt;
&lt;li&gt;七、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一问题现象"&gt;一、问题现象&lt;/h2&gt;
&lt;p&gt;我在用 &lt;code&gt;go_router + StatefulShellRoute&lt;/code&gt; 做多 Tab 导航时，遇到了一个很烦的坑：&lt;/p&gt;</description></item><item><title>Flutter 路由方案对比：原生 Router 与 go_router</title><link>https://saucewu.github.io/posts/flutter-%E8%B7%AF%E7%94%B1%E6%96%B9%E6%A1%88%E5%AF%B9%E6%AF%94%E5%8E%9F%E7%94%9F-router-%E4%B8%8E-go_router/</link><pubDate>Mon, 10 Nov 2025 14:00:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E8%B7%AF%E7%94%B1%E6%96%B9%E6%A1%88%E5%AF%B9%E6%AF%94%E5%8E%9F%E7%94%9F-router-%E4%B8%8E-go_router/</guid><description>&lt;h1 id="flutter-路由方案对比原生-router-与-go_router"&gt;Flutter 路由方案对比：原生 Router 与 go_router&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、为什么 Flutter 路由总让人觉得“有点拧巴”&lt;/li&gt;
&lt;li&gt;二、先区分三套东西：Navigator、原生 Router、go_router&lt;/li&gt;
&lt;li&gt;三、原生 Router 的价值到底是什么&lt;/li&gt;
&lt;li&gt;四、go_router 做了什么封装&lt;/li&gt;
&lt;li&gt;五、go_router 的独特优势&lt;/li&gt;
&lt;li&gt;六、什么时候该选原生 Router，什么时候该选 go_router&lt;/li&gt;
&lt;li&gt;七、常见误区&lt;/li&gt;
&lt;li&gt;八、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一为什么-flutter-路由总让人觉得有点拧巴"&gt;一、为什么 Flutter 路由总让人觉得“有点拧巴”&lt;/h2&gt;
&lt;p&gt;很多人第一次接触 Flutter 路由时，会觉得：&lt;/p&gt;</description></item><item><title>Flutter 键盘弹起不流畅的根因与优化方案</title><link>https://saucewu.github.io/posts/flutter-%E9%94%AE%E7%9B%98%E5%BC%B9%E8%B5%B7%E4%B8%8D%E6%B5%81%E7%95%85%E7%9A%84%E6%A0%B9%E5%9B%A0%E4%B8%8E%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88/</link><pubDate>Thu, 23 Oct 2025 19:20:00 +0000</pubDate><guid>https://saucewu.github.io/posts/flutter-%E9%94%AE%E7%9B%98%E5%BC%B9%E8%B5%B7%E4%B8%8D%E6%B5%81%E7%95%85%E7%9A%84%E6%A0%B9%E5%9B%A0%E4%B8%8E%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88/</guid><description>&lt;h1 id="flutter-键盘弹起不流畅的根因与优化方案"&gt;Flutter 键盘弹起不流畅的根因与优化方案&lt;/h1&gt;
&lt;h2 id="目录"&gt;目录&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;一、现象：为什么总感觉“不顺”&lt;/li&gt;
&lt;li&gt;二、根因 1：Flutter 响应的是 Insets 变化，不是系统级联动动画&lt;/li&gt;
&lt;li&gt;三、根因 2：键盘一弹，往往整棵页面都在重排&lt;/li&gt;
&lt;li&gt;四、根因 3：默认方案优先保证“别挡住”，不保证“像原生一样丝滑”&lt;/li&gt;
&lt;li&gt;五、几个最常见的踩坑场景&lt;/li&gt;
&lt;li&gt;六、优化思路：不要让整页跟着键盘一起动&lt;/li&gt;
&lt;li&gt;七、几种常见实现方案&lt;/li&gt;
&lt;li&gt;八、一个更稳的实战建议&lt;/li&gt;
&lt;li&gt;九、总结&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id="一现象为什么总感觉不顺"&gt;一、现象：为什么总感觉“不顺”&lt;/h2&gt;
&lt;p&gt;很多 Flutter 开发者都遇到过这个问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;输入框获取焦点时，页面像被突然“顶”了一下&lt;/li&gt;
&lt;li&gt;底部输入栏和键盘不是一起上来的，而是分两拍&lt;/li&gt;
&lt;li&gt;列表先跳一下，输入框再动一下&lt;/li&gt;
&lt;li&gt;iOS 上尤其明显，总觉得没有原生页面跟键盘联动得自然&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;功能上看，它通常是对的：&lt;/p&gt;</description></item><item><title>Flutter K线系统拆解（五）：绘图工具系统设计与工程优化</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%BA%94%E7%BB%98%E5%9B%BE%E5%B7%A5%E5%85%B7%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%B7%A5%E7%A8%8B%E4%BC%98%E5%8C%96/</link><pubDate>Mon, 21 Jul 2025 13:35:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%BA%94%E7%BB%98%E5%9B%BE%E5%B7%A5%E5%85%B7%E7%B3%BB%E7%BB%9F%E8%AE%BE%E8%AE%A1%E4%B8%8E%E5%B7%A5%E7%A8%8B%E4%BC%98%E5%8C%96/</guid><description>&lt;h1 id="flutter-k线系统拆解五绘图工具系统设计与工程优化"&gt;Flutter K线系统拆解（五）：绘图工具系统设计与工程优化&lt;/h1&gt;
&lt;h2 id="为什么要单独讲这一篇"&gt;为什么要单独讲这一篇&lt;/h2&gt;
&lt;p&gt;K 线里的“画线工具”看起来是附属能力，但实现难度其实很高。&lt;br&gt;
它同时涉及：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;绘图对象数据建模&lt;/li&gt;
&lt;li&gt;点位命中与拖拽编辑&lt;/li&gt;
&lt;li&gt;坐标与价格/时间映射&lt;/li&gt;
&lt;li&gt;持久化与跨周期恢复&lt;/li&gt;
&lt;li&gt;手势冲突与性能控制&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;如果这套系统没设计好，常见问题就是：点不中、拖不稳、误触多、数据丢、越画越卡。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一先看系统分层抽象视角"&gt;一、先看系统分层（抽象视角）&lt;/h2&gt;
&lt;p&gt;一个完整的绘图工具系统，建议拆成 4 层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;控制层：管理当前绘图对象集合、选中状态、缓存恢复、默认样式&lt;/li&gt;
&lt;li&gt;绘图对象层：定义线段/射线/矩形/圆/斐波那契等结构与完成条件&lt;/li&gt;
&lt;li&gt;渲染命中层：负责路径构建、绘制、命中测试、控制点渲染&lt;/li&gt;
&lt;li&gt;交互层：手势状态机（创建/编辑/移动/缩放）与工具面板联动&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;这类分层在交易类图表里经过大量实践验证，长期维护成本也更低。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二绘图对象模型怎么设计更稳"&gt;二、绘图对象模型怎么设计更稳&lt;/h2&gt;
&lt;h3 id="1-基础绘图对象字段"&gt;1) 基础绘图对象字段&lt;/h3&gt;
&lt;p&gt;建议每个绘图对象至少包含：&lt;/p&gt;</description></item><item><title>Flutter K线系统拆解（四）：交互体系与性能工程</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E5%9B%9B%E4%BA%A4%E4%BA%92%E4%BD%93%E7%B3%BB%E4%B8%8E%E6%80%A7%E8%83%BD%E5%B7%A5%E7%A8%8B/</link><pubDate>Mon, 07 Apr 2025 13:15:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E5%9B%9B%E4%BA%A4%E4%BA%92%E4%BD%93%E7%B3%BB%E4%B8%8E%E6%80%A7%E8%83%BD%E5%B7%A5%E7%A8%8B/</guid><description>&lt;h1 id="flutter-k线系统拆解四交互体系与性能工程"&gt;Flutter K线系统拆解（四）：交互体系与性能工程&lt;/h1&gt;
&lt;h2 id="为什么交互比指标更容易暴露问题"&gt;为什么交互比指标更容易暴露问题&lt;/h2&gt;
&lt;p&gt;很多交易页在功能上都能“跑起来”，但只要用户开始频繁缩放、拖动、长按，体验差异会立刻被放大。&lt;br&gt;
体感里的“丝滑”和“卡顿”，通常不来自单一 bug，而是交互链路里多个小问题叠加：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;手势事件触发过密&lt;/li&gt;
&lt;li&gt;状态更新范围过大&lt;/li&gt;
&lt;li&gt;动画和计算抢同一帧预算&lt;/li&gt;
&lt;li&gt;非关键绘制在高频交互里持续执行&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;所以这一篇的重点不是“某个技巧”，而是如何把交互链路做成一套稳定的工程系统。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一交互设计里的三个关键决定"&gt;一、交互设计里的三个关键决定&lt;/h2&gt;
&lt;h3 id="1-方向锁定先判断用户意图再决定谁接管手势"&gt;1) 方向锁定：先判断用户意图，再决定谁接管手势&lt;/h3&gt;
&lt;p&gt;在图表可编辑、页面又可滚动的场景里，方向锁是刚需。&lt;br&gt;
当手势轨迹偏垂直时，把事件交给外层滚动容器；偏水平时再由图表处理，这能明显减少“手势打架”。&lt;/p&gt;
&lt;p&gt;方向锁不是锦上添花，而是复杂页面里“能不能用”的分水岭。&lt;/p&gt;
&lt;h3 id="2-状态分离长按拖拽缩放不要混成一个开关"&gt;2) 状态分离：长按、拖拽、缩放不要混成一个开关&lt;/h3&gt;
&lt;p&gt;把 &lt;code&gt;isLongPress&lt;/code&gt;、&lt;code&gt;isDrag&lt;/code&gt;、&lt;code&gt;isScale&lt;/code&gt; 独立维护，收益很直接：&lt;br&gt;
状态更可控、回归更容易、边界问题更少。&lt;br&gt;
尤其在十字线与拖拽共存时，状态分离可以显著降低互相干扰。&lt;/p&gt;
&lt;h3 id="3-绘制隔离高频区域和低频区域分开"&gt;3) 绘制隔离：高频区域和低频区域分开&lt;/h3&gt;
&lt;p&gt;给图表主体加 &lt;code&gt;RepaintBoundary&lt;/code&gt;，本质是在缩小重绘传播范围。&lt;br&gt;
在包含导航栏、侧边信息、浮层工具条的复杂页面里，这个动作通常会带来稳定的帧率收益。&lt;/p&gt;</description></item><item><title>Flutter K线系统拆解（三）：渲染管线与绘制分层</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%B8%89%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E7%BB%98%E5%88%B6%E5%88%86%E5%B1%82/</link><pubDate>Tue, 01 Apr 2025 13:00:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%B8%89%E6%B8%B2%E6%9F%93%E7%AE%A1%E7%BA%BF%E4%B8%8E%E7%BB%98%E5%88%B6%E5%88%86%E5%B1%82/</guid><description>&lt;h1 id="flutter-k线系统拆解三渲染管线与绘制分层"&gt;Flutter K线系统拆解（三）：渲染管线与绘制分层&lt;/h1&gt;
&lt;p&gt;这一篇聚焦渲染层最关键的问题：&lt;br&gt;
如何把复杂 K 线绘制从“巨型 paint 方法”重构成可维护、可扩展、可定位问题的阶段化系统。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一渲染底座先算清楚再开始画"&gt;一、渲染底座：先算清楚，再开始画&lt;/h2&gt;
&lt;p&gt;一帧绘制开始前，建议先完成 5 件事：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;计算绘制区域（主图、成交量、副图）&lt;/li&gt;
&lt;li&gt;根据缩放和滚动计算可视区索引范围&lt;/li&gt;
&lt;li&gt;在可视区内扫描 max/min（主图、vol、副图）&lt;/li&gt;
&lt;li&gt;初始化渲染器&lt;/li&gt;
&lt;li&gt;构建并执行渲染管线&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;其中最影响性能的是两点：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;像素坐标反推数据索引&lt;/li&gt;
&lt;li&gt;只绘制可见区，不遍历全量数据&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;滚动和缩放能否稳定流畅，基本就看这一步是否扎实。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二渲染器组装把画什么与怎么调度分开"&gt;二、渲染器组装：把“画什么”与“怎么调度”分开&lt;/h2&gt;
&lt;p&gt;渲染层建议把主图、成交量、副图拆成独立渲染器，由装配层按当前模式组合：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;主图：&lt;code&gt;CandleRenderer&lt;/code&gt; 或 &lt;code&gt;TimeLineRenderer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;成交量：&lt;code&gt;VolRenderer&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;副图：&lt;code&gt;SecondaryRenderer&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;再通过策略层控制是否启用某些区域（比如成交量、副图）。&lt;/p&gt;</description></item><item><title>Flutter K线系统拆解（二）：指标引擎与增量计算</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%BA%8C%E6%8C%87%E6%A0%87%E5%BC%95%E6%93%8E%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%AE%A1%E7%AE%97/</link><pubDate>Mon, 31 Mar 2025 12:45:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%BA%8C%E6%8C%87%E6%A0%87%E5%BC%95%E6%93%8E%E4%B8%8E%E5%A2%9E%E9%87%8F%E8%AE%A1%E7%AE%97/</guid><description>&lt;h1 id="flutter-k线系统拆解二指标引擎与增量计算"&gt;Flutter K线系统拆解（二）：指标引擎与增量计算&lt;/h1&gt;
&lt;h2 id="目标"&gt;目标&lt;/h2&gt;
&lt;p&gt;这篇只讲一件事：&lt;br&gt;
如何把 K 线指标计算做成&lt;strong&gt;高性能、可维护、可测试&lt;/strong&gt;的生产级引擎。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一最佳实践架构三层拆分"&gt;一、最佳实践架构：三层拆分&lt;/h2&gt;
&lt;p&gt;指标模块建议拆成三层：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;code&gt;IndicatorConfig&lt;/code&gt;（配置层）：维护 MA/BOLL/MACD 等参数与开关&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IndicatorEngine&lt;/code&gt;（计算层）：只负责输入数据、输出结果，不关心 UI&lt;/li&gt;
&lt;li&gt;&lt;code&gt;ChartAdapter&lt;/code&gt;（消费层）：把结果喂给渲染，不做任何业务计算&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;核心原则：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;渲染层只读结果，不参与计算&lt;/li&gt;
&lt;li&gt;计算层纯函数化，避免隐式全局状态&lt;/li&gt;
&lt;li&gt;配置变更和行情更新走不同刷新路径&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id="二指标计算顺序要固定"&gt;二、指标计算顺序要固定&lt;/h2&gt;
&lt;p&gt;生产上要明确“依赖顺序”，不能随意调整。&lt;/p&gt;
&lt;p&gt;推荐顺序：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;输入 &lt;code&gt;List&amp;lt;KLineEntity&amp;gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;MA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;BOLL&lt;/code&gt;（依赖 MA）&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;Volume MA&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;KDJ&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;MACD&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;RSI&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;WR&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;计算 &lt;code&gt;SuperTrend&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;结果回填到每个 &lt;code&gt;KLineEntity&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;渲染层直接读取实体字段&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;建议把顺序写进统一入口方法，避免外部随意单独调用。&lt;/p&gt;</description></item><item><title>Flutter K线系统拆解（一）：架构分层与领域模型</title><link>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%B8%80%E6%9E%B6%E6%9E%84%E5%88%86%E5%B1%82%E4%B8%8E%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8B/</link><pubDate>Thu, 20 Mar 2025 12:30:00 +0900</pubDate><guid>https://saucewu.github.io/posts/flutter-k%E7%BA%BF%E7%B3%BB%E7%BB%9F%E6%8B%86%E8%A7%A3%E4%B8%80%E6%9E%B6%E6%9E%84%E5%88%86%E5%B1%82%E4%B8%8E%E9%A2%86%E5%9F%9F%E6%A8%A1%E5%9E%8B/</guid><description>&lt;h1 id="flutter-k线系统拆解一架构分层与领域模型"&gt;Flutter K线系统拆解（一）：架构分层与领域模型&lt;/h1&gt;
&lt;p&gt;K 线在 demo 阶段通常不难，难的是进入真实交易场景之后的长期维护。&lt;br&gt;
一旦业务进入高频迭代，常见问题很快会暴露出来：主题体系耦合、模式分支散落、改动范围不可控、扩展成本越来越高。&lt;/p&gt;
&lt;p&gt;这一篇只讲架构层面的核心问题：&lt;br&gt;
如何把 K 线从“能画出来的组件”做成“可长期演进的系统”。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一推荐的系统分层"&gt;一、推荐的系统分层&lt;/h2&gt;
&lt;p&gt;一套可维护的 K 线实现，通常至少要有这几层：&lt;/p&gt;
&lt;pre tabindex="0"&gt;&lt;code&gt;入口与装配层：图表 widget 生命周期与运行时状态
SDK 抽象层：主题、国际化、事件、屏幕适配等外部能力
数据与指标层：行情实体、指标结果、计算引擎
渲染核心层：坐标、可视区、渲染器管理
渲染阶段层：背景/网格/主图/十字线/叠加层等阶段化执行
策略扩展层：现货、合约等模式差异封装
工具叠加层：订单标记、绘图工具、信息浮层
&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;分层的价值不在“看起来规范”，而在于改动隔离：&lt;br&gt;
新增一个能力时，只需要改对应层，不会把整个系统拖下水。&lt;/p&gt;</description></item><item><title>交易所 WebSocket 连接管理踩坑录</title><link>https://saucewu.github.io/posts/%E4%BA%A4%E6%98%93%E6%89%80-websocket-%E8%BF%9E%E6%8E%A5%E7%AE%A1%E7%90%86%E8%B8%A9%E5%9D%91%E5%BD%95/</link><pubDate>Sat, 21 May 2022 10:00:00 +0900</pubDate><guid>https://saucewu.github.io/posts/%E4%BA%A4%E6%98%93%E6%89%80-websocket-%E8%BF%9E%E6%8E%A5%E7%AE%A1%E7%90%86%E8%B8%A9%E5%9D%91%E5%BD%95/</guid><description>&lt;h1 id="交易所-websocket-连接管理踩坑录"&gt;交易所 WebSocket 连接管理踩坑录&lt;/h1&gt;
&lt;p&gt;交易所 App 对实时连接的依赖极高，行情、深度、订单状态全靠 WebSocket 推送，连接一断用户就处于盲区。从基础到深度整理踩过的坑。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一重连与指数退避"&gt;一、重连与指数退避&lt;/h2&gt;
&lt;p&gt;裸写的 &lt;code&gt;WebSocket.connect&lt;/code&gt; 断了就断了，没有自愈能力：&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"&gt;&lt;code class="language-dart" data-lang="dart"&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;Future&lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt;&lt;span style="color:#f92672"&gt;&amp;gt;&lt;/span&gt; connect(&lt;span style="color:#66d9ef"&gt;String&lt;/span&gt; url) &lt;span style="color:#66d9ef"&gt;async&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;while&lt;/span&gt; (&lt;span style="color:#f92672"&gt;!&lt;/span&gt;_disposed &lt;span style="color:#f92672"&gt;&amp;amp;&amp;amp;&lt;/span&gt; _retryCount &lt;span style="color:#f92672"&gt;&amp;lt;&lt;/span&gt; _maxRetries) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;try&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _socket &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; WebSocket.connect(url);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _retryCount &lt;span style="color:#f92672"&gt;=&lt;/span&gt; &lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;await&lt;/span&gt; _socket&lt;span style="color:#f92672"&gt;!&lt;/span&gt;.listen(
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onData: _onMessage,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onDone: () &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; _scheduleReconnect(url),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; onError: (_) &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; _scheduleReconnect(url),
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; cancelOnError: &lt;span style="color:#66d9ef"&gt;true&lt;/span&gt;,
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; ).asFuture();
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; } &lt;span style="color:#66d9ef"&gt;catch&lt;/span&gt; (_) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _scheduleReconnect(url);
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;break&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;&lt;span style="color:#66d9ef"&gt;void&lt;/span&gt; _scheduleReconnect(&lt;span style="color:#66d9ef"&gt;String&lt;/span&gt; url) {
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;if&lt;/span&gt; (_disposed) &lt;span style="color:#66d9ef"&gt;return&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; &lt;span style="color:#66d9ef"&gt;final&lt;/span&gt; delay &lt;span style="color:#f92672"&gt;=&lt;/span&gt; _baseDelay &lt;span style="color:#f92672"&gt;*&lt;/span&gt; (&lt;span style="color:#ae81ff"&gt;1&lt;/span&gt; &lt;span style="color:#f92672"&gt;&amp;lt;&amp;lt;&lt;/span&gt; _retryCount.clamp(&lt;span style="color:#ae81ff"&gt;0&lt;/span&gt;, &lt;span style="color:#ae81ff"&gt;6&lt;/span&gt;)); &lt;span style="color:#75715e"&gt;// 1s → 2s → 4s … → 64s
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; _retryCount&lt;span style="color:#f92672"&gt;++&lt;/span&gt;;
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt; Future.delayed(delay, () &lt;span style="color:#f92672"&gt;=&amp;gt;&lt;/span&gt; connect(url));
&lt;/span&gt;&lt;/span&gt;&lt;span style="display:flex;"&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;指数退避必须加。服务端重启时如果所有客户端同时重连，会瞬间打死服务器。&lt;/p&gt;</description></item><item><title>Clean Architecture 是什么：一篇讲清分层与依赖</title><link>https://saucewu.github.io/posts/clean-architecture-%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%80%E7%AF%87%E8%AE%B2%E6%B8%85%E5%88%86%E5%B1%82%E4%B8%8E%E4%BE%9D%E8%B5%96/</link><pubDate>Sat, 04 Sep 2021 21:20:00 +0900</pubDate><guid>https://saucewu.github.io/posts/clean-architecture-%E6%98%AF%E4%BB%80%E4%B9%88%E4%B8%80%E7%AF%87%E8%AE%B2%E6%B8%85%E5%88%86%E5%B1%82%E4%B8%8E%E4%BE%9D%E8%B5%96/</guid><description>&lt;h1 id="clean-architecture-是什么一篇讲清分层与依赖"&gt;Clean Architecture 是什么：一篇讲清分层与依赖&lt;/h1&gt;
&lt;p&gt;&lt;code&gt;Clean Architecture&lt;/code&gt; 常被说成“分层架构”，但它最关键的不是分层本身，而是：&lt;br&gt;
&lt;strong&gt;依赖方向必须可控。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="一clean-想解决什么问题"&gt;一、Clean 想解决什么问题&lt;/h2&gt;
&lt;p&gt;项目变大后，最容易失控的是依赖关系：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;页面直接调 API&lt;/li&gt;
&lt;li&gt;业务规则写在网络回调里&lt;/li&gt;
&lt;li&gt;换个数据库或 SDK，核心逻辑跟着重写&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Clean 的核心目标是：&lt;br&gt;
&lt;strong&gt;让核心业务不依赖外部框架和实现细节。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id="二经典四层怎么理解"&gt;二、经典四层怎么理解&lt;/h2&gt;
&lt;h3 id="1-presentation表现层"&gt;1) Presentation（表现层）&lt;/h3&gt;
&lt;p&gt;页面、组件、状态管理。&lt;br&gt;
负责“怎么展示”和“怎么交互”。&lt;/p&gt;
&lt;h3 id="2-application应用层"&gt;2) Application（应用层）&lt;/h3&gt;
&lt;p&gt;UseCase（用例）编排流程。&lt;br&gt;
负责“做什么流程”，不负责“怎么请求网络”。&lt;/p&gt;</description></item></channel></rss>