Merge pull request 'feature/edit-image' (#29) from feature/edit-image into main

Reviewed-on: #29
This commit is contained in:
v4l3n71n 2024-12-15 20:23:19 +01:00
commit 8d8c30f506
4 changed files with 261 additions and 29 deletions

View File

@ -63,8 +63,6 @@ class CameraState extends State<Camera> {
_initializeControllerFuture = _controller.initialize();
}
Future<void> getCamera() async {}
Future<void> pickImage() async {
final imagePicker = ImagePicker();
final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);

View File

@ -0,0 +1,154 @@
import 'dart:async';
import 'dart:io';
import '../classes/events.dart';
import 'package:image_picker/image_picker.dart';
import 'EditEvent.dart';
import 'package:camera/camera.dart';
import 'package:flutter/material.dart';
Future<void> main() async {
// Ensure that plugin services are initialized so that `availableCameras()`
// can be called before `runApp()`
WidgetsFlutterBinding.ensureInitialized();
// Obtain a list of the available cameras on the device.
final cameras = await availableCameras();
// Get a specific camera from the list of available cameras.
final firstCamera = cameras.first;
Events? events;
runApp(
MaterialApp(
theme: ThemeData.dark(),
home: CameraEdit(
// Pass the appropriate camera to the TakePictureScreen widget.
camera: firstCamera,
events: events),
),
);
}
// A screen that allows users to take a picture using a given camera.
class CameraEdit extends StatefulWidget {
const CameraEdit({super.key, required this.camera, required this.events});
final Events? events;
final CameraDescription camera;
@override
CameraEditState createState() => CameraEditState();
}
class CameraEditState extends State<CameraEdit> {
late CameraController _controller;
late Future<void> _initializeControllerFuture;
@override
void initState() {
super.initState();
// To display the current output from the Camera,
// create a CameraController.
_controller = CameraController(
// Get a specific camera from the list of available cameras.
widget.camera,
// Define the resolution to use.
ResolutionPreset.medium,
);
// Next, initialize the controller. This returns a Future.
_initializeControllerFuture = _controller.initialize();
}
Future<void> pickImage() async {
final imagePicker = ImagePicker();
final pickedFile = await imagePicker.pickImage(source: ImageSource.gallery);
if (pickedFile != null) {
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => EditEvent(
// Pass the automatically generated path to
// the DisplayPictureScreen widget.
events: widget.events,
imgPath: pickedFile.path,
),
),
);
}
}
@override
void dispose() {
// Dispose of the controller when the widget is disposed.
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Take a picture')),
// You must wait until the controller is initialized before displaying the
// camera preview. Use a FutureBuilder to display a loading spinner until the
// controller has finished initializing.
body: FutureBuilder<void>(
future: _initializeControllerFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
// If the Future is complete, display the preview.
return CameraPreview(_controller);
} else {
// Otherwise, display a loading indicator.
return const Center(child: CircularProgressIndicator());
}
},
),
floatingActionButton: Padding(
padding: const EdgeInsets.all(8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
FloatingActionButton(
onPressed: pickImage,
child: Icon(Icons.photo_library),
),
SizedBox(width: 40),
FloatingActionButton(
// 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;
// Attempt to take a picture and get the file `image`
// where it was saved.
final image = await _controller.takePicture();
if (!context.mounted) return;
// If the picture was taken, display it on a new screen.
await Navigator.of(context).push(
MaterialPageRoute(
builder: (context) => EditEvent(
// Pass the automatically generated path to
// the DisplayPictureScreen widget.
events: widget.events,
imgPath: image.path,
),
),
);
} catch (e) {
// If an error occurs, log the error to the console.
print(e);
}
},
child: const Icon(Icons.camera_alt),
)
],
)));
}
}

View File

@ -1,3 +1,4 @@
import 'package:covas_mobile/pages/CameraEdit.dart';
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:http/http.dart' as http;
@ -7,9 +8,14 @@ import 'package:textfield_tags/textfield_tags.dart';
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import '../classes/events.dart';
import 'ItemMenu.dart';
import 'CameraEdit.dart';
import 'package:camera/camera.dart';
import '../classes/alert.dart';
import '../classes/eventAdded.dart';
@ -26,15 +32,21 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
debugShowCheckedModeBanner: false,
home: EditEvent(events: events),
home: EditEvent(
events: events,
imgPath: "",
),
);
}
}
class EditEvent extends StatefulWidget {
const EditEvent({Key? key, required this.events}) : super(key: key);
const EditEvent({Key? key, required this.events, required this.imgPath})
: super(key: key);
final Events? events;
final String imgPath;
@override
_EditEventState createState() => _EditEventState();
}
@ -196,12 +208,41 @@ class _EditEventState extends State<EditEvent>
if (responseGet.statusCode == 200) {
var events = jsonDecode(utf8.decode(responseGet.bodyBytes));
if (events.length > 0) {
showErrorDialog(context, "Evenement deja existant");
if (events[0]["id"] != widget.events!.id) {
showErrorDialog(context, "Evenement deja existant");
}
return;
}
}
if (widget.imgPath.isNotEmpty) {
final params = {
'expiration': '15552000',
'key': dotenv.env["IMGBB_API_KEY"],
};
print("Post Img");
final urlPost = Uri.parse('https://api.imgbb.com/1/upload')
.replace(queryParameters: params);
File image = File(widget.imgPath);
Uint8List _bytes = await image.readAsBytes();
String _base64String = base64.encode(_bytes);
final req = http.MultipartRequest('POST', urlPost)
..fields['image'] = _base64String;
final stream = await req.send();
final res = await http.Response.fromStream(stream);
final status = res.statusCode;
print("code status imgbb ${status}");
if (status != 200) {
showErrorDialog(context, "Image non posté");
return;
}
var body = json.decode(utf8.decode(res.bodyBytes));
imgUrl = body["data"]["url"];
}
var urlPut =
Uri.parse("${globals.api}/events/${widget.events!.id}");
var responsePut = await http.put(urlPut,
@ -411,6 +452,14 @@ class _EditEventState extends State<EditEvent>
);
}
Future<void> popCamera() async {
await availableCameras().then((value) => Navigator.push(
context,
MaterialPageRoute(
builder: (_) =>
CameraEdit(camera: value.first, events: widget.events))));
}
@override
Widget build(BuildContext context) {
return Scaffold(
@ -425,30 +474,57 @@ class _EditEventState extends State<EditEvent>
child: SingleChildScrollView(
child: Column(
children: <Widget>[
Padding(
if (widget.imgPath.isNotEmpty)
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Image.network(
imgUrl,
width: MediaQuery.of(context).size.width *
0.5, // 50% of screen width
height: MediaQuery.of(context).size.height * 0.5,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child; // The image has finished loading
}
return Center(
child: CircularProgressIndicator(),
);
},
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return Center(
child: Icon(Icons.error,
size: MediaQuery.of(context).size.width * 0.1),
);
},
)),
child: Center(
child: Container(
width: 200,
height: 150,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(100.0)),
child: Image.file(File(widget.imgPath))),
),
),
if (widget.imgPath.isEmpty)
Padding(
padding: const EdgeInsets.only(top: 60.0),
child: Image.network(
imgUrl,
width: MediaQuery.of(context).size.width *
0.5, // 50% of screen width
height: MediaQuery.of(context).size.height * 0.5,
loadingBuilder: (BuildContext context, Widget child,
ImageChunkEvent? loadingProgress) {
if (loadingProgress == null) {
return child; // The image has finished loading
}
return Center(
child: CircularProgressIndicator(),
);
},
errorBuilder: (BuildContext context, Object error,
StackTrace? stackTrace) {
return Center(
child: Icon(Icons.error,
size: MediaQuery.of(context).size.width * 0.1),
);
},
)),
Padding(
padding: EdgeInsets.symmetric(horizontal: 15),
child: ElevatedButton.icon(
onPressed: popCamera,
icon: Icon(Icons.edit, size: 16), // Edit icon
label: Text("Edit Image"), // Button text
style: ElevatedButton.styleFrom(
backgroundColor: Colors.blue, // Button color
foregroundColor: Colors.white, // Text color
padding:
EdgeInsets.symmetric(horizontal: 10, vertical: 5),
),
),
),
Padding(
//padding: const EdgeInsets.only(left:15.0,right: 15.0,top:0,bottom: 0),
padding: EdgeInsets.symmetric(horizontal: 15),

View File

@ -408,7 +408,11 @@ class _ItemMenuState extends State<ItemMenu> with ShowErrorDialog {
onPressed: () {
Navigator.push(
context,
MaterialPageRoute(builder: (_) => EditEvent(events: events)),
MaterialPageRoute(
builder: (_) => EditEvent(
events: events,
imgPath: "",
)),
);
},
backgroundColor: Colors.blue,