猫哥课堂 ducafecat.com
开通 VIP 会员, 观看所有视频、附件、猫哥密友、猫哥 VIP 群

我用 Flutter Gemini 写了一个水贴 APP

google gemini api

视频

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 来实现一个水贴的工具。

Gemini 水贴首页

gemini 内容生成

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-dart-and-flutter-apps-00573e560381

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

gemini ai key

模型说明

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 测试

postman gemini post

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

标题

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

标题、发送、复制

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 技术,而且黑暗巫术也很好。");
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

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: "这两张图片是关于什么内容?");
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

小结

感谢阅读本文

如果有什么建议,请在评论中让我知道。我很乐意改进。


flutter 学习路径


© 猫哥 ducafecat.com

end


Copyright 2024 ducafecat. All rights reserved.
微信: ducafecat, line: ducafecat,京ICP备2021009050号-3