【问题标题】:How to add images to any shapes in flutter?如何在颤动中将图像添加到任何形状?
【发布时间】:2020-10-15 13:18:50
【问题描述】:

我是 Flutter 的新手,遇到了在 Flutter 中向形状添加图像的问题。我已经创建了自定义形状,但无法将图像插入其中。这是我想要实现的目标: A picture inside a custom shape

我尝试将 Image 作为 CustomPainter 的子级,但仍然没有好的结果。

谁能推荐一个好的方法?

【问题讨论】:

标签: flutter dart flutter-layout flutter-web dart-webui


【解决方案1】:

使用 CustomPaint,您可以将图像用作覆盖原始图像的蒙版,并仅显示蒙版中包含的图像内容。在那种情况下是一个形状。在下面的代码中,shape.png 就像蒙版一样,只有 png 文件中不透明的部分会显示 image.jpeg 中的内容。因此,您需要创建一个具有所需形状的 png 文件,并将其作为叠加应用到图像上。

import 'dart:typed_data';
import 'dart:ui' as ui;

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;


class MaskedImageWithPainter extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  ui.Image mask;
  ui.Image image;

  @override
  void initState() {
    super.initState();
    load('assets/shape.png').then((i) {
      setState(() {
        mask = i;
      });
    });
    load('assets/image.jpeg').then((i) {
      setState(() {
        image = i;
      });
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(backgroundColor: Colors.blue, title: Text('I am a title')),
      body: SafeArea(
        child: SizedBox(
          width: 200.0,
          height: 200.0,
          child: CustomPaint(painter: OverlayPainter(mask, image)),
        ),
      ),
    );
  }

  Future<ui.Image> load(String asset) async {
    ByteData data = await rootBundle.load(asset);
    ui.Codec codec = await ui.instantiateImageCodec(data.buffer.asUint8List());
    ui.FrameInfo fi = await codec.getNextFrame();
    return fi.image;
  }
}

class OverlayPainter extends CustomPainter {
  ui.Image mask;
  ui.Image image;

  OverlayPainter(this.mask, this.image);

  @override
  void paint(Canvas canvas, Size size) {
    if (image != null && mask != null) {
      var rect = Rect.fromLTRB(0, 0, 200, 200);
      Size outputSize = rect.size;
      Paint paint = new Paint();

      //Mask
      Size maskInputSize = Size(mask.width.toDouble(), mask.height.toDouble());
      final FittedSizes maskFittedSizes =
          applyBoxFit(BoxFit.cover, maskInputSize, outputSize);
      final Size maskSourceSize = maskFittedSizes.source;

      final Rect maskSourceRect = Alignment.center
          .inscribe(maskSourceSize, Offset.zero & maskInputSize);

      canvas.saveLayer(rect, paint);
      canvas.drawImageRect(mask, maskSourceRect, rect, paint);

      //Image
      Size inputSize = Size(image.width.toDouble(), image.height.toDouble());
      final FittedSizes fittedSizes =
          applyBoxFit(BoxFit.cover, inputSize, outputSize);
      final Size sourceSize = fittedSizes.source;
      final Rect sourceRect =
          Alignment.center.inscribe(sourceSize, Offset.zero & inputSize);

      canvas.drawImageRect(
          image, sourceRect, rect, paint..blendMode = BlendMode.srcIn);
      canvas.restore();
    }
  }

  @override
  bool shouldRepaint(OverlayPainter oldDelegate) {
    return mask != oldDelegate.mask || image != oldDelegate.image;
  }
}

【讨论】:

    【解决方案2】:

    就像 Mindhun MP 写的那样,您需要使用自定义剪裁器。 下面是一个裁剪容器的例子:

    ClipPath(
          clipper: _CustomClipper(),
          child: Container(...),
    );
    

    您的情况应该与我的情况相似,因此请查看 quadraticBezierTo 函数。 我的例子:

    class _CustomClipper extends CustomClipper<Path> {
      @override
      Path getClip(Size size) {
        final double heightDelta = size.height / 2.2;
    
        return Path()
          ..addRect(
              Rect.fromLTWH(0, heightDelta, size.width, size.height - heightDelta))
          ..moveTo(0, heightDelta)
          ..quadraticBezierTo(
            size.width / 2,
            heightDelta - size.width / 2,
            size.width,
            heightDelta,
          );
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) => true;
    }
    

    另外,here 是一篇很棒的文章,它详细解释了 Flutter 中的路径。

    【讨论】:

    • 非常感谢@oziem。这是创建我想要的形状的一个很好的例子。
    【解决方案3】:

    正如 Midhun MP 所说,使用 ClipPath 包装您的图像

      @override
      Widget build(BuildContext context) {
        return ClipPath(
          child: image,
          clipper: AnyClipper(),
        );
      }
    

    并使用CustomClipper

    class AnyClipper extends CustomClipper<Path> {
      @override
      Path getClip(Size size) {
        var path = Path();
        //... do something
        return path;
      }
    
      @override
      bool shouldReclip(CustomClipper<Path> oldClipper) => false;
    }
    

    【讨论】:

    • 谢谢@Me_ 指导我:)
    猜你喜欢
    • 2021-06-22
    • 2022-11-30
    • 1970-01-01
    • 1970-01-01
    • 2021-07-21
    • 2018-10-31
    • 2019-11-05
    • 2022-11-01
    • 2021-08-11
    相关资源
    最近更新 更多