Reputation: 1512
I have an AspectRatio problem with my Flutter camera (Plugin "camera_camera) and an image that I put on top of it with transparency as a layer.
I send you a screenshot of the problem. In the screenshot you can see the open camera and above it the picture I took right in front of it. Unfortunately you can see at different places that it does not match.
How do I get the camera to show exactly the same proportions as I photographed it from exactly the same position before?
If this helps: I recorded also a video with the issue: https://danielederosa.de/downloads/flutter_issue.mp4
My Code
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
if (!controller.value.isInitialized) {
return Container(
color: theme.colorScheme.onPrimary,
child: Center(child: CircularProgressIndicator()));
}
return Scaffold(
appBar: CupertinoNavigationBar(
backgroundColor: theme.colorScheme.primary,
border: Border.symmetric(
vertical: BorderSide.none, horizontal: BorderSide.none),
automaticallyImplyLeading: false,
leading: IconButton(
icon: Icon(
Icons.chevron_left,
size: 30,
color: theme.colorScheme.onPrimary,
),
onPressed: () => Navigator.pop(context),
),
middle: Text("Memories",
style: TextStyle(
color: theme.colorScheme.onPrimary,
fontSize: theme.textTheme.headline3.fontSize)),
),
body: Container(
child: Column(
children: [
Expanded(
child: Camera(
mode: CameraMode.normal,
imageMask: lastPicture != null
? new Positioned.fill(
child: new Opacity(
opacity: 0.3,
child: RotatedBox(
quarterTurns: 1,
child: new Image.file(
File(lastPicture),
fit: BoxFit.cover,
),
),
),
)
: Container(),
onFile: (File file) {
_workWithImage(file);
},
),
),
],
),
),
);
}
I also tried to wrap the Camera widget into an AspectRatio
widget with aspectRatio: 3/4
because my saved image are saved in this aspectRatio. But without success.
Do you have any idea to solve this issue?
Upvotes: 2
Views: 1210
Reputation: 1512
I found a solution and got it to work.
Example code
@override
Widget build(BuildContext context) {
final theme = Theme.of(context);
var deviceSize = MediaQuery.of(context).size;
var sizeWidth = MediaQuery.of(context).size.width;
final deviceRatio = deviceSize.width / deviceSize.height;
var isPortrait = MediaQuery.of(context).orientation == Orientation.portrait;
return Scaffold(
backgroundColor: theme.colorScheme.primary,
appBar: CupertinoNavigationBar(
backgroundColor: theme.colorScheme.primary,
border: Border.symmetric(
vertical: BorderSide.none, horizontal: BorderSide.none),
automaticallyImplyLeading: false,
leading: IconButton(
icon: Icon(
Icons.chevron_left,
size: 30,
color: theme.colorScheme.onPrimary,
),
onPressed: () => Navigator.pop(context),
),
middle: Text(APP_NAME,
style: TextStyle(
color: theme.colorScheme.onPrimary,
fontSize: theme.textTheme.headline3.fontSize)),
),
body: NativeDeviceOrientationReader(
useSensor: true,
builder: (context) {
NativeDeviceOrientation orientation =
NativeDeviceOrientationReader.orientation(context);
return Stack(children: [
FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return MeasureSize(
onChange: (size) {
setState(() {
cameraSize = size;
});
},
child: Transform.scale(
scale: cameraController.value.aspectRatio / deviceRatio,
child: Center(
child: AspectRatio(
aspectRatio: cameraController.value.aspectRatio,
child: ClipRect(
child: OverflowBox(
alignment: Alignment.center,
child: FittedBox(
fit: BoxFit.fitWidth,
child: Container(
width: sizeWidth,
height: sizeWidth /
cameraController.value.aspectRatio,
child: CameraPreview(
cameraController), // this is my CameraPreview
),
),
),
),
),
),
),
);
} else {
// Otherwise, display a loading indicator.
return Center(child: CircularProgressIndicator());
}
},
),
helpMode == true
? Transform.scale(
scale: cameraController.value.aspectRatio / deviceRatio,
child: Center(
child: Opacity(
opacity: .3,
child: orientation ==
NativeDeviceOrientation.landscapeLeft ||
orientation ==
NativeDeviceOrientation.landscapeRight
? RotatedBox(
quarterTurns: orientation ==
NativeDeviceOrientation.landscapeLeft
? 1
: 3,
child: Image.file(
File(lastPicture),
height: cameraSize.width,
fit: BoxFit.contain,
))
: Image.file(
File(lastPicture),
width: cameraSize.width,
height: cameraSize.height,
fit: BoxFit.contain,
),
),
),
)
: Container(),
]);
},
),
floatingActionButtonLocation: FloatingActionButtonLocation.centerDocked,
floatingActionButton: Container(
transform: Matrix4.translationValues(0.0, -8.0, 0.0),
child: FloatingActionButton(
backgroundColor: theme.colorScheme.primary,
child: Icon(
Icons.camera_alt,
color: theme.colorScheme.onPrimary,
),
// Provide an onPressed callback.
onPressed: () async {
// Take the Picture in a try / catch block. If anything goes wrong,
// catch the error.
try {
// Ensure that the camera is initialized.
//await _initializeControllerFuture;
// Construct the path where the image should be saved using the path
// package.
final path = join(
// Store the picture in the temp directory.
// Find the temp directory using the `path_provider` plugin.
(await getTemporaryDirectory()).path,
'${DateTime.now()}.png',
);
// Attempt to take a picture and log where it's been saved.
await cameraController.takePicture(path);
_workWithImage(File(path));
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
),
),
);
}
}
typedef void OnWidgetSizeChange(Size size);
class MeasureSize extends StatefulWidget {
final Widget child;
final OnWidgetSizeChange onChange;
const MeasureSize({
Key key,
@required this.onChange,
@required this.child,
}) : super(key: key);
@override
_MeasureSizeState createState() => _MeasureSizeState();
}
class _MeasureSizeState extends State<MeasureSize> {
@override
Widget build(BuildContext context) {
SchedulerBinding.instance.addPostFrameCallback(postFrameCallback);
return Container(
key: widgetKey,
child: widget.child,
);
}
var widgetKey = GlobalKey();
var oldSize;
void postFrameCallback(_) {
var context = widgetKey.currentContext;
if (context == null) return;
var newSize = context.size;
if (oldSize == newSize) return;
oldSize = newSize;
widget.onChange(newSize);
}
}
I created the MeasureSize
class. With this class I get the dimensions of a child widget. In this case I need the width and height from the camera. (Transform.scale
) After I got this I had only to set this dimensions for the image overlay:
Image.file(
File(lastPicture),
width: cameraSize.width,
height: cameraSize.height,
fit: BoxFit.contain,
),
Now the image overlay fits to this what the camera displays.
Upvotes: 1