Skip to content

Commit

Permalink
Merge pull request #7 from softmarshmallow/feature/line-painter-redesign
Browse files Browse the repository at this point in the history
Feature/line painter redesign
  • Loading branch information
softmarshmallow authored Sep 3, 2020
2 parents 0bb4bad + 772a0cb commit 7b0f83c
Show file tree
Hide file tree
Showing 7 changed files with 196 additions and 58 deletions.
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ Timeline(
altOffset: Offset(0, -24),
// ...
);
```
```
## [0.1.1+12] - supports anchor & offset for timeline & indicator
11 changes: 7 additions & 4 deletions example/lib/screen/plain_timeline_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,10 @@ class _PlainTimelineDemoScreenState extends State<PlainTimelineDemoScreen> {

TimelineEventDisplay get plainEventDisplay {
return TimelineEventDisplay(
anchor: IndicatorPosition.top,
indicatorOffset: Offset(0, 24),
child: TimelineEventCard(
title: Text("just \n\n\n\n now"),
title: Text("multi\nline\ntitle\nawesome!"),
content: Text("someone commented on your timeline ${DateTime.now()}"),
),
indicator: randomIndicator);
Expand All @@ -91,11 +93,12 @@ class _PlainTimelineDemoScreenState extends State<PlainTimelineDemoScreen> {

Widget _buildTimeline() {
return TimelineTheme(
data: TimelineThemeData(lineColor: Colors.blueAccent, itemGap: 180),
data: TimelineThemeData(
lineColor: Colors.blueAccent, itemGap: 100, lineGap: 0),
child: Timeline(
indicatorPosition: IndicatorPosition.top,
altOffset: Offset(0, -24),
anchor: IndicatorPosition.center,
indicatorSize: 56,
altOffset: Offset(10, 40),
events: events,
));
}
Expand Down
2 changes: 1 addition & 1 deletion example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ packages:
path: ".."
relative: true
source: path
version: "0.0.4+6"
version: "0.0.4+11"
matcher:
dependency: transitive
description:
Expand Down
8 changes: 5 additions & 3 deletions lib/event_item.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ class TimelineEventDisplay {
this.indicator,
this.indicatorSize,
this.forceLineDrawing = false,
this.indicatorPosition,
this.anchor,
this.indicatorOffset = const Offset(0, 0),
});

final Widget child;
Expand All @@ -20,8 +21,9 @@ class TimelineEventDisplay {
/// enables indicator line drawing even no indicator is passed.
final bool forceLineDrawing;

/// [indicatorPosition] overrides the default IndicatorPosition
final IndicatorPosition indicatorPosition;
/// [anchor] overrides the default IndicatorPosition
final IndicatorPosition anchor;
final Offset indicatorOffset;

bool get hasIndicator {
return indicator != null;
Expand Down
25 changes: 20 additions & 5 deletions lib/indicator_position.dart
Original file line number Diff line number Diff line change
@@ -1,5 +1,20 @@
enum IndicatorPosition{
top,
center,
bottom
}
import 'package:flutter/widgets.dart';

enum IndicatorPosition { top, center, bottom }

extension Mapper on IndicatorPosition {
Alignment get asAlignment {
switch (this) {
case IndicatorPosition.top:
return Alignment.topCenter;
break;
case IndicatorPosition.center:
return Alignment.center;
break;
case IndicatorPosition.bottom:
return Alignment.bottomCenter;
break;
}
return Alignment.center;
}
}
203 changes: 160 additions & 43 deletions lib/timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class Timeline extends StatelessWidget {
// item gap will be ignored when custom separatorBuilder is provided
this.separatorBuilder,
this.altOffset = const Offset(0, 0),
this.indicatorPosition = IndicatorPosition.center})
this.anchor = IndicatorPosition.center})
: itemCount = events.length;

final Offset altOffset;
Expand All @@ -35,8 +35,8 @@ class Timeline extends StatelessWidget {
final bool primary;
final bool reverse;

/// [indicatorPosition] describes where the indicator drawing should start. use it with alt offset
final IndicatorPosition indicatorPosition;
/// [anchor] describes where the indicator drawing should start. use it with alt offset
final IndicatorPosition anchor;

final IndexedWidgetBuilder separatorBuilder;

Expand Down Expand Up @@ -98,11 +98,13 @@ class Timeline extends StatelessWidget {
return event.hasIndicator;
}

Widget buildWrappedIndicator(Widget child, {double width, double height}) {
Widget buildWrappedIndicator(Widget child,
{double width, double height, Offset indicatorOffset}) {
final offset = altOffset + indicatorOffset;
return Container(
width: width,
height: height,
transform: Matrix4.translationValues(altOffset.dx, altOffset.dy, 0.0),
transform: Matrix4.translationValues(offset.dx, offset.dy, 0.0),
child: child,
);
}
Expand All @@ -114,11 +116,11 @@ class Timeline extends StatelessWidget {
bool nextHasIndicator,
TimelineEventDisplay event,
TimelineThemeData theme}) {
var overrideIndicatorSize =
final overrideIndicatorSize =
event.indicatorSize != null ? event.indicatorSize : indicatorSize;
var overrideIndicatorPosition = event.indicatorPosition != null
? event.indicatorPosition
: indicatorPosition;
final overrideIndicatorPosition =
event.anchor != null ? event.anchor : anchor;
final indicatorOffset = event.indicatorOffset;

var line = CustomPaint(
painter: _LineIndicatorPainter(
Expand All @@ -137,6 +139,7 @@ class Timeline extends StatelessWidget {
prevHasIndicator: prevHasIndicator,
nextHasIndicator: nextHasIndicator,
indicatorPosition: overrideIndicatorPosition,
indicatorOffset: indicatorOffset,
),
child: SizedBox(height: double.infinity, width: indicatorSize),
);
Expand All @@ -145,9 +148,10 @@ class Timeline extends StatelessWidget {
line,
Positioned.fill(
child: Align(
alignment: Alignment.center,
alignment: overrideIndicatorPosition.asAlignment,
child: buildWrappedIndicator(
event.indicator,
indicatorOffset: indicatorOffset,
width: overrideIndicatorSize,
height: overrideIndicatorSize,
)),
Expand All @@ -173,6 +177,7 @@ class _LineIndicatorPainter extends CustomPainter {
@required this.nextHasIndicator,
@required this.prevHasIndicator,
@required this.itemGap,
@required this.indicatorOffset,
@required this.indicatorPosition})
: linePaint = Paint()
..color = lineColor
Expand All @@ -183,6 +188,7 @@ class _LineIndicatorPainter extends CustomPainter {
final Offset altOffset;
final bool hideDefaultIndicator;
final double indicatorSize;
final Offset indicatorOffset;
final double maxIndicatorSize;
final double lineGap;
final StrokeCap strokeCap;
Expand All @@ -198,39 +204,137 @@ class _LineIndicatorPainter extends CustomPainter {
final IndicatorPosition indicatorPosition;

double get altX {
return altOffset.dx;
return altOffset.dx + indicatorOffset.dx;
}

double get altY {
return altOffset.dy;
return altOffset.dy + indicatorOffset.dy;
}

@override
void paint(Canvas canvas, Size size) {
final indicatorRadius = indicatorSize / 2;
final maxIndicatorRadius = maxIndicatorSize / 2;
final indicatorMargin = indicatorRadius + lineGap;
final safeItemGap = (indicatorRadius) + itemGap;
double topStartY = 0.0;
// indicator's radius
final radius = indicatorSize / 2;
final height = size.height;
final halfHeight = height / 2;
final double halfItemGap = itemGap / 2;

// region calculate starting point
/*
// initial start point
// works well
Offset indicatorCenterStartPoint;
switch (indicatorPosition) {
case IndicatorPosition.top:
topStartY = -size.height / 2;
indicatorCenterStartPoint = size.topCenter(Offset(0, radius));
break;
case IndicatorPosition.center:
topStartY = 0;
indicatorCenterStartPoint = size.center(Offset.zero);
break;
case IndicatorPosition.bottom:
// startY = size.height / 2;
indicatorCenterStartPoint = size.bottomCenter(Offset(0, -radius));
break;
}*/
// endregion
}

// alt start point
Offset indicatorCenter = indicatorCenterStartPoint.translate(altX, altY);

// region upper line
if (!isFirst) {
double additionalGap = 0;
if (!prevHasIndicator) additionalGap = halfItemGap;
final additionalTop = getAdditionalY(height, mode: "upper");

// works well
Offset topStart = indicatorCenter.translate(
0,
// the altY + radius is the default start point.
// adding half item gap is also by default.
// the below two items does not get affected by the indicator position
-(((altY + radius) + halfItemGap) //
+
(additionalGap) +
(additionalTop) //
));

// works well
Offset topEnd = indicatorCenter.translate(0, -radius - lineGap);

// draw upper line
if (!isFirst) canvas.drawLine(topStart, topEnd, linePaint);
}
// endregion upper line

// endregion downer line
if (!isLast) {
double additionalGap = 0;
if (!nextHasIndicator) additionalGap = halfItemGap;

final additionalBottom = getAdditionalY(height, mode: "downer");

// works well
Offset bottomEnd = indicatorCenter.translate(
0,
(radius + halfItemGap - altY) +
(additionalGap) //
+
(additionalBottom) //
);

// works well
Offset bottomStart = indicatorCenter.translate(0, radius + lineGap);
if (!isLast) canvas.drawLine(bottomStart, bottomEnd, linePaint);
}
// endregion downer line
}

double getAdditionalY(double height, {@required String mode}) {
double add = 0;
// the additional size should be
if (mode == "upper") {
switch (indicatorPosition) {
case IndicatorPosition.top:
add = 0;
break;
case IndicatorPosition.center:
add = (height - indicatorSize) / 2;
break;
case IndicatorPosition.bottom:
add = height - indicatorSize;
break;
}
return add;
}

if (mode == "downer") {
switch (indicatorPosition) {
case IndicatorPosition.top:
add = height - indicatorSize;
break;
case IndicatorPosition.center:
add = (height - indicatorSize) / 2;
break;
case IndicatorPosition.bottom:
add = 0;
break;
}
return add;
}

throw FlutterError("$mode is not a supported mode");
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}

// painter v1
/*
// region override top, bottom calculator for filling empty space between events
double overrideOffsetYForTop = altY;
double overrideOffsetYForBottom = altY;
if (!prevHasIndicator) {
overrideOffsetYForTop = 0.0;
}
Expand All @@ -239,26 +343,39 @@ class _LineIndicatorPainter extends CustomPainter {
}
// endregion
final top = size.topLeft(Offset(maxIndicatorRadius + altX,
topStartY - safeItemGap + overrideOffsetYForTop));
final topOfCenter = size.centerLeft(
Offset(maxIndicatorRadius + altX, -indicatorMargin + altY),
);
final inboundTop = size.topCenter(Offset.zero);
final outboundTop = inboundTop.translate(
altX, topStartY - safeItemGap + overrideOffsetYForTop);
// final outboundTop = size.topLeft(Offset(maxIndicatorRadius + altX,
// topStartY - safeItemGap + overrideOffsetYForTop));
final bottom = size.bottomLeft(Offset(maxIndicatorRadius + altX,
0.0 + safeItemGap + overrideOffsetYForBottom));
final bottomOfCenter = size.centerLeft(
Offset(maxIndicatorRadius + altX, indicatorMargin + altY),
);
// region center
// FIXME
final center = size.center(Offset.zero);
final topOfCenter = center.translate(altX, -indicatorMargin + altY);
final bottomOfCenter = center.translate(altX, indicatorMargin + altY);
// endregion
final inboundBottom = size.bottomCenter(Offset.zero);
final outboundBottom =
inboundBottom.translate(altX, safeItemGap + overrideOffsetYForBottom);
// region calculate starting point
/*
switch (indicatorPosition) {
case IndicatorPosition.top:
topStartY = -size.height / 2;
break;
case IndicatorPosition.center:
topStartY = 0;
break;
case IndicatorPosition.bottom:
// startY = size.height / 2;
break;
}*/
// endregion
// if not first, draw top-to-center upper line
if (!isFirst) canvas.drawLine(top, topOfCenter, linePaint);
// if (!isFirst) canvas.drawLine(outboundTop, topOfCenter, linePaint);
// if not last, draw center-to-bottom bottom line
if (!isLast) canvas.drawLine(bottomOfCenter, bottom, linePaint);
}

@override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
if (!isLast) canvas.drawLine(bottomOfCenter, outboundBottom, testLinePaint);
* */
Loading

0 comments on commit 7b0f83c

Please sign in to comment.