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

目录

前言:手风琴组件适配的技术探索

在移动应用开发中,手风琴组件是一种常见的UI元素,用于在有限的空间内展示和管理多级内容。当我们将Flutter应用适配到OpenHarmony平台时,如何实现一个功能完善、交互友好的手风琴组件成为了一个重要挑战。

本次开发中,我们参考了Flutter三方库shadcn_ui的设计理念,实现了一个适配OpenHarmony平台的手风琴组件。这个组件不仅具备基本的展开/收起功能,还支持多种配置选项,如是否允许多项展开、自定义颜色和阴影等,为用户提供了良好的使用体验。

通过本次实践,我们不仅掌握了在OpenHarmony平台上实现复杂UI组件的方法,也积累了跨平台开发的宝贵经验。本文将详细介绍手风琴组件的实现过程、技术要点以及开发中遇到的问题和解决方案。

混合工程结构深度解析

项目目录架构

当Flutter项目集成鸿蒙支持后,典型的项目结构会发生显著变化。以下是经过ohos_flutter插件初始化后的项目结构:

my_flutter_harmony_app/
├── lib/                          # Flutter业务代码(基本不变)
│   ├── main.dart                 # 应用入口
│   ├── accordion_widget.dart     # 手风琴组件
├── pubspec.yaml                  # Flutter依赖配置
├── ohos/                         # 鸿蒙原生层(核心适配区)
│   ├── entry/                    # 主模块
│   │   └── src/main/
│   │       ├── ets/              # ArkTS代码
│   │       │   ├── entryability/
│   │       │   │   ├── EntryAbility.ets       # 主Ability
│   │       │   └── pages/
│   │       │       ├── Index.ets           # 主页面
│   │       ├── resources/        # 鸿蒙资源文件
│   │       │   ├── base/
│   │       │   │   ├── element/  # 字符串等
│   │       │   │   ├── media/    # 图片资源
│   │       │   │   └── profile/  # 配置文件
│   │       └── module.json5       # 应用核心配置
└── README.md

展示效果图片

flutter 实时预览 效果展示
在这里插入图片描述

运行到鸿蒙虚拟设备中效果展示
在这里插入图片描述

引入第三方库 shadcn_ui

在本次开发中,我们使用了shadcn_ui第三方库的设计理念来实现手风琴功能。虽然我们没有直接使用shadcn_ui库的代码,但我们参考了其设计风格和交互模式,实现了一个类似的手风琴组件。

在pubspec.yaml文件中,我们添加了shadcn_ui依赖:

dependencies:
  flutter:
    sdk: flutter
  cupertino_icons: ^1.0.8
  shadcn_ui: ^0.1.0

功能代码实现

1. 手风琴组件开发

我们创建了一个名为AccordionWidget的自定义组件,它是一个StatefulWidget,用于生成和显示手风琴控件。这个组件支持多种配置选项,包括手风琴项列表、是否允许多项展开、背景颜色、标题颜色、内容颜色和阴影等。

核心数据结构

首先,我们定义了AccordionItem数据结构,用于描述手风琴的每个项。

class AccordionItem {
  final String title;
  final Widget content;

  AccordionItem({required this.title, required this.content});
}

手风琴组件实现

接下来,我们实现了AccordionWidget组件,它包含了手风琴的核心逻辑和UI渲染。

class AccordionWidget extends StatefulWidget {
  final List<AccordionItem> items;
  final bool allowMultipleExpanded;
  final Color? backgroundColor;
  final Color? titleColor;
  final Color? contentColor;
  final double? elevation;

  const AccordionWidget({
    Key? key,
    required this.items,
    this.allowMultipleExpanded = false,
    this.backgroundColor,
    this.titleColor,
    this.contentColor,
    this.elevation,
  }) : super(key: key);

  
  _AccordionWidgetState createState() => _AccordionWidgetState();
}

状态管理

_AccordionWidgetState负责管理组件的状态,包括每个手风琴项的展开状态。当组件的属性发生变化时,它会更新内部状态。

class _AccordionWidgetState extends State<AccordionWidget> {
  late List<bool> _expandedStates;

  
  void initState() {
    super.initState();
    _expandedStates = List<bool>.filled(widget.items.length, false);
  }

  
  void didUpdateWidget(covariant AccordionWidget oldWidget) {
    super.didUpdateWidget(oldWidget);
    if (oldWidget.items.length != widget.items.length) {
      _expandedStates = List<bool>.filled(widget.items.length, false);
    }
  }

  void _toggleExpand(int index) {
    setState(() {
      if (widget.allowMultipleExpanded) {
        _expandedStates[index] = !_expandedStates[index];
      } else {
        for (int i = 0; i < _expandedStates.length; i++) {
          _expandedStates[i] = i == index;
        }
      }
    });
  }

  
  Widget build(BuildContext context) {
    return Card(
      elevation: widget.elevation ?? 2,
      color: widget.backgroundColor ?? Colors.white,
      child: Column(
        children: widget.items.asMap().entries.map((entry) {
          int index = entry.key;
          AccordionItem item = entry.value;
          bool isExpanded = _expandedStates[index];

          return Column(
            children: [
              Divider(height: 1, thickness: 1, color: Colors.grey[200]),
              InkWell(
                onTap: () => _toggleExpand(index),
                child: Container(
                  padding: const EdgeInsets.all(16),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        item.title,
                        style: TextStyle(
                          color: widget.titleColor ?? Colors.black,
                          fontWeight: FontWeight.w500,
                          fontSize: 16,
                        ),
                      ),
                      Icon(
                        isExpanded ? Icons.keyboard_arrow_up : Icons.keyboard_arrow_down,
                        size: 20,
                        color: widget.titleColor ?? Colors.black,
                      ),
                    ],
                  ),
                ),
              ),
              if (isExpanded)
                Container(
                  padding: const EdgeInsets.all(16),
                  color: widget.contentColor ?? Colors.grey[50],
                  child: item.content,
                ),
            ],
          );
        }).toList(),
      ),
    );
  }
}

2. 主应用集成

main.dart文件中,我们集成了AccordionWidget组件,并添加了交互功能,实现了手风琴的展开和收起效果。

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

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

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

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

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

  final String title;

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

class _MyHomePageState extends State<MyHomePage> {
  late List<AccordionItem> _accordionItems;

  
  void initState() {
    super.initState();
    _initializeAccordionItems();
  }

  void _initializeAccordionItems() {
    _accordionItems = [
      AccordionItem(
        title: '手风琴项 1',
        content: const Text(
          '这是手风琴项 1 的内容,您可以在这里添加任何类型的 widget,如文本、图片、按钮等。',
          style: TextStyle(fontSize: 14),
        ),
      ),
      AccordionItem(
        title: '手风琴项 2',
        content: const Text(
          '这是手风琴项 2 的内容,您可以在这里添加任何类型的 widget,如文本、图片、按钮等。',
          style: TextStyle(fontSize: 14),
        ),
      ),
      AccordionItem(
        title: '手风琴项 3',
        content: const Text(
          '这是手风琴项 3 的内容,您可以在这里添加任何类型的 widget,如文本、图片、按钮等。',
          style: TextStyle(fontSize: 14),
        ),
      ),
      AccordionItem(
        title: '手风琴项 4',
        content: const Text(
          '这是手风琴项 4 的内容,您可以在这里添加任何类型的 widget,如文本、图片、按钮等。',
          style: TextStyle(fontSize: 14),
        ),
      ),
    ];
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(widget.title),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            const Text(
              'Shadcn UI - 手风琴',
              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: 20),
            AccordionWidget(
              items: _accordionItems,
              allowMultipleExpanded: false,
              backgroundColor: Colors.white,
              titleColor: Colors.black,
              contentColor: Colors.grey[50],
              elevation: 2,
            ),
          ],
        ),
      ),
    );
  }
}

3. 使用方法

要使用AccordionWidget组件,只需在需要显示手风琴的地方添加以下代码:

AccordionWidget(
  items: [
    AccordionItem(
      title: '手风琴项 1',
      content: Text('这是手风琴项 1 的内容'),
    ),
    AccordionItem(
      title: '手风琴项 2',
      content: Text('这是手风琴项 2 的内容'),
    ),
    // 更多手风琴项...
  ],
  allowMultipleExpanded: false, // 是否允许多项展开
  backgroundColor: Colors.white, // 背景颜色
  titleColor: Colors.black, // 标题颜色
  contentColor: Colors.grey[50], // 内容区域颜色
  elevation: 2, // 阴影
)

配置选项

  • items:手风琴项列表
  • allowMultipleExpanded:是否允许多项展开
  • backgroundColor:背景颜色
  • titleColor:标题颜色
  • contentColor:内容区域颜色
  • elevation:阴影

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

  1. 图标名称错误:在开发过程中,我们遇到了图标名称错误的问题。最初我们使用了Icons.arrow_upIcons.arrow_down,但在Flutter中,正确的图标名称是Icons.keyboard_arrow_upIcons.keyboard_arrow_down。解决方案是使用正确的图标名称。

  2. 状态管理问题:在处理手风琴的展开/收起状态时,需要确保状态管理正确。当手风琴项数量发生变化时,需要重新初始化状态数组。解决方案是在didUpdateWidget方法中检测items长度变化并重新初始化状态。

  3. 布局问题:手风琴内容区域的高度需要根据内容自动调整。解决方案是使用Container包裹内容,并让其根据内容自动调整高度。

  4. 交互体验问题:为了提供良好的交互体验,需要为手风琴项添加点击反馈。解决方案是使用InkWell包裹标题区域,提供点击反馈效果。

  5. 样式一致性问题:确保手风琴的样式与应用整体风格一致。解决方案是提供可配置的颜色和阴影选项,让开发者可以根据应用风格进行调整。

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

  1. 自定义组件开发:我们创建了一个可复用的AccordionWidget组件,支持多种配置选项和交互功能。

  2. 状态管理:使用StatefulWidgetsetState方法来管理手风琴的展开/收起状态。

  3. 布局和样式:使用CardColumnRow等布局组件构建手风琴的UI结构,并通过配置选项支持自定义样式。

  4. 事件处理:实现了手风琴项的点击事件处理,支持展开/收起功能。

  5. 数据结构设计:定义了AccordionItem数据结构,用于描述手风琴的每个项。

  6. 组件生命周期:使用initStatedidUpdateWidget方法管理组件的生命周期,确保状态正确初始化和更新。

  7. 跨平台适配:确保手风琴组件在OpenHarmony平台上能够正常显示和交互。

  8. 代码组织:将手风琴组件抽离到单独的文件中,提高了代码的可维护性和复用性。

通过本次开发,我们不仅实现了一个功能完善的手风琴组件,也积累了在OpenHarmony平台上开发Flutter应用的经验。这个手风琴组件可以直接应用到实际项目中,为用户提供良好的内容展示和交互体验。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐