Skip to content
This repository has been archived by the owner on Dec 13, 2024. It is now read-only.

Commit

Permalink
Add ImageDialog with camera support
Browse files Browse the repository at this point in the history
  • Loading branch information
aquib-sh committed Jun 7, 2023
1 parent 0e51321 commit 8f7e30c
Show file tree
Hide file tree
Showing 8 changed files with 316 additions and 65 deletions.
3 changes: 3 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

<application
android:label="geotales"
android:name="${applicationName}"
Expand Down
4 changes: 3 additions & 1 deletion lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import 'package:geotales/providers/file_provider.dart';
import 'package:geotales/providers/map_provider.dart';
import 'package:geotales/providers/session_provider.dart';
import 'package:geotales/screens/login_screen.dart';
import 'package:geotales/widgets/file_upload_dialog.dart';
//import 'package:geotales/screens/splash_screen.dart';
import 'package:provider/provider.dart';

Expand All @@ -31,7 +32,8 @@ class MyApp extends StatelessWidget {
ChangeNotifierProvider(
create: (context) => SessionProvider(FirebaseAuth.instance)),
ChangeNotifierProvider(create: (context) => MapProvider()),
ChangeNotifierProvider(create: (context) => FileProvider())
ChangeNotifierProvider(create: (context) => FileProvider()),
ChangeNotifierProvider(create: (context) => FileUploadProvider())
],
child: MaterialApp(
debugShowCheckedModeBanner: false,
Expand Down
3 changes: 1 addition & 2 deletions lib/providers/map_provider.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_map/plugin_api.dart';
import 'package:geolocator/geolocator.dart';
import 'package:geotales/models/user_file.dart';
import 'package:geotales/widgets/marker_dialog.dart';
import 'package:latlong2/latlong.dart';

Expand All @@ -13,7 +12,7 @@ class MapProvider extends ChangeNotifier {
final MapController mapController = MapController();

// Basic map settings
LatLng currentLocation = const LatLng(18.9932969, 72.8202488);
LatLng currentLocation = LatLng(18.9932969, 72.8202488);
final double zoom = 10.0;
final double maxZoom = 18.0;
final double minZoom = 1.0;
Expand Down
193 changes: 193 additions & 0 deletions lib/widgets/file_upload_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import 'dart:io';

import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:provider/provider.dart';
import 'package:flutter/foundation.dart' show kIsWeb;

class FileUploadDialog extends StatelessWidget {
@override
Widget build(BuildContext context) {
return ChangeNotifierProvider<FileUploadProvider>(
create: (_) => FileUploadProvider(),
child: Consumer<FileUploadProvider>(
builder: (context, fileUploadProvider, _) {
return MaterialApp(
debugShowCheckedModeBanner: false,
title: 'File Upload Dialog',
home: Scaffold(
body: Center(
child: Column(
children: [
Expanded(
child: ListView.builder(
itemCount: fileUploadProvider.imageDataList.length,
itemBuilder: (context, index) {
return DismissibleImage(
imageUrl: fileUploadProvider.imageDataList[index]
['url'],
imagePath: fileUploadProvider.imageDataList[index]
['path'],
onDismissed: () {
fileUploadProvider.removeImage(index);
},
);
},
),
),
Row(
children: [
SelectButton(
onPressed: () async {
final pickedFile = await ImagePicker().pickImage(
source: ImageSource.gallery,
imageQuality: 80,
);
if (pickedFile != null) {
final bytes = await pickedFile.readAsBytes();
final imageData = {
'filename': pickedFile.name,
'data': bytes,
'url': pickedFile.path,
'path': pickedFile.path,
'mimeType': pickedFile.mimeType
};
fileUploadProvider.addImage(imageData);
}
},
),
const Spacer(),
SelectButton(
onPressed: () async {
final pickedFile = await ImagePicker().pickImage(
source: ImageSource.camera,
imageQuality: 80,
);
if (pickedFile != null) {
final bytes = await pickedFile.readAsBytes();
final imageData = {
'filename': pickedFile.name,
'data': bytes,
'url': pickedFile.path,
'path': pickedFile.path,
'mimeType': pickedFile.mimeType
};
fileUploadProvider.addImage(imageData);
}
},
buttonText: 'Capture',
),
const Spacer(),
ElevatedButton(
onPressed: fileUploadProvider.canUpload
? fileUploadProvider.uploadImages
: null,
child: const Text('Upload'),
),
],
),
],
),
),
),
);
},
),
);
}
}

class FileUploadProvider with ChangeNotifier {
final List<Map<String, dynamic>> _imageDataList = [];

List<Map<String, dynamic>> get imageDataList => _imageDataList;

bool get canUpload => _imageDataList.isNotEmpty;

void addImage(Map<String, dynamic> imageData) {
_imageDataList.add(imageData);
notifyListeners();
}

void removeImage(int index) {
_imageDataList.removeAt(index);
notifyListeners();
}

void uploadImages() async {
for (var imageData in _imageDataList) {
// Perform the file upload logic using the provider data
// ...

// Simulating file upload delay
await Future.delayed(Duration(seconds: 2));

// Remove the uploaded image from the list
_imageDataList.remove(imageData);
notifyListeners();
}
}
}

class DismissibleImage extends StatelessWidget {
final String imageUrl;
final String imagePath;
final VoidCallback onDismissed;

const DismissibleImage({
Key? key,
required this.imageUrl,
required this.imagePath,
required this.onDismissed,
}) : super(key: key);

@override
Widget build(BuildContext context) {
Widget imageWidget;

if (kIsWeb) {
imageWidget = Image.network(imageUrl);
} else {
imageWidget = Image.file(File(imagePath));
}

return Dismissible(
key: Key(kIsWeb ? imageUrl : imagePath),
direction: DismissDirection.up,
onDismissed: (_) => onDismissed(),
child: Stack(
children: [
imageWidget,
Positioned(
top: 8,
right: 8,
child: IconButton(
icon: const Icon(Icons.cancel),
color: Colors.red,
onPressed: () => onDismissed(),
),
),
],
),
);
}
}

class SelectButton extends StatelessWidget {
final void Function()? onPressed;
final String buttonText;

const SelectButton({
Key? key,
this.onPressed,
this.buttonText = 'Select',
}) : super(key: key);

@override
Widget build(BuildContext context) {
return ElevatedButton(
onPressed: onPressed,
child: Text(buttonText),
);
}
}
131 changes: 71 additions & 60 deletions lib/widgets/marker_dialog.dart
Original file line number Diff line number Diff line change
@@ -1,20 +1,30 @@
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:geotales/providers/file_provider.dart';
import 'package:geotales/providers/map_provider.dart';
import 'package:geotales/widgets/file_upload_dialog.dart';
import 'package:provider/provider.dart';

class MarkerDialog extends StatelessWidget {
final BoxConstraints constraints;
const MarkerDialog({super.key, required this.constraints});

const MarkerDialog({Key? key, required this.constraints}) : super(key: key);

@override
Widget build(BuildContext context) {
return Consumer2<FileProvider, MapProvider>(
builder: (context, files, map, child) => AlertDialog(
title: const Text("Files in this location"),
content: Column(
return Container(
color: Colors.transparent,
child: Dialog(
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8.0),
),
elevation: 0.0,
backgroundColor: Colors.white,
child: SingleChildScrollView(
child: Container(
padding: const EdgeInsets.all(16.0),
child: Consumer2<FileProvider, MapProvider>(
builder: (context, files, map, child) => Column(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisSize: MainAxisSize.min,
children: [
Expand All @@ -23,65 +33,66 @@ class MarkerDialog extends StatelessWidget {
style: const TextStyle(fontSize: 12, color: Colors.brown),
),
SizedBox(
height: constraints.maxHeight * 0.7,
width: constraints.maxWidth * 0.9,
child: ListView.builder(
height: constraints.maxHeight * 0.5,
child: ListView.separated(
itemCount: files.markerFiles.length,
separatorBuilder: (context, index) => Divider(),
itemBuilder: (context, index) {
return (files.markerFiles.isNotEmpty)
? ListTile(
title: Text(files.markerFiles[index].fileName,
style: const TextStyle(
fontSize: 15, color: Colors.black)),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Posted by ${files.markerFiles[index].userEmail}",
style: const TextStyle(
fontSize: 12, color: Colors.blueGrey),
),
Text(
"Date: ${files.markerFiles[index].uploadTimestamp}",
style: const TextStyle(
fontSize: 12,
color: Colors.blueGrey))
],
),
leading: Image.memory(
base64Decode(
files.markerFiles[index].base64Image),
width: 50,
height: 50,
fit: BoxFit.cover,
),
)
: null;
final markerFile = files.markerFiles[index];
return ListTile(
title: Text(
markerFile.fileName,
style: const TextStyle(
fontSize: 15, color: Colors.black),
),
subtitle: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
"Posted by ${markerFile.userEmail}",
style: const TextStyle(
fontSize: 12, color: Colors.blueGrey),
),
Text(
"Date: ${markerFile.uploadTimestamp}",
style: const TextStyle(
fontSize: 12, color: Colors.blueGrey),
),
],
),
leading: Image.memory(
base64Decode(markerFile.base64Image),
width: 50,
height: 50,
fit: BoxFit.cover,
),
);
},
),
),
const SizedBox(height: 16.0),
Align(
alignment: Alignment.centerRight,
child: TextButton(
onPressed: () {
showDialog(
context: context,
builder: (BuildContext context) {
return AlertDialog(
content: FileUploadDialog(),
);
},
);
},
child: const Text("Upload File"),
),
)
),
],
),
actions: [
TextButton(
onPressed: () {
//showDialog(
// context: context,
// builder: (BuildContext context) {
// return AlertDialog(
// content: ImageUploaderDialog(
// location: widget.coordinates,
// userId: widget.userId,
// userEmail: widget.userEmail,
// onFileUpload: fetchImages, // Pass the callback
// ),
// );
// },
//);
},
child: const Text("Upload File"),
),
],
));
),
),
),
),
);
}
}
Loading

0 comments on commit 8f7e30c

Please sign in to comment.