Flutter网页端图片裁剪插件native_image_cropper_web的使用

发布于 1周前 作者 zlyuanteng 来自 Flutter

Flutter网页端图片裁剪插件 native_image_cropper_web 的使用

native_image_cropper_webnative_image_cropper 插件的Web实现。该插件允许开发者在Flutter Web应用中进行图片裁剪操作。

使用方法

由于该插件是被认可的联合插件(endorsed federated plugin),你可以直接使用 native_image_cropper,而不需要额外配置。当你添加 native_image_cropper 到你的项目时,这个Web实现会自动包含在你的应用中。

Web平台的限制

  • JPEG格式支持:由于Flutter引擎Skia不支持JPEG格式,当前版本只支持裁剪为PNG格式。
  • 并发问题:在Web平台上,Dart不支持隔离(isolate)用于并发处理,这意味着对于大图片,UI可能会冻结。开发团队计划在未来实现对JPEG的支持,并研究利用Web Workers来解决这个问题。

示例代码

以下是一个完整的示例demo,展示了如何在Flutter Web应用中使用 native_image_cropper_web 进行图片裁剪:

import 'dart:async';
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:native_image_cropper_web/native_image_cropper_web.dart';

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

class MyApp extends StatefulWidget {
  const MyApp({Key? key}) : super(key: key);

  static const String imageName = 'sail-boat';

  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final NativeImageCropperPlugin _nativeImageCropperWeb = NativeImageCropperPlugin();
  ImageFormat _format = ImageFormat.jpg;

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Native Image Cropper Web Example'),
        ),
        body: FutureBuilder<Uint8List>(
          future: _getBytes(),
          builder: (context, snapshot) {
            if (snapshot.hasData) {
              final bytes = snapshot.data!;
              return Column(
                children: [
                  Expanded(
                    child: Image.memory(bytes),
                  ),
                  const SizedBox(height: 20),
                  Row(
                    mainAxisAlignment: MainAxisAlignment.spaceEvenly,
                    children: [
                      DropdownButton<ImageFormat>(
                        value: _format,
                        items: [ImageFormat.png, ImageFormat.jpg].map((ImageFormat format) {
                          return DropdownMenuItem<ImageFormat>(
                            value: format,
                            child: Text(format.toString().split('.').last.toUpperCase()),
                          );
                        }).toList(),
                        onChanged: (ImageFormat? newValue) {
                          setState(() {
                            _format = newValue!;
                          });
                        },
                      ),
                      ElevatedButton.icon(
                        icon: const Icon(Icons.crop),
                        label: const Text('Crop Rect'),
                        onPressed: () => _crop(
                          context: context,
                          bytes: bytes,
                          method: _nativeImageCropperWeb.cropRect,
                        ),
                      ),
                      ElevatedButton.icon(
                        icon: const Icon(Icons.crop),
                        label: const Text('Crop Oval'),
                        onPressed: () => _crop(
                          context: context,
                          bytes: bytes,
                          method: _nativeImageCropperWeb.cropOval,
                        ),
                      ),
                    ],
                  ),
                ],
              );
            } else if (snapshot.hasError) {
              return Center(child: Text('Error: ${snapshot.error}'));
            }
            return const Center(child: CircularProgressIndicator());
          },
        ),
      ),
    );
  }

  Future<void> _crop({
    required BuildContext context,
    required Uint8List bytes,
    required Future<Uint8List> Function({
      required Uint8List bytes,
      required int x,
      required int y,
      required int width,
      required int height,
      required ImageFormat format,
    }) method,
  }) async {
    final croppedBytes = await method(
      bytes: bytes,
      x: 1200,
      y: 900,
      width: 600,
      height: 600,
      format: _format,
    );

    if (mounted) {
      Navigator.push(
        context,
        MaterialPageRoute(
          builder: (context) => ResultPage(
            bytes: croppedBytes,
            format: _format,
          ),
        ),
      );
    }
  }

  Future<Uint8List> _getBytes() async {
    final byteData = await rootBundle.load('assets/${MyApp.imageName}.png');
    return byteData.buffer.asUint8List();
  }
}

class ResultPage extends StatelessWidget {
  final Uint8List bytes;
  final ImageFormat format;

  const ResultPage({required this.bytes, required this.format, Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Cropped Image (${format.toString().split('.').last.toUpperCase()})'),
      ),
      body: Center(
        child: Image.memory(bytes),
      ),
    );
  }
}

说明

  • DropdownButton 用于选择裁剪后的图片格式。
  • ElevatedButton 提供了矩形和椭圆形裁剪选项。
  • FutureBuilder 用于异步加载初始图片并显示加载进度条。

请确保在 pubspec.yaml 文件中正确添加依赖项:

dependencies:
  flutter:
    sdk: flutter
  native_image_cropper: ^x.x.x # 替换为最新版本号
  # 其他依赖...

同时,在 pubspec.yaml 中添加图片资源:

flutter:
  assets:
    - assets/sail-boat.png

这样,你就可以在Flutter Web应用中实现图片裁剪功能了!


更多关于Flutter网页端图片裁剪插件native_image_cropper_web的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter网页端图片裁剪插件native_image_cropper_web的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


当然,下面是一个关于如何在Flutter网页端使用 native_image_cropper_web 插件来实现图片裁剪功能的代码示例。请确保你已经按照插件的官方文档完成了依赖配置和必要的设置。

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

dependencies:
  flutter:
    sdk: flutter
  native_image_cropper_web: ^x.y.z  # 请使用最新版本号替换 x.y.z

然后运行 flutter pub get 来获取依赖。

接下来,是一个简单的 Flutter 应用示例,展示了如何使用 native_image_cropper_web 插件来选择并裁剪图片。

main.dart

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: ImageCropperScreen(),
    );
  }
}

class ImageCropperScreen extends StatefulWidget {
  @override
  _ImageCropperScreenState createState() => _ImageCropperScreenState();
}

class _ImageCropperScreenState extends State<ImageCropperScreen> {
  File? croppedImage;

  Future<void> pickAndCropImage() async {
    try {
      // 打开文件选择器并选择一个图片文件
      File? imageFile = await NativeImageCropperWeb.pickImage(source: ImagePickerSourceType.galleryOrCamera);
      if (imageFile == null) return;

      // 定义裁剪选项
      CropOptions cropOptions = CropOptions(
        aspectRatioPresets: [
          CropAspectRatioPreset.square,
          CropAspectRatioPreset.ratio3x2,
          CropAspectRatioPreset.original,
          CropAspectRatioPreset.ratio4x3,
          CropAspectRatioPreset.ratio16x9
        ],
        androidUiSettings: AndroidUiSettings(
          toolbarTitle: 'Cropper',
          toolbarColor: Colors.deepOrange,
          toolbarWidgetColor: Colors.white,
          initAspectRatio: CropAspectRatioPreset.original,
          lockAspectRatio: false,
        ),
        iosUiSettings: IOSUiSettings(
          minimumAspectRatio: 1.0,
        ),
      );

      // 执行裁剪操作
      File? croppedFile = await NativeImageCropperWeb.cropImage(imageFile.path, cropOptions);
      setState(() {
        croppedImage = croppedFile;
      });
    } catch (e) {
      print(e);
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Cropper Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            ElevatedButton(
              onPressed: pickAndCropImage,
              child: Text('Pick and Crop Image'),
            ),
            if (croppedImage != null)
              Image.file(
                croppedImage!,
                width: 300,
                height: 300,
                fit: BoxFit.cover,
              ),
          ],
        ),
      ),
    );
  }
}

注意事项

  1. 权限处理:由于网页环境对文件系统访问的限制,你可能需要处理用户权限,但这通常是在用户选择文件时由浏览器自动处理的。

  2. 兼容性native_image_cropper_web 插件依赖于浏览器的原生功能,因此在不同浏览器上可能会有不同的行为表现。请确保在支持的浏览器上进行测试。

  3. UI定制:你可以根据需要调整 CropOptions 中的参数来定制裁剪器的UI和行为。

  4. 错误处理:示例代码中简单地打印了错误,但在实际应用中,你可能需要更细致的错误处理逻辑,例如向用户显示错误信息。

通过以上代码,你应该能够在Flutter网页端实现基本的图片选择和裁剪功能。如果你需要更多高级功能或定制,请参考 native_image_cropper_web 插件的官方文档。

回到顶部