Implemented note templates and enhanced usability by adjusting the dimension of the note dialogs

This commit is contained in:
Dennis Nemec
2026-01-08 14:10:21 +01:00
parent 6a53d2d716
commit ffdd7fa0ff
6 changed files with 98 additions and 56 deletions

View File

@ -25,15 +25,17 @@ class NoteBloc extends Bloc<NoteEvent, NoteState> {
required this.opBloc, required this.opBloc,
required this.deliveryId, required this.deliveryId,
}) : super(NoteInitial()) { }) : super(NoteInitial()) {
_combinedSubscription = CombineLatestStream.combine2( _combinedSubscription = CombineLatestStream.combine3(
repository.notes, repository.notes,
repository.images, repository.images,
(note, image) => {"note": note, "image": image}, repository.templates,
(note, image, templates) => {"note": note, "image": image, "templates": templates},
).listen( ).listen(
(data) => add( (data) => add(
DataUpdated( DataUpdated(
images: data["image"] as List<ImageNote>, images: data["image"] as List<ImageNote>,
notes: data["note"] as List<Note>, notes: data["note"] as List<Note>,
templates: data["templates"] as List<NoteTemplate>
), ),
), ),
); );
@ -56,7 +58,7 @@ class NoteBloc extends Bloc<NoteEvent, NoteState> {
} }
Future<void> _dataUpdated(DataUpdated event, Emitter<NoteState> emit) async { Future<void> _dataUpdated(DataUpdated event, Emitter<NoteState> emit) async {
emit(NoteLoaded(notes: event.notes, images: event.images)); emit(NoteLoaded(notes: event.notes, images: event.images, templates: event.templates));
} }
Future<void> _reset(ResetNotes event, Emitter<NoteState> emit) async { Future<void> _reset(ResetNotes event, Emitter<NoteState> emit) async {

View File

@ -70,6 +70,7 @@ class ImageUpdated extends NoteEvent {
class DataUpdated extends NoteEvent { class DataUpdated extends NoteEvent {
final List<ImageNote> images; final List<ImageNote> images;
final List<Note> notes; final List<Note> notes;
final List<NoteTemplate> templates;
DataUpdated({required this.images, required this.notes}); DataUpdated({required this.images, required this.notes, required this.templates});
} }

View File

@ -41,14 +41,6 @@ class _NoteAddDialogState extends State<NoteAddDialog> {
void _onSave() { void _onSave() {
String content = _noteController.text; String content = _noteController.text;
if (_noteSelectionController.text.isNotEmpty) {
NoteTemplate template = widget.templates.firstWhere(
(note) => note.title == _noteSelectionController.text,
);
content = template.content;
}
context.read<NoteBloc>().add( context.read<NoteBloc>().add(
AddNote(note: content, deliveryId: widget.delivery), AddNote(note: content, deliveryId: widget.delivery),
); );
@ -60,12 +52,12 @@ class _NoteAddDialogState extends State<NoteAddDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Dialog( return Dialog(
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.75, width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.45, height: MediaQuery.of(context).size.height * 0.6,
child: Padding( child: Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: ListView(
mainAxisAlignment: MainAxisAlignment.spaceAround, //mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [ children: [
Row( Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween, mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -88,11 +80,10 @@ class _NoteAddDialogState extends State<NoteAddDialog> {
controller: _noteSelectionController, controller: _noteSelectionController,
onSelected: (int? value) { onSelected: (int? value) {
setState(() { setState(() {
_noteSelectionController.text = _noteController.text =
widget.templates[value!].title; widget.templates[value!].content;
}); });
}, },
enabled: _isCustomNotesEmpty,
width: double.infinity, width: double.infinity,
label: const Text("Notiz auswählen"), label: const Text("Notiz auswählen"),
dropdownMenuEntries: dropdownMenuEntries:
@ -106,20 +97,22 @@ class _NoteAddDialogState extends State<NoteAddDialog> {
), ),
const Padding( const Padding(
padding: EdgeInsets.only(top: 0.0, bottom: 0.0), padding: EdgeInsets.only(top: 0.0, bottom: 0.0),
child: Text("oder"), child: Center(child: Text("oder")),
), ),
Padding( Padding(
padding: const EdgeInsets.only(top: 10.0, bottom: 20.0), padding: const EdgeInsets.only(top: 10.0, bottom: 20.0),
child: TextFormField( child: TextFormField(
onTapOutside: (_) { _noteFieldFocusNode.unfocus(); },
controller: _noteController, controller: _noteController,
enabled: _noteSelectionController.text.isEmpty, textInputAction: TextInputAction.done,
onFieldSubmitted: (_) {_noteFieldFocusNode.unfocus();},
focusNode: _noteFieldFocusNode, focusNode: _noteFieldFocusNode,
decoration: const InputDecoration( decoration: const InputDecoration(
labelText: "Eigene Notiz", labelText: "Eigene Notiz",
border: OutlineInputBorder(), border: OutlineInputBorder(),
), ),
minLines: 3, minLines: 8,
maxLines: 5, maxLines: 10,
), ),
), ),
Row( Row(
@ -136,7 +129,10 @@ class _NoteAddDialogState extends State<NoteAddDialog> {
Padding( Padding(
padding: const EdgeInsets.only(left: 10.0), padding: const EdgeInsets.only(left: 10.0),
child: OutlinedButton( child: OutlinedButton(
onPressed: null, onPressed: () {
_noteController.clear();
_noteSelectionController.clear();
},
child: const Text("Zurücksetzen"), child: const Text("Zurücksetzen"),
), ),
), ),

View File

@ -17,11 +17,14 @@ class NoteEditDialog extends StatefulWidget {
class _NoteEditDialogState extends State<NoteEditDialog> { class _NoteEditDialogState extends State<NoteEditDialog> {
final _formKey = GlobalKey<FormState>(); final _formKey = GlobalKey<FormState>();
late TextEditingController _editController; late TextEditingController _editController;
late FocusNode _noteFieldFocusNode;
@override @override
void initState() { void initState() {
super.initState(); super.initState();
_noteFieldFocusNode = FocusNode();
_editController = TextEditingController(text: widget.note.content); _editController = TextEditingController(text: widget.note.content);
} }
@ -40,11 +43,11 @@ class _NoteEditDialogState extends State<NoteEditDialog> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Dialog( return Dialog(
child: SizedBox( child: SizedBox(
width: MediaQuery.of(context).size.width * 0.75, width: MediaQuery.of(context).size.width,
height: MediaQuery.of(context).size.height * 0.32, height: MediaQuery.of(context).size.height * 0.5,
child: Padding( child: Padding(
padding: const EdgeInsets.all(20), padding: const EdgeInsets.all(20),
child: Column( child: ListView(
children: [ children: [
Text( Text(
"Notiz bearbeiten", "Notiz bearbeiten",
@ -59,13 +62,18 @@ class _NoteEditDialogState extends State<NoteEditDialog> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
TextFormField( TextFormField(
onTapOutside: (event) { focusNode: _noteFieldFocusNode,
FocusScope.of(context).unfocus(); onTapOutside: (_) {
_noteFieldFocusNode.unfocus();
},
textInputAction: TextInputAction.done,
onFieldSubmitted: (_) {
_noteFieldFocusNode.unfocus();
}, },
decoration: InputDecoration(label: const Text("Notiz")), decoration: InputDecoration(label: const Text("Notiz")),
controller: _editController, controller: _editController,
minLines: 4, minLines: 10,
maxLines: 8, maxLines: 12,
), ),
Padding( Padding(

View File

@ -9,6 +9,8 @@ import 'package:hl_lieferservice/feature/delivery/overview/bloc/tour_state.dart'
import '../../../../../model/delivery.dart'; import '../../../../../model/delivery.dart';
enum NoteItemAction { noteEdit, noteDelete }
class NoteListItem extends StatelessWidget { class NoteListItem extends StatelessWidget {
final NoteInformation note; final NoteInformation note;
final String deliveryId; final String deliveryId;
@ -31,13 +33,25 @@ class NoteListItem extends StatelessWidget {
.tour .tour
.discountArticleNumber; .discountArticleNumber;
if (note.article != null && note.article?.articleNumber == discountArticleId) { if (note.article != null &&
note.article?.articleNumber == discountArticleId) {
return const Text("Begründung der Gutschrift"); return const Text("Begründung der Gutschrift");
} }
return note.article != null ? Text(note.article!.name) : null; return note.article != null ? Text(note.article!.name) : null;
} }
void _onEdit(BuildContext context) {
showDialog(
context: context,
builder:
(_) => BlocProvider.value(
value: context.read<NoteBloc>(),
child: NoteEditDialog(note: note.note),
),
);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Padding( return Padding(
@ -47,16 +61,21 @@ class NoteListItem extends StatelessWidget {
subtitle: _subtitle(context), subtitle: _subtitle(context),
tileColor: Theme.of(context).colorScheme.surfaceContainerLowest, tileColor: Theme.of(context).colorScheme.surfaceContainerLowest,
leading: CircleAvatar(child: Text("${index + 1}")), leading: CircleAvatar(child: Text("${index + 1}")),
trailing: PopupMenuButton( trailing: PopupMenuButton<NoteItemAction>(
itemBuilder: (context) { onSelected: (NoteItemAction action) {
switch (action) {
case NoteItemAction.noteDelete:
_onDelete(context);
break;
case NoteItemAction.noteEdit:
_onEdit(context);
break;
}
},
itemBuilder: (_) {
return [ return [
PopupMenuItem( PopupMenuItem<NoteItemAction>(
onTap: () { value: NoteItemAction.noteEdit,
showDialog(
context: context,
builder: (context) => NoteEditDialog(note: note.note),
);
},
child: Row( child: Row(
children: [ children: [
Icon(Icons.edit, color: Colors.blueAccent), Icon(Icons.edit, color: Colors.blueAccent),
@ -67,10 +86,8 @@ class NoteListItem extends StatelessWidget {
], ],
), ),
), ),
PopupMenuItem( PopupMenuItem<NoteItemAction>(
onTap: () { value: NoteItemAction.noteDelete,
_onDelete(context);
},
child: Row( child: Row(
children: [ children: [
Icon(Icons.delete, color: Colors.redAccent), Icon(Icons.delete, color: Colors.redAccent),

View File

@ -13,6 +13,11 @@ import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart';
import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart'; import 'package:hl_lieferservice/widget/operations/bloc/operation_event.dart';
import 'package:image_picker/image_picker.dart'; import 'package:image_picker/image_picker.dart';
enum NoteAction {
addNote,
addImage
}
class NoteOverview extends StatefulWidget { class NoteOverview extends StatefulWidget {
final List<NoteInformation> notes; final List<NoteInformation> notes;
final List<NoteTemplate> templates; final List<NoteTemplate> templates;
@ -35,12 +40,15 @@ class _NoteOverviewState extends State<NoteOverview> {
final _imagePicker = ImagePicker(); final _imagePicker = ImagePicker();
Widget _notes() { Widget _notes() {
for (final note in widget.notes) {
debugPrint("Note: ${note.note.content}");
debugPrint("NOTE Article: ${note.article?.name.toString()}");
}
return NoteList(notes: widget.notes, deliveryId: widget.deliveryId); return NoteList(notes: widget.notes, deliveryId: widget.deliveryId);
} }
Widget _images() { Widget _images() {
debugPrint("IMAGES: ${widget.images}");
return NoteImageOverview( return NoteImageOverview(
images: widget.images, images: widget.images,
deliveryId: widget.deliveryId, deliveryId: widget.deliveryId,
@ -50,11 +58,11 @@ class _NoteOverviewState extends State<NoteOverview> {
void _onAddNote(BuildContext context) { void _onAddNote(BuildContext context) {
showDialog( showDialog(
context: context, context: context,
builder: (context) { builder: (_) {
return NoteAddDialog( return BlocProvider.value(value: context.read<NoteBloc>(), child: NoteAddDialog(
delivery: widget.deliveryId, delivery: widget.deliveryId,
templates: widget.templates, templates: widget.templates,
); ));
}, },
); );
} }
@ -107,10 +115,21 @@ class _NoteOverviewState extends State<NoteOverview> {
alignment: Alignment.bottomRight, alignment: Alignment.bottomRight,
child: Padding( child: Padding(
padding: const EdgeInsets.only(right: 25), padding: const EdgeInsets.only(right: 25),
child: PopupMenuButton( child: PopupMenuButton<NoteAction>(
itemBuilder: (context) { onSelected: (NoteAction action) {
switch (action) {
case NoteAction.addNote:
_onAddNote(context);
break;
case NoteAction.addImage:
_onAddImage(context);
break;
}
},
itemBuilder: (_) {
return [ return [
PopupMenuItem( PopupMenuItem<NoteAction>(
value: NoteAction.addNote,
child: Row( child: Row(
children: [ children: [
Icon( Icon(
@ -123,9 +142,9 @@ class _NoteOverviewState extends State<NoteOverview> {
), ),
], ],
), ),
onTap: () => _onAddNote(context),
), ),
PopupMenuItem( PopupMenuItem<NoteAction>(
value: NoteAction.addImage,
child: Row( child: Row(
children: [ children: [
Icon( Icon(
@ -138,7 +157,6 @@ class _NoteOverviewState extends State<NoteOverview> {
), ),
], ],
), ),
onTap: () => _onAddImage(context),
), ),
]; ];
}, },