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


摘要

本文以一款基础级习惯打卡App为例,完整展示了如何使用Flutter框架,结合鸿蒙已适配的三方库,实现从项目创建、功能开发到OpenHarmony平台适配的全流程。文章重点分享了鸿蒙化开发中的关键配置、常见问题与解决方案,为开发者提供可直接落地的实践参考。


一、项目概述

1.1 项目背景

Flutter 作为高性能跨平台框架,凭借一套代码多端运行的优势,在 OpenHarmony 生态中也得到了广泛支持。本项目旨在开发一款习惯打卡App,实现添加习惯、打卡记录、数据持久化等核心功能,并确保其能在鸿蒙设备上稳定运行。

1.2 技术选型与依赖

本项目使用的三方库均已在 AtomGit 的 OpenHarmony TPC Flutter 生态仓库完成鸿蒙适配,可直接在 OpenHarmony 环境中安全使用。

库名称 版本 功能说明 鸿蒙适配状态
shared_preferences ^2.2.3 本地轻量数据存储 ✅ 已鸿蒙化
provider ^6.1.2 状态管理 ✅ 已鸿蒙化
intl ^0.19.0 时间日期格式化 ✅ 已鸿蒙化

二、开发环境准备

  1. Flutter SDK 3.10+ 稳定版
  2. Dart SDK (随 Flutter 自带)
  3. VS Code / Android Studio
  4. DevEco Studio (OpenHarmony 开发环境)

三、项目创建与依赖配置

3.1 创建项目

flutter create habit_tracker
cd habit_tracker

3.2 配置 pubspec.yaml

dependencies:
  flutter:
    sdk: flutter
  shared_preferences: ^2.2.3
  provider: ^6.1.2
  intl: ^0.19.0

执行安装:

flutter pub get

四、核心代码实现

4.1 数据模型 habit_model.dart

class Habit {
  final String id;
  final String name;
  final List<DateTime> completedDays;

  Habit({
    required this.id,
    required this.name,
    List<DateTime>? completedDays,
  }) : completedDays = completedDays ?? [];

  bool get isCompletedToday {
    final now = DateTime.now();
    return completedDays.any((day) =>
      day.year == now.year &&
      day.month == now.month &&
      day.day == now.day
    );
  }

  Map<String, dynamic> toJson() {
    return {
      'id': id,
      'name': name,
      'completedDays': completedDays.map((e) => e.toIso8601String()).toList(),
    };
  }

  static Habit fromJson(Map<String, dynamic> json) {
    return Habit(
      id: json['id'],
      name: json['name'],
      completedDays: (json['completedDays'] as List)
          .map((e) => DateTime.parse(e))
          .toList(),
    );
  }
}

4.2 状态管理与数据持久化 habit_provider.dart

import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import 'habit_model.dart';

class HabitProvider extends ChangeNotifier {
  List<Habit> _habits = [];
  SharedPreferences? _prefs;

  List<Habit> get habits => _habits;

  Future<void> initProvider() async {
    try {
      _prefs = await SharedPreferences.getInstance();
      _loadHabits();
    } catch (e) {
      debugPrint('初始化失败:$e');
    }
  }

  void _loadHabits() {
    if (_prefs == null) return;
    final data = _prefs!.getString('habits');
    if (data != null) {
      try {
        final List<dynamic> jsonList = jsonDecode(data);
        _habits = jsonList.map((e) => Habit.fromJson(e)).toList();
      } catch (_) {
        _habits = [];
      }
    }
    notifyListeners();
  }

  Future<void> _saveHabits() async {
    if (_prefs == null) return;
    final jsonList = _habits.map((e) => e.toJson()).toList();
    await _prefs!.setString('habits', jsonEncode(jsonList));
    notifyListeners();
  }

  Future<void> addHabit(String name) async {
    final newHabit = Habit(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      name: name,
    );
    _habits.add(newHabit);
    await _saveHabits();
  }

  Future<void> toggleComplete(Habit habit) async {
    final now = DateTime.now();
    final index = _habits.indexOf(habit);

    if (habit.isCompletedToday) {
      _habits[index].completedDays.removeWhere((day) =>
          day.year == now.year && day.month == now.month && day.day == now.day);
    } else {
      _habits[index].completedDays.add(now);
    }
    await _saveHabits();
  }

  Future<void> deleteHabit(habit) async {
    _habits.remove(habit);
    await _saveHabits();
  }
}

4.3 主界面 main.dart

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

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MyApp());
}

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

  
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      create: (context) => HabitProvider(),
      child: MaterialApp(
        title: '习惯打卡',
        theme: ThemeData(primarySwatch: Colors.blue),
        debugShowCheckedModeBanner: false,
        home: const HabitHomePage(),
      ),
    );
  }
}

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

  
  State<HabitHomePage> createState() => _HabitHomePageState();
}

class _HabitHomePageState extends State<HabitHomePage> {
  final TextEditingController _controller = TextEditingController();
  bool _isLoading = true;

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

  Future<void> _initData() async {
    final habitProvider = Provider.of<HabitProvider>(context, listen: false);
    await habitProvider.initProvider();
    if (mounted) {
      setState(() {
        _isLoading = false;
      });
    }
  }

  
  Widget build(BuildContext context) {
    final habitProvider = Provider.of<HabitProvider>(context);

    return Scaffold(
      appBar: AppBar(
        title: const Text('每日习惯打卡'),
      ),
      body: _isLoading
          ? const Center(child: CircularProgressIndicator())
          : Column(
              children: [
                Padding(
                  padding: const EdgeInsets.all(16),
                  child: Row(
                    children: [
                      Expanded(
                        child: TextField(
                          controller: _controller,
                          decoration: const InputDecoration(
                            hintText: '输入新习惯,例如:早起、阅读',
                            border: OutlineInputBorder(),
                          ),
                        ),
                      ),
                      const SizedBox(width: 10),
                      ElevatedButton(
                        onPressed: () async {
                          if (_controller.text.isNotEmpty) {
                            await habitProvider.addHabit(_controller.text);
                            _controller.clear();
                          }
                        },
                        child: const Text('添加'),
                      ),
                    ],
                  ),
                ),
                Expanded(
                  child: habitProvider.habits.isEmpty
                      ? const Center(child: Text('暂无习惯,点击添加吧~'))
                      : ListView.builder(
                          itemCount: habitProvider.habits.length,
                          itemBuilder: (context, index) {
                            final habit = habitProvider.habits[index];
                            return ListTile(
                              title: Text(habit.name),
                              subtitle: Text('已打卡:${habit.completedDays.length} 天'),
                              trailing: IconButton(
                                icon: Icon(
                                  habit.isCompletedToday
                                      ? Icons.check_circle
                                      : Icons.circle_outlined,
                                  color: habit.isCompletedToday ? Colors.green : null,
                                ),
                                onPressed: () async {
                                  await habitProvider.toggleComplete(habit);
                                },
                              ),
                              onLongPress: () async {
                                await habitProvider.deleteHabit(habit);
                              },
                            );
                          },
                        ),
                ),
              ],
            ),
    );
  }
}

五、鸿蒙化适配与运行验证

5.1 关键适配要点

  1. 路径与缓存清理:项目路径必须为纯英文,无空格、中文。
    flutter clean
    flutter pub get
    
  2. 工程重建:重新生成鸿蒙工程文件,解决构建错误。
    flutter create --platforms=harmony .
    
  3. 异常捕获与加载状态:为 shared_preferences 的初始化增加 try-catch 处理,并在界面上提供加载状态,避免白屏。

5.2 运行验证

通过 DevEco Studio 连接鸿蒙模拟器或真机,执行:

flutter run

应用成功启动,可正常添加习惯、打卡、删除,数据持久化功能正常,无白屏或崩溃现象。

在这里插入图片描述


六、常见问题与解决方案

问题现象 原因分析 解决方案
应用启动白屏 主线程阻塞,异步初始化未完成 增加加载状态,将数据初始化移入 initState
添加习惯无反应 未使用 await 调用异步方法,UI未刷新 按钮点击事件添加 await,并确保 Provider 调用 notifyListeners()
鸿蒙构建报错 ENOENT 路径含中文/空格,构建文件缺失 移动项目至纯英文路径,清理缓存并重建工程

七、项目总结

本项目成功完成了基于 Flutter 的习惯打卡App开发,并通过使用 AtomGit 生态中已适配的三方库,实现了在 OpenHarmony 平台上的稳定运行。通过本次实践,我们验证了 Flutter 跨平台开发在鸿蒙生态中的可行性,并总结了一套从开发到适配的完整流程。


结语

Flutter 与 OpenHarmony 的结合,为跨平台开发提供了广阔的前景。通过本次实践,希望能为更多开发者提供参考,共同推动开源鸿蒙跨平台生态的繁荣发展。


Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐