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