Skip to content

Commit d396a4e

Browse files
authored
Add the basic features of TimelineTile and TimelineNode (chulwoo-park#7)
* Add Indicator mixin * Update TimelineNode's fields * Update comments some themes * Update default size in indicator theme * Add getEffectivePosition function in Indicator * Add nodePosition, indicatorPosition in TimelineTheme * Update export list * Add timelines.direction template * Remove example analysis exclusion * Update indicator effective position to use TimelineThemeData.indicatorPosition * Add TimelineTileNode mixin * Update TimelineTile features * Update TimelineNode features * Fix components alignment * Add OutlinedDotIndicator * Update example
1 parent ac3780c commit d396a4e

11 files changed

+659
-224
lines changed

analysis_options.yaml

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,4 @@
1-
analyzer:
2-
exclude:
3-
- example/**
4-
5-
include: package:pedantic/analysis_options.yaml
1+
iinclude: package:pedantic/analysis_options.yaml
62

73
linter:
84
rules:

example/lib/main.dart

Lines changed: 215 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:flutter/material.dart';
2+
import 'package:flutter/rendering.dart';
23
import 'package:timelines/timelines.dart';
34

45
void main() {
@@ -10,9 +11,8 @@ class MyApp extends StatelessWidget {
1011
Widget build(BuildContext context) {
1112
return MaterialApp(
1213
title: 'Timelines Demo',
13-
theme: ThemeData(
14-
primarySwatch: Colors.blue,
15-
),
14+
theme: ThemeData.light(),
15+
darkTheme: ThemeData.dark(),
1616
home: MyHomePage(title: 'Timelines Demo Home Page'),
1717
);
1818
}
@@ -35,72 +35,177 @@ class _MyHomePageState extends State<MyHomePage> {
3535
@override
3636
Widget build(BuildContext context) {
3737
return TimelineTheme(
38-
data: TimelineThemeData(),
38+
data: TimelineThemeData(
39+
indicatorTheme: IndicatorThemeData(size: 15.0),
40+
),
3941
child: Scaffold(
4042
appBar: AppBar(
4143
title: Text(widget.title),
4244
),
43-
body: ListView.builder(
45+
body: CustomScrollView(
4446
shrinkWrap: true,
45-
itemBuilder: (context, index) {
46-
if (index == 20) {
47-
return TimelineTheme(
48-
data: TimelineThemeData.horizontal(),
49-
child: SizedBox(
50-
height: 100,
51-
child: ListView.builder(
52-
scrollDirection: Axis.horizontal,
53-
shrinkWrap: true,
54-
itemBuilder: (context, index) {
55-
return TimelineTile(
56-
child: Card(
57-
child: Text("BYE"),
58-
),
59-
);
60-
},
47+
slivers: [
48+
SliverPadding(
49+
padding: EdgeInsets.only(top: 40.0),
50+
),
51+
SliverList(
52+
delegate: SliverChildListDelegate(
53+
[
54+
ExampleRow(
55+
[
56+
Container(color: Colors.white),
57+
],
6158
),
62-
),
63-
);
64-
}
65-
66-
final child = Padding(
67-
padding: const EdgeInsets.all(8.0),
68-
child: Card(
69-
child: Container(
70-
padding: EdgeInsets.all(20.0),
71-
width: 200.0,
72-
child: Text(
73-
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
74-
overflow: TextOverflow.ellipsis,
75-
maxLines: 4,
59+
ExampleRow(
60+
[
61+
Text('DotIndicator'),
62+
DotIndicator(),
63+
],
7664
),
77-
),
65+
SizedBox(height: 10.0),
66+
ExampleRow(
67+
[
68+
Text('SolidLineConnector'),
69+
SizedBox(
70+
height: 20.0,
71+
child: SolidLineConnector(),
72+
),
73+
],
74+
),
75+
SizedBox(height: 10.0),
76+
ExampleRow(
77+
[
78+
Text('TimelineNode.simple'),
79+
SizedBox(
80+
height: 50.0,
81+
child: TimelineNode.simple(),
82+
),
83+
],
84+
),
85+
],
7886
),
79-
);
80-
if (index == 3) {
81-
return TimelineTile(
82-
child: child,
83-
indicatorPosition: 0.5,
84-
indicatorChild: DotIndicator(
85-
child: Padding(
86-
padding: const EdgeInsets.all(4.0),
87-
child: CircularProgressIndicator(
88-
strokeWidth: 2.0,
89-
valueColor: AlwaysStoppedAnimation(Colors.white),
87+
),
88+
SliverList(
89+
delegate: SliverChildBuilderDelegate(
90+
(context, index) {
91+
final contents = Card(
92+
margin: EdgeInsetsDirectional.fromSTEB(20.0, 20.0, 20.0, 20.0),
93+
child: Container(
94+
padding: EdgeInsets.all(20.0),
95+
width: 200.0,
96+
child: Text(
97+
'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
98+
overflow: TextOverflow.ellipsis,
99+
maxLines: 4,
100+
),
90101
),
91-
),
92-
),
93-
);
94-
}
102+
);
103+
104+
final now = DateTime.now();
105+
final oppositeContents = Text(
106+
'${now.hour}:${now.minute}',
107+
overflow: TextOverflow.ellipsis,
108+
maxLines: 4,
109+
);
95110

96-
return TimelineTile(
97-
child: child,
98-
indicatorPosition: 0.5,
99-
drawStartLine: index > 0,
100-
drawEndLine: index < 19,
101-
);
102-
},
103-
itemCount: 21,
111+
var node;
112+
if (index != 1) {
113+
node = TimelineNode.simple(
114+
indicatorPosition: 0.2,
115+
indicatorSize: index == 6 ? 0 : 15.0,
116+
drawStartConnector: index > 0,
117+
indicatorChild: index == 2 ? _LoadingIndicatorChild() : null,
118+
);
119+
} else {
120+
node = TimelineNode(
121+
indicator: OutlinedDotIndicator(
122+
borderWidth: 2.0,
123+
),
124+
startConnector: SolidLineConnector(),
125+
endConnector: SolidLineConnector(),
126+
indicatorPosition: 0.2,
127+
);
128+
}
129+
130+
return TimelineTile(
131+
node: node,
132+
oppositeContents: Align(
133+
alignment: Alignment.topRight,
134+
child: Padding(
135+
padding: const EdgeInsetsDirectional.only(
136+
top: 26.0,
137+
end: 20.0,
138+
),
139+
child: oppositeContents,
140+
),
141+
),
142+
contents: contents,
143+
);
144+
},
145+
childCount: 20,
146+
),
147+
),
148+
],
149+
// itemBuilder: (context, index) {
150+
// if (index == 20) {
151+
// return TimelineTheme(
152+
// data: TimelineThemeData.horizontal(),
153+
// child: SizedBox(
154+
// height: 100,
155+
// child: ListView.builder(
156+
// scrollDirection: Axis.horizontal,
157+
// shrinkWrap: true,
158+
// itemBuilder: (context, index) {
159+
// return TimelineTile(
160+
// node: TimelineNode.simple(),
161+
// contents: Card(
162+
// child: Text("BYE"),
163+
// ),
164+
// );
165+
// },
166+
// ),
167+
// ),
168+
// );
169+
// }
170+
//
171+
// BoxShape.circle;
172+
// final child = Container(
173+
// padding: EdgeInsets.all(20.0),
174+
// width: 200.0,
175+
// color: index.isOdd ? Colors.yellow : Colors.blue,
176+
// child: Text(
177+
// 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.',
178+
// overflow: TextOverflow.ellipsis,
179+
// maxLines: 4,
180+
// ),
181+
// );
182+
// if (index == 3) {
183+
// return TimelineTile(
184+
// node: TimelineNode.simple(
185+
// indicatorChild: Padding(
186+
// padding: const EdgeInsets.all(4.0),
187+
// child: CircularProgressIndicator(
188+
// strokeWidth: 2.0,
189+
// valueColor: AlwaysStoppedAnimation(Colors.white),
190+
// ),
191+
// ),
192+
// ),
193+
// contents: Card(
194+
// child: Text("BYE"),
195+
// ),
196+
// );
197+
// }
198+
//
199+
// return TimelineTile(
200+
// node: TimelineNode.simple(
201+
// indicatorSize: index == 6 ? 0 : 15.0,
202+
// drawStartConnector: index > 0,
203+
// drawEndConnector: index < 19,
204+
// ),
205+
// contents: child,
206+
// );
207+
// },
208+
// itemCount: 21,
104209
),
105210
floatingActionButton: FloatingActionButton(
106211
onPressed: _addEvent,
@@ -111,3 +216,54 @@ class _MyHomePageState extends State<MyHomePage> {
111216
);
112217
}
113218
}
219+
220+
class ExampleRow extends StatelessWidget {
221+
const ExampleRow(
222+
this.children, {
223+
Key key,
224+
this.width = 300,
225+
this.height = 30,
226+
}) : super(key: key);
227+
228+
final List<Widget> children;
229+
final double width;
230+
final double height;
231+
232+
@override
233+
Widget build(BuildContext context) {
234+
return Center(
235+
child: Container(
236+
width: width,
237+
height: height,
238+
child: Row(
239+
mainAxisSize: MainAxisSize.min,
240+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
241+
children: children
242+
.map(
243+
(child) => Flexible(
244+
flex: 1,
245+
child: child,
246+
),
247+
)
248+
.toList(),
249+
),
250+
),
251+
);
252+
}
253+
}
254+
255+
class _LoadingIndicatorChild extends StatelessWidget {
256+
@override
257+
Widget build(BuildContext context) {
258+
return Center(
259+
child: Container(
260+
width: 8.0,
261+
height: 8.0,
262+
child: CircularProgressIndicator(
263+
strokeWidth: 1.0,
264+
valueColor: AlwaysStoppedAnimation(Colors.white),
265+
),
266+
),
267+
);
268+
}
269+
}

lib/src/connector_theme.dart

Lines changed: 9 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -8,27 +8,23 @@ import 'connectors.dart';
88
import 'timeline_node.dart';
99
import 'timeline_theme.dart';
1010

11-
/// Defines the visual properties of [SolidLineConnector], connectors
12-
/// inside [TimelineNode].
11+
/// Defines the visual properties of [SolidLineConnector], connectors inside [TimelineNode].
1312
///
14-
/// Descendant widgets obtain the current [ConnectorThemeData] object using
15-
/// `ConnectorTheme.of(context)`. Instances of [ConnectorThemeData]
13+
/// Descendant widgets obtain the current [ConnectorThemeData] object using `ConnectorTheme.of(context)`. Instances of
14+
/// [ConnectorThemeData]
1615
/// can be customized with [ConnectorThemeData.copyWith].
1716
///
18-
/// Typically a [ConnectorThemeData] is specified as part of the overall
19-
/// [TimelineTheme] with [TimelineThemeData.connectorTheme].
17+
/// Typically a [ConnectorThemeData] is specified as part of the overall [TimelineTheme] with
18+
/// [TimelineThemeData.connectorTheme].
2019
///
21-
/// All [ConnectorThemeData] properties are `null` by default. When null,
22-
/// the widgets will provide their own defaults.
20+
/// All [ConnectorThemeData] properties are `null` by default. When null, the widgets will provide their own defaults.
2321
///
2422
/// See also:
2523
///
26-
/// * [TimelineThemeData], which describes the overall theme information for the
27-
/// timeline.
24+
/// * [TimelineThemeData], which describes the overall theme information for the timeline.
2825
@immutable
2926
class ConnectorThemeData with Diagnosticable {
30-
/// Creates a theme that can be used for [ConnectorTheme] or
31-
/// [TimelineThemeData.connectorTheme].
27+
/// Creates a theme that can be used for [ConnectorTheme] or [TimelineThemeData.connectorTheme].
3228
const ConnectorThemeData({
3329
this.color,
3430
this.space,
@@ -52,8 +48,7 @@ class ConnectorThemeData with Diagnosticable {
5248
/// The amount of empty space at the end edge of [SolidLineConnector].
5349
final double endIndent;
5450

55-
/// Creates a copy of this object with the given fields replaced with the
56-
/// new values.
51+
/// Creates a copy of this object with the given fields replaced with the new values.
5752
ConnectorThemeData copyWith({
5853
Color color,
5954
double space,

0 commit comments

Comments
 (0)