前言

本文档详细介绍了一个完整的 Flutter + Spring Boot 全栈项目,包含跨平台移动应用和后端服务的完整技术实现。

项目组成:

  • 前端: Flutter 3.x 跨平台应用(iOS/Android/Web)
  • 后端: Spring Boot 3.1.5 + Java 17 RESTful API
  • 数据库: MySQL 8.0+
  • 认证: JWT Token 认证

技术栈对比

为什么选择 Flutter?

核心优势:

  • 一套代码,多端运行 - iOS、Android、Web 全覆盖
  • 高性能渲染 - Skia 引擎,直接绘制到 Canvas
  • 热重载开发 - 修改代码立即生效,开发效率极高
  • 丰富的组件库 - Material Design 和 Cupertino 风格
  • Dart 语言 - JIT(开发)+ AOT(生产)双模式编译

与原生对比:
| 特性 | Flutter | 原生 (Swift/Kotlin) |
|——|———|——————-|
| 开发效率 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
| 性能 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 跨平台 | ⭐⭐⭐⭐⭐ | ⭐ |
| 热重载 | ✅ | ❌ |
| 社区生态 | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |

为什么选择 Spring Boot?

核心优势:

  • 约定优于配置 - 零配置启动
  • 内嵌服务器 - Tomcat/Jetty 直接打包
  • 自动配置 - 根据依赖自动装配
  • 生产就绪 - 监控、指标、健康检查
  • 强大生态 - Spring 全家桶支持

项目架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
┌──────────────────────┐
│ Flutter App │
│ (跨平台前端) │
- iOS/Android/Web
- Dart 语言 │
- Provider 状态管理 │
└──────────┬───────────┘
│ HTTP/REST API
│ JSON 数据

┌──────────────────────┐
│ Java Service │
│ (Spring Boot 后端) │
- RESTful API │
- JWT 认证 │
- JPA 数据访问 │
└──────────┬───────────┘
│ JDBC

┌──────────────────────┐
│ MySQL 数据库 │
- users 表 │
- products 表 │
- orders 表 │
└──────────────────────┘

Flutter 前端详解

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
ios-flutter-project/
├── pubspec.yaml # Flutter 配置
├── lib/ # 源代码
│ ├── main.dart # 应用入口
│ ├── models/ # 数据模型
│ │ ├── user.dart
│ │ ├── product.dart
│ │ ├── order.dart
│ │ └── cart_item.dart
│ ├── screens/ # 页面组件
│ │ ├── main_shell.dart # 主框架
│ │ ├── login_page.dart # 登录
│ │ ├── product_list_page.dart # 商品列表
│ │ ├── cart_page.dart # 购物车
│ │ └── ...
│ ├── services/ # 服务层
│ └── providers/ # 状态管理
└── test/ # 测试

核心数据模型

User 模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class User {
final int? id;
final String name;
final String email;
final String? phone;
final DateTime? createdAt;

User({
this.id,
required this.name,
required this.email,
this.phone,
this.createdAt,
});

factory User.fromJson(Map<String, dynamic> json) {
return User(
id: json['id'],
name: json['name'] ?? '',
email: json['email'] ?? '',
phone: json['phone'],
createdAt: json['created_at'] != null
? DateTime.parse(json['created_at'])
: null,
);
}

Map<String, dynamic> toJson() {
return {
'id': id,
'name': name,
'email': email,
'phone': phone,
'created_at': createdAt?.toIso8601String(),
};
}
}

状态管理 (Provider)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
// 定义 CartProvider
class CartProvider with ChangeNotifier {
final List<CartItem> _items = [];

List<CartItem> get items => _items;

double get totalAmount {
return _items.fold(0.0, (sum, item) => sum + item.totalPrice);
}

void addItem(CartItem item) {
_items.add(item);
notifyListeners(); // 通知 UI 重建
}

void removeItem(int productId) {
_items.removeWhere((item) => item.product.id == productId);
notifyListeners();
}
}

// 在 main.dart 中注册
void main() {
runApp(
MultiProvider(
providers: [
ChangeNotifierProvider(create: (_) => CartProvider()),
],
child: const MyApp(),
),
);
}

// 在 Widget 中使用
class CartPage extends StatelessWidget {
@override
Widget build(BuildContext context) {
final cart = context.watch<CartProvider>();

return Text('总价:¥${cart.totalAmount}');
}
}

Widget 树结构

1
2
3
4
5
6
7
8
9
10
11
12
13
MaterialApp
└── MainShell (StatefulWidget)
├── NavigationBar (底部导航栏)
│ ├── 首页
│ ├── 商品
│ ├── 订单
│ └── 我的
└── Scaffold
├── AppBar (顶部栏)
├── Body (内容区)
│ └── ListView.builder
│ └── ListTile
└── BottomNavigationBar

热重载开发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 启动应用
flutter run

# 开发时按以下按键:
# r - 热重载 (Hot Reload)
# R - 热重启 (Hot Restart)
# q - 退出
# h - 帮助

# 查看支持的设备
flutter devices

# 指定设备运行
flutter run -d <device_id>

Spring Boot 后端详解

项目结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
java-service-project/
├── pom.xml # Maven 配置
├── src/main/java/com/example/crud/
│ ├── CrudApplication.java # 主入口
│ ├── controller/ # 控制器层
│ │ ├── AuthController.java # 认证接口
│ │ ├── UserController.java # 用户接口
│ │ ├── ProductController.java # 商品接口
│ │ └── OrderController.java # 订单接口
│ ├── model/ # 数据模型
│ │ ├── User.java
│ │ ├── Product.java
│ │ ├── Order.java
│ │ └── OrderItem.java
│ ├── repository/ # 数据访问层
│ │ ├── UserRepository.java
│ │ ├── ProductRepository.java
│ │ └── OrderRepository.java
│ ├── config/ # 配置类
│ │ ├── SecurityConfig.java # 安全配置
│ │ └── DataSeeder.java # 数据初始化
│ └── security/ # 安全模块
│ ├── JwtUtil.java # JWT 工具
│ └── JwtAuthFilter.java # JWT 过滤器
└── src/main/resources/
└── application.properties # 应用配置

核心依赖 (pom.xml)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<dependencies>
<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Data JPA -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<!-- Spring Security -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

<!-- JWT -->
<dependency>
<groupId>io.jsonwebtoken</groupId>
<artifactId>jjwt-api</artifactId>
<version>0.11.5</version>
</dependency>

<!-- MySQL -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>

RESTful API 接口

1. 用户登录

1
2
3
4
5
6
7
8
9
10
11
12
13
POST /api/auth/login
Content-Type: application/json

{
"username": "admin",
"password": "password123"
}

Response:
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"username": "admin"
}

2. 获取商品列表

1
2
3
4
5
6
7
8
9
10
11
12
13
14
GET /api/products?active=true
Authorization: Bearer {token}

Response:
[
{
"id": 1,
"name": "iPhone 15",
"description": "Apple 最新智能手机",
"price": 7999.00,
"stock": 100,
"active": true
}
]

3. 创建订单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
POST /api/orders
Content-Type: application/json
Authorization: Bearer {token}

{
"userId": 1,
"items": [
{
"productId": 1,
"quantity": 2,
"price": 7999.00
}
]
}

Response: 201 Created

JWT 认证流程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1. 用户登录 (POST /api/auth/login)

2. 验证用户名密码

3. 生成 JWT Token

4. 返回 Token 给客户端

5. 客户端保存 Token (SharedPreferences)

6. 后续请求携带 Token

7. JwtAuthFilter 验证 Token

8. 验证通过,访问资源

Spring Security 配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Configuration
@EnableWebSecurity
@EnableMethodSecurity
public class SecurityConfig {

private final JwtAuthFilter jwtAuthFilter;

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.csrf(csrf -> csrf.disable())
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/auth/**").permitAll()
.requestMatchers("/api/users/**").authenticated()
.requestMatchers("/api/products/**").permitAll()
.requestMatchers("/api/orders/**").authenticated()
.anyRequest().authenticated()
)
.sessionManagement(session ->
session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
.addFilterBefore(jwtAuthFilter,
UsernamePasswordAuthenticationFilter.class);

return http.build();
}
}

数据库设计

ER 图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
┌─────────────┐       ┌─────────────┐
User │ │ Product
├─────────────┤ ├─────────────┤
│ id │ │ id │
│ name │ │ name │
│ email │ │ description │
│ phone │ │ price │
│ created_at │ │ stock │
│ updated_at │ │ active │
└─────────────┘ └─────────────┘
│ │
│ │
▼ ▼
┌─────────────┐ ┌─────────────┐
Order │◄──────│ OrderItem
├─────────────┤ ├─────────────┤
│ id │ │ id │
│ user_id │ │ order_id │
│ total_amount│ │ product_id │
│ status │ │ quantity │
│ order_date │ │ price │
└─────────────┘ └─────────────┘

建表语句

users 表

1
2
3
4
5
6
7
8
CREATE TABLE users (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
phone VARCHAR(20),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

products 表

1
2
3
4
5
6
7
8
9
10
CREATE TABLE products (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255) NOT NULL,
description TEXT,
price DECIMAL(10,2) NOT NULL,
stock INT DEFAULT 0,
active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

orders 表

1
2
3
4
5
6
7
8
CREATE TABLE orders (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
user_id BIGINT NOT NULL,
total_amount DECIMAL(10,2) NOT NULL,
status VARCHAR(50) NOT NULL,
order_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);

构建和部署

Flutter 前端

前置条件

1
2
3
4
5
6
7
8
# 安装 Flutter
flutter --version

# 检查环境
flutter doctor

# 安装依赖
flutter pub get

运行应用

1
2
3
4
5
6
7
8
9
10
11
12
13
# iOS 模拟器
flutter run

# Android 模拟器
flutter run -d android

# Web
flutter run -d chrome

# 构建发布版本
flutter build ios
flutter build apk
flutter build web

Java 后端

前置条件

  • JDK 17+
  • Maven 3.8+
  • MySQL 8.0+

步骤

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# 1. 克隆项目
git clone git@github.com:DavidYuanX/JavaService.git
cd java-service-project

# 2. 创建数据库
mysql -u root -p
CREATE DATABASE crud_db;

# 3. 配置数据库连接
# 编辑 src/main/resources/application.properties
spring.datasource.url=jdbc:mysql://localhost:3306/crud_db
spring.datasource.username=root
spring.datasource.password=your_password

# 4. 构建项目
mvn clean package

# 5. 运行应用
mvn spring-boot:run

# 或运行 JAR
java -jar target/crud-backend-1.0.0.jar

Docker 部署

Dockerfile (后端)

1
2
3
4
5
6
7
8
9
FROM eclipse-temurin:17-jdk-alpine

WORKDIR /app

COPY target/crud-backend-1.0.0.jar app.jar

EXPOSE 8080

ENTRYPOINT ["java", "-jar", "app.jar"]

构建和运行

1
2
3
4
5
6
7
# 构建
docker build -t crud-service .

# 运行
docker run -p 8080:8080 \
-e SPRING_DATASOURCE_URL=jdbc:mysql://host.docker.internal:3306/crud_db \
crud-service

开发工具推荐

Flutter 开发

  • IDE: VS Code / Android Studio
  • 调试工具: Flutter DevTools
  • API 测试: Postman / Insomnia
  • 版本控制: Git + GitHub

Java 开发

  • IDE: IntelliJ IDEA / Eclipse
  • 构建工具: Maven
  • 数据库管理: MySQL Workbench / DBeaver
  • API 文档: Swagger / OpenAPI

性能优化建议

Flutter 前端

1. 列表性能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 使用 const widget
ListView.builder(
itemCount: items.length,
itemBuilder: (context, index) {
return const ListTile(
leading: Icon(Icons.person),
);
},
);

// 使用 Key 优化重建
ListTile(
key: ValueKey(item.id),
title: Text(item.name),
)

2. 图片缓存

1
2
3
4
5
6
// 使用 cached_network_image
CachedNetworkImage(
imageUrl: product.imageUrl,
placeholder: (context, url) => CircularProgressIndicator(),
errorWidget: (context, url, error) => Icon(Icons.error),
)

Java 后端

1. 数据库索引

1
2
3
CREATE INDEX idx_email ON users(email);
CREATE INDEX idx_user_id ON orders(user_id);
CREATE INDEX idx_active ON products(active);

2. 连接池配置

1
2
3
4
# HikariCP 配置
spring.datasource.hikari.maximum-pool-size=10
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.connection-timeout=20000

3. 缓存配置

1
2
3
4
5
6
7
8
// 使用 Spring Cache
@Cacheable(value = "products", key = "#id")
@GetMapping("/{id}")
public ResponseEntity<Product> getProductById(@PathVariable Long id) {
return productRepository.findById(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}

常见问题排查

Flutter 前端

Q1: 热重载不生效

解决方案:

1
flutter clean && flutter pub get && flutter run

Q2: iOS 构建失败

解决方案:

1
2
3
4
cd ios
pod install
cd ..
flutter build ios

Q3: 网络请求失败

解决方案:

1
2
3
4
5
6
<!-- iOS Info.plist -->
<key>NSAppTransportSecurity</key>
<dict>
<key>NSAllowsArbitraryLoads</key>
<true/>
</dict>

Java 后端

Q1: 数据库连接错误

解决方案:

1
2
3
4
5
6
7
# 检查数据库配置
spring.datasource.url=jdbc:mysql://localhost:3306/crud_db
spring.datasource.username=root
spring.datasource.password=correct_password

# 确保 MySQL 服务已启动
systemctl status mysql

Q2: JWT Token 验证失败

解决方案:

1
2
3
# 确保所有实例使用相同的 secret
jwt.secret=YourVeryLongAndSecureSecretKeyHere123456789
jwt.expiration=86400000


总结

通过本文档,你学习了:

  1. Flutter 跨平台开发 - 一套代码多端运行
  2. Spring Boot 后端开发 - RESTful API 设计
  3. JWT 认证机制 - 安全的 Token 认证
  4. 状态管理 - Provider 模式
  5. 数据库设计 - MySQL 表结构设计
  6. 构建部署 - Docker 容器化部署

参考资源

Flutter

Spring Boot

其他


文档版本: 1.0.0
更新日期: 2026-03-13
维护者: David