@@ -4,21 +4,24 @@ import 'package:flutter_timeline/event_item.dart';
44import 'package:flutter_timeline/timeline_theme.dart' ;
55import 'package:flutter_timeline/timeline_theme_data.dart' ;
66
7+ import 'indicator_position.dart' ;
8+
79class 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
127160class _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