Flutter图片裁剪插件dynamic_image_crop的使用

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

Flutter图片裁剪插件dynamic_image_crop的使用

dynamic_image_crop 是一个用于在Flutter中裁剪图片成各种形状的插件。本文将详细介绍如何使用这个插件,并提供一个完整的示例demo。

介绍

dynamic_image_crop 支持将图片裁剪为用户绘制的形状,以及矩形、圆形和三角形等形状。它还允许更改裁剪形状线的颜色和宽度。

Demo GIF

你可以通过以下链接测试该插件:demo_link

特性

  • 支持将图片裁剪为用户绘制的形状。
  • 支持将图片裁剪为矩形、圆形和三角形形状。
  • 支持更改裁剪形状线的颜色。
  • 支持更改裁剪形状线的宽度。

使用方法

安装

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

dependencies:
  flutter:
    sdk: flutter
  dynamic_image_crop: ^latest_version

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

示例代码

以下是完整的示例代码,展示了如何使用dynamic_image_crop插件进行图片裁剪。

import 'dart:ui';
import 'package:dynamic_image_crop/dynamic_image_crop.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

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

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

  @override
  State<TestApp> createState() => _TestAppState();
}

class _TestAppState extends State<TestApp> {
  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: CropScreen(),
    );
  }
}

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

  @override
  State<CropScreen> createState() => _CropScreenState();
}

class _CropScreenState extends State<CropScreen> {
  CropType cropType = CropType.none;
  Uint8List? image;
  final cropController = CropController();
  final urlController = TextEditingController();

  final initialUrl = 'https://miro.medium.com/v2/1*bzC0ul7jBVhOJiastVGKlw.png';

  @override
  void initState() {
    urlController.text = initialUrl;
    loadImage(initialUrl);
    super.initState();
  }

  void loadImage(
    String url, {
    void Function(Uint8List)? callback,
    ImageByteFormat imageFormat = ImageByteFormat.png,
  }) {
    Image.network(url).image.resolve(ImageConfiguration.empty).addListener(
      ImageStreamListener((info, _) async {
        try {
          final byteData = await info.image.toByteData(format: imageFormat);
          setState(() {
            image = byteData!.buffer.asUint8List();
            if (image != null) {
              callback?.call(image!);
            }
          });
        } catch (e) {
          debugPrint('try another image byte format: $e');
        }
      }),
    );
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      floatingActionButtonLocation: FloatingActionButtonLocation.endTop,
      floatingActionButton: FloatingActionButton.extended(
        onPressed: cropController.cropImage,
        backgroundColor: Colors.white,
        label: const Text('Crop', style: TextStyle(color: Colors.black)),
      ),
      body: SafeArea(
        child: Builder(
          builder: (context) {
            if (image == null) {
              return const Center(child: CircularProgressIndicator());
            } else {
              return Column(
                children: [
                  Expanded(
                    child: Center(
                      child: DynamicImageCrop(
                        controller: cropController,
                        image: image!,
                        onResult: (image, width, height) {
                          sendResultImage(image, context);
                        },
                      ),
                    ),
                  ),
                  Container(
                    padding: const EdgeInsets.all(16),
                    color: Colors.black,
                    child: Center(
                      child: Column(
                        children: [
                          SingleChildScrollView(
                            scrollDirection: Axis.horizontal,
                            child: buildButtons(),
                          ),
                          Row(
                            children: [
                              Expanded(
                                child: TextField(
                                  controller: urlController,
                                  decoration: const InputDecoration(
                                    hintText: 'Enter URL',
                                  ),
                                  onChanged: (value) {
                                    urlController.text = value;
                                  },
                                  style: const TextStyle(color: Colors.white),
                                ),
                              ),
                              Padding(
                                padding: const EdgeInsets.only(left: 8),
                                child: IconButton(
                                  onPressed: () {
                                    loadImage(
                                      urlController.text,
                                      callback: cropController.changeImage,
                                    );
                                  },
                                  icon: const Icon(
                                    CupertinoIcons.search_circle_fill,
                                    color: Colors.white,
                                  ),
                                ),
                              ),
                            ],
                          ),
                        ],
                      ),
                    ),
                  )
                ],
              );
            }
          },
        ),
      ),
    );
  }

  Widget buildButtons() {
    return Row(
      children: [
        IconButton(
          onPressed: () => changeShape(CropType.rectangle),
          icon: const Icon(CupertinoIcons.rectangle_fill, color: Colors.white),
        ),
        const SizedBox(width: 8),
        IconButton(
          onPressed: () => changeShape(CropType.circle),
          icon: const Icon(CupertinoIcons.circle_fill, color: Colors.white),
        ),
        const SizedBox(width: 8),
        IconButton(
          onPressed: () => changeShape(CropType.triangle),
          icon: const Icon(CupertinoIcons.triangle_fill, color: Colors.white),
        ),
        const SizedBox(width: 8),
        IconButton(
          onPressed: () => changeShape(CropType.drawing),
          icon: const Icon(CupertinoIcons.pencil_outline, color: Colors.white),
        ),
        const SizedBox(width: 8),
        IconButton(
          onPressed: cropController.clearCropArea,
          icon: const Icon(
            CupertinoIcons.clear_circled_solid,
            color: Colors.white,
          ),
        ),
      ],
    );
  }

  void changeShape(CropType type) {
    cropController.changeType(type);
  }

  void sendResultImage(
    Uint8List? bytes,
    BuildContext context,
  ) {
    if (bytes != null) {
      Navigator.push(
        context,
        MaterialPageRoute<dynamic>(
          builder: (_) => ResultScreen(
            image: bytes,
          ),
        ),
      );
    }
  }
}

class ResultScreen extends StatelessWidget {
  const ResultScreen({
    required this.image,
    super.key,
  });

  final Uint8List image;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: Colors.black,
      floatingActionButtonLocation: FloatingActionButtonLocation.startTop,
      floatingActionButton: FloatingActionButton(
        backgroundColor: Colors.white,
        onPressed: () => Navigator.pop(context),
        child: const Icon(Icons.arrow_back, color: Colors.black),
      ),
      body: Center(
        child: Image.memory(
          image,
          fit: BoxFit.cover,
        ),
      ),
    );
  }
}

控制器方法

CropController 提供了以下方法:

void cropImage() {}
void changeType(CropType type) {}
void changeImage(Uint8List image) {}
void clearCropArea() {}

裁剪类型

CropType 枚举提供了以下类型:

enum CropType {
  rectangle, circle, triangle, drawing, none
}

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

1 回复

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


当然,以下是如何在Flutter项目中使用dynamic_image_crop插件来实现图片裁剪功能的代码示例。这个插件允许用户在设备上动态裁剪图片。

1. 添加依赖

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

dependencies:
  flutter:
    sdk: flutter
  dynamic_image_crop: ^0.1.0  # 请检查最新版本号

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

2. 导入插件

在你的Dart文件中导入dynamic_image_crop插件:

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:dynamic_image_crop/dynamic_image_crop.dart';

注意:dynamic_image_crop插件通常需要与image_picker插件一起使用来获取图片。

3. 实现图片选择和裁剪功能

下面是一个完整的示例,展示了如何从图库中选择图片并使用dynamic_image_crop进行裁剪:

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

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

class ImageCropScreen extends StatefulWidget {
  @override
  _ImageCropScreenState createState() => _ImageCropScreenState();
}

class _ImageCropScreenState extends State<ImageCropScreen> {
  File? _imageFile;
  CroppedFile? _croppedFile;

  final ImagePicker _picker = ImagePicker();

  Future<void> _pickImage(ImageSource source) async {
    final pickedFile = await _picker.pickImage(source: source);

    if (pickedFile != null) {
      setState(() {
        _imageFile = File(pickedFile.path);
      });

      // 裁剪图片
      _cropImage();
    }
  }

  Future<void> _cropImage() async {
    if (_imageFile == null) return;

    final croppedFile = await ImageCropper().cropImage(
      sourcePath: _imageFile!.path,
      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,
      ),
    );

    if (croppedFile != null) {
      setState(() {
        _croppedFile = croppedFile;
      });

      // 显示裁剪后的图片
      showCroppedImage();
    }
  }

  void showCroppedImage() {
    if (_croppedFile != null) {
      // 这里可以显示裁剪后的图片,例如使用 Image.file(_croppedFile!.path)
      print('Cropped image path: ${_croppedFile!.path}');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Image Crop Example'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            _imageFile != null
                ? Image.file(_imageFile!)
                : Text('No image selected.'),
            SizedBox(height: 20),
            ElevatedButton(
              onPressed: () => _pickImage(ImageSource.gallery),
              child: Text('Pick from gallery'),
            ),
            SizedBox(height: 10),
            ElevatedButton(
              onPressed: () => _pickImage(ImageSource.camera),
              child: Text('Take a picture'),
            ),
          ],
        ),
      ),
    );
  }
}

4. 注意事项

  • 确保在AndroidManifest.xmlInfo.plist中添加必要的权限和配置,以便访问相机和图库。
  • 根据需要调整裁剪设置,例如裁剪比例和UI样式。

这个示例展示了如何使用dynamic_image_crop插件来选择和裁剪图片。你可以根据实际需求进一步自定义和扩展这个示例。

回到顶部