Flutter App 全套实战学习计划:从零搭建到 APK 部署

本文以仓库中的 flutter_demo_app为案例,按照真实交付顺序拆成 7 个阶段。每个阶段都有目标、操作、代码骨架、验收标准和常见问题。建议按顺序完成,每阶段都保留一个可运行版本。

1.先认识本项目

当前代码已经具备一个可运行的 UI 原型:

lib/

main.dart # 应用入口和主题

config/ # 颜色、尺寸、文字规范

pages/login/ # 登录页

pages/home/ # 首页、记录、我的

widgets/ # 通用输入框、按钮、卡片

这是"页面优先"的起点。学习过程中逐步补上 app/data/domain/services/,不要一次性重写全部页面。

2.项目搭建:环境配置与软件安装

2.1 推荐环境
  • Flutter stable(以 flutter --version 输出为准)
  • Dart SDK 由 Flutter 自带,需满足 pubspec.yaml 的 SDK 约束
  • Android Studio:Android SDK、Platform Tools、模拟器、JDK
  • VS Code + Flutter/Dart 插件(或 Android Studio 插件)
  • Git、Chrome(调试 Web 可选)

安装后依次执行:

flutter doctor -v

flutter devices

flutter create flutter_demo_app

cd flutter_demo_app

flutter pub get

flutter run

flutter doctor中带!的项先解决;Android 许可可用flutter doctor --android-licenses` 接受。团队应固定 Flutter 版本(FVM、CI 镜像或文档记录),避免不同版本生成不同锁文件。

2.2 初始化与提交纪律

git init

git add .

git commit -m "chore: bootstrap flutter app"

确认 .gitignore 已忽略 .dart_tool/build/、密钥和本地配置。不要提交 android/key.properties、Keystore 或生产 API 密钥。

阶段验收:新机器能执行 flutter pub get && flutter test,模拟器能看到登录页。

3.工程化与架构熟悉

3.1 从页面目录演进为分层目录

推荐结构:

lib/

app/ # App、路由、主题、环境

config/ # 颜色、尺寸、常量

core/ # 网络、异常、存储、日志

data/

models/ # JSON 模型

datasources/ # API/本地数据源

repositories/ # 仓储实现

domain/ # 业务接口和用例(可选)

pages/ # 页面和页面状态

widgets/ # 跨页面通用组件

页面只负责展示和用户事件;Repository 决定从网络还是缓存取数据;Model 负责 JSON 转换。这样在线、离线和测试都能替换数据源。

3.2 依赖与质量门禁

按需要添加依赖(版本以项目当前稳定版为准):

dependencies:

dio: ^5.0.0 # HTTP、拦截器、超时

go_router: ^14.0.0 # 声明式路由和鉴权重定向

shared_preferences: ^2.0.0 # 轻量配置

flutter_riverpod: ^2.0.0 # 可测试的状态管理

json_annotation: ^4.0.0

dev_dependencies:

build_runner: ^2.0.0

json_serializable: ^6.0.0

不要为了"完整架构"盲目引入包:只有当登录状态、列表分页或缓存开始复杂时再引入状态管理和生成器。

每次提交前执行:

dart format lib test

flutter analyze

flutter test

4.全局配置与封装

4.1 主题、颜色、间距

仓库已有 ColorConfigSizeConfigTextConfig,统一在 ThemeData 汇总,页面不直接散落颜色值:

javascript 复制代码
ThemeData buildTheme() => ThemeData(
  useMaterial3: true,
  colorScheme: ColorScheme.fromSeed(seedColor: ColorConfig.primary),
  scaffoldBackgroundColor: ColorConfig.page,
  inputDecorationTheme: const InputDecorationTheme(isDense: true),
);

常量建议集中为 AppConstants(接口超时、分页大小、缓存 key、产品名),环境差异放 AppConfig

javascript 复制代码
class AppConfig {
  static const environment = String.fromEnvironment('APP_ENV', defaultValue: 'dev');
  static const apiBaseUrl = String.fromEnvironment('API_BASE_URL', defaultValue: 'https://api.example.com');
}

运行时注入:flutter run --dart-define=APP_ENV=dev --dart-define=API_BASE_URL=https://dev-api.example.com

4.2 网络、异常和存储封装

Dio统一配置 baseUrl、超时、token 拦截器和错误映射;Repository 不直接操作Dio。本地存储只保存 token、记住的账号和离线队列,敏感 token 生产环境改用 flutter_secure_storage。

javascript 复制代码
class ApiClient {
  ApiClient(String baseUrl) : dio = Dio(BaseOptions(
    baseUrl: baseUrl,
    connectTimeout: const Duration(seconds: 10),
    receiveTimeout: const Duration(seconds: 15),
  ));
  final Dio dio;
}

所有错误转成可显示的业务错误(网络不可用、登录失败、服务端错误),页面只处理结果,不解析 HTTP 状态码。

阶段验收:修改主题色只改一处;开发/测试 API 可通过 --dart-define 切换;网络异常能显示明确提示。

5. 页面交互开发:登录、首页、表单、路由、弹窗和状态

5.1 路由与登录态

go_router 集中定义 /login/home/records,并用 refreshListenable 或 provider 监听 token。未登录访问业务页重定向到登录页,退出登录清除 token 后回到 /login。不要在每个按钮里手写嵌套 MaterialPageRoute

5.2 登录表单

将当前 TextField 升级为 Form + GlobalKey<FormState>:账号必填且长度合法,密码至少 6 位;提交时先 validate(),再将按钮置为 loading,防止重复点击。密码框增加显示/隐藏图标,错误使用 InputDecoration.errorText,成功后保存 token 和记住账号。

javascript 复制代码
final formKey = GlobalKey<FormState>();
if (!(formKey.currentState?.validate() ?? false)) return;
setState(() => loading = true);
try {
  final session = await authRepository.login(account.text.trim(), password.text);
  if (!mounted) return;
  ref.read(sessionProvider.notifier).setSession(session);
} on AppException catch (e) {
  if (mounted) showAppError(context, e.message);
} finally {
  if (mounted) setState(() => loading = false);
}
5.3 首页状态与交互

首页拆成"用户信息、实验室选择、快捷操作、记录列表、我的"组件。列表必须具备 loading、空数据、错误、下拉刷新和分页状态;实验室选择用单一状态保存当前 id,而不是只弹 Toast。删除、退出登录等破坏性操作使用 showDialog 二次确认;轻量反馈使用 SnackBar

5.4 响应式和无障碍

LayoutBuilder/MediaQuery 适配手机和大屏;避免固定高度包裹可变文本。按钮提供语义化文本,颜色不能作为唯一状态提示,输入控件设置键盘类型和 textInputAction

阶段验收:空密码不能提交;提交期间按钮不可重复点击;登录成功可返回首页,退出后不能用返回键回到业务页;首页可刷新并正确显示空/错/加载状态。

6. 数据交互:前后端接口与在线/离线模式

6.1 定义模型和接口

LoginRequestSessionInspectionRecord 为例,使用 json_serializable 生成 fromJson/toJson。Repository 接口表达业务意图:loginlistRecordssaveInspection,而不是暴露 Dio

javascript 复制代码
abstract interface class RecordRepository {
  Future<List<InspectionRecord>> list({int page = 1});
  Future<void> save(InspectionRecord record);
}

为每个接口写请求参数、响应示例、鉴权要求、错误码和超时策略;后端字段变更时只改 model/data 层。

6.2 在线优先与离线队列

读取采用 cache-first 或 network-first 策略并明确 UI 标识"离线数据";写入在线直接提交,网络失败则写入本地队列,带 pending/syncing/synced/failed 状态、重试次数和幂等 requestId。应用启动和网络恢复时执行同步,冲突按服务端版本号或最后修改时间处理,不能静默覆盖。

javascript 复制代码
Future<List<InspectionRecord>> listRecords() async {
  try {
    final remote = await api.listRecords();
    await cache.replace(remote);
    return remote;
  } on DioException {
    return cache.read();
  }
}

测试重点:断网仍能查看最近缓存;重复点击不会产生两条记录;同步失败可重试且用户能看到原因;token 过期会清队列或要求重新登录(按业务策略)。

7. 打包部署与性能优化

7.1 Android 签名

生成并保护 Keystore(路径示例仅用于本机):

keytool -genkeypair -v -keystore ~/upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload

android/key.properties 配置(该文件已经加入 .gitignore):

storePassword=替换为实际密码

keyPassword=替换为实际密码

keyAlias=upload

storeFile=/绝对路径/upload-keystore.jks

android/app/build.gradle.kts 顶部读取配置,并让 release 构建引用签名:

javascript 复制代码
import java.util.Properties

val keystoreProperties = Properties().apply {
    val file = rootProject.file("key.properties")
    if (file.exists()) file.inputStream().use(::load)
}

android {
    signingConfigs {
        create("release") {
            keyAlias = keystoreProperties["keyAlias"] as String
            keyPassword = keystoreProperties["keyPassword"] as String
            storeFile = file(keystoreProperties["storeFile"] as String)
            storePassword = keystoreProperties["storePassword"] as String
        }
    }
    buildTypes {
        release { signingConfig = signingConfigs.getByName("release") }
    }
}

CI 使用 Secret 在构建时生成配置,绝不把密码或 Keystore 提交到仓库。正式发布前使用 apksigner verify --verbose app-release.apk 检查签名,并把 Keystore 加密备份;丢失上传密钥会显著增加后续发版成本。

7.2 版本、构建和产物

pubspec.yamlversion: 1.2.0+15 分别对应 versionName/versionCode。构建命令:

flutter clean && flutter pub get

flutter build apk --release --split-per-abi --build-name=1.2.0 --build-number=15

flutter build appbundle --release

优先发布 AAB 到 Google Play;内部分发可用 APK。构建前检查图标、包名、隐私声明、网络安全配置和崩溃上报。

7.3 性能清单

  • constListView.builder,避免大列表一次性 List.generate
  • 图片指定尺寸并缓存,避免在 build 中做 JSON/排序/网络请求。
  • 用 Flutter DevTools 的 Performance、Memory、Network 面板检查首帧、卡顿和泄漏。
  • release 包关闭 debug 日志;按 ABI 拆包并压缩资源。
  • 以真实低端设备测量 60fps、启动耗时、内存峰值,而不是只看模拟器。

阶段验收:release APK 可安装、签名校验通过、版本号正确;关键页面滚动无明显掉帧;线上 API 地址由构建参数注入。

8. 常用命令速查

flutter channel stable && flutter upgrade

切换Flutter到stable稳定分支,并升级SDK到当前分支最新版本

flutter doctor -v

Flutter环境全面体检,-v输出详细信息,排查SDK、Android、Xcode等环境问题

flutter pub get

根据pubspec.yaml下载/同步项目全部第三方依赖包

flutter pub add dio

快速添加第三方依赖库dio(网络请求库),自动写入配置并执行

pub get flutter pub outdated

检测项目所有依赖,查看哪些包存在可升级新版本

dart format .

格式化当前项目全部Dart代码,统一官方编码风格

flutter analyze

静态代码检查,不运行程序,扫描代码警告、潜在错误、语法问题

flutter test

执行test目录下全部单元测试用例,无需真机/模拟器

flutter test --coverage

执行单元测试,同时生成代码测试覆盖率报告

flutter run -d <device>

指定设备编译并运行项目,<device>填写设备名称/序列号,支持热重载调试

flutter logs

查看连接设备上App运行日志(print打印输出)

flutter clean

清理编译缓存,删除build、.dart_tool等产物,解决编译异常时使用

flutter build apk --release

构建Android正式发布版APK安装包,产物可直接给安卓手机安装

flutter build appbundle --release

构建AAB包,用于应用商店上架,不能直接手机安装

adb devices

adb命令:列出所有已连接的安卓真机/模拟器设备

adb install -r build/app/outputs/flutter-apk/app-release.apk

adb命令:覆盖安装release版本apk到设备,-r保留应用原有数据

遇到"依赖/生成文件异常"先 flutter clean && flutter pub get;遇到布局问题用 DevTools Inspector;遇到 Android 构建问题先核对 JDK、Gradle、compileSdk 与 Flutter 版本矩阵。

9.推荐学习节奏与交付物

按 3 周、每天 1.5~2 小时推进:

  1. 第 1~2 天:环境、Git、Flutter/Dart 基础,交付可运行空壳。
  2. 第 3~5 天:目录、主题、通用组件、静态登录/首页,交付 UI 原型。
  3. 第 6~9 天:路由、表单校验、状态管理、弹窗和测试,交付可操作 Demo。
  4. 第 10~13 天:API、模型、Repository、错误处理,交付在线版。
  5. 第 14~16 天:缓存、离线队列、同步和断网测试,交付离线可用版。
  6. 第 17~19 天:签名、CI 构建、DevTools 性能优化,交付 release APK/AAB。
  7. 第 20~21 天:补文档、回归测试、版本发布清单,完成一次模拟上线。

最终验收应包括:新环境搭建文档、架构图、接口文档、测试报告、签名构建说明、APK/AAB 产物和回滚方案。完成这套闭环后,再学习动画、国际化、推送、后台任务等专项能力,收益会更高。

相关推荐
会编程的吕洞宾3 小时前
DeepAgents In Action学习(Second)
android·java·学习
我爱cope3 小时前
【计算机网络 | 数据链路层7:VLAN:一台交换机如何划分多个逻辑局域网?】
网络·学习·计算机网络
GitLqr4 小时前
Flutter 实战:图片上传前的压缩优化,解决带宽与存储的“大户”问题
flutter·全栈·dart
babe小鑫4 小时前
2026信管专业学习数据分析的价值
学习·数据挖掘·数据分析
编程圈子4 小时前
电机驱动开发学习25. 弱磁控制与全速域增益调度
驱动开发·学习
Be for thing5 小时前
【嵌入式成长3】STC89C51外部中断|中断原理、寄存器、中断服务函数
单片机·嵌入式硬件·学习
优化Henry6 小时前
学习笔记之爱立信站点指向证书执行
运维·服务器·网络·笔记·学习·信息与通信
IT古董7 小时前
【WMS学习笔记系列】03-功能模块设计
笔记·学习·系统架构
进哥AI研习社8 小时前
AtomCode 跨端开发实战:Flutter 项目从原型到多端运行的 AI 驱动体验
人工智能·flutter