Skip to content

Commit ecbb0a4

Browse files
✅ added alt offset feature (2/2)
1 parent 218ec68 commit ecbb0a4

File tree

3 files changed

+122
-45
lines changed

3 files changed

+122
-45
lines changed

example/lib/screen/plain_timeline_demo.dart

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import 'dart:math';
22

33
import 'package:flutter/material.dart';
44
import 'package:flutter_timeline/flutter_timeline.dart';
5+
import 'package:flutter_timeline/indicator_position.dart';
56
import 'package:flutter_timeline/timeline_theme.dart';
67
import 'package:flutter_timeline/timeline_theme_data.dart';
78

@@ -80,7 +81,7 @@ class _PlainTimelineDemoScreenState extends State<PlainTimelineDemoScreen> {
8081
TimelineEventDisplay get plainEventDisplay {
8182
return TimelineEventDisplay(
8283
child: TimelineEventCard(
83-
title: Text("just now"),
84+
title: Text("just \n\n\n\n now"),
8485
content: Text("someone commented on your timeline ${DateTime.now()}"),
8586
),
8687
indicator: randomIndicator);
@@ -92,6 +93,8 @@ class _PlainTimelineDemoScreenState extends State<PlainTimelineDemoScreen> {
9293
return TimelineTheme(
9394
data: TimelineThemeData(lineColor: Colors.blueAccent),
9495
child: Timeline(
96+
indicatorPosition: IndicatorPosition.top,
97+
altOffset: Offset(0, -24),
9598
indicatorSize: 56,
9699
events: events,
97100
));

lib/event_item.dart

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

3+
import 'indicator_position.dart';
4+
35
class TimelineEventDisplay {
4-
TimelineEventDisplay(
5-
{@required @required this.child,
6-
this.indicator,
7-
this.indicatorSize,
8-
this.forceLineDrawing = false});
6+
TimelineEventDisplay({
7+
@required @required this.child,
8+
this.indicator,
9+
this.indicatorSize,
10+
this.forceLineDrawing = false,
11+
this.indicatorPosition,
12+
});
913

1014
final Widget child;
1115

@@ -16,6 +20,9 @@ class TimelineEventDisplay {
1620
/// enables indicator line drawing even no indicator is passed.
1721
final bool forceLineDrawing;
1822

23+
/// [indicatorPosition] overrides the default IndicatorPosition
24+
final IndicatorPosition indicatorPosition;
25+
1926
bool get hasIndicator {
2027
return indicator != null;
2128
}

lib/timeline.dart

Lines changed: 106 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,24 @@ import 'package:flutter_timeline/event_item.dart';
44
import 'package:flutter_timeline/timeline_theme.dart';
55
import 'package:flutter_timeline/timeline_theme_data.dart';
66

7+
import 'indicator_position.dart';
8+
79
class Timeline extends StatelessWidget {
8-
const Timeline({
9-
@required this.events,
10-
this.isLeftAligned = true,
11-
this.padding = const EdgeInsets.all(8),
12-
this.controller,
13-
this.physics,
14-
this.shrinkWrap = true,
15-
this.primary = false,
16-
this.reverse = false,
17-
this.indicatorSize = 12.0,
18-
// item gap will be ignored when custom separatorBuilder is provided
19-
this.separatorBuilder,
20-
this.altOffset = const Offset(0, 0),
21-
}) : itemCount = events.length;
10+
const Timeline(
11+
{@required this.events,
12+
this.isLeftAligned = true,
13+
this.padding = const EdgeInsets.all(8),
14+
this.controller,
15+
this.physics,
16+
this.shrinkWrap = true,
17+
this.primary = false,
18+
this.reverse = false,
19+
this.indicatorSize = 12.0,
20+
// item gap will be ignored when custom separatorBuilder is provided
21+
this.separatorBuilder,
22+
this.altOffset = const Offset(0, 0),
23+
this.indicatorPosition = IndicatorPosition.center})
24+
: itemCount = events.length;
2225

2326
final Offset altOffset;
2427
final List<TimelineEventDisplay> events;
@@ -32,6 +35,9 @@ class Timeline extends StatelessWidget {
3235
final bool primary;
3336
final bool reverse;
3437

38+
/// [indicatorPosition] describes where the indicator drawing should start. use it with alt offset
39+
final IndicatorPosition indicatorPosition;
40+
3541
final IndexedWidgetBuilder separatorBuilder;
3642

3743
@override
@@ -50,13 +56,24 @@ class Timeline extends StatelessWidget {
5056
primary: primary,
5157
itemBuilder: (context, index) {
5258
final event = events[index];
59+
// safely get prev, next events
60+
TimelineEventDisplay prevEvent;
61+
TimelineEventDisplay nextEvent;
62+
if (index != 0) {
63+
prevEvent = events[index - 1];
64+
}
65+
if (index != events.length - 1) {
66+
nextEvent = events[index + 1];
67+
}
5368
final isFirst = index == 0;
5469
final isLast = index == itemCount - 1;
5570
final timelineTile = <Widget>[
5671
if (event.hasIndicator)
5772
_buildIndicatorSection(
5873
isFirst: isFirst,
5974
isLast: isLast,
75+
prevHasIndicator: _eventHasIndicator(prevEvent),
76+
nextHasIndicator: _eventHasIndicator(nextEvent),
6077
event: event,
6178
theme: timelineTheme),
6279
if (event.hasIndicator) SizedBox(width: timelineTheme.gutterSpacing),
@@ -74,6 +91,13 @@ class Timeline extends StatelessWidget {
7491
);
7592
}
7693

94+
bool _eventHasIndicator(TimelineEventDisplay event) {
95+
if (event == null) {
96+
return false;
97+
}
98+
return event.hasIndicator;
99+
}
100+
77101
Widget buildWrappedIndicator(Widget child, {double width, double height}) {
78102
return Container(
79103
width: width,
@@ -86,10 +110,16 @@ class Timeline extends StatelessWidget {
86110
Widget _buildIndicatorSection(
87111
{bool isFirst,
88112
bool isLast,
113+
bool prevHasIndicator,
114+
bool nextHasIndicator,
89115
TimelineEventDisplay event,
90116
TimelineThemeData theme}) {
91117
var overrideIndicatorSize =
92118
event.indicatorSize != null ? event.indicatorSize : indicatorSize;
119+
var overrideIndicatorPosition = event.indicatorPosition != null
120+
? event.indicatorPosition
121+
: indicatorPosition;
122+
93123
var line = CustomPaint(
94124
painter: _LineIndicatorPainter(
95125
hideDefaultIndicator: event.child != null,
@@ -104,6 +134,9 @@ class Timeline extends StatelessWidget {
104134
style: theme.style,
105135
itemGap: theme.itemGap,
106136
altOffset: altOffset,
137+
prevHasIndicator: prevHasIndicator,
138+
nextHasIndicator: nextHasIndicator,
139+
indicatorPosition: overrideIndicatorPosition,
107140
),
108141
child: SizedBox(height: double.infinity, width: indicatorSize),
109142
);
@@ -125,20 +158,23 @@ class Timeline extends StatelessWidget {
125158
}
126159

127160
class _LineIndicatorPainter extends CustomPainter {
128-
_LineIndicatorPainter({
129-
@required this.hideDefaultIndicator,
130-
@required this.indicatorSize,
131-
@required this.altOffset,
132-
@required this.maxIndicatorSize,
133-
@required this.lineGap,
134-
@required this.strokeCap,
135-
@required this.strokeWidth,
136-
@required this.style,
137-
@required this.lineColor,
138-
@required this.isFirst,
139-
@required this.isLast,
140-
@required this.itemGap,
141-
}) : linePaint = Paint()
161+
_LineIndicatorPainter(
162+
{@required this.hideDefaultIndicator,
163+
@required this.indicatorSize,
164+
@required this.altOffset,
165+
@required this.maxIndicatorSize,
166+
@required this.lineGap,
167+
@required this.strokeCap,
168+
@required this.strokeWidth,
169+
@required this.style,
170+
@required this.lineColor,
171+
@required this.isFirst,
172+
@required this.isLast,
173+
@required this.nextHasIndicator,
174+
@required this.prevHasIndicator,
175+
@required this.itemGap,
176+
@required this.indicatorPosition})
177+
: linePaint = Paint()
142178
..color = lineColor
143179
..strokeCap = strokeCap
144180
..strokeWidth = strokeWidth
@@ -156,31 +192,62 @@ class _LineIndicatorPainter extends CustomPainter {
156192
final Paint linePaint;
157193
final bool isFirst;
158194
final bool isLast;
195+
final bool nextHasIndicator;
196+
final bool prevHasIndicator;
159197
final double itemGap;
198+
final IndicatorPosition indicatorPosition;
199+
200+
double get altX {
201+
return altOffset.dx;
202+
}
203+
204+
double get altY {
205+
return altOffset.dy;
206+
}
160207

161208
@override
162209
void paint(Canvas canvas, Size size) {
163210
final indicatorRadius = indicatorSize / 2;
164211
final maxIndicatorRadius = maxIndicatorSize / 2;
165212
final indicatorMargin = indicatorRadius + lineGap;
166213
final safeItemGap = (indicatorSize / 2) + lineGap;
167-
final altY = altOffset.dy;
214+
double topStartY = 0.0;
215+
// region calculate starting point
216+
/*
217+
switch (indicatorPosition) {
218+
case IndicatorPosition.top:
219+
topStartY = -size.height / 2;
220+
break;
221+
case IndicatorPosition.center:
222+
topStartY = 0;
223+
break;
224+
case IndicatorPosition.bottom:
225+
// startY = size.height / 2;
226+
break;
227+
}*/
228+
// endregion
168229

169-
// todo
170-
// calculate starting point
171-
// calculate alt point
172-
// use alt point as default point
230+
// region override top, bottom calculator for filling empty space between events
231+
double overrideOffsetYForTop = altY;
232+
double overrideOffsetYForBottom = altY;
233+
if (!prevHasIndicator) {
234+
overrideOffsetYForTop = 0.0;
235+
}
236+
if (!nextHasIndicator) {
237+
overrideOffsetYForBottom = 0.0;
238+
}
239+
// endregion
173240

174-
final top =
175-
size.topLeft(Offset(maxIndicatorRadius, 0.0 - safeItemGap + altY));
241+
final top = size.topLeft(Offset(maxIndicatorRadius + altX,
242+
topStartY - safeItemGap + overrideOffsetYForTop));
176243
final topOfCenter = size.centerLeft(
177-
Offset(maxIndicatorRadius, -indicatorMargin + altY),
244+
Offset(maxIndicatorRadius + altX, -indicatorMargin + altY),
178245
);
179246

180-
final bottom =
181-
size.bottomLeft(Offset(maxIndicatorRadius, 0.0 + safeItemGap + altY));
247+
final bottom = size.bottomLeft(Offset(maxIndicatorRadius + altX,
248+
0.0 + safeItemGap + overrideOffsetYForBottom));
182249
final bottomOfCenter = size.centerLeft(
183-
Offset(maxIndicatorRadius, indicatorMargin + altY),
250+
Offset(maxIndicatorRadius + altX, indicatorMargin + altY),
184251
);
185252

186253
// if not first, draw top-to-center upper line

0 commit comments

Comments
 (0)