Flutter for OpenHarmony字典查询 App 全栈解析:从搜索交互到详情展示的完整实

Flutter for OpenHarmony字典查询 App 全栈解析:从搜索交互到详情展示的完整实

在移动应用开发中,工具类应用 (如字典、计算器、备忘录)是检验开发者对

UI/UX、状态管理和数据流理解的绝佳练兵场。本文将深入剖析一个基于 Flutter 构建的 中文词语字典查询 App

的完整代码,全面讲解其 搜索交互、历史记录、结果展示和详情页设计 四大核心模块的实现原理与最佳实践。


完整效果展示

一、整体架构:简洁而高效的单页面应用 (SPA)

该 App 采用经典的 单页面 + 路由跳转 架构:

  • DictionaryHomeScreen: 主页,负责搜索、结果显示和历史记录管理。
  • DictionaryDetailScreen: 详情页,展示单个词语的完整信息。

通过 Navigator.push 实现页面间的平滑过渡,符合 Material Design 规范。

dart 复制代码
// 跳转到详情页的核心代码
Navigator.push(
  context,
  MaterialPageRoute(
    builder: (context) => DictionaryDetailScreen(entry: entry),
  ),
);

这种分离关注点的设计,使得主页专注于 "发现" ,详情页专注于 "理解",职责清晰,易于维护。


二、主页 (DictionaryHomeScreen):智能搜索中枢

主页是用户与字典交互的第一入口,其核心功能围绕 搜索 展开。

2.1 状态管理:驱动 UI 变化的引擎

主页使用 StatefulWidget 管理四个关键状态:

状态变量 类型 作用
_searchController TextEditingController 绑定搜索框的输入内容
_searchResults List<DictionaryEntry> 存储当前搜索匹配的结果
_isSearching bool 标记是否处于搜索加载中
_searchHistory List<String> 记录用户的搜索历史

这些状态共同决定了 UI 的四种主要形态:

  1. 空闲态:显示提示语 "输入词语开始查询"。
  2. 加载态 :显示 CircularProgressIndicator
  3. 无结果态:显示 "未找到相关词语"。
  4. 结果列表态:展示匹配的词语卡片。

2.2 智能搜索逻辑:_performSearch 方法

这是整个 App 的 业务逻辑核心

dart 复制代码
void _performSearch(String query) {
  if (query.trim().isEmpty) {
    setState(() { _searchResults = []; });
    return;
  }

  // ... 设置 _isSearching 为 true

  Future.delayed(const Duration(milliseconds: 300), () {
    final results = _dictionaryData.where((entry) {
      return entry.word.contains(query) || // 匹配词语
             entry.pinyin.toLowerCase().contains(query.toLowerCase()) || // 匹配拼音
             entry.definition.contains(query); // 匹配释义
    }).toList();

    // ... 更新搜索历史

    setState(() {
      _searchResults = results;
      _isSearching = false;
    });
  });
}

关键亮点

  • 多维度匹配 :同时支持按 词语、拼音、释义 进行模糊搜索,极大提升了查词效率。
  • 模拟网络延迟 :使用 Future.delayed 模拟真实 API 请求的延迟,让 UI 反馈更真实。
  • 防抖优化 :虽然代码中未显式实现防抖(debounce),但 onChanged 直接触发搜索,在简单场景下可接受。对于复杂场景,建议加入防抖以避免频繁请求。

2.3 搜索历史:提升用户体验的贴心设计

搜索历史功能让用户能快速回溯之前的查询,是优秀 UX 的体现。

dart 复制代码
// 添加到历史
if (query.isNotEmpty && !_searchHistory.contains(query)) {
  _searchHistory.insert(0, query); // 最新查询放在最前面
  if (_searchHistory.length > 10) {
    _searchHistory.removeLast(); // 限制最多10条
  }
}

// 清除历史
_searchHistory.clear();

// 删除单条历史
_searchHistory.remove(term);

UI 实现

  • 使用 Wrap 布局展示历史词条,自动换行,适应不同屏幕。
  • 每个词条用 Chip 组件呈现,并带有删除按钮 (onDeleted),操作直观。

💡 条件渲染 :历史记录区域仅在 _searchController.text.isEmpty 时显示,避免与搜索结果冲突。

2.4 搜索栏与结果列表:精致的 UI 细节

  • 搜索栏
  • 圆角设计 (borderRadius: 12),内填充 (filled: true),视觉上更柔和。
  • 动态显示清除按钮 (suffixIcon),方便用户一键清空。
  • 微阴影 (BoxShadow) 提升层次感。
  • 结果列表项 (ListTile)
  • 主标题:词语本身,加粗大号字体。
  • 副标题:包含拼音和释义(截断两行),信息密度高。
  • 右侧图标chevron_right 明确指示可点击进入详情。
  • 容器 :使用 Card 组件,带圆角和阴影,形成独立的信息块。

三、详情页 (DictionaryDetailScreen):沉浸式学习体验

当用户点击某个词语后,会进入一个精心设计的详情页,提供全方位的语言学习支持。

3.1 数据传递:安全可靠的参数注入

详情页通过构造函数接收一个完整的 DictionaryEntry 对象。

dart 复制代码
class DictionaryDetailScreen extends StatelessWidget {
  final DictionaryEntry entry; // 接收的数据

  const DictionaryDetailScreen({super.key, required this.entry});
  // ...
}
  • required 关键字 :确保调用方必须传入 entry,避免空指针异常。
  • final 修饰:保证数据在页面生命周期内不可变,符合 Flutter 的声明式编程思想。

3.2 模块化布局:_buildSection 的复用艺术

详情页内容被清晰地划分为 词语信息、释义、例句、相关词语 四个模块。为了减少重复代码,作者巧妙地封装了一个通用的 _buildSection 方法。

dart 复制代码
Widget _buildSection(String title, IconData icon, Widget content) {
  return Column(
    crossAxisAlignment: CrossAxisAlignment.start,
    children: [
      Row(children: [Icon(icon), Text(title)]), // 统一的标题栏
      const SizedBox(height: 12),
      content, // 各模块自定义的内容
    ],
  );
}

优势

  • 一致性:所有模块拥有统一的标题样式和间距。
  • 可维护性:修改标题样式只需改动一处。
  • 可扩展性:未来新增模块(如"近义词"、"反义词")变得异常简单。

3.3 各模块设计亮点

  1. 词语信息区 (顶部 Card)

    • 超大字号 (fontSize: 48) 突出显示词语,营造视觉焦点。
    • 拼音 以次级字号展示,辅助发音。
    • 词性 (partOfSpeech) 用彩色标签 (Container) 高亮,信息一目了然。
  2. 释义区

    • 简洁明了,直接展示 definition 字段。
  3. 例句区

    • 引用样式 :使用引号 (") 和斜体 (fontStyle: FontStyle.italic) 模拟真实引用。
    • 背景色 :浅黄色 (Colors.amber[50]) 背景,与普通文本区分,提升可读性。
  4. 相关词语区

    • 头像式 Chip :每个 Chip 带有圆形头像 (CircleAvatar),头像内显示词语首字,设计新颖且节省空间。
    • 色彩搭配 :蓝色系 (Colors.blue[50]) 背景,与 App 主色调 (0xFF4A90E2) 呼应。
  5. 操作按钮区

    • 双按钮布局收藏分享 是字典类 App 的核心操作。
    • 视觉区分收藏 用红色系强调,"分享"用主蓝色系,符合用户心智模型。
    • 即时反馈 :点击后弹出 SnackBar,告知用户操作成功。

四、数据模型 (DictionaryEntry):结构化的基石

一个清晰的数据模型是构建健壮应用的前提。

dart 复制代码
class DictionaryEntry {
  final String word;           // 词语
  final String pinyin;         // 拼音
  final String partOfSpeech;   // 词性
  final String definition;     // 释义
  final String example;        // 例句
  final List<String> relatedWords; // 相关词语
}
  • final 字段 :保证对象创建后不可变,线程安全,也便于 Flutter 的 const 构造和性能优化。
  • 强类型:每个字段都有明确的类型,避免运行时错误。
  • 自解释性:字段命名清晰,无需额外注释即可理解其含义。

五、总结:一个教科书级的 Flutter 工具应用

这个字典 App 虽小,却五脏俱全,完美展示了 Flutter 开发的核心思想:

  1. 声明式 UI:UI 是状态的函数,状态改变,UI 自动更新。
  2. 组件化 :将复杂界面拆解为 Card, Chip, _buildSection 等可复用组件。
  3. 状态管理 :合理使用 StatefulWidget 管理局部状态,逻辑清晰。
  4. 用户体验至上:从搜索历史、加载指示器到详情页的精心排版,处处体现对用户的尊重。
  5. 代码规范 :使用 requiredfinal 等关键字,保证了代码的健壮性和可读性。

🌐 加入社区

欢迎加入 开源鸿蒙跨平台开发者社区 ,获取最新资源与技术支持:

👉 开源鸿蒙跨平台开发者社区


技术因分享而进步,生态因共建而繁荣

------ 晚霞的不甘 · 与您共赴鸿蒙跨平台开发之旅

bash 复制代码
import 'package:flutter/material.dart';

void main() {
  runApp(const DictionaryApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: '字典查询',
      theme: ThemeData(
        primaryColor: const Color(0xFF4A90E2),
        useMaterial3: true,
      ),
      home: const DictionaryHomeScreen(),
    );
  }
}

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

  @override
  State<DictionaryHomeScreen> createState() => _DictionaryHomeScreenState();
}

class _DictionaryHomeScreenState extends State<DictionaryHomeScreen> {
  final TextEditingController _searchController = TextEditingController();
  List<DictionaryEntry> _searchResults = [];
  bool _isSearching = false;
  List<String> _searchHistory = [];

  // 模拟字典数据
  final List<DictionaryEntry> _dictionaryData = [
    DictionaryEntry(
      word: '学习',
      pinyin: 'xué xí',
      partOfSpeech: '动词',
      definition: '通过阅读、听讲、研究、实践等途径获得知识或技能。',
      example: '我们要努力学习科学文化知识。',
      relatedWords: ['自学', '研习', '进修'],
    ),
    DictionaryEntry(
      word: '努力',
      pinyin: 'nǔ lì',
      partOfSpeech: '形容词',
      definition: '尽最大力量;尽一切可能。',
      example: '他工作非常努力。',
      relatedWords: ['勤奋', '刻苦', '尽力'],
    ),
    DictionaryEntry(
      word: '知识',
      pinyin: 'zhī shi',
      partOfSpeech: '名词',
      definition: '人们在认识世界、改造世界的过程中积累起来的经验。',
      example: '知识就是力量。',
      relatedWords: ['常识', '学识', '见识'],
    ),
    DictionaryEntry(
      word: '文化',
      pinyin: 'wén huà',
      partOfSpeech: '名词',
      definition: '人类在社会实践中所创造的物质财富和精神财富的总和。',
      example: '中华文明历史悠久,文化灿烂。',
      relatedWords: ['文明', '艺术', '传统'],
    ),
    DictionaryEntry(
      word: '创新',
      pinyin: 'chuàng xīn',
      partOfSpeech: '动词',
      definition: '抛开旧的,创造新的。',
      example: '科技创新推动了社会进步。',
      relatedWords: ['改革', '创造', '发明'],
    ),
    DictionaryEntry(
      word: '发展',
      pinyin: 'fā zhǎn',
      partOfSpeech: '动词',
      definition: '事物由小到大、由简到繁、由低级到高级的变化。',
      example: '经济持续健康发展。',
      relatedWords: ['进步', '增长', '扩展'],
    ),
    DictionaryEntry(
      word: '理想',
      pinyin: 'lǐ xiǎng',
      partOfSpeech: '名词',
      definition: '对未来事物的想象或希望。',
      example: '每个人都有自己的理想。',
      relatedWords: ['梦想', '抱负', '目标'],
    ),
    DictionaryEntry(
      word: '坚持',
      pinyin: 'jiān chí',
      partOfSpeech: '动词',
      definition: '坚决保持、维持或进行。',
      example: '坚持就是胜利。',
      relatedWords: ['坚守', '执着', '持续'],
    ),
    DictionaryEntry(
      word: '成功',
      pinyin: 'chéng gōng',
      partOfSpeech: '名词',
      definition: '达到预期的目的或结果。',
      example: '经过不懈努力,他终于成功了。',
      relatedWords: ['胜利', '成就', '收获'],
    ),
    DictionaryEntry(
      word: '友谊',
      pinyin: 'yǒu yì',
      partOfSpeech: '名词',
      definition: '朋友之间的交情。',
      example: '友谊是人生最宝贵的财富之一。',
      relatedWords: ['友情', '交情', '伙伴'],
    ),
  ];

  void _performSearch(String query) {
    if (query.trim().isEmpty) {
      setState(() {
        _searchResults = [];
      });
      return;
    }

    setState(() {
      _isSearching = true;
    });

    // 模拟搜索延迟
    Future.delayed(const Duration(milliseconds: 300), () {
      final results = _dictionaryData.where((entry) {
        return entry.word.contains(query) ||
            entry.pinyin.toLowerCase().contains(query.toLowerCase()) ||
            entry.definition.contains(query);
      }).toList();

      // 添加到搜索历史
      if (query.isNotEmpty && !_searchHistory.contains(query)) {
        setState(() {
          _searchHistory.insert(0, query);
          if (_searchHistory.length > 10) {
            _searchHistory.removeLast();
          }
        });
      }

      setState(() {
        _searchResults = results;
        _isSearching = false;
      });
    });
  }

  void _showDetailScreen(DictionaryEntry entry) {
    Navigator.push(
      context,
      MaterialPageRoute(
          builder: (context) => DictionaryDetailScreen(entry: entry)),
    );
  }

  @override
  void dispose() {
    _searchController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('字典查询'),
        backgroundColor: const Color(0xFF4A90E2),
        elevation: 0,
      ),
      body: Column(
        children: [
          // 搜索栏
          Container(
            padding: const EdgeInsets.all(16),
            decoration: BoxDecoration(
              color: Colors.white,
              boxShadow: [
                BoxShadow(
                  color: Colors.grey.withOpacity(0.1),
                  blurRadius: 4,
                  offset: const Offset(0, 2),
                ),
              ],
            ),
            child: TextField(
              controller: _searchController,
              decoration: InputDecoration(
                hintText: '输入词语或文章进行查询...',
                prefixIcon: const Icon(Icons.search),
                suffixIcon: _searchController.text.isNotEmpty
                    ? IconButton(
                        icon: const Icon(Icons.clear),
                        onPressed: () {
                          _searchController.clear();
                          setState(() {
                            _searchResults = [];
                          });
                        },
                      )
                    : null,
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(12),
                  borderSide: BorderSide.none,
                ),
                filled: true,
                fillColor: Colors.grey[100],
              ),
              onChanged: (value) {
                _performSearch(value);
              },
              onSubmitted: (value) {
                _performSearch(value);
              },
            ),
          ),

          // 搜索结果
          Expanded(
            child: _isSearching
                ? const Center(
                    child: CircularProgressIndicator(),
                  )
                : _searchResults.isEmpty
                    ? Column(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Icon(
                            Icons.book_outlined,
                            size: 100,
                            color: Colors.grey[300],
                          ),
                          const SizedBox(height: 20),
                          Text(
                            _searchController.text.isEmpty
                                ? '输入词语开始查询'
                                : '未找到相关词语',
                            style: TextStyle(
                              fontSize: 18,
                              color: Colors.grey[600],
                            ),
                          ),
                        ],
                      )
                    : ListView.builder(
                        padding: const EdgeInsets.all(16),
                        itemCount: _searchResults.length,
                        itemBuilder: (context, index) {
                          final entry = _searchResults[index];
                          return Card(
                            elevation: 2,
                            margin: const EdgeInsets.only(bottom: 12),
                            shape: RoundedRectangleBorder(
                              borderRadius: BorderRadius.circular(12),
                            ),
                            child: ListTile(
                              contentPadding: const EdgeInsets.all(16),
                              title: Text(
                                entry.word,
                                style: const TextStyle(
                                  fontSize: 20,
                                  fontWeight: FontWeight.bold,
                                ),
                              ),
                              subtitle: Column(
                                crossAxisAlignment: CrossAxisAlignment.start,
                                children: [
                                  const SizedBox(height: 4),
                                  Text(
                                    entry.pinyin,
                                    style: TextStyle(
                                      fontSize: 14,
                                      color: Colors.grey[600],
                                    ),
                                  ),
                                  const SizedBox(height: 8),
                                  Text(
                                    entry.definition,
                                    maxLines: 2,
                                    overflow: TextOverflow.ellipsis,
                                    style: TextStyle(
                                      fontSize: 14,
                                      color: Colors.grey[800],
                                    ),
                                  ),
                                ],
                              ),
                              trailing: const Icon(
                                Icons.chevron_right,
                                color: Colors.grey,
                              ),
                              onTap: () => _showDetailScreen(entry),
                            ),
                          );
                        },
                      ),
          ),

          // 搜索历史
          if (_searchHistory.isNotEmpty && _searchController.text.isEmpty)
            Container(
              padding: const EdgeInsets.all(16),
              decoration: BoxDecoration(
                color: Colors.white,
                border: Border(
                  top: BorderSide(color: Colors.grey[200]!),
                ),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceBetween,
                    children: [
                      const Text(
                        '搜索历史',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      TextButton(
                        onPressed: () {
                          setState(() {
                            _searchHistory.clear();
                          });
                        },
                        child: const Text('清除'),
                      ),
                    ],
                  ),
                  const SizedBox(height: 8),
                  Wrap(
                    spacing: 8,
                    runSpacing: 8,
                    children: _searchHistory.map((term) {
                      return Chip(
                        label: Text(term),
                        onDeleted: () {
                          setState(() {
                            _searchHistory.remove(term);
                          });
                        },
                        deleteIcon: const Icon(Icons.close, size: 16),
                        backgroundColor: Colors.grey[100],
                        padding: const EdgeInsets.symmetric(horizontal: 8),
                      );
                    }).toList(),
                  ),
                ],
              ),
            ),
        ],
      ),
    );
  }
}

// 词语详情页面
class DictionaryDetailScreen extends StatelessWidget {
  final DictionaryEntry entry;

  const DictionaryDetailScreen({super.key, required this.entry});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(entry.word),
        backgroundColor: const Color(0xFF4A90E2),
        elevation: 0,
      ),
      body: SingleChildScrollView(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // 词语和拼音
            Card(
              elevation: 4,
              shape: RoundedRectangleBorder(
                borderRadius: BorderRadius.circular(16),
              ),
              child: Padding(
                padding: const EdgeInsets.all(24),
                child: Column(
                  children: [
                    Text(
                      entry.word,
                      style: const TextStyle(
                        fontSize: 48,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    const SizedBox(height: 12),
                    Text(
                      entry.pinyin,
                      style: TextStyle(
                        fontSize: 24,
                        color: Colors.grey[600],
                      ),
                    ),
                    const SizedBox(height: 12),
                    Container(
                      padding: const EdgeInsets.symmetric(
                        horizontal: 16,
                        vertical: 8,
                      ),
                      decoration: BoxDecoration(
                        color: const Color(0xFF4A90E2).withOpacity(0.1),
                        borderRadius: BorderRadius.circular(20),
                      ),
                      child: Text(
                        entry.partOfSpeech,
                        style: const TextStyle(
                          color: Color(0xFF4A90E2),
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 24),

            // 释义
            _buildSection(
              '释义',
              Icons.info_outline,
              Text(
                entry.definition,
                style: const TextStyle(fontSize: 16, height: 1.6),
              ),
            ),

            const SizedBox(height: 24),

            // 例句
            _buildSection(
              '例句',
              Icons.format_quote,
              Container(
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Colors.amber[50],
                  borderRadius: BorderRadius.circular(12),
                  border: Border.all(color: Colors.amber[200]!),
                ),
                child: Row(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    const Text(
                      '"',
                      style: TextStyle(
                        fontSize: 32,
                        color: Colors.amber,
                        fontWeight: FontWeight.bold,
                      ),
                    ),
                    Expanded(
                      child: Text(
                        entry.example,
                        style: const TextStyle(
                          fontSize: 16,
                          fontStyle: FontStyle.italic,
                          height: 1.6,
                        ),
                      ),
                    ),
                  ],
                ),
              ),
            ),

            const SizedBox(height: 24),

            // 相关词语
            _buildSection(
              '相关词语',
              Icons.link,
              Wrap(
                spacing: 12,
                runSpacing: 12,
                children: entry.relatedWords.map((word) {
                  return Chip(
                    label: Text(word),
                    avatar: CircleAvatar(
                      backgroundColor: const Color(0xFF4A90E2),
                      child: Text(
                        word[0],
                        style: const TextStyle(
                          color: Colors.white,
                          fontSize: 12,
                        ),
                      ),
                    ),
                    backgroundColor: Colors.blue[50],
                    padding:
                        const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
                  );
                }).toList(),
              ),
            ),

            const SizedBox(height: 32),

            // 操作按钮
            Row(
              children: [
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(
                          content: Text('已收藏'),
                          duration: Duration(seconds: 1),
                        ),
                      );
                    },
                    icon: const Icon(Icons.favorite_border),
                    label: const Text('收藏'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.red[50],
                      foregroundColor: Colors.red,
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                    ),
                  ),
                ),
                const SizedBox(width: 16),
                Expanded(
                  child: ElevatedButton.icon(
                    onPressed: () {
                      ScaffoldMessenger.of(context).showSnackBar(
                        const SnackBar(
                          content: Text('已分享'),
                          duration: Duration(seconds: 1),
                        ),
                      );
                    },
                    icon: const Icon(Icons.share),
                    label: const Text('分享'),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: const Color(0xFF4A90E2),
                      foregroundColor: Colors.white,
                      padding: const EdgeInsets.symmetric(vertical: 16),
                      shape: RoundedRectangleBorder(
                        borderRadius: BorderRadius.circular(12),
                      ),
                    ),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  Widget _buildSection(String title, IconData icon, Widget content) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Row(
          children: [
            Icon(icon, color: const Color(0xFF4A90E2)),
            const SizedBox(width: 8),
            Text(
              title,
              style: const TextStyle(
                fontSize: 18,
                fontWeight: FontWeight.bold,
              ),
            ),
          ],
        ),
        const SizedBox(height: 12),
        content,
      ],
    );
  }
}

// 字典条目数据模型
class DictionaryEntry {
  final String word;
  final String pinyin;
  final String partOfSpeech;
  final String definition;
  final String example;
  final List<String> relatedWords;

  DictionaryEntry({
    required this.word,
    required this.pinyin,
    required this.partOfSpeech,
    required this.definition,
    required this.example,
    required this.relatedWords,
  });
}
相关推荐
时光慢煮2 小时前
从进度可视化出发:基于 Flutter × OpenHarmony 的驾照学习助手实践
学习·flutter·华为·开源·openharmony
子春一2 小时前
Flutter for OpenHarmony:构建一个工业级 Flutter 计算器,深入解析表达式解析、状态管理与 Material 3 交互设计
flutter·交互
kirk_wang2 小时前
Flutter艺术探索-设计模式在Flutter中的应用:单例、工厂、观察者
flutter·移动开发·flutter教程·移动开发教程
2601_949847752 小时前
Flutter for OpenHarmony 剧本杀组队App实战:关于我们页面实现
开发语言·javascript·flutter
子春一2 小时前
Flutter for OpenHarmony:构建一个交互式 Flutter RGB 颜色选择器,深入解析状态驱动 UI、HEX 转换与无障碍色彩对比
flutter·ui
数据知道2 小时前
PostgreSQL 实战:如何优雅高效地进行全文检索
大数据·数据库·postgresql·全文检索
IT陈图图2 小时前
Flutter × OpenHarmony 实战:优雅构建确认对话框的组件化方案
开发语言·javascript·flutter
雨季6662 小时前
Flutter 三端应用实战:OpenHarmony 简易文本末尾字符查看器开发指南
开发语言·javascript·flutter
chen_song_2 小时前
Agent 经典范式构建之 ReAct (Reasoning and Acting): 一种将“思考”和“行动”紧密结合的范式,让智能体边想边做,动态调整
前端·react.js·前端框架