Timeline tile in Flutter is used to show progression of a given task to the user. It shows the level of completion of a task with the completed and incomplete steps for a certain event.
In today’s applications, timeline tile is widely used and plays a significant role in improving user friendliness and user’s experience.
Timeline tile is used in various scenarios. For example, delivery applications, multi-steps forms, and progress tracking. In other words, it’s used to show users the starting point, milestones, and ending point in a highly visual and easy tracking format.
Furthermore, it illustrate sequence of required steps in a very effective and attractive way. So, it acts as a roadmap to display a chain of activities or events that the user needs to complete to achieve a certain goal.
Now, Timeline tile can be displayed in both horizontal and vertical direction. Moreover, It’s very easy to implement and highly customizable.
In this tutorial, we’ll go through a complete flutter Timeline tile example using timeline_tile
package.
So, without further ado, let’s start our tutorial!
Table of Contents
- Setting up Flutter project
- Timeline Tile Constructor
- Timeline Tile properties
- Delivery Timeline Example
- Success Steps Timeline Tile Example
- Conclusion
Setting up Flutter project
Firstly , create a new Flutter project then open pubspec.yaml
file to add timeline_tile
dependency then click Pub get.
timeline_tile: ^2.0.0
Then, create a new dart file and import the following library
import 'package:timeline_tile/timeline_tile.dart';
Timeline Tile Constructor
const TimelineTile({
Key? key,
this.axis = TimelineAxis.vertical,
this.alignment = TimelineAlign.start,
this.startChild,
this.endChild,
this.lineXY,
this.hasIndicator = true,
this.isFirst = false,
this.isLast = false,
this.indicatorStyle = const IndicatorStyle(width: 25),
this.beforeLineStyle = const LineStyle(),
LineStyle? afterLineStyle,
})
Timeline Tile properties
Property | Description |
axis | defines the direction of the timeline tile to be horizontal or vertical. By default, Timeline tile is set to horizontal direction. |
alignment | specifies the tile alignment. By default, it’s set to TimelineTileAlign.Start. It has 4 values which are start, end, center, and manual. Manual alignment is used to manually align the timeline tile. |
startChild | the widget that takes place before the timeline tile |
endChild | the widget that takes place after the timeline tile |
hasIndicator | specifies whether the tile has an indicator or not. By default, hasIndicator is set to true |
indicatorStyle | customizes the appearance of the tile’s indicator including its color, width, height, and icon. |
lineXY | defines the position of the timeline in case of manual alignment. It takes values between 0 and 1. |
isFirst | specifies if the tile is the first tile of the sequence. If set to true, the tile will not have a line before the indicator |
isLast | specifies if the tile is the last tile of the sequence. If set to true, the tile will not have a line after the indicator |
beforeLineStyle | customizes the line before the tile’s indicator such as the thickness and color. |
afterLineStyle | customizes the line after the tile’s indicator such as the thickness and color. |
Delivery Timeline Example
In this example, we’ll create a timeline to show progress indicator of food order. It shows a sequence of order status that help the user track their order easily.
We’ll be creating 3 tiles to show the progress of order receiving, processing and delivering. In addition, we’ll use the floating action button to illustrate the progression and how the tile’s icon and color change accordingly.
Now, before we create the first tile, let’s creating an integer value index
that will be responsible for controlling the changes in tiles appearance when the progress changes.
int index = 0;
Creating the first timeline tile
TimelineTile(
axis: TimelineAxis.vertical,
indicatorStyle: IndicatorStyle(
color: index == 0 ? Colors.pink : Colors.pink.shade200,
height: 40,
width: 40,
iconStyle: IconStyle(
color: Colors.white,
iconData: index ==0 ? Icons.emoji_emotions_outlined :Icons.check,
),
),
isFirst: true,
afterLineStyle: LineStyle(
color: index > 0 ? Colors.green : Colors.grey
),
alignment: TimelineAlign.start,
endChild: Row(
children: [
SizedBox(width: 30,),
Container(
margin: EdgeInsets.only(top: 10,bottom:30),
height: 80,
width: 280,
decoration: BoxDecoration(
boxShadow:
[
BoxShadow(
blurRadius: 6,
color: Colors.grey
),
],
//borderRadius: BorderRadius.all(Radius.circular(24)),
border: Border(
top: BorderSide(
color: Colors.pink,
width: 5
)
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Text('Ordered Received', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('We\'ve received your order', style: TextStyle(fontSize: 16),),
)
],
),
),
],
),
),

Creating the second timeline tile
TimelineTile(
axis: TimelineAxis.vertical,
indicatorStyle: IndicatorStyle(
color: index == 1 ? Colors.pink : Colors.pink.shade200,
height: 40,
width: 40,
iconStyle: IconStyle(
color: Colors.white,
iconData: index == 1 ? Icons.fastfood_rounded :Icons.check,
),
),
beforeLineStyle: LineStyle(
color: index > 0 ? Colors.green : Colors.grey
),
afterLineStyle: LineStyle(
color: index > 1 ? Colors.green : Colors.grey
),
alignment: TimelineAlign.start,
endChild: Row(
children: [
SizedBox(width: 30,),
Container(
margin: EdgeInsets.only(top: 10,bottom:30),
height: 80,
width: 280,
decoration: BoxDecoration(
boxShadow:
[
BoxShadow(
blurRadius: 6,
color: Colors.grey
),
],
border: Border(
top: BorderSide(
color: Colors.pink,
width: 5
)
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Text('Order in Process', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Your order is being prepared', style: TextStyle(fontSize: 16),),
)
],
),
),
],
),
),

Implementing the third timeline tile
TimelineTile(
axis: TimelineAxis.vertical,
indicatorStyle: IndicatorStyle(
color: index == 2 ? Colors.pink : Colors.pink.shade200,
height: 40,
width: 40,
iconStyle: IconStyle(
color: Colors.white,
iconData: index == 2 ? Icons.delivery_dining_outlined :Icons.check,
),
),
isLast: true,
beforeLineStyle: LineStyle(
color: index > 1 ? Colors.green : Colors.grey,
),
alignment: TimelineAlign.start,
endChild: Row(
children: [
SizedBox(width: 30,),
Container(
margin: EdgeInsets.only(top: 10,bottom:30),
height: 80,
width: 280,
decoration: BoxDecoration(
boxShadow:
[
BoxShadow(
blurRadius: 6,
color: Colors.grey
),
],
border: Border(
top: BorderSide(
color: Colors.pink,
width: 5
)
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Text('Order in Delivery', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Your order is on its way to you!', style: TextStyle(fontSize: 16),),
)
],
),
),
],
),
),

Adding floating action button
floatingActionButton: FloatingActionButton(
onPressed: () {
//increase
setState(() {
index +=1;
});
},
child: Icon(
index < 3 ? Icons.navigate_next : Icons.done ),
),

Complete Code
int index = 0;
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
title: Text('Flutter Timeline Tile Example'),
centerTitle: true,
),
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(left: 8, right: 8),
child: Column(
children: [
TimelineTile(
axis: TimelineAxis.vertical,
indicatorStyle: IndicatorStyle(
color: index == 0 ? Colors.pink : Colors.pink.shade200,
height: 40,
width: 40,
iconStyle: IconStyle(
color: Colors.white,
iconData: index ==0 ? Icons.emoji_emotions_outlined :Icons.check,
),
),
isFirst: true,
afterLineStyle: LineStyle(
color: index > 0 ? Colors.green : Colors.grey
),
alignment: TimelineAlign.start,
endChild: Row(
children: [
SizedBox(width: 30,),
Container(
margin: EdgeInsets.only(top: 10,bottom:30),
height: 80,
width: 280,
decoration: BoxDecoration(
boxShadow:
[
BoxShadow(
blurRadius: 6,
color: Colors.grey
),
],
//borderRadius: BorderRadius.all(Radius.circular(24)),
border: Border(
top: BorderSide(
color: Colors.pink,
width: 5
)
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Text('Ordered Received', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('We\'ve received your order', style: TextStyle(fontSize: 16),),
)
],
),
),
],
),
),
TimelineTile(
axis: TimelineAxis.vertical,
indicatorStyle: IndicatorStyle(
color: index == 1 ? Colors.pink : Colors.pink.shade200,
height: 40,
width: 40,
iconStyle: IconStyle(
color: Colors.white,
iconData: index == 1 ? Icons.fastfood_rounded :Icons.check,
),
),
beforeLineStyle: LineStyle(
color: index > 0 ? Colors.green : Colors.grey
),
afterLineStyle: LineStyle(
color: index > 1 ? Colors.green : Colors.grey
),
alignment: TimelineAlign.start,
endChild: Row(
children: [
SizedBox(width: 30,),
Container(
margin: EdgeInsets.only(top: 10,bottom:30),
height: 80,
width: 280,
decoration: BoxDecoration(
boxShadow:
[
BoxShadow(
blurRadius: 6,
color: Colors.grey
),
],
border: Border(
top: BorderSide(
color: Colors.pink,
width: 5
)
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Text('Order in Process', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Your order is being prepared', style: TextStyle(fontSize: 16),),
)
],
),
),
],
),
),
TimelineTile(
axis: TimelineAxis.vertical,
indicatorStyle: IndicatorStyle(
color: index == 2 ? Colors.pink : Colors.pink.shade200,
height: 40,
width: 40,
iconStyle: IconStyle(
color: Colors.white,
iconData: index == 2 ? Icons.delivery_dining_outlined :Icons.check,
),
),
isLast: true,
beforeLineStyle: LineStyle(
color: index > 1 ? Colors.green : Colors.grey,
),
alignment: TimelineAlign.start,
endChild: Row(
children: [
SizedBox(width: 30,),
Container(
margin: EdgeInsets.only(top: 10,bottom:30),
height: 80,
width: 280,
decoration: BoxDecoration(
boxShadow:
[
BoxShadow(
blurRadius: 6,
color: Colors.grey
),
],
border: Border(
top: BorderSide(
color: Colors.pink,
width: 5
)
),
color: Colors.white,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.only(left: 8.0, top: 8.0),
child: Text('Order in Delivery', style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),),
),
Padding(
padding: EdgeInsets.all(8.0),
child: Text('Your order is on its way to you!', style: TextStyle(fontSize: 16),),
)
],
),
),
],
),
),
],
),
),
),
floatingActionButton: FloatingActionButton(
onPressed: () {
//increase
setState(() {
index +=1;
});
},
child: Icon(
index < 3 ? Icons.navigate_next : Icons.done ),
),
);
}

Success Steps Timeline Tile Example
Besides using timeline to show progress of events, we can use it to create a story timeline to deliver the user a specific message. In this example, we’ll create a timeline in a different appearance by combining Timeline Tile and Timeline Divider. In this example, we’ll manually position the tiles and dividers to show the user 5 steps for success. Now, timeline divider is used to display a horizontal or vertical line to separate the timeline tiles and enhance the appearance of timeline.
Creating the first timeline tile
TimelineTile(
axis: TimelineAxis.vertical,
alignment: TimelineAlign.manual,
lineXY: 0.1,
isFirst: true,
indicatorStyle: IndicatorStyle(
width: 40,
height:40,
indicator: Container(
width: 50,
height: 50,
child: Center(child: Text('1', style: TextStyle(fontSize: 26, color: Colors.white, fontFamily: 'Dancing Script'),)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.pink
),
)
),
afterLineStyle: LineStyle(color: Colors.pink),
endChild: Container(
margin: EdgeInsets.only(top: 20, left: 40, right: 10, bottom: 10),
width: 200,
height: 140,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Believe in yourself!', textAlign: TextAlign.right, style: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold, fontSize: 24),),
SizedBox(height: 10),
Text('belive that you can achieve your dreams'
' when you want to do so. Trust that you cando any thing you set your mind to',textAlign: TextAlign.right, style: TextStyle(color: Colors.pink[500], fontSize: 20),)
],
),
),
),

Creating the Timeline divider
TimelineDivider(
thickness: 4,
begin: 0.1,
end: 0.9,
color: Colors.pink,
axis: TimelineAxis.horizontal,
),

Complete Code
Scaffold(
backgroundColor: Colors.grey[200],
appBar: AppBar(
title: Text('Flutter Timeline Tile Example'),
centerTitle: true,
),
body: SingleChildScrollView(
child: Container(
padding: EdgeInsets.only(left: 8, right: 8),
child: Column(
children: [
Container(
width: MediaQuery.of(context).size.width,
height: 70,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(20),
color: Colors.pink.withOpacity(0.2)
),
margin: EdgeInsets.only(top: 20, left: 10, right: 10),
padding: EdgeInsets.only(left: 10, right: 10),
child: Center(
child: Text(
'5 Steps to be successful',
style: TextStyle(
fontSize: 30,
color: Colors.pink
),
),
),
),
//1
TimelineTile(
axis: TimelineAxis.vertical,
alignment: TimelineAlign.manual,
lineXY: 0.1,
isFirst: true,
indicatorStyle: IndicatorStyle(
width: 40,
height:40,
indicator: Container(
width: 50,
height: 50,
child: Center(child: Text('1', style: TextStyle(fontSize: 26, color: Colors.white, fontFamily: 'Dancing Script'),)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.pink
),
)
),
afterLineStyle: LineStyle(color: Colors.pink),
endChild: Container(
margin: EdgeInsets.only(top: 20, left: 40, right: 10, bottom: 10),
width: 200,
height: 140,
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Believe in yourself!', textAlign: TextAlign.right, style: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold, fontSize: 24),),
SizedBox(height: 10),
Text('belive that you can achieve your dreams'
' when you want to do so. Trust that you cando any thing you set your mind to',textAlign: TextAlign.right, style: TextStyle(color: Colors.pink[500], fontSize: 20),)
],
),
),
),
TimelineDivider(
thickness: 4,
begin: 0.1,
end: 0.9,
color: Colors.pink,
axis: TimelineAxis.horizontal,
),
//2
TimelineTile(
axis: TimelineAxis.vertical,
alignment: TimelineAlign.manual,
lineXY: 0.9,
beforeLineStyle: LineStyle(color: Colors.pink),
afterLineStyle: LineStyle(color: Colors.pink),
indicatorStyle: IndicatorStyle(
width: 40,
height:40,
indicator: Container(
width: 50,
height: 50,
child: Center(child: Text('2', style: TextStyle(fontSize: 26, color: Colors.white, fontFamily: 'Dancing Script'),)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.pink
),
)
),
startChild: Container(
width: 50,
height: 140,
margin: EdgeInsets.only(top: 30, bottom: 10, left: 10, right: 40),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Work Hard!', textAlign: TextAlign.left, style: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold, fontSize: 24),),
SizedBox(height: 10),
Text('don\'t stop when you\'re tired, stop when you\'re done! \ndo whatever it takes no matter how hard it is',textAlign: TextAlign.left, style: TextStyle(color: Colors.pink[500], fontSize: 20),)
],
),
),
),
TimelineDivider(
axis: TimelineAxis.horizontal,
thickness: 4,
color: Colors.pink,
begin: 0.055,
end: 0.9,
),
//3
TimelineTile(
axis: TimelineAxis.vertical,
alignment: TimelineAlign.manual,
lineXY: 0.01,
indicatorStyle: IndicatorStyle(
width: 40,
height:40,
indicator: Container(
width: 50,
height: 50,
child: Center(child: Text('3', style: TextStyle(fontSize: 26, color: Colors.white, fontFamily: 'Dancing Script'),)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.pink
),
)
),
beforeLineStyle: LineStyle(color: Colors.pink),
endChild: Container(
width: 50,
height: 140,
margin: EdgeInsets.only(top: 20, bottom: 20, left: 50, right: 10),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Set measurable goals!', textAlign: TextAlign.right, style: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold, fontSize: 24),),
SizedBox(height: 10),
Text('break your big goal into smaller achievable and measurable goals that you can achieve in short periods',textAlign: TextAlign.right, style: TextStyle(color: Colors.pink[500], fontSize: 20),)
],
),
),
),
TimelineDivider(
thickness: 4,
color: Colors.pink,
begin: 0.055,
end: 0.945,
axis: TimelineAxis.horizontal,
),
//4
TimelineTile(
axis: TimelineAxis.vertical,
alignment: TimelineAlign.manual,
lineXY: 0.99,
indicatorStyle: IndicatorStyle(
width: 40,
height:40,
indicator: Container(
width: 50,
height: 50,
child: Center(child: Text('4', style: TextStyle(fontSize: 26, color: Colors.white, fontFamily: 'Dancing Script'),)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.pink
),
)
),
beforeLineStyle: LineStyle(color: Colors.pink),
afterLineStyle: LineStyle(color:Colors.pink),
startChild: Container(
width: 50,
height: 140,
margin: EdgeInsets.only(top: 20, bottom: 10, left: 50, right: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Discipline!', textAlign: TextAlign.left, style: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold, fontSize: 24),),
SizedBox(height: 10),
Text('be consistent and finish your daily tasks. Don\'t procrastinate and fight every excuse you make',textAlign: TextAlign.left, style: TextStyle(color: Colors.pink[500], fontSize: 20),)
],
),
),
),
TimelineDivider(
axis: TimelineAxis.horizontal,
begin: 0.055,
end: 0.945,
thickness: 4,
color: Colors.pink,
),
//5
TimelineTile(
axis: TimelineAxis.vertical,
alignment: TimelineAlign.manual,
lineXY: 0.01,
beforeLineStyle: LineStyle(color: Colors.pink),
indicatorStyle: IndicatorStyle(
width: 40,
height:40,
indicator: Container(
width: 50,
height: 50,
child: Center(child: Text('5', style: TextStyle(fontSize: 26, color: Colors.white, fontFamily: 'Dancing Script'),)),
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(25),
color: Colors.pink
),
)
),
isLast: true,
endChild: Container(
margin: EdgeInsets.only(top: 20, left: 40, right: 10, bottom: 20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.end,
children: [
Text('Time Management!', textAlign: TextAlign.right, style: TextStyle(color: Colors.pink, fontWeight: FontWeight.bold, fontSize: 24),),
SizedBox(height: 10),
Text('Make sure to manage your time by doing tasks with higher priority first and allocate enough time to each task',textAlign: TextAlign.right, style: TextStyle(color: Colors.pink[500], fontSize: 20),)
],
),
),
),
],
),
),
),
)

Conclusion
Wooo, you’ve just finished Flutter timeline tile example! Congrats!
It’s very practical to learn how to implement timelines in your apps as they’ve become very popular. We learnt how to create timeline tile in both vertical and manual alignment. In addition, we learnt how to use Timeline divider to create beautiful timeline UI.
If you wish to learn more about Flutter, I have a whole category called Flutter that’s ripe for you to explore. You can learn about Flutter widgets, layouts, onboarding screen, tabbar, inkwell and gesture detector, stepper, listviews, gridviews, In-app messages, and practice with a bunch of real examples and many more!
Also, don’t forget to like and share my Facebook page, share the post with those who care to learn, and subscribe to my blog to be one of the firsts to know about my newest posts!
Thank you!