Skip to content

Commit

Permalink
feat: add clearOnSave bool
Browse files Browse the repository at this point in the history
  • Loading branch information
matthiasn committed Jun 16, 2024
1 parent b833a02 commit 642b34a
Show file tree
Hide file tree
Showing 3 changed files with 108 additions and 58 deletions.
22 changes: 21 additions & 1 deletion lib/features/tasks/ui/checkbox_item_widget.dart
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import 'package:flutter/material.dart';
import 'package:lotti/features/tasks/ui/title_text_field.dart';

// ignore: avoid_positional_boolean_parameters
typedef BoolCallback = void Function(bool);
Expand All @@ -23,6 +24,7 @@ class CheckboxItemWidget extends StatefulWidget {

class _CheckboxItemWidgetState extends State<CheckboxItemWidget> {
late bool _isChecked;
bool _isEditing = false;

@override
void initState() {
Expand All @@ -36,8 +38,26 @@ class _CheckboxItemWidgetState extends State<CheckboxItemWidget> {
title: GestureDetector(
onTap: () {
debugPrint('Tapped');
setState(() {
_isEditing = true;
});
},
child: Text(widget.title),
child: _isEditing
? TitleTextField(
initialValue: widget.title,
onSave: (title) {
debugPrint('Saved: $title');
setState(() {
_isEditing = false;
});
},
onClear: () {
setState(() {
_isEditing = false;
});
},
)
: Text(widget.title),
),
value: _isChecked,
controlAffinity: ListTileControlAffinity.leading,
Expand Down
17 changes: 12 additions & 5 deletions lib/features/tasks/ui/checklist_widget.dart
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,18 @@ class _ChecklistWidgetState extends State<ChecklistWidget> {
semanticsLabel: 'Checklist progress',
),
children: [
TitleTextField(
onSave: (title) {
debugPrint('Saved: $title');
},
semanticsLabel: 'Add item to checklist',
Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
child: TitleTextField(
onSave: (title) {
debugPrint('Saved: $title');
},
clearOnSave: true,
semanticsLabel: 'Add item to checklist',
),
),
...widget.itemIds.map(CheckboxItemWrapper.new),
],
Expand Down
127 changes: 75 additions & 52 deletions lib/features/tasks/ui/title_text_field.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,83 +7,106 @@ typedef StringCallback = void Function(String);
class TitleTextField extends StatefulWidget {
const TitleTextField({
required this.onSave,
this.onClear,
this.clearOnSave = false,
this.initialValue,
this.semanticsLabel,
super.key,
});

final String? initialValue;
final StringCallback onSave;
final VoidCallback? onClear;
final String? semanticsLabel;
final bool clearOnSave;

@override
State<TitleTextField> createState() => _TitleTextFieldState();
}

class _TitleTextFieldState extends State<TitleTextField> {
final _controller = TextEditingController();
bool _isEditing = false;
bool _showClearButton = false;
bool _dirty = false;

@override
void initState() {
_controller.text = widget.initialValue ?? '';
if (widget.initialValue != null) {
_showClearButton = true;
}
super.initState();
}

@override
Widget build(BuildContext context) {
void onSave(String? value) {
widget.onSave(value ?? _controller.text);
setState(() => _isEditing = false);
if (widget.clearOnSave) {
_controller.clear();
}
setState(() {
_showClearButton = false;
_dirty = false;
});
}

return Padding(
padding: const EdgeInsets.symmetric(
horizontal: 20,
vertical: 10,
),
child: TextField(
controller: _controller,
onChanged: (value) {
setState(() {
_isEditing = value.isNotEmpty;
});
},
decoration: inputDecoration(
labelText: context.messages.checklistAddItem,
semanticsLabel: widget.semanticsLabel,
themeData: Theme.of(context),
).copyWith(
floatingLabelBehavior: FloatingLabelBehavior.never,
suffixIcon: AnimatedOpacity(
curve: Curves.easeInOutQuint,
opacity: _isEditing ? 1.0 : 0.0,
duration: const Duration(milliseconds: 400),
child: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
IconButton(
icon: const Icon(
Icons.check_circle,
size: 30,
semanticLabel: 'save item',
),
onPressed: () => onSave(_controller.text),
return TextField(
controller: _controller,
onChanged: (value) {
setState(() {
_dirty = value != widget.initialValue;
_showClearButton = value != widget.initialValue;
});
},
decoration: inputDecoration(
labelText: context.messages.checklistAddItem,
semanticsLabel: widget.semanticsLabel,
themeData: Theme.of(context),
).copyWith(
floatingLabelBehavior: FloatingLabelBehavior.never,
suffixIcon: Row(
mainAxisSize: MainAxisSize.min,
mainAxisAlignment: MainAxisAlignment.end,
children: [
AnimatedOpacity(
curve: Curves.easeInOutQuint,
opacity: _dirty ? 1.0 : 0.0,
duration: const Duration(milliseconds: 400),
child: IconButton(
icon: const Icon(
Icons.check_circle,
size: 30,
semanticLabel: 'save item',
),
IconButton(
icon: const Icon(
Icons.cancel_outlined,
size: 30,
semanticLabel: 'discard changes',
),
onPressed: () {
_controller.clear();
setState(() => _isEditing = false);
},
onPressed: () => onSave(_controller.text),
),
),
AnimatedOpacity(
curve: Curves.easeInOutQuint,
opacity: _showClearButton ? 1.0 : 0.0,
duration: const Duration(milliseconds: 400),
child: IconButton(
icon: const Icon(
Icons.cancel_outlined,
size: 30,
semanticLabel: 'discard changes',
),
],
onPressed: () {
_controller.clear();
widget.onClear?.call();
setState(() => _showClearButton = false);
},
),
),
),
],
),
showCursor: true,
minLines: 1,
maxLines: 3,
textInputAction: TextInputAction.done,
onSubmitted: onSave,
),
showCursor: true,
minLines: 1,
maxLines: 3,
textInputAction: TextInputAction.done,
onSubmitted: onSave,
);
}
}

0 comments on commit 642b34a

Please sign in to comment.