Android 原生项目添加 Flutter Activity 示例及常见报错解决方案

本文档从 Android 原生项目集成 Flutter Activity 开始,逐步讲解实现步骤,并针对集成过程中常见的报错(如文件找不到、类找不到、序列化异常)提供完整解决方案,适配最新 Flutter 版本,可直接复制使用。

一、Android 原生项目添加 Flutter Activity 完整示例

1.1 环境准备

  1. 本地已安装 Flutter SDK(配置好环境变量);

  2. Android Studio 安装 Flutter、Dart 插件;

  3. 现有 Android 原生项目(Java/Kotlin 均可);

  4. 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(模块级配置)
  1. 最低 SDK 版本要求:minSdk ≥ 16(Flutter 官方要求);

  2. 添加 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(推荐,可定制)
  1. 创建自定义 Activity(Java/Kotlin 均可,以 Java 为例):
java 复制代码
import io.flutter.embedding.android.FlutterActivity;

public class FlutterPageActivity extends FlutterActivity {

    // 可重写方法自定义 Flutter 初始化(如指定路由、传递参数)

}
  1. 在 AndroidManifest.xml 中注册 Activity(必须):
xml 复制代码
<activity

    android:name=".FlutterPageActivity"

    android:theme="@style/Theme.AppCompat" />
  1. 启动自定义 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:测试运行
  1. 先执行 Flutter 模块初始化(进入 gospot-flutter 目录,执行 flutter pub get);

  2. 回到 Android 项目,点击 Android Studio 右上角「Sync Project with Gradle Files」(大象图标);

  3. 运行 Android 原生项目,点击按钮,即可跳转到 Flutter 页面;

  4. 支持热重载/热重启:修改 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 模块 ,然后

  1. 打开终端,进入 Flutter 模块目录(本文路径:F:\ytdd\code\gospot-flutter);

  2. 执行 Flutter 初始化命令,自动生成 .android 目录及目标文件:

bash 复制代码
flutter pub get
3. 验证与终极修复
  1. 执行命令后,检查目录结构,确认 .android/include_flutter.groovy 已生成:

    gospot-flutter/

    ├─ .android/

    │ └─ include_flutter.groovy ✅ 生成成功

    └─ lib/

  2. 若仍未生成,删除 .android、.ios 目录,重新执行 flutter pub get;

  3. 确认 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. 解决方案
  1. 进入 Flutter 模块目录(F:\ytdd\code\gospot-flutter);

  2. 仅执行以下命令,即可生成 .android 目录及 include_flutter.groovy 文件:

bash 复制代码
flutter pub get
  1. 回到 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. 解决方案(按顺序执行)
  1. 确认配置正确性:

    • app/build.gradle 中已添加 implementation project(':flutter') 依赖;

    • settings.gradle 中已正确引入 Flutter Module(参考 1.2.1 步骤)。

  2. 同步 Gradle:点击 Android Studio 右上角「Sync Project with Gradle Files」(大象图标);

  3. 清理并重新编译项目:

    • Build → Clean Project;

    • Build → Rebuild Project。

  4. 重启 Android Studio 和运行的 App;

  5. 若仍报错,进入 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种情况:

  1. Action 类未实现 Parcelable 或 Serializable 序列化接口;

  2. 若实现了 Parcelable,则 writeToParcel(写)与 createFromParcel(读)的字段顺序不一致;

  3. 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. 常见注意事项
  1. 跳转 FlutterActivity 时,优先使用 FlutterActivity.createDefaultIntent(this) 初始化 Intent,避免手动创建 Intent 时遗漏配置;

  2. 若使用 Parcelable 序列化(未推荐),务必保证 writeToParcel 与 createFromParcel 的字段读写顺序完全一致;

  3. 避免传递包含复杂引用的对象(如 Context、View 等),此类对象无法序列化,会导致报错;

  4. 修改序列化配置后,需 Clean Project → Rebuild Project,避免缓存导致配置不生效。

三、总结

  1. 核心流程:Android 原生集成 Flutter Activity,需先创建 Flutter Module,再配置 Gradle 依赖,最后通过 Intent 跳转;

  2. 常见坑点:文件找不到(执行 flutter pub get)、命令失效(放弃 make-host-app-editable)、类找不到(同步+清理编译)、序列化异常(传递基础类型/JSON);

  3. 最佳实践:跳转 Flutter 页面时,优先传递基础类型或 JSON 字符串,避免复杂对象序列化,降低报错概率。

相关推荐
于慨2 小时前
Flutter Android gradle 8.14 file lock, incompatibility issue
android·flutter·issue
千百元2 小时前
HBuildX 打包成apk完整过程
android
流星白龙11 小时前
【MySQL】7.MySQL基本查询(2)
android·mysql·adb
mldlds12 小时前
MySQL加减间隔时间函数DATE_ADD和DATE_SUB的详解
android·数据库·mysql
钛态13 小时前
Flutter for OpenHarmony:mockito 单元测试的替身演员,轻松模拟复杂依赖(测试驱动开发必备) 深度解析与鸿蒙适配指南
服务器·驱动开发·安全·flutter·华为·单元测试·harmonyos
智算菩萨14 小时前
MP3音频编码原理深度解析与Python全参数调优实战:从心理声学模型到LAME编码器精细控制
android·python·音视频
studyForMokey15 小时前
【Android面试】Activity生命周期专题
android·面试·职场和发展
chehaoman16 小时前
MySQL的索引
android·数据库·mysql
念格16 小时前
Flutter 弹窗 UI 不刷新?用 StatefulBuilder 解决
flutter