文章主题:

666AI工具大全,助力做AI时代先行者!

转载说明:原创不易,未经授权,谢绝任何形式的转载

1680622346509.webp

开篇

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

🔥🚀OpenAI的ChatGPT热度飙升,最近更是引发了全球关注,尤其是GPT-4的发布,引领了一场技术风暴。无数创意和实用案例正在涌现,而ChatGPT的互动方式已悄然转变,不再局限于chat.openai.com直接聊天。对我来说,它不仅是灵感催生器,Flutter代码片段的小助手,甚至是撰写博客文章构思的得力伙伴!💡🌈尽管ChatGPT给出的大纲展望广阔,但它的建议往往充满启发性,让我不得不对大纲进行微调,以确保内容的精炼与连贯。🚀📝

🌟改写版🚀尽管OpenAI的旗舰聊天机器人备受期待,但它在用户交互方面的表现却并未达到预期。其界面设计相对简陋,功能受限,导致用户在保存和回顾对话历史时常遇到困扰。相比之下,一些创新者已巧妙地利用Web技术的力量,打造了如TypingMind这类ChatGPT客户端应用,它们不仅优化了用户体验,还提供了更为流畅、个性化的互动环境。🚀

Flutter开发者的心声🌟:ChatGPT选Flutter?绝对没错!这款神器以其无与伦比的跨平台优势及丰富的UI组件,简直是客户端应用开发的理想伙伴。一次编写,四面开花!🚀无论是Web、iOS、Android还是那熟悉的桌面世界(Windows、macOS/Linux),Flutter都能轻松驾驭,无缝适配,让你的应用触手可及。🌍拥抱创新,选择Flutter,你的项目将闪耀无限可能!✨

ChatGPT API

🌟🚀利用OpenAI的力量?轻松几步!💡首先,探索无限可能——注册并解锁API密钥的神秘之旅!📝🎉只需几分钟,您的创新项目将跃升新台阶。别忘了,费用是透明的,按需付费——确保财务规划无压力。💰💸无论频率如何,我们的gpt-3.5-turbo模型都将提供经济实惠的选择。偶尔的大规模使用?没问题,但我们鼓励明智消费——避免不必要的开销。💡节省每一分钱,让创新更自由!💪享受高质量服务的同时,保护隐私安全,我们是您值得信赖的伙伴。🛡️💻让我们一起开启AI新篇章,让技术赋能您的成功!🏆

🌟🚀改写后:作为文章写作专家,我将引领你探索高效沟通的新路径!通过Chat API,🔥掌握OpenAI两大力作——gpt-3.5turbo与gpt-4的力量,它们如同智慧引擎,为你提供无尽创意和流畅对话。欲深入了解这一强大工具,只需访问我们的链接💡[Chat API详情](https://api.example.com/chat),这里详尽解析如何充分利用这些先进模型进行深度交流。SEO优化提示:使用行业关键词如”Chat API”, “OpenAI models”, “gpt-3.5-turbo”, “gpt-4”, “智慧引擎”, “深度交流”等,同时保持语句通顺和信息准确。记得加入一些表情符号,如💡、🚀来提升可读性和吸引力。

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: gpt3.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。

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

1680622347518.png

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

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的对话中,我们问了一个跟进问题“给我展示代码”。

1680622348075.png

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

flutter pub add markdown_widget

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

MarkdownWidget( data: content, shrinkWrap: true, )

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

1680622348593.png

错误处理

如果我们从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

直接翻译,有自行改编和添加部分,翻译水平有限,难免有疏漏,欢迎指正

ai_linghr_sousou1.png

AI时代,掌握AI大模型第一手资讯!AI时代不落人后!

免费ChatGPT问答,办公、写作、生活好得力助手!

扫码右边公众号,驾驭AI生产力!

Leave a Reply

Your email address will not be published. Required fields are marked *