Returns blacked out image
Package just returns a blacked out image.
Source code:
import 'dart:typed_data';
import 'package:crop/crop.dart';
import 'package:dotted_border/dotted_border.dart';
import 'package:flutter/material.dart';
import 'package:camera/camera.dart';
import 'package:extended_image/extended_image.dart';
import 'dart:io';
import 'dart:math';
import 'package:path_provider/path_provider.dart';
import 'dart:ui' as ui;
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Camera Cropper',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
CameraController? _controller;
late List<CameraDescription> _cameras;
File? _capturedImage;
final TransformationController _transformationController = TransformationController();
final CropController _cropController = CropController();
@override
void initState() {
super.initState();
_getCameras();
}
_getCameras() async {
_cameras = await availableCameras();
_controller = CameraController(_cameras[0], ResolutionPreset.max);
await _controller?.initialize();
if (mounted) {
setState(() {});
}
}
String _randomNonceString([int length = 32]) {
final random = Random();
final charCodes = List<int>.generate(length, (_) {
late int codeUnit;
switch (random.nextInt(3)) {
case 0:
codeUnit = random.nextInt(10) + 48;
break;
case 1:
codeUnit = random.nextInt(26) + 65;
break;
case 2:
codeUnit = random.nextInt(26) + 97;
break;
}
return codeUnit;
});
return String.fromCharCodes(charCodes);
}
@override
void dispose() {
super.dispose();
_controller?.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.black,
body: _controller == null
? const LoadingIndicator()
: Stack(
alignment: Alignment.bottomCenter,
children: [
Column(
children: [
Expanded(
child: InteractiveViewer(
constrained: false,
transformationController: _transformationController,
child: SizedBox(
width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height,
child: _capturedImage != null ? ExtendedImage.file(_capturedImage!) : CameraPreview(_controller!),
),
),
),
],
),
if (_capturedImage == null) ResizableWidget(cropController: _cropController),
Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
TextButton(
child: const Icon(
Icons.arrow_back_ios,
color: Colors.white,
size: 40,
),
onPressed: () {
setState(() {
_capturedImage = null;
});
},
),
GestureDetector(
onTap: () async {
// XFile image = await _controller!.takePicture();
// _capturedImage = File(image.path);
final double pixelRatio = MediaQuery.of(context).devicePixelRatio;
final ui.Image cropped = await _cropController.crop(pixelRatio: pixelRatio);
final ByteData? byteData = await cropped.toByteData(format: ui.ImageByteFormat.png);
final Uint8List buffer = byteData!.buffer.asUint8List();
final Directory dir = await getTemporaryDirectory();
final String path = '${dir.path}/${_randomNonceString()}.png';
if (!(await File(path).exists())) {
File imageFile = await File(path).create();
_capturedImage = await imageFile.writeAsBytes(buffer);
setState(() {});
} else {
_capturedImage = await File(path).writeAsBytes(buffer);
setState(() {});
}
},
child: const CircleAvatar(
radius: 30,
backgroundColor: Colors.white,
),
),
TextButton(
child: Icon(
_controller!.value.flashMode == FlashMode.auto
? Icons.flash_auto
: _controller!.value.flashMode == FlashMode.off
? Icons.flash_off
: Icons.flash_on,
color: Colors.white,
size: 40,
),
onPressed: () async {
await _controller!.setFlashMode(_controller!.value.flashMode == FlashMode.auto
? FlashMode.off
: _controller!.value.flashMode == FlashMode.off
? FlashMode.always
: FlashMode.auto);
setState(() {});
},
),
],
),
)
],
),
);
}
}
class ResizableWidget extends StatefulWidget {
const ResizableWidget({Key? key, required this.cropController}) : super(key: key);
final CropController cropController;
@override
State<ResizableWidget> createState() => _ResizableWidgetState();
}
const ballDiameter = 30.0;
class _ResizableWidgetState extends State<ResizableWidget> {
double height = 200;
double width = 250;
double top = 250;
double left = 55;
void onDrag(double dx, double dy) {
var newHeight = height + dy;
var newWidth = width + dx;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
});
}
@override
Widget build(BuildContext context) {
return Stack(
children: <Widget>[
Positioned(
top: top,
left: left,
child: DottedBorder(
borderType: BorderType.RRect,
radius: const Radius.circular(20),
dashPattern: const [8, 8],
color: Colors.white,
strokeWidth: 2,
child: SizedBox(
height: height,
width: width,
child: Crop(
controller: widget.cropController,
shape: BoxShape.rectangle,
backgroundColor: Colors.transparent,
child: Container(
decoration: const BoxDecoration(
color: Colors.transparent,
),
height: height,
width: width,
),
),
),
),
),
// top left
Positioned(
top: top - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
var newHeight = height - 2 * mid;
var newWidth = width - 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top + mid;
left = left + mid;
});
},
),
),
// top middle
Positioned(
top: top - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = height - dy;
setState(() {
height = newHeight > 0 ? newHeight : 0;
top = top + dy;
});
},
),
),
// top right
Positioned(
top: top - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + (dy * -1)) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
),
),
// center right
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newWidth = width + dx;
setState(() {
width = newWidth > 0 ? newWidth : 0;
});
},
),
),
// bottom right
Positioned(
top: top + height - ballDiameter / 2,
left: left + width - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = (dx + dy) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
),
),
// bottom center
Positioned(
top: top + height - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newHeight = height + dy;
setState(() {
height = newHeight > 0 ? newHeight : 0;
});
},
),
),
// bottom left
Positioned(
top: top + height - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var mid = ((dx * -1) + dy) / 2;
var newHeight = height + 2 * mid;
var newWidth = width + 2 * mid;
setState(() {
height = newHeight > 0 ? newHeight : 0;
width = newWidth > 0 ? newWidth : 0;
top = top - mid;
left = left - mid;
});
},
),
),
//left center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
var newWidth = width - dx;
setState(() {
width = newWidth > 0 ? newWidth : 0;
left = left + dx;
});
},
),
),
// center center
Positioned(
top: top + height / 2 - ballDiameter / 2,
left: left + width / 2 - ballDiameter / 2,
child: ManipulatingBall(
onDrag: (dx, dy) {
setState(() {
top = top + dy;
left = left + dx;
});
},
),
),
],
);
}
}
class ManipulatingBall extends StatefulWidget {
const ManipulatingBall({Key? key, required this.onDrag}) : super(key: key);
final Function onDrag;
@override
State<ManipulatingBall> createState() => _ManipulatingBallState();
}
class _ManipulatingBallState extends State<ManipulatingBall> {
double _initX = 0;
double _initY = 0;
_handleDrag(details) {
setState(() {
_initX = details.globalPosition.dx;
_initY = details.globalPosition.dy;
});
}
_handleUpdate(details) {
var dx = details.globalPosition.dx - _initX;
var dy = details.globalPosition.dy - _initY;
_initX = details.globalPosition.dx;
_initY = details.globalPosition.dy;
widget.onDrag(dx, dy);
}
@override
Widget build(BuildContext context) {
return GestureDetector(
onPanStart: _handleDrag,
onPanUpdate: _handleUpdate,
child: Container(
width: ballDiameter,
height: ballDiameter,
decoration: BoxDecoration(
color: Colors.pink.withOpacity(0.5),
shape: BoxShape.circle,
),
),
);
}
}
class LoadingIndicator extends StatelessWidget {
const LoadingIndicator({Key? key}) : super(key: key);
@override
Widget build(BuildContext context) {
return const Center(
child: CircularProgressIndicator(color: Colors.pink),
);
}
}
Pubspec:
name: camera_cropper
description: A new Flutter project.
publish_to: 'none'
version: 1.0.0+1
environment:
sdk: '>=2.18.0-130.0.dev <3.0.0'
dependencies:
flutter:
sdk: flutter
camera: ^0.9.6
image: ^3.2.0
extended_image: ^6.2.1
dotted_border: ^2.0.0+2
crop: ^0.5.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^2.0.1
flutter:
uses-material-design: true
Flutter doctor:
Running flutter doctor... Doctor summary (to see all details, run flutter doctor -v): [✓] Flutter (Channel master, 3.1.0-0.0.pre.821, on macOS 12.3.1 21E258 darwin-arm, locale en-ZA) [✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1) [✓] Xcode - develop for iOS and macOS (Xcode 13.4) [✓] Chrome - develop for the web [✓] Android Studio (version 2021.1) [✓] Android Studio (version 2021.1) [✓] IntelliJ IDEA Ultimate Edition (version 2022.1.1) [✓] IntelliJ IDEA Ultimate Edition (version 2022.1.1) [✓] IntelliJ IDEA Ultimate Edition (version 2022.1) [✓] Connected device (3 available) [✓] HTTP Host Availability
• No issues found!
Are you using Flutter 3.0? There is an issue with it. Check:
#78 https://github.com/flutter/flutter/issues/103803
This is fixed in the latest Flutter version on master channel. I am closing this issue but please open a new issue anytime.