@@ -20,7 +20,7 @@ class Timeline extends StatelessWidget {
2020 // item gap will be ignored when custom separatorBuilder is provided
2121 this .separatorBuilder,
2222 this .altOffset = const Offset (0 , 0 ),
23- this .indicatorPosition = IndicatorPosition .center})
23+ this .anchor = IndicatorPosition .center})
2424 : itemCount = events.length;
2525
2626 final Offset altOffset;
@@ -35,8 +35,8 @@ class Timeline extends StatelessWidget {
3535 final bool primary;
3636 final bool reverse;
3737
38- /// [indicatorPosition ] describes where the indicator drawing should start. use it with alt offset
39- final IndicatorPosition indicatorPosition ;
38+ /// [anchor ] describes where the indicator drawing should start. use it with alt offset
39+ final IndicatorPosition anchor ;
4040
4141 final IndexedWidgetBuilder separatorBuilder;
4242
@@ -98,11 +98,13 @@ class Timeline extends StatelessWidget {
9898 return event.hasIndicator;
9999 }
100100
101- Widget buildWrappedIndicator (Widget child, {double width, double height}) {
101+ Widget buildWrappedIndicator (Widget child,
102+ {double width, double height, Offset indicatorOffset}) {
103+ final offset = altOffset + indicatorOffset;
102104 return Container (
103105 width: width,
104106 height: height,
105- transform: Matrix4 .translationValues (altOffset .dx, altOffset .dy, 0.0 ),
107+ transform: Matrix4 .translationValues (offset .dx, offset .dy, 0.0 ),
106108 child: child,
107109 );
108110 }
@@ -114,11 +116,11 @@ class Timeline extends StatelessWidget {
114116 bool nextHasIndicator,
115117 TimelineEventDisplay event,
116118 TimelineThemeData theme}) {
117- var overrideIndicatorSize =
119+ final overrideIndicatorSize =
118120 event.indicatorSize != null ? event.indicatorSize : indicatorSize;
119- var overrideIndicatorPosition = event.indicatorPosition != null
120- ? event.indicatorPosition
121- : indicatorPosition ;
121+ final overrideIndicatorPosition =
122+ event.anchor != null ? event.anchor : anchor;
123+ final indicatorOffset = event.indicatorOffset ;
122124
123125 var line = CustomPaint (
124126 painter: _LineIndicatorPainter (
@@ -137,6 +139,7 @@ class Timeline extends StatelessWidget {
137139 prevHasIndicator: prevHasIndicator,
138140 nextHasIndicator: nextHasIndicator,
139141 indicatorPosition: overrideIndicatorPosition,
142+ indicatorOffset: indicatorOffset,
140143 ),
141144 child: SizedBox (height: double .infinity, width: indicatorSize),
142145 );
@@ -145,9 +148,10 @@ class Timeline extends StatelessWidget {
145148 line,
146149 Positioned .fill (
147150 child: Align (
148- alignment: Alignment .center ,
151+ alignment: overrideIndicatorPosition.asAlignment ,
149152 child: buildWrappedIndicator (
150153 event.indicator,
154+ indicatorOffset: indicatorOffset,
151155 width: overrideIndicatorSize,
152156 height: overrideIndicatorSize,
153157 )),
@@ -173,6 +177,7 @@ class _LineIndicatorPainter extends CustomPainter {
173177 @required this .nextHasIndicator,
174178 @required this .prevHasIndicator,
175179 @required this .itemGap,
180+ @required this .indicatorOffset,
176181 @required this .indicatorPosition})
177182 : linePaint = Paint ()
178183 ..color = lineColor
@@ -183,6 +188,7 @@ class _LineIndicatorPainter extends CustomPainter {
183188 final Offset altOffset;
184189 final bool hideDefaultIndicator;
185190 final double indicatorSize;
191+ final Offset indicatorOffset;
186192 final double maxIndicatorSize;
187193 final double lineGap;
188194 final StrokeCap strokeCap;
@@ -198,39 +204,137 @@ class _LineIndicatorPainter extends CustomPainter {
198204 final IndicatorPosition indicatorPosition;
199205
200206 double get altX {
201- return altOffset.dx;
207+ return altOffset.dx + indicatorOffset.dx ;
202208 }
203209
204210 double get altY {
205- return altOffset.dy;
211+ return altOffset.dy + indicatorOffset.dy ;
206212 }
207213
208214 @override
209215 void paint (Canvas canvas, Size size) {
210- final indicatorRadius = indicatorSize / 2 ;
211- final maxIndicatorRadius = maxIndicatorSize / 2 ;
212- final indicatorMargin = indicatorRadius + lineGap ;
213- final safeItemGap = (indicatorRadius) + itemGap ;
214- double topStartY = 0.0 ;
216+ // indicator's radius
217+ final radius = indicatorSize / 2 ;
218+ final height = size.height ;
219+ final halfHeight = height / 2 ;
220+ final double halfItemGap = itemGap / 2 ;
215221
216- // region calculate starting point
217- /*
222+ // initial start point
223+ // works well
224+ Offset indicatorCenterStartPoint;
218225 switch (indicatorPosition) {
219226 case IndicatorPosition .top:
220- topStartY = - size.height / 2 ;
227+ indicatorCenterStartPoint = size.topCenter ( Offset ( 0 , radius)) ;
221228 break ;
222229 case IndicatorPosition .center:
223- topStartY = 0 ;
230+ indicatorCenterStartPoint = size. center ( Offset .zero) ;
224231 break ;
225232 case IndicatorPosition .bottom:
226- // startY = size.height / 2 ;
233+ indicatorCenterStartPoint = size.bottomCenter ( Offset ( 0 , - radius)) ;
227234 break ;
228- }*/
229- // endregion
235+ }
236+
237+ // alt start point
238+ Offset indicatorCenter = indicatorCenterStartPoint.translate (altX, altY);
239+
240+ // region upper line
241+ if (! isFirst) {
242+ double additionalGap = 0 ;
243+ if (! prevHasIndicator) additionalGap = halfItemGap;
244+ final additionalTop = getAdditionalY (height, mode: "upper" );
245+
246+ // works well
247+ Offset topStart = indicatorCenter.translate (
248+ 0 ,
249+ // the altY + radius is the default start point.
250+ // adding half item gap is also by default.
251+ // the below two items does not get affected by the indicator position
252+ - (((altY + radius) + halfItemGap) //
253+ +
254+ (additionalGap) +
255+ (additionalTop) //
256+ ));
257+
258+ // works well
259+ Offset topEnd = indicatorCenter.translate (0 , - radius - lineGap);
260+
261+ // draw upper line
262+ if (! isFirst) canvas.drawLine (topStart, topEnd, linePaint);
263+ }
264+ // endregion upper line
265+
266+ // endregion downer line
267+ if (! isLast) {
268+ double additionalGap = 0 ;
269+ if (! nextHasIndicator) additionalGap = halfItemGap;
270+
271+ final additionalBottom = getAdditionalY (height, mode: "downer" );
272+
273+ // works well
274+ Offset bottomEnd = indicatorCenter.translate (
275+ 0 ,
276+ (radius + halfItemGap - altY) +
277+ (additionalGap) //
278+ +
279+ (additionalBottom) //
280+ );
281+
282+ // works well
283+ Offset bottomStart = indicatorCenter.translate (0 , radius + lineGap);
284+ if (! isLast) canvas.drawLine (bottomStart, bottomEnd, linePaint);
285+ }
286+ // endregion downer line
287+ }
288+
289+ double getAdditionalY (double height, {@required String mode}) {
290+ double add = 0 ;
291+ // the additional size should be
292+ if (mode == "upper" ) {
293+ switch (indicatorPosition) {
294+ case IndicatorPosition .top:
295+ add = 0 ;
296+ break ;
297+ case IndicatorPosition .center:
298+ add = (height - indicatorSize) / 2 ;
299+ break ;
300+ case IndicatorPosition .bottom:
301+ add = height - indicatorSize;
302+ break ;
303+ }
304+ return add;
305+ }
306+
307+ if (mode == "downer" ) {
308+ switch (indicatorPosition) {
309+ case IndicatorPosition .top:
310+ add = height - indicatorSize;
311+ break ;
312+ case IndicatorPosition .center:
313+ add = (height - indicatorSize) / 2 ;
314+ break ;
315+ case IndicatorPosition .bottom:
316+ add = 0 ;
317+ break ;
318+ }
319+ return add;
320+ }
321+
322+ throw FlutterError ("$mode is not a supported mode" );
323+ }
324+
325+ @override
326+ bool shouldRepaint (CustomPainter oldDelegate) {
327+ return true ;
328+ }
329+ }
330+
331+ // painter v1
332+ /*
230333
231334 // region override top, bottom calculator for filling empty space between events
232335 double overrideOffsetYForTop = altY;
233336 double overrideOffsetYForBottom = altY;
337+
234338 if (!prevHasIndicator) {
235339 overrideOffsetYForTop = 0.0;
236340 }
@@ -239,26 +343,39 @@ class _LineIndicatorPainter extends CustomPainter {
239343 }
240344 // endregion
241345
242- final top = size.topLeft (Offset (maxIndicatorRadius + altX,
243- topStartY - safeItemGap + overrideOffsetYForTop));
244- final topOfCenter = size. centerLeft (
245- Offset (maxIndicatorRadius + altX, - indicatorMargin + altY) ,
246- );
346+ final inboundTop = size.topCenter (Offset.zero);
347+ final outboundTop = inboundTop.translate(
348+ altX, topStartY - safeItemGap + overrideOffsetYForTop);
349+ // final outboundTop = size.topLeft( Offset(maxIndicatorRadius + altX,
350+ // topStartY - safeItemGap + overrideOffsetYForTop) );
247351
248- final bottom = size.bottomLeft (Offset (maxIndicatorRadius + altX,
249- 0.0 + safeItemGap + overrideOffsetYForBottom));
250- final bottomOfCenter = size.centerLeft (
251- Offset (maxIndicatorRadius + altX, indicatorMargin + altY),
252- );
352+ // region center
353+ // FIXME
354+ final center = size.center(Offset.zero);
355+ final topOfCenter = center.translate(altX, -indicatorMargin + altY);
356+ final bottomOfCenter = center.translate(altX, indicatorMargin + altY);
357+ // endregion
358+ final inboundBottom = size.bottomCenter(Offset.zero);
359+ final outboundBottom =
360+ inboundBottom.translate(altX, safeItemGap + overrideOffsetYForBottom);
361+
362+ // region calculate starting point
363+ /*
364+ switch (indicatorPosition) {
365+ case IndicatorPosition.top:
366+ topStartY = -size.height / 2;
367+ break;
368+ case IndicatorPosition.center:
369+ topStartY = 0;
370+ break;
371+ case IndicatorPosition.bottom:
372+ // startY = size.height / 2;
373+ break;
374+ }*/
375+ // endregion
253376
254377 // if not first, draw top-to-center upper line
255- if (! isFirst) canvas.drawLine (top , topOfCenter, linePaint);
378+ // if (!isFirst) canvas.drawLine(outboundTop , topOfCenter, linePaint);
256379 // if not last, draw center-to-bottom bottom line
257- if (! isLast) canvas.drawLine (bottomOfCenter, bottom, linePaint);
258- }
259-
260- @override
261- bool shouldRepaint (CustomPainter oldDelegate) {
262- return true ;
263- }
264- }
380+ if (!isLast) canvas.drawLine(bottomOfCenter, outboundBottom, testLinePaint);
381+ * */
0 commit comments