我用 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