本文档从 Android 原生项目集成 Flutter Activity 开始,逐步讲解实现步骤,并针对集成过程中常见的报错(如文件找不到、类找不到、序列化异常)提供完整解决方案,适配最新 Flutter 版本,可直接复制使用。
一、Android 原生项目添加 Flutter Activity 完整示例
1.1 环境准备
-
本地已安装 Flutter SDK(配置好环境变量);
-
Android Studio 安装 Flutter、Dart 插件;
-
现有 Android 原生项目(Java/Kotlin 均可);
-
Flutter 与 Android 项目同级目录存放(推荐)。
1.2 核心步骤
步骤1:创建 Flutter Module(关键)
Android 原生项目不能直接引入 Flutter,需先创建 Flutter Module(依赖库):
打开终端,进入 Android 项目的父目录,执行以下命令(模块名可自定义,本文以 gospot-flutter 为例):
bash
# 创建 flutter module(名称自定义)
flutter create -t module gospot-flutter
注意不要使用android studio创建项目
最终目录结构如下:
你的项目根目录/
├─ android_native_project/ # 你的现有 Android 原生项目
└─ gospot-flutter/ # 刚创建的 Flutter 模块
步骤2:配置 Android 项目依赖
(1)修改 settings.gradle(项目级配置)
添加 Flutter Module 依赖,确保路径与 Flutter 模块名一致:
gradle
rootProject.name = "MyAndroidApp"
include ':app'
// 👇 新增:引入 Flutter Module(路径需与模块名一致)
setBinding(new Binding([gradle: this]))
evaluate(new File(
settingsDir.parentFile,
'gospot-flutter/.android/include_flutter.groovy'
))
(2)修改 app/build.gradle(模块级配置)
-
最低 SDK 版本要求:minSdk ≥ 16(Flutter 官方要求);
-
添加 Flutter 依赖:
gradle
android {
// 必须修改,确保 minSdk ≥ 16
defaultConfig {
minSdk 16
// ... 其他配置(applicationId、versionCode 等)
}
}
dependencies {
// 👇 新增:依赖 Flutter 模块
implementation project(':flutter')
// ... 其他依赖
}
步骤3:编写 Flutter 页面代码
打开 gospot-flutter/lib/main.dart,替换为测试页面(可根据需求修改):
dart
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text("Flutter 页面"),
backgroundColor: Colors.blue,
),
body: const Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"这是 Android 启动的 Flutter Activity",
style: TextStyle(fontSize: 18),
),
SizedBox(height: 20),
Text(
"Flutter 页面加载成功 ✅",
style: TextStyle(color: Colors.green, fontSize: 16),
),
],
),
),
),
);
}
}
步骤4:Android 原生中创建并启动 Flutter Activity
提供两种方式,推荐方式2(自定义 Activity,可定制化),方式1最简用于快速测试。
方式1:使用 Flutter 官方提供的 FlutterActivity(最简单)
无需自定义 Activity,直接在 Android 代码中跳转:
Kotlin 代码
kotlin
import io.flutter.embedding.android.FlutterActivity
// 点击按钮跳转(示例:按钮 id 为 btn_open_flutter)
btn_open_flutter.setOnClickListener {
// 启动默认 Flutter 页面(对应 main.dart)
startActivity(FlutterActivity.createDefaultIntent(this@MainActivity))
}
Java 代码
java
import io.flutter.embedding.android.FlutterActivity;
import android.content.Intent;
// 点击按钮跳转(示例:按钮 id 为 btn_open_flutter)
findViewById(R.id.btn_open_flutter).setOnClickListener(v -> {
Intent intent = FlutterActivity.createDefaultIntent(MainActivity.this);
startActivity(intent);
});
方式2:自定义 Flutter Activity(推荐,可定制)
- 创建自定义 Activity(Java/Kotlin 均可,以 Java 为例):
java
import io.flutter.embedding.android.FlutterActivity;
public class FlutterPageActivity extends FlutterActivity {
// 可重写方法自定义 Flutter 初始化(如指定路由、传递参数)
}
- 在 AndroidManifest.xml 中注册 Activity(必须):
xml
<activity
android:name=".FlutterPageActivity"
android:theme="@style/Theme.AppCompat" />
- 启动自定义 Flutter Activity(Java 代码):
java
import android.content.Intent;
findViewById(R.id.btn_open_flutter).setOnClickListener(v -> {
Intent intent = new Intent(MainActivity.this, FlutterPageActivity.class);
startActivity(intent);
});
步骤5:测试运行
-
先执行 Flutter 模块初始化(进入 gospot-flutter 目录,执行 flutter pub get);
-
回到 Android 项目,点击 Android Studio 右上角「Sync Project with Gradle Files」(大象图标);
-
运行 Android 原生项目,点击按钮,即可跳转到 Flutter 页面;
-
支持热重载/热重启:修改 Flutter 代码,保存即可刷新页面。
1.3 进阶:Android 与 Flutter 传递参数
(1)Android 向 Flutter 传参(基础类型)
java
// Java 代码,跳转时传递基础类型参数
Intent intent = FlutterActivity
.withNewEngine()
.initialRoute("actionId=2&actionName=Ollie") // 传递路由参数
.build(this);
startActivity(intent);
(2)Flutter 接收参数
dart
import 'package:flutter/material.dart';
void main() => runApp(MyApp(
// 获取 Android 传递的路由参数
initParams: WidgetsBinding.instance.window.defaultRouteName,
));
class MyApp extends StatelessWidget {
final String initParams;
const MyApp({super.key, required this.initParams});
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
body: Center(child: Text("Android 传入参数:$initParams")),
),
);
}
}
二、集成过程中常见报错及解决方案
报错1:java.io.FileNotFoundException: F:\ytdd\code\gospot-flutter.android\include_flutter.groovy
1. 报错原因
Android 项目找不到 Flutter Module 里的 .android/include_flutter.groovy 文件,该文件是 Flutter 自动生成的,未执行初始化命令则不会生成。
2. 解决方案(按顺序执行)
使用上文的命令行创建flutter 模块 ,然后
-
打开终端,进入 Flutter 模块目录(本文路径:F:\ytdd\code\gospot-flutter);
-
执行 Flutter 初始化命令,自动生成 .android 目录及目标文件:
bash
flutter pub get
3. 验证与终极修复
-
执行命令后,检查目录结构,确认 .android/include_flutter.groovy 已生成:
gospot-flutter/
├─ .android/
│ └─ include_flutter.groovy ✅ 生成成功
└─ lib/
-
若仍未生成,删除 .android、.ios 目录,重新执行 flutter pub get;
-
确认 settings.gradle 中引入路径与 Flutter 模块名一致(参考 1.2.1 步骤配置)。
报错2:Could not find a command named "make-host-app-editable"
1. 报错原因
Flutter 3.x 及以上版本已废弃 make-host-app-editable 命令,无需手动执行该命令,仅需执行 flutter pub get 即可自动生成 .android 目录。
2. 解决方案
-
进入 Flutter 模块目录(F:\ytdd\code\gospot-flutter);
-
仅执行以下命令,即可生成 .android 目录及 include_flutter.groovy 文件:
bash
flutter pub get
- 回到 Android 项目,点击「Sync Project with Gradle Files」同步配置即可。
报错3:java.lang.ClassNotFoundException: Didn't find class "io.flutter.embedding.android.FlutterActivity" on path
1. 报错原因
Android 项目虽配置了 Flutter 依赖,但 Gradle 未同步成功、缓存异常,导致 FlutterActivity 类未被打包,运行时无法找到。
2. 解决方案(按顺序执行)
-
确认配置正确性:
-
app/build.gradle 中已添加 implementation project(':flutter') 依赖;
-
settings.gradle 中已正确引入 Flutter Module(参考 1.2.1 步骤)。
-
-
同步 Gradle:点击 Android Studio 右上角「Sync Project with Gradle Files」(大象图标);
-
清理并重新编译项目:
-
Build → Clean Project;
-
Build → Rebuild Project。
-
-
重启 Android Studio 和运行的 App;
-
若仍报错,进入 Flutter 模块目录,执行以下命令后重新同步编译:
bash
flutter clean
flutter pub get
报错4:java.lang.IllegalArgumentException: Parcel: unknown type for value Action(...)}
1. 报错概述
在 Android 原生项目跳转 FlutterActivity 时,出现如下序列化相关报错:
java
java.lang.IllegalArgumentException: Parcel: unknown type for value Action(id=2, category=街式, subCategory=平地, subCategoryEn=Flat, name=Ollie, difficulty=EASY, star=1, level=中阶, stance=Regular, learn=学习要领, error=常见错误, preAction=1)
核心结论:该报错与 Flutter 集成无关,属于 Android 原生 Intent 传递自定义对象时,序列化配置异常导致。
2. 报错根本原因
通过 Intent 传递自定义 Action 类对象时,未满足 Android 序列化要求,具体分为3种情况:
-
Action 类未实现 Parcelable 或 Serializable 序列化接口;
-
若实现了 Parcelable,则 writeToParcel(写)与 createFromParcel(读)的字段顺序不一致;
-
Action 类包含不支持序列化的字段类型(如自定义接口、未序列化的内部类等)。
Android 系统无法解析未正确序列化的自定义对象,因此抛出 Parcel: unknown type for value 异常。
3. 修复方案(按推荐优先级排序)
方案1:最简修复(推荐,适配 Flutter 跳转场景)
核心逻辑:跳转 FlutterActivity 时,无需传递完整自定义对象,仅传递基础数据类型(字符串、数字等)即可,避免序列化操作。
错误写法(当前报错写法)
java
// 错误:直接传递 Action 自定义对象
Intent intent = new Intent(this, FlutterActivity.class);
intent.putExtra("action", action); // 传递未正确序列化的对象
startActivity(intent);
正确写法
java
// 正确:仅传递基础类型数据(id、名称、分类等)
Intent intent = FlutterActivity.createDefaultIntent(this);
// 传递 int、String 等基础类型,避免序列化
intent.putExtra("actionId", action.getId());
intent.putExtra("actionName", action.getName());
intent.putExtra("category", action.getCategory());
intent.putExtra("difficulty", action.getDifficulty().toString());
startActivity(intent);
说明:该方案无需修改 Action 类,直接规避序列化问题,适配 Flutter 跳转的核心需求(Flutter 仅需基础数据即可渲染页面)。
方案2:实现 Serializable 序列化(简单通用)
若必须传递完整 Action 对象,推荐使用 Serializable(代码侵入性低,无需手动实现读写方法)。
步骤1:修改 Action 类
java
// 给 Action 类实现 Serializable 接口
public class Action implements Serializable { // 核心修改:添加 Serializable
// 原有字段(无需额外修改)
private int id;
private String category;
private String subCategory;
// ... 其他字段、getter/setter 方法
}
步骤2:传递与接收对象
java
// 1. 传递 Action 对象
Intent intent = FlutterActivity.createDefaultIntent(this);
intent.putExtra("action", action); // 此时可正常传递
startActivity(intent);
// 2. 接收 Action 对象(若需在 Android 端接收,如自定义 FlutterActivity)
Action action = (Action) getIntent().getSerializableExtra("action");
注意:Serializable 会序列化整个对象,若对象字段较多,建议优先使用方案1或方案3。
方案3:Flutter 最佳实践(传递 JSON 字符串)
核心逻辑:将 Action 对象转为 JSON 字符串传递,Flutter 端接收后再解析为对应实体类,既避免序列化异常,也适配 Flutter 数据处理习惯。
步骤1:依赖 Gson(用于对象转 JSON)
在 app/build.gradle 中添加 Gson 依赖(若未添加):
gradle
dependencies {
// Gson 依赖,用于对象与 JSON 互转
implementation 'com.google.code.gson:gson:2.10.1'
}
步骤2:传递 JSON 字符串
java
// 1. 将 Action 对象转为 JSON 字符串
String actionJson = new Gson().toJson(action);
// 2. 传递 JSON 字符串(基础类型,无序列化问题)
Intent intent = FlutterActivity.createDefaultIntent(this);
intent.putExtra("actionJson", actionJson);
startActivity(intent);
步骤3:Flutter 端接收解析(补充)
Flutter 端通过 ModalRoute 或 MethodChannel 获取 JSON 后,解析为实体类:
dart
// Flutter 代码示例
import 'dart:convert';
// 获取 Android 传递的 JSON 字符串
String? actionJson = ModalRoute.of(context)?.settings.arguments as String?;
if (actionJson != null) {
// 解析为 Action 实体类
Map<String, dynamic> actionMap = json.decode(actionJson);
Action action = Action.fromJson(actionMap); // 需自定义 fromJson 方法
}
4. 常见注意事项
-
跳转 FlutterActivity 时,优先使用 FlutterActivity.createDefaultIntent(this) 初始化 Intent,避免手动创建 Intent 时遗漏配置;
-
若使用 Parcelable 序列化(未推荐),务必保证 writeToParcel 与 createFromParcel 的字段读写顺序完全一致;
-
避免传递包含复杂引用的对象(如 Context、View 等),此类对象无法序列化,会导致报错;
-
修改序列化配置后,需 Clean Project → Rebuild Project,避免缓存导致配置不生效。
三、总结
-
核心流程:Android 原生集成 Flutter Activity,需先创建 Flutter Module,再配置 Gradle 依赖,最后通过 Intent 跳转;
-
常见坑点:文件找不到(执行 flutter pub get)、命令失效(放弃 make-host-app-editable)、类找不到(同步+清理编译)、序列化异常(传递基础类型/JSON);
-
最佳实践:跳转 Flutter 页面时,优先传递基础类型或 JSON 字符串,避免复杂对象序列化,降低报错概率。