跳转至

Flutter 图像生成

在 Flutter 中调用 DALL-E、Flux、Seedream 等图像生成模型,支持图像预览和下载。


功能预览

图像生成页面

左侧导航选择 🖼️ 图像生成 → 输入提示词 → 生成图片 → 网格展示结果


目录


基础概念

图像生成 API 接受文本提示,返回图像 URL 或 Base64 编码图像:

{
  'model': 'flux-1-dev',      // 图像模型
  'prompt': '一只可爱的柯基',  // 提示词
  'n': 1,                      // 生成数量
  'size': '1024x1024',         // 图像尺寸
  'response_format': 'url',    // url 或 b64_json
}

常用模型:

  • flux-1-dev / flux-1-pro - 高质量图像
  • black-forest-labs-flux-1-pro-ultra - 最高画质
  • dall-e-3 / dall-e-2 - OpenAI DALL-E 系列
  • doubao-seedream-4-0-250828 - 中文提示词更友好

调用图像生成 API

Future<List<String>> generateImages(String prompt, {
  int n = 1,
  String size = '1024x1024',
}) async {
  final response = await http.post(
    Uri.parse('${dotenv.env['APINEXUS_BASE_URL']!}/images/generations'),
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ${dotenv.env['APINEXUS_API_KEY']!}',
    },
    body: json.encode({
      'model': 'flux-1-dev',
      'prompt': prompt,
      'n': n,
      'size': size,
      'response_format': 'url',
    }),
  );
  final data = json.decode(response.body);
  return [for (final item in data['data']) item['url'] as String];
}

在 Flutter 中显示图像

使用 cached_network_image 实现带缓存的网络图像加载:

import 'package:cached_network_image/cached_network_image.dart';

class ImageDisplay extends StatelessWidget {
  final String url;
  const ImageDisplay(this.url, {super.key});

  @override
  Widget build(BuildContext context) {
    return CachedNetworkImage(
      imageUrl: url,
      placeholder: (_, __) => const Center(
        child: CircularProgressIndicator(),
      ),
      errorWidget: (_, __, ___) => const Icon(Icons.error),
      fit: BoxFit.cover,
    );
  }
}

图像下载到本地

import 'dart:io';
import 'package:http/http.dart' as http;
import 'package:flutter/services.dart';
import 'package:path_provider/path_provider.dart';

Future<File> downloadImage(String url, String filename) async {
  final bytes = await http.readBytes(Uri.parse(url));
  final dir = await getApplicationDocumentsDirectory();
  final file = File('${dir.path}/$filename');
  await file.writeAsBytes(bytes);
  return file;
}

参数详解

参数 类型 说明
model string 图像模型名称
prompt string 描述图像内容的文本,支持中英文
n number 生成图像数量,默认 1
size string 1024x1024, 512x512, 768x1024
response_format string url(默认)或 b64_json(Base64)

完整示例

一个完整的图像生成页面:

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

  @override
  State<ImageGenerationPage> createState() => _ImageGenerationPageState();
}

class _ImageGenerationPageState extends State<ImageGenerationPage> {
  final _controller = TextEditingController(text: '一只可爱的柯基');
  List<String> _urls = [];
  bool _loading = false;

  Future<void> _generate() async {
    if (_controller.text.isEmpty || _loading) return;
    setState(() => _loading = true);
    try {
      final urls = await generateImages(_controller.text);
      setState(() => _urls = urls);
    } catch (e) {
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('生成失败: $e')),
      );
    } finally {
      setState(() => _loading = false);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('图像生成')),
      body: Column(
        children: [
          Padding(
            padding: const EdgeInsets.all(16),
            child: Row(
              children: [
                Expanded(
                  child: TextField(
                    controller: _controller,
                    decoration: const InputDecoration(hintText: '描述图像内容'),
                  ),
                ),
                const SizedBox(width: 8),
                ElevatedButton(
                  onPressed: _loading ? null : _generate,
                  child: Text(_loading ? '生成中...' : '生成'),
                ),
              ],
            ),
          ),
          Expanded(
            child: GridView.builder(
              padding: const EdgeInsets.all(16),
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2,
                crossAxisSpacing: 8,
                mainAxisSpacing: 8,
              ),
              itemCount: _urls.length,
              itemBuilder: (_, i) => ClipRRect(
                borderRadius: BorderRadius.circular(8),
                child: Image.network(_urls[i], fit: BoxFit.cover),
              ),
            ),
          ),
        ],
      ),
    );
  }
}

继续学习