使用Flutter构建ChatGPT客户端:快速入门指南

这篇具有很好参考价值的文章主要介绍了使用Flutter构建ChatGPT客户端:快速入门指南。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

使用Flutter构建ChatGPT客户端:快速入门指南

开篇

在本篇博客文章中,我们将通过OpenAI的API构建一个简单的对话界面,与ChatGPT进行聊天。

近来OpenAI和ChatGPT的热度很高,尤其是最近发布的GPT-4。这类工具的大量使用案例已经涌现出来,但目前人们最常用ChatGPT的方式是通过chat.openai.com进行聊天。我一直在使用ChatGPT来激发灵感、编写一些Flutter代码片段,甚至是撰写这篇博客文章的大纲!当然,它建议的大纲非常乐观,因此我不得不略过一些部分,但它仍然提供了足够的指引,让我能够立即开始。

然而,OpenAI的官方聊天界面体验并不好。它非常有限,聊天历史记录经常无法正常工作。已经有人使用Web技术构建了更好的UI和用户体验的ChatGPT客户端应用程序,例如TypingMind。

作为Flutter开发者,我不禁想到Flutter非常适合作为ChatGPT客户端应用程序的选择!Flutter具有跨平台能力和丰富的UI组件,是这样一个项目的完美选择。我们可以编写一次代码,然后将应用程序发布在Web、iOS、Android以及桌面平台上:Windows、macOS和Linux。

ChatGPT API

要使用OpenAI的任何API,您需要注册并获取API密钥。请注意,API使用可能会产生费用,您需要提供付款详细信息。我们将使用的gpt-3.5-turbo模型相对便宜,除非您经常使用它,否则不应花费太多钱。

具体来说,我们将使用Chat API(聊天完成),该API支持两个OpenAI的模型:gpt-3.5-turbo和gpt-4。我们可以在此处找到Chat API的完整参考,其中涉及在
https://api.openai.com/v1/chat/completions 上执行POST请求。

此时,我们可以使用http库向Chat API发送必需的数据并解析响应。但是,由于Dart和Flutter社区的贡献,已经在pub.dev上提供了一个可用的包:dart_openai。它将为我们进行API请求并返回解析后的响应,因此我们只需获取响应文本并在应用程序中显示即可。

以下是一个接受用户消息并返回ChatGPT响应的方法:

Future<String> completeChat(String message) async {
  final chatCompletion = await OpenAI.instance.chat.create(
    model: 'gpt-3.5-turbo',
    messages: [
      OpenAIChatCompletionChoiceMessageModel(
        content: message,
        role: 'user',
      ),
    ],
  );
  return chatCompletion.choices.first.message.content;
}

由于这将是一次对话,因此我们需要在请求中传递以前的消息,以便ChatGPT具有到目前为止整个对话的上下文,而不仅仅是用户的最后一条消息。

class ChatMessage {
  ChatMessage(this.content, this.isUserMessage);

  final String content;
  final bool isUserMessage;
}

Future<String> completeChat(List<ChatMessage> messages) async {
  final chatCompletion = await OpenAI.instance.chat.create(
    model: 'gpt-3.5-turbo',
    messages: [
      ...previousMessages.map(
        (e) => OpenAIChatCompletionChoiceMessageModel(
          role: e.isUserMessage ? 'user' : 'assistant',
          content: e.content,
        ),
      ),
    ],
  );
  return chatCompletion.choices.first.message.content;
}

上面的方法接受用户的最后一条消息以及对话中的所有先前消息。请注意,在API请求中,ChatGPT的响应标有助手的角色。

现在,我们把最终版本的completeChat方法放到一个ChatApi类中,以便稍后使用。

// models/chat_message.dart
class ChatMessage {
  ChatMessage(this.content, this.isUserMessage);

  final String content;
  final bool isUserMessage;
}
// api/chat_api.dart
import 'package:chatgpt_client/models/chat_message.dart';
import 'package:chatgpt_client/secrets.dart';
import 'package:dart_openai/openai.dart';

class ChatApi {
static const _model = 'gpt-3.5-turbo'; // 设置模型为"gpt-3.5-turbo"

ChatApi() { // 构造函数,设置OpenAI的apiKey和organization
OpenAI.apiKey = openAiApiKey;
OpenAI.organization = openAiOrg;
}

Future<String> completeChat(List<ChatMessage> messages) async {
// 定义方法completeChat,接收ChatMessage列表类型参数messages,并返回Future<String>类型
final chatCompletion = await OpenAI.instance.chat.create( // 调用OpenAI的chat.create方法,获取ChatCompletionModel对象
model: _model, // 传递模型参数
messages: messages // 传递对话历史消息参数,并将ChatMessage列表转换为OpenAIChatCompletionChoiceMessageModel列表
.map((e) => OpenAIChatCompletionChoiceMessageModel(
role: e.isUserMessage ? 'user' : 'assistant', // 指定消息发送者角色
content: e.content, // 传递消息文本内容
))
.toList(),
);
return chatCompletion.choices.first.message.content; // 返回ChatCompletionModel对象中的消息内容
}
}

请注意,在构造函数中,我们设置了API密钥和组织ID。如果没有API密钥,任何请求都将失败。组织ID是可选的,如果您在OpenAI平台上设置了多个组织,可以提供组织ID。

// secrets.dart
const openAiApiKey = 'YOUR_API_KEY';
const openAiOrg = 'YOUR_ORGANIZATION_ID';

为了避免将敏感信息提交到版本控制中,我们通常会将其存储在 secrets 文件中并在 .gitignore 中添加 secrets 文件,以便在提交时忽略这些文件。在 GitHub 上的项目存储库中,通常会提供一个名为 secrets_example.dart 的文件,其中包含占位符值,供开发人员参考并创建自己的 secrets 文件。

API密钥的注意事项

在本文中,我们正在构建一个客户端应用程序。像这样硬编码API密钥的应用程序不应该发布。由于API使用可能会产生费用,您不希望暴露自己的API密钥。

如果您想发布这样的应用程序,您有两个选择:

1.允许用户提供自己的API密钥开始聊天。用户可以通过应用程序提供他们的密钥,您可以将其安全地存储在本地存储中,以在每个API请求中使用。

2.而不是直接调用聊天API,可以调用一个服务器或边缘函数,然后使用自己的令牌调用聊天API。这样,您就不会暴露自己的API密钥,可以控制流量,并具有其他授权和速率限制要求。如果您选择这种方法,您可能需要考虑赚钱,因为经常使用应用程序的用户将花费您的钱!

构建前端界面

现在 Chat API 已准备好使用,是时候开始构建 UI 了。如果你从零开始,可以使用 "flutter create" 命令初始化一个 Flutter 项目:

flutter create my_chatgpt_client

UI将是相当标准的,包含两个主要的组件:消息编辑器和消息气泡。主屏幕将是聊天中所有消息的列表(作为消息气泡),消息编辑器在底部,我们可以在其中输入消息。

让我们从消息编辑器小部件开始:

// widgets/message_composer.dart

import 'package:flutter/material.dart';

class MessageComposer extends StatelessWidget {
  MessageComposer({
    required this.onSubmitted, // 回调函数,当用户输入文本提交时调用
    required this.awaitingResponse, // 是否正在等待 Chat API 的响应
    super.key,
  });

  final TextEditingController _messageController = TextEditingController(); // 用于文本输入的控制器

  final void Function(String) onSubmitted; // 回调函数,当用户输入文本提交时调用
  final bool awaitingResponse; // 是否正在等待 Chat API 的响应

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: const EdgeInsets.all(12), // 内边距
      color: Theme.of(context).colorScheme.secondaryContainer.withOpacity(0.05), // 颜色
      child: SafeArea(
        child: Row(
          children: [
            Expanded(
              child: !awaitingResponse // 如果没有正在等待 Chat API 的响应
                  ? TextField(
                      controller: _messageController, // 将文本控制器传递给 TextField 组件
                      onSubmitted: onSubmitted, // 当用户提交文本时调用 onSubmitted 回调函数
                      decoration: const InputDecoration(
                        hintText: 'Write your message here...', // 提示文本
                        border: InputBorder.none, // 边框
                      ),
                    )
                  : Row(
                      mainAxisAlignment: MainAxisAlignment.center, // 将子组件水平居中
                      children: const [
                        SizedBox(
                          height: 24,
                          width: 24,
                          child: CircularProgressIndicator(), // 等待指示器
                        ),
                        Padding(
                          padding: EdgeInsets.all(16),
                          child: Text('Fetching response...'), // 正在获取响应
                        ),
                      ],
                    ),
            ),
            IconButton(
              onPressed: !awaitingResponse // 如果没有正在等待 Chat API 的响应
                  ? () => onSubmitted(_messageController.text) // 当用户点击发送按钮时调用 onSubmitted 回调函数
                  : null, // 禁用按钮
              icon: const Icon(Icons.send), // 发送图标
            ),
          ],
        ),
      ),
    );
  }
}

消息编辑器将在文本字段被提交(例如,按下Enter键)或者我们点击右侧的发送按钮时,调用我们传递给它的onSubmitted方法。我们可以使用awaitingResponse标志隐藏文本字段并禁用发送按钮。当我们等待API的响应时,我们将将此标志设置为true以表示消息正在提交中。

消息气泡小部件是一个简单的容器,根据消息是用户消息还是AI生成的消息,具有不同的背景颜色和发送者名称:

// widgets/message_bubble.dart
import 'package:flutter/material.dart';

class MessageBubble extends StatelessWidget {
  const MessageBubble({
    required this.content,
    required this.isUserMessage,
    super.key,
  });

  final String content; // 消息内容
  final bool isUserMessage; // 是否为用户消息

  @override
  Widget build(BuildContext context) {
    final themeData = Theme.of(context);
    return Container(
      margin: const EdgeInsets.all(8), // 设置组件的边距
      decoration: BoxDecoration(
        color: isUserMessage // 根据消息类型设置不同的背景颜色
            ? themeData.colorScheme.primary.withOpacity(0.4)
            : themeData.colorScheme.secondary.withOpacity(0.4),
        borderRadius: const BorderRadius.all(Radius.circular(12)), // 设置圆角边框
      ),
      child: Padding(
        padding: const EdgeInsets.all(12), // 设置内边距
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              children: [
                Text(
                  isUserMessage ? 'You' : 'AI', // 根据消息类型显示发送者
                  style: const TextStyle(fontWeight: FontWeight.bold),
                ),
              ],
            ),
            const SizedBox(height: 8),
            Text(content), // 显示消息内容
          ],
        ),
      ),
    );
  }
}

现在我们已经准备好了所有必要的小部件,现在让我们将它们全部组合在主页上。下面是主聊天页面的代码:

// chat_page.dart
import 'package:chatgpt_client/api/chat_api.dart';
import 'package:chatgpt_client/models/chat_message.dart';
import 'package:chatgpt_client/widgets/message_bubble.dart';
import 'package:chatgpt_client/widgets/message_composer.dart';
import 'package:flutter/material.dart';

class ChatPage extends StatefulWidget {
  const ChatPage({
    required this.chatApi,
    Key? key,
  }) : super(key: key);

  final ChatApi chatApi;

  @override
  State<ChatPage> createState() => _ChatPageState();
}

class _ChatPageState extends State<ChatPage> {
  final _messages = <ChatMessage>[
    ChatMessage('Hello, how can I help?', false),
  ];
  var _awaitingResponse = false;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Chat')),
      body: Column(
        children: [
          Expanded(
            child: ListView(
              children: [
                // 使用_spread_操作符将每个聊天消息转换为MessageBubble widget
                ..._messages.map(
                  (msg) => MessageBubble(
                    content: msg.content,
                    isUserMessage: msg.isUserMessage,
                  ),
                ),
              ],
            ),
          ),
          // 使用MessageComposer widget显示用户输入
          MessageComposer(
            onSubmitted: _onSubmitted,
            awaitingResponse: _awaitingResponse,
          ),
        ],
      ),
    );
  }

这是一个有状态的widget,它开始时显示消息"How can I help?",这样我们就不会从空白聊天开始了。

最后一部分是_onSubmitted方法,当用户提交一条消息时,它会通过消息编辑器被调用。

// _onSubmitted方法将会在用户输入一条新的聊天消息后被调用
Future<void> _onSubmitted(String message) async {
  setState(() {
    // 在UI中显示用户刚刚输入的消息
    _messages.add(ChatMessage(message, true));
    // 等待服务器的回复
    _awaitingResponse = true;
  });
  // 发送用户的消息到API,等待API返回回复
  final response = await widget.chatApi.completeChat(_messages);
  setState(() {
    // 在UI中显示API的回复
    _messages.add(ChatMessage(response, false));
    // 回复已经接收完成,不再等待
    _awaitingResponse = false;
  });
}

当用户提交一条新的消息时,我们会在 setState 中将该消息添加到聊天消息中,并将 _awaitingResponse 设置为 true。这将在对话中显示用户的消息,并禁用消息编辑器。

接下来,我们将所有消息传递给聊天 API,并等待响应。一旦我们得到了响应,我们会在 _messages 中添加它作为聊天消息,并在第二个 setState 调用中将 _awaitingResponse 设置回 false。

至此,对话流程就完成了!现在让我们看看它的实际效果:

使用Flutter构建ChatGPT客户端:快速入门指南

这是应用程序的代码和主要方法:

import 'package:chatgpt_client/api/chat_api.dart';
import 'package:chatgpt_client/chat_page.dart';
import 'package:flutter/material.dart';

void main() {
  // 运行ChatApp
  runApp(ChatApp(chatApi: ChatApi()));
}

class ChatApp extends StatelessWidget {
  // 带有chatApi参数的构造函数
  const ChatApp({required this.chatApi, Key? key}) : super(key: key);

  final ChatApi chatApi; // chatApi属性

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // MaterialApp,包含了应用的所有基本元素
      title: 'ChatGPT Client', // 应用的标题
      theme: ThemeData(
        // 设置应用的主题色
        colorScheme: ColorScheme.fromSeed(
          seedColor: Colors.teal, // 主色调
          secondary: Colors.lime, // 次要色调
        ),
      ),
      home: ChatPage(chatApi: chatApi), // ChatPage是这个应用的首页
    );
  }
}

解析markdown

在我们之前与ChatGPT的对话中,我们问了一个跟进问题“给我展示代码”。

使用Flutter构建ChatGPT客户端:快速入门指南

我们在回复中得到了相当数量的Flutter代码,但它们都是以markdown格式呈现的!我们将使用markdown_widget包来解决这个问题。

flutter pub add markdown_widget

在MessageBubble组件中,用MarkdownWidget替换包含消息内容的Text组件:

MarkdownWidget(
  data: content,
  shrinkWrap: true,
)

经过一次热重载,我们可以看到代码现在已经被正确解析了。这很容易!

使用Flutter构建ChatGPT客户端:快速入门指南

错误处理

如果我们从OpenAI得到了错误响应怎么办?在测试中,我遇到了一些429(Too Many Requests)异常。如果你调用API太频繁,也可能出现这种错误,但也可能是因为OpenAI API总共接收到了太多的请求。

我们至少要处理这个错误并显示一个有用的消息。下面是修改后的_onSubmitted方法:

Future<void> _onSubmitted(String message) async {
  setState(() {
    _messages.add(ChatMessage(message, true)); // 将输入的消息添加到对话列表
    _awaitingResponse = true; // 设置正在等待响应标志
  });
  try {
    final response = await widget.chatApi.completeChat(_messages); // 调用 ChatApi 的 completeChat 方法来获取 OpenAI 的响应
    setState(() {
      _messages.add(ChatMessage(response, false)); // 将响应添加到对话列表
      _awaitingResponse = false; // 取消等待响应标志
    });
  } catch (err) {
    ScaffoldMessenger.of(context).showSnackBar(
      const SnackBar(content: Text('An error occurred. Please try again.')), // 显示错误信息提示条
    );
    setState(() {
      _awaitingResponse = false; // 取消等待响应标志
    });
  }
}

当然,这还可以进一步改进。我们可以提供一个选项,在不需要发送新消息的情况下重试响应,但也可以在 ChatApi 中自动重试请求而不显示错误。

结束

总结一下,现在我们已经有一个完全可用的聊天应用程序,可以随时在任何平台上与ChatGPT聊天了!

在这篇文章中,我们展示了如何构建一个基本的聊天应用程序,通过OpenAI的聊天API与ChatGPT进行对话。我们还添加了一些附加功能,如markdown解析和错误处理。

这些功能相当基础,但是我们可以通过这样的应用程序实现更多有用的功能,比如能够复制和/或共享响应。此外,我们可以使用本地或云数据库来存储对话,以便随时访问它们。

您可以在这里找到源代码。

https://github.com/dartling/chatgpt_client

今天的分享就到这里,感谢你的阅读,希望能够帮助到你,文章创作不易,如果你喜欢我的分享,别忘了点赞转发,让更多有需要的人看到,最后别忘记关注「前端达人」,你的支持将是我分享最大的动力,后续我会持续输出更多内容,敬请期待。

原文:
https://dartling.dev/building-a-chatgpt-client-app-with-flutter

作者:Christos

直接翻译,有自行改编和添加部分,翻译水平有限,难免有疏漏,欢迎指正文章来源地址https://www.toymoban.com/news/detail-451779.html

到了这里,关于使用Flutter构建ChatGPT客户端:快速入门指南的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • ChatGPT桌面客户端支持gpt4模型,附使用说明

    #软件核心功能: 1、支持OpenAI官方秘钥及API2D双秘钥使用;如果全局魔法,可以自己用官方秘钥;没魔法国内可直接使用API2D秘钥; 2、内置GPT4模型选项,如果你的官方秘钥支持可直接使用;你也可以注册API2D秘钥进行使用(非推广,这真是国内最简单的方式了); 3、支持连

    2024年02月07日
    浏览(26)
  • 【物联网】EMQX(二)——docker快速搭建EMQX 和 MQTTX客户端使用

    在上一篇文章中,小编向大家介绍了物联网必然会用到的消息服务器EMQ,相信大家也对EMQ有了一定的了解,那么接下来,小编从这篇文章正式开始展开对EMQ的学习教程,本章节来记录一下如何对EMQ进行安装。 EMQ作为第三方中间件,可以安装到很多种平台上,比如macOS、window

    2024年02月03日
    浏览(41)
  • 【ChatGPT】如何使用Qt设计SocketIO类型的WebSocket协议客户端

    本文第一次发布 2023年03月24日09点28分 Authors ChatGPT / THDMI 最近写 Flask-SocketIO 服务端的时候,苦于不熟悉前端代码,想试着用 QT 来写客户端,但不清楚该怎么描述,以致于好像一直查的都是 RAW 类型的 WebSocket 协议处理,于是一直没找到 event 和 room 或者 namespace 处理方式。 简单

    2023年04月21日
    浏览(50)
  • spring data elasticsearch使用7.x客户端兼容es 8.x和使用ssl构建RestHighLevelClient

    es在7.x中默认加入elastic security组件所以java client需要使用ssl连接es server. es 8.x 中废弃了 RestHighLevelClient ,使用新版的 java api client ,但是spring data elasticsearch还未更新到该版本.所以需要兼容es 8.x 如下是RestHighLevelClient构建方法: spring data elasticsearch客户端依赖(基于spring boot2.7使用最新

    2024年02月13日
    浏览(34)
  • 【Flutter】Dio 强大的Dart/Flutter HTTP客户端

    Dio是一个强大的Dart/Flutter HTTP客户端,支持全局配置、拦截器、FormData、请求取消、文件上传/下载、超时等功能。 首先,

    2024年02月11日
    浏览(35)
  • flutter 客户端日志上传定位错误信息

    flutter 开发的app 安装到真机上 无法定位报错信息,只能使用usb连接电脑 使用adb logcat来查看日志效率低下。 如果将flutter 开发的app 运行的时候 将日志写进一个日志文件里面去,然后给flutter app搭建一个http服务器,后端知道对应app的ip后,直连对应app 获取日志文件。 获取本机

    2024年01月21日
    浏览(27)
  • netty构建udp服务器以及发送报文到客户端客户端详细案例

    目录 一、基于netty创建udp服务端以及对应通道设置关键 二、发送数据 三、netty中的ChannelOption常用参数说明 1、ChannelOption.SO_BACKLOG 2、ChannelOption.SO_REUSEADDR 3、ChannelOption.SO_KEEPALIVE 4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF 5、ChannelOption.SO_LINGER 6、ChannelOption.TCP_NODELAY 以上代码中

    2024年04月09日
    浏览(50)
  • 快速搭建springboot websocket客户端

    WebSocket 是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。 HTML5 定义的 WebSocket 协议,能更好的节省服务器资源和带宽,并且能够更实时地进行通讯。 浏览器通过 JavaSc

    2024年02月06日
    浏览(49)
  • java代码构建简单http服务器和客户端

    初识http a、超文本传输 、应用层的面向对象的协议,概念介绍网上资源一大堆,关键是基于TCP/IP通信协议来传递数据。 b、一开始接触web项目,都是先接触的servlet,tomcat服务器默认实现的一套http规范,提供了基础服务和组件环境,直接拿到请求、构建正文、响应客户端 然而

    2024年02月10日
    浏览(36)
  • Python网络编程实战:构建TCP服务器与客户端

    Python网络编程实战:构建TCP服务器与客户端 在信息化时代,网络编程是软件开发中不可或缺的一部分。Python作为一种功能强大的编程语言,提供了丰富的网络编程库和工具,使得开发者能够轻松构建各种网络应用。本文将详细介绍如何在Python中进行网络编程,特别是如何使用

    2024年04月15日
    浏览(34)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包