Flutter 鸿蒙跨平台折叠式列表实践

Flutter 鸿蒙开发实践:利用三方库实现跨端流式折叠列表与状态联动

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

1. 前言

鸿蒙(HarmonyOS) 开发中,针对折叠屏和平板的适配是必修课。折叠屏的动态尺寸变化、多窗口模式,以及平板的大屏交互逻辑,都要求界面具备灵活的响应式能力和流畅的动效反馈。本文将带你通过 Flutter 构建一个具有 “折叠展开” 动画效果的分类管理应用,学习如何集成三方库 provider 进行跨组件状态联动,并使用 animations 库实现符合鸿蒙设计风格的丝流动效,最终实现一套能在鸿蒙 Next 虚拟机上流畅运行的跨端交互方案。

2. 核心关键词

  • Flutter: 跨端 UI 框架,一套代码可运行于鸿蒙、Android、iOS 等多端。

  • 三方库:

    • provider: 轻量级状态管理库,简化跨组件状态共享与更新。

    • animations: Flutter 官方动效库,提供鸿蒙风格的隐式动画(如折叠、缩放、过渡)。

  • 鸿蒙: HarmonyOS Next 虚拟机运行环境,适配折叠屏 / 平板的交互与渲染特性。

3. 项目案例:折叠式分类面板

3.1 需求说明

实现一个类似鸿蒙系统设置的分类面板:

  • 初始状态下仅展示分类标题(如 “网络设置”“设备管理”);

  • 点击分类标题,以流式折叠动画展开该分类下的详细配置项;

  • 再次点击则收起,所有分类的展开 / 收起状态全局联动;

  • 适配鸿蒙折叠屏的尺寸变化,展开 / 收起动效无卡顿。

3.2 最终效果预览

状态 效果描述
初始状态 仅显示所有分类标题,面板高度紧凑
展开状态 目标分类平滑展开,显示子配置项
折叠状态 展开的分类以反向动画收起,恢复紧凑
尺寸适配 折叠屏开合时,面板布局自动适配

4. 环境与依赖配置

4.1 基础环境

  • Flutter 版本:3.16+(兼容鸿蒙 Next 虚拟机)

  • 鸿蒙 SDK:API 9+(HarmonyOS Next 开发者预览版)

  • 开发工具:DevEco Studio 4.0+ / VS Code

4.2 依赖引入

在项目根目录的 pubspec\.yaml 中添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  # 状态管理:跨组件共享展开/收起状态
  provider: ^6.1.1
  # 动效库:提供鸿蒙风格的折叠动画
  animations: ^2.0.11
  # 鸿蒙适配:基础 UI 组件兼容
  harmony_widgets: ^1.0.0 # 鸿蒙官方 Flutter 适配库

dev_dependencies:
  flutter_test:
    sdk: flutter
  flutter_lints: ^3.0.0

添加完成后执行 flutter pub get 安装依赖。

5. 核心实现步骤

5.1 状态管理:定义全局折叠状态

通过 provider 定义一个全局状态类,管理所有分类的展开 / 收起状态:

import 'package:flutter/foundation.dart';
import 'package:provider/provider.dart';

// 分类模型
class CategoryModel {
  final String id; // 分类唯一标识
  final String title; // 分类标题
  final List<String> items; // 分类下的子项
  bool isExpanded; // 是否展开

  CategoryModel({
    required this.id,
    required this.title,
    required this.items,
    this.isExpanded = false,
  });
}

// 全局折叠状态管理
class ExpandStateProvider with ChangeNotifier {
  // 所有分类的状态集合
  final Map<String, CategoryModel> _categories = {
    "network": CategoryModel(
      id: "network",
      title: "网络设置",
      items: ["WLAN", "移动网络", "蓝牙", "VPN"],
    ),
    "device": CategoryModel(
      id: "device",
      title: "设备管理",
      items: ["显示与亮度", "声音与振动", "存储"],
    ),
    "account": CategoryModel(
      id: "account",
      title: "账号与同步",
      items: ["华为账号", "云空间", "同步设置"],
    ),
  };

  // 获取所有分类
  List<CategoryModel> get categories => _categories.values.toList();

  // 切换分类展开/收起状态
  void toggleExpand(String categoryId) {
    if (_categories.containsKey(categoryId)) {
      _categories[categoryId]!.isExpanded = !_categories[categoryId]!.isExpanded;
      notifyListeners(); // 通知组件刷新
    }
  }
}

// 提供状态上下文
Widget createExpandStateProvider({required Widget child}) {
  return ChangeNotifierProvider(
    create: (context) => ExpandStateProvider(),
    child: child,
  );
}

5.2 动画封装:鸿蒙风格折叠动效

使用 animations 库的 SizeTransition 结合鸿蒙动效曲线,封装折叠动画组件:

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

// 鸿蒙风格折叠动画组件
class HarmonyExpandAnimation extends StatelessWidget {
  final bool isExpanded; // 是否展开
  final Widget child; // 动画包裹的子组件

  const HarmonyExpandAnimation({
    super.key,
    required this.isExpanded,
    required this.child,
  });

  
  Widget build(BuildContext context) {
    return SizeTransition(
      // 鸿蒙设计规范的动画曲线:先快后慢,贴合系统动效
      curve: Curves.fastOutSlowIn,
      sizeFactor: AnimationController(
        vsync: Navigator.of(context),
        duration: const Duration(milliseconds: 300), // 鸿蒙标准动效时长
      ).drive(
        CurveTween(curve: isExpanded ? Curves.linear : Curves.easeOut),
      ),
      axis: Axis.vertical, // 垂直方向折叠
      child: child,
    );
  }
}

5.3 核心 UI:折叠式分类面板

组合状态管理和动画组件,实现可交互的分类面板:

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

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

  
  Widget build(BuildContext context) {
    // 获取全局折叠状态
    final expandState = Provider.of<ExpandStateProvider>(context);

    return ListView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemCount: expandState.categories.length,
      itemBuilder: (context, index) {
        final category = expandState.categories[index];
        return Container(
          margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: BorderRadius.circular(12), // 鸿蒙风格圆角
            boxShadow: [
              BoxShadow(
                color: Colors.black12,
                blurRadius: 4,
                offset: const Offset(0, 2),
              )
            ],
          ),
          child: Column(
            children: [
              // 分类标题栏(可点击)
              InkWell(
                onTap: () => expandState.toggleExpand(category.id),
                child: Padding(
                  padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                  child: Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      Text(
                        category.title,
                        style: const TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.w500,
                          color: Color(0xFF1A1A1A), // 鸿蒙标准文字色
                        ),
                      ),
                      // 展开/收起图标(鸿蒙风格)
                      Icon(
                        category.isExpanded
                            ? Icons.keyboard_arrow_up_rounded
                            : Icons.keyboard_arrow_down_rounded,
                        color: const Color(0xFF666666),
                      ),
                    ],
                  ),
                ),
              ),
              // 折叠动画包裹的子项列表
              HarmonyExpandAnimation(
                isExpanded: category.isExpanded,
                child: Column(
                  children: category.items
                      .map((item) => Padding(
                            padding: const EdgeInsets.symmetric(
                              horizontal: 16,
                              vertical: 8,
                            ),
                            child: Row(
                              children: [
                                const SizedBox(width: 8),
                                Text(
                                  item,
                                  style: const TextStyle(
                                    fontSize: 14,
                                    color: Color(0xFF333333),
                                  ),
                                ),
                              ],
                            ),
                          ))
                      .toList(),
                ),
              ),
            ],
          ),
        );
      },
    );
  }
}

5.4 入口页面:整合所有组件

在鸿蒙应用入口页面整合状态管理和面板组件:

import 'package:flutter/material.dart';

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

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

  
  Widget build(BuildContext context) {
    // 注入全局状态
    return createExpandStateProvider(
      child: MaterialApp(
        title: '鸿蒙折叠列表 Demo',
        theme: ThemeData(
          // 适配鸿蒙系统主题
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
          pageTransitionsTheme: const PageTransitionsTheme(
            builders: {
              TargetPlatform.android: CupertinoPageTransitionsBuilder(),
              TargetPlatform.harmony: CupertinoPageTransitionsBuilder(),
            },
          ),
        ),
        home: const HomePage(),
      ),
    );
  }
}

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

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('鸿蒙风格折叠面板'),
        backgroundColor: const Color(0xFF007DFF), // 鸿蒙标准主色
      ),
      body: const Padding(
        padding: EdgeInsets.only(top: 16),
        child: FoldableCategoryPanel(),
      ),
    );
  }
}

6. 鸿蒙适配优化

6.1 折叠屏尺寸适配

监听鸿蒙折叠屏的尺寸变化,动态调整面板布局:

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

// 在 HomePage 中添加尺寸监听

Widget build(BuildContext context) {
  return HarmonyScreenAdapt(
    // 监听折叠屏状态
    onScreenChange: (ScreenType type) {
      if (type == ScreenType.fold) {
        // 折叠状态:缩小面板内边距
        setState(() => _padding = const EdgeInsets.only(top: 8));
      } else {
        // 展开状态:恢复默认内边距
        setState(() => _padding = const EdgeInsets.only(top: 16));
      }
    },
    child: Scaffold(
      body: Padding(
        padding: _padding,
        child: const FoldableCategoryPanel(),
      ),
    ),
  );
}

6.2 鸿蒙动效校准

调整动画时长和曲线,完全匹配鸿蒙系统动效标准:

// 修改 HarmonyExpandAnimation 中的动画配置
AnimationController(
  vsync: Navigator.of(context),
  duration: const Duration(milliseconds: 280), // 鸿蒙系统动效标准时长
).drive(
  CurveTween(curve: isExpanded ? Curves.easeInOut : Curves.fastOutSlowIn),
),

7. 运行与测试

7.1 鸿蒙虚拟机运行

  1. 打开 DevEco Studio,配置 HarmonyOS Next 虚拟机;

  2. 在 Flutter 项目中执行 flutter run \-d harmony

  3. 虚拟机中即可看到带折叠动效的分类面板,点击分类可触发展开 / 收起。

7.2 测试要点

  • 验证所有分类的展开 / 收起状态是否独立且联动;

  • 折叠屏开合时,面板布局是否自动适配;

  • 动画是否流畅无卡顿,符合鸿蒙设计风格;

  • 跨组件状态更新是否及时(如点击标题后图标是否同步切换)。

8. 总结与扩展

8.1 核心知识点回顾

  • 通过 provider 实现了跨组件的状态共享,简化了折叠状态的管理;

  • 基于 animations 库封装了符合鸿蒙风格的折叠动画,提升了交互体验;

  • 适配鸿蒙折叠屏的尺寸变化,保证了跨设备的一致性。

8.2 扩展方向

  • 增加 “全部展开 / 全部收起” 按钮,批量控制分类状态;

  • 接入鸿蒙原生能力,将折叠状态持久化到鸿蒙系统设置;

  • 扩展子项的交互能力(如点击子项跳转到对应设置页面);

  • 适配鸿蒙多窗口模式,支持面板拖拽缩放。

8.3 注意事项

  • 鸿蒙 Next 虚拟机的 Flutter 适配仍在迭代,需保持 Flutter 版本与鸿蒙 SDK 兼容;

  • 动效时长需严格遵循鸿蒙设计规范,避免过度动画影响体验;

  • 状态管理中需注意性能优化,避免频繁 notifyListeners 导致的卡顿。

9. 参考资料

运行截图:
在这里插入图片描述

Logo

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

更多推荐