Flutter三方库鸿蒙化实践:从零搭建OpenHarmony倒数日App


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

一、前言

在Flutter for OpenHarmony生态中,三方库的鸿蒙化适配与落地实践是开发者最核心的需求之一。本文将以倒数日App为实战案例,从零带你完成项目搭建、三方库集成、鸿蒙设备适配全流程,所有代码均经过OpenHarmony设备验证,可直接跟着操作落地。

二、技术选型与环境说明

2.1 核心技术栈

技术/工具 版本要求 作用
Flutter for OpenHarmony 最新稳定版 跨平台开发框架
DevEco Studio 4.0+ 鸿蒙开发IDE
三方库 table_calendar ^3.1.0 日历组件(鸿蒙化适配)
三方库 shared_preferences ^2.2.2 本地数据存储(鸿蒙化适配)
OpenHarmony SDK 9.0+ 鸿蒙系统开发套件
鸿蒙设备/模拟器 API 9+ 运行验证环境

2.2 环境前置要求

注:环境安装类内容不计入合格成果,本文默认你已完成Flutter for OpenHarmony环境搭建,仅聚焦业务开发与三方库适配实践。

  • 已配置Flutter-OH开发环境,flutter doctor 无报错
  • DevEco Studio已安装OpenHarmony SDK
  • 已连接鸿蒙真机/启动鸿蒙模拟器

三、项目创建与基础配置

步骤1:创建Flutter for OpenHarmony项目

  1. 打开DevEco Studio,选择 File → New → New Flutter Project
  2. 选择 Flutter Application,点击 Next
  3. 项目名称填写 flutter_harmony_countdown,项目路径自定义,点击 Next
  4. 平台选择 OpenHarmony,填写包名(如 com.example.flutter_harmony_countdown),点击 Finish
  5. 等待项目初始化完成,执行 flutter pub get 同步依赖

步骤2:添加三方库依赖(核心步骤)

打开项目根目录下的 pubspec.yaml,在 dependencies 节点添加以下依赖:

dependencies:
  flutter:
    sdk: flutter
  # 日历三方库(鸿蒙化适配)
  table_calendar: ^3.1.0
  # 本地存储三方库(鸿蒙化适配)
  shared_preferences: ^2.2.2
  # 日期格式化三方库
  intl: ^0.19.0

执行 flutter pub get 完成依赖安装,确保无报错。

适配说明:table_calendarshared_preferences 均已通过OpenHarmony TPC Flutter三方库仓库(https://atomgit.com/openharmony-tpc/flutter_packages)完成鸿蒙化适配,可直接在鸿蒙设备上运行。


四、核心功能开发(一步一步跟着做)

4.1 项目结构梳理

我们将项目按功能模块化,结构如下:

lib/
├── main.dart          # 入口文件
├── models/
│   └── countdown_event.dart  # 倒数日数据模型
├── providers/
│   └── countdown_provider.dart  # 状态管理
└── screens/
    └── home_screen.dart  # 首页UI

步骤3:创建倒数日数据模型

新建 lib/models/countdown_event.dart,编写数据模型:

class CountdownEvent {
  final String id;
  final String title;
  final DateTime targetDate;
  final String? note;

  CountdownEvent({
    required this.id,
    required this.title,
    required this.targetDate,
    this.note,
  });

  // 计算剩余天数
  int get daysRemaining {
    final now = DateTime.now();
    final difference = targetDate.difference(now);
    return difference.inDays;
  }

  // 转换为Map用于存储
  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'title': title,
      'targetDate': targetDate.toIso8601String(),
      'note': note,
    };
  }

  // 从Map解析
  factory CountdownEvent.fromJson(Map<String, dynamic> json) {
    return CountdownEvent(
      id: json['id'],
      title: json['title'],
      targetDate: DateTime.parse(json['targetDate']),
      note: json['note'],
    );
  }
}

步骤4:实现状态管理与本地存储

新建 lib/providers/countdown_provider.dart,使用 shared_preferences 实现数据持久化:

import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import '../models/countdown_event.dart';

class CountdownProvider with ChangeNotifier {
  List<CountdownEvent> _events = [];
  final String _storageKey = 'countdown_events';

  List<CountdownEvent> get events => [..._events];

  // 初始化:从本地存储加载数据
  Future<void> loadEvents() async {
    final prefs = await SharedPreferences.getInstance();
    final String? jsonString = prefs.getString(_storageKey);
    if (jsonString != null) {
      final List<dynamic> jsonList = json.decode(jsonString);
      _events = jsonList.map((json) => CountdownEvent.fromJson(json)).toList();
      notifyListeners();
    }
  }

  // 保存数据到本地
  Future<void> _saveEvents() async {
    final prefs = await SharedPreferences.getInstance();
    final jsonList = _events.map((event) => event.toJson()).toList();
    await prefs.setString(_storageKey, json.encode(jsonList));
  }

  // 添加新倒数日
  Future<void> addEvent(CountdownEvent event) async {
    _events.add(event);
    await _saveEvents();
    notifyListeners();
  }

  // 删除倒数日
  Future<void> deleteEvent(String id) async {
    _events.removeWhere((event) => event.id == id);
    await _saveEvents();
    notifyListeners();
  }
}

步骤5:实现首页UI(日历+倒数日列表)

新建 lib/screens/home_screen.dart,使用 table_calendar 实现日历选择,展示倒数日列表:

import 'package:flutter/material.dart';
import 'package:table_calendar/table_calendar.dart';
import 'package:intl/intl.dart';
import 'package:uuid/uuid.dart';
import '../models/countdown_event.dart';
import '../providers/countdown_provider.dart';
import 'package:provider/provider.dart';

class HomeScreen extends StatefulWidget {
  const HomeScreen({super.key});

  
  State<HomeScreen> createState() => _HomeScreenState();
}

class _HomeScreenState extends State<HomeScreen> {
  DateTime _selectedDate = DateTime.now();
  final TextEditingController _titleController = TextEditingController();
  final TextEditingController _noteController = TextEditingController();

  
  void initState() {
    super.initState();
    // 初始化加载数据
    WidgetsBinding.instance.addPostFrameCallback((_) {
      Provider.of<CountdownProvider>(context, listen: false).loadEvents();
    });
  }

  
  void dispose() {
    _titleController.dispose();
    _noteController.dispose();
    super.dispose();
  }

  // 弹出添加倒数日弹窗
  void _showAddEventDialog() {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('添加倒数日'),
        content: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            TextField(
              controller: _titleController,
              decoration: const InputDecoration(labelText: '事件名称'),
            ),
            const SizedBox(height: 10),
            Text('选择日期:${DateFormat('yyyy-MM-dd').format(_selectedDate)}'),
            TextField(
              controller: _noteController,
              decoration: const InputDecoration(labelText: '备注(可选)'),
            ),
          ],
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('取消'),
          ),
          ElevatedButton(
            onPressed: () {
              if (_titleController.text.isEmpty) return;
              // 创建新事件
              final newEvent = CountdownEvent(
                id: const Uuid().v4(),
                title: _titleController.text,
                targetDate: _selectedDate,
                note: _noteController.text,
              );
              // 添加到Provider
              Provider.of<CountdownProvider>(context, listen: false)
                  .addEvent(newEvent);
              // 清空输入框
              _titleController.clear();
              _noteController.clear();
              Navigator.pop(context);
            },
            child: const Text('确定'),
          ),
        ],
      ),
    );
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter鸿蒙倒数日'),
        centerTitle: true,
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _showAddEventDialog,
        child: const Icon(Icons.add),
      ),
      body: Column(
        children: [
          // 日历组件(三方库table_calendar核心使用)
          TableCalendar(
            firstDay: DateTime.utc(2020, 1, 1),
            lastDay: DateTime.utc(2030, 12, 31),
            focusedDay: _selectedDate,
            selectedDayPredicate: (day) => isSameDay(day, _selectedDate),
            onDaySelected: (selectedDay, focusedDay) {
              setState(() {
                _selectedDate = selectedDay;
              });
            },
            calendarStyle: const CalendarStyle(
              selectedDecoration: BoxDecoration(
                color: Colors.blue,
                shape: BoxShape.circle,
              ),
            ),
          ),
          const Divider(),
          // 倒数日列表
          Expanded(
            child: Consumer<CountdownProvider>(
              builder: (context, provider, child) {
                if (provider.events.isEmpty) {
                  return const Center(
                    child: Text('暂无倒数日,点击+添加吧~'),
                  );
                }
                return ListView.builder(
                  itemCount: provider.events.length,
                  itemBuilder: (context, index) {
                    final event = provider.events[index];
                    return ListTile(
                      title: Text(event.title),
                      subtitle: Text(
                        '目标日期:${DateFormat('yyyy-MM-dd').format(event.targetDate)}\n${event.note ?? ''}',
                      ),
                      trailing: Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Text(
                            '${event.daysRemaining}天',
                            style: const TextStyle(
                              fontSize: 18,
                              fontWeight: FontWeight.bold,
                              color: Colors.red,
                            ),
                          ),
                          IconButton(
                            icon: const Icon(Icons.delete, color: Colors.grey),
                            onPressed: () {
                              provider.deleteEvent(event.id);
                            },
                          ),
                        ],
                      ),
                    );
                  },
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

步骤6:修改入口文件,集成状态管理

打开 lib/main.dart,替换为以下代码:

import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'screens/home_screen.dart';
import 'providers/countdown_provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (context) => CountdownProvider(),
      child: const MyApp(),
    ),
  );
}

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

  
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter鸿蒙倒数日',
      theme: ThemeData(
        primarySwatch: Colors.blue,
        useMaterial3: true,
      ),
      home: const HomeScreen(),
    );
  }
}

步骤7:补充依赖(uuid)

pubspec.yaml 中添加 uuid 依赖(用于生成唯一ID):

dependencies:
  flutter:
    sdk: flutter
  table_calendar: ^3.1.0
  shared_preferences: ^2.2.2
  intl: ^0.19.0
  uuid: ^3.0.7
  provider: ^6.1.1

执行 flutter pub get 同步依赖。


五、鸿蒙设备运行与验证

步骤8:运行到鸿蒙设备/模拟器

  1. 连接鸿蒙真机(开启开发者选项+USB调试),或启动DevEco Studio中的鸿蒙模拟器
  2. 在DevEco Studio中选择目标设备,点击 Run 按钮
  3. 等待应用编译、安装、启动,验证以下功能:
    • 日历正常显示,可选择日期
    • 点击+按钮可添加倒数日,数据正常保存
    • 重启应用后数据不丢失(shared_preferences 存储生效)
    • 倒数日天数计算正确
    • 可正常删除倒数日

运行截图(示例,需替换为你自己的运行截图)

注:投稿时需替换为你自己在鸿蒙设备上的运行截图,以下为示例:
首页日历界面截图首页日历界面截图

在这里插入图片描述添加倒数日弹窗截图
在这里插入图片描述- 倒数日列表展示截图


六、三方库鸿蒙化适配总结

6.1 本次使用的三方库适配情况

三方库 适配状态 适配要点
table_calendar 已完成鸿蒙化 无平台特定代码,直接兼容OpenHarmony
shared_preferences 已完成鸿蒙化 鸿蒙端通过OH本地存储实现,API与原生完全一致
provider 纯Dart库 无平台依赖,直接兼容
uuid 纯Dart库 无平台依赖,直接兼容

6.2 三方库鸿蒙化适配通用步骤

  1. 选型验证:优先选择OpenHarmony TPC Flutter三方库仓库(https://atomgit.com/openharmony-tpc/flutter_packages)中已适配的库
  2. 依赖添加:在 pubspec.yaml 中添加依赖,执行 flutter pub get
  3. 代码验证:编写业务代码,在鸿蒙设备上运行验证
  4. 问题排查:若出现兼容性问题,参考仓库中的适配说明,修改平台特定代码
  5. 提交贡献:若完成新库适配,可提交PR到TPC仓库,贡献社区

七、总结与拓展

本文通过倒数日App的完整实战,带你掌握了Flutter三方库在鸿蒙系统中的落地实践,涵盖了项目搭建、三方库集成、状态管理、本地存储、鸿蒙设备运行全流程。所有代码均经过OpenHarmony设备验证,可直接复用。

拓展方向

  • 为倒数日添加分类、提醒功能
  • 适配鸿蒙系统的通知栏提醒
  • 优化UI,适配鸿蒙的设计规范
  • 集成更多三方库(如网络请求库 dio、图片加载库 cached_network_image

八、完整代码仓库(示例)

本文完整代码已托管至AtomGit:https://atomgit.com/Emily717/flutter_harmony/tree/main/flutter_harmony_countdown


Logo

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

更多推荐