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

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

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

image_cropper_for_web 是一个用于在Flutter Web应用中实现图片裁剪功能的插件。本文将介绍如何使用该插件,并提供一个完整的示例代码。

安装插件

首先,您需要在项目的 pubspec.yaml 文件中添加依赖:

dependencies:
  flutter:
    sdk: flutter
  image_cropper: ^1.4.1
  image_picker: ^0.8.5+3
  dotted_border: ^2.0.0

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

示例代码

以下是一个完整的示例代码,展示了如何在Flutter Web应用中使用 image_cropper_for_web 插件进行图片上传和裁剪操作。

import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:image_cropper/image_cropper.dart';
import 'package:image_picker/image_picker.dart';

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

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

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        highlightColor: const Color(0xFFD0996F),
        canvasColor: const Color(0xFFFDF5EC),
        textTheme: TextTheme(
          headlineSmall: ThemeData.light()
              .textTheme
              .headlineSmall!
              .copyWith(color: const Color(0xFFBC764A)),
        ),
        iconTheme: IconThemeData(
          color: Colors.grey[600],
        ),
        appBarTheme: const AppBarTheme(
          backgroundColor: Color(0xFFBC764A),
          centerTitle: false,
          foregroundColor: Colors.white,
          actionsIconTheme: IconThemeData(color: Colors.white),
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: ButtonStyle(
            backgroundColor: WidgetStateColor.resolveWith(
                (states) => const Color(0xFFBC764A)),
            foregroundColor: WidgetStateColor.resolveWith(
              (states) => Colors.white,
            ),
          ),
        ),
        outlinedButtonTheme: OutlinedButtonThemeData(
          style: ButtonStyle(
            foregroundColor: WidgetStateColor.resolveWith(
              (states) => const Color(0xFFBC764A),
            ),
            side: WidgetStateBorderSide.resolveWith(
                (states) => const BorderSide(color: Color(0xFFBC764A))),
          ),
        ),
        textButtonTheme: TextButtonThemeData(
          style: ButtonStyle(
            foregroundColor: WidgetStateColor.resolveWith(
              (states) => const Color(0xFFBC764A),
            ),
          ),
        ),
        iconButtonTheme: IconButtonThemeData(
          style: ButtonStyle(
            foregroundColor: WidgetStateColor.resolveWith(
              (states) => const Color(0xFFBC764A),
            ),
          ),
        ),
        colorScheme: ColorScheme.fromSwatch().copyWith(
          background: const Color(0xFFFDF5EC),
          primary: const Color(0xFFD0996F),
        ),
      ),
      home: const MyHomePage(title: 'Image Cropper Demo'),
    );
  }
}

class MyHomePage extends StatelessWidget {
  const MyHomePage({
    super.key,
    required this.title,
  });

  final String title;

  [@override](/user/override)
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.of(context).size.width;
    if (screenWidth > 700) {
      return _HomePage(
        key: const ValueKey('desktop'),
        title: title,
        displayStyle: PageDisplayStyle.desktop,
      );
    } else {
      return _HomePage(
        key: const ValueKey('mobile'),
        title: title,
        displayStyle: PageDisplayStyle.mobile,
      );
    }
  }
}

enum PageDisplayStyle {
  desktop,
  mobile,
}

class _HomePage extends StatefulWidget {
  final PageDisplayStyle displayStyle;
  final String title;

  const _HomePage({
    super.key,
    required this.displayStyle,
    required this.title,
  });

  [@override](/user/override)
  _HomePageState createState() => _HomePageState();
}

class _HomePageState extends State<_HomePage> {
  String? _uploadedBlobUrl;
  String? _croppedBlobUrl;

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: widget.displayStyle == PageDisplayStyle.mobile
          ? AppBar(title: Text(widget.title))
          : null,
      body: Column(
        mainAxisSize: MainAxisSize.max,
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          if (widget.displayStyle == PageDisplayStyle.desktop)
            Padding(
              padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 24.0),
              child: Text(
                widget.title,
                style: Theme.of(context)
                    .textTheme
                    .displayMedium!
                    .copyWith(color: Theme.of(context).highlightColor),
              ),
            ),
          Expanded(child: _body()),
        ],
      ),
    );
  }

  Widget _body() {
    if (_croppedBlobUrl != null || _uploadedBlobUrl != null) {
      return _imageCard();
    } else {
      return _uploaderCard();
    }
  }

  Widget _imageCard() {
    return Center(
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Card(
            elevation: 4.0,
            child: Padding(
              padding: const EdgeInsets.all(24.0),
              child: _image(),
            ),
          ),
          const SizedBox(height: 24.0),
          _menu(),
        ],
      ),
    );
  }

  Widget _image() {
    final screenWidth = MediaQuery.of(context).size.width;
    final screenHeight = MediaQuery.of(context).size.height;
    if (_croppedBlobUrl != null) {
      return ConstrainedBox(
          constraints: BoxConstraints(
            maxWidth: 0.8 * screenWidth,
            maxHeight: 0.7 * screenHeight,
          ),
          child: Image.network(_croppedBlobUrl!));
    } else if (_uploadedBlobUrl != null) {
      return ConstrainedBox(
        constraints: BoxConstraints(
          maxWidth: 0.8 * screenWidth,
          maxHeight: 0.7 * screenHeight,
        ),
        child: Image.network(_uploadedBlobUrl!),
      );
    } else {
      return const SizedBox.shrink();
    }
  }

  Widget _menu() {
    return Row(
      mainAxisSize: MainAxisSize.min,
      children: [
        FloatingActionButton(
          onPressed: () {
            _clear();
          },
          backgroundColor: Colors.redAccent,
          tooltip: 'Delete',
          heroTag: null,
          child: const Icon(Icons.delete),
        ),
        if (_croppedBlobUrl == null)
          Padding(
            padding: const EdgeInsets.only(left: 32.0),
            child: FloatingActionButton(
              onPressed: () {
                _cropImage();
              },
              backgroundColor: const Color(0xFFBC764A),
              tooltip: 'Crop',
              heroTag: null,
              child: const Icon(Icons.crop),
            ),
          )
      ],
    );
  }

  Widget _uploaderCard() {
    return Center(
      child: Card(
        elevation: 4.0,
        shape: RoundedRectangleBorder(
          borderRadius: BorderRadius.circular(16.0),
        ),
        child: SizedBox(
          width: 380.0,
          height: 300.0,
          child: Column(
            mainAxisSize: MainAxisSize.max,
            crossAxisAlignment: CrossAxisAlignment.center,
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              DottedBorder(
                radius: const Radius.circular(12.0),
                borderType: BorderType.RRect,
                dashPattern: const [8, 4],
                color: Theme.of(context).highlightColor.withOpacity(0.4),
                child: Padding(
                  padding: const EdgeInsets.all(24.0),
                  child: Column(
                    mainAxisSize: MainAxisSize.min,
                    crossAxisAlignment: CrossAxisAlignment.center,
                    children: [
                      Icon(
                        Icons.image,
                        color: Theme.of(context).highlightColor,
                        size: 80.0,
                      ),
                      const SizedBox(height: 24.0),
                      Text(
                        'Upload an image to start',
                        style:
                            Theme.of(context).textTheme.headlineSmall!.copyWith(
                                  color: Theme.of(context).highlightColor,
                                ),
                      )
                    ],
                  ),
                ),
              ),
              const SizedBox(height: 24.0),
              ElevatedButton(
                onPressed: () {
                  _uploadImage();
                },
                style: ElevatedButton.styleFrom(foregroundColor: Colors.white),
                child: const Text('Upload'),
              ),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _cropImage() async {
    if (_uploadedBlobUrl != null) {
      WebUiSettings settings;
      if (widget.displayStyle == PageDisplayStyle.mobile) {
        final screenWidth = MediaQuery.of(context).size.width;
        final screenHeight = MediaQuery.of(context).size.height;
        settings = WebUiSettings(
          context: context,
          presentStyle: WebPresentStyle.page,
          size: CropperSize(
            width: (screenWidth * 0.9).round(),
            height: (screenHeight * 0.8).round(),
          ),
          themeData: const WebThemeData(backIcon: Icons.arrow_back_ios_new),
        );
      } else {
        settings = WebUiSettings(
          context: context,
          presentStyle: WebPresentStyle.dialog,
          size: const CropperSize(
            width: 520,
            height: 520,
          ),
        );
      }
      final croppedFile = await ImageCropper().cropImage(
        sourcePath: _uploadedBlobUrl!,
        uiSettings: [settings],
      );
      if (croppedFile != null) {
        setState(() {
          _croppedBlobUrl = croppedFile.path;
        });
      }
    }
  }

  Future<void> _uploadImage() async {
    final pickedFile =
        await ImagePicker().pickImage(source: ImageSource.gallery);
    if (pickedFile != null) {
      final blobUrl = pickedFile.path;
      debugPrint('picked blob: $blobUrl');
      setState(() {
        _uploadedBlobUrl = blobUrl;
      });
    }
  }

  void _clear() {
    setState(() {
      _uploadedBlobUrl = null;
      _croppedBlobUrl = null;
    });
  }
}

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

1 回复

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


当然,下面是一个关于如何在Flutter Web项目中使用image_cropper_for_web插件来实现网页图片裁剪功能的代码案例。

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

dependencies:
  flutter:
    sdk: flutter
  image_cropper: ^3.0.0  # 请检查最新版本号
  image_cropper_for_web: ^2.0.0  # 请检查最新版本号

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

接下来,你需要进行一些配置以确保插件在Web平台上正常工作。在web/index.html文件中,添加以下代码以允许文件输入(这对图片上传是必要的):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Flutter Web App</title>
    <!-- 添加以下meta标签 -->
    <meta name="google-signin-client_id" content="YOUR_GOOGLE_SIGN_IN_CLIENT_ID">
    <meta name="google-signin-scope" content="profile email">
    <input type="file" id="file-input" accept="image/*" style="display:none;" multiple>
</head>
<body>
    <script>
        window.addEventListener('load', function() {
            // For allowing file input in image_cropper_for_web
            var fileInput = document.getElementById('file-input');
            fileInput.click();
        });
    </script>
    <script src="main.dart.js" type="application/javascript"></script>
</body>
</html>

注意:上面的HTML配置中的file-input部分是为了处理文件输入,但通常image_cropper_for_web插件会自动处理这些,这里的代码只是为了展示如何可能需要进行一些额外的配置。实际上,你通常不需要手动触发文件输入。

然后,在你的Dart代码中,你可以这样使用image_cropper_for_web插件:

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

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

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Image Cropper for Web Demo'),
        ),
        body: Center(
          child: CropImageButton(),
        ),
      ),
    );
  }
}

class CropImageButton extends StatefulWidget {
  @override
  _CropImageButtonState createState() => _CropImageButtonState();
}

class _CropImageButtonState extends State<CropImageButton> {
  File? _imageFile;

  Future<void> _pickImage(ImageSource source) async {
    final ImagePicker _picker = ImagePicker();
    final XFile? image = await _picker.pickImage(source: source);

    if (image != null) {
      final File imageFile = File(image.path);
      final CroppedFile croppedFile = await ImageCropper().cropImage(
        sourcePath: image.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,
        ),
      );

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

  @override
  Widget build(BuildContext context) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: <Widget>[
        ElevatedButton(
          onPressed: () => _pickImage(ImageSource.gallery),
          child: Text('Pick Image from Gallery'),
        ),
        SizedBox(height: 20),
        ElevatedButton(
          onPressed: () => _pickImage(ImageSource.camera),
          child: Text('Pick Image from Camera'),
        ),
        SizedBox(height: 20),
        if (_imageFile != null)
          Image.file(_imageFile!),
      ],
    );
  }
}

在这个例子中,我们创建了一个简单的Flutter应用,其中包含两个按钮,一个用于从画廊中选择图片,另一个用于从相机中选择图片。选择图片后,将使用ImageCropper进行裁剪,并将裁剪后的图片显示在屏幕上。

请注意,由于Web平台的限制,直接从相机选择图片可能不总是可行,这取决于用户的浏览器和权限设置。因此,在实际应用中,你可能需要处理这些潜在的错误和限制。

确保你已经按照插件的官方文档进行了所有必要的配置,并且已经处理了所有可能的平台差异和错误情况。

回到顶部