Skip to content

Commit d2c2418

Browse files
authored
Merge pull request #64 from CodandoTV/feature/expandable-fab-menu
Expandable Menu for Large Screens
2 parents bbe2a5a + 56fb288 commit d2c2418

17 files changed

Lines changed: 255 additions & 163 deletions
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import 'package:flutter/material.dart';
2+
3+
class CardWrapperWidget extends StatelessWidget {
4+
final Color backgroundColor;
5+
final Function()? onTap;
6+
final Widget child;
7+
8+
const CardWrapperWidget(
9+
{super.key,
10+
required this.child,
11+
required this.onTap,
12+
required this.backgroundColor});
13+
14+
@override
15+
Widget build(BuildContext context) {
16+
return Card(
17+
elevation: 2,
18+
shape: const RoundedRectangleBorder(
19+
borderRadius: BorderRadius.all(Radius.circular(12)),
20+
),
21+
color: backgroundColor,
22+
clipBehavior: Clip.hardEdge,
23+
child: InkWell(
24+
onTap: onTap,
25+
child: child,
26+
),
27+
);
28+
}
29+
}

lib/ui/components/widgets/checklist_form_widget.dart renamed to lib/ui/components/widgets/checklist/checklist_form_widget.dart

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:todoapp/ui/components/form_validator.dart';
3+
import 'package:todoapp/ui/components/widgets/form/form_field_widget.dart';
34

45
class ChecklistFormWidget extends StatelessWidget {
56
final Key formKey;
@@ -24,21 +25,15 @@ class ChecklistFormWidget extends StatelessWidget {
2425
autovalidateMode: AutovalidateMode.onUserInteraction,
2526
child: Column(
2627
children: [
27-
TextFormField(
28-
autofocus: true,
29-
controller: checklistEditingController,
30-
decoration: InputDecoration(
28+
FormFieldWidget(
3129
labelText: checklistLabel,
32-
labelStyle: Theme.of(context).textTheme.titleMedium,
33-
border: const OutlineInputBorder(),
34-
),
35-
validator: (value) {
36-
if (formScreenValidator.validateValue(value) == false) {
37-
return checklistErrorMessage;
38-
}
39-
return null;
40-
},
41-
),
30+
controller: checklistEditingController,
31+
validator: (value) {
32+
if (formScreenValidator.validateValue(value) == false) {
33+
return checklistErrorMessage;
34+
}
35+
return null;
36+
}),
4237
],
4338
),
4439
);

lib/ui/components/widgets/checklist/checklist_full_widget.dart

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,17 @@ class ChecklistsListFullWidgetState extends State<ChecklistsListFullWidget> {
7575
}
7676
}
7777

78+
void onSortTasks() {
79+
_tasksViewModel.onSort();
80+
}
81+
82+
Future<void> onShareTasks() async {
83+
final checklistName = selected?.title;
84+
if (checklistName != null) {
85+
await _tasksViewModel.shareTasks(checklistName: checklistName);
86+
}
87+
}
88+
7889
Widget _buildTaskList(BuildContext context) {
7990
final localizations = AppLocalizations.of(context)!;
8091
return Row(

lib/ui/components/widgets/checklist/checklist_item_widget.dart

Lines changed: 5 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22

33
import 'package:todoapp/data/model/checklist.dart';
4+
import 'package:todoapp/ui/components/widgets/card_wrapper_widget.dart';
45

56
class ChecklistItemWidget extends StatelessWidget {
67
final Checklist checklist;
@@ -43,27 +44,10 @@ class ChecklistItemWidget extends StatelessWidget {
4344
backgroundColor = Theme.of(context).colorScheme.tertiaryContainer;
4445
}
4546

46-
return Card(
47-
elevation: 2,
48-
shape: const RoundedRectangleBorder(
49-
borderRadius: BorderRadius.all(
50-
Radius.circular(12),
51-
),
52-
),
53-
color: backgroundColor,
54-
clipBehavior: Clip.hardEdge,
55-
child: InkWell(
56-
onTap: () => onSelectChecklist(checklist),
57-
child: Padding(
58-
padding: const EdgeInsets.only(
59-
left: 8,
60-
right: 8,
61-
top: 16,
62-
bottom: 16,
63-
),
64-
child: _internalContent(context, checklist),
65-
),
66-
),
47+
return CardWrapperWidget(
48+
onTap: () => onSelectChecklist(checklist),
49+
backgroundColor: backgroundColor,
50+
child: _internalContent(context, checklist),
6751
);
6852
}
6953
}

lib/ui/components/widgets/checklist/checklist_navigation_rail_menu.dart

Lines changed: 0 additions & 44 deletions
This file was deleted.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
import 'package:flutter/material.dart';
2+
import 'package:flutter_expandable_fab/flutter_expandable_fab.dart';
3+
import 'package:todoapp/ui/components/widgets/checklist/fabmenu/checklist_fabmenu_item.dart';
4+
import 'package:todoapp/ui/l10n/app_localizations.dart';
5+
6+
class ChecklistExpandableFabMenu extends StatefulWidget {
7+
final Function() onNewChecklistPressed;
8+
final Function() onNewTaskPressed;
9+
final Function() onSharePressed;
10+
final Function() onSortPressed;
11+
12+
const ChecklistExpandableFabMenu({
13+
super.key,
14+
required this.onNewChecklistPressed,
15+
required this.onNewTaskPressed,
16+
required this.onSharePressed,
17+
required this.onSortPressed,
18+
});
19+
20+
@override
21+
State<ChecklistExpandableFabMenu> createState() =>
22+
_ChecklistExpandableFabMenuState();
23+
}
24+
25+
class _ChecklistExpandableFabMenuState
26+
extends State<ChecklistExpandableFabMenu> {
27+
final _key = GlobalKey<ExpandableFabState>();
28+
29+
@override
30+
Widget build(BuildContext context) {
31+
final localizations = AppLocalizations.of(context)!;
32+
33+
return ExpandableFab(
34+
key: _key,
35+
type: ExpandableFabType.side,
36+
overlayStyle: const ExpandableFabOverlayStyle(
37+
blur: 1.0,
38+
),
39+
children: [
40+
ChecklistFabMenuItem(
41+
heroTag: 'new_task',
42+
onPressed: () => {
43+
_key.currentState?.close(),
44+
widget.onNewTaskPressed(),
45+
},
46+
label: localizations.task,
47+
icon: Icons.add_task,
48+
),
49+
ChecklistFabMenuItem(
50+
heroTag: 'new_checklist',
51+
onPressed: () => {
52+
_key.currentState?.close(),
53+
widget.onNewChecklistPressed(),
54+
},
55+
label: localizations.checklist,
56+
icon: Icons.plus_one,
57+
),
58+
ChecklistFabMenuItem(
59+
heroTag: 'share',
60+
onPressed: () => {
61+
_key.currentState?.close(),
62+
widget.onSharePressed(),
63+
},
64+
label: localizations.share,
65+
icon: Icons.share,
66+
),
67+
ChecklistFabMenuItem(
68+
heroTag: 'sort',
69+
onPressed: () => {
70+
_key.currentState?.close(),
71+
widget.onSortPressed(),
72+
},
73+
label: localizations.sort,
74+
icon: Icons.sort,
75+
)
76+
],
77+
);
78+
}
79+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import 'package:flutter/material.dart';
2+
3+
class ChecklistFabMenuItem extends StatelessWidget {
4+
final String heroTag;
5+
final String label;
6+
final IconData icon;
7+
final Function() onPressed;
8+
9+
const ChecklistFabMenuItem(
10+
{super.key,
11+
required this.label,
12+
required this.onPressed,
13+
required this.icon,
14+
required this.heroTag});
15+
16+
@override
17+
Widget build(BuildContext context) {
18+
return Column(
19+
children: [
20+
FloatingActionButton.small(
21+
heroTag: heroTag,
22+
onPressed: onPressed,
23+
child: Icon(icon),
24+
),
25+
const SizedBox(height: 8),
26+
Text(label),
27+
],
28+
);
29+
}
30+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import 'package:flutter/material.dart';
2+
3+
class FormFieldWidget extends StatelessWidget {
4+
final String labelText;
5+
final TextEditingController controller;
6+
final String? Function(String?)? validator;
7+
8+
const FormFieldWidget({
9+
super.key,
10+
required this.labelText,
11+
required this.controller,
12+
this.validator,
13+
});
14+
15+
@override
16+
Widget build(BuildContext context) {
17+
return TextFormField(
18+
autofocus: true,
19+
maxLines: 3,
20+
controller: controller,
21+
decoration: InputDecoration(
22+
labelText: labelText,
23+
labelStyle: Theme.of(context).textTheme.titleMedium,
24+
border: const OutlineInputBorder(),
25+
),
26+
validator: validator,
27+
);
28+
}
29+
}

lib/ui/components/widgets/task/task_cell_widget.dart

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import 'package:flutter/material.dart';
22
import 'package:todoapp/data/model/task.dart';
3+
import 'package:todoapp/ui/components/widgets/card_wrapper_widget.dart';
34
import 'package:todoapp/ui/components/widgets/task/task_title_widget.dart';
45

56
class TaskCellWidget extends StatelessWidget {
@@ -20,13 +21,9 @@ class TaskCellWidget extends StatelessWidget {
2021

2122
@override
2223
Widget build(BuildContext context) {
23-
const cardShape = RoundedRectangleBorder(
24-
borderRadius: BorderRadius.all(Radius.circular(8)),
25-
);
26-
27-
return Card(
28-
elevation: 4,
29-
shape: cardShape,
24+
return CardWrapperWidget(
25+
onTap: null,
26+
backgroundColor: Theme.of(context).colorScheme.surfaceBright,
3027
child: ListTile(
3128
leading: IconButton(
3229
onPressed: () => {onRemoveTask(task)},

lib/ui/components/widgets/task_form_widget.dart renamed to lib/ui/components/widgets/task/task_form_widget.dart

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import 'package:flutter/material.dart';
22

33
import 'package:todoapp/ui/components/form_validator.dart';
4+
import 'package:todoapp/ui/components/widgets/form/form_field_widget.dart';
45

56
class TaskFormWidget extends StatelessWidget {
67
final Key formKey;
@@ -25,14 +26,9 @@ class TaskFormWidget extends StatelessWidget {
2526
autovalidateMode: AutovalidateMode.onUserInteraction,
2627
child: Column(
2728
children: [
28-
TextFormField(
29-
autofocus: true,
29+
FormFieldWidget(
30+
labelText: taskLabel,
3031
controller: taskEditingController,
31-
decoration: InputDecoration(
32-
labelText: taskLabel,
33-
labelStyle: Theme.of(context).textTheme.titleMedium,
34-
border: const OutlineInputBorder(),
35-
),
3632
validator: (value) {
3733
if (formScreenValidator.validateValue(value) == false) {
3834
return taskErrorMessage;

0 commit comments

Comments
 (0)