Out of my league but I've put together the bones of a camera app (actiually MS Copilot did it).
Edit: A screenie of the app in action:
The last issue is to have the photo saved where I want it to go, e.g. "Internal shared storage/DCIM/AppTestPhotos".
The immediately related code:
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.camera),
onPressed: () async {
final XFile photo = await controller.takePicture();
String filePath = photo.path;
print('File path from photo.path: $filePath');
await Gal.putImage(filePath);
print('Image saved at: $filePath');
},
),
Using the default path from the XFile, the file path is indicated to be:
/data/user/0/com.example.cam_test_2_one_screen/cache/CAP555419134659270816.jpg
...which shows up in the "Internal shared storage/Pictures" Folder on the phone. I've tried amending the path to get it to show up in "Internal shared storage/DCIM/AppTestPhotos" but with no luck.
Any thoughts about how to get this to work? So close it seems.
Since it's not too long, here's the full main.dart file:
import 'dart:math';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';
import 'dart:io';
import 'package:permission_handler/permission_handler.dart';
import 'package:gal/gal.dart';
import 'package:path/path.dart' as p;
List<CameraDescription> cameras = [];
void main() async {
WidgetsFlutterBinding.ensureInitialized();
cameras = await availableCameras();
runApp(const CameraApp());
}
class CameraApp extends StatelessWidget {
const CameraApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: CameraScreen(),
);
}
}
class CameraScreen extends StatefulWidget {
const CameraScreen({super.key});
@override
// ignore: library_private_types_in_public_api
_CameraScreenState createState() => _CameraScreenState();
}
class _CameraScreenState extends State<CameraScreen> {
late CameraController controller;
bool showFocusCircle = false;
double x = 0;
double y = 0;
@override
void initState() {
super.initState();
controller = CameraController(cameras[0], ResolutionPreset.medium);
controller.initialize().then((_) {
if (!mounted) {
return;
}
setState(() {});
});
}
@override
Widget build(BuildContext context) {
if (!controller.value.isInitialized) {
return Container();
}
return Scaffold(
appBar: AppBar(title: const Text('Take a picture')),
body: GestureDetector(
onTapUp: (details) {
_onTap(details);
},
child: Stack(
children: [
Center(child: CameraPreview(controller)),
if (showFocusCircle)
Positioned(
top: y - 20,
left: x - 20,
child: Container(
height: 40,
width: 40,
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(color: Colors.white, width: 1.5),
),
),
),
],
),
),
floatingActionButton: FloatingActionButton(
child: const Icon(Icons.camera),
onPressed: () async {
final XFile photo = await controller.takePicture();
String filePath = photo.path;
print('File path from photo.path: $filePath');
await Gal.putImage(filePath);
print('Image saved at: $filePath');
},
),
// floatingActionButton: FloatingActionButton(
// child: const Icon(Icons.camera),
// onPressed: () async {
// final XFile photo = await controller.takePicture();
// final Directory? extDir = await getExternalStorageDirectory();
// if (extDir != null) {
// final String dirPath = '${extDir.path}/DCIM/AppTestPhotos';
// await Directory(dirPath).create(recursive: true);
// final String filePath = '$dirPath/${DateTime.now()}.jpg';
// await photo.saveTo(filePath);
// print('Picture saved at: $filePath');
// }
// },
// ),
// floatingActionButton: FloatingActionButton(
// child: const Icon(Icons.camera),
// onPressed: () async {
// final XFile photo = await controller.takePicture();
// final Directory? extDir = await getExternalStorageDirectory();
// if (extDir != null) {
// final String dirPath = '${extDir.path}/AppTestPhotos';
// await Directory(dirPath).create(recursive: true);
// final String filePath = '$dirPath/${DateTime.now()}.jpg';
// await photo.saveTo(filePath);
// print('Picture saved at: $filePath');
// }
// },
// ),
// floatingActionButton: FloatingActionButton(
// child: Icon(Icons.camera),
// onPressed: () async {
// final XFile photo = await controller.takePicture();
// print('Picture saved at: ${photo.path}');
// },
// ),
);
}
Future<void> _onTap(TapUpDetails details) async {
if (controller.value.isInitialized) {
showFocusCircle = true;
x = details.localPosition.dx;
y = details.localPosition.dy;
double fullWidth = MediaQuery.of(context).size.width;
double cameraHeight = fullWidth * controller.value.aspectRatio;
double xp = x / fullWidth;
double yp = y / cameraHeight;
Offset point = Offset(xp, yp);
await controller.setFocusPoint(point);
setState(() {
Future.delayed(const Duration(seconds: 2)).whenComplete(() {
setState(() {
showFocusCircle = false;
});
});
});
}
}
}
Future<void> _requestStoragePermission() async {
final status = await Permission.storage.request();
print('Permission status: $status');
}
FloatingActionButton(
child: const Icon(Icons.camera),
onPressed: () async {
final XFile photo = await controller.takePicture();
Uint8List captureImage = File(photo.path).readAsBytesSync();
String _directory = '';
if (Platform.isIOS) {
_directory = (await getApplicationSupportDirectory()).path;
} else {
/// It might not work with latest Android version
/// Check Permission WRITE_EXTERNAL_STORAGE
_directory = '/storage/emulated/0/DCIM';
}
final path = '$_directory/captured${const Uuid().v1()}.png';
final imagePath = await File(path).create();
await imagePath.writeAsBytes(captureImage);
await Gal.putImage(path);
print('Image saved at: $path');
},
),