# lure_pro_app **Repository Path**: 296803441/lure_pro_app ## Basic Information - **Project Name**: lure_pro_app - **Description**: No description available - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-10 - **Last Updated**: 2026-07-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 高级 Flutter BLoC 模板 [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) ![Flutter](https://img.shields.io/badge/Flutter-3.44.0-02569B?logo=flutter) ![Dart](https://img.shields.io/badge/Dart-3.12.0-0175C2?logo=dart) ![Platforms](https://img.shields.io/badge/Platforms-Android%20%7C%20iOS%20%7C%20Web%20%7C%20macOS%20%7C%20Linux%20%7C%20Windows-2E7D32) ![Open Source](https://img.shields.io/badge/Open%20Source-Community%20Template-black) 一个面向生产、社区友好的 Flutter 启动模板,采用 BLoC、Repository 模式、响应式 UI、基于角色的访问控制、国际化和多环境支持。旨在帮助您更快地从原型过渡到可维护的产品,跨移动、Web 和桌面平台。 **有用链接:[Wiki](https://deepwiki.com/cevheri/flutter-bloc-advanced) · [转换日志](docs/clean-modernize-strengthen.md) · [报告问题](https://github.com/cevheri/flutter-bloc-advanced/issues) · [贡献指南](#贡献指南) ## 为什么选择此模板? - **生产导向的架构**:清晰分离表现层、业务逻辑层和数据访问层 - **开箱即用的功能**:认证、基于角色的路由、用户管理、本地化、主题和仪表板流程 - **本地 Mock 模式**:快速开发,支持真实 API 集成的生产模式 - **开源基础项目**:适用于团队、副业项目、内部产品和社区贡献 ## 截图 以下截图帮助贡献者和采用者在克隆或运行项目之前快速了解当前的用户体验。 ### Web 体验 | 深色主题登录 | 浅色主题登录 | | --- | --- | | ![Web login screen in dark theme](docs/screenshots/web-login-dark.png) | ![Web login screen in light theme](docs/screenshots/web-login-light.png) | ![Web user management list screen](docs/screenshots/web-list-user.png) ### 移动端体验 | 登录 | 编辑用户 | | --- | --- | | ![Mobile login screen](docs/screenshots/mobile-login.png) | ![Mobile edit user screen](docs/screenshots/mobile-edit-user.png) | ## 开箱即用的功能 ### 认证 - 用户名和密码登录 - 注册流程 - 忘记密码流程 - OTP 发送和验证流程 - **Token 存储** - JWT 和刷新 Token 通过 `flutter_secure_storage` 持久化 (iOS Keychain、Android 自定义 AES-GCM 加密)。Keychain 可访问性固定为 `first_unlock_this_device`,因此 Token 保留在设备上 (没有 iCloud Keychain 同步)。 非保密的会话字段 (`username`、`roles`) 保留在 `SharedPreferences` 中以便 同步访问。从旧版本升级后的首次启动运行一次性 从遗留的明文密钥迁移到安全存储的迁移。 ### 用户管理 - 创建、更新、删除和列出用户 - 账户资料视图和更新 - 更改密码页面 ### 访问控制 - 管理员和用户角色的基于角色的路由 - 公共和私有路由分离 - 受保护的仅管理员页面 ### 崩溃报告 (Sentry,可选) 模板附带 Sentry 适配器 (`SentryAnalyticsService`) 和保守的 PII / Token 清理器, 但 **没有提交 DSN** - 带有提交的 DSN 的公共模板会将每个分支的事件泄漏到原始项目的配额中。在构建时提供 DSN: ```shell fvm flutter run --target lib/main/main_prod.dart \ --dart-define=SENTRY_DSN=https://YOUR_PUBLIC_KEY@oXXX.ingest.sentry.io/YOUR_PROJECT_ID ``` ```shell fvm flutter build apk --release --target lib/main/main_prod.dart \ --dart-define=SENTRY_DSN=https://... ``` - **没有 DSN,或非生产构建**:`AppDependencies.createAnalyticsService()` 返回 `LogAnalyticsService`。错误会落入 `AppLogger`。没有网络出站。 - **有 DSN + 生产构建**:引导程序调用 `SentryFlutter.init(...)`,分析接口切换到 Sentry 支持的实现。`SentryNavigatorObserver` 自动注册,因此路由更改成为面包屑。 `sentryBeforeSend` (见 `lib/infrastructure/analytics/sentry_scrub.dart`) 在任何事件离开设备之前丢弃以下内容: - `Authorization` / `Cookie` / `Set-Cookie` 标头 (不区分大小写) - 名称包含 `password`、`otp`、`token`、`refreshToken` 的正文键 (不区分大小写的子字符串) - 异常值 + 事件消息中的 JWT 形状字符串 (3 个 base64url 段) - 替换为 `[REDACTED_JWT]` 清理器独立于 SDK 进行单元测试;在生产中采用之前,请根据您分支的 PII 表面进行审查。添加新的清理规则是单个文件的更改。 默认采样率:`tracesSampleRate: 0.2`。如果您的事件预算需要不同的节奏,请在 `AppBootstrap.run` 中调整。 ### 证书固定 (可选) HTTP 客户端支持证书固定,以防御通过用户安装的根 CA、恶意中介和其他形式的设备信任存储妥协的 MITM 攻击。**默认关闭** - 模板附带一个空的固定列表,因为固定错误的证书会使每台安装的应用变砖。 工作原理: - `ProfileConstants.certificatePins` 是 base64 SHA-256 哈希的 `List`。空列表 = 固定禁用 (使用系统信任);非空列表安装自定义的 Dio 适配器,使用 `SecurityContext(withTrustedRoots: false)`,因此 **每个** 证书都通过 `badCertificateCallback` 进行固定检查。这是即使针对系统信任但对抗性的 CA 强制执行固定的唯一正确方法。 - 固定不匹配 → `DioExceptionType.badCertificate`,`ResilienceInterceptor` 已将其视为不可重试。`AppErrorCode.networkCertInvalid` 可用于希望显示类型化错误的存储库层代码路径。 - **Web 平台完全无效**。浏览器控制 TLS;从 JS 我们无法拦截握手。改为在服务器端使用 HSTS / CT 固定。 从您的实时后端提取固定: ```shell openssl s_client -servername api.example.com -connect api.example.com:443 < /dev/null 2>/dev/null \ | openssl x509 -outform DER \ | openssl dgst -sha256 -binary \ | openssl enc -base64 ``` (**v1 注意事项 - 完整证书哈希,而不是 SPKI)。OWASP 推荐的固定是 `SHA-256(SubjectPublicKeyInfo)`,只要重用密钥对时可以在证书轮换时生存。从 X.509 DER 中提取 SPKI 需要 ASN.1 解析;我们提供完整证书哈希以简化和可审计性。升级时存储的固定形状保持相同 - 只有哈希输入更改。在生产中采用之前,在您的分支中跟踪此差距。 密钥轮换过程: 1. 在轮换服务器密钥**之前**,将新的 (备份) 固定添加到 `certificatePins` 中。发布该应用版本。 2. 将服务器证书轮换到新的密钥对。 3. 在以后的版本中,删除旧的固定。 这避免了一个窗口,在该窗口中,使用旧应用版本的飞行中的用户无法访问新证书。 ### 不活动自动注销 `IdleTimeoutObserver` (`lib/core/security/`) 在可配置的指针不活动窗口后将用户注销。默认阈值在生产中为 **15 分钟**,在开发/测试中为 **禁用** (其中热重载 + 模拟会话会使 15 分钟的踢出变得敌意。 - 通过 `ProfileConstants.idleTimeout` 配置。通过编辑 `lib/infrastructure/config/environment.dart` 中的环境映射的 `IDLE_TIMEOUT_SECONDS` 条目 (秒的整数) 为每个环境覆盖。设置为 `null` 以禁用。 - 后台感知:在 iOS 上,当应用挂起时,Dart `Timer` 会暂停,因此观察者还会在生命周期转换时捕获挂钟 `DateTime.now()`;超过阈值的恢复会立即触发。 - 显示:当计时器触发时,用户会看到本地化的提示 ("您因不活动而被注销。"),并且路由器重定向到登录。原因标记为 `SessionExpiredReason.idleTimeout`,因此日志和 UI 可以将其与手动注销区分开来。 - 活动范围:`MaterialApp.router` 子树上的指针按下 / 指针移动事件会重置计时器。仅滚动的会话 (长表单阅读视图) 会延长,因为滚动会导致指针移动事件。 期望不活动超时的合规基准:SOC2 CC6.1、ISO 27001 A.9.4.2、GDPR 第 32 条。 ### 幂等突变 (可选) 当客户端在瞬态故障重试时,`POST` / `PUT` / `PATCH` 重试可能会创建重复记录。模板附带一个可选的 `IdempotencyInterceptor`,它附加一个 `Idempotency-Key: ` 标头,因此配置正确的后端可以去重。 **该标头是一个契约,而不是保证。** 服务器必须配置为按此标头去重 (请参阅 Stripe 的工作示例,IETF 草案 `idempotency-key-header`)。如果服务器忽略它,此功能就是戏剧 - 服务器端仍然会创建重复项。在生产调用站点上选择加入之前,请确认支持。 按调用选择加入: ```dart await ApiClient.post('/admin/users', user, idempotent: true); await ApiClient.put('/admin/users', user, pathParams: '42', idempotent: true); await ApiClient.patch('/admin/users', user, pathParams: '42', idempotent: true); ``` 工作示例:`UserRepository.create` 选择加入 (见 `lib/features/users/data/repositories/user_repository.dart`)。 **重试稳定性**:一旦生成,密钥会存储在 `RequestOptions.extra['_idempotency_key']` 上,并在每次后续传递中重用 - 涵盖 `ResilienceInterceptor` 重试 (瞬态 5xx) 和 `TokenRefreshInterceptor` 在 401 + 刷新后的重新提交。 `GET`、`HEAD`、`OPTIONS`、`DELETE` 永远不会获得标头 (根据 HTTP 语义安全/幂等)。 ### 屏幕捕获保护 (可选) 敏感屏幕的机制 (凭据、OTP、付款、Token) **默认禁用**。模板的工作是提供钩子,而不是对每个分支施加 UX - 许多应用程序合法地希望用户截图二维码、收据等。 - 帮助程序:`ScreenCaptureProtection.enable()` / `.disable()` 在 `lib/core/security/screen_capture_protection.dart` 中。 - 按屏幕混入:`ScreenCaptureProtected` 在 `lib/core/security/screen_capture_protected.dart` 中 - 添加到 `State` 中,它会在 `initState` 时自动启用,在 `dispose` 时释放。 平台行为 (**不对称 - 请阅读此内容**): | 平台 | 发生了什么 | | --- | --- | | Android | `FLAG_SECURE`:截图和屏幕录制被阻止;最近应用预览呈现为黑色。 | | iOS | iOS 不提供阻止截图的 API (Apple 政策)。任务切换器快照是**模糊**的,因此屏幕上的秘密不会泄漏到应用预览中。前台时的截图仍然成功。 | | Web / 桌面 | 无操作。 | 选择加入示例 (在 `LoginScreen` 中取消注释): ```dart import 'package:flutter_bloc_advance/core/security/screen_capture_protected.dart'; class _LoginScreenState extends State with ScreenCaptureProtected, SingleTickerProviderStateMixin { // ... } ``` 何时启用:凭据输入、OTP 屏幕、Token 显示。 何时不启用:用户应该捕获内容的屏幕 (二维码、收据)。 ### 服务器驱动的动态表单 - 16 种受支持的字段类型:文本、电子邮件、密码、数字、电话、文本区域、 下拉、多选、日期、日期时间、切换、复选框、单选、滑块、 节标题、分隔符 - 仅架构加载 (`/dynamic-forms/sample` - CRM 潜在客户演示) 或捆绑 `{schema, values}` 加载,带有每个实例的 `pathParams` (`/user/:id/extended-info` - 用户扩展信息厨房水槽,涵盖所有 16 种类型) - 通过 `DynamicFormsFeatureRoutes.withBloc(context, child)` 从任何功能可重用 - 引擎位于 `shared/dynamic_forms/` 下,与功能无关 ### UI 和开发者体验 - 深色和浅色主题 - 响应式布局支持 - 英语和土耳其语本地化 - 设计系统基础,包含可重用组件 - 多平台支持 Android、iOS、Web、macOS、Linux 和 Windows ### 架构 - 用于状态管理的 BLoC - 功能优先的干净架构,带有领域层 (实体 + 存储库接口) - 用于业务逻辑隔离的用例 - 带有 `Result` 密封类型的存储库模式 (没有原始异常) - 类型化错误层次结构 (`NetworkError`、`AuthError`、`ValidationError`、`ServerError` 等) - 带有拦截器链的 Dio HTTP 客户端 (认证、日志、模拟) - 具有干净边界的基于功能的路由 - 模型的手动 JSON 序列化 - 用于本地和生产模式的环境驱动配置 - 强制依赖规则的架构保护测试 ![高级架构图](docs/high-level-architecture.svg) ## 快速开始 ### 先决条件 - Flutter `3.44.0` 和 Dart `3.12.0` - [FVM](https://fvm.app/documentation/getting-started/installation) 推荐用于版本一致性 - 用于 Android 构建的 Android SDK - 用于 iOS 和 macOS 构建的 Xcode ### 安装 FVM ```shell # macOS / Linux brew tap leoafarias/fvm brew install fvm # Windows choco install fvm ``` ### 设置 ```shell git clone https://github.com/cevheri/flutter-bloc-advanced.git cd flutter-bloc-advanced fvm install 3.44.0 fvm use 3.44.0 fvm flutter pub get ``` ### 使用 Mock 数据本地运行 所有本地请求都使用 `assets/mock/`,因此您可以先探索应用程序而无需先设置后端。 ```shell # 移动端 fvm flutter run --target lib/main/main_local.dart # Web fvm flutter run -d chrome --target lib/main/main_local.dart # 带有特定端口的 Web fvm flutter run -d chrome --web-port 3000 --target lib/main/main_local.dart ``` ### 演示凭据 | 角色 | 用户名 | 密码 | 访问权限 | | --- | --- | --- | --- | | 管理员 | `admin` | `admin` | 所有页面 | | 用户 | `user` | `user` | 自己的个人资料和设置 | ### 针对真实 API 运行 ```shell # 移动端 fvm flutter run --target lib/main/main_prod.dart # Web fvm flutter run -d chrome --target lib/main/main_prod.dart ``` 生产环境配置在 `lib/infrastructure/config/environment.dart` 中。 ## 技术栈 | 类别 | 技术 | | --- | --- | | Flutter | 3.44.0 | | Dart | 3.12.0 | | 状态管理 | flutter_bloc 9.1.1、bloc_concurrency 0.3.0、stream_transform 2.1.1 | | 路由 | go_router 17.2.3 | | HTTP | dio 5.9.2 (拦截器链:连接性、认证、Token 刷新、弹性、模拟、缓存、开发控制台、日志) | | 表单 | flutter_form_builder 10.3.0+2 | | 本地化 | intl 0.20.2、intl_utils 2.8.14 | | 存储 | shared_preferences 2.5.5、flutter_secure_storage 10.2.0 | | 图表 | fl_chart 1.2.0 | | 测试 | flutter_test、bloc_test、mocktail、test 1.31.0 | ## 项目结构 ```text lib/ app/ # 应用基础架构 di/ # 依赖注入 (手动,没有 get_it) router/ # 集中式路由,使用 go_router shell/ # 响应式外壳 (侧边栏、顶部栏、底部导航、命令面板) theme/ # 主题管理 core/ # 零依赖基础 errors/ # 应用错误密封层次结构 + API 异常 result/ # Result 密封类型 (成功/失败) logging/ # 结构化日志 (AppLogger) security/ # JWT 工具 features/ # 基于功能的干净架构模块 / application/ # BLoC、用例 data/ # 模型、存储库 (具体实现) domain/ # 实体、存储库接口 navigation/ # 功能特定的路由 presentation/ # 页面和功能特定的 Widget infrastructure/ # 跨领域技术关注点 config/ # 环境、模板配置 http/ # Dio ApiClient + 拦截器 (认证、日志、模拟) storage/ # 本地存储 (SharedPreferences) shared/ # 功能之间的共享基础 design_system/ # 主题、排版、颜色令牌、16 个组件 dynamic_forms/ # 服务器驱动的表单引擎 (16 种字段类型、捆绑加载、公共 withBloc 辅助) models/ # 跨功能实体 (UserEntity、PagedResult) widgets/ # 可重用组件 (按钮、表单、对话框) generated/ # 本地化生成文件 (不要编辑) l10n/ # ARB 翻译文件 main/ # 入口点 (main_local.dart、main_prod.dart) test/ # 镜像 lib/ 结构 architecture/ # 导入保护测试 (依赖规则强制) features/ # 功能测试 (应用、数据、展示) mocks/ # Mock 类和假数据 ``` ## 自定义您的项目 所有模板占位符使用 `__KEYWORD__` 命名。在项目中对 `__` 进行**全局搜索**,将每个关键字替换为您自己的值,然后您就完成了。 | 关键字 | 描述 | 示例 | 使用位置 | | --- | --- | --- | --- | | `__APP_NAME__` | 完整应用名称 | `Acme Dashboard` | template_config.dart、manifest.json | | `__APP_SHORT_NAME__` | 短名称 (PWA、移动) | `Acme` | template_config.dart、manifest.json | | `__APP_DESCRIPTION__` | 单行应用描述 | `Team collaboration platform` | template_config.dart、manifest.json | | `__PROD_API_URL__` | 生产 API 端点 | `https://api.acme.com/v1` | template_config.dart、index.html | | `__WEB_BASE_URL__` | 部署 URL (尾随 `/`) | `https://app.acme.com/` | template_config.dart、index.html、sitemap.xml | | `__GITHUB_REPO_URL__` | GitHub 仓库 URL | `https://github.com/acme/dashboard` | template_config.dart | | `__AUTHOR_NAME__` | 开发者或公司名称 | `Acme Inc.` | template_config.dart、index.html、humans.txt | | `__AUTHOR_EMAIL__` | 联系电子邮件 | `dev@acme.com` | template_config.dart、CONTRIBUTING.md | | `__AUTHOR_URL__` | 作者网站 / 个人资料 | `https://github.com/acme` | template_config.dart、index.html、humans.txt | | `__AUTHOR_LOCATION__` | 作者位置 | `San Francisco, CA` | humans.txt | 还需要手动更新: 1. **`pubspec.yaml`** - 包名称、版本、描述。 2. **`web/index.html`** - 页面 ``、元描述、Open Graph / Twitter Card 文本。 3. **`lib/l10n/intl_en.arb` / `intl_tr.arb`** - 应用特定的翻译。 中央配置文件是 `lib/infrastructure/config/template_config.dart` - 整个应用中的 Dart 代码从此类读取。 ## 构建、测试和质量 ### 构建 ```shell # Android APK fvm flutter build apk --target lib/main/main_prod.dart # iOS fvm flutter build ios --target lib/main/main_prod.dart # Web fvm flutter build web --target lib/main/main_prod.dart ``` ### 测试 ```shell # 运行所有测试 fvm flutter test # 带覆盖率 fvm flutter test --coverage # 对调试顺序相关的问题很有用 fvm flutter test --concurrency=1 --test-randomize-ordering-seed=random # 仅运行 Widget 测试 / 除 Widget 测试外的所有内容 (快速单元循环) fvm flutter test --tags widget fvm flutter test --exclude-tags widget ``` 标签在 `dart_test.yaml` 中声明 (`widget`、`golden`;`integration` 保留给未来可能的设备上套件)。端到端冒烟 (`test/integration/app_smoke_test.dart`) 在模拟模式下启动整个应用并驱动登录 → 仪表板;它作为正常 `flutter test` 的一部分无头运行。 ```shell # Golden / 视觉回归测试 (alchemist,CI/Ahem 渲染) fvm flutter test --tags golden # 运行 goldens fvm flutter test --tags golden --update-goldens # 在有意的 UI 更改后重新生成 ``` Golden 测试位于它们覆盖的代码旁边 (功能优先,镜像 `lib/`;图像在同级 `goldens/ci/` 目录中),并使用 alchemist 的 CI (Ahem 渲染) 变体,因此它们在 macOS/Linux/CI 之间相同。在有意的视觉更改后,使用 `--update-goldens` 重新生成。 有关测试结构、全局引导以及如何为每一层编写测试,请参阅 [`docs/testing-architecture.md`](docs/testing-architecture.md)。 ### 分析和格式化 ```shell fvm dart analyze fvm dart fix --apply fvm dart format . --line-length=120 ``` ### Git 钩子 (可选) 安装预提交 (格式化 + 分析) 和预推送 (测试) 钩子: ```shell bash scripts/setup_hooks.sh ``` ### 测试覆盖率重点 | 层 | 测试内容 | | --- | --- | | 用例 | 业务逻辑委托、输入路由 | | 模型 / 实体 | `fromJson`、`toJson`、相等性、`copyWith` | | 映射器 | 实体 ↔ 模型转换 | | 存储库 | API 调用、Result 类型返回、错误映射 | | BLoC | 状态转换、事件处理、错误状态 | | 屏幕 | Widget 渲染、用户交互、导航 | | 架构 | 强制依赖规则的导入保护测试 | ## CI/CD 此仓库中包含的 GitHub Actions 工作流: - 用于构建和测试自动化的 `build_and_test.yml` - 用于 Web 构建的 `build-web.yml` - 用于 SonarQube 分析的 `sonar_scanner.yml` 要启用 SonarQube,将 `SONAR_TOKEN` 秘密添加到您的仓库或组织中。 ## Android 工具 | 组件 | 版本 | | --- | --- | | Gradle | 8.14 | | Android Gradle 插件 | 8.11.1 | | Kotlin | 2.2.20 | | Java 兼容性 | 17 | | NDK | 动态 (`flutter.ndkVersion`) | | 构建配置 | Kotlin DSL (`.gradle.kts`) | ## 添加新功能 1. 在 `lib/features/<feature>/` 下创建新的功能文件夹。 2. 在 `domain/entities/` 中定义领域实体,在 `domain/repositories/` 中定义存储库接口。 3. 在 `data/models/` 中实现数据模型,在 `data/repositories/` 中实现存储库 (返回 `Result<T>`)。 4. 在 `application/usecases/` 中添加用例 (每个操作一个类)。 5. 在 `application/` 中使用 `switch (result) { case Success: ... case Failure: ... }` 创建 BLoC(s)。 6. 在 `presentation/pages/` 中实现 UI。 7. 在 `navigation/` 中定义功能路由。 8. 在 `lib/app/router/app_router.dart` 中注册功能路由。 9. 在 `lib/app/di/app_dependencies.dart` (存储库) 和 `app_scope.dart` (BLoC) 中注册 DI。 10. 在 `test/features/<feature>/` 中添加测试,镜像功能结构。 **依赖规则** (由架构保护测试强制执行): - `core/` 不导入项目的任何其他内容 - `shared/` 仅从 `core/` 导入 - `features/` 从 `shared/`、`infrastructure/`、`core/` 导入 - 永远不从其他功能导入 - `app/` 可以从所有层导入 ## 贡献指南 欢迎社区贡献。如果您想改进模板、添加功能、完善文档或优化 UI,请随时提出问题或提交拉取请求。 1. 分叉仓库。 2. 从您的分支创建功能分支。 3. 在相关时,使用测试或文档更新进行更改。 4. 运行 `fvm dart analyze` 和 `fvm flutter test`。 5. 打开拉取请求,带有更改的清晰摘要。 如果您不确定从哪里开始,文档改进、截图刷新和测试覆盖增强都是有价值的贡献。 ## 文档 | 文档 | 描述 | | --- | --- | | [转换日志](docs/clean-modernize-strengthen.md) | 发生了什么变化、为什么、前后比较 | | [架构迁移](docs/architecture-migration_en.md) | 功能优先迁移指南 | | [功能优先边界](docs/feature-first-clean-boundaries.md) | 干净架构设计文档 | | [升级指南 - Flutter 3.44.0](docs/upgrade_flutter_3.44.0.md) | Flutter 3.41.8 → 3.44.0 升级说明 (v0.21.0) | | [升级指南 - Flutter 3.41.4](docs/upgrade_flutter_3.41.4.md) | 历史:Flutter 3.41.4 升级说明 (v0.18.0) | ## 参考 [![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/cevheri/flutter-bloc-advanced) - [理解 Flutter BLoC:综合指南](https://cevheri.medium.com/understanding-flutter-bloc-a-comprehensive-guide-7100dabe3975) - [Flutter 文档](https://flutter.dev/) - [BLoC 库](https://bloclibrary.dev/) - [pub.dev 上的 flutter_bloc](https://pub.dev/packages/flutter_bloc) - [pub.dev 上的 go_router](https://pub.dev/packages/go_router) - [pub.dev 上的 dio](https://pub.dev/packages/dio) ## 许可证 此项目根据 MIT 许可证获得许可。有关详细信息,请参阅 [LICENSE](LICENSE) 文件。