Skip to content

Commit 2280e0e

Browse files
RichiB20Riccardo Baldassa
andauthored
Dev draggable (#13)
* dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * dev draggable cell - @RichiB20 * fix typo - @RichiB20 --------- Co-authored-by: Riccardo Baldassa <r.baldassa@insideapp.net>
1 parent 9e583ce commit 2280e0e

File tree

13 files changed

+194
-98
lines changed

13 files changed

+194
-98
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
## [0.9.3] - 2025-07-17
2+
3+
@RichiB20
4+
5+
* Add parent and children limitation during dragging
6+
* Add `limitStart` and `limitEnd` for activity
7+
* Add `allowParentIndependentDateMovement` for gantt
8+
* Fix typo Gant -> Gantt
9+
110
## [0.9.2] - 2025-07-08
211

312
@rickypid

README.md

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -71,20 +71,20 @@ Gantt(
7171

7272
The main chart container with these key properties:
7373

74-
| Property | Type | Description |
75-
|------------------|-------------------------|----------------------------|
76-
| `startDate` ` | DateTime` | Initial visible date |
77-
| `activities` ` | List<GantActivity>` | Activities to display |
78-
| `holidays` ` | List<GantDateHoliday>`, | Special dates to highlight |
79-
| `theme` ` | GanttTheme` | Visual customization |
80-
| `controller` ` | GanttController` | Programmatic control |
74+
| Property | Type | Description |
75+
|--------------|------------------------|----------------------------|
76+
| `startDate` | DateTime | Initial visible date |
77+
| `activities` | List<GanttActivity> | Activities to display |
78+
| `holidays` | List<GanttDateHoliday> | Special dates to highlight |
79+
| `theme` | GanttTheme | Visual customization |
80+
| `controller` | GanttController | Programmatic control |
8181

82-
#### `GantActivity`
82+
#### `GanttActivity`
8383

8484
Represents a task with:
8585

8686
```dart
87-
GantActivity(
87+
GanttActivity(
8888
start: DateTime.now(),
8989
end: DateTime.now().add(Duration(days: 5)),
9090
title: 'Task Name',
@@ -119,7 +119,7 @@ controller.setActivities(newActivities);
119119
[**Custom Builders:**]
120120

121121
```dart
122-
GantActivity(
122+
GanttActivity(
123123
cellBuilder: (date) => YourCustomWidget(date),
124124
titleWidget: YourTitleWidget(),
125125
)

example/lib/main.dart

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class MyHomePage extends StatefulWidget {
5454

5555
class _MyHomePageState extends State<MyHomePage> {
5656
late final GanttController controller;
57-
late final List<GantActivity> _activities;
57+
late final List<GanttActivity> _activities;
5858

5959
@override
6060
void initState() {
@@ -70,7 +70,7 @@ class _MyHomePageState extends State<MyHomePage> {
7070
controller.addOnActivityChangedListener(_onActivityChanged);
7171
_activities = [
7272
// ✅ Main activity with children inside range
73-
GantActivity(
73+
GanttActivity(
7474
key: 'task1',
7575
start: now.subtract(const Duration(days: 3)),
7676
end: now.add(const Duration(days: 6)),
@@ -87,77 +87,77 @@ class _MyHomePageState extends State<MyHomePage> {
8787
),
8888
),
8989
actions: [
90-
GantActivityAction(
90+
GanttActivityAction(
9191
icon: Icons.visibility,
9292
tooltip: 'View',
9393
onTap: () => debugPrint('Viewing WO-1001'),
9494
),
95-
GantActivityAction(
95+
GanttActivityAction(
9696
icon: Icons.edit,
9797
tooltip: 'Edit',
9898
onTap: () => debugPrint('Editing WO-1001'),
9999
),
100-
GantActivityAction(
100+
GanttActivityAction(
101101
icon: Icons.delete,
102102
tooltip: 'Delete',
103103
onTap: () => debugPrint('Deleting WO-1001'),
104104
),
105105
],
106106
children: [
107-
GantActivity(
107+
GanttActivity(
108108
key: 'task1.sub1',
109109
start: now.subtract(const Duration(days: 2)),
110110
end: now.add(const Duration(days: 1)),
111111
title: 'Subtask 1',
112112
tooltip: 'WO-1001-1 | Subtask',
113113
color: const Color(0xFF81C784),
114114
actions: [
115-
GantActivityAction(
115+
GanttActivityAction(
116116
icon: Icons.check,
117117
tooltip: 'Mark done',
118118
onTap: () => debugPrint('Marking subtask done'),
119119
),
120120
],
121121
),
122-
GantActivity(
122+
GanttActivity(
123123
key: 'task1.sub2',
124124
start: now,
125125
end: now.add(const Duration(days: 5)),
126126
title: 'Subtask 2',
127127
tooltip: 'WO-1001-2 | Subtask with nested children',
128128
color: const Color(0xFF9575CD),
129129
actions: [
130-
GantActivityAction(
130+
GanttActivityAction(
131131
icon: Icons.add,
132132
tooltip: 'Add nested task',
133133
onTap: () => debugPrint('Add nested to WO-1001-2'),
134134
),
135135
],
136136
children: [
137-
GantActivity(
137+
GanttActivity(
138138
key: 'task1.sub2.subA',
139139
start: now.add(const Duration(days: 1)),
140140
end: now.add(const Duration(days: 3)),
141141
title: 'Nested Subtask A',
142142
tooltip: 'WO-1001-2A | Second-level task',
143143
color: const Color(0xFFBA68C8),
144144
actions: [
145-
GantActivityAction(
145+
GanttActivityAction(
146146
icon: Icons.edit,
147147
tooltip: 'Edit',
148148
onTap: () => debugPrint('Editing nested A'),
149149
),
150150
],
151151
),
152-
GantActivity(
152+
GanttActivity(
153153
key: 'task1.sub2.subB',
154154
start: now.add(const Duration(days: 2)),
155155
end: now.add(const Duration(days: 4)),
156156
title: 'Nested Subtask B',
157157
tooltip: 'WO-1001-2B | Continued',
158158
color: const Color(0xFFFF8A65),
159159
actions: [
160-
GantActivityAction(
160+
GanttActivityAction(
161161
icon: Icons.delete,
162162
tooltip: 'Delete',
163163
onTap: () => debugPrint('Deleting nested B'),
@@ -170,7 +170,7 @@ class _MyHomePageState extends State<MyHomePage> {
170170
),
171171

172172
// ✅ Standalone task near today
173-
GantActivity(
173+
GanttActivity(
174174
key: 'task2',
175175
start: now.add(const Duration(days: 1)),
176176
end: now.add(const Duration(days: 8)),
@@ -180,7 +180,7 @@ class _MyHomePageState extends State<MyHomePage> {
180180
),
181181

182182
// ✅ Activity from one month ago
183-
GantActivity(
183+
GanttActivity(
184184
key: 'task3',
185185
start: monthAgo.subtract(const Duration(days: 3)),
186186
end: monthAgo.add(const Duration(days: 3)),
@@ -190,7 +190,7 @@ class _MyHomePageState extends State<MyHomePage> {
190190
),
191191

192192
// ✅ Activity a few days ago
193-
GantActivity(
193+
GanttActivity(
194194
key: 'task4',
195195
start: now.subtract(const Duration(days: 10)),
196196
end: now.subtract(const Duration(days: 4)),
@@ -200,7 +200,7 @@ class _MyHomePageState extends State<MyHomePage> {
200200
),
201201

202202
// ✅ Future activity
203-
GantActivity(
203+
GanttActivity(
204204
key: 'task5',
205205
start: monthLater.subtract(const Duration(days: 5)),
206206
end: monthLater.add(const Duration(days: 2)),
@@ -210,7 +210,7 @@ class _MyHomePageState extends State<MyHomePage> {
210210
),
211211

212212
// ✅ Long-term task
213-
GantActivity(
213+
GanttActivity(
214214
key: 'task6',
215215
start: now.subtract(const Duration(days: 10)),
216216
end: monthLater,

lib/src/classes/activity.dart

Lines changed: 73 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import 'package:flutter/material.dart';
33
import '../utils/datetime.dart';
44

55
/// An action that can be performed on a Gantt activity.
6-
class GantActivityAction {
6+
class GanttActivityAction {
77
/// The icon representing the action.
88
final IconData icon;
99

@@ -14,7 +14,7 @@ class GantActivityAction {
1414
final String? tooltip;
1515

1616
/// Creates an activity action with an icon, tap handler, and optional tooltip.
17-
const GantActivityAction({
17+
const GanttActivityAction({
1818
required this.icon,
1919
required this.onTap,
2020
this.tooltip,
@@ -25,7 +25,7 @@ class GantActivityAction {
2525
///
2626
/// Each activity has a start/end date, title, and optional styling properties.
2727
/// Activities can be hierarchical with parent-child relationships.
28-
class GantActivity<T> {
28+
class GanttActivity<T> {
2929
/// Unique identifier for the activity.
3030
late String key;
3131

@@ -54,16 +54,16 @@ class GantActivity<T> {
5454
final Widget? iconTitle;
5555

5656
/// The segments that make up this activity.
57-
final List<GantActivitySegment>? segments;
57+
final List<GanttActivitySegment>? segments;
5858

5959
/// Child activities that are hierarchically under this one.
60-
final List<GantActivity>? children;
60+
final List<GanttActivity>? children;
6161

6262
/// Actions that can be performed on this activity.
63-
final List<GantActivityAction>? actions;
63+
final List<GanttActivityAction>? actions;
6464

6565
/// Callback when the activity cell is tapped.
66-
final Function(GantActivity activity)? onCellTap;
66+
final Function(GanttActivity activity)? onCellTap;
6767

6868
/// Builder function for custom single cell rendering.
6969
final Widget Function(DateTime cellDate)? cellBuilder;
@@ -75,24 +75,30 @@ class GantActivity<T> {
7575
final bool showCell;
7676

7777
/// Builder function for custom cell rendering.
78-
final Widget Function(GantActivity activity)? builder;
78+
final Widget Function(GanttActivity activity)? builder;
7979

8080
/// Optional custom data associated with the activity.
8181
final T? data;
8282

83-
GantActivity? _parent;
83+
GanttActivity? _parent;
8484

8585
/// The parent activity, if this is a child activity.
86-
GantActivity? get parent => _parent;
86+
GanttActivity? get parent => _parent;
8787

88-
/// Creates a [GantActivity] with the specified properties.
88+
/// The limit of the start date of the activity.
89+
late DateTime? limitStart;
90+
91+
/// The limit of the end date of the activity.
92+
late DateTime? limitEnd;
93+
94+
/// Creates a [GanttActivity] with the specified properties.
8995
///
9096
/// Throws an [AssertionError] if:
9197
/// - Start date is after end date
9298
/// - Only one between [title] and [titleWidget] must be provided
9399
/// - Any segment dates fall outside the activity dates
94100
/// - Any child activity dates fall outside this activity's dates
95-
GantActivity({
101+
GanttActivity({
96102
required this.key,
97103
required DateTime start,
98104
required DateTime end,
@@ -111,6 +117,8 @@ class GantActivity<T> {
111117
this.showCell = true,
112118
this.builder,
113119
this.data,
120+
this.limitStart,
121+
this.limitEnd,
114122
}) : assert(
115123
start.toDate.isBeforeOrSame(end.toDate) &&
116124
((title == null) != (titleWidget == null)) &&
@@ -145,22 +153,65 @@ class GantActivity<T> {
145153
String toString() => title ?? super.toString();
146154

147155
/// Gets a flat list of this activity and all its descendants.
148-
List<GantActivity> get plainList => [this, ...children?.plainList ?? []];
156+
List<GanttActivity> get plainList => [this, ...children?.plainList ?? []];
157+
158+
bool validStartMoveToParent(int days) =>
159+
parent == null ||
160+
!parent!.showCell ||
161+
start.addDays(days).isAfterOrSame(parent!.start);
162+
163+
bool validStartMoveToChildren(int days) =>
164+
(children?.isEmpty ?? true) == true ||
165+
start
166+
.addDays(days)
167+
.isBeforeOrSame(
168+
DateTimeEx.firstDateFromList(
169+
children!.map((e) => e.start).toList(),
170+
),
171+
);
172+
173+
bool validEndMoveToParent(int days) =>
174+
parent == null ||
175+
!parent!.showCell ||
176+
end.addDays(days).isBeforeOrSame(parent!.end);
177+
178+
bool validEndMoveToChildren(int days) =>
179+
(children?.isEmpty ?? true) == true ||
180+
end
181+
.addDays(days)
182+
.isAfterOrSame(
183+
DateTimeEx.lastDateFromList(children!.map((e) => e.end).toList()),
184+
);
185+
186+
bool validMoveToParent(int days) =>
187+
validStartMoveToParent(days) && validEndMoveToParent(days);
188+
189+
bool validStartMove(int days) =>
190+
validStartMoveToParent(days) &&
191+
validStartMoveToChildren(days) &&
192+
(limitStart == null || start.addDays(days).isAfterOrSame(limitStart!));
193+
194+
bool validEndMove(int days) =>
195+
validEndMoveToParent(days) &&
196+
validEndMoveToChildren(days) &&
197+
(limitEnd == null || end.addDays(days).isBeforeOrSame(limitEnd!));
198+
199+
bool validMove(int days) => validStartMove(days) && validEndMove(days);
149200
}
150201

151-
/// Extension methods for working with lists of [GantActivity].
152-
extension GantActivityListExtension on List<GantActivity> {
202+
/// Extension methods for working with lists of [GanttActivity].
203+
extension GanttActivityListExtension on List<GanttActivity> {
153204
/// Gets a flat list of all activities and their descendants.
154-
List<GantActivity> get plainList {
155-
final as = <GantActivity>[];
205+
List<GanttActivity> get plainList {
206+
final as = <GanttActivity>[];
156207
for (var a in this) {
157208
as.addAll(a.plainList);
158209
}
159210
return as;
160211
}
161212

162213
/// Finds an activity by its key in the flattened list.
163-
GantActivity? getFromKey(String key) {
214+
GanttActivity? getFromKey(String key) {
164215
final i = plainList.indexWhere((e) => e.key == key);
165216
return i < 0 ? null : plainList[i];
166217
}
@@ -169,7 +220,7 @@ extension GantActivityListExtension on List<GantActivity> {
169220
/// A segment of a Gantt activity.
170221
///
171222
/// Activities can be divided into segments to show progress or phases.
172-
class GantActivitySegment {
223+
class GanttActivitySegment {
173224
/// The start date of the segment.
174225
late DateTime start;
175226

@@ -183,15 +234,15 @@ class GantActivitySegment {
183234
final String description;
184235

185236
/// Callback when the segment is tapped.
186-
final Function(GantActivity activity)? onTap;
237+
final Function(GanttActivity activity)? onTap;
187238

188239
/// The color of the segment.
189240
final Color? color;
190241

191-
/// Creates a [GantActivitySegment].
242+
/// Creates a [GanttActivitySegment].
192243
///
193244
/// Throws an [AssertionError] if start date is after end date.
194-
GantActivitySegment({
245+
GanttActivitySegment({
195246
required DateTime start,
196247
required DateTime end,
197248
required this.title,

0 commit comments

Comments
 (0)