Flutter框架跨平台鸿蒙开发——每日饮水APP的开发流程

🚀运行效果展示

Flutter框架跨平台鸿蒙开发------每日饮水APP的开发流程

前言

在快节奏的现代生活中,很多人常常忘记定时喝水,长期缺水会对身体健康造成不良影响。为了解决这个问题,我们开发了一款基于Flutter框架的跨平台每日饮水提醒APP,支持Android、iOS、Web和鸿蒙系统。本文将详细介绍该APP的开发流程、核心功能实现和技术要点。

应用介绍

🔍 功能概述

每日饮水APP是一款帮助用户养成良好饮水习惯的应用,主要功能包括:

  • ✅ 实时显示今日喝水进度
  • ✅ 支持快速添加喝水记录(100ml、200ml、300ml、500ml)
  • ✅ 自定义提醒间隔(30-120分钟)
  • ✅ 可设置每日喝水目标(1000-3000ml)
  • ✅ 支持设置提醒时间范围
  • ✅ 记录每次喝水的时间和量
  • ✅ 定时推送喝水提醒通知

🎨 界面设计

应用采用简洁现代的设计风格,主要包含以下界面:

  1. 主界面:显示今日喝水进度、快速添加按钮和今日记录列表
  2. 设置界面:提供提醒设置功能,包括启用开关、提醒间隔、每日目标和提醒时间范围

技术栈选择

技术/框架 版本 用途
Flutter 3.6.2 跨平台UI框架
Dart 3.0.0 开发语言
flutter_local_notifications 19.5.0 本地通知
shared_preferences 2.5.3 数据存储
timezone 0.10.1 时区处理
json_annotation 4.9.0 JSON序列化

开发流程和架构设计

📊 开发流程图

项目初始化
需求分析
技术选型
架构设计
UI设计
核心功能实现
测试
部署
维护和迭代

🏗️ 架构设计

应用采用分层架构设计,主要包含以下层次:

  1. 模型层 :定义数据模型,如WaterReminderSettingsWaterLog
  2. 服务层 :处理业务逻辑,如StorageServiceWaterReminderService
  3. UI层 :负责界面展示,如WaterReminderScreenWaterReminderSettingsScreen

本地通知
数据存储
时区处理
UI层
服务层
模型层
外部依赖
flutter_local_notifications
shared_preferences
timezone

核心功能实现及代码展示

1. 数据模型设计

dart 复制代码
// water_reminder_model.dart
import 'package:json_annotation/json_annotation.dart';

part 'water_reminder_model.g.dart';

/// 喝水提醒设置模型
@JsonSerializable()
class WaterReminderSettings {
  /// 是否启用提醒
  final bool isEnabled;
  
  /// 提醒间隔(分钟)
  final int reminderInterval;
  
  /// 每日喝水目标(毫升)
  final int dailyGoal;
  
  /// 开始提醒时间
  final int startHour;
  
  /// 结束提醒时间
  final int endHour;
  
  /// 构造函数
  WaterReminderSettings({
    this.isEnabled = true,
    this.reminderInterval = 60,
    this.dailyGoal = 2000,
    this.startHour = 8,
    this.endHour = 22,
  });
  
  /// 从JSON创建模型
  factory WaterReminderSettings.fromJson(Map<String, dynamic> json) =>
      _$WaterReminderSettingsFromJson(json);
  
  /// 转换为JSON
  Map<String, dynamic> toJson() => _$WaterReminderSettingsToJson(this);
}

/// 喝水记录模型
@JsonSerializable()
class WaterLog {
  /// 记录ID
  final String id;
  
  /// 喝水量(毫升)
  final int amount;
  
  /// 记录时间
  final DateTime time;
  
  /// 构造函数
  WaterLog({
    required this.id,
    required this.amount,
    required this.time,
  });
  
  /// 从JSON创建模型
  factory WaterLog.fromJson(Map<String, dynamic> json) =>
      _$WaterLogFromJson(json);
  
  /// 转换为JSON
  Map<String, dynamic> toJson() => _$WaterLogToJson(this);
}

2. 存储服务实现

dart 复制代码
// storage_service.dart
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../models/water_reminder_model.dart';

/// 本地存储服务
class StorageService {
  /// 单例实例
  static final StorageService _instance = StorageService._internal();
  
  /// SharedPreferences实例
  SharedPreferences? _prefs;
  
  /// 构造函数
  factory StorageService() => _instance;
  
  /// 内部构造函数
  StorageService._internal();
  
  /// 初始化存储服务
  Future<void> init() async {
    try {
      _prefs = await SharedPreferences.getInstance();
      debugPrint('SharedPreferences初始化成功');
    } catch (e) {
      debugPrint('SharedPreferences初始化失败: $e');
      _prefs = null;
    }
  }
  
  /// 保存喝水提醒设置
  Future<void> saveWaterReminderSettings(WaterReminderSettings settings) async {
    final json = jsonEncode(settings.toJson());
    await _prefs?.setString('water_reminder_settings', json);
  }
  
  /// 获取喝水提醒设置
  Future<WaterReminderSettings> getWaterReminderSettings() async {
    try {
      if (_prefs == null) {
        return WaterReminderSettings();
      }
      
      final json = _prefs!.getString('water_reminder_settings');
      if (json == null) {
        return WaterReminderSettings();
      }
      
      return WaterReminderSettings.fromJson(jsonDecode(json) as Map<String, dynamic>);
    } catch (e) {
      debugPrint('获取喝水提醒设置时出错: $e');
      return WaterReminderSettings();
    }
  }
  
  /// 保存喝水记录
  Future<void> saveWaterLog(WaterLog log) async {
    final jsonList = _prefs?.getString('water_logs') ?? '[]';
    final List<dynamic> logs = jsonDecode(jsonList);
    logs.add(log.toJson());
    await _prefs?.setString('water_logs', jsonEncode(logs));
  }
  
  /// 获取今日喝水记录
  Future<List<WaterLog>> getTodayWaterLogs() async {
    try {
      if (_prefs == null) {
        return [];
      }
      
      final jsonList = _prefs?.getString('water_logs') ?? '[]';
      final List<dynamic> logs = jsonDecode(jsonList);
      
      final today = DateTime.now();
      final todayStart = DateTime(today.year, today.month, today.day);
      final todayEnd = DateTime(today.year, today.month, today.day, 23, 59, 59);
      
      return logs
          .map((e) => WaterLog.fromJson(e as Map<String, dynamic>))
          .where((log) => log.time.isAfter(todayStart) && log.time.isBefore(todayEnd))
          .toList();
    } catch (e) {
      debugPrint('获取今日喝水记录时出错: $e');
      return [];
    }
  }
}

3. 提醒服务实现

dart 复制代码
// water_reminder_service.dart
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:timezone/timezone.dart' as tz;
import '../models/water_reminder_model.dart';
import 'storage_service.dart';

/// 喝水提醒服务
class WaterReminderService {
  /// 单例实例
  static final WaterReminderService _instance = WaterReminderService._internal();
  
  /// 本地通知插件
  final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = 
      FlutterLocalNotificationsPlugin();
  
  /// 存储服务
  final StorageService _storageService = StorageService();
  
  /// 当前提醒设置
  WaterReminderSettings? _currentSettings;
  
  /// 构造函数
  factory WaterReminderService() => _instance;
  
  /// 内部构造函数
  WaterReminderService._internal();
  
  /// 初始化服务
  Future<void> init() async {
    try {
      // 初始化本地通知
      await _initLocalNotifications();
      
      // 加载设置
      _currentSettings = await _storageService.getWaterReminderSettings();
      
      // 根据设置启动或停止提醒
      if (_currentSettings?.isEnabled ?? false) {
        await startReminders();
      }
    } catch (e, stackTrace) {
      debugPrint('初始化喝水提醒服务失败: $e');
      debugPrint('堆栈跟踪: $stackTrace');
    }
  }
  
  /// 初始化本地通知
  Future<void> _initLocalNotifications() async {
    // Android配置
    const AndroidInitializationSettings initializationSettingsAndroid = 
        AndroidInitializationSettings('@mipmap/ic_launcher');
    
    // iOS配置
    const DarwinInitializationSettings initializationSettingsIOS = 
        DarwinInitializationSettings(
      requestAlertPermission: true,
      requestBadgePermission: true,
      requestSoundPermission: true,
    );
    
    // 初始化设置
    const InitializationSettings initializationSettings = InitializationSettings(
      android: initializationSettingsAndroid,
      iOS: initializationSettingsIOS,
    );
    
    await _flutterLocalNotificationsPlugin.initialize(initializationSettings);
  }
  
  /// 启动提醒
  Future<void> startReminders() async {
    if (_currentSettings == null || !_currentSettings!.isEnabled) {
      return;
    }
    
    // 取消之前的所有提醒
    await cancelAllReminders();
    
    // 获取当前时间
    final now = DateTime.now();
    
    // 计算下一次提醒时间
    DateTime nextReminderTime = now.add(
      Duration(minutes: _currentSettings!.reminderInterval),
    );
    
    // 确保提醒时间在设定的范围内
    nextReminderTime = _ensureTimeInRange(nextReminderTime);
    
    // 调度通知
    await _scheduleNotification(nextReminderTime);
    
    debugPrint('喝水提醒已启动,下一次提醒时间:$nextReminderTime');
  }
  
  /// 确保时间在设定的范围内
  DateTime _ensureTimeInRange(DateTime time) {
    if (_currentSettings == null) {
      return time;
    }
    
    final startHour = _currentSettings!.startHour;
    final endHour = _currentSettings!.endHour;
    
    // 如果时间在范围内,直接返回
    if (time.hour >= startHour && time.hour < endHour) {
      return time;
    }
    
    // 如果时间早于开始时间,设置为今天的开始时间
    if (time.hour < startHour) {
      return DateTime(
        time.year,
        time.month,
        time.day,
        startHour,
        0,
        0,
      );
    }
    
    // 如果时间晚于结束时间,设置为明天的开始时间
    return DateTime(
      time.year,
      time.month,
      time.day + 1,
      startHour,
      0,
      0,
    );
  }
  
  /// 调度通知
  Future<void> _scheduleNotification(DateTime scheduledTime) async {
    // 转换为时区时间
    final tz.TZDateTime tzScheduledTime = tz.TZDateTime.from(
      scheduledTime,
      tz.local,
    );
    
    // 创建通知详细信息
    const NotificationDetails notificationDetails = NotificationDetails(
      android: AndroidNotificationDetails(
        'water_reminder_channel',
        '喝水提醒',
        channelDescription: '定时提醒您喝水',
        importance: Importance.high,
        priority: Priority.high,
      ),
      iOS: DarwinNotificationDetails(),
    );
    
    // 调度通知
    await _flutterLocalNotificationsPlugin.zonedSchedule(
      0,
      '喝水时间到!',
      '记得喝一杯水哦,保持身体健康!',
      tzScheduledTime,
      notificationDetails,
      androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle,
      matchDateTimeComponents: DateTimeComponents.time,
    );
  }
  
  /// 停止提醒
  Future<void> stopReminders() async {
    await cancelAllReminders();
    debugPrint('喝水提醒已停止');
  }
  
  /// 取消所有提醒
  Future<void> cancelAllReminders() async {
    await _flutterLocalNotificationsPlugin.cancelAll();
  }
  
  /// 更新提醒设置
  Future<void> updateSettings(WaterReminderSettings newSettings) async {
    // 保存新设置
    await _storageService.saveWaterReminderSettings(newSettings);
    _currentSettings = newSettings;
    
    // 根据新设置启动或停止提醒
    if (newSettings.isEnabled) {
      await startReminders();
    } else {
      await stopReminders();
    }
  }
}

4. 主界面实现

dart 复制代码
// water_reminder_screen.dart
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../models/water_reminder_model.dart';
import '../services/storage_service.dart';
import 'water_reminder_settings_screen.dart';

/// 喝水提醒主屏幕
class WaterReminderScreen extends StatefulWidget {
  /// 构造函数
  const WaterReminderScreen({super.key});

  @override
  State<WaterReminderScreen> createState() => _WaterReminderScreenState();
}

class _WaterReminderScreenState extends State<WaterReminderScreen> {
  /// 存储服务
  final StorageService _storageService = StorageService();
  
  /// 今日喝水记录
  List<WaterLog> _todayLogs = [];
  
  /// 提醒设置
  WaterReminderSettings? _settings;
  
  /// 今日喝水总量
  int _todayTotal = 0;
  
  /// 加载数据
  Future<void> _loadData() async {
    // 加载设置
    final settings = await _storageService.getWaterReminderSettings();
    
    // 加载今日记录
    final logs = await _storageService.getTodayWaterLogs();
    
    // 计算今日总量
    final total = logs.fold(0, (sum, log) => sum + log.amount);
    
    setState(() {
      _settings = settings;
      _todayLogs = logs;
      _todayTotal = total;
    });
  }
  
  /// 添加喝水记录
  Future<void> _addWaterLog(int amount) async {
    final log = WaterLog(
      id: DateTime.now().millisecondsSinceEpoch.toString(),
      amount: amount,
      time: DateTime.now(),
    );
    
    await _storageService.saveWaterLog(log);
    await _loadData();
  }
  
  @override
  void initState() {
    super.initState();
    _loadData();
  }
  
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('喝水提醒'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: () async {
              // 跳转到设置页面
              final result = await Navigator.push(
                context,
                MaterialPageRoute(
                  builder: (context) => const WaterReminderSettingsScreen(),
                ),
              );
              
              // 如果设置有变化,重新加载数据
              if (result == true) {
                await _loadData();
              }
            },
          ),
        ],
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.stretch,
          children: [
            // 喝水进度卡片
            Card(
              elevation: 4,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(16),
              ),
              child: Padding(
                padding: const EdgeInsets.all(24.0),
                child: Column(
                  children: [
                    const Text(
                      '今日喝水进度',
                      style: TextStyle(
                        fontSize: 18,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 24),
                    
                    // 进度环 - 使用自定义绘制
                    SizedBox(
                      width: 200,
                      height: 200,
                      child: CustomPaint(
                        painter: _ProgressRingPainter(
                          progress: (_todayTotal / (_settings?.dailyGoal ?? 2000)).clamp(0.0, 1.0),
                        ),
                        child: Center(
                          child: Column(
                            mainAxisAlignment: MainAxisAlignment.center,
                            children: [
                              Text(
                                '$_todayTotal',
                                style: const TextStyle(
                                  fontSize: 48,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                              Text(
                                'ml / ${_settings?.dailyGoal ?? 2000}ml',
                                style: const TextStyle(
                                  fontSize: 16,
                                  color: Colors.grey,
                                ),
                              ),
                            ],
                          ),
                        ),
                      ),
                    ),
                    
                    const SizedBox(height: 24),
                    
                    // 进度百分比
                    Text(
                      '${((_todayTotal / (_settings?.dailyGoal ?? 2000)) * 100).toInt()}%',
                      style: const TextStyle(
                        fontSize: 24,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                  ],
                ),
              ),
            ),
            
            const SizedBox(height: 24),
            
            // 快速添加按钮
            const Text(
              '快速添加',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            
            const SizedBox(height: 16),
            
            // 使用Wrap替代GridView,避免溢出问题
            Wrap(
              spacing: 16.0,
              runSpacing: 16.0,
              alignment: WrapAlignment.spaceEvenly,
              children: [
                _buildQuickAddButton(100),
                _buildQuickAddButton(200),
                _buildQuickAddButton(300),
                _buildQuickAddButton(500),
              ],
            ),
            
            const SizedBox(height: 24),
            
            // 今日记录
            const Text(
              '今日记录',
              style: TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
            
            const SizedBox(height: 16),
            
            _todayLogs.isEmpty
                ? const Center(
                    child: Padding(
                      padding: EdgeInsets.all(24.0),
                      child: Text('今日还没有喝水记录哦'),
                    ),
                  )
                : ListView.builder(
                    shrinkWrap: true,
                    physics: const NeverScrollableScrollPhysics(),
                    itemCount: _todayLogs.length,
                    itemBuilder: (context, index) {
                      final log = _todayLogs[index];
                      return Card(
                        elevation: 2,
                        shape: RoundedRectangleBorder(
                          borderRadius: BorderRadius.circular(12),
                        ),
                        child: Padding(
                          padding: const EdgeInsets.all(16.0),
                          child: Row(
                            mainAxisAlignment: MainAxisAlignment.spaceBetween,
                            children: [
                              Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  Text(
                                    '${log.amount} ml',
                                    style: const TextStyle(
                                      fontSize: 18,
                                      fontWeight: FontWeight.bold,
                                    ),
                                  ),
                                  Text(
                                    DateFormat('HH:mm').format(log.time),
                                    style: const TextStyle(
                                      color: Colors.grey,
                                    ),
                                  ),
                                ],
                              ),
                              const Icon(
                                Icons.local_drink,
                                color: Colors.blue,
                                size: 32,
                              ),
                            ],
                          ),
                        ),
                      );
                    },
                  ),
          ],
        ),
      ),
    );
  }
  
  /// 构建快速添加按钮
  Widget _buildQuickAddButton(int amount) {
    return GestureDetector(
      onTap: () => _addWaterLog(amount),
      child: Card(
        elevation: 2,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(12),
        ),
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              const Icon(
                Icons.add,
                color: Colors.blue,
                size: 32,
              ),
              const SizedBox(height: 8),
              Text(
                '$amount ml',
                style: const TextStyle(
                  fontSize: 16,
                  fontWeight: FontWeight.bold,
                ),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

/// 自定义进度环绘制器
class _ProgressRingPainter extends CustomPainter {
  /// 进度值(0-1)
  final double progress;
  
  /// 背景色
  final Color backgroundColor;
  
  /// 进度色
  final Color progressColor;
  
  /// 线宽
  final double strokeWidth;
  
  /// 构造函数
  _ProgressRingPainter({
    required this.progress,
    this.backgroundColor = const Color(0xFFE0E0E0),
    this.progressColor = Colors.blue,
    this.strokeWidth = 15.0,
  });
  
  @override
  void paint(Canvas canvas, Size size) {
    // 计算中心点
    final center = Offset(size.width / 2, size.height / 2);
    
    // 计算半径
    final radius = (size.width - strokeWidth) / 2;
    
    // 创建背景画笔
    final backgroundPaint = Paint()
      ..color = backgroundColor
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;
    
    // 创建进度画笔
    final progressPaint = Paint()
      ..color = progressColor
      ..strokeWidth = strokeWidth
      ..style = PaintingStyle.stroke
      ..strokeCap = StrokeCap.round;
    
    // 绘制背景圆环
    canvas.drawCircle(center, radius, backgroundPaint);
    
    // 绘制进度圆环
    final arcAngle = progress * 2 * 3.14159265359;
    canvas.drawArc(
      Rect.fromCircle(center: center, radius: radius),
      -0.5 * 3.14159265359, // 起始角度(-90度)
      arcAngle, // 扫描角度
      false, // 是否使用中心点
      progressPaint,
    );
  }
  
  @override
  bool shouldRepaint(covariant _ProgressRingPainter oldDelegate) {
    return oldDelegate.progress != progress ||
           oldDelegate.backgroundColor != backgroundColor ||
           oldDelegate.progressColor != progressColor ||
           oldDelegate.strokeWidth != strokeWidth;
  }
}

5. 鸿蒙系统适配

为了支持鸿蒙系统,我们需要进行以下适配工作:

  1. 添加鸿蒙平台支持 :在pubspec.yaml中添加鸿蒙平台配置
  2. 处理平台特定API:使用条件编译处理不同平台的API差异
  3. 测试鸿蒙设备:在鸿蒙设备或模拟器上进行测试
yaml 复制代码
# pubspec.yaml
flutter:
  uses-material-design: true
  assets:
    - assets/images/
  # 添加鸿蒙平台支持
  plugin:
    platforms:
      ohos:
        package: com.example.flutter_text
        pluginClass: FlutterTextPlugin

测试和部署

🧪 测试流程

  1. 单元测试:对核心功能进行单元测试
  2. 集成测试:测试不同组件之间的交互
  3. UI测试:测试界面布局和交互
  4. 跨平台测试:在不同平台上进行测试

🚀 部署流程

  1. Web部署

    bash 复制代码
    flutter build web
  2. Android部署

    bash 复制代码
    flutter build apk
  3. iOS部署

    bash 复制代码
    flutter build ios
  4. 鸿蒙部署

    bash 复制代码
    flutter build ohos

遇到的问题和解决方案

1. 进度环显示异常

问题 :在某些设备上,使用两个CircularProgressIndicator嵌套的方式显示进度环时,会出现异常显示。

解决方案 :使用自定义绘制的_ProgressRingPainter类来绘制进度环,确保进度环能够正确显示。

2. 快速添加按钮溢出

问题 :在小屏幕设备上,使用GridView.count时,快速添加按钮会溢出屏幕。

解决方案 :将GridView.count替换为Wrap组件,使按钮能够根据屏幕宽度自动换行。

3. 鸿蒙系统数据库初始化失败

问题 :在鸿蒙系统上,调用getApplicationDocumentsDirectory方法时会失败。

解决方案 :由于喝水提醒功能只使用SharedPreferences,不依赖数据库,因此跳过了数据库初始化步骤。

4. 时区初始化错误

问题 :在某些平台上,调用tz.initializeTimeZones()方法时会出现方法未找到的错误。

解决方案 :移除了tz.initializeTimeZones()方法调用,该方法在最新版本的timezone库中已不再需要。

总结和展望

📋 项目总结

通过本项目,我们成功开发了一款基于Flutter框架的跨平台每日饮水提醒APP,支持Android、iOS、Web和鸿蒙系统。应用具有以下特点:

  1. 跨平台兼容:使用Flutter框架实现一次开发,多平台运行
  2. 功能完整:包含喝水记录、提醒设置、进度展示等核心功能
  3. UI美观:采用现代化的设计风格,界面简洁易用
  4. 性能优良:优化了应用启动和运行性能
  5. 稳定性高:添加了异常处理机制,提高了应用的稳定性

🔮 未来展望

  1. 添加数据分析功能:分析用户的喝水习惯,提供个性化建议
  2. 支持多用户:添加用户登录功能,支持多设备同步
  3. 添加社交功能:支持用户之间的互动和分享
  4. 优化通知机制:提供更灵活的通知设置
  5. 支持更多语言:添加多语言支持

结论

Flutter框架为跨平台开发提供了强大的支持,使我们能够高效地开发出兼容多种平台的应用。通过本项目的实践,我们深入了解了Flutter框架的特性和鸿蒙系统的适配要点,积累了宝贵的跨平台开发经验。

每日饮水APP的开发不仅解决了用户忘记喝水的问题,也展示了Flutter框架在跨平台开发中的优势。我们相信,随着Flutter框架和鸿蒙系统的不断发展,跨平台开发将变得更加高效和便捷。


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

相关推荐
鸣弦artha2 小时前
Flutter框架跨平台鸿蒙开发——Image Widget加载状态管理
android·flutter
新镜2 小时前
【Flutter】Slider 自定义trackShape时最大最小值无法填满进度条问题
flutter
大雷神2 小时前
HarmonyOS智慧农业管理应用开发教程--高高种地---第8篇:地图标记与可视化
harmonyos
2501_944526422 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 主题切换实现
android·开发语言·javascript·python·flutter·游戏·django
kirk_wang2 小时前
Flutter艺术探索-RESTful API集成:Flutter后端对接实战
flutter·移动开发·flutter教程·移动开发教程
某zhuan3 小时前
Flutter环境搭建(VS Code和Android Studio)
android·flutter·android studio
小雨下雨的雨3 小时前
触手可及的微观世界:基于 Flutter 的 3D 血细胞交互教学应用开发
flutter·3d·华为·矩阵·交互·harmonyos·鸿蒙系统
Miguo94well3 小时前
Flutter框架跨平台鸿蒙开发——结婚请柬生成器开发流程
flutter·华为·harmonyos
Miguo94well3 小时前
Flutter框架跨平台鸿蒙开发——记忆力练习APP开发流程
flutter·华为·harmonyos·鸿蒙