如何在颤动中将图像添加到任何形状?
How to add images to any shapes in flutter?
我是 flutter 的新手,遇到了在 flutter 中向形状添加图像的问题。我已经创建了自定义形状,但无法将图像插入其中。这是我想要实现的目标:
A picture inside a custom shape
我尝试将 Image 作为 CustomPainter 的子级,但仍然没有很好的结果。
任何人都可以提出一个好的方法吗?
正如 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;
}
如 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 中的路径。
使用 CustomPaint,您可以将图像用作覆盖原始图像的蒙版,并仅显示蒙版中包含的图像内容。在那种情况下是一个形状。在下面的代码中 shape.png 就像掩码一样,只是 png 文件中不透明的部分将显示 [=18= 中的内容]。因此,您需要创建一个具有所需形状的 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;
}
}
我是 flutter 的新手,遇到了在 flutter 中向形状添加图像的问题。我已经创建了自定义形状,但无法将图像插入其中。这是我想要实现的目标: A picture inside a custom shape
我尝试将 Image 作为 CustomPainter 的子级,但仍然没有很好的结果。
任何人都可以提出一个好的方法吗?
正如 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;
}
如 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 中的路径。
使用 CustomPaint,您可以将图像用作覆盖原始图像的蒙版,并仅显示蒙版中包含的图像内容。在那种情况下是一个形状。在下面的代码中 shape.png 就像掩码一样,只是 png 文件中不透明的部分将显示 [=18= 中的内容]。因此,您需要创建一个具有所需形状的 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;
}
}