Flutter图像生成插件flutter_stable_diffusion_core_ml的使用

Flutter图像生成插件flutter_stable_diffusion_core_ml的使用

flutter_stable_diffusion_core_ml 是一个用于在 macOS 和 iOS 平台上实现 Stable Diffusion 图像生成功能的 Flutter 插件。该插件基于 Apple 提供的 ml-stable-diffusion 实现。

使用方法

此插件已被 Flutter 官方推荐为 endorsed federated plugin,这意味着你可以直接使用 flutter_stable_diffusion 而无需手动添加到 pubspec.yaml 文件中。当使用时,它会自动包含在你的项目中。

然而,如果你需要导入此包以直接使用其 API,则仍需将其添加到 pubspec.yaml 文件中。


示例代码

以下是一个完整的示例代码,展示如何使用 flutter_stable_diffusion_core_ml 插件进行图像生成。

import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_stable_diffusion_platform_interface/flutter_stable_diffusion_platform_interface.dart';
import 'package:path_provider/path_provider.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String _status = 'idle'; // 当前状态
  late final TextEditingController _promptTextController =
      TextEditingController(); // 提示文本控制器
  late final TextEditingController _negativePromptTextController =
      TextEditingController(); // 负提示文本控制器
  late final TextEditingController _stepCountTextController =
      TextEditingController(text: "20"); // 步骤数控制器
  late final TextEditingController _scaleTextController =
      TextEditingController(text: "7.5"); // 指导比例控制器
  List<int>? _imageData; // 生成图像的数据
  bool _isLoaded = false; // 是否加载模型
  bool _generateing = false; // 是否正在生成
  bool _cancelGenerateing = false; // 是否取消生成
  ValueNotifier<String> _generateProgressValue =
      ValueNotifier<String>(''); // 生成进度通知器

  PlatformStableDiffusionPipeline? _pipline; // 管道实例
  PlatformStableDiffusionPipelineGenerateCancelToken? _cancelToken; // 取消令牌

  [@override](/user/override)
  void initState() {
    super.initState();
  }

  /// 开始生成图像
  void tapGenerate() async {
    if (_pipline == null ||
        _isLoaded == false ||
        _promptTextController.text.isEmpty) {
      return;
    }
    try {
      setState(() {
        _generateing = true;
        _cancelGenerateing = false;
        _status = 'generating';
      });
      _generateProgressValue.value = 'generating';
      _cancelToken = StableDiffusionPlatformInterface.instance!
          .createPlatformPipelineGenerateCancelToken();

      var result = await _pipline!.generate(
        PlatformStableDiffusionPipelineGenerateParams(
          prompt: _promptTextController.text,
          negativePrompt: _negativePromptTextController.text,
          stepCount: int.parse(_stepCountTextController.text),
          guidanceScale: double.parse(_scaleTextController.text),
        ),
        onProgress: (progress) async {
          _generateProgressValue.value =
              "${progress.step}/${progress.stepCount}";
        },
        cancelToken: _cancelToken,
      );

      if (result.isSuccess) {
        setState(() {
          _generateing = false;
          _cancelGenerateing = false;
          _imageData = result.imageData;
          _status = 'Success';
        });
      } else if (result.isCancelled) {
        setState(() {
          _generateing = false;
          _cancelGenerateing = false;
          _status = 'Cancelled';
        });
      } else {
        setState(() {
          _generateing = false;
          _cancelGenerateing = false;
          _status = '${result.message}';
        });
      }
    } catch (e) {
      await _cancelToken?.cancel();
      setState(() {
        _generateing = false;
        _cancelGenerateing = false;
        _status = 'generate fail, $e';
      });
    }
  }

  /// 取消生成
  void tapCancel() async {
    setState(() {
      _cancelGenerateing = true;
      _status = 'cancelling';
    });
    _cancelToken?.cancel();
  }

  /// 构建生成控件
  Widget buildGenerateWidgets(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        const Text("Prompts", style: TextStyle(fontWeight: FontWeight.bold)),
        const SizedBox(height: 4),
        Container(
          decoration: BoxDecoration(
            color: Theme.of(context).cardColor,
            borderRadius: BorderRadius.circular(4),
          ),
          height: 38,
          alignment: Alignment.centerLeft,
          child: TextField(
            controller: _promptTextController,
            decoration: const InputDecoration(
              isDense: true,
              border: InputBorder.none,
              hintText: 'prompt',
            ),
          ),
        ),
        const SizedBox(height: 4),
        Container(
          decoration: BoxDecoration(
            color: Theme.of(context).cardColor,
            borderRadius: BorderRadius.circular(4),
          ),
          height: 38,
          alignment: Alignment.centerLeft,
          child: TextField(
            controller: _negativePromptTextController,
            decoration: const InputDecoration(
              isDense: true,
              border: InputBorder.none,
              hintText: 'negative prompt',
            ),
          ),
        ),
        const Divider(),
        Row(
          children: [
            Expanded(
              child: Container(
                decoration: BoxDecoration(
                  color: Theme.of(context).cardColor,
                  borderRadius: BorderRadius.circular(4),
                ),
                height: 38,
                alignment: Alignment.centerLeft,
                child: TextField(
                  controller: _stepCountTextController,
                  keyboardType:
                      const TextInputType.numberWithOptions(decimal: false),
                  onChanged: (text) {
                    if (text.isEmpty) {
                      _stepCountTextController.value = const TextEditingValue(
                        text: "1",
                        selection:
                            TextSelection(baseOffset: 0, extentOffset: 1),
                      );
                    }
                  },
                  decoration: const InputDecoration(
                    isDense: true,
                    border: InputBorder.none,
                    prefix: Text("step count:"),
                  ),
                ),
              ),
            ),
            Container(
              width: 1,
              color: Theme.of(context).dividerColor,
              height: 28,
              margin: const EdgeInsets.symmetric(horizontal: 4),
            ),
            Expanded(
              child: Container(
                decoration: BoxDecoration(
                  color: Theme.of(context).cardColor,
                  borderRadius: BorderRadius.circular(4),
                ),
                height: 38,
                alignment: Alignment.centerLeft,
                child: TextField(
                  controller: _scaleTextController,
                  keyboardType:
                      const TextInputType.numberWithOptions(decimal: true),
                  onChanged: (text) {
                    if (text.isEmpty) {
                      _scaleTextController.value = const TextEditingValue(
                        text: "1",
                        selection:
                            TextSelection(baseOffset: 0, extentOffset: 1),
                      );
                    }
                  },
                  decoration: const InputDecoration(
                    isDense: true,
                    border: InputBorder.none,
                    prefix: Text("guidance scale:"),
                    hintText: '',
                  ),
                ),
              ),
            ),
          ],
        ),
        Row(
          children: [
            Expanded(
              child: TextButton(
                onPressed:
                    !_generateing && !_cancelGenerateing ? tapGenerate : null,
                child: const Text("Generate"),
              ),
            ),
            Expanded(
              child: TextButton(
                onPressed:
                    _generateing && !_cancelGenerateing ? tapCancel : null,
                child: const Text("Cancel"),
              ),
            ),
          ],
        ),
        ValueListenableBuilder(
          valueListenable: _generateProgressValue,
          builder: (context, value, child) {
            return Text(value);
          },
        ),
      ],
    );
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            crossAxisAlignment: CrossAxisAlignment.center,
            children: [
              TextButton(
                onPressed: () async {
                  try {
                    var result = await FilePicker.platform.getDirectoryPath();
                    if (result != null) {
                      setState(() {
                        _isLoaded = false;
                        _status = 'loading';
                      });
                      await _cancelToken?.cancel();
                      await _pipline?.dispose();
                      _pipline = StableDiffusionPlatformInterface.instance!
                          .createPlatformPipeline(
                        PlatformStableDiffusionPipelineCreationParams(
                            modelPath: result),
                      );
                      await _pipline?.loadResources();
                      setState(() {
                        _isLoaded = true;
                        _status = 'loaded';
                      });
                    }
                  } catch (e) {
                    setState(() {
                      _status = 'model load fail, $e';
                      _pipline = null;
                      _isLoaded = false;
                    });
                  }
                },
                child: const Text("picker model"),
              ),
              if (_imageData != null)
                Image.memory(Uint8List.fromList(_imageData!)),
              Text('current status: $_status\n'),
              if (_isLoaded) buildGenerateWidgets(context),
            ],
          ),
        ),
      ),
    );
  }
}

更多关于Flutter图像生成插件flutter_stable_diffusion_core_ml的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter图像生成插件flutter_stable_diffusion_core_ml的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


flutter_stable_diffusion_core_ml 是一个 Flutter 插件,用于在 iOS 设备上使用 Core ML 框架运行 Stable Diffusion 模型来生成图像。这个插件允许你在 Flutter 应用中集成 AI 图像生成功能。

安装插件

首先,你需要在 pubspec.yaml 文件中添加 flutter_stable_diffusion_core_ml 插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  flutter_stable_diffusion_core_ml: ^0.0.1  # 请检查最新版本

然后运行 flutter pub get 来安装依赖。

配置 iOS 项目

由于这个插件依赖于 Core ML,因此它只能在 iOS 设备上运行。你需要确保你的 iOS 项目已经正确配置。

  1. 启用 Core ML 支持:在 Xcode 中打开你的 iOS 项目,确保 CoreML.framework 已经添加到 Linked Frameworks and Libraries 中。

  2. 添加模型文件:你需要将 Stable Diffusion 的 Core ML 模型文件(通常是 .mlmodel 文件)添加到你的 Xcode 项目中。确保模型文件被正确包含在 Copy Bundle Resources 中。

使用插件

在你的 Flutter 代码中,你可以使用 flutter_stable_diffusion_core_ml 插件来生成图像。以下是一个简单的示例:

import 'package:flutter/material.dart';
import 'package:flutter_stable_diffusion_core_ml/flutter_stable_diffusion_core_ml.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Stable Diffusion Example'),
        ),
        body: Center(
          child: ImageGenerator(),
        ),
      ),
    );
  }
}

class ImageGenerator extends StatefulWidget {
  [@override](/user/override)
  _ImageGeneratorState createState() => _ImageGeneratorState();
}

class _ImageGeneratorState extends State<ImageGenerator> {
  Uint8List? _generatedImage;

  Future<void> _generateImage() async {
    try {
      final result = await FlutterStableDiffusionCoreMl.generateImage(
        prompt: "A beautiful landscape with mountains and a river",
        steps: 50,
        guidanceScale: 7.5,
        seed: 42,
      );

      setState(() {
        _generatedImage = result;
      });
    } catch (e) {
      print("Error generating image: $e");
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        if (_generatedImage != null)
          Image.memory(_generatedImage!)
        else
          Text("No image generated yet."),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: _generateImage,
          child: Text("Generate Image"),
        ),
      ],
    );
  }
}
回到顶部