在这里插入图片描述

用药知识功能为用户提供了丰富的药品使用和储存知识,帮助用户安全合理地使用药品。本文将介绍如何在Flutter for OpenHarmony应用中实现用药知识功能,包括知识列表展示、分类管理、详情页面跳转等特性。

功能设计思路

用药知识页面包含多个知识主题,每个主题都有标题、副标题、图标和详细内容。主题涵盖药品储存、用药安全、药物相互作用、儿童用药、老年人用药、过期药品处理等方面。用户点击某个主题可以查看详细的知识内容,内容使用Markdown格式展示,层次分明。

页面采用列表布局,每个知识主题是一个卡片。卡片左侧是带背景色的图标,中间是标题和副标题,右侧是右箭头。不同主题使用不同的颜色和图标,让页面更加生动。

页面结构实现

页面使用无状态组件,知识数据直接定义在build方法中:


Widget build(BuildContext context) {
  final knowledgeList = [
    {
      'title': '药品储存指南',
      'subtitle': '正确储存药品,保证药效',
      'icon': Icons.inventory_2,
      'color': Colors.blue,
      'content': '''
# 药品储存指南

## 一般储存原则
1. **避光保存**:大多数药品应避免阳光直射
2. **阴凉干燥**:保持储存环境干燥,避免潮湿
3. **密封保存**:开封后注意密封,防止受潮变质

## 温度要求
- **常温保存**:10-30°C
- **阴凉保存**:不超过20°C
- **冷藏保存**:2-8°C(如胰岛素、部分眼药水)
''',
    },
    // 更多知识主题...
  ];

  return Scaffold(
    appBar: AppBar(title: const Text('用药知识')),
    body: ListView.builder(
      padding: EdgeInsets.all(16.w),
      itemCount: knowledgeList.length,
      itemBuilder: (context, index) {
        final item = knowledgeList[index];
        return GestureDetector(
          onTap: () => Get.to(() => KnowledgeDetailScreen(
                title: item['title'] as String,
                content: item['content'] as String,
              )),
          child: 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: const Offset(0, 2)),
              ],
            ),
            child: Row(
              children: [
                Container(
                  width: 48.w,
                  height: 48.w,
                  decoration: BoxDecoration(
                    color: (item['color'] as Color).withOpacity(0.1),
                    borderRadius: BorderRadius.circular(12.r),
                  ),
                  child: Icon(item['icon'] as IconData, color: item['color'] as Color, size: 24.sp),
                ),
                SizedBox(width: 12.w),
                Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      Text(item['title'] as String,
                          style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
                      SizedBox(height: 4.h),
                      Text(item['subtitle'] as String,
                          style: TextStyle(fontSize: 12.sp, color: Colors.grey[600])),
                    ],
                  ),
                ),
                const Icon(Icons.chevron_right, color: Colors.grey),
              ],
            ),
          ),
        );
      },
    ),
  );
}

知识数据使用Map列表存储,每个Map包含标题、副标题、图标、颜色和内容。使用ListView.builder构建列表,每个列表项是一个可点击的卡片。点击卡片会跳转到知识详情页面,传递标题和内容参数。

知识卡片设计

每个知识主题的卡片采用统一的设计风格:

child: 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: const Offset(0, 2)),
    ],
  ),
  child: Row(
    children: [
      Container(
        width: 48.w,
        height: 48.w,
        decoration: BoxDecoration(
          color: (item['color'] as Color).withOpacity(0.1),
          borderRadius: BorderRadius.circular(12.r),
        ),
        child: Icon(item['icon'] as IconData, color: item['color'] as Color, size: 24.sp),
      ),
      SizedBox(width: 12.w),
      Expanded(
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(item['title'] as String,
                style: TextStyle(fontSize: 16.sp, fontWeight: FontWeight.bold)),
            SizedBox(height: 4.h),
            Text(item['subtitle'] as String,
                style: TextStyle(fontSize: 12.sp, color: Colors.grey[600])),
          ],
        ),
      ),
      const Icon(Icons.chevron_right, color: Colors.grey),
    ],
  ),
),

卡片使用白色背景和浅色阴影,视觉上清爽舒适。左侧图标使用半透明背景色,与图标颜色相呼应。中间是标题和副标题的垂直布局,右侧是灰色右箭头提示可点击。

知识内容组织

知识内容使用Markdown格式编写,层次分明:

'content': '''
# 药品储存指南

## 一般储存原则
1. **避光保存**:大多数药品应避免阳光直射
2. **阴凉干燥**:保持储存环境干燥,避免潮湿
3. **密封保存**:开封后注意密封,防止受潮变质

## 温度要求
- **常温保存**:10-30°C
- **阴凉保存**:不超过20°C
- **冷藏保存**:2-8°C(如胰岛素、部分眼药水)

## 特殊药品储存
- **液体药品**:开封后注意有效期,通常较短
- **栓剂**:夏季建议冷藏保存
- **喷雾剂**:避免高温和阳光直射
''',

内容使用一级标题、二级标题、列表、加粗等Markdown语法,让信息结构清晰。每个主题都包含多个小节,内容详实专业。这种格式化的内容在详情页面中会被正确渲染,阅读体验良好。

多主题设计

页面包含六个知识主题,每个主题使用不同的颜色和图标:

{
  'title': '药品储存指南',
  'subtitle': '正确储存药品,保证药效',
  'icon': Icons.inventory_2,
  'color': Colors.blue,
},
{
  'title': '用药安全须知',
  'subtitle': '安全用药,健康生活',
  'icon': Icons.security,
  'color': Colors.green,
},
{
  'title': '常见药物相互作用',
  'subtitle': '了解药物配伍禁忌',
  'icon': Icons.warning,
  'color': Colors.orange,
},
{
  'title': '儿童用药指南',
  'subtitle': '儿童安全用药知识',
  'icon': Icons.child_care,
  'color': Colors.pink,
},
{
  'title': '老年人用药注意',
  'subtitle': '老年人安全用药要点',
  'icon': Icons.elderly,
  'color': Colors.purple,
},
{
  'title': '过期药品处理',
  'subtitle': '正确处理过期药品',
  'icon': Icons.delete_forever,
  'color': Colors.red,
},

药品储存用蓝色和库存图标,用药安全用绿色和安全图标,药物相互作用用橙色和警告图标,儿童用药用粉色和儿童图标,老年人用药用紫色和老年人图标,过期药品处理用红色和删除图标。这种设计让每个主题都有鲜明的视觉特征,用户可以快速识别。

页面跳转

点击知识卡片会跳转到详情页面:

return GestureDetector(
  onTap: () => Get.to(() => KnowledgeDetailScreen(
        title: item['title'] as String,
        content: item['content'] as String,
      )),
  child: Container(
    // 卡片内容
  ),
);

使用GestureDetector包裹卡片,点击时通过Get.to跳转到详情页面。详情页面接收标题和内容两个参数,使用Markdown渲染器展示内容。这种设计让知识的查看体验流畅自然。

技术要点

数据驱动:知识数据使用Map列表存储,通过ListView.builder动态构建UI。这种设计让添加新知识主题变得非常简单,只需在列表中添加新的Map即可。

Markdown格式:知识内容使用Markdown格式编写,支持标题、列表、加粗等语法。在详情页面中使用Markdown渲染器展示,让内容层次分明,阅读体验良好。

视觉设计:每个主题使用不同的颜色和图标,让页面更加生动。图标背景使用半透明颜色,与图标颜色相呼应,视觉效果统一协调。

交互设计:卡片采用可点击设计,右侧有右箭头提示。点击后跳转到详情页面,交互流畅自然。用户可以方便地浏览和学习各类用药知识。

知识详情页面实现

知识详情页面使用Markdown渲染器展示内容:

class KnowledgeDetailScreen extends StatelessWidget {
  final String title;
  final String content;

  const KnowledgeDetailScreen({
    super.key,
    required this.title,
    required this.content,
  });

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text(title)),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.w),
        child: MarkdownBody(
          data: content,
          styleSheet: MarkdownStyleSheet(
            h1: TextStyle(fontSize: 22.sp, fontWeight: FontWeight.bold),
            h2: TextStyle(fontSize: 18.sp, fontWeight: FontWeight.bold),
            p: TextStyle(fontSize: 14.sp, height: 1.6),
            listBullet: TextStyle(fontSize: 14.sp),
          ),
        ),
      ),
    );
  }
}

详情页面接收标题和内容两个参数。使用flutter_markdown库的MarkdownBody组件渲染Markdown内容。通过MarkdownStyleSheet自定义样式,设置标题大小、段落行高等属性。SingleChildScrollView让长内容可以滚动查看。

搜索功能实现

为知识列表添加搜索功能,方便用户快速找到需要的内容:

class _MedicineKnowledgeScreenState extends State<MedicineKnowledgeScreen> {
  String _searchQuery = '';
  final TextEditingController _searchController = TextEditingController();

  List<Map<String, dynamic>> _filterKnowledge(List<Map<String, dynamic>> list) {
    if (_searchQuery.isEmpty) return list;
    return list.where((item) {
      final title = item['title'] as String;
      final subtitle = item['subtitle'] as String;
      final content = item['content'] as String;
      return title.contains(_searchQuery) ||
          subtitle.contains(_searchQuery) ||
          content.contains(_searchQuery);
    }).toList();
  }

  
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('用药知识'),
        bottom: PreferredSize(
          preferredSize: Size.fromHeight(56.h),
          child: Padding(
            padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 8.h),
            child: TextField(
              controller: _searchController,
              decoration: InputDecoration(
                hintText: '搜索知识...',
                prefixIcon: const Icon(Icons.search),
                filled: true,
                fillColor: Colors.white,
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(8.r),
                  borderSide: BorderSide.none,
                ),
              ),
              onChanged: (v) => setState(() => _searchQuery = v),
            ),
          ),
        ),
      ),
      body: ListView.builder(
        padding: EdgeInsets.all(16.w),
        itemCount: _filterKnowledge(knowledgeList).length,
        itemBuilder: (context, index) {
          final item = _filterKnowledge(knowledgeList)[index];
          return _buildKnowledgeCard(item);
        },
      ),
    );
  }
}

搜索框放置在AppBar的bottom区域,始终可见。用户输入时实时过滤知识列表,搜索范围包括标题、副标题和内容。这种设计让用户可以快速定位到需要的知识主题。

收藏功能实现

为知识添加收藏功能,方便用户保存常用内容:

Set<String> _favorites = {};

Widget _buildKnowledgeCard(Map<String, dynamic> item) {
  final title = item['title'] as String;
  final isFavorite = _favorites.contains(title);

  return GestureDetector(
    onTap: () => Get.to(() => KnowledgeDetailScreen(
          title: title,
          content: item['content'] as String,
        )),
    child: 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: const Offset(0, 2),
          ),
        ],
      ),
      child: Row(
        children: [
          // 图标和内容...
          IconButton(
            icon: Icon(
              isFavorite ? Icons.star : Icons.star_border,
              color: isFavorite ? Colors.amber : Colors.grey,
            ),
            onPressed: () {
              setState(() {
                if (isFavorite) {
                  _favorites.remove(title);
                } else {
                  _favorites.add(title);
                }
              });
            },
          ),
        ],
      ),
    ),
  );
}

使用Set存储收藏的知识标题。卡片右侧添加收藏按钮,点击切换收藏状态。已收藏的知识显示黄色实心星星,未收藏的显示灰色空心星星。收藏状态可以持久化存储到本地,下次打开应用时恢复。

阅读进度追踪

追踪用户的阅读进度,显示已读和未读状态:

Set<String> _readItems = {};

Widget _buildReadIndicator(String title) {
  final isRead = _readItems.contains(title);
  return Container(
    width: 8.w,
    height: 8.w,
    decoration: BoxDecoration(
      color: isRead ? Colors.green : Colors.orange,
      shape: BoxShape.circle,
    ),
  );
}

void _markAsRead(String title) {
  setState(() => _readItems.add(title));
  _saveReadProgress();
}

使用小圆点指示阅读状态,绿色表示已读,橙色表示未读。当用户进入详情页面时,自动标记为已读。阅读进度保存到本地存储,用户可以清楚地看到哪些知识还没有学习。

分类筛选功能

添加分类筛选,让用户可以按类别查看知识:

String _selectedCategory = '全部';
final List<String> _categories = ['全部', '储存', '安全', '特殊人群', '处理'];

Widget _buildCategoryFilter() {
  return SizedBox(
    height: 40.h,
    child: ListView.builder(
      scrollDirection: Axis.horizontal,
      padding: EdgeInsets.symmetric(horizontal: 16.w),
      itemCount: _categories.length,
      itemBuilder: (context, index) {
        final category = _categories[index];
        final isSelected = category == _selectedCategory;
        return GestureDetector(
          onTap: () => setState(() => _selectedCategory = category),
          child: Container(
            margin: EdgeInsets.only(right: 8.w),
            padding: EdgeInsets.symmetric(horizontal: 16.w),
            decoration: BoxDecoration(
              color: isSelected ? const Color(0xFF00897B) : Colors.grey[200],
              borderRadius: BorderRadius.circular(20.r),
            ),
            alignment: Alignment.center,
            child: Text(
              category,
              style: TextStyle(
                color: isSelected ? Colors.white : Colors.grey[700],
                fontSize: 14.sp,
              ),
            ),
          ),
        );
      },
    ),
  );
}

分类筛选使用水平滚动的标签列表。选中的分类使用主题色背景和白色文字,未选中的使用灰色背景。点击分类标签会筛选出对应类别的知识,方便用户按需学习。

字体大小调节

为详情页面添加字体大小调节功能,适应不同用户的阅读习惯:

double _fontSize = 14.0;

Widget _buildFontSizeControl() {
  return Row(
    mainAxisAlignment: MainAxisAlignment.center,
    children: [
      IconButton(
        icon: const Icon(Icons.text_decrease),
        onPressed: () {
          if (_fontSize > 12) {
            setState(() => _fontSize -= 2);
          }
        },
      ),
      Text('字体大小', style: TextStyle(fontSize: 14.sp)),
      IconButton(
        icon: const Icon(Icons.text_increase),
        onPressed: () {
          if (_fontSize < 20) {
            setState(() => _fontSize += 2);
          }
        },
      ),
    ],
  );
}

字体大小调节控件放在详情页面顶部或底部。用户可以点击加减按钮调整字体大小,范围限制在12-20之间。字体大小设置可以保存到本地,下次打开时自动应用。这个功能对老年用户特别友好。

总结

用药知识功能通过丰富的知识内容和友好的交互设计,为用户提供了实用的用药指导。从药品储存到用药安全,从特殊人群用药到药品处理,涵盖了用药的各个方面。这种知识普及功能让应用不仅是管理工具,更是健康教育平台,帮助用户建立正确的用药观念。


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

Logo

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

更多推荐