1. 插件介绍

Flutter Books 是一个展示如何在现有原生应用中嵌入 Flutter 模块的示例项目,特别演示了如何使用 Pigeon 插件在 Flutter 和宿主应用之间进行高效通信。

这个示例项目模拟了一个真实的图书目录应用场景:

  • 现有原生鸿蒙应用已经实现了图书列表功能
  • 使用 Flutter 实现一个新的图书详情页面
  • 通过 Pigeon 实现 Flutter 与鸿蒙原生代码之间的无缝数据交互
  • 保持原生应用的现有中间件约束,确保技术栈一致性

2. 插件功能特性

  • 支持在鸿蒙应用中嵌入 Flutter 模块
  • 使用 Pigeon 自动生成跨平台互操作 API 和数据类
  • 实现 Flutter 与鸿蒙原生之间的双向通信
  • 保持原生应用的开发模式和架构约束
  • 支持图书信息的展示和编辑功能
  • 提供完整的示例代码,展示真实场景下的集成方案

3. 快速开始

3.1 环境要求

  • Flutter SDK 2.19.5 或更高版本
  • HarmonyOS SDK 3.0 或更高版本
  • DevEco Studio 3.0 或更高版本
  • JDK 11 或更高版本

3.2 引入依赖

由于这是一个自定义修改版本的三方库,需要通过 Git 形式引入。在项目的 pubspec.yaml 文件中添加以下配置:

dependencies:
  flutter:
    sdk: flutter
  flutter_module_books:
    git:
      url: "https://atomgit.com/flutter/samples.git"
      path: "add_to_app/books/flutter_module_books"

 dev_dependencies:
  pigeon: ^11.0.0

然后运行以下命令获取依赖:

flutter pub get

4. 使用方法

4.1 鸿蒙应用中嵌入 Flutter 图书详情页面

  1. 在鸿蒙应用的布局文件中添加 Flutter 视图容器:
<?xml version="1.0" encoding="utf-8"?>
<DirectionalLayout
    xmlns:ohos="http://schemas.huawei.com/res/ohos"
    ohos:height="match_parent"
    ohos:width="match_parent"
    ohos:orientation="vertical">
    
    <!-- 鸿蒙原生图书列表 -->
    <ListContainer
        ohos:id="$+id:book_list"
        ohos:height="match_parent"
        ohos:width="match_parent"
        ohos:orientation="vertical"/>
</DirectionalLayout>
  1. 在鸿蒙应用的 AbilitySlice 中初始化图书列表并处理点击事件:
import ohos.aafwk.ability.AbilitySlice;
import ohos.aafwk.content.Intent;
import ohos.aafwk.content.Operation;
import ohos.agp.components.Component;
import ohos.agp.components.ListContainer;
import ohos.agp.components.Text;
import java.util.ArrayList;
import java.util.List;

public class MainAbilitySlice extends AbilitySlice {
    private List<Book> bookList = new ArrayList<>();
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_main);
        
        // 初始化图书数据
        initBookData();
        
        // 设置列表容器
        ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_book_list);
        BookAdapter adapter = new BookAdapter(this, bookList);
        listContainer.setItemProvider(adapter);
        
        // 处理列表项点击事件
        listContainer.setItemClickedListener((listContainer1, component, position, id) -> {
            Book selectedBook = bookList.get(position);
            openFlutterBookDetails(selectedBook);
        });
    }
    
    private void initBookData() {
        // 模拟从API获取图书数据
        Book book1 = new Book();
        book1.setTitle("Flutter实战");
        book1.setAuthor("冷启动");
        book1.setSubtitle("基于Flutter 2.0的应用开发实践");
        book1.setSummary("本书详细介绍了Flutter的核心概念和开发技巧...");
        book1.setPublishDate("2021-06-01");
        book1.setPageCount(420);
        
        Thumbnail thumbnail1 = new Thumbnail();
        thumbnail1.setUrl("https://example.com/flutter_book.jpg");
        book1.setThumbnail(thumbnail1);
        
        bookList.add(book1);
        
        // 添加更多图书...
    }
    
    private void openFlutterBookDetails(Book book) {
        // 创建跳转到Flutter图书详情页面的意图
        Intent intent = new Intent();
        Operation operation = new Intent.OperationBuilder()
            .withDeviceId("")
            .withBundleName("dev.flutter.example.harmonyos_books")
            .withAbilityName("FlutterBookAbility")
            .build();
        intent.setOperation(operation);
        
        // 通过Intent传递图书数据
        intent.setParam("book", book);
        
        // 启动Flutter图书详情页面
        startAbilityForResult(intent, 1001);
    }
    
    @Override
    protected void onAbilityResult(int requestCode, int resultCode, Intent resultData) {
        super.onAbilityResult(requestCode, resultCode, resultData);
        
        if (requestCode == 1001 && resultCode == RESULT_OK) {
            // 处理从Flutter返回的编辑后的图书数据
            Book editedBook = resultData.getParam("editedBook", Book.class);
            updateBookInList(editedBook);
        }
    }
    
    private void updateBookInList(Book editedBook) {
        // 更新图书列表中的数据
        for (int i = 0; i < bookList.size(); i++) {
            if (bookList.get(i).getTitle().equals(editedBook.getTitle())) {
                bookList.set(i, editedBook);
                break;
            }
        }
        
        // 刷新列表
        ListContainer listContainer = (ListContainer) findComponentById(ResourceTable.Id_book_list);
        ((BookAdapter) listContainer.getItemProvider()).notifyDataChanged();
    }
}

4.2 创建 Flutter 图书详情 Ability

  1. 在鸿蒙应用中创建一个新的 Ability 来承载 Flutter 视图:
import com.huawei.flutter.hmos.adapter.FlutterView;
import ohos.aafwk.ability.Ability;
import ohos.aafwk.content.Intent;
import ohos.agp.components.ComponentContainer;
import ohos.agp.window.dialog.ToastDialog;
import java.util.HashMap;
import java.util.Map;

public class FlutterBookAbility extends Ability {
    private FlutterView flutterView;
    private Book currentBook;
    
    @Override
    public void onStart(Intent intent) {
        super.onStart(intent);
        super.setUIContent(ResourceTable.Layout_ability_flutter_book);
        
        // 获取传递过来的图书数据
        currentBook = intent.getParam("book", Book.class);
        
        // 初始化 FlutterView
        ComponentContainer container = (ComponentContainer) findComponentById(ResourceTable.Id_flutter_container);
        flutterView = new FlutterView(this);
        flutterView.setBundleName("dev.flutter.example.flutter_module_books");
        flutterView.setEntrypoint("main");
        container.addComponent(flutterView);
        
        // 初始化 Flutter 引擎
        flutterView.initialize();
        
        // 注册 Pigeon API
        registerPigeonApi();
        
        // 向 Flutter 传递图书数据
        sendBookToFlutter();
    }
    
    private void registerPigeonApi() {
        // 注册 HostBookApi 实现
        HostBookApi.setup(flutterView.getFlutterEngine().getDartExecutor().getBinaryMessenger(), new HostBookApi() {
            @Override
            public void cancel() {
                // 处理取消操作
                terminateAbility();
            }
            
            @Override
            public void finishEditingBook(Book editedBook) {
                // 处理编辑完成的图书数据
                Intent resultIntent = new Intent();
                resultIntent.setParam("editedBook", editedBook);
                setResult(RESULT_OK, resultIntent);
                terminateAbility();
            }
        });
    }
    
    private void sendBookToFlutter() {
        // 获取 FlutterBookApi 实例
        FlutterBookApi flutterApi = FlutterBookApi.getApi(flutterView.getFlutterEngine().getDartExecutor().getBinaryMessenger());
        
        // 向 Flutter 发送图书数据
        flutterApi.displayBookDetails(currentBook, new PigeonReply<Void>() {
            @Override
            public void reply(Void reply) {
                // 处理发送成功的回调
            }
            
            @Override
            public void error(String errorCode, String errorMessage, Object errorDetails) {
                // 处理发送失败的回调
                new ToastDialog(FlutterBookAbility.this)
                    .setText("Failed to send book data to Flutter: " + errorMessage)
                    .show();
            }
        });
    }
    
    @Override
    protected void onForeground(Intent intent) {
        super.onForeground(intent);
        if (flutterView != null) {
            flutterView.onForeground();
        }
    }
    
    @Override
    protected void onBackground() {
        super.onBackground();
        if (flutterView != null) {
            flutterView.onBackground();
        }
    }
    
    @Override
    protected void onStop() {
        super.onStop();
        if (flutterView != null) {
            flutterView.onStop();
        }
    }
}

4.3 Flutter 模块开发

  1. 使用 Pigeon 生成的 API 在 Flutter 中显示图书详情:
import 'package:flutter/material.dart';
import 'package:flutter_module_books/api.dart';

void main() {
  runApp(BookDetailsApp());
}

class BookDetailsApp extends StatelessWidget {
  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '图书详情',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: BookDetailsScreen(),
    );
  }
}

class BookDetailsScreen extends StatefulWidget {
  
  _BookDetailsScreenState createState() => _BookDetailsScreenState();
}

class _BookDetailsScreenState extends State<BookDetailsScreen> {
  Book? _currentBook;
  final TextEditingController _titleController = TextEditingController();
  final TextEditingController _authorController = TextEditingController();
  final TextEditingController _summaryController = TextEditingController();
  
  
  void initState() {
    super.initState();
    
    // 注册 FlutterBookApi 实现
    FlutterBookApi.setup(FlutterBookApiImpl(this));
  }
  
  void setBook(Book book) {
    setState(() {
      _currentBook = book;
      _titleController.text = book.title ?? '';
      _authorController.text = book.author ?? '';
      _summaryController.text = book.summary ?? '';
    });
  }
  
  
  Widget build(BuildContext context) {
    if (_currentBook == null) {
      return Scaffold(
        appBar: AppBar(title: Text('图书详情')),
        body: Center(child: CircularProgressIndicator()),
      );
    }
    
    return Scaffold(
      appBar: AppBar(
        title: Text('图书详情'),
        actions: [
          IconButton(
            icon: Icon(Icons.save),
            onPressed: () => saveChanges(),
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            if (_currentBook?.thumbnail?.url != null)
              Image.network(
                _currentBook!.thumbnail!.url!,
                height: 200,
                width: double.infinity,
                fit: BoxFit.cover,
              ),
            SizedBox(height: 16.0),
            TextField(
              controller: _titleController,
              decoration: InputDecoration(labelText: '书名'),
              style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
            ),
            SizedBox(height: 8.0),
            TextField(
              controller: _authorController,
              decoration: InputDecoration(labelText: '作者'),
            ),
            SizedBox(height: 8.0),
            TextField(
              controller: _summaryController,
              decoration: InputDecoration(labelText: '简介'),
              maxLines: null,
            ),
            SizedBox(height: 16.0),
            Text('出版日期: ${_currentBook?.publishDate ?? '未知'}'),
            Text('页数: ${_currentBook?.pageCount ?? '未知'}'),
          ],
        ),
      ),
    );
  }
  
  void saveChanges() {
    // 创建编辑后的图书对象
    Book editedBook = Book()
      ..title = _titleController.text
      ..author = _authorController.text
      ..summary = _summaryController.text
      ..subtitle = _currentBook?.subtitle
      ..publishDate = _currentBook?.publishDate
      ..pageCount = _currentBook?.pageCount
      ..thumbnail = _currentBook?.thumbnail;
    
    // 调用 Host API 完成编辑
    HostBookApi api = HostBookApi();
    api.finishEditingBook(editedBook);
  }
}

class FlutterBookApiImpl extends FlutterBookApi {
  final _BookDetailsScreenState _state;
  
  FlutterBookApiImpl(this._state);
  
  
  void displayBookDetails(Book book) {
    _state.setBook(book);
  }
}

5. 使用 Pigeon 生成 API

Pigeon 是一个用于生成跨平台互操作代码的工具,可以自动创建 Flutter 与原生代码之间的通信层。以下是如何使用 Pigeon 的步骤:

5.1 创建 Pigeon Schema

在 Flutter 模块中创建 pigeon/schema.dart 文件,定义数据模型和 API 接口:

import 'package:pigeon/pigeon.dart';

class Book {
  String? title;
  String? subtitle;
  String? author;
  String? summary;
  String? publishDate;
  int? pageCount;
  Thumbnail? thumbnail;
}

class Thumbnail {
  String? url;
}

()
abstract class FlutterBookApi {
  void displayBookDetails(Book book);
}

()
abstract class HostBookApi {
  void cancel();
  void finishEditingBook(Book book);
}

5.2 生成互操作代码

运行以下命令生成 Flutter 与鸿蒙原生之间的互操作代码:

flutter pub run pigeon --input pigeon/schema.dart \
  --dart_out lib/api.dart \
  --java_out ../harmonyos_books/entry/src/main/java/dev/flutter/example/books/Api.java \
  --java_package "dev.flutter.example.books"

这将生成以下文件:

  • lib/api.dart - Flutter 侧的 API 代码
  • Api.java - 鸿蒙原生侧的 API 代码

6. 构建与运行

6.1 构建 Flutter 模块

在 Flutter 模块目录下运行以下命令构建 Flutter AAR:

flutter build aar --no-debug --no-profile

6.2 构建鸿蒙应用

在 DevEco Studio 中打开鸿蒙应用项目,然后点击 “Build” -> “Build HAP(s)/APP(s)” -> “Build APP(s)” 构建完整的鸿蒙应用。

6.3 运行应用

将鸿蒙设备连接到开发机,然后在 DevEco Studio 中点击 “Run” 按钮或使用以下命令运行应用:

hap build install

7. 常见问题与解决方案

7.1 Flutter 视图无法加载

问题:鸿蒙应用中 Flutter 视图显示为空白或无法加载。

解决方案

  1. 检查 Flutter SDK 版本是否符合要求(2.19.5 或更高)
  2. 确保 Flutter 模块已正确构建(运行 flutter build aar
  3. 检查鸿蒙应用的 config.json 文件中是否已配置 Flutter 相关权限
  4. 查看日志获取详细错误信息:hdc shell logcat -s Flutter

7.2 Pigeon API 调用失败

问题:Flutter 与鸿蒙原生之间的 Pigeon API 调用失败。

解决方案

  1. 确保已正确生成 Pigeon API 代码
  2. 检查 API 接口名称和参数是否匹配
  3. 确保已在两侧正确注册 API 实现
  4. 查看日志获取详细错误信息

7.3 性能问题

问题:嵌入 Flutter 视图后,鸿蒙应用的性能下降。

解决方案

  1. 避免在同一屏幕上嵌入过多 Flutter 视图
  2. 优化 Flutter 代码,减少不必要的重建和重绘
  3. 合理使用 Flutter 的异步加载和缓存机制
  4. 考虑使用 Flutter 的 release 模式构建应用(flutter build aar --release

8. 总结

Flutter Books 示例项目展示了如何在鸿蒙系统上实现 Flutter 与原生应用的无缝集成,特别是通过 Pigeon 插件实现高效的数据通信。

通过本文的指南,您已经了解了如何:

  • 在现有鸿蒙应用中嵌入 Flutter 模块
  • 使用 Pigeon 生成跨平台互操作代码
  • 实现 Flutter 与鸿蒙原生之间的双向通信
  • 构建和运行混合应用

这种集成方式允许开发者在保持现有应用架构和技术栈的同时,利用 Flutter 的跨平台能力快速开发新功能,为鸿蒙应用开发提供了更大的灵活性和效率。

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐