123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244 |
- import 'dart:io';
- import 'package:flutter/material.dart';
- import 'package:image_picker/image_picker.dart';
- import 'package:exif/exif.dart';
- import 'image_helper.dart';
- import 'package:file_picker/file_picker.dart';
- import 'package:path/path.dart' as path;
- class RemoveMetadataPage extends StatefulWidget {
- const RemoveMetadataPage({super.key});
- @override
- _RemoveMetadataPageState createState() => _RemoveMetadataPageState();
- }
- class _RemoveMetadataPageState extends State<RemoveMetadataPage> {
- File? _image;
- Map<String, IfdTag> _metadata = {};
- String? shareImagePath;
- bool _isImagePickerActive = false;
- // Show a dialog asking if the user wants to replace an existing file
- Future<int> _showReplaceDialog(BuildContext context) async {
- return (await showDialog<int>(
- context: context,
- builder: (BuildContext context) {
- return AlertDialog(
- title: Text('File Already Exists'),
- content: Text(
- 'A file with this name already exists. Do you want to replace it? If not, the new file will be renamed with "clean_" at the beginning.'),
- actions: <Widget>[
- TextButton(
- onPressed: () {
- Navigator.of(context).pop(0);
- },
- child: Text('Cancel'),
- ),
- TextButton(
- onPressed: () {
- Navigator.of(context).pop(1);
- },
- child: Text('Replace'),
- ),
- ],
- );
- },
- )) ??
- 0;
- }
- // Select an image from the gallery
- Future<void> getImage() async {
- if (_isImagePickerActive) {
- print('Image picker is already active.');
- return;
- }
- setState(() {
- _isImagePickerActive = true;
- });
- final picker = ImagePicker();
- final pickedFile = await picker.pickImage(source: ImageSource.gallery);
- setState(() {
- _isImagePickerActive = false;
- });
- if (pickedFile != null) {
- _image = File(pickedFile.path);
- Map<String, IfdTag>? data = await ImageHelper.extractMetadata(_image!);
- setState(() {
- _metadata = data ?? {};
- });
- } else {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('No image selected.')),
- );
- }
- }
- // Remove metadata from the image and save it
- void removeMetadata() async {
- if (_image == null) return;
- String? selectedDirectory = await FilePicker.platform
- .getDirectoryPath(dialogTitle: "Select Directory To Save The Image");
- if (selectedDirectory == null) {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('No directory selected.')),
- );
- return;
- }
- String originalFileName = path.basename(_image!.path);
- String targetPath = path.join(selectedDirectory, originalFileName);
- // Check if the file already exists in the selected directory
- if (await File(targetPath).exists()) {
- int choice = await _showReplaceDialog(context);
- if (choice == 0) {
- // User chose not to replace the file
- originalFileName = 'clean_$originalFileName';
- targetPath = path.join(selectedDirectory, originalFileName);
- }
- }
- File? cleanedImage;
- try {
- cleanedImage = await ImageHelper.saveImageWithoutMetadata(_image!);
- await ImageHelper.saveToDirectory(
- cleanedImage, selectedDirectory, originalFileName);
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('Image saved to gallery.')),
- );
- // Delete The _image Cach File
- if (Platform.isAndroid || Platform.isIOS) {
- await _image!.delete();
- }
- shareImagePath = targetPath;
- setState(() {
- _metadata = {};
- });
- } catch (e) {
- ScaffoldMessenger.of(context).showSnackBar(
- const SnackBar(content: Text('Failed to save image to gallery.')),
- );
- } finally {
- // Delete the temporary file after saving it to the gallery
- await cleanedImage?.delete();
- }
- }
- // Check if remove metadata button should be visible
- bool shouldShowRemoveButton() {
- return _image != null && _metadata.isNotEmpty;
- }
- // Check if share button should be visible
- bool shouldShowShareButton() {
- return _image != null && _metadata.isEmpty && Platform.isAndroid;
- }
- // Build metadata list
- Widget _buildMetadataList() {
- if (_metadata.isEmpty) {
- return const Text('No metadata available.');
- }
- return ListView(
- shrinkWrap: true,
- children: _metadata.entries.map((entry) {
- return ListTile(
- title: Text(entry.key),
- subtitle: Text(entry.value.toString()),
- );
- }).toList(),
- );
- }
- @override
- Widget build(BuildContext context) {
- return Scaffold(
- appBar: AppBar(
- title: const Text('Image Meta Cleaner'),
- actions: [
- IconButton(
- icon: const Icon(Icons.info),
- onPressed: () {
- Navigator.pushNamed(context, '/about');
- },
- ),
- ],
- ),
- body: Column(
- crossAxisAlignment: CrossAxisAlignment.start,
- children: [
- Center(
- child: _image == null
- ? const Text('No image selected.')
- : Image.file(
- _image!,
- height: 300,
- width: 300,
- fit: BoxFit.cover,
- ),
- ),
- const SizedBox(height: 20),
- const Padding(
- padding: EdgeInsets.symmetric(horizontal: 20),
- child: Text(
- 'Image Metadata:',
- style: TextStyle(
- fontWeight: FontWeight.bold,
- fontSize: 18,
- ),
- ),
- ),
- const SizedBox(height: 10),
- Expanded(
- child: Padding(
- padding: const EdgeInsets.symmetric(horizontal: 20),
- child: _buildMetadataList(),
- ),
- ),
- ],
- ),
- floatingActionButton: Column(
- mainAxisAlignment: MainAxisAlignment.end,
- children: [
- FloatingActionButton(
- onPressed: getImage,
- tooltip: 'Pick Image',
- child: const Icon(Icons.photo),
- ),
- const SizedBox(height: 16),
- Visibility(
- visible: shouldShowRemoveButton(),
- child: FloatingActionButton(
- onPressed: removeMetadata,
- tooltip: 'Remove Metadata',
- child: const Icon(Icons.delete),
- ),
- ),
- Visibility(
- visible: shouldShowShareButton(),
- child: FloatingActionButton(
- onPressed: () => ImageHelper.shareImage(shareImagePath!),
- tooltip: 'Share Image',
- child: const Icon(Icons.share),
- ),
- ),
- ],
- ),
- );
- }
- }
|