Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added assets/fonts/Lato-Regular.ttf
Binary file not shown.
Binary file added assets/fonts/Montserrat-Regular.ttf
Binary file not shown.
Binary file added assets/fonts/OpenSans-Regular.ttf
Binary file not shown.
Binary file added assets/fonts/Oswald-Regular.ttf
Binary file not shown.
Binary file added assets/fonts/Roboto_Condensed-Regular.ttf
Binary file not shown.
77 changes: 77 additions & 0 deletions lib/draw_canvas/Dialogs/add_text_overlay_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import 'package:flutter/material.dart';
import 'package:magic_epaper_app/draw_canvas/models/overlay_item.dart';

Widget buildTextOverlayDialog({
required BuildContext context,
required Color selectedColor,
required void Function(OverlayItem) onItemCreated,
}) {
TextEditingController controller = TextEditingController();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider disposing the TextEditingController.

Since the controller is created in the dialog without being disposed, there is a potential for a memory leak. Even in dialog code, ensuring proper disposal can improve resource management.

double selectedFontSize = 24;
String selectedFont = 'Roboto';
List<String> fontOptions = [
'Roboto',
'Open Sans',
'Lato',
'Montserrat',
'Oswald'
];

return StatefulBuilder(
builder: (context, setDialogState) => AlertDialog(
title: Text("Enter Text"),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
TextField(controller: controller),
const SizedBox(height: 10),
DropdownButton<String>(
value: selectedFont,
onChanged: (value) => setDialogState(() => selectedFont = value!),
items: fontOptions.map((font) {
return DropdownMenuItem(
value: font,
child: Text(font, style: TextStyle(fontFamily: font)),
);
}).toList(),
),
const SizedBox(height: 10),
Row(
children: [
Text("Font size: ${selectedFontSize.toInt()}"),
Expanded(
child: Slider(
value: selectedFontSize,
min: 12,
max: 48,
divisions: 12,
onChanged: (value) =>
setDialogState(() => selectedFontSize = value),
),
),
],
),
],
),
actions: [
TextButton(
onPressed: () {
final text = controller.text;
Navigator.pop(context);
if (text.isNotEmpty) {
onItemCreated(
OverlayItem.text(
text: text,
color: selectedColor,
font: selectedFont,
fontSize: selectedFontSize,
),
);
}
},
child: Text("Add"),
),
],
),
);
}
26 changes: 26 additions & 0 deletions lib/draw_canvas/Dialogs/build_image_preview_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';

Widget buildImagePreviewDialog({
required BuildContext context,
required Uint8List image,
required VoidCallback onSubmit,
}) {
return AlertDialog(
title: const Text("Preview Captured Image"),
content: SingleChildScrollView(child: Image.memory(image)),
actions: [
TextButton(
onPressed: () => Navigator.pop(context),
child: const Text("Close"),
),
TextButton(
onPressed: () {
Navigator.pop(context);
onSubmit();
},
child: const Text("Submit"),
),
],
);
}
17 changes: 17 additions & 0 deletions lib/draw_canvas/Dialogs/pick_color_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:flutter/material.dart';
import 'package:flutter_colorpicker/flutter_colorpicker.dart';

Widget buildColorPickerDialog(BuildContext context, Color selectedColor,
ValueChanged<Color> onColorPicked) {
return AlertDialog(
title: const Text("Pick a color"),
content: BlockPicker(
availableColors: [Colors.black, Colors.white, Colors.red],
pickerColor: selectedColor,
onColorChanged: (color) {
onColorPicked(color);
Navigator.of(context).pop();
},
),
);
}
30 changes: 30 additions & 0 deletions lib/draw_canvas/Dialogs/show_layer_manager_dialog.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'package:flutter/material.dart';
import 'package:magic_epaper_app/draw_canvas/models/overlay_item.dart';

Widget buildLayerManagerDialog({
required List<OverlayItem> items,
required void Function(int oldIndex, int newIndex) onReorder,
required void Function(void Function()) setModalState,
}) {
return ReorderableListView(
onReorder: (oldIndex, newIndex) {
onReorder(oldIndex, newIndex);
setModalState(() {});
},
children: [
for (int i = 0; i < items.length; i++)
ListTile(
key: ValueKey(items[i].id),
title: Text(
items[i].type == 'text'
? items[i].text ?? 'Text Layer'
: items[i].label ?? 'Image Layer',
),
leading: Icon(
items[i].type == 'text' ? Icons.text_fields : Icons.image,
),
trailing: const Icon(Icons.drag_handle),
),
],
);
}
9 changes: 9 additions & 0 deletions lib/draw_canvas/ImageAdjust/image_adjust_parms.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'package:image/image.dart' as img;

class ImageAdjustParams {
final img.Image image;
final double brightness;
final double contrast;

ImageAdjustParams(this.image, this.brightness, this.contrast);
}
18 changes: 18 additions & 0 deletions lib/draw_canvas/ImageAdjust/process_image.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import 'dart:typed_data';
import 'package:image/image.dart' as img;
import 'package:flutter/foundation.dart';
import 'package:magic_epaper_app/draw_canvas/ImageAdjust/image_adjust_parms.dart';

Uint8List processImage(ImageAdjustParams params) {
final img.Image adjusted = img.adjustColor(
params.image.clone(),
brightness: params.brightness,
contrast: params.contrast,
);
return Uint8List.fromList(img.encodePng(adjusted));
}

img.Image decodeImage(Uint8List bytes) {
final original = img.decodeImage(bytes)!;
return img.copyResize(original, width: 512);
}
135 changes: 135 additions & 0 deletions lib/draw_canvas/helper_functions/helper_functions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:magic_epaper_app/draw_canvas/Dialogs/add_text_overlay_dialog.dart';
import 'package:magic_epaper_app/draw_canvas/Dialogs/build_image_preview_dialog.dart';
import 'package:magic_epaper_app/draw_canvas/Dialogs/pick_color_dialog.dart';
import 'package:magic_epaper_app/draw_canvas/Dialogs/show_layer_manager_dialog.dart';
import 'package:magic_epaper_app/draw_canvas/models/overlay_item.dart';
import 'package:magic_epaper_app/draw_canvas/view/image_adjust_page.dart';
import 'package:magic_epaper_app/util/epd/edp.dart';
import 'package:screenshot/screenshot.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:image_cropper/image_cropper.dart';

Future<void> pickColorDialog(BuildContext context, Color selectedColor,
ValueChanged<Color> onColorPicked) async {
showDialog(
context: context,
builder: (_) =>
buildColorPickerDialog(context, selectedColor, onColorPicked),
);
}

void addTextOverlayDialog({
required BuildContext context,
required Color selectedColor,
required void Function(OverlayItem) onItemCreated,
}) {
showDialog(
context: context,
builder: (_) => buildTextOverlayDialog(
context: context,
selectedColor: selectedColor,
onItemCreated: onItemCreated,
),
);
}

void showLayerManagerModal({
required BuildContext context,
required List<OverlayItem> items,
required void Function(int oldIndex, int newIndex) onReorder,
}) {
showModalBottomSheet(
context: context,
builder: (_) {
return StatefulBuilder(
builder: (context, setModalState) {
return buildLayerManagerDialog(
items: items,
onReorder: onReorder,
setModalState: setModalState,
);
},
);
},
);
}

Future<void> pickImageFromGallery({
required void Function(OverlayItem) onImagePicked,
}) async {
final ImagePicker picker = ImagePicker();
final XFile? image = await picker.pickImage(source: ImageSource.gallery);

if (image != null) {
final bytes = await image.readAsBytes();
final name = image.name;

onImagePicked(
OverlayItem.image(
imageBytes: bytes,
label: name,
),
);
}
}

Future<void> captureAndProcessImage({
required BuildContext context,
required ScreenshotController controller,
required Epd epd,
required void Function(Uint8List adjustedBytes) onImageExported,
required void Function() onCaptureStart,
required void Function() onCaptureEnd,
}) async {
onCaptureStart();

await Future.delayed(const Duration(milliseconds: 100));
final image = await controller.capture();

onCaptureEnd();

if (image == null) {
ScaffoldMessenger.of(context)
.showSnackBar(const SnackBar(content: Text("Failed to capture image")));
return;
}

showDialog(
context: context,
builder: (_) => buildImagePreviewDialog(
context: context,
image: image,
onSubmit: () async {
final tempDir = await getTemporaryDirectory();
final tempPath = '${tempDir.path}/captured_image.png';
final file = await File(tempPath).writeAsBytes(image);

final croppedFile = await ImageCropper().cropImage(
sourcePath: file.path,
aspectRatio: CropAspectRatio(
ratioX: epd.width.toDouble(),
ratioY: epd.height.toDouble(),
),
);

if (croppedFile != null) {
final croppedBytes = await File(croppedFile.path).readAsBytes();
final adjustedBytes = await Navigator.push(
context,
MaterialPageRoute(
builder: (_) => ImageAdjustScreen(imageBytes: croppedBytes),
),
);

if (adjustedBytes != null) {
onImageExported(adjustedBytes);
}
}
},
),
);
}
8 changes: 8 additions & 0 deletions lib/draw_canvas/models/draw_line.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import 'package:flutter/material.dart';

class DrawnLine {
List<Offset> path;
Color color;
double width;
DrawnLine(this.path, this.color, this.width);
}
44 changes: 44 additions & 0 deletions lib/draw_canvas/models/overlay_item.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';

enum OverlayType { text, image }

class OverlayItem {
final String id;
final String type;
final String? text;
final Uint8List? imageBytes;
final Color? color;
final String font;
final double fontSize;
final String? label;
Offset position;
double scale;
double rotation;

OverlayItem.text({
required this.text,
required this.color,
this.font = 'Roboto',
this.fontSize = 24.0,
this.label,
this.position = const Offset(100, 100),
this.scale = 1.0,
this.rotation = 0.0,
}) : id = UniqueKey().toString(),
type = 'text',
imageBytes = null;

OverlayItem.image({
required this.imageBytes,
this.font = 'Roboto',
this.fontSize = 24.0,
this.label,
this.position = const Offset(100, 100),
this.scale = 1.0,
this.rotation = 0.0,
}) : id = UniqueKey().toString(),
type = 'image',
text = null,
color = null;
}
25 changes: 25 additions & 0 deletions lib/draw_canvas/models/sketcher.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import 'package:flutter/material.dart';
import 'package:magic_epaper_app/draw_canvas/models/draw_line.dart';

class Sketcher extends CustomPainter {
final List<DrawnLine> lines;
Sketcher(this.lines);

@override
void paint(Canvas canvas, Size size) {
for (var line in lines) {
final paint = Paint()
..color = line.color
..strokeWidth = line.width
..strokeCap = StrokeCap.round
..style = PaintingStyle.stroke;

for (int i = 0; i < line.path.length - 1; i++) {
canvas.drawLine(line.path[i], line.path[i + 1], paint);
}
}
}

@override
bool shouldRepaint(Sketcher oldDelegate) => true;
}
Loading
Loading