Skip to content

Commit

Permalink
✅ added alt offset feature (2/2)
Browse files Browse the repository at this point in the history
  • Loading branch information
softmarshmallow committed Aug 31, 2020
1 parent 218ec68 commit ecbb0a4
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 45 deletions.
5 changes: 4 additions & 1 deletion example/lib/screen/plain_timeline_demo.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter_timeline/flutter_timeline.dart';
import 'package:flutter_timeline/indicator_position.dart';
import 'package:flutter_timeline/timeline_theme.dart';
import 'package:flutter_timeline/timeline_theme_data.dart';

Expand Down Expand Up @@ -80,7 +81,7 @@ class _PlainTimelineDemoScreenState extends State<PlainTimelineDemoScreen> {
TimelineEventDisplay get plainEventDisplay {
return TimelineEventDisplay(
child: TimelineEventCard(
title: Text("just now"),
title: Text("just \n\n\n\n now"),
content: Text("someone commented on your timeline ${DateTime.now()}"),
),
indicator: randomIndicator);
Expand All @@ -92,6 +93,8 @@ class _PlainTimelineDemoScreenState extends State<PlainTimelineDemoScreen> {
return TimelineTheme(
data: TimelineThemeData(lineColor: Colors.blueAccent),
child: Timeline(
indicatorPosition: IndicatorPosition.top,
altOffset: Offset(0, -24),
indicatorSize: 56,
events: events,
));
Expand Down
17 changes: 12 additions & 5 deletions lib/event_item.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import 'package:flutter/material.dart';

import 'indicator_position.dart';

class TimelineEventDisplay {
TimelineEventDisplay(
{@required @required this.child,
this.indicator,
this.indicatorSize,
this.forceLineDrawing = false});
TimelineEventDisplay({
@required @required this.child,
this.indicator,
this.indicatorSize,
this.forceLineDrawing = false,
this.indicatorPosition,
});

final Widget child;

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

/// [indicatorPosition] overrides the default IndicatorPosition
final IndicatorPosition indicatorPosition;

bool get hasIndicator {
return indicator != null;
}
Expand Down
145 changes: 106 additions & 39 deletions lib/timeline.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@ import 'package:flutter_timeline/event_item.dart';
import 'package:flutter_timeline/timeline_theme.dart';
import 'package:flutter_timeline/timeline_theme_data.dart';

import 'indicator_position.dart';

class Timeline extends StatelessWidget {
const Timeline({
@required this.events,
this.isLeftAligned = true,
this.padding = const EdgeInsets.all(8),
this.controller,
this.physics,
this.shrinkWrap = true,
this.primary = false,
this.reverse = false,
this.indicatorSize = 12.0,
// item gap will be ignored when custom separatorBuilder is provided
this.separatorBuilder,
this.altOffset = const Offset(0, 0),
}) : itemCount = events.length;
const Timeline(
{@required this.events,
this.isLeftAligned = true,
this.padding = const EdgeInsets.all(8),
this.controller,
this.physics,
this.shrinkWrap = true,
this.primary = false,
this.reverse = false,
this.indicatorSize = 12.0,
// item gap will be ignored when custom separatorBuilder is provided
this.separatorBuilder,
this.altOffset = const Offset(0, 0),
this.indicatorPosition = IndicatorPosition.center})
: itemCount = events.length;

final Offset altOffset;
final List<TimelineEventDisplay> events;
Expand All @@ -32,6 +35,9 @@ 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;

final IndexedWidgetBuilder separatorBuilder;

@override
Expand All @@ -50,13 +56,24 @@ class Timeline extends StatelessWidget {
primary: primary,
itemBuilder: (context, index) {
final event = events[index];
// safely get prev, next events
TimelineEventDisplay prevEvent;
TimelineEventDisplay nextEvent;
if (index != 0) {
prevEvent = events[index - 1];
}
if (index != events.length - 1) {
nextEvent = events[index + 1];
}
final isFirst = index == 0;
final isLast = index == itemCount - 1;
final timelineTile = <Widget>[
if (event.hasIndicator)
_buildIndicatorSection(
isFirst: isFirst,
isLast: isLast,
prevHasIndicator: _eventHasIndicator(prevEvent),
nextHasIndicator: _eventHasIndicator(nextEvent),
event: event,
theme: timelineTheme),
if (event.hasIndicator) SizedBox(width: timelineTheme.gutterSpacing),
Expand All @@ -74,6 +91,13 @@ class Timeline extends StatelessWidget {
);
}

bool _eventHasIndicator(TimelineEventDisplay event) {
if (event == null) {
return false;
}
return event.hasIndicator;
}

Widget buildWrappedIndicator(Widget child, {double width, double height}) {
return Container(
width: width,
Expand All @@ -86,10 +110,16 @@ class Timeline extends StatelessWidget {
Widget _buildIndicatorSection(
{bool isFirst,
bool isLast,
bool prevHasIndicator,
bool nextHasIndicator,
TimelineEventDisplay event,
TimelineThemeData theme}) {
var overrideIndicatorSize =
event.indicatorSize != null ? event.indicatorSize : indicatorSize;
var overrideIndicatorPosition = event.indicatorPosition != null
? event.indicatorPosition
: indicatorPosition;

var line = CustomPaint(
painter: _LineIndicatorPainter(
hideDefaultIndicator: event.child != null,
Expand All @@ -104,6 +134,9 @@ class Timeline extends StatelessWidget {
style: theme.style,
itemGap: theme.itemGap,
altOffset: altOffset,
prevHasIndicator: prevHasIndicator,
nextHasIndicator: nextHasIndicator,
indicatorPosition: overrideIndicatorPosition,
),
child: SizedBox(height: double.infinity, width: indicatorSize),
);
Expand All @@ -125,20 +158,23 @@ class Timeline extends StatelessWidget {
}

class _LineIndicatorPainter extends CustomPainter {
_LineIndicatorPainter({
@required this.hideDefaultIndicator,
@required this.indicatorSize,
@required this.altOffset,
@required this.maxIndicatorSize,
@required this.lineGap,
@required this.strokeCap,
@required this.strokeWidth,
@required this.style,
@required this.lineColor,
@required this.isFirst,
@required this.isLast,
@required this.itemGap,
}) : linePaint = Paint()
_LineIndicatorPainter(
{@required this.hideDefaultIndicator,
@required this.indicatorSize,
@required this.altOffset,
@required this.maxIndicatorSize,
@required this.lineGap,
@required this.strokeCap,
@required this.strokeWidth,
@required this.style,
@required this.lineColor,
@required this.isFirst,
@required this.isLast,
@required this.nextHasIndicator,
@required this.prevHasIndicator,
@required this.itemGap,
@required this.indicatorPosition})
: linePaint = Paint()
..color = lineColor
..strokeCap = strokeCap
..strokeWidth = strokeWidth
Expand All @@ -156,31 +192,62 @@ class _LineIndicatorPainter extends CustomPainter {
final Paint linePaint;
final bool isFirst;
final bool isLast;
final bool nextHasIndicator;
final bool prevHasIndicator;
final double itemGap;
final IndicatorPosition indicatorPosition;

double get altX {
return altOffset.dx;
}

double get altY {
return altOffset.dy;
}

@override
void paint(Canvas canvas, Size size) {
final indicatorRadius = indicatorSize / 2;
final maxIndicatorRadius = maxIndicatorSize / 2;
final indicatorMargin = indicatorRadius + lineGap;
final safeItemGap = (indicatorSize / 2) + lineGap;
final altY = altOffset.dy;
double topStartY = 0.0;
// 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

// todo
// calculate starting point
// calculate alt point
// use alt point as default point
// region override top, bottom calculator for filling empty space between events
double overrideOffsetYForTop = altY;
double overrideOffsetYForBottom = altY;
if (!prevHasIndicator) {
overrideOffsetYForTop = 0.0;
}
if (!nextHasIndicator) {
overrideOffsetYForBottom = 0.0;
}
// endregion

final top =
size.topLeft(Offset(maxIndicatorRadius, 0.0 - safeItemGap + altY));
final top = size.topLeft(Offset(maxIndicatorRadius + altX,
topStartY - safeItemGap + overrideOffsetYForTop));
final topOfCenter = size.centerLeft(
Offset(maxIndicatorRadius, -indicatorMargin + altY),
Offset(maxIndicatorRadius + altX, -indicatorMargin + altY),
);

final bottom =
size.bottomLeft(Offset(maxIndicatorRadius, 0.0 + safeItemGap + altY));
final bottom = size.bottomLeft(Offset(maxIndicatorRadius + altX,
0.0 + safeItemGap + overrideOffsetYForBottom));
final bottomOfCenter = size.centerLeft(
Offset(maxIndicatorRadius, indicatorMargin + altY),
Offset(maxIndicatorRadius + altX, indicatorMargin + altY),
);

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

0 comments on commit ecbb0a4

Please sign in to comment.