Flutter for OpenHarmony从基础到惊艳:深度解析新版 AI 聊天应用的三大跃迁

在上一篇《打造你的第一个 Flutter
聊天机器人》中,我们实现了一个功能完整但界面朴素的聊天原型。而今天展示的全新升级版,则是一次从“能用”到“令人惊艳”的质变——它不仅保留了核心交互逻辑,更在视觉设计、用户体验与情感化细节上实现了三大维度的全面跃迁。本文将带你逐层拆解这场华丽进化。


完整效果展示
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、主页革命:从空白列表到沉浸式欢迎中心

1. 告别冷启动:精心设计的欢迎屏

旧版应用打开即为空白聊天区,用户需自行输入首条消息。新版则引入了完整的欢迎界面_buildWelcomeScreen):

  • 中央 AI 头像:120×120 的渐变圆形容器 + Icons.smart_toy 图标,配合深色背景下的柔和阴影,营造出“智能体正在待命”的仪式感;
  • 引导性副标题:“随时为你解答问题” —— 简洁传递产品价值;
  • 快捷提示词卡片:4 个高频场景(写诗、AI 介绍、故事、编程学习)以可点击标签形式呈现,大幅降低用户首次交互门槛

💡 设计心理学:用户面对空白输入框常感迷茫,而预设问题如同“对话脚手架”,引导用户快速进入状态。

2. 动态切换机制

通过条件判断实现智能视图切换:

Expanded(
  child: _messages.isEmpty 
      ? _buildWelcomeScreen() 
      : ListView(...),
)

在这里插入图片描述

  • 首次打开 → 显示欢迎屏;
  • 发送任意消息后 → 自动切换为聊天列表;
  • 无冗余状态管理,代码简洁高效。

二、聊天体验升级:流式输出与时间戳的沉浸感

1. 模拟真实打字:流式响应(Streaming Response)

这是本次最核心的技术亮点!旧版一次性显示完整回复,新版则通过 _startStreamingResponse 实现逐字符输出

Timer.periodic(Duration(milliseconds: 30), (timer) {
  setState(() { _currentStreamingText = fullText.substring(0, currentIndex + 1); });
  currentIndex++;
});
  • 每 30ms 输出一个字符,模拟人类打字速度;
  • 用户可实时看到 AI “思考并书写” 的过程,显著提升对话真实感与期待感

2. 动态闪烁光标:细节中的生命力

在流式输出时,气泡末尾会显示一个自动闪烁的紫色光标

TweenAnimationBuilder<double>(
  tween: Tween(begin: 0.0, end: 1.0),
  duration: Duration(milliseconds: 500),
  builder: (context, value, child) => Opacity(opacity: value, ...),
)

在这里插入图片描述

  • 光标颜色 #6C63FF 与主题色一致;
  • 500ms 周期淡入淡出,模仿终端输入效果;
  • 微小动画赋予 AI “生命感”,暗示“我还在输入”。

3. 精准时间戳:专业级对话记录

每条消息下方新增发送时间:

final timeStr = '${timestamp.hour}:${timestamp.minute}';
Text(timeStr, style: TextStyle(color: Colors.grey[600], fontSize: 11));
  • 时间显示在气泡外侧,避免干扰正文;
  • 用户消息靠右对齐时间,AI 消息靠左,布局逻辑严谨
  • 为未来实现“消息按天分组”奠定基础。

三、视觉语言重塑:深空紫调的专业美学

1. 全新色彩系统

元素 旧版 新版
主题色 Colors.blue #6C63FF(科技紫)
背景色 #121212 #0A0E27(深空蓝黑)
卡片色 #1A1F3A(带蓝调灰)
  • #6C63FF 是 Figma 社区流行的“赛博紫”,兼具科技感与亲和力;
  • #0A0E27 比纯黑更柔和,减少 OLED 屏幕的视觉疲劳;
  • 整体配色灵感源自 VS Code 深色主题,契合开发者审美。

2. 消息气泡全面进化

  • 用户气泡:采用 LinearGradient([#6C63FF, #9B8FFF]) 渐变填充,取代单一蓝色;
  • AI 气泡:使用 #1A1F3A 卡片色 + 微阴影,质感如磨砂玻璃;
  • 圆角优化:从 18 提升至 20,更符合 Material 3 圆润趋势;
  • 头像统一:双方均使用 Icons.psychology(大脑图标),强化“智能助手”定位。

3. 输入区域精致打磨

  • 聚焦边框:获得焦点时显示 2px 紫色边框,反馈清晰;
  • 发送按钮:改为渐变背景 + 阴影,点击区域扩大为 InkWell
  • 表情按钮占位:左侧预留 emoji_emotions_outlined 按钮,为未来扩展留接口;
  • 安全区域适配:包裹 SafeArea,确保 iPhone 刘海屏下正常显示。

四、内容深度增强:千问风格的专业回复

新版 _getAIResponse 返回的内容长度与结构复杂度大幅提升:

  • 多段落结构:使用 \n\n 分隔逻辑段落(如诗歌、故事、学习建议);
  • 列表化表达:关键信息以 1. 2. 3. 列出,提升可读性;
  • 场景化结尾:每条回复均以鼓励性语句收尾(“希望你喜欢!”、“加油!”);
  • 上下文感知:对“如何…”类问题提供方法论框架,而非泛泛而谈。

示例:当用户问“如何学习编程”,AI 不仅列出步骤,还推荐具体平台(LeetCode、GitHub),实用性远超普通聊天机器人


五、工程细节:健壮性与性能优化

  1. 内存安全 所有异步操作前检查 if (!mounted) return;,避免 setState 在组件销毁后调用。

  2. 滚动平滑 _scrollToBottom 使用 Future.delayed + animateTo,确保新消息出现后才滚动,杜绝跳闪

  3. 资源释放 dispose() 中清理 TimerScrollControllerTextEditingController防止内存泄漏

  4. 文本处理 输入时自动 trim() 并过滤空消息,提升健壮性。


结语:小应用,大思考

这个看似简单的聊天应用,实则凝聚了现代 UI/UX 设计的诸多精髓

  • 情感化设计:闪烁光标、流式输出、欢迎引导 —— 让机器有了温度;
  • 专业级视觉:深空紫配色、精准间距、光影层次 —— 传递品质感;
  • 用户为中心:降低首次使用门槛、提供结构化信息、优化交互反馈 —— 尊重用户时间。

🌐 加入社区

欢迎加入 开源鸿蒙跨平台开发者社区,获取最新资源与技术支持:
👉 开源鸿蒙跨平台开发者社区
完整代码展示

import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'AI 智能助手',
      theme: ThemeData(
        primaryColor: const Color(0xFF6C63FF),
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF6C63FF),
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
        scaffoldBackgroundColor: const Color(0xFF0A0E27),
        cardColor: const Color(0xFF1A1F3A),
      ),
      home: const ChatScreen(),
      debugShowCheckedModeBanner: false,
    );
  }
}

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

  @override
  State<ChatScreen> createState() => _ChatScreenState();
}

class _ChatScreenState extends State<ChatScreen> {
  // 消息列表 - 支持流式输出
  final List<Map<String, dynamic>> _messages = [];
  final TextEditingController _controller = TextEditingController();
  bool _isTyping = false; // AI 是否正在思考
  Timer? _responseTimer; // 用于定时器管理
  final ScrollController _scrollController = ScrollController();
  String _currentStreamingText = ''; // 当前流式输出的文本

  // 欢迎提示词
  final List<String> _welcomePrompts = [
    '帮我写一首诗',
    '介绍一下人工智能',
    '讲个有趣的故事',
    '如何学习编程',
  ];

  // 发送消息
  void _sendMessage() {
    if (_controller.text.trim().isEmpty || _isTyping) return;

    final String text = _controller.text.trim();
    _controller.clear();

    // 添加用户消息
    setState(() {
      _messages.add({
        'text': text,
        'isUser': true,
        'timestamp': DateTime.now(),
      });
      _isTyping = true;
      _currentStreamingText = '';
    });

    // 滚动到底部
    _scrollToBottom();

    // 模拟 AI 思考后开始流式输出
    _responseTimer = Timer(const Duration(milliseconds: 800), () {
      if (!mounted) return;
      final String fullResponse = _getAIResponse(text);
      _startStreamingResponse(fullResponse);
    });
  }

  // 流式输出响应(模拟千问的打字效果)
  void _startStreamingResponse(String fullText) {
    int currentIndex = 0;
    Timer.periodic(const Duration(milliseconds: 30), (timer) {
      if (!mounted) {
        timer.cancel();
        return;
      }

      if (currentIndex >= fullText.length) {
        timer.cancel();
        setState(() {
          _isTyping = false;
          _messages.add({
            'text': fullText,
            'isUser': false,
            'timestamp': DateTime.now(),
          });
          _currentStreamingText = '';
        });
        return;
      }

      setState(() {
        _currentStreamingText = fullText.substring(0, currentIndex + 1);
      });

      // 实时滚动到底部
      _scrollToBottom();

      currentIndex++;
    });
  }

  // 滚动到底部
  void _scrollToBottom() {
    Future.delayed(const Duration(milliseconds: 100), () {
      if (_scrollController.hasClients) {
        _scrollController.animateTo(
          _scrollController.position.maxScrollExtent,
          duration: const Duration(milliseconds: 300),
          curve: Curves.easeOut,
        );
      }
    });
  }

  // 发送欢迎提示词
  void _sendWelcomePrompt(String prompt) {
    _controller.text = prompt;
    _sendMessage();
  }

  // AI 回复逻辑 (模拟千问风格)
  String _getAIResponse(String input) {
    input = input.toLowerCase();

    if (input.contains('你叫什么') || input.contains('名字')) {
      return '我是千问,由阿里云开发的大语言模型。我致力于为用户提供智能、高效的对话体验,能够回答问题、创作内容、分析信息等。很高兴认识你!';
    } else if (input.contains('你好') || input.contains('hello')) {
      return '你好!很高兴见到你。我是千问,一个由阿里云开发的人工智能助手。我可以帮助你解答问题、提供建议、辅助创作等。今天有什么我可以帮你的吗?';
    } else if (input.contains('再见') || input.contains('拜拜')) {
      return '再见!很高兴今天能够帮助你。如果以后有任何问题或需要协助,随时都可以来找我。祝你一切顺利,生活愉快!';
    } else if (input.contains('天气')) {
      return '抱歉,我无法获取实时的天气信息。不过,我可以告诉你一些关于天气的知识,或者你可以查看手机上的天气预报应用来获取准确的天气情况。需要我为你做些其他事情吗?';
    } else if (input.contains('笑话')) {
      return '好吧,来个程序员笑话:有一天,0 对 8 说:"你的腰带可真紧啊!" 8 对 0 说:"你看看你自己,胖得都变形了!" 😄';
    } else if (input.contains('诗')) {
      return '好的,让我为你创作一首诗:\n\n星河长夜独徘徊,\n月影如钩照楼台。\n心事谁知难入梦,\n春风不解故人来。\n\n这首诗表达了夜深人静时的孤独与思念之情。希望你喜欢!';
    } else if (input.contains('人工智能') || input.contains('ai')) {
      return '人工智能(AI)是计算机科学的一个分支,致力于创建能够模仿人类智能行为的系统。它包括机器学习、深度学习、自然语言处理等多个领域。\n\n现代AI可以:\n• 处理和分析大量数据\n• 识别图像和语音\n• 理解和生成自然语言\n• 辅助决策和创作\n\nAI正在改变我们的生活方式,从智能手机到自动驾驶,从医疗诊断到科学研究,AI的应用无处不在。';
    } else if (input.contains('故事')) {
      return '好的,让我给你讲个有趣的故事:\n\n从前有个程序员,他每天都在写代码。有一天,他的代码出了bug,他找了三天三夜都没找到。\n\n最后,他绝望地向同事求助。同事看了五分钟后,指着屏幕说:"你这里少了一个分号。"\n\n程序员恍然大悟,从那以后,他学会了——多喝咖啡,少写bug!\n\n这个小故事告诉我们:有时候,答案就在眼前,只是我们当局者迷。';
    } else if (input.contains('编程') || input.contains('代码')) {
      return '学习编程是一个很好的选择!以下是一些建议:\n\n1. 选择一门语言\n   • Python:适合初学者,语法简洁\n   • JavaScript:Web开发必备\n   • Java/Go:后端开发\n\n2. 学习路径\n   • 基础语法\n   • 数据结构与算法\n   • 项目实战\n   • 持续练习\n\n3. 推荐资源\n   • 在线课程:Coursera、MOOC\n   • 练习平台:LeetCode、牛客网\n   • 开源项目:GitHub\n\n记住,编程需要耐心和持续的实践。遇到问题不要放弃,坚持下去就一定会有收获!加油!';
    } else if (input.contains('如何') ||
        input.contains('怎么') ||
        input.contains('怎么做')) {
      return '这是一个很好的问题。让我为你分析一下:\n\n首先,我们需要明确问题的具体目标和约束条件。然后,可以采取以下步骤:\n\n1. 明确需求:深入了解你想要达成的具体目标\n2. 分析现状:评估当前的资源和条件\n3. 制定计划:分解任务,制定可执行的步骤\n4. 执行行动:按计划逐步实施\n5. 评估反馈:根据结果调整和优化\n\n如果你能提供更多细节,我可以给出更具体的建议。';
    } else {
      // 随机通用回复 - 更丰富多样
      final List<String> generics = [
        '这是个很好的问题!从多个角度来看,我认为关键在于理解问题的本质。你提到的话题很有深度,让我来详细解释一下...\n\n首先,我们需要考虑的是,这个问题涉及多个层面。从技术角度来看,它关系到具体实现;从实践角度来看,它又牵涉到实际应用。\n\n我的建议是,你可以尝试从以下几个方面入手:分析现状、明确目标、制定计划、持续优化。如果有任何疑问,随时问我!',
        '我理解你的意思。这个问题确实值得深入探讨。让我分享一些想法:\n\n• 要点一:这个问题的核心在于如何找到最合适的解决方案\n• 要点二:考虑到实际情况,可能需要多方面权衡\n• 要点三:建议你可以尝试不同的方法,找到最适合你的那一个\n\n希望这些建议对你有帮助!如果需要更详细的解释,请告诉我。',
        '感谢你的提问!这是一个很有价值的话题。\n\n我认为,关键在于掌握正确的方法论。首先,我们需要系统性地分析问题;其次,要善于利用可用的资源和工具;最后,保持开放的学习态度,不断迭代改进。\n\n如果你能提供更多背景信息,我可以给出更具针对性的建议。',
        '你提的这个问题很好,让我来帮你理清思路。\n\n从根本上说,这个问题的解决需要遵循一定的原则:目标明确、方法科学、执行有效。\n\n我建议你可以:1. 先梳理清楚核心需求 2. 然后制定详细的方案 3. 接着分步骤实施 4. 最后根据反馈调整优化。\n\n在这个过程中,如果遇到任何困难,随时可以来问我,我很乐意提供帮助!',
      ];
      return generics[Random().nextInt(generics.length)];
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFF0A0E27),
      appBar: AppBar(
        elevation: 0,
        backgroundColor: Colors.transparent,
        title: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Container(
              padding: const EdgeInsets.all(8),
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  colors: [Color(0xFF6C63FF), Color(0xFF9B8FFF)],
                ),
                borderRadius: BorderRadius.circular(12),
              ),
              child: const Icon(
                Icons.psychology,
                color: Colors.white,
                size: 24,
              ),
            ),
            const SizedBox(width: 12),
            const Text(
              'AI 智能助手',
              style: TextStyle(
                fontSize: 20,
                fontWeight: FontWeight.bold,
                color: Colors.white,
              ),
            ),
          ],
        ),
        centerTitle: true,
        actions: [
          Container(
            margin: const EdgeInsets.only(right: 12),
            decoration: BoxDecoration(
              color: const Color(0xFF1A1F3A),
              borderRadius: BorderRadius.circular(12),
            ),
            child: IconButton(
              icon: const Icon(Icons.more_vert, color: Colors.white70),
              onPressed: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                    content: Text('更多功能开发中...'),
                    backgroundColor: Color(0xFF6C63FF),
                  ),
                );
              },
            ),
          )
        ],
      ),
      body: Column(
        children: [
          // 消息列表
          Expanded(
            child: _messages.isEmpty
                ? _buildWelcomeScreen()
                : ListView(
                    controller: _scrollController,
                    padding: const EdgeInsets.symmetric(vertical: 8),
                    children: [
                      ..._messages.map(
                          (msg) => _buildMessageBubble(msg, msg['isUser'])),
                      if (_isTyping) _buildStreamingResponse(),
                      const SizedBox(height: 20),
                    ],
                  ),
          ),

          // 输入区域
          Container(
            padding: const EdgeInsets.fromLTRB(16, 12, 16, 16),
            decoration: BoxDecoration(
              color: const Color(0xFF0A0E27),
              boxShadow: [
                BoxShadow(
                  color: Colors.black.withValues(alpha: 0.3),
                  blurRadius: 10,
                  offset: const Offset(0, -2),
                ),
              ],
            ),
            child: SafeArea(
              child: Row(
                children: [
                  Container(
                    decoration: BoxDecoration(
                      color: const Color(0xFF1A1F3A),
                      borderRadius: BorderRadius.circular(25),
                    ),
                    child: IconButton(
                      icon: const Icon(Icons.emoji_emotions_outlined,
                          color: Colors.white70),
                      onPressed: () {},
                    ),
                  ),
                  const SizedBox(width: 8),
                  Expanded(
                    child: TextField(
                      controller: _controller,
                      maxLines: null,
                      style: const TextStyle(color: Colors.white),
                      decoration: InputDecoration(
                        hintText: '给千问发送消息...',
                        hintStyle: TextStyle(color: Colors.grey[500]),
                        filled: true,
                        fillColor: const Color(0xFF1A1F3A),
                        border: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(25),
                          borderSide: BorderSide.none,
                        ),
                        enabledBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(25),
                          borderSide: BorderSide.none,
                        ),
                        focusedBorder: OutlineInputBorder(
                          borderRadius: BorderRadius.circular(25),
                          borderSide: const BorderSide(
                            color: Color(0xFF6C63FF),
                            width: 2,
                          ),
                        ),
                        contentPadding: const EdgeInsets.symmetric(
                            horizontal: 20, vertical: 14),
                      ),
                      onSubmitted: (value) => _sendMessage(),
                      textInputAction: TextInputAction.send,
                    ),
                  ),
                  const SizedBox(width: 8),
                  Container(
                    decoration: BoxDecoration(
                      gradient: const LinearGradient(
                        colors: [Color(0xFF6C63FF), Color(0xFF9B8FFF)],
                      ),
                      borderRadius: BorderRadius.circular(25),
                      boxShadow: [
                        BoxShadow(
                          color: const Color(0xFF6C63FF).withValues(alpha: 0.3),
                          blurRadius: 8,
                          offset: const Offset(0, 4),
                        ),
                      ],
                    ),
                    child: InkWell(
                      borderRadius: BorderRadius.circular(25),
                      onTap: _sendMessage,
                      child: const Padding(
                        padding: EdgeInsets.all(12),
                        child: Icon(Icons.send, color: Colors.white),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _controller.dispose();
    _responseTimer?.cancel();
    _scrollController.dispose();
    super.dispose();
  }

  // 构建欢迎界面
  Widget _buildWelcomeScreen() {
    return Center(
      child: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            // AI 头像动画
            Container(
              width: 120,
              height: 120,
              decoration: BoxDecoration(
                gradient: const LinearGradient(
                  begin: Alignment.topLeft,
                  end: Alignment.bottomRight,
                  colors: [Color(0xFF6C63FF), Color(0xFF9B8FFF)],
                ),
                borderRadius: BorderRadius.circular(30),
                boxShadow: [
                  BoxShadow(
                    color: const Color(0xFF6C63FF).withValues(alpha: 0.4),
                    blurRadius: 20,
                    offset: const Offset(0, 10),
                  ),
                ],
              ),
              child: const Icon(
                Icons.smart_toy,
                size: 60,
                color: Colors.white,
              ),
            ),
            const SizedBox(height: 24),
            const Text(
              'AI 智能助手',
              style: TextStyle(
                fontSize: 28,
                fontWeight: FontWeight.bold,
                color: Colors.white,
                letterSpacing: 1,
              ),
            ),
            const SizedBox(height: 8),
            Text(
              '随时为你解答问题',
              style: TextStyle(
                fontSize: 16,
                color: Colors.grey[400],
              ),
            ),
            const SizedBox(height: 40),
            // 快捷提示词
            const Text(
              '试试这些问题:',
              style: TextStyle(
                fontSize: 14,
                color: Colors.grey,
                fontWeight: FontWeight.w500,
              ),
            ),
            const SizedBox(height: 16),
            Wrap(
              spacing: 12,
              runSpacing: 12,
              alignment: WrapAlignment.center,
              children: _welcomePrompts.map((prompt) {
                return InkWell(
                  onTap: () => _sendWelcomePrompt(prompt),
                  borderRadius: BorderRadius.circular(20),
                  child: Container(
                    padding: const EdgeInsets.symmetric(
                        horizontal: 16, vertical: 10),
                    decoration: BoxDecoration(
                      color: const Color(0xFF1A1F3A),
                      borderRadius: BorderRadius.circular(20),
                      border: Border.all(
                        color: const Color(0xFF6C63FF).withValues(alpha: 0.3),
                        width: 1,
                      ),
                    ),
                    child: Text(
                      prompt,
                      style: const TextStyle(
                        color: Colors.white70,
                        fontSize: 14,
                      ),
                    ),
                  ),
                );
              }).toList(),
            ),
          ],
        ),
      ),
    );
  }

  // 构建消息气泡
  Widget _buildMessageBubble(Map<String, dynamic> msg, bool isUser) {
    final DateTime? timestamp = msg['timestamp'];
    final timeStr = timestamp != null
        ? '${timestamp.hour.toString().padLeft(2, '0')}:${timestamp.minute.toString().padLeft(2, '0')}'
        : '';

    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
      child: Column(
        crossAxisAlignment:
            isUser ? CrossAxisAlignment.end : CrossAxisAlignment.start,
        children: [
          Row(
            mainAxisAlignment:
                isUser ? MainAxisAlignment.end : MainAxisAlignment.start,
            crossAxisAlignment: CrossAxisAlignment.start,
            children: [
              if (!isUser) ...[
                Container(
                  padding: const EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    gradient: const LinearGradient(
                      colors: [Color(0xFF6C63FF), Color(0xFF9B8FFF)],
                    ),
                    borderRadius: BorderRadius.circular(12),
                  ),
                  child: const Icon(
                    Icons.psychology,
                    color: Colors.white,
                    size: 18,
                  ),
                ),
                const SizedBox(width: 10),
              ],
              Flexible(
                child: Container(
                  padding:
                      const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
                  decoration: BoxDecoration(
                    gradient: isUser
                        ? const LinearGradient(
                            colors: [Color(0xFF6C63FF), Color(0xFF9B8FFF)],
                          )
                        : null,
                    color: isUser ? null : const Color(0xFF1A1F3A),
                    borderRadius: BorderRadius.only(
                      topLeft: const Radius.circular(20),
                      topRight: const Radius.circular(20),
                      bottomLeft: Radius.circular(isUser ? 20 : 5),
                      bottomRight: Radius.circular(isUser ? 5 : 20),
                    ),
                    boxShadow: [
                      BoxShadow(
                        color: (isUser ? const Color(0xFF6C63FF) : Colors.black)
                            .withValues(alpha: 0.15),
                        blurRadius: 8,
                        offset: const Offset(0, 2),
                      ),
                    ],
                  ),
                  constraints: BoxConstraints(
                    maxWidth: MediaQuery.of(context).size.width * 0.75,
                  ),
                  child: Text(
                    msg['text'],
                    style: TextStyle(
                      color: isUser ? Colors.white : Colors.white70,
                      fontSize: 15,
                      height: 1.5,
                    ),
                  ),
                ),
              ),
              if (isUser) const SizedBox(width: 10),
            ],
          ),
          if (timeStr.isNotEmpty) ...[
            const SizedBox(height: 4),
            Padding(
              padding: EdgeInsets.only(
                left: isUser ? 0 : 48,
                right: isUser ? 48 : 0,
              ),
              child: Text(
                timeStr,
                style: TextStyle(
                  color: Colors.grey[600],
                  fontSize: 11,
                ),
              ),
            ),
          ],
        ],
      ),
    );
  }

  // 构建流式输出响应
  Widget _buildStreamingResponse() {
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 6),
      child: Row(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Container(
            padding: const EdgeInsets.all(8),
            decoration: BoxDecoration(
              gradient: const LinearGradient(
                colors: [Color(0xFF6C63FF), Color(0xFF9B8FFF)],
              ),
              borderRadius: BorderRadius.circular(12),
            ),
            child: const Icon(
              Icons.psychology,
              color: Colors.white,
              size: 18,
            ),
          ),
          const SizedBox(width: 10),
          Flexible(
            child: Container(
              padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
              decoration: BoxDecoration(
                color: const Color(0xFF1A1F3A),
                borderRadius: BorderRadius.only(
                  topLeft: const Radius.circular(20),
                  topRight: const Radius.circular(20),
                  bottomLeft: const Radius.circular(5),
                  bottomRight: const Radius.circular(20),
                ),
                boxShadow: [
                  BoxShadow(
                    color: Colors.black.withValues(alpha: 0.15),
                    blurRadius: 8,
                    offset: const Offset(0, 2),
                  ),
                ],
              ),
              constraints: BoxConstraints(
                maxWidth: MediaQuery.of(context).size.width * 0.75,
              ),
              child: Row(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Expanded(
                    child: Text(
                      _currentStreamingText,
                      style: const TextStyle(
                        color: Colors.white70,
                        fontSize: 15,
                        height: 1.5,
                      ),
                    ),
                  ),
                  const SizedBox(width: 8),
                  _buildBlinkingCursor(),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  // 闪烁光标
  Widget _buildBlinkingCursor() {
    return TweenAnimationBuilder<double>(
      tween: Tween(begin: 0.0, end: 1.0),
      duration: const Duration(milliseconds: 500),
      builder: (context, value, child) {
        return Opacity(
          opacity: value,
          child: Container(
            width: 2,
            height: 18,
            color: const Color(0xFF6C63FF),
          ),
        );
      },
      onEnd: () {
        setState(() {});
      },
    );
  }
}

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐