Flutter 自定义按钮组组件 - 技术解析文档

一、项目背景概述

本项目是一个基于 Flutter 框架开发的自定义按钮组组件演示应用,实现了一组具有单选功能的可交互按钮集合。按钮组作为一种常见的UI控件,在表单填写、选项切换、分类筛选等场景中有着广泛的应用。

项目实现的按钮组支持单选模式和取消选中功能,按钮之间自动排列并保持统一的间距,选中状态有平滑的颜色过渡动画。用户可以点击不同的按钮进行选择,再次点击已选中的按钮可以取消选择。应用采用 Material 3 设计规范,整体界面简洁现代,交互流畅自然。

本项目的技术价值在于完全自主实现了按钮组组件,没有依赖任何第三方UI库。开发者深入理解了 Flutter 的动画系统和手势识别机制,通过 AnimatedContainer 实现了状态切换的平滑过渡,通过 GestureDetector 处理点击事件,构建了一个功能完整、可高度定制的按钮组组件。

二、架构分析

整体架构图

应用入口

根应用组件

主页组件

状态管理层

选中索引状态

按钮标签列表

选中结果文本

界面布局层

标题说明区域

按钮组展示区域

选中结果显示

按钮组组件

内部状态管理

按钮渲染层

手势检测

动画容器

文本标签

架构分层说明

项目采用经典的组件化分层架构:

第一层:应用入口层
应用入口层负责全局配置和应用初始化,包括应用标题、主题色、调试标志等。该层使用无状态组件实现,配置了深紫色主题和 Material 3 设计规范。

第二层:页面状态层
页面状态层管理按钮组的选中状态、按钮标签数据和结果显示文本。该层使用有状态组件实现,通过回调函数接收按钮组的选中变化,并更新页面状态。

第三层:按钮组组件层
按钮组组件层是本项目的核心,封装了按钮组的完整功能。该层接收按钮标签列表、选中索引、选中回调等参数,内部维护选中状态,管理按钮的布局和交互。

第四层:按钮渲染层
按钮渲染层负责单个按钮的视觉呈现和交互响应。每个按钮由手势检测器、动画容器和文本标签组成,实现了点击检测和状态切换动画。

三、入口组件流程

应用启动流程

应用从主函数启动,调用 runApp 挂载根组件。根组件配置了应用标题和主题,主题使用深紫色种子色生成 ColorScheme,并启用 Material 3 设计规范。初始路由为主页组件。

页面初始化流程

主页组件初始化时设置初始状态:选中索引为 -1(表示未选中任何按钮),按钮标签列表包含5个按钮(按钮1到按钮5),选中结果文本为"未选择任何按钮"。

页面使用 Scaffold 脚手架,主体内容居中排列。内容区域使用 Column 垂直布局,依次为标题、说明文字、按钮组和结果文本。布局简洁清晰,重点突出按钮组的交互效果。

四、核心组件逐段解析

主页面状态组件

主页面状态组件管理着按钮组的选中状态和结果显示。

选中状态管理
页面维护一个选中索引变量 _selectedIndex,初始值为 -1,表示没有选中任何按钮。当用户点击按钮组中的某个按钮时,选中回调函数被触发,更新选中索引和结果文本。

选中处理函数接收按钮的索引值,如果索引有效(在按钮列表范围内),则更新选中索引,并将结果文本设置为"选中了:按钮X"。如果索引为 -1(表示取消选中),则将结果文本恢复为"未选择任何按钮"。

按钮数据配置
按钮标签数据存储在一个字符串列表中,包含了5个按钮的显示文本。这种数据驱动的设计使得按钮的数量和文本可以灵活配置,只需修改列表内容即可改变按钮组的显示。

按钮组组件通过 buttons 属性接收这个标签列表,内部根据列表长度动态生成对应数量的按钮。这种设计遵循了数据驱动UI的理念,数据是唯一的数据源。

按钮组自定义组件

按钮组组件是本项目的核心实现,提供了完整的按钮组功能。

组件属性设计
组件提供了丰富的可配置属性,包括:

  • buttons:按钮标签列表,决定按钮的数量和显示文本
  • selectedIndex:初始选中索引,支持外部控制选中状态
  • onSelected:选中回调函数,点击按钮时触发
  • width/height:按钮尺寸,控制每个按钮的大小
  • backgroundColor/selectedColor:按钮背景色和选中色
  • textColor/selectedTextColor:文字颜色和选中文字颜色
  • borderRadius:按钮圆角,控制按钮形状
  • isRadio:是否为单选模式
  • enableDeselect:是否允许取消选中

这些属性覆盖了按钮组的主要定制需求,使用者可以根据业务场景灵活调整按钮的外观和行为。

内部状态管理
组件内部维护一个 _selectedIndex 变量来追踪当前选中的按钮索引。初始化时从外部 selectedIndex 属性获取初始值。当外部属性变化时,通过 didUpdateWidget 同步更新内部状态,确保内外状态一致。

这种内外状态分离的设计有两个好处:一是组件可以独立工作,即使外部不传入选中状态也能正常运行;二是支持外部控制,外部可以通过修改 selectedIndex 来改变组件的选中状态。

点击处理逻辑
点击处理函数 _handleTap 是按钮组交互逻辑的核心。根据 isRadio 和 enableDeselect 的配置,点击行为有所不同:

在单选模式下,如果点击的是已选中的按钮且允许取消选中,则将选中状态设为 null(取消选中),并回调 -1 表示取消。如果点击的是未选中的按钮,则更新选中索引为该按钮的索引,并回调对应的索引值。

在非单选模式下(多选模式),目前实现较为简单,直接回调点击的索引值,由外部处理多选逻辑。内部状态仍然只追踪单选,这是当前实现的一个限制。

五、状态管理

状态变量分析

本项目使用 setState 进行状态管理,分为页面级状态和组件级状态两层。

页面级状态
页面维护三个状态变量:

  • 选中索引:记录当前选中的按钮位置,初始为 -1
  • 按钮标签列表:按钮的数据来源,初始为5个按钮
  • 选中结果文本:显示当前选中状态的描述文本

其中,按钮标签列表虽然存储在状态中,但在本项目中是静态不变的。将其放在状态中是为了支持未来可能的动态变更,比如从网络加载按钮数据。

组件级状态
按钮组组件内部维护一个选中索引状态,用于控制按钮的视觉表现。这个状态与外部传入的 selectedIndex 属性保持同步,但在组件内部独立管理。

这种双层状态管理模式是 Flutter 组件开发的常见模式:父组件维护业务状态,子组件维护UI表现状态,两者通过属性和回调进行同步。

状态同步机制

内外状态的同步通过两个方向实现:

外部到内部的同步:当外部 selectedIndex 属性变化时,组件的 didUpdateWidget 会检测到变化,并调用 setState 更新内部 _selectedIndex。这确保了外部状态的改变能够反映到组件的UI上。

内部到外部的同步:当用户点击按钮时,组件内部更新 _selectedIndex 状态,同时调用 onSelected 回调通知外部。外部接收到回调后,更新自己的选中状态,从而完成内部状态到外部状态的同步。

这种双向同步机制确保了内外状态的一致性,同时保持了组件的独立性和可复用性。

取消选中机制

项目实现了一个相对少见的功能——取消选中。在传统的单选按钮组中,一旦选中了某个选项,就不能取消选择,只能切换到其他选项。但本项目通过 enableDeselect 参数支持取消选中功能。

取消选中的实现逻辑很简单:当用户点击已选中的按钮时,如果 enableDeselect 为 true,则将选中状态设为 null,并回调 -1。这使得按钮组的行为更加灵活,适用于"可选可不选"的场景。

六、关键代码详解

Wrap 流式布局

按钮组使用 Wrap 组件进行布局,这是一种流式布局控件,当子组件超出一行宽度时会自动换行。Wrap 组件非常适合按钮组这种数量不固定的场景。

间距设置
Wrap 组件有两个间距参数:spacing 是主轴方向(水平方向)上子组件之间的间距,runSpacing 是交叉轴方向(垂直方向)上行与行之间的间距。项目中两者都设置为10像素,确保按钮之间有均匀的间距。

使用 Wrap 而不是 Row 的好处在于:当按钮数量较多或屏幕宽度较小时,按钮会自动换行显示,不会出现溢出错误。这使得按钮组具有更好的响应式表现,适配不同屏幕尺寸。

按钮生成方式
按钮通过 asMap().entries.map() 的方式从标签列表生成。asMap() 将列表转换为索引-值对的映射,entries 获取所有键值对,map 遍历每个键值对并生成对应的按钮组件。

这种生成方式的优势在于可以同时获取索引和值,索引用于判断选中状态和处理点击事件,值用于显示按钮文本。相比使用 for 循环的方式,这种函数式写法更加简洁优雅。

AnimatedContainer 动画容器

每个按钮使用 AnimatedContainer 组件实现状态切换的平滑过渡动画。AnimatedContainer 是 Flutter 提供的一个隐式动画组件,当它的属性变化时会自动产生过渡动画。

动画属性
AnimatedContainer 的动画属性包括:

  • duration:动画持续时间,项目中设置为200毫秒
  • color:背景颜色,选中时为选中色,未选中时为背景色
  • border:边框颜色,与背景色保持一致
  • width/height:按钮尺寸,保持不变

当选中状态改变时,背景色从一种颜色渐变到另一种颜色,产生平滑的视觉过渡。200毫秒的动画时长是一个经验值,既不会太快导致用户感知不到,也不会太慢影响操作流畅度。

装饰设计
按钮的装饰使用 BoxDecoration,包含颜色、圆角和边框三个部分。颜色根据选中状态动态变化,圆角通过 borderRadius 属性设置,边框宽度为2像素,颜色与背景色相同。

边框的设计有一个细节:边框颜色与背景色相同。这样在未选中状态下边框几乎不可见,但当选中状态变化时,边框颜色也会跟随变化,使得颜色过渡更加均匀。如果没有边框,颜色变化可能会显得比较突兀。

GestureDetector 手势检测

按钮的点击交互通过 GestureDetector 组件实现。GestureDetector 是一个手势检测的万能组件,可以检测各种触摸手势,如点击、双击、长按、拖动等。

本项目只使用了 onTap 回调来处理点击事件。当用户点击按钮时,_handleTap 函数被调用,传入按钮的索引值,然后根据当前选中状态和配置决定下一步操作。

使用 GestureDetector 而不是 ElevatedButton 或 TextButton 的原因是为了获得完全的外观控制权。Flutter 内置的按钮组件有自己的样式和动效,定制起来相对麻烦,而使用 GestureDetector + AnimatedContainer 的组合可以自由控制按钮的所有视觉属性。

文本样式动态变化

按钮的文本样式同样根据选中状态动态变化:选中时文字为选中文字色、粗体;未选中时为普通文字色、常规字重。

文本样式的动态变化虽然没有显式的动画过渡,但由于 AnimatedContainer 的背景色变化提供了足够的视觉反馈,文本样式的瞬时变化并不会显得突兀。如果需要更精细的效果,可以使用 AnimatedDefaultTextStyle 组件实现文本样式的平滑过渡。

七、技术总结

技术亮点

纯自主实现:项目完全自主实现了按钮组组件,没有依赖任何第三方UI库。从布局、动画到交互逻辑全部手写实现,展现了对 Flutter 基础组件的熟练掌握和灵活运用能力。

隐式动画运用:巧妙使用 AnimatedContainer 实现选中状态切换的平滑过渡,代码简洁而效果良好。隐式动画是 Flutter 的一大特色,只需改变属性值就能自动产生动画效果,大大降低了动画开发的复杂度。

流式布局适配:使用 Wrap 组件实现自适应的流式布局,按钮数量变化或屏幕尺寸变化时都能正确显示,不会出现溢出错误。这种响应式设计提升了组件的适用范围。

灵活的配置选项:组件提供了丰富的可配置属性,包括尺寸、颜色、圆角、单选/多选、是否允许取消选中等。使用者可以通过属性配置轻松定制按钮组的外观和行为,满足不同业务场景的需求。

可优化方向

多选模式完善:目前 isRadio 为 false 时的多选模式实现不完整,组件内部仍然只追踪单选状态。可以完善多选功能,支持同时选中多个按钮,内部维护选中索引集合。

更多布局模式:目前只支持 Wrap 流式布局,可以增加更多布局选项,如水平排列(Row)、垂直排列(Column)、网格排列(GridView)等,适应更多的界面设计需求。

交互增强:可以添加更多交互效果,如点击时的缩放动画、长按效果、滑动选中(类似 SegmentedControl)等。还可以支持禁用状态,让某些按钮不可点击。

性能优化:当按钮数量很多时,每次点击都会导致所有按钮重建。可以考虑使用更细粒度的状态管理,只重建状态发生变化的按钮,提升大量按钮场景下的性能。

适用场景

本项目实现的按钮组组件适用于以下场景:

  • 表单选项:在表单中作为单选或多选控件,用于性别、类型、状态等选项的选择
  • 分类筛选:在列表页面顶部作为分类标签,点击切换不同的分类或筛选条件
  • 评分选择:用作星级评分或等级选择,如满意度评分、优先级选择等
  • 视图切换:用于切换不同的视图模式,如图标视图/列表视图切换、日/周/月视图切换等
  • 数量选择:用于选择数量、人数、份数等小范围数字选项
  • 标签选择:文章或商品的标签选择,支持多选和取消选择

总体而言,这是一个实现精良的 Flutter 自定义组件项目。按钮组虽然是一个相对简单的UI控件,但项目通过细致的动画处理、灵活的配置选项和良好的代码组织,展现了 Flutter 自定义组件开发的最佳实践。对于学习 Flutter 组件开发和动画处理的开发者来说,本项目具有很好的参考价值。

请添加图片描述

Flutter三方库 group_button 适配 OpenHarmony —— 实现自定义按钮

目录

前言:自定义按钮在OpenHarmony上的实现

在移动应用开发中,按钮是最基础也是最常用的UI组件之一。不同场景下,我们需要不同样式和功能的按钮来满足用户交互需求。随着OpenHarmony生态的不断发展,越来越多的Flutter应用开始适配这一平台。本文将详细介绍如何在Flutter项目中实现自定义按钮功能,并成功适配到OpenHarmony平台。

我们将通过抽离组件的方式,实现一个功能完整、交互友好的自定义按钮组。这个组件不仅支持多种样式配置,还提供了丰富的交互效果,能够满足不同场景的使用需求。同时,我们会详细讲解在开发过程中遇到的问题及解决方案,为开发者提供参考。

混合工程结构深度解析

项目目录架构

当Flutter项目集成鸿蒙支持后,典型的项目结构会发生显著变化。以下是当前项目的结构:

fluuter_openHarmony/
├── lib/                          # Flutter业务代码
│   ├── main.dart                 # 应用入口
│   └── group_button_widget.dart  # 自定义按钮组件
├── pubspec.yaml                  # Flutter依赖配置
├── ohos/                         # 鸿蒙原生层(核心适配区)
│   ├── AppScope/                 # 应用范围配置
│   ├── entry/                    # 主模块
│   │   └── src/main/
│   │       ├── ets/              # ArkTS代码
│   │       │   ├── entryability/  # 入口能力
│   │       │   └── pages/         # 页面
│   │       └── resources/        # 鸿蒙资源文件
│   ├── hvigor/                   # 构建工具配置
│   ├── build-profile.json5      # 构建配置
│   └── oh-package.json5         # 鸿蒙依赖管理
└── README.md

展示效果图片

flutter 实时预览 效果展示

运行到鸿蒙虚拟设备中效果展示

引入第三方库

在本次开发中,我们使用了 group_button 第三方库来实现自定义按钮功能。group_button 是一个功能强大的 Flutter 库,提供了灵活的按钮组实现方案。我们在 pubspec.yaml 文件中添加了以下依赖:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8
  group_button: ^4.6.0

功能代码实现

1. 自定义按钮组件开发

我们创建了一个名为 GroupButtonWidget 的自定义组件,它是一个 StatefulWidget,用于生成和显示自定义按钮组。这个组件支持多种配置选项,包括按钮标签、选中状态、样式设置等。

核心代码实现

import 'package:flutter/material.dart';

class GroupButtonWidget extends StatefulWidget {
  final List<String> buttons;
  final int? selectedIndex;
  final Function(int) onSelected;
  final double width;
  final double height;
  final Color backgroundColor;
  final Color selectedColor;
  final Color textColor;
  final Color selectedTextColor;
  final BorderRadius borderRadius;
  final bool isRadio;
  final bool enableDeselect;

  const GroupButtonWidget({
    Key? key,
    required this.buttons,
    this.selectedIndex,
    required this.onSelected,
    this.width = 120,
    this.height = 40,
    this.backgroundColor = Colors.grey,
    this.selectedColor = Colors.blue,
    this.textColor = Colors.white,
    this.selectedTextColor = Colors.white,
    this.borderRadius = const BorderRadius.all(Radius.circular(8)),
    this.isRadio = true,
    this.enableDeselect = false,
  }) : super(key: key);

  
  _GroupButtonWidgetState createState() => _GroupButtonWidgetState();
}

状态管理

_GroupButtonWidgetState 负责管理组件的状态,包括按钮的选中状态和状态更新。当组件的属性发生变化时,它会更新内部状态。

class _GroupButtonWidgetState extends State<GroupButtonWidget> {
  late int? _selectedIndex;

  
  void initState() {
    super.initState();
    _selectedIndex = widget.selectedIndex;
  }

  
  void didUpdateWidget(covariant GroupButtonWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.selectedIndex != widget.selectedIndex) {
      setState(() {
        _selectedIndex = widget.selectedIndex;
      });
    }
  }

点击事件处理

_handleTap 方法处理按钮的点击事件,根据是否启用取消选择功能来决定点击行为。

void _handleTap(int index) {
  if (widget.isRadio) {
    if (widget.enableDeselect && _selectedIndex == index) {
      setState(() {
        _selectedIndex = null;
      });
      widget.onSelected(-1);
    } else {
      setState(() {
        _selectedIndex = index;
      });
      widget.onSelected(index);
    }
  } else {
    // 这里可以实现多选逻辑
    widget.onSelected(index);
  }
}

组件构建

build 方法负责构建组件的UI,使用 Wrap 组件来布局按钮,并为每个按钮添加点击事件和动画效果。


Widget build(BuildContext context) {
  return Wrap(
    spacing: 10,
    runSpacing: 10,
    children: widget.buttons.asMap().entries.map((entry) {
      int index = entry.key;
      String label = entry.value;
      bool isSelected = _selectedIndex == index;

      return GestureDetector(
        onTap: () => _handleTap(index),
        child: AnimatedContainer(
          duration: const Duration(milliseconds: 200),
          width: widget.width,
          height: widget.height,
          decoration: BoxDecoration(
            color: isSelected ? widget.selectedColor : widget.backgroundColor,
            borderRadius: widget.borderRadius,
            border: Border.all(
              color: isSelected ? widget.selectedColor : widget.backgroundColor,
              width: 2,
            ),
          ),
          child: Center(
            child: Text(
              label,
              style: TextStyle(
                color: isSelected ? widget.selectedTextColor : widget.textColor,
                fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
              ),
            ),
          ),
        ),
      );
    }).toList(),
  );
}

2. 主应用集成

main.dart 文件中,我们集成了 GroupButtonWidget 组件,并添加了交互功能,包括显示选中状态和处理按钮点击事件。

import 'package:flutter/material.dart';
import 'group_button_widget.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter 自定义按钮',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      debugShowCheckedModeBanner: false,
      home: const MyHomePage(title: 'Flutter 自定义按钮'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  int _selectedIndex = -1;
  List<String> _buttonLabels = ['按钮 1', '按钮 2', '按钮 3', '按钮 4', '按钮 5'];
  String _selectedText = '未选择任何按钮';

  void _handleButtonSelected(int index) {
    setState(() {
      _selectedIndex = index;
      if (index >= 0 && index < _buttonLabels.length) {
        _selectedText = '选中了:${_buttonLabels[index]}';
      } else {
        _selectedText = '未选择任何按钮';
      }
    });
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Center(
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                '自定义按钮组',
                style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 10),
              const Text(
                '点击按钮进行选择',
                style: TextStyle(fontSize: 16, color: Colors.grey),
                textAlign: TextAlign.center,
              ),
              const SizedBox(height: 40),
              GroupButtonWidget(
                buttons: _buttonLabels,
                selectedIndex: _selectedIndex >= 0 ? _selectedIndex : null,
                onSelected: _handleButtonSelected,
                width: 100,
                height: 40,
                backgroundColor: Colors.grey[300]!,
                selectedColor: Colors.blue,
                textColor: Colors.black,
                selectedTextColor: Colors.white,
                borderRadius: const BorderRadius.all(Radius.circular(20)),
                enableDeselect: true,
              ),
              const SizedBox(height: 40),
              Text(
                _selectedText,
                style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
                textAlign: TextAlign.center,
              ),
            ],
          ),
        ),
      ),
    );
  }
}

3. 使用方法

要使用 GroupButtonWidget 组件,只需在需要显示按钮组的地方添加以下代码:

GroupButtonWidget(
  buttons: ['按钮 1', '按钮 2', '按钮 3'],
  selectedIndex: _selectedIndex,
  onSelected: (index) {
    // 处理按钮选择事件
  },
  width: 100,
  height: 40,
  backgroundColor: Colors.grey[300]!,
  selectedColor: Colors.blue,
  textColor: Colors.black,
  selectedTextColor: Colors.white,
  borderRadius: BorderRadius.circular(20),
  enableDeselect: true,
)

配置选项

  • buttons:按钮标签列表
  • selectedIndex:当前选中的按钮索引
  • onSelected:按钮选择回调函数
  • width:按钮宽度
  • height:按钮高度
  • backgroundColor:按钮背景颜色
  • selectedColor:选中状态的按钮颜色
  • textColor:按钮文字颜色
  • selectedTextColor:选中状态的按钮文字颜色
  • borderRadius:按钮圆角
  • isRadio:是否为单选模式
  • enableDeselect:是否启用取消选择功能

本次开发中容易遇到的问题

  1. 依赖解析问题

    • 问题:在适配OpenHarmony时,第三方库可能无法正常解析
    • 解决方案:确保在 pubspec.yaml 文件中正确添加依赖,并运行 flutter pub get 命令下载依赖
  2. 状态管理问题

    • 问题:在使用自定义组件时,状态更新可能不及时
    • 解决方案:正确实现 didUpdateWidget 方法,确保组件属性变化时状态能够及时更新
  3. 布局问题

    • 问题:按钮组在不同屏幕尺寸下的布局可能不理想
    • 解决方案:使用 Wrap 组件来布局按钮,使其能够根据屏幕尺寸自动调整
  4. 动画效果问题

    • 问题:按钮状态变化时的动画效果可能不流畅
    • 解决方案:使用 AnimatedContainer 组件,为状态变化添加平滑的动画效果
  5. 跨平台适配问题

    • 问题:在不同平台上的显示效果可能不一致
    • 解决方案:使用 Flutter 提供的跨平台组件和 API,避免使用平台特定的功能

总结本次开发中用到的技术点

  1. 自定义组件开发

    • 使用 StatefulWidgetState 管理组件状态
    • 实现 didUpdateWidget 方法,确保属性变化时状态能够及时更新
    • 提供丰富的配置选项,增强组件的灵活性和可复用性
  2. 布局和样式

    • 使用 Wrap 组件实现按钮的自动布局
    • 使用 AnimatedContainer 实现平滑的状态切换动画
    • 使用 BoxDecoration 自定义按钮的外观
  3. 事件处理

    • 实现按钮点击事件处理
    • 支持单选和取消选择功能
    • 通过回调函数将选择结果传递给父组件
  4. 状态管理

    • 使用 setState 更新组件状态
    • 处理组件属性变化时的状态更新
    • 维护选中状态的一致性
  5. 跨平台适配

    • 使用 Flutter 提供的跨平台组件
    • 确保在 OpenHarmony 平台上的正常运行
    • 处理平台差异,确保一致的用户体验
  6. 依赖管理

    • 在 pubspec.yaml 文件中添加第三方库依赖
    • 运行 flutter pub get 命令下载依赖
    • 确保依赖的版本兼容性

通过本次开发,我们成功实现了一个功能完整、交互友好的自定义按钮组,并成功适配到 OpenHarmony 平台。这个实现不仅满足了基本的按钮功能需求,还提供了丰富的配置选项和交互效果,能够适应不同场景的使用需求。同时,我们也解决了开发过程中遇到的各种问题,为类似项目的开发提供了参考。

请添加图片描述

flutter_openHarmony(简称 Flutter‑OH)

注意:不是Google官方产物,是OpenHarmony社区TPC组织维护的Flutter引擎移植版本。把Flutter的Dart/Skia引擎做底层改造,让Flutter应用可以直接编译输出 HAP包,跑在OpenHarmony/纯血鸿蒙设备上,不需要依赖Android兼容层。

简单讲:一套Dart/Flutter业务代码,可以同时编译 Android、iOS、OpenHarmony(HAP)

核心原理

对Flutter Engine做Embedder嵌入适配,对接OpenHarmony Rosen图形管线、UIAbility生命周期,通过MethodChannel实现 Dart ↔ ArkTS双向通信,Flutter自绘UI渲染到鸿蒙Surface,复用方舟编译器、系统权限、分布式能力。

  • Dart业务代码几乎不变
  • 底层引擎适配鸿蒙图形、线程、生命周期
  • 输出产物是标准HAP应用包,可上架鸿蒙应用市场

主要优势

  1. 存量Flutter项目低成本接入鸿蒙生态
    纯Dart业务、纯Widget界面几乎不用改代码即可编译出鸿蒙HAP;只有带Android/iOS原生桥接的插件,才需要做鸿蒙适配替换。已经有成熟Flutter App,想快速覆盖鸿蒙设备,不用全部重写ArkTS。

  2. 多端UI高度一致性
    Flutter自绘渲染,不受各平台控件差异影响,手机、平板、车机界面表现统一;滚动、动画、首页各类动效(轮播、吸顶、骨架屏、入场动画)跨平台表现一致,和你前面问的App首页各种效果可以一套代码全部实现。

  3. 继承Flutter完整开发体验
    保留热重载、DevTools调试、完整Widget组件库;pub.dev海量纯Dart三方库直接复用,是鸿蒙跨端方案里三方库最丰富的方案。提供定制CLI,一条命令完成编译、真机调试、打包HAP。

  4. 可调用OpenHarmony原生系统能力
    支持调用分布式软总线、分布式数据KV、原子化服务、鸿蒙权限体系、硬件能力;Flutter页面和ArkTS原生页面可以混合开发、互相跳转,复杂原生逻辑继续写ArkTS,UI业务交给Flutter实现。

  5. 全场景设备覆盖
    支持OpenHarmony手机、平板、智慧屏、车机等设备,适合需要多终端统一UI的业务。引擎做了懒加载,跟随UIAbility生命周期启停,控制内存占用,减少后台资源消耗。

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐