Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions lib/constants/color_constants.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import 'package:flutter/material.dart';
const Color colorPrimary = Color(0xFFD32F2F);
const Color colorPrimaryDark = Color(0xFFC72C2C);
const Color colorAccent = Color(0xFFD32F2F);
const Color colorBlack = Colors.black;
const Color colorWhite = Colors.white;

/// Knob Colors
const Color backCircleColor = Color(0xFFEDEDED);
Expand Down
260 changes: 190 additions & 70 deletions lib/view/image_editor.dart
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import 'dart:typed_data';

import 'package:flutter/material.dart';
import 'package:magic_epaper_app/pro_image_editor/features/movable_background_image.dart';
import 'package:magic_epaper_app/view/widget/flip_controls.dart';
import 'package:magic_epaper_app/util/image_editor_utils.dart';
import 'package:magic_epaper_app/view/widget/image_list.dart';
import 'package:provider/provider.dart';
Expand All @@ -11,6 +9,7 @@ import 'package:image/image.dart' as img;
import 'package:magic_epaper_app/provider/image_loader.dart';
import 'package:magic_epaper_app/util/epd/epd.dart';
import 'package:magic_epaper_app/constants/color_constants.dart';
import 'package:magic_epaper_app/util/protocol.dart';

class ImageEditor extends StatefulWidget {
final Epd epd;
Expand All @@ -21,9 +20,22 @@ class ImageEditor extends StatefulWidget {
}

class _ImageEditorState extends State<ImageEditor> {
int _selectedFilterIndex = 0;
bool flipHorizontal = false;
bool flipVertical = false;

img.Image? _processedSourceImage;
List<img.Image> _rawImages = [];
List<Uint8List> _processedPngs = [];

void _onFilterSelected(int index) {
if (_selectedFilterIndex != index) {
setState(() {
_selectedFilterIndex = index;
});
}
}

void toggleFlipHorizontal() {
setState(() {
flipHorizontal = !flipHorizontal;
Expand All @@ -36,25 +48,44 @@ class _ImageEditorState extends State<ImageEditor> {
});
}

@override
Widget build(BuildContext context) {
var imgLoader = context.watch<ImageLoader>();
final orgImg = imgLoader.image;
void _updateProcessedImages(img.Image? sourceImage) {
if (sourceImage == null) {
if (_rawImages.isNotEmpty) {
setState(() {
_processedSourceImage = null;
_rawImages = [];
_processedPngs = [];
});
}
return;
}

final List<img.Image> processedImgs = orgImg != null
? processImages(
originalImage: orgImg,
epd: widget.epd,
)
: [];
if (_processedSourceImage == sourceImage) {
return;
}

final imgList = ImageList(
imgList: processedImgs,
_rawImages = processImages(
originalImage: sourceImage,
epd: widget.epd,
flipHorizontal: flipHorizontal,
flipVertical: flipVertical,
);

_processedPngs = _rawImages
.map((rawImg) => img.encodePng(img.copyRotate(rawImg, angle: 90)))
.toList();

setState(() {
_processedSourceImage = sourceImage;
_selectedFilterIndex = 0;
flipHorizontal = false;
flipVertical = false;
});
}

@override
Widget build(BuildContext context) {
var imgLoader = context.watch<ImageLoader>();
_updateProcessedImages(imgLoader.image);

return Scaffold(
backgroundColor: Colors.white,
appBar: AppBar(
Expand All @@ -63,70 +94,159 @@ class _ImageEditorState extends State<ImageEditor> {
),
backgroundColor: colorAccent,
elevation: 0,
title: const Center(
child: Padding(
padding: EdgeInsets.symmetric(vertical: 16.0),
child: Text(
'Select Your Filter',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
color: Colors.white,
),
),
),
title: const Text(
'Select a Filter',
style: TextStyle(color: Colors.white, fontWeight: FontWeight.bold),
),
toolbarHeight: 85,
actions: <Widget>[
Padding(
padding:
const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
child: TextButton(
style: TextButton.styleFrom(
backgroundColor: Colors.white.withValues(alpha: 0.2),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
if (_rawImages.isNotEmpty)
Padding(
padding: const EdgeInsets.only(right: 12.0),
child: TextButton(
onPressed: () {
img.Image finalImg = _rawImages[_selectedFilterIndex];

if (flipHorizontal) {
finalImg = img.flipHorizontal(finalImg);
}
if (flipVertical) {
finalImg = img.flipVertical(finalImg);
}
Protocol(epd: widget.epd).writeImages(finalImg);
},
style: TextButton.styleFrom(
backgroundColor: colorAccent,
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(8),
side: const BorderSide(color: Colors.white, width: 1),
),
),
),
onPressed: () {
imgLoader.pickImage(
width: widget.epd.width, height: widget.epd.height);
},
child: const Text(
"Import Image",
style: TextStyle(fontWeight: FontWeight.bold),
child: const Text('Transfer'),
),
),
),
TextButton(
onPressed: () async {
final canvasBytes = await Navigator.of(context).push<Uint8List>(
MaterialPageRoute(
builder: (context) => const MovableBackgroundImageExample(),
),
);
imgLoader.updateImage(
bytes: canvasBytes!,
width: widget.epd.width,
height: widget.epd.height,
);
},
child: const Text("Open Editor"),
),
],
),
floatingActionButton: orgImg != null
? FlipControls(
onFlipHorizontal: toggleFlipHorizontal,
onFlipVertical: toggleFlipVertical,
)
: null,
body: SafeArea(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0),
child: imgList,
child: _processedPngs.isNotEmpty
? ImageList(
key: ValueKey(_processedSourceImage),
processedPngs: _processedPngs,
epd: widget.epd,
selectedIndex: _selectedFilterIndex,
flipHorizontal: flipHorizontal,
flipVertical: flipVertical,
onFilterSelected: _onFilterSelected,
onFlipHorizontal: toggleFlipHorizontal,
onFlipVertical: toggleFlipVertical,
)
: const Center(
child: Text(
"Import an image to begin",
style: TextStyle(color: Colors.grey, fontSize: 16),
),
),
),
),
bottomNavigationBar: BottomActionMenu(
epd: widget.epd,
imgLoader: imgLoader,
),
);
}
}

class BottomActionMenu extends StatelessWidget {
final Epd epd;
final ImageLoader imgLoader;

const BottomActionMenu({
super.key,
required this.epd,
required this.imgLoader,
});

@override
Widget build(BuildContext context) {
return Container(
height: 75,
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: colorBlack.withOpacity(0.1),
spreadRadius: 0,
blurRadius: 10,
offset: const Offset(0, -5),
),
],
),
child: Center(
child: SingleChildScrollView(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
child: Row(
children: [
_buildActionButton(
context: context,
icon: Icons.add_photo_alternate_outlined,
label: 'Import New',
onTap: () {
imgLoader.pickImage(width: epd.width, height: epd.height);
},
),
_buildActionButton(
context: context,
icon: Icons.edit_outlined,
label: 'Open Editor',
onTap: () async {
final canvasBytes =
await Navigator.of(context).push<Uint8List>(
MaterialPageRoute(
builder: (context) =>
const MovableBackgroundImageExample(),
),
);
if (canvasBytes != null) {
imgLoader.updateImage(
bytes: canvasBytes,
width: epd.width,
height: epd.height,
);
}
},
),
],
),
),
),
);
}

Widget _buildActionButton({
required BuildContext context,
required IconData icon,
required String label,
required VoidCallback onTap,
}) {
return Padding(
padding: const EdgeInsets.symmetric(horizontal: 12.0),
child: InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(24),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon, color: colorAccent, size: 26),
const SizedBox(height: 4),
Text(label,
style: const TextStyle(color: colorBlack, fontSize: 12)),
],
),
),
),
);
Expand Down
51 changes: 0 additions & 51 deletions lib/view/widget/flip_controls.dart

This file was deleted.

Loading