开箱即用的 Flutter 通用脚手架——FlutterKit

0 阅读9分钟

在 AI 辅助开发逐渐成为日常的今天,一套脚手架不仅要减少重复工作,也要让 AI 看懂项目结构、遵循开发规范,并在明确的架构边界内完成开发。

此前已经分享过鸿蒙和安卓版本的快速开发框架,这次继续带来 Flutter 版本。FlutterKit 基于 Flutter + Dart + GetX + TDesign Flutter,内置网络、分页、数据库、状态管理、导航、屏幕适配等常用能力,支持深色模式、国际化和多端适配,欢迎一起学习交流。

项目同时提供项目级 AGENTS.md 和覆盖 UI、数据、导航、主题、预览等场景的专项 Skill,帮助 AI 参与功能开发、代码重构、单元测试和文档维护。

项目亮点

  • 开箱即用:内置网络、分页、数据库、状态管理等基础能力,无需重复搭建
  • 完整示例:每个功能模块都提供可运行的 Demo 页面,方便直接查看效果和代码
  • 质量保障:Core 与 Demo 均配套单元测试,行覆盖率不低于 98%,分支覆盖率不低于 95%
  • 模块化架构:使用 bootstrap、core、routes、feature 划分职责,业务模块之间保持隔离
  • 现代化技术栈:Flutter + Dart + GetX + TDesign Flutter,支持 Android、iOS、Web 和桌面端
  • 屏幕适配:支持手机、折叠屏、平板以及横竖屏布局切换
  • 深色模式:支持浅色、深色和跟随系统,并持久化用户选择
  • 国际化支持:内置中英文语言切换,Core 与 Feature 文案分模块维护
  • 在线文档:框架文档与代码同步维护,方便学习、查阅和二次开发
  • AI 辅助开发:提供项目级 AGENTS.md 和专项 Skill,帮助 AI 工具理解框架边界

如果项目对你有帮助,请给个 Star 支持 ⭐ 这对我来说很重要,能给我带来长期更新维护的动力!

项目地址GitHub | Gitee
在线文档flutter.dusksnow.top
Demo 下载www.pgyer.com/FlutterKit

项目预览

💡 说明:框架提供了多种示例页面,展示网络、分页、数据库、主题、导航、状态管理等能力,支持手机、折叠屏和平板等设备形态。

📱 手机

mobile.png

📱 平板

tablet.png

架构设计

FlutterKit 采用模块化分层架构,从上到下分为业务层、核心层和入口层:

业务层(Feature)

  • 功能模块:按业务域拆分,目前包含认证、用户、主页和 Demo 等模块
  • 页面分层:使用 View + Logic + State + Binding 组织页面代码
  • 模块组件:单模块复用的 Widget、数据和服务保留在 Feature 内
  • 国际化资源:每个 Feature 单独维护文案 Key 和中英文翻译

核心层(Core)

  • base:基础父类与页面状态封装(BaseLogic、BaseNetworkLogic、BaseListLogic、BaseRefreshLogic、BaseTabLogic)
  • data:统一数据入口,保存 Repository 与静态 Preview Data
  • database:基于 sqflite 的本地数据库能力
  • datastore:基于 shared_preferences 的轻量级本地存储
  • design_system:主题、颜色、间距、字体、形状、阴影和 Widget 链式扩展
  • localization:Core 公共文案与翻译聚合
  • model:请求模型、响应模型、分页模型和业务实体
  • network:Dio、Retrofit、拦截器、DataSource 和 Debug 网络面板
  • result:RequestHelper、统一错误格式化和请求结果处理
  • service:用户状态、登录状态和跨页面共享 Service
  • ui:通用 UI 组件、响应式断点和 Widget Preview
  • util:Storage、Route、Toast、Alert、Size、Common 等工具

入口层(Application)

  • 应用入口:负责 Flutter 应用启动与 GetMaterialApp 构建
  • 启动初始化:统一初始化存储、主题、语言、Service 和网络调试工具
  • 路由注册:汇总各模块 GetPage、Binding、参数、结果和路由守卫
  • 多端配置:维护 Android、iOS、Web、macOS、Linux、Windows 原生工程

依赖关系

  • feature 依赖 core:业务模块可以使用 Core 提供的公共能力
  • feature 不直接依赖 network / database:业务模块通过 Repository 访问底层数据
  • routes 负责模块跳转:Feature 之间不直接 import 对方的页面实现
  • core 不依赖 feature:公共能力不能反向引用具体业务模块

architecture.png

技术栈

类别技术选型说明
编程语言DartFlutter 官方开发语言
UI 框架Flutter + TDesign Flutter跨平台 UI 与业务组件
架构模式View + Logic + State + Binding页面分层与依赖注册
状态与导航GetXRx、Obx、GetPage、Binding、Service
网络请求Dio + RetrofitHTTP 客户端、拦截器和接口声明
数据存储sqflite + shared_preferences数据库与轻量级本地存储
屏幕适配flutter_screenutil + BreakpointType尺寸适配与响应式断点

核心能力

1. 网络请求封装

基于 Dio + Retrofit 的网络请求封装,采用 DataSource → Repository → Logic 三层架构,框架统一处理错误、loading、toast、dialog 和页面状态。

功能特性:

  • 统一管理 Dio 实例、baseUrl、timeout 和 headers
  • 使用 Retrofit 声明类型安全的网络 DataSource
  • 自动处理 HTTP 状态码与业务错误码
  • 支持 RequestHelper 链式配置 loading、toast、dialog 和错误回调
  • 支持 BaseNetwork 自动管理加载、错误、空数据和成功状态
  • Debug 模式提供 Alice 网络请求面板和 PrettyDioLogger 日志

使用案例:

/// 请求商品详情。
Future<void> requestGoodsDetail() {
  return RequestHelper.repository<Goods>(
    GoodsRepository().getGoodsInfo(1),
  )
      .loading(true) // 请求过程显示 Loading。
      .toast(true) // 业务失败自动显示 Toast。
      .execute()
      .then<void>((Goods? goods) {
        LogUtil.d('商品详情:${goods?.title}');
      })
      .catchError((Object error) {
        LogUtil.e(error.toString());
      });
}

单次请求场景(自动处理加载、错误、空数据和成功状态):

/// 网络请求示例页 State。
class NetworkDemoState extends BaseNetworkState {
  /// 商品详情。
  final Rx<Goods> goods = Goods().obs;
}

/// 网络请求示例页 Logic。
class NetworkDemoLogic extends BaseNetworkLogic<Goods> {
  /// 页面展示状态。
  final NetworkDemoState networkDemoState = NetworkDemoState();

  /// 商品仓库。
  final GoodsRepository _goodsRepository = GoodsRepository();

  /// 网络父类复用页面声明的 State。
  @override
  NetworkDemoState get networkState => networkDemoState;

  /// 商品详情请求。
  @override
  Future<BaseResponse<Goods>> Function()? get apiRequest =>
      () => _goodsRepository.getGoodsInfo(1);

  /// 保存商品详情。
  @override
  void requestOk(Goods data) => networkDemoState.goods.value = data;
}
/// 网络请求示例页。
class NetworkDemoView extends BaseNetworkView<NetworkDemoLogic> {
  /// 创建网络请求示例页。
  NetworkDemoView({super.key, super.logic});

  /// 构建请求成功后的页面内容。
  @override
  Widget bodyContent(NetworkDemoLogic logic) {
    return Obx(
      () => GoodsDetailContent(
        goods: logic.networkDemoState.goods.value,
      ),
    );
  }
}

BaseNetworkLogic 会调用 apiRequest,请求成功后执行 requestOk()BaseNetworkView 根据当前状态展示 Loading、错误页、空页面或业务内容。

具体实现代码,请参考:网络请求文档 | 结果处理文档

2. 分页列表封装

统一封装分页列表的首次加载、下拉刷新、上拉加载、空态、错误处理和无更多数据,开发者只需关心请求参数与列表项渲染。

功能特性:

  • 自动处理加载中、成功、失败和空数据四种状态
  • 自动处理下拉刷新与上拉加载更多
  • 自动管理 currentPagepageSize 和分页请求参数
  • 自动判断是否还有更多数据
  • 加载更多失败后自动恢复到上一成功页
  • 下一页返回空列表时保留已有内容并标记无更多数据
  • 支持自定义加载、错误、空态和列表项 Widget

Logic 实现:

/// 网络列表示例页 State。
class NetworkListDemoState extends BaseListState<Goods> {
  /// 单页商品数量。
  @override
  int get pageSize => 15;
}

/// 网络列表示例页 Logic。
class NetworkListDemoLogic extends BaseListLogic<Goods> {
  /// 创建网络列表示例页 Logic。
  ///
  /// [goodsRepository] 可选商品仓库,测试和预览可注入 Fake 实现。
  NetworkListDemoLogic({GoodsRepository? goodsRepository})
      : _goodsRepository = goodsRepository ?? GoodsRepository();

  /// 页面分页状态。
  final NetworkListDemoState networkListDemoState = NetworkListDemoState();

  /// 分页父类复用页面声明的 State。
  @override
  NetworkListDemoState get listState => networkListDemoState;

  /// 商品仓库。
  final GoodsRepository _goodsRepository;

  /// 商品分页请求。
  @override
  Future<BaseResponse<BaseListResponse<Goods>>> Function()? get apiRequest {
    return () => _goodsRepository.getGoodsPage(
      GoodsSearchRequest(
        page: listState.currentPage,
        size: listState.pageSize,
      ),
    );
  }
}

页面使用:

/// 网络列表示例页。
class NetworkListDemoView
    extends BaseListView<NetworkListDemoLogic, Goods> {
  /// 创建网络列表示例页。
  NetworkListDemoView({super.key, super.logic});

  /// 构建商品列表项。
  ///
  /// [item] 当前商品。
  /// [index] 当前索引。
  @override
  Widget itemWidget(Goods item, int index) {
    return GoodsListCard(goods: item);
  }
}

使用步骤:

  1. State 继承 BaseListState<T>,按需重写 pageSize
  2. Logic 继承 BaseListLogic<T> 并实现 apiRequest
  3. View 继承 BaseListView<Logic, T> 并实现 itemWidget
  4. 父类统一管理 EasyRefresh、页码、列表状态和无更多数据

具体实现代码,请参考:分页列表文档

3. 状态管理

基于 GetX 的 Rx、Obx 和 GetxService 实现页面状态与全局状态共享。

功能特性:

  • 类型安全的响应式状态定义
  • Obx 自动监听 Rx 变化并更新 Widget
  • 页面状态由 Feature State 管理
  • 跨页面状态使用 GetxService 管理
  • 主题、语言和用户状态支持持久化恢复
  • Binding 统一注册页面 Logic,避免在 View 中创建控制器

状态定义:

/// Demo 计数器服务。
class DemoCounterService extends GetxService {
  /// 当前计数值。
  final RxInt count = 0.obs;

  /// 计数值加一。
  void increase() {
    count.value += 1;
  }
}

使用案例:

/// 构建状态管理示例。
Widget buildCounter() {
  final DemoCounterService service = Get.find<DemoCounterService>();

  return Obx(() {
    return Column(
      children: <Widget>[
        TDText('当前计数:${service.count.value}'),
        TDButton(text: '增加', onTap: service.increase),
      ],
    );
  });
}

页面临时状态放在 State,跨页面状态放在 Service,主题和语言等应用级状态放在 Application。不建议把所有 Rx 都放进全局 Service。

具体实现代码,请参考:状态管理文档

4. 导航管理

统一管理路由注册、页面跳转、参数传递、结果回传和登录拦截。

功能特性:

  • 类型安全的路由参数与返回结果
  • 模块化路由注册(Routes + Pages)
  • 统一模块 Navigator
  • 支持带参跳转与结果回传
  • 支持登录拦截与 GetMiddleware 路由守卫
  • Android 使用平台原生转场并支持预测性返回

路由定义:

/// Demo 模块路由。
abstract final class DemoRoutes {
  static const String navigationWithArgs = '/demo/navigation-with-args';
  static const String navigationResult = '/demo/navigation-result';
}

/// Demo 模块导航参数。
class DemoParams {
  const DemoParams({required this.goodsId});
  final int goodsId;
}

/// Demo 模块导航结果。
class DemoResult {
  const DemoResult({required this.id, required this.message});
  final int id;
  final String message;
}

Navigator 封装(推荐):

/// Demo 模块导航器。
abstract final class DemoNavigator {
  /// 跳转到带参导航示例页。
  static Future<T?>? toNavigationWithArgs<T>(int goodsId) {
    return toPage<T>(
      DemoRoutes.navigationWithArgs,
      arguments: DemoParams(goodsId: goodsId),
    );
  }

  /// 跳转到结果回传示例页。
  static Future<DemoResult?>? toNavigationResult() {
    return toPage<DemoResult>(DemoRoutes.navigationResult);
  }
}

使用案例:

/// 打开带参页面。
Future<void> openGoodsPage() async {
  await DemoNavigator.toNavigationWithArgs<void>(10086);
}

/// 打开结果回传页面。
Future<void> openResultPage() async {
  final DemoResult? result = await DemoNavigator.toNavigationResult();
  if (result != null) {
    ToastUtil.success(result.message);
  }
}
/// 读取当前页面参数。
class NavigationWithArgsLogic extends BaseLogic {
  late final DemoParams params = args as DemoParams;
}

/// 返回页面结果。
void submitResult() {
  back<DemoResult>(const DemoResult(id: 1, message: '操作成功'));
}

应用根节点使用 Transition.native,Android Manifest 显式开启 android:enableOnBackInvokedCallback="true",让 Android 14 及以上页面接入预测性返回。

具体实现代码,请参考:导航管理文档

5. 屏幕适配

完整的屏幕适配方案,支持手机、折叠屏、平板以及横竖屏布局。

功能特性:

  • XS / SM / MD / LG 四档响应式断点
  • BuildContext.bp() 响应式取值
  • BreakpointType.bp() 支持 LayoutBuilder 局部约束
  • flutter_screenutil 提供基础尺寸适配
  • SafeArea 与 MediaQuery 处理刘海、挖孔和手势区域
  • 主页面根据横竖屏切换底部导航和左侧导航
  • Widget Preview 同时检查多种设备尺寸

断点规则:

断点说明宽度范围(dp)
XS超小屏< 320
SM小屏320 - 599
MD中屏600 - 839
LG大屏>= 840

网格布局适配:

/// 构建响应式商品网格。
Widget buildGoodsGrid(BuildContext context, List<Goods> goods) {
  final int columns = context.bp<int>(xs: 2, sm: 2, md: 3, lg: 4);

  return GridView.builder(
    gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
      crossAxisCount: columns,
      crossAxisSpacing: 12,
      mainAxisSpacing: 12,
    ),
    itemCount: goods.length,
    itemBuilder: (BuildContext context, int index) {
      return GoodsListCard(goods: goods[index]);
    },
  );
}

文本适配:

/// 构建响应式文本。
Widget buildAdaptiveText(BuildContext context) {
  final double fontSize = context.bp<double>(
    xs: 14,
    sm: 14,
    md: 16,
    lg: 22,
  );

  return Text(
    '大屏适配示例文字',
    style: TextStyle(fontSize: fontSize),
  );
}

导航栏位置调整:

/// 根据屏幕方向切换导航位置。
Widget buildMainLayout(
  BuildContext context,
  Widget page,
  Widget sideNavigation,
) {
  final bool isLandscape =
      MediaQuery.orientationOf(context) == Orientation.landscape;

  if (isLandscape) {
    return Row(
      children: <Widget>[
        sideNavigation,
        Expanded(child: page),
      ],
    );
  }

  return page;
}

安全区适配:

普通页面顶部通过 BaseView.head() 的 AppBar 避开状态栏;底部操作区使用 SafeArea(top: false) 避开系统手势区域。

/// 构建页面底部操作按钮。
///
/// [onSubmit] 提交回调。
Widget buildBottomAction(VoidCallback onSubmit) {
  return SafeArea(
    top: false,
    child: TDButton(text: '提交', onTap: onSubmit),
  );
}

具体实现代码,请参考:屏幕适配文档 | 安全区文档

6. 数据库封装

基于 sqflite 的本地数据库能力,采用 DataSource → Repository 架构,业务层通过 Repository 访问数据库。

实体定义(简化示例):

/// Demo 表实体。
class DemoEntity {
  const DemoEntity({
    this.id,
    this.title = '',
    this.description,
  });

  final int? id;
  final String title;
  final String? description;

  /// 转换为数据库字段 Map。
  Map<String, dynamic> toMap({bool includeId = true}) {
    return <String, dynamic>{
      if (includeId) 'id': id,
      'title': title,
      'description': description,
    };
  }
}

使用案例:

/// 数据库示例页 Logic。
class DatabaseDemoLogic extends BaseLogic {
  /// Demo 数据仓库。
  final DemoRepository _demoRepository = DemoRepository();

  /// Demo 记录列表。
  final RxList<DemoEntity> items = <DemoEntity>[].obs;

  /// 保存记录。
  Future<void> save() async {
    await _demoRepository.createDemo('示例标题', description: '示例描述');
    items.value = await _demoRepository.getAll();
  }

}

DatabaseProvider 统一管理数据库连接、并发首次打开、建表和关闭。DataSource 负责 SQL、表名和字段映射,Feature 不直接持有 Database

具体实现代码,请参考:数据库文档

7. 本地存储

基于 shared_preferences 的轻量级本地存储,采用 DataSource → Repository 架构,业务层通过 Repository 访问 Token、主题、语言和用户缓存。

使用案例:

/// 本地存储示例页 Logic。
class LocalStorageDemoLogic extends BaseLogic {
  /// 用户信息存储仓库。
  final UserInfoStoreRepository _userInfoStoreRepository =
      UserInfoStoreRepository();

  /// 当前用户信息。
  final Rxn<User> user = Rxn<User>();

  /// 保存用户信息。
  Future<void> saveUser() async {
    const User demoUser = User(
      id: 10086,
      nickName: '演示用户',
      phone: '18800000000',
    );
    await _userInfoStoreRepository.saveUserInfo(demoUser);
    user.value = await _userInfoStoreRepository.getUserInfo();
  }

  /// 清除用户信息。
  Future<void> clearUser() async {
    await _userInfoStoreRepository.clearUserInfo();
    user.value = null;
  }
}

存储 Key、默认值和 JSON 转换都留在 DataSource 实现中。损坏的用户 JSON 会按无缓存处理,不会把解析异常直接抛给页面。

具体实现代码,请参考:本地存储文档

工具类

框架内置了常用工具,避免业务层重复实现:

  • ToastUtil:普通、成功、警告和错误 Toast
  • AlertUtil:确认、反馈和业务提示对话框
  • RouteUtil:页面跳转、返回、参数读取和导航栈处理
  • StorageUtil:SharedPreferences 基础读写
  • CommonUtil:复制、震动、平台判断和通用判断

具体使用方法,请参考:工具类文档

项目结构

FlutterKit/
├── android / ios / web / ...  # 六端原生工程
├── assets/                     # 图片、图标、主题 JSON 和生成源图
├── docs/                       # FlutterKit 与 TDesign 本地文档
├── lib/
│   ├── bootstrap/              # 启动初始化器
│   ├── core/                   # 核心模块
│   │   ├── base/               # 基础父类
│   │   ├── data/               # Repository 与 Preview Data
│   │   ├── database/           # 数据库
│   │   ├── datastore/          # 本地存储
│   │   ├── design_system/      # 设计系统与链式扩展
│   │   ├── model/              # 数据模型
│   │   ├── network/            # 网络层
│   │   ├── result/             # 结果处理
│   │   ├── service/            # 全局 Service
│   │   └── ui / util / ...     # UI、响应式和工具类
│   ├── feature/                # 功能模块
│   │   ├── auth/               # 认证模块
│   │   ├── demo/               # 示例模块
│   │   ├── main/               # 主模块
│   │   └── user/               # 用户模块
│   ├── routes/                 # 路由、参数、结果和守卫
│   ├── application.dart        # 初始化编排与应用级状态
│   └── main.dart               # Flutter 应用入口
├── test/                       # 单元测试与 Widget 测试
└── tool/                       # 覆盖率检查等脚本

相关资源:

如果这个项目对你有帮助,请给个 ⭐ Star 支持!