Flutter AI助手开发实战:集成ChatGPT与图像生成API
1. 项目概述:一个全能的Flutter AI助手应用
最近在GitHub上看到一个挺有意思的开源项目,叫“Ai-Assistant-In-Flutter-Using-ChatGpt”。这是一个用Flutter框架开发的移动应用,核心功能是把当下热门的AI能力,比如ChatGPT对话、AI绘画和语言翻译,打包进一个手机App里。对于想学习如何将大模型API集成到实际应用中的Flutter开发者,或者单纯想自己动手做一个功能全面的AI工具App的朋友来说,这个项目提供了一个非常不错的参考样板。
这个项目最吸引我的地方在于它的“全家桶”思路。它没有只满足于做一个简单的聊天机器人,而是把文本生成(ChatGPT和Google Gemini)、图像生成(基于文本描述)、甚至多语言翻译都整合在了一起。这意味着你在学习过程中,能接触到不同类型AI接口的调用、状态管理、UI适配以及错误处理等一整套实战技能。项目还展示了如何集成第三方服务,比如用Lexica进行图像搜索,以及如何处理应用内广告(Facebook Ads),这些都是商业化App开发中绕不开的实际问题。
从技术栈来看,它纯用Dart和Flutter实现,保证了跨平台的一致性(iOS和Android)。UI方面用了很多现代Material Design组件,支持明暗主题切换,还加入了Lottie动画、页面过渡动画等细节,说明作者在用户体验上花了不少心思。无论你是Flutter新手想通过一个综合项目快速提升,还是有一定经验的开发者想寻找AI集成的具体方案,这个项目都值得你clone下来仔细研究一番。接下来,我就结合自己的开发经验,把这个项目的核心设计、关键实现步骤以及那些容易踩坑的地方,给你掰开揉碎了讲清楚。
2. 核心功能模块深度解析
这个AI助手应用主要包含了三大核心功能模块:智能对话、图像生成和语言翻译。每个模块背后都涉及不同的AI服务提供商和技术实现思路,理解这些是复现或定制开发的关键。
2.1 智能对话模块:双引擎驱动
项目同时集成了OpenAI的ChatGPT(或GPT系列模型)和Google的Gemini AI作为对话引擎。这种“双备份”设计很实用,它不仅能作为功能上的互补(不同模型可能擅长不同领域),更重要的是给了用户选择和应对服务不稳定的余地。比如,当某个API服务暂时不可用或达到调用限额时,可以无缝切换到另一个引擎。
在实现上,你需要为两个服务分别创建对应的API客户端类。对于OpenAI,通常会使用 http 或 dio 包发起POST请求到其聊天补全端点(例如 https://api.openai.com/v1/chat/completions ),并在请求头中携带 Authorization: Bearer $yourApiKey 。请求体需要构造一个包含 model (如 gpt-3.5-turbo )、 messages (对话历史列表)和 max_tokens 等参数的JSON对象。对于Google Gemini,则需要通过其官方SDK(如 google_generative_ai )进行初始化并调用生成内容的方法。
注意:API密钥的安全存储是重中之重。 绝对不要将密钥硬编码在代码中或提交到公开的版本控制系统。标准的做法是使用
flutter_dotenv之类的包将密钥存储在项目根目录的.env文件里,并将其添加到.gitignore中。在运行时通过DotEnv().get('OPENAI_API_KEY')来读取。对于需要分发给用户的App,更安全的做法是构建一个自己的后端代理服务器,所有AI API调用都通过你的服务器中转,这样可以将密钥完全保护在服务端。
对话界面的状态管理是另一个核心。你需要维护一个 List<ChatMessage> 来存储对话历史,每条消息应包含角色( user 或 assistant )、内容和时间戳。当用户发送消息时,将其添加到列表并更新UI;然后异步调用AI接口,在等待响应时显示一个加载指示器;收到响应后,再将AI的回复添加到列表并刷新UI。这里推荐使用 Provider 或 Riverpod 这类状态管理库,可以更清晰地分离业务逻辑和UI。
2.2 AI图像生成模块:从文生图到图搜图
图像生成模块同样提供了两种能力:一是根据文本描述生成全新的图像,二是基于现有图像或概念进行搜索。前者主要调用OpenAI的DALL·E API,后者则集成了Lexica.art的搜索API。
使用DALL·E API时,你需要发送一个包含 prompt (文本描述)、 n (生成数量)和 size (图像尺寸,如 1024x1024 )的请求。返回的通常是一个包含图片URL的列表。在Flutter中,你可以直接用 Image.network widget来显示这些网络图片。这里有一个性能优化点:考虑到生成的图片可能较大,建议使用 cached_network_image 包,它可以自动缓存图片,避免重复下载,提升滚动浏览的流畅度。
Lexica的图像搜索功能实现起来略有不同。它通常需要一个搜索查询词(query),返回的是与其索引中风格、内容匹配的现有图片。这更像是“灵感搜索”而非“无中生有”。集成时,你需要查阅Lexica的API文档,构造正确的搜索请求,并处理返回的图片画廊数据。
这个模块的UI设计挑战在于如何优雅地展示图片网格,并处理可能较长的生成等待时间。你可以使用 GridView.builder 来构建自适应列数的图片墙,并为每张图片添加点击放大查看的功能。在生成过程中,显示一个进度指示器和取消按钮是提升用户体验的好方法。
2.3 多语言翻译模块:混合策略保障可用性
翻译模块采用了混合策略,结合了GPT的翻译能力和Google Translation API。为什么要用两种?因为目的不同。使用GPT(特别是GPT-4)进行翻译,其优势在于对上下文、语调和细微差别的理解可能更出色,翻译结果可能更自然、更具创意。而Google Translation API则以其稳定性、支持语言对众多和速度快著称。
实现GPT翻译,本质上就是构造一个特定的对话提示(Prompt),例如:“请将以下文本从中文翻译成英文:${textToTranslate}”。然后将返回的结果作为翻译内容。而集成Google Translation API,如果是直接调用其官方API(需要付费),流程与调用OpenAI类似。也有开发者会使用一些社区维护的、非官方的免费Flutter包,但需要注意其稳定性和合规风险。
在UI上,一个典型的翻译界面需要:两个下拉菜单用于选择源语言和目标语言(语言列表可以硬编码,也可以从API动态获取)、一个文本输入框、一个翻译按钮以及一个显示结果的区域。为了提升体验,可以加入“交换语言”按钮和“复制结果”按钮。状态管理上,需要处理好翻译请求发起、等待和结果显示这几个状态。
3. 项目架构与关键技术实现
要构建这样一个功能聚合的应用,一个清晰、可维护的架构至关重要。我们不能把所有代码都堆在UI页面里,需要做好分层和解耦。
3.1 应用架构设计与状态管理
我推荐采用一种简化的“分层架构”,至少包含数据层、业务逻辑层和表现层。
- 数据层(Repository/DataSource) :这一层负责与外界“对话”。你需要创建诸如
OpenAIRepository、GeminiRepository、ImageSearchRepository等类。它们内部封装了具体的网络请求逻辑(使用dio或http),处理API密钥、构造请求体、发送请求、解析响应,并将原始数据转换为应用内部能理解的模型对象(如AIMessage、GeneratedImage)。这一层应该对上层隐藏API调用的细节。 - 业务逻辑层(Provider/Bloc/Cubit) :这一层是应用的大脑。我们使用状态管理工具(如
Riverpod的StateNotifierProvider或flutter_bloc的Cubit)来创建各种“提供者”。例如,一个ChatProvider会持有当前的对话列表状态,并暴露sendMessage(String text, String modelType)这样的方法。在这个方法内部,它会调用数据层的Repository,并根据返回结果更新自己的状态(如从loading变为success并添加新消息)。所有与用户交互相关的逻辑、错误处理都可以放在这里。 - 表现层(UI) :这一层就是Flutter的Widget树。它们通过
Consumer(Riverpod)或BlocBuilder(flutter_bloc)监听业务逻辑层提供者的状态变化。当状态改变时,UI自动重建并刷新。例如,ChatPage的UI会根据ChatProvider中的消息列表来构建ListView。
这种架构的好处是显而易见的:代码职责清晰,便于单元测试(可以Mock数据层),当需要更换AI服务提供商或添加新功能时,影响范围被控制在最小。
3.2 UI/UX实现细节与动画
从项目截图看,作者在UI上下了功夫。要实现类似的现代感,可以从以下几个方面入手:
- 主题与明暗模式 :使用Flutter的
ThemeData来定义一套完整的色彩、字体和形状规范。通过一个SettingsProvider来管理用户选择的主题模式(亮色/暗色),并在整个App中应用。记得在MaterialApp的theme和darkTheme属性中分别配置。 - 自定义AppBar与导航栏 :设计一个带有品牌标识、主题切换按钮和菜单抽屉的AppBar。底部导航栏或侧边栏用于在三大核心功能(聊天、绘画、翻译)间切换。
- 聊天气泡与列表 :聊天界面是重点。为用户和AI的消息设计不同的气泡样式(颜色、对齐方式、尾部装饰)。使用
ListView.separated或ListView.builder来高效构建长列表。为AI消息的流入添加一个微妙的渐现动画(FadeTransition)或滑动动画,能极大提升对话的生动感。 - Lottie动画集成 :在加载等待、空状态或操作成功反馈时使用Lottie动画,比传统的旋转图标更有表现力。使用
lottie包,你可以轻松地将JSON格式的动画文件嵌入到应用中。例如,在等待AI回复时,播放一个思考中的大脑动画。 - 页面过渡动画 :使用
PageRouteBuilder自定义页面之间的转场动画,比如从主页面进入聊天详情页时的渐变+缩放效果,能让应用感觉更精致。
3.3 第三方服务集成要点
项目提到了Facebook Ads和AppWrite的集成,这些都是扩展应用能力的常见选择。
- Facebook Ads(用于示例) :集成广告SDK主要是为了应用变现。你需要按照Facebook开发者文档,在项目中添加
audience_network依赖,并在Android的AndroidManifest.xml和iOS的Info.plist中配置必要的元数据和权限。初始化SDK后,就可以在UI的合适位置(如聊天列表底部)插入横幅广告或插页式广告组件。 务必注意广告的展示频率和位置,不能影响核心用户体验,并且要严格遵守各平台关于广告内容与展示的开发者政策。 - AppWrite云集成 :AppWrite是一个开源的BaaS(后端即服务),可以用来处理用户认证、数据库存储、文件存储等后端需求。在这个AI助手应用中,可以用它来存储用户的对话历史(实现跨设备同步)、保存生成的图片、或者管理用户配置。集成时,需要在AppWrite云端创建项目、配置数据库集合,然后在Flutter端使用
appwrite包进行数据读写操作。这相当于为你的应用快速添加了一个可扩展的后端。
4. 从零开始的开发实操指南
假设我们现在要从零开始,构建一个类似功能的AI助手应用。我会把关键步骤和代码要点梳理出来,你可以跟着一步步实现。
4.1 环境搭建与项目初始化
首先,确保你的开发环境就绪:
- 安装Flutter SDK并配置好环境变量。运行
flutter doctor检查,确保没有警告项,尤其是Android和iOS的开发环境。 - 选择一个IDE,推荐Android Studio(IntelliJ)或VS Code,并安装好Flutter和Dart插件。
- 打开终端,运行
flutter create ai_assistant_flutter创建一个新的Flutter项目。 - 进入项目目录,打开
pubspec.yaml文件,这是我们管理依赖的入口。根据前面提到的功能,我们需要预先添加一些核心依赖:
dependencies:
flutter:
sdk: flutter
# 状态管理 - 以Riverpod为例
flutter_riverpod: ^2.4.9
# 网络请求
dio: ^5.4.0
# 环境变量管理
flutter_dotenv: ^5.1.0
# 图片缓存
cached_network_image: ^3.3.0
# Lottie动画
lottie: ^2.7.0
# 应用内广告(如需要)
facebook_audience_network: ^0.15.0
# Google Gemini SDK(如果官方有)
# google_generative_ai: ^0.2.0
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^3.0.0
运行 flutter pub get 来获取所有依赖包。
4.2 核心API服务层封装
接下来,创建 lib/services/ 目录,在这里封装所有对外的API调用。
1. OpenAI服务封装 ( openai_service.dart ):
import 'dart:convert';
import 'package:dio/dio.dart';
import 'package:flutter_dotenv/flutter_dotenv.dart';
class OpenAIService {
final Dio _dio = Dio();
static final String? _apiKey = dotenv.get('OPENAI_API_KEY');
static const String _baseUrl = 'https://api.openai.com/v1';
OpenAIService() {
// 配置Dio拦截器,统一添加认证头
_dio.interceptors.add(InterceptorsWrapper(
onRequest: (options, handler) {
options.headers['Authorization'] = 'Bearer $_apiKey';
options.headers['Content-Type'] = 'application/json';
return handler.next(options);
},
));
}
// 聊天补全
Future<String> createChatCompletion(List<Map<String, String>> messages, {String model = 'gpt-3.5-turbo'}) async {
try {
final response = await _dio.post(
'$_baseUrl/chat/completions',
data: jsonEncode({
'model': model,
'messages': messages,
'max_tokens': 1000,
'temperature': 0.7,
}),
);
return response.data['choices'][0]['message']['content'];
} on DioException catch (e) {
// 这里可以细化错误处理,比如根据状态码提示“额度不足”、“网络超时”等
throw Exception('OpenAI API请求失败: ${e.message}');
}
}
// 图像生成
Future<List<String>> generateImage(String prompt, {int n = 1, String size = '1024x1024'}) async {
try {
final response = await _dio.post(
'$_baseUrl/images/generations',
data: jsonEncode({
'prompt': prompt,
'n': n,
'size': size,
}),
);
List<String> imageUrls = [];
for (var item in response.data['data']) {
imageUrls.add(item['url']);
}
return imageUrls;
} on DioException catch (e) {
throw Exception('DALL·E图像生成失败: ${e.message}');
}
}
}
2. 创建环境变量文件: 在项目根目录创建 .env 文件,内容如下:
OPENAI_API_KEY=你的_openai_api_key_在这里
GEMINI_API_KEY=你的_gemini_api_key_在这里
切记 ,将 .env 添加到 .gitignore 文件中,避免密钥泄露。
4.3 状态管理与业务逻辑实现
以聊天功能为例,使用Riverpod实现状态管理。
1. 定义数据模型 ( lib/models/chat_message.dart ):
class ChatMessage {
final String id;
final String role; // 'user' or 'assistant'
final String content;
final DateTime timestamp;
final bool isLoading; // 用于标记是否为正在生成的AI消息
ChatMessage({
required this.id,
required this.role,
required this.content,
DateTime? timestamp,
this.isLoading = false,
}) : timestamp = timestamp ?? DateTime.now();
// 可以添加toMap/fromMap方法,方便持久化存储
}
2. 创建聊天状态管理Provider ( lib/providers/chat_provider.dart ):
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ai_assistant_flutter/models/chat_message.dart';
import 'package:ai_assistant_flutter/services/openai_service.dart';
// 状态类
class ChatState {
final List<ChatMessage> messages;
final bool isGenerating;
final String? error;
ChatState({
this.messages = const [],
this.isGenerating = false,
this.error,
});
// 复制更新方法,用于不可变状态
ChatState copyWith({
List<ChatMessage>? messages,
bool? isGenerating,
String? error,
}) {
return ChatState(
messages: messages ?? this.messages,
isGenerating: isGenerating ?? this.isGenerating,
error: error ?? this.error,
);
}
}
// 状态管理Notifier
class ChatNotifier extends StateNotifier<ChatState> {
final OpenAIService _openAIService;
ChatNotifier(this._openAIService) : super(ChatState());
// 发送消息
Future<void> sendMessage(String text, {String model = 'gpt-3.5-turbo'}) async {
// 1. 添加用户消息
final userMessage = ChatMessage(
id: DateTime.now().millisecondsSinceEpoch.toString(),
role: 'user',
content: text,
);
state = state.copyWith(
messages: [...state.messages, userMessage],
isGenerating: true,
error: null,
);
// 2. 添加一个临时的、加载中的AI消息占位
final tempAiMessageId = (DateTime.now().millisecondsSinceEpoch + 1).toString();
final loadingAiMessage = ChatMessage(
id: tempAiMessageId,
role: 'assistant',
content: '...',
isLoading: true,
);
state = state.copyWith(messages: [...state.messages, loadingAiMessage]);
try {
// 3. 准备对话历史,调用API
final messageHistory = state.messages
.where((msg) => !msg.isLoading)
.map((msg) => {'role': msg.role, 'content': msg.content})
.toList();
final aiResponse = await _openAIService.createChatCompletion(messageHistory, model: model);
// 4. 移除加载占位,添加真实的AI回复
final realAiMessage = ChatMessage(
id: tempAiMessageId, // 使用相同的ID替换
role: 'assistant',
content: aiResponse,
isLoading: false,
);
final updatedMessages = List<ChatMessage>.from(state.messages);
final index = updatedMessages.indexWhere((msg) => msg.id == tempAiMessageId);
if (index != -1) {
updatedMessages[index] = realAiMessage;
}
state = state.copyWith(messages: updatedMessages, isGenerating: false);
} catch (e) {
// 5. 出错处理:移除加载占位,显示错误
final updatedMessages = state.messages.where((msg) => msg.id != tempAiMessageId).toList();
state = state.copyWith(
messages: updatedMessages,
isGenerating: false,
error: e.toString(),
);
// 可以在这里添加错误提示,如使用SnackBar
}
}
// 清空对话
void clearChat() {
state = ChatState();
}
}
// 全局Provider定义
final chatProvider = StateNotifierProvider<ChatNotifier, ChatState>((ref) {
final openAIService = ref.watch(openAiServiceProvider); // 需要先定义openAiServiceProvider
return ChatNotifier(openAIService);
});
4.4 主要UI页面构建
聊天主页面 ( lib/pages/chat_page.dart ) 简化示例:
import 'package:flutter/material.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
import 'package:ai_assistant_flutter/providers/chat_provider.dart';
import 'package:ai_assistant_flutter/widgets/chat_bubble.dart'; // 假设有一个自定义的气泡组件
class ChatPage extends ConsumerWidget {
final TextEditingController _textController = TextEditingController();
@override
Widget build(BuildContext context, WidgetRef ref) {
final chatState = ref.watch(chatProvider);
final chatNotifier = ref.read(chatProvider.notifier);
return Scaffold(
appBar: AppBar(title: Text('AI对话助手')),
body: Column(
children: [
// 聊天消息列表
Expanded(
child: ListView.builder(
padding: EdgeInsets.all(8.0),
reverse: false, // 通常最新消息在底部,所以不reverse,靠ListView的滚动
itemCount: chatState.messages.length,
itemBuilder: (context, index) {
final message = chatState.messages[index];
return ChatBubble(message: message);
},
),
),
// 错误提示
if (chatState.error != null)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: Text(
'出错: ${chatState.error}',
style: TextStyle(color: Colors.red),
),
),
// 输入区域
Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
children: [
Expanded(
child: TextField(
controller: _textController,
decoration: InputDecoration(
hintText: '输入您的问题...',
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(24.0),
),
contentPadding: EdgeInsets.symmetric(horizontal: 16.0),
),
onSubmitted: (_) => _sendMessage(chatNotifier),
),
),
SizedBox(width: 8.0),
CircleAvatar(
backgroundColor: chatState.isGenerating ? Colors.grey : Theme.of(context).primaryColor,
child: IconButton(
icon: chatState.isGenerating
? SizedBox(
width: 20,
height: 20,
child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white),
)
: Icon(Icons.send, color: Colors.white),
onPressed: chatState.isGenerating ? null : () => _sendMessage(chatNotifier),
),
),
],
),
),
],
),
);
}
void _sendMessage(ChatNotifier notifier) {
final text = _textController.text.trim();
if (text.isEmpty) return;
notifier.sendMessage(text);
_textController.clear();
}
}
图像生成和翻译页面的构建逻辑类似,核心都是:UI构建、用户输入捕获、调用对应的Provider方法、根据状态更新UI。
5. 开发中的常见问题与优化策略
在实际开发这类AI集成应用时,你肯定会遇到一些共性问题。这里我总结了一些常见坑点和优化思路。
5.1 网络请求与错误处理
网络请求是这类应用最不稳定的环节。除了基本的 try-catch ,你需要更精细化的处理:
- 超时设置 :在Dio中务必配置连接超时、发送超时和接收超时。
final dio = Dio(BaseOptions( connectTimeout: Duration(seconds: 10), sendTimeout: Duration(seconds: 10), receiveTimeout: Duration(seconds: 30), // 生成式AI响应可能较慢 )); - 重试机制 :对于因网络波动导致的失败,可以实现简单的重试逻辑。但要注意,对于OpenAI API因额度不足(402状态码)或内容违规(400状态码)返回的错误,不应重试。
- 友好的错误提示 :不要直接把
DioException的原始信息抛给用户。应该根据错误类型(网络错误、API密钥无效、额度不足、内容过滤等)转换为用户能理解的中文提示,如“网络连接超时,请检查后重试”或“AI服务暂时无法响应,请稍后再试”。 - 加载状态管理 :在请求发起和接收期间,必须清晰地向用户反馈加载状态(如按钮禁用、显示进度条、消息区域显示“正在思考...”)。这能有效防止用户因等待而重复点击。
5.2 性能优化与用户体验
- 图片加载优化 :这是图像生成模块的性能关键。务必使用
cached_network_image,并合理配置placeholder(加载中)和errorWidget(加载失败)的显示。对于大量图片的列表,考虑使用ListView或GridView的懒加载特性,并设置cacheExtent预渲染区域以提升滚动性能。 - 对话历史管理 :长时间的对话会导致
messages列表越来越大,每次调用API发送的上下文也会变长,增加成本和延迟。建议提供“清空对话”按钮。更高级的做法是,实现一个本地数据库(如sqflite或hive)来存储历史对话,并设计一个摘要机制,在上下文窗口有限时,自动将早期对话总结成一段摘要,而不是发送全部原始内容。 - 状态持久化 :用户不希望每次打开App都从头开始。使用
shared_preferences或hive来持久化用户的主题设置、选择的AI模型、API密钥(经过安全加密后)等配置信息。 - 减少不必要的重建 :在构建UI时,使用
Consumer精确订阅所需的状态变量,避免因父Widget状态变化导致整个子树重建。对于复杂的子Widget,考虑使用const构造函数或Memoization技术。
5.3 发布前检查清单
当你完成开发,准备打包发布APK或上传到应用商店前,请务必检查以下事项:
- 移除所有调试信息 :检查是否还有
print语句、调试横幅(debugShowCheckedModeBanner: false)和测试用的API密钥。 - 混淆与压缩代码(Android) :在
android/app/build.gradle中启用代码混淆(ProGuard或R8)以减小APK体积并增加反编译难度。buildTypes { release { signingConfig signingConfigs.release minifyEnabled true // 启用代码压缩混淆 shrinkResources true // 移除无用资源 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } - 配置应用图标与启动图 :按照Flutter官方文档,为Android和iOS分别配置好各尺寸的应用图标和启动屏。
- 权限声明审查 :检查
AndroidManifest.xml和Info.plist,确保只声明了应用实际需要的权限(如网络权限)。对于AI应用,通常只需要网络权限。 - 隐私政策与条款 :如果你的应用收集了用户输入的数据(对话内容、生成的图片),即使只是临时用于API调用,也最好准备一份隐私政策,说明数据如何被使用、存储(如果存储)以及与第三方AI服务共享的情况。许多应用商店要求提供隐私政策链接。
- API成本与限额监控 :正式上线后,务必在OpenAI、Google Cloud等平台设置用量告警和预算限制,防止因意外流量或恶意使用导致巨额账单。
5.4 扩展方向与进阶思考
这个基础框架之上,还有很大的扩展空间:
- 多模态交互 :除了文本和图片,是否可以集成语音输入输出?使用
speech_to_text和text_to_speech包,让用户能和AI语音对话。 - 本地模型部署 :对于隐私要求高的场景,可以研究在设备端运行小型开源模型(如通过
flutter_tflite集成某些轻量级模型),但这会对应用体积和计算性能提出挑战。 - 提示词工程与管理 :为翻译、总结、代码生成等不同场景预设高质量的提示词(Prompt)模板,并允许用户保存和自定义自己的模板库。
- 后端代理与缓存 :如前所述,构建自己的后端服务来代理所有AI API调用。这样做除了保护密钥,还可以在后端实现响应缓存(对相同问题缓存答案以节省成本和提速)、访问频率限制、用户认证和付费套餐管理等高级功能。
开发这样一个综合性的Flutter AI应用,最大的收获不仅仅是学会了调用几个API,而是如何将不同的技术模块(状态管理、网络、UI、本地存储)有机地整合在一起,构建出一个稳定、可用、用户体验良好的产品。过程中遇到的每一个错误和性能瓶颈,都是宝贵的经验。建议你在实现基本功能后,多思考如何优化,并尝试添加一两个自己构思的特色功能,这才是从“复现”到“掌握”的关键一步。
更多推荐



所有评论(0)