主仪表板是智慧养老应用的核心入口,它就像是老年用户的健康管家,每天打开应用第一眼看到的就是这个界面。在这个界面上,用户可以快速了解自己今天的健康状况、查看重要提醒、访问常用功能。对于老年用户来说,一个设计良好的主仪表板能够大大降低使用门槛,让他们感到安心和便捷。

在实际开发过程中,我们发现老年用户最关心的是"今天需要做什么"和"我的健康状况如何"。基于这个洞察,我们将主仪表板设计成了一个信息中心,把最重要的信息放在最显眼的位置,用最简单的方式呈现出来。

项目架构与技术选型

在开始编写代码之前,我们先来了解一下整个主仪表板模块的架构设计。这个项目采用了GetX框架进行状态管理,这是一个轻量级但功能强大的Flutter状态管理方案。

###在这里插入图片描述

为什么选择GetX?

GetX的优势在于它的简洁性和高性能。对于智慧养老这样的应用,我们需要确保界面响应迅速,老年用户点击按钮后能立即看到反馈。GetX的响应式编程让我们可以轻松实现这一点,而且代码量相比其他方案要少很多。

模块文件结构

主仪表板模块包含三个核心文件:

  • dashboard_controller.dart - 负责业务逻辑和数据管理
  • dashboard_view.dart - 负责界面展示和用户交互
  • dashboard_binding.dart - 负责依赖注入和生命周期管理

这种分层设计让代码职责清晰,维护起来也更加方便。

Controller层的实现

Controller是整个模块的大脑,它负责管理所有的数据和业务逻辑。让我们从最基础的部分开始看起。

导入必要的依赖包

import 'package:get/get.dart';
import 'package:intl/intl.dart';

这两行代码的作用:第一行导入GetX框架,它提供了状态管理、路由导航等核心功能。第二行导入intl包,这是Flutter官方的国际化工具,我们用它来格式化时间和日期。虽然看起来简单,但选对工具能让后续开发事半功倍。

定义Controller类和响应式变量

class DashboardController extends GetxController {
  var currentTime = ''.obs;
  var currentDate = ''.obs;
  var weatherInfo = '晴天 22°C'.obs;
  var healthScore = 85.obs;
  var todaySteps = 6543.obs;
  var heartRate = 72.obs;
  var bloodPressure = '120/80'.obs;

响应式变量的设计思路:这里定义的每个变量后面都加了.obs,这是GetX的响应式标记。当这些变量的值发生变化时,界面会自动更新,不需要手动调用setState。比如currentTime用来显示当前时间,healthScore显示健康评分,这些数据都是老年用户最关心的核心信息。

我们把健康评分默认设置为85分,这是一个比较理想的数值,能给用户带来积极的心理暗示。实际应用中,这个分数会根据用户的各项健康数据动态计算。

快捷功能列表的数据结构

  var quickActions = <Map<String, dynamic>>[].obs;
  var todayReminders = <Map<String, dynamic>>[].obs;

为什么使用Map结构:快捷功能和今日提醒都使用了Map列表来存储。这种灵活的数据结构让我们可以轻松添加或修改功能项,而不需要创建复杂的数据类。每个Map包含了功能的标题、图标、颜色、路由等信息,这样的设计既简单又实用。

初始化生命周期

  
  void onInit() {
    super.onInit();
    _initializeData();
    _updateTime();
  }

生命周期管理的重要性onInit方法在Controller创建时自动调用,这是初始化数据的最佳时机。我们在这里调用了两个私有方法:_initializeData用来准备快捷功能和提醒数据,_updateTime用来启动时间更新机制。这种分步初始化的方式让代码逻辑更加清晰。

初始化快捷功能数据

  void _initializeData() {
    quickActions.value = [
      {
        'title': '紧急呼叫',
        'icon': 'emergency',
        'color': 0xFFFF4757,
        'route': '/emergency'
      },
      {
        'title': '天气预报',
        'icon': 'weather',
        'color': 0xFF3742FA,
        'route': '/weather'
      },
      {
        'title': '用药提醒',
        'icon': 'medication',
        'color': 0xFF2ED573,
        'route': '/medication'
      },

快捷功能的设计考量:这里定义了8个快捷功能,每个功能都有自己的颜色标识。紧急呼叫使用红色(0xFFFF4757),这是一个醒目的警示色,确保老年用户在紧急情况下能快速找到。天气预报用蓝色,用药提醒用绿色,这些颜色的选择都经过了仔细考虑,既要符合功能特性,又要保证整体界面的和谐统一。

我们把紧急呼叫放在第一位,这不是随意的决定。在与老年用户的访谈中,我们发现他们最担心的就是遇到紧急情况时找不到求助按钮。所以我们把这个功能放在最显眼的位置,让他们用得安心。

继续添加其他快捷功能

      {
        'title': '预约挂号',
        'icon': 'appointment',
        'color': 0xFFFF6B35,
        'route': '/appointment'
      },
      {
        'title': '健康报告',
        'icon': 'health_report',
        'color': 0xFF5352ED,
        'route': '/health-report'
      },
      {
        'title': '家政服务',
        'icon': 'housekeeping',
        'color': 0xFFFF9F43,
        'route': '/housekeeping'
      },
      {
        'title': '新闻资讯',
        'icon': 'news',
        'color': 0xFF1E90FF,
        'route': '/news'
      },
      {
        'title': '日程安排',
        'icon': 'calendar',
        'color': 0xFF26C6DA,
        'route': '/calendar'
      },
    ];

功能排序的策略:这8个功能的排列顺序是经过精心设计的。前面是紧急和健康相关的功能,后面是生活服务类功能。这样的排序符合老年用户的使用优先级,让他们能够快速找到最常用的功能。

每个功能都配置了独立的路由地址,点击后会跳转到对应的详细页面。这种模块化的设计让整个应用的扩展性非常好,未来如果要添加新功能,只需要在这个列表里加一项就可以了。

初始化今日提醒数据

    todayReminders.value = [
      {
        'time': '09:00',
        'title': '服用降压药',
        'type': 'medication',
        'completed': false,
      },
      {
        'time': '14:00',
        'title': '下午体检',
        'type': 'appointment',
        'completed': false,
      },
      {
        'time': '18:00',
        'title': '晚餐后散步',
        'type': 'exercise',
        'completed': false,
      },
    ];
  }

提醒功能的人性化设计:今日提醒是主仪表板的另一个核心功能。我们为每条提醒设置了时间、标题、类型和完成状态。这里展示的是示例数据,实际应用中会从数据库或服务器获取用户的个性化提醒。

注意每条提醒都有一个completed字段,用户可以标记提醒为已完成。这个小细节能给用户带来成就感,特别是对于需要长期坚持的健康管理任务,这种正向反馈非常重要。

实现时间自动更新

  void _updateTime() {
    final now = DateTime.now();
    currentTime.value = DateFormat('HH:mm').format(now);
    
    final weekdays = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'];
    final weekday = weekdays[now.weekday % 7];
    currentDate.value = '${now.year}年${now.month.toString().padLeft(2, '0')}月${now.day.toString().padLeft(2, '0')}日 $weekday';
    
    Future.delayed(Duration(minutes: 1), _updateTime);
  }

时间更新的技术细节:这个方法实现了时间的自动更新功能。我们使用DateFormat来格式化时间,显示为"HH:mm"格式,比如"09:30"。日期部分则手动拼接成中文格式,包含年月日和星期几。

这里有个巧妙的设计:使用Future.delayed实现了一个简单的定时器,每分钟自动调用一次自己。这种递归调用的方式比使用Timer更加轻量,而且不会造成内存泄漏。对于老年用户来说,看到实时更新的时间会让他们感觉应用是"活"的,增加信任感。

页面导航功能

  void navigateToPage(String route) {
    Get.toNamed(route);
  }

简洁的导航实现:这个方法负责处理页面跳转。GetX的toNamed方法让路由导航变得非常简单,只需要传入路由名称就可以跳转到对应页面。这种声明式的导航方式比传统的Navigator.push要清晰很多,而且支持路由参数传递和返回值处理。

提醒状态切换

  void toggleReminder(int index) {
    todayReminders[index]['completed'] = !todayReminders[index]['completed'];
    todayReminders.refresh();
  }

状态管理的小技巧:当用户点击提醒项时,这个方法会切换该提醒的完成状态。这里有个重要的细节:修改Map中的值后,需要调用refresh()方法来通知界面更新。这是因为GetX无法自动检测到嵌套对象内部的变化,需要我们手动触发刷新。

生命周期管理

  
  void onReady() {
    super.onReady();
  }

  
  void onClose() {
    super.onClose();
  }
}

完整的生命周期onReady在界面渲染完成后调用,适合执行一些需要等待界面准备好的操作。onClose在Controller销毁时调用,这里可以清理资源、取消订阅等。虽然当前这两个方法是空的,但保留它们是一个好习惯,方便后续扩展。

Binding层的实现

Binding负责依赖注入,它确保Controller在需要时被正确创建和初始化。

import 'package:get/get.dart';
import 'dashboard_controller.dart';

class DashboardBinding extends Bindings {
  
  void dependencies() {
    Get.lazyPut<DashboardController>(
      () => DashboardController(),
    );
  }
}

依赖注入的优势:这里使用lazyPut而不是put,意味着Controller只有在真正被使用时才会创建。这种懒加载的方式能够优化应用启动速度,特别是当应用有很多页面时,这个优化效果会更加明显。

Binding的另一个好处是它让依赖关系变得清晰可控。如果将来需要替换Controller的实现,只需要修改这里的代码,不需要改动View层的任何内容。

View层的核心实现思路

View层负责界面展示,它会使用Controller中的数据来渲染UI。虽然这里没有展示完整的View代码,但我们可以讨论一下关键的设计思路。

响应式UI更新

在View中,我们会使用ObxGetX组件来包裹需要响应式更新的部分。比如显示时间的Text组件:

Obx(() => Text(controller.currentTime.value))

响应式的魔力:这一行代码就实现了时间的自动更新。当Controller中的currentTime变化时,这个Text会自动重新渲染。不需要手动调用setState,不需要管理订阅,一切都是自动的。这种声明式的UI编程方式让代码变得简洁优雅。

适老化设计原则

在实现View时,我们遵循了几个重要的适老化设计原则:

字体大小要够大:所有文字至少使用18sp的字体,重要信息使用20sp甚至更大。老年用户的视力普遍不太好,大字体能让他们看得更清楚,用得更舒心。

颜色对比要明显:我们使用深色文字配浅色背景,确保足够的对比度。同时避免使用过于鲜艳刺眼的颜色,选择柔和但清晰的配色方案。

按钮要够大:所有可点击的元素至少44x44dp,这是苹果和谷歌都推荐的最小触摸目标尺寸。对于老年用户,我们甚至会做得更大一些,确保他们能够准确点击。

布局要简洁:避免在一个屏幕上放太多信息,每个卡片只展示最核心的内容。信息过载会让老年用户感到困惑和焦虑,简洁的设计反而更有效。

性能优化的实践经验

在开发过程中,我们特别注意了性能优化,确保应用在各种设备上都能流畅运行。

避免不必要的重建

GetX的响应式系统已经很高效了,但我们还是要注意不要过度使用。只有真正需要响应式更新的部分才用Obx包裹,静态内容直接渲染就好。这样可以减少不必要的性能开销。

合理使用懒加载

快捷功能的图标我们采用了懒加载策略,只有当图标出现在屏幕上时才加载。这样可以加快首屏渲染速度,提升用户体验。

数据缓存策略

健康数据、提醒信息等会缓存在本地,避免每次打开应用都要从服务器获取。这不仅能提升加载速度,还能在网络不好的情况下保证应用可用。

实际开发中遇到的问题

在实现主仪表板的过程中,我们遇到了一些有趣的问题,分享出来供大家参考。

时间格式化的坑

最开始我们直接使用DateFormat的中文本地化,结果发现在某些设备上会显示乱码。后来改成手动拼接中文字符串,虽然代码多了几行,但兼容性好了很多。这个教训告诉我们,在做适老化应用时,稳定性比代码优雅更重要。

响应式更新的细节

修改Map或List内部的值时,GetX不会自动检测到变化。我们需要手动调用refresh()方法。这个细节在文档里有提到,但实际开发时还是容易忘记,导致界面不更新。

内存管理

最初版本的时间更新使用了Timer,结果发现长时间运行后会有轻微的内存泄漏。改用Future.delayed的递归调用后,这个问题就解决了。这提醒我们要时刻关注应用的内存使用情况。

测试与验证

完成开发后,我们进行了全面的测试,确保功能稳定可靠。

功能测试

我们测试了所有的快捷功能跳转,确保每个按钮都能正确导航到对应页面。测试了提醒的标记功能,验证状态切换是否正常。测试了时间更新,确认每分钟都能准确刷新。

适老化测试

我们邀请了几位老年用户进行实际测试。他们的反馈非常宝贵:有人说字体还可以再大一点,有人建议紧急呼叫按钮的颜色要更醒目。根据这些反馈,我们做了多次迭代优化。

性能测试

在不同配置的设备上测试应用性能,确保即使在低端设备上也能流畅运行。监控内存使用,确保长时间运行不会出现内存泄漏。测试网络异常情况下的表现,确保应用不会崩溃。

总结与展望

主仪表板是整个智慧养老应用的门面,它的设计和实现直接影响用户的第一印象。通过使用GetX框架,我们实现了一个响应迅速、易于维护的主仪表板。

在开发过程中,我们始终把老年用户的需求放在第一位。大字体、高对比度、简洁的布局,这些看似简单的设计背后,都是对用户需求的深入理解和细致考量。

未来我们还计划添加更多个性化功能,比如根据用户的使用习惯自动调整快捷功能的顺序,根据健康数据智能推荐相关服务。技术是为人服务的,只有真正理解用户需求,才能做出有温度的产品。


欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
id: json[‘id’],
createdAt: DateTime.parse(json[‘createdAt’]),
updatedAt: DateTime.parse(json[‘updatedAt’]),
data: json[‘data’],
);
}
}


这个数据模型为主仪表板实现提供了基础的数据结构支持。我们使用了通用的设计模式,包含了ID、时间戳和数据字段,便于后续的扩展和维护。

## Controller业务逻辑实现

```dart
import 'package:get/get.dart';
import 'package:flutter/material.dart';

class DashboardController extends GetxController {
  final RxList<DashboardModel> items = <DashboardModel>[].obs;
  final RxBool isLoading = false.obs;
  final RxString errorMessage = ''.obs;

  @override
  void onInit() {
    super.onInit();
    loadData();
  }

  Future<void> loadData() async {
    try {
      isLoading.value = true;
      errorMessage.value = '';
      
      // 模拟数据加载
      await Future.delayed(Duration(seconds: 1));
      
      // 这里应该从实际的数据源加载数据
      items.value = _generateSampleData();
      
    } catch (e) {
      errorMessage.value = '加载数据失败:${e.toString()}';
      Get.snackbar('错误', errorMessage.value);
    } finally {
      isLoading.value = false;
    }
  }

  List<DashboardModel> _generateSampleData() {
    // 生成示例数据
    return List.generate(5, (index) {
      return DashboardModel(
        id: 'item_$index',
        createdAt: DateTime.now().subtract(Duration(days: index)),
        updatedAt: DateTime.now(),
        data: {'title': '项目 ${index + 1}', 'description': '这是第 ${index + 1} 个项目'},
      );
    });
  }

  void addItem(DashboardModel item) {
    items.insert(0, item);
    Get.snackbar('成功', '添加成功');
  }

  void updateItem(String id, DashboardModel item) {
    final index = items.indexWhere((element) => element.id == id);
    if (index != -1) {
      items[index] = item;
      Get.snackbar('成功', '更新成功');
    }
  }

  void deleteItem(String id) {
    items.removeWhere((element) => element.id == id);
    Get.snackbar('成功', '删除成功');
  }

  @override
  void onClose() {
    super.onClose();
  }
}

Controller实现了完整的业务逻辑,包括数据加载、增删改查等基本操作。我们使用GetX的响应式编程,确保UI能够及时响应数据变化。

View界面实现

import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';
import 'dashboard_controller.dart';

class DashboardView extends GetView<DashboardController> {
  const DashboardView({Key? key}) : super(key: key);

  
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Color(0xFFF8F9FA),
      appBar: AppBar(
        title: Text(
          '主仪表板',
          style: TextStyle(
            fontSize: 20.sp,
            fontWeight: FontWeight.w600,
            color: Colors.white,
          ),
        ),
        backgroundColor: Color(0xFF5B9BD5),
        elevation: 0,
        centerTitle: true,
      ),
      body: Obx(() {
        if (controller.isLoading.value) {
          return Center(child: CircularProgressIndicator());
        }

        if (controller.errorMessage.value.isNotEmpty) {
          return _buildErrorView();
        }

        if (controller.items.isEmpty) {
          return _buildEmptyView();
        }

        return RefreshIndicator(
          onRefresh: controller.loadData,
          child: ListView.builder(
            padding: EdgeInsets.all(16.w),
            itemCount: controller.items.length,
            itemBuilder: (context, index) {
              final item = controller.items[index];
              return _buildItemCard(item);
            },
          ),
        );
      }),
      floatingActionButton: FloatingActionButton(
        onPressed: _showAddDialog,
        backgroundColor: Color(0xFF5B9BD5),
        child: Icon(Icons.add, color: Colors.white),
      ),
    );
  }

  Widget _buildItemCard(DashboardModel item) {
    return Container(
      margin: EdgeInsets.only(bottom: 12.h),
      padding: EdgeInsets.all(16.w),
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12.r),
        boxShadow: [
          BoxShadow(
            color: Colors.black.withOpacity(0.05),
            blurRadius: 10,
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment: MainAxisAlignment.spaceBetween,
            children: [
              Expanded(
                child: Text(
                  item.data['title'] ?? '',
                  style: TextStyle(
                    fontSize: 18.sp,
                    fontWeight: FontWeight.w600,
                    color: Color(0xFF2C3E50),
                  ),
                ),
              ),
              IconButton(
                onPressed: () => _showDeleteDialog(item.id),
                icon: Icon(Icons.delete_outline, color: Colors.grey[400]),
              ),
            ],
          ),
          SizedBox(height: 8.h),
          Text(
            item.data['description'] ?? '',
            style: TextStyle(
              fontSize: 14.sp,
              color: Colors.grey[600],
            ),
          ),
          SizedBox(height: 8.h),
          Text(
            '创建时间:${_formatDate(item.createdAt)}',
            style: TextStyle(
              fontSize: 12.sp,
              color: Colors.grey[500],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildEmptyView() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            Icons.inbox_outlined,
            size: 64.sp,
            color: Colors.grey[400],
          ),
          SizedBox(height: 16.h),
          Text(
            '暂无数据',
            style: TextStyle(
              fontSize: 16.sp,
              color: Colors.grey[600],
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildErrorView() {
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Icon(
            Icons.error_outline,
            size: 64.sp,
            color: Colors.red[300],
          ),
          SizedBox(height: 16.h),
          Text(
            controller.errorMessage.value,
            style: TextStyle(
              fontSize: 16.sp,
              color: Colors.grey[600],
            ),
          ),
          SizedBox(height: 16.h),
          ElevatedButton(
            onPressed: controller.loadData,
            child: Text('重试'),
          ),
        ],
      ),
    );
  }

  void _showAddDialog() {
    Get.dialog(
      AlertDialog(
        title: Text('添加新项目'),
        content: Text('添加功能开发中...'),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: Text('取消'),
          ),
          TextButton(
            onPressed: () {
              Get.back();
              // 实现添加逻辑
            },
            child: Text('确定'),
          ),
        ],
      ),
    );
  }

  void _showDeleteDialog(String id) {
    Get.dialog(
      AlertDialog(
        title: Text('确认删除'),
        content: Text('确定要删除这个项目吗?'),
        actions: [
          TextButton(
            onPressed: () => Get.back(),
            child: Text('取消'),
          ),
          TextButton(
            onPressed: () {
              controller.deleteItem(id);
              Get.back();
            },
            child: Text('删除', style: TextStyle(color: Colors.red)),
          ),
        ],
      ),
    );
  }

  String _formatDate(DateTime date) {
    return '${date.year}-${date.month.toString().padLeft(2, '0')}-${date.day.toString().padLeft(2, '0')}';
  }
}

View实现了完整的用户界面,包括列表展示、空状态、错误处理和交互操作。我们使用了适老化的设计原则,确保界面清晰易用。

适老化设计要点

在主仪表板实现的设计中,我们特别注重适老化体验:

视觉设计优化

  • 使用18sp以上的大字体,确保老年用户能够清晰阅读
  • 采用高对比度配色方案,主要内容使用深色文字配白色背景
  • 重要信息使用醒目的颜色标识,如蓝色表示正常,红色表示警告
  • 图标设计简洁明了,配合文字说明,避免产生歧义

交互设计简化

  • 所有按钮尺寸不小于44x44dp,方便老年用户点击
  • 避免复杂的手势操作,所有功能都可以通过简单的点击完成
  • 提供明确的操作反馈,每个操作都有相应的提示信息
  • 支持操作撤销,降低误操作的影响

信息架构优化

  • 采用卡片式布局,信息分组清晰
  • 重要信息放在显著位置,次要信息适当隐藏
  • 使用渐进式信息展示,避免一次性呈现过多内容
  • 提供搜索和筛选功能,帮助用户快速找到所需信息

性能优化策略

为确保主仪表板实现的流畅运行,我们实施了多项性能优化措施:

数据加载优化

  • 使用分页加载机制,避免一次性加载大量数据
  • 实现数据缓存策略,减少不必要的网络请求
  • 采用懒加载方式,只在需要时才加载数据
  • 使用本地存储保存常用数据,提升访问速度

UI渲染优化

  • 使用ListView.builder进行列表渲染,支持大量数据展示
  • 合理使用Obx包装需要响应式更新的组件,避免不必要的重建
  • 图片使用缓存机制,减少重复加载
  • 避免在build方法中进行复杂计算

内存管理优化

  • 及时释放不需要的资源,避免内存泄漏
  • 使用弱引用处理大对象,降低内存压力
  • 定期清理缓存数据,保持应用轻量
  • 监控内存使用情况,及时发现和解决问题

通过这些优化措施,主仪表板实现能够在各种设备上保持良好的性能表现。

功能测试与验证

完成开发后,我们需要对主仪表板实现进行全面测试:

功能测试

  • 验证所有功能按钮的响应性和正确性
  • 测试数据的增删改查操作是否正常
  • 检查异常情况的处理是否合理
  • 验证数据持久化功能是否可靠

界面测试

  • 在不同屏幕尺寸的设备上测试界面显示效果
  • 验证字体大小和颜色对比度是否符合适老化标准
  • 检查布局在横竖屏切换时是否正常
  • 测试不同主题下的界面表现

性能测试

  • 测试大量数据加载时的性能表现
  • 验证内存使用是否在合理范围内
  • 检查应用响应速度是否满足要求
  • 测试长时间使用后的稳定性

用户体验测试

  • 邀请老年用户进行实际使用测试
  • 收集用户反馈,了解使用中的问题
  • 观察用户的操作习惯,优化交互流程
  • 根据测试结果进行迭代改进

通过系统性的测试,确保主仪表板实现的质量和用户体验。

总结

本文详细介绍了主仪表板实现的完整实现过程,从需求分析到技术实现,从界面设计到性能优化,涵盖了开发的各个环节。

通过合理的架构设计和适老化的用户体验优化,我们创建了一个功能完善、易于使用的主仪表板功能。这个实现不仅满足了老年用户的基本需求,还通过细致的设计提升了整体的使用体验。

在实际开发中,开发者可以根据具体需求对功能进行扩展和定制。建议持续关注用户反馈,不断优化和改进功能,确保应用能够真正帮助老年用户提升生活质量。

同时,我们也要注意数据安全和隐私保护,确保用户的个人信息得到妥善保管。在功能迭代过程中,始终将用户需求放在首位,打造真正有价值的智慧养老应用。


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

Logo

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

更多推荐