我正在尝试在我的应用中拍照。我正在使用camera程序包,并试图显示CameraPreview
小部件的中心方形裁剪版本。
我的目标是显示预览的中心正方形(全角),从顶部和底部修剪均匀的量。
我一直在努力使其工作,因此我使用固定图像创建了一个最小的示例。 (为我在椅子上呆呆的照片道歉):
import 'package:flutter/material.dart';
void main() => runApp(new MyApp());
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Example',
theme: ThemeData(),
home: Scaffold(
body: Example(),
),
);
}
}
class Example extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Column(
children: <Widget>[
CroppedCameraPreview(),
// Something to occupy the rest of the space
Expanded(
child: Container(),
)
],
);
}
}
class CroppedCameraPreview extends StatelessWidget {
@override
Widget build(BuildContext context) {
// We will pretend this is a camera preview (to make demo easier)
var cameraImage = Image.network("https://i.imgur.com/gZfg4jm.jpg");
var aspectRatio = 1280 / 720;
return Container(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.width,
child: ClipRect(
child: new OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitWidth,
child: cameraImage,
),
),
),
);
}
}
这很好用-我得到了全屏宽度的图像,中间被裁剪并推到了应用程序的顶部。
但是,如果我将这段代码放到现有应用中,并用cameraImage
替换CameraPreview
,则会出现很多布局错误:
flutter: ══╡ EXCEPTION CAUGHT BY RENDERING LIBRARY ╞═════════════════════════════════════════════════════════
flutter: The following assertion was thrown during performResize():
flutter: TextureBox object was given an infinite size during layout.
flutter: This probably means that it is a render object that tries to be as big as possible, but it was put
flutter: inside another render object that allows its children to pick their own size.
flutter: The nearest ancestor providing an unbounded width constraint is:
flutter: RenderFittedBox#0bd54 NEEDS-LAYOUT NEEDS-PAINT
flutter: creator: FittedBox ← OverflowBox ← ClipRect ← ConstrainedBox ← Container ← Stack ← ConstrainedBox
flutter: ← Container ← CameraWidget ← Column ← CameraPage ← MediaQuery ← ⋯
flutter: parentData: offset=Offset(0.0, 0.0) (can use size)
flutter: constraints: BoxConstraints(w=320.0, h=320.0)
flutter: size: MISSING
flutter: fit: fitWidth
flutter: alignment: center
flutter: textDirection: ltr
flutter: The nearest ancestor providing an unbounded height constraint is:
flutter: RenderFittedBox#0bd54 NEEDS-LAYOUT NEEDS-PAINT
flutter: creator: FittedBox ← OverflowBox ← ClipRect ← ConstrainedBox ← Container ← Stack ← ConstrainedBox
flutter: ← Container ← CameraWidget ← Column ← CameraPage ← MediaQuery ← ⋯
flutter: parentData: offset=Offset(0.0, 0.0) (can use size)
flutter: constraints: BoxConstraints(w=320.0, h=320.0)
flutter: size: MISSING
flutter: fit: fitWidth
flutter: alignment: center
flutter: textDirection: ltr
flutter: The constraints that applied to the TextureBox were:
flutter: BoxConstraints(unconstrained)
flutter: The exact size it was given was:
flutter: Size(Infinity, Infinity)
flutter: See https://flutter.io/layout/ for more information.
有人可以建议我为什么在预览时遇到错误以及如何避免这些错误吗?
答案 0 :(得分:11)
我通过给CameraPreview
实例指定特定大小并将其包装在Container
中来解决了这个问题:
var size = MediaQuery.of(context).size.width;
// ...
Container(
width: size,
height: size,
child: ClipRect(
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
width: size,
height:
size / widget.cameraController.value.aspectRatio,
child: camera, // this is my CameraPreview
),
),
),
),
);
为了回应Luke的评论,我然后使用此代码对所得到的图像进行平方裁剪。 (因为即使预览为正方形,捕获的图像仍然是标准比例。)
Future<String> _resizePhoto(String filePath) async {
ImageProperties properties =
await FlutterNativeImage.getImageProperties(filePath);
int width = properties.width;
var offset = (properties.height - properties.width) / 2;
File croppedFile = await FlutterNativeImage.cropImage(
filePath, 0, offset.round(), width, width);
return croppedFile.path;
}
这使用https://github.com/btastic/flutter_native_image。自从我使用这段代码已经有一段时间了-认为它目前仅适用于人像图像,但应该可以轻松扩展以处理风景。
答案 1 :(得分:2)
我有一个类似于答案中的代码段。
与答案相同,它支持相机的纵横比与屏幕的纵横比不同的情况。
尽管我的版本有所不同:它不需要使用媒体查询来获取设备大小,因此它可以适应任何父对象的宽度(不仅仅是全屏宽度)
....
return AspectRatio(
aspectRatio: 1,
child: ClipRect(
child: Transform.scale(
scale: 1 / _cameraController.value.aspectRatio,
child: Center(
child: AspectRatio(
aspectRatio: _cameraController.value.aspectRatio,
child: CameraPreview(_cameraController),
),
),
),
),
);
要居中裁剪图像,请参见下面的代码段。
它对于纵向和横向图像同样适用。它还可以选择镜像图像(如果要保留自拍相机的原始镜像外观,则很有用)
import 'dart:io';
import 'dart:math';
import 'package:flutter/rendering.dart';
import 'package:image/image.dart' as IMG;
class ImageProcessor {
static Future cropSquare(String srcFilePath, String destFilePath, bool flip) async {
var bytes = await File(srcFilePath).readAsBytes();
IMG.Image src = IMG.decodeImage(bytes);
var cropSize = min(src.width, src.height);
int offsetX = (src.width - min(src.width, src.height)) ~/ 2;
int offsetY = (src.height - min(src.width, src.height)) ~/ 2;
IMG.Image destImage =
IMG.copyCrop(src, offsetX, offsetY, cropSize, cropSize);
if (flip) {
destImage = IMG.flipVertical(destImage);
}
var jpg = IMG.encodeJpg(destImage);
await File(destFilePath).writeAsBytes(jpg);
}
}
此代码需要 image 软件包。将其添加到 pubspec.yaml :
dependencies:
image: ^2.1.4
答案 2 :(得分:1)
经过这里的一些测试,我建议将 CameraPreview(不是图像本身)裁剪为任何矩形形状:
Widget buildCameraPreview(CameraController cameraController) {
final double previewAspectRatio = 0.7;
return AspectRatio(
aspectRatio: 1 / previewAspectRatio,
child: ClipRect(
child: Transform.scale(
scale: cameraController.value.aspectRatio / previewAspectRatio,
child: Center(
child: CameraPreview(cameraController),
),
),
),
);
}
优点类似于@VeganHunter 的解决方案,但可以通过使用 previewAspectRatio 扩展到任何矩形形状。 previewAspectRatio 值为 1 将产生方形,值为 0.5 将是其宽度高度的一半的矩形,cameraController.value.aspectRatio 的值将是全尺寸预览。
此外,以下是为什么这项工作有效的解释:
Flutter 的一件事是让子级溢出其父级通常是不可能的(因为在布局阶段计算小部件大小的方式)。 Transform.scale 的使用在这里至关重要,因为根据文档:
<块引用>此对象在绘制之前应用其变换,这意味着在计算此小部件的子级(以及此小部件)消耗的空间量时不考虑该变换。
这意味着小部件将能够在放大时溢出其容器,我们可以使用 CLipRect 剪辑(从而隐藏)溢出的部分,将预览的整体大小限制为其父级大小。仅使用 Container 是不可能达到相同效果的,因为它们会在布局阶段根据可用空间自行缩放,并且不会溢出,因此没有任何可剪辑的内容。
选择比例 (cameraController.value.aspectRatio * previewAspectRatio) 以使预览的宽度与其父级的宽度相匹配。
如果此解决方案对您不起作用,请尝试将所有 cameraController.value.aspectRatio 替换为其倒数(1 / cameraController.value.aspectRatio)。
这仅针对纵向模式进行了测试,横向可能需要进行一些调整。
答案 3 :(得分:0)
我就是这样解决的。
SizedBox(
width: width,
height: height,
child: SingleChildScrollView(
child: AspectRatio(
aspectRatio: _cameraController.value.aspectRatio,
child: CameraPreview(_cameraController),
),
),
)
答案 4 :(得分:-1)
简单的裁剪方法
来自记忆:
Container(
width: 40.0,
height: 40.0,
decoration: BoxDecoration(
image: DecorationImage(
image: MemoryImage(photo),
fit: BoxFit.cover,
),
),
)
来自资产:
Container(
width: 40.0,
height: 40.0,
decoration: BoxDecoration(
image: DecorationImage(
image: AssetImage('assets/images/photo.jpg'),
fit: BoxFit.cover,
),
),
)