Skip to content

Commit f98b2a3

Browse files
committed
fix: align preparation delete action with tile
1 parent 818bb10 commit f98b2a3

2 files changed

Lines changed: 175 additions & 136 deletions

File tree

lib/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/components/preparation_form_list_field.dart

Lines changed: 147 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_svg/svg.dart';
3+
import 'package:flutter_swipe_action_cell/core/cell.dart';
34
import 'package:on_time_front/core/validation/backend_constraints.dart';
45
import 'package:on_time_front/l10n/app_localizations.dart';
56
import 'package:on_time_front/presentation/onboarding/preparation_name_select/input_models/preparation_name_input_model.dart';
@@ -18,7 +19,9 @@ class PreparationFormListField extends StatefulWidget {
1819
this.onNameFocusLost,
1920
this.onPreparationTimeChanged,
2021
this.onPreparationTimeTapped,
22+
this.onRemove,
2123
this.onNameSaved,
24+
this.canRemove = true,
2225
this.isAdding = false,
2326
this.showValidationErrors = false,
2427
this.focusNode,
@@ -30,7 +33,9 @@ class PreparationFormListField extends StatefulWidget {
3033
final ValueChanged<String>? onNameFocusLost;
3134
final ValueChanged<Duration>? onPreparationTimeChanged;
3235
final VoidCallback? onPreparationTimeTapped;
36+
final VoidCallback? onRemove;
3337
final VoidCallback? onNameSaved;
38+
final bool canRemove;
3439
final bool isAdding;
3540
final bool showValidationErrors;
3641
final FocusNode? focusNode;
@@ -139,6 +144,125 @@ class _PreparationFormListFieldState extends State<PreparationFormListField> {
139144
};
140145
}
141146

147+
Widget _buildTile({
148+
required BuildContext context,
149+
required String? nameErrorText,
150+
required String? timeErrorText,
151+
}) {
152+
final textTheme = Theme.of(context).textTheme;
153+
final colorScheme = Theme.of(context).colorScheme;
154+
return Tile(
155+
key: ValueKey<String>(widget.preparationStep.id),
156+
style: TileStyle(padding: EdgeInsets.fromLTRB(21, 19, 21, 19)),
157+
leading: widget.index == null
158+
? dragIndicatorSvg
159+
: ReorderableDragStartListener(
160+
index: widget.index!,
161+
child: dragIndicatorSvg,
162+
),
163+
trailing: PreparationTimeInput(
164+
time: widget.preparationStep.preparationTime.value,
165+
hasError: timeErrorText != null,
166+
onTap: widget.onPreparationTimeTapped,
167+
onPreparationTimeChanged: widget.onPreparationTimeChanged,
168+
),
169+
child: Expanded(
170+
child: Padding(
171+
padding: const EdgeInsets.symmetric(horizontal: 18.0),
172+
child: Container(
173+
constraints: BoxConstraints(minHeight: 30),
174+
child: Center(
175+
child: TextFormField(
176+
scrollPadding: EdgeInsets.only(
177+
bottom: MediaQuery.of(context).viewInsets.bottom + 56,
178+
),
179+
initialValue: widget.preparationStep.preparationName.value,
180+
onChanged: (value) {
181+
_nameValue = value;
182+
widget.onNameChanged?.call(value);
183+
},
184+
onFieldSubmitted: (value) {
185+
_nameValue = value;
186+
widget.onNameFocusLost?.call(value);
187+
widget.onNameSaved?.call();
188+
},
189+
onTapOutside: (event) {
190+
FocusManager.instance.primaryFocus?.unfocus();
191+
},
192+
decoration: InputDecoration(
193+
isDense: true,
194+
border: nameErrorText == null
195+
? InputBorder.none
196+
: UnderlineInputBorder(
197+
borderSide: BorderSide(
198+
color: colorScheme.error,
199+
width: 1.5,
200+
),
201+
),
202+
enabledBorder: nameErrorText == null
203+
? InputBorder.none
204+
: UnderlineInputBorder(
205+
borderSide: BorderSide(
206+
color: colorScheme.error,
207+
width: 1.5,
208+
),
209+
),
210+
focusedBorder: nameErrorText == null
211+
? InputBorder.none
212+
: UnderlineInputBorder(
213+
borderSide: BorderSide(
214+
color: colorScheme.error,
215+
width: 2,
216+
),
217+
),
218+
contentPadding: EdgeInsets.all(3.0),
219+
),
220+
style: textTheme.bodyLarge,
221+
focusNode: _effectiveFocusNode,
222+
),
223+
),
224+
),
225+
),
226+
),
227+
);
228+
}
229+
230+
Widget _buildSwipeableTile({
231+
required BuildContext context,
232+
required String? nameErrorText,
233+
required String? timeErrorText,
234+
}) {
235+
final tile = _buildTile(
236+
context: context,
237+
nameErrorText: nameErrorText,
238+
timeErrorText: timeErrorText,
239+
);
240+
if (widget.onRemove == null) {
241+
return tile;
242+
}
243+
244+
return SwipeActionCell(
245+
key: ValueKey<String>('swipe_${widget.preparationStep.id}'),
246+
backgroundColor: Colors.transparent,
247+
trailingActions: [
248+
SwipeAction(
249+
onTap: (controller) {
250+
if (!widget.canRemove) {
251+
return;
252+
}
253+
widget.onRemove?.call();
254+
},
255+
color: Colors.transparent,
256+
content: _SwipeActionContent(
257+
icon: const Icon(Icons.delete, color: Colors.white, size: 24),
258+
color: Theme.of(context).colorScheme.error,
259+
),
260+
),
261+
],
262+
child: tile,
263+
);
264+
}
265+
142266
@override
143267
Widget build(BuildContext context) {
144268
final textTheme = Theme.of(context).textTheme;
@@ -151,80 +275,10 @@ class _PreparationFormListFieldState extends State<PreparationFormListField> {
151275
child: Column(
152276
crossAxisAlignment: CrossAxisAlignment.stretch,
153277
children: [
154-
Tile(
155-
key: ValueKey<String>(widget.preparationStep.id),
156-
style: TileStyle(padding: EdgeInsets.fromLTRB(21, 19, 21, 19)),
157-
leading: widget.index == null
158-
? dragIndicatorSvg
159-
: ReorderableDragStartListener(
160-
index: widget.index!,
161-
child: dragIndicatorSvg,
162-
),
163-
trailing: PreparationTimeInput(
164-
time: widget.preparationStep.preparationTime.value,
165-
hasError: timeErrorText != null,
166-
onTap: widget.onPreparationTimeTapped,
167-
onPreparationTimeChanged: widget.onPreparationTimeChanged,
168-
),
169-
child: Expanded(
170-
child: Padding(
171-
padding: const EdgeInsets.symmetric(horizontal: 18.0),
172-
child: Container(
173-
constraints: BoxConstraints(minHeight: 30),
174-
child: Center(
175-
child: TextFormField(
176-
scrollPadding: EdgeInsets.only(
177-
bottom: MediaQuery.of(context).viewInsets.bottom + 56,
178-
),
179-
initialValue:
180-
widget.preparationStep.preparationName.value,
181-
onChanged: (value) {
182-
_nameValue = value;
183-
widget.onNameChanged?.call(value);
184-
},
185-
onFieldSubmitted: (value) {
186-
_nameValue = value;
187-
widget.onNameFocusLost?.call(value);
188-
widget.onNameSaved?.call();
189-
},
190-
onTapOutside: (event) {
191-
FocusManager.instance.primaryFocus?.unfocus();
192-
},
193-
decoration: InputDecoration(
194-
isDense: true,
195-
border: nameErrorText == null
196-
? InputBorder.none
197-
: UnderlineInputBorder(
198-
borderSide: BorderSide(
199-
color: colorScheme.error,
200-
width: 1.5,
201-
),
202-
),
203-
enabledBorder: nameErrorText == null
204-
? InputBorder.none
205-
: UnderlineInputBorder(
206-
borderSide: BorderSide(
207-
color: colorScheme.error,
208-
width: 1.5,
209-
),
210-
),
211-
focusedBorder: nameErrorText == null
212-
? InputBorder.none
213-
: UnderlineInputBorder(
214-
borderSide: BorderSide(
215-
color: colorScheme.error,
216-
width: 2,
217-
),
218-
),
219-
contentPadding: EdgeInsets.all(3.0),
220-
),
221-
style: textTheme.bodyLarge,
222-
focusNode: _effectiveFocusNode,
223-
),
224-
),
225-
),
226-
),
227-
),
278+
_buildSwipeableTile(
279+
context: context,
280+
nameErrorText: nameErrorText,
281+
timeErrorText: timeErrorText,
228282
),
229283
if (errorTexts.isNotEmpty)
230284
Padding(
@@ -250,3 +304,22 @@ class _PreparationFormListFieldState extends State<PreparationFormListField> {
250304
);
251305
}
252306
}
307+
308+
class _SwipeActionContent extends StatelessWidget {
309+
const _SwipeActionContent({required this.icon, required this.color});
310+
311+
final Widget icon;
312+
final Color color;
313+
314+
@override
315+
Widget build(BuildContext context) {
316+
return Container(
317+
decoration: BoxDecoration(
318+
borderRadius: BorderRadius.circular(12),
319+
color: color,
320+
),
321+
padding: const EdgeInsets.all(18.0),
322+
child: icon,
323+
);
324+
}
325+
}

lib/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/components/preparation_form_reorderable_list.dart

Lines changed: 28 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,9 @@
11
import 'package:flutter/material.dart';
22
import 'package:flutter_bloc/flutter_bloc.dart';
3-
import 'package:flutter_swipe_action_cell/core/cell.dart';
43
import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/bloc/preparation_form_bloc.dart';
54
import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/components/preparation_form_list_field.dart';
65
import 'package:on_time_front/presentation/schedule_create/schedule_spare_and_preparing_time/preparation_form/cubit/preparation_step_form_cubit.dart';
76

8-
class _SwipeActionContent extends StatelessWidget {
9-
const _SwipeActionContent({required this.icon, required this.color});
10-
11-
final Widget icon;
12-
final Color color;
13-
14-
@override
15-
Widget build(BuildContext context) {
16-
return Container(
17-
decoration: BoxDecoration(
18-
borderRadius: BorderRadius.circular(12),
19-
color: color,
20-
),
21-
padding: const EdgeInsets.all(18.0),
22-
child: icon,
23-
);
24-
}
25-
}
26-
277
class PreparationFormReorderableList extends StatelessWidget {
288
const PreparationFormReorderableList({
299
super.key,
@@ -72,50 +52,36 @@ class PreparationFormReorderableList extends StatelessWidget {
7252
itemCount: preparationStepList.length,
7353
itemBuilder: (context, index) {
7454
final step = preparationStepList[index];
75-
final theme = Theme.of(context);
7655

77-
return SwipeActionCell(
78-
key: ValueKey<String>(step.id),
79-
backgroundColor: Colors.transparent,
80-
trailingActions: [
81-
SwipeAction(
82-
onTap: (controller) {
83-
if (preparationStepList.length <= 1) return;
84-
context.read<PreparationFormBloc>().add(
85-
PreparationFormPreparationStepRemoved(
86-
preparationStepId: step.id,
87-
),
88-
);
89-
},
90-
color: Colors.transparent,
91-
content: _SwipeActionContent(
92-
icon: const Icon(Icons.delete, color: Colors.white, size: 24),
93-
color: theme.colorScheme.error,
56+
return PreparationFormListField(
57+
key:
58+
stepKeyFor?.call(step.id) ??
59+
ValueKey<String>('field_${step.id}'),
60+
index: index,
61+
isAdding: step.id == addingStepId,
62+
canRemove: preparationStepList.length > 1,
63+
showValidationErrors: showValidationErrors,
64+
focusNode: nameFocusNodeFor?.call(step.id),
65+
preparationStep: step,
66+
onRemove: () {
67+
context.read<PreparationFormBloc>().add(
68+
PreparationFormPreparationStepRemoved(
69+
preparationStepId: step.id,
9470
),
95-
),
96-
],
97-
child: PreparationFormListField(
98-
key:
99-
stepKeyFor?.call(step.id) ??
100-
ValueKey<String>('field_${step.id}'),
101-
index: index,
102-
isAdding: step.id == addingStepId,
103-
showValidationErrors: showValidationErrors,
104-
focusNode: nameFocusNodeFor?.call(step.id),
105-
preparationStep: step,
106-
onNameChanged: (value) {
107-
onNameChanged(index, value);
108-
},
109-
onNameFocusLost: (value) {
110-
onNameChanged(index, value);
111-
},
112-
onPreparationTimeTapped: () {
113-
onTimeChanged(index, step.preparationTime.value);
114-
},
115-
onPreparationTimeChanged: (value) {
116-
onTimeChanged(index, value);
117-
},
118-
),
71+
);
72+
},
73+
onNameChanged: (value) {
74+
onNameChanged(index, value);
75+
},
76+
onNameFocusLost: (value) {
77+
onNameChanged(index, value);
78+
},
79+
onPreparationTimeTapped: () {
80+
onTimeChanged(index, step.preparationTime.value);
81+
},
82+
onPreparationTimeChanged: (value) {
83+
onTimeChanged(index, value);
84+
},
11985
);
12086
},
12187
onReorder: (int oldIndex, int newIndex) {

0 commit comments

Comments
 (0)