共计 7589 个字符,预计需要花费 19 分钟才能阅读完成。
我用 Flutter Gemini 写了一个水贴 APP
视频
https://youtu.be/sEpJWfNwbmk
https://www.bilibili.com/video/BV1gH4y177sy/
前言
原文 https://ducafecat.com/blog/flutter-gemini-ai-integration
本文通过 Flutter 插件 google_generative_ai 疾速的集成了 google ai gemini 来实现一个水贴的工具。
代码
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_gemini
gemini 介绍
门户站
https://gemini.google.com/
开发站
https://ai.google.dev/
Google Cloud 免费
https://console.cloud.google.com
参考
https://medium.com/flutter/harness-the-gemini-api-in-your-dar…
https://ai.google.dev/tutorials/dart_quickstart?hl=zh-cn
https://ai.google.dev/models/gemini?hl=zh-cn
技术限度
限度国家 IP
限度模拟器运行,须要真机运行
筹备工作
获取 gemini api key
https://aistudio.google.com/app/apikey?hl=zh-cn
模型阐明
https://ai.google.dev/models/gemini?hl=zh-cn
测试无效
申请地址 url
https://generativelanguage.googleapis.com/v1beta/models/gemini-pro:generateContent?key=YOUR_API_KEY
参数
{ | |
"contents": [ | |
{ | |
"parts": [ | |
{"text": "介绍下如何疾速学习 Flutter."} | |
] | |
} | |
] | |
} |
postman 测试
Flutter 开发步骤
增加 pub 包
pubspec.yaml
dependencies: | |
flutter: | |
sdk: flutter | |
# The following adds the Cupertino Icons font to your application. | |
# Use with the CupertinoIcons class for iOS style icons. | |
cupertino_icons: ^1.0.2 | |
google_generative_ai: ^0.2.1 | |
dio: ^5.4.1 | |
flutter_markdown: ^0.6.20 |
插件包站:
https://flutter.ducafecat.com
https://pub.dev
筹备图片
pubspec.yaml
# The following section is specific to Flutter packages. | |
flutter: | |
# The following line ensures that the Material Icons font is | |
# included with your application, so that you can use the icons in | |
# the material Icons class. | |
uses-material-design: true | |
# To add assets to your application, add an assets section, like this: | |
assets: | |
- assets/images/ |
筹备了 1.jpg 2.jpg , 一会让 gemini 辨认下图片内容。
配置 API Key
.vscode/launch.json
{ | |
// 应用 IntelliSense 理解相干属性。// 悬停以查看现有属性的形容。// 欲了解更多信息,请拜访: https://go.microsoft.com/fwlink/?linkid=830387 | |
"version": "0.2.0", | |
"configurations": [ | |
{ | |
"name": "flutter_application_gemini", | |
"request": "launch", | |
"type": "dart", | |
"flutterMode": "debug", | |
"program": "lib/main.dart", | |
"args": ["--dart-define=API_KEY=you key"] | |
}, | |
{"name": "flutter_application_gemini (profile mode)", | |
"request": "launch", | |
"type": "dart", | |
"flutterMode": "profile" | |
}, | |
{"name": "flutter_application_gemini (release mode)", | |
"request": "launch", | |
"type": "dart", | |
"flutterMode": "release" | |
} | |
] | |
} |
lib/main.dart
class MyApp extends StatelessWidget {const MyApp({super.key}); | |
static String apiKey = const String.fromEnvironment('API_KEY'); | |
...... |
首页
lib/index.dart
题目
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: const Text('Google Gemini AI 水贴'), | |
), | |
body: _buildView(),); | |
} |
主视图
Widget _buildView() { | |
return Container(padding: const EdgeInsets.all(10), | |
child: GridView.extent( | |
maxCrossAxisExtent: 150, | |
mainAxisSpacing: 20, | |
crossAxisSpacing: 20, | |
children: <Widget>[ | |
// 1 内容生成 | |
_buildItem( | |
"内容生成", | |
onTap: () => Navigator.push( | |
context, | |
MaterialPageRoute(builder: (context) => const ContentPage()), | |
), | |
), | |
// 2 散失内容生成 | |
_buildItem( | |
"散失内容", | |
onTap: () => Navigator.push( | |
context, | |
MaterialPageRoute(builder: (context) => const StreamPage()), | |
), | |
), | |
// 3 图片辨认 | |
_buildItem( | |
"图片辨认", | |
onTap: () => Navigator.push( | |
context, | |
MaterialPageRoute(builder: (context) => const VersionPage()), | |
), | |
), | |
], | |
), | |
); | |
} |
单元格
Widget _buildItem(String title, {Function()? onTap}) { | |
return GestureDetector( | |
onTap: onTap, | |
child: Container( | |
decoration: BoxDecoration(border: Border.all(color: Colors.grey), | |
), | |
child: Center( | |
child: Text( | |
title, | |
style: const TextStyle(fontSize: 18), | |
)), | |
), | |
); | |
} |
内容生成
lib/content.dart
题目、发送、复制
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: const Text('内容生成'), | |
actions: [ | |
// 复制 | |
IconButton(onPressed: () {Clipboard.setData(ClipboardData(text: _content)); | |
}, | |
icon: const Icon(Icons.copy), | |
), | |
// 提交 | |
IconButton(onPressed: () async {var res = await _doContentGeneration(_textController.text); | |
setState(() {_content = res ?? "";}); | |
}, | |
icon: const Icon(Icons.send), | |
) | |
], | |
), | |
body: _buildView(),); | |
} |
输入框
final TextEditingController _textController = TextEditingController(text: "你当初是论坛水贴王子,请介绍下油管 up 主猫哥 (ducafecat), 他讲 flutter 技术,而且光明巫术也很好。");
@override | |
void dispose() {_textController.dispose(); | |
super.dispose();} |
ai 生成
/// ai 内容 | |
String _content = ""; |
/// 生成文字内容 | |
Future<String?> _doContentGeneration(String value) async { | |
// 生成模型 | |
final model = GenerativeModel( | |
// 模型名称 | |
model: 'gemini-pro', | |
// API 密钥 | |
apiKey: MyApp.apiKey, | |
// 依据可能的无害性调整您看到回复的可能性。基于内容无害性的概率进行屏蔽。safetySettings: [SafetySetting(HarmCategory.harassment, HarmBlockThreshold.medium), // 骚扰 | |
SafetySetting(HarmCategory.hateSpeech, HarmBlockThreshold.medium), // 怨恨舆论 | |
SafetySetting(HarmCategory.sexuallyExplicit, HarmBlockThreshold.medium), // x 暗示 | |
SafetySetting(HarmCategory.dangerousContent, HarmBlockThreshold.medium), // 危险内容 | |
], | |
); | |
// 发问词列表 | |
final content = [Content.text(value), | |
]; | |
// 申请返回 | |
final response = await model.generateContent(content); | |
return response.text; | |
} |
主视图
Widget _buildView() { | |
return Column( | |
children: [ | |
// 输入框 | |
TextField( | |
controller: _textController, | |
maxLines: 3, | |
decoration: const InputDecoration(labelText: '输出你的提醒词',), | |
), | |
// 内容 | |
Expanded(child: Markdown(data: _content)), | |
], | |
); | |
} |
散失内容
lib/stream.dart
流式文字内容
/// 生成文字内容 | |
Future<void> _doContentStream(String value) async { | |
// 生成模型 | |
final model = GenerativeModel( | |
// 模型名称 | |
model: 'gemini-pro', | |
// API 密钥 | |
apiKey: MyApp.apiKey, | |
// 依据可能的无害性调整您看到回复的可能性。基于内容无害性的概率进行屏蔽。safetySettings: [SafetySetting(HarmCategory.harassment, HarmBlockThreshold.medium), // 骚扰 | |
SafetySetting(HarmCategory.hateSpeech, HarmBlockThreshold.medium), // 怨恨舆论 | |
SafetySetting(HarmCategory.sexuallyExplicit, HarmBlockThreshold.medium), // x 暗示 | |
SafetySetting(HarmCategory.dangerousContent, HarmBlockThreshold.medium), // 危险内容 | |
], | |
); | |
// 发问词列表 | |
final content = [Content.text(value), | |
]; | |
// 清空 | |
setState(() {_content = "";}); | |
// 散失接管 | |
model.generateContentStream(content).listen((event) {setState(() {_content += event.text ?? "";}); | |
}); | |
} |
图片辨认
lib/vision.dart
build
@override | |
Widget build(BuildContext context) { | |
return Scaffold( | |
appBar: AppBar(title: const Text('图片辨认'), | |
actions: [ | |
// 复制 | |
IconButton(onPressed: () {Clipboard.setData(ClipboardData(text: _content)); | |
}, | |
icon: const Icon(Icons.copy), | |
), | |
// 提交 | |
IconButton(onPressed: () async {var res = await _doVisionGeneration(_textController.text); | |
setState(() {_content = res ?? "";}); | |
}, | |
icon: const Icon(Icons.send), | |
) | |
], | |
), | |
body: _buildView(),); | |
} |
主视图
Widget _buildView() { | |
return Column( | |
children: [ | |
// 输入框 | |
TextField( | |
controller: _textController, | |
// maxLines: 2, | |
decoration: const InputDecoration(labelText: '输出你的提醒词',), | |
), | |
// 图片列表 | |
SizedBox( | |
height: 160, | |
child: _buildImagesList(),), | |
// 内容 | |
Expanded(child: Markdown(data: _content)), | |
], | |
); | |
} |
输入框
final TextEditingController _textController = | |
TextEditingController(text: "这两张图片是对于什么内容?"); |
@override | |
void dispose() {_textController.dispose(); | |
super.dispose();} |
图片列表
/// 图片列表 | |
Widget _buildImagesList() { | |
return Container(padding: const EdgeInsets.all(10), | |
child: GridView.extent( | |
maxCrossAxisExtent: 150, | |
mainAxisSpacing: 20, | |
crossAxisSpacing: 20, | |
children: <Widget>[Image.asset("assets/images/1.jpg"), | |
Image.asset("assets/images/2.jpg"), | |
], | |
), | |
); | |
} |
图片辨认
/// ai 内容 | |
String _content = ""; |
/// 读取图片 | |
Future<Uint8List> loadImage(String path) async {final ByteData data = await rootBundle.load(path); | |
return data.buffer.asUint8List();} |
/// 图片辨认 | |
Future<String?> _doVisionGeneration(String value) async { | |
// 生成模型 | |
final model = GenerativeModel( | |
// 模型名称 | |
model: 'gemini-pro-vision', | |
// API 密钥 | |
apiKey: MyApp.apiKey, | |
// 依据可能的无害性调整您看到回复的可能性。基于内容无害性的概率进行屏蔽。safetySettings: [SafetySetting(HarmCategory.harassment, HarmBlockThreshold.medium), // 骚扰 | |
SafetySetting(HarmCategory.hateSpeech, HarmBlockThreshold.medium), // 怨恨舆论 | |
SafetySetting(HarmCategory.sexuallyExplicit, HarmBlockThreshold.medium), // x 暗示 | |
SafetySetting(HarmCategory.dangerousContent, HarmBlockThreshold.medium), // 危险内容 | |
], | |
); | |
// 发问词列表 | |
final (firstImage, secondImage) = await (loadImage('assets/images/1.jpg'), | |
loadImage('assets/images/2.jpg'), | |
// File('assets/images/1.jpg').readAsBytes(), | |
// File('assets/images/1.jpg').readAsBytes()).wait; | |
final prompt = TextPart(value); | |
final imageParts = [DataPart('image/jpeg', firstImage), | |
DataPart('image/jpeg', secondImage), | |
]; | |
final response = await model.generateContent([Content.multi([prompt, ...imageParts]) | |
]); | |
// 申请返回 | |
return response.text; | |
} |
代码
https://github.com/ducafecat/flutter_develop_tips/tree/main/flutter_application_gemini
小结
感激浏览本文
如果有什么倡议,请在评论中让我晓得。我很乐意改良。
© 猫哥
ducafecat.com
end