Flutter horizontal divider
Lets design horizontal divider widget having these features:
- Thickness and color selection
- Define left & right indent, margin insets
- Define any widget (maybe icon, maybe text, anything) as label with label alignmet(combination of [top, center, bottom] x [left, center, right]) and label margin insets
First of all, let’s design a prototype that will wrap the divider:
Our outer wrapper would be a container, because we could apply margin which to be determined by user as EdgeInsets.
Place a row widget inside this wrapper container, so we could determine left and right indent as sized boxes. Left sized box width will be left indent amount and the same way right sized box width will be the right indent amount.
Lastly, we would insert line and label widget inside Expanded widget, to cover up existing space in row, between sized boxes.
So, our divider widget return will be something like:
Container(
margin: marginInsets ?? EdgeInsets.zero,
child: Row(
children: [
SizedBox(width: leftIndent ?? 0.0),
Expanded(child: lineAndLabelColumn)
SizedBox(width: rightIndent ?? 0.0),
],
),
)
Notice that our code design perfectly suited to our prototype, anyway always try to make a habit of coding in line with the prototype. lineAndLabelColumn will be created on label, labelAlignment, label margin insets etc.
Let’s create our horizontal divider class outline with necessary parameters: (this is just an outline, we will design everything below)
Please notice that, we creating lineWidget as Expandable Container, its height will be divider thickness and its color will be line color.
And, labelWidgetWrapper will be an EmptyWidget(see below) if a label widget not defined. Otherwise, we put label widget into a Container (if label margin is defined), because label margin insets would be container parameter.
Lastly, if we need wrap lineWidget (and we will see why we need it below), we will use lineWidgetWrapper. Now, lets design our prototypes of divider based on label and label alignment:
So, lets coding for center alignments:
if (labelPosition == Alignment.center) {
lineWidgetWrapper = Row(children: [
lineWidget,
labelWidgetWrapper,
lineWidget,
]);
} else if (labelPosition == Alignment.centerLeft) {
lineWidgetWrapper = Row(children: [
labelWidgetWrapper,
lineWidget,
]);
} else if (labelPosition == Alignment.centerRight) {
lineWidgetWrapper = Row(children: [
lineWidget,
labelWidgetWrapper,
]);
}
- If label alignment is center, please notice that we create a row widget, adding into expandable line around label widget.
- If label alignment is center left, we create a row widget and placed line right side of label widget. Remember that, line widget is expandable.
- If label alignment is center right, we create a row widget and placed line left side of label widget.
Lets keep going with design top side alignment prototypes:
So, lets coding for top alignments:
if (labelPosition == Alignment.topCenter) {
lineWidgetWrapper = Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 25.0),
Flexible(child: labelWidgetWrapper),
SizedBox(width: 25.0),
],
),
Row(children: [lineWidget]),
],
);
} else if (labelPosition == Alignment.topLeft) {
lineWidgetWrapper = Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Flexible(child: labelWidgetWrapper),
SizedBox(width: 25.0),
],
),
Row(children: [lineWidget]),
],
);
} else if (labelPosition == Alignment.topRight) {
lineWidgetWrapper = Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(width: 25.0),
Flexible(child: labelWidgetWrapper),
],
),
Row(children: [lineWidget]),
],
);
}
Please notice that, when label alignment is any top or any bottom, our column includes two different row widgets, one wraps line widget and the other is wraps label itself.
Bottom alignment prototypes will be the same with top alignment prototypes, except line wrapper row and label wrapper row will be upside down.
So, lets coding for bottom alignments:
if (labelPosition == Alignment.bottomCenter) {
lineWidgetWrapper = Column(
children: [
Row(children: [lineWidget]),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(width: 25.0),
Flexible(child: labelWidgetWrapper),
SizedBox(width: 25.0),
],
),
],
);
} else if (labelPosition == Alignment.bottomLeft) {
lineWidgetWrapper = Column(
children: [
Row(children: [lineWidget]),
Row(
mainAxisAlignment: MainAxisAlignment.start,
children: [
Flexible(child: labelWidgetWrapper),
SizedBox(width: 25.0),
],
),
],
);
} else if (labelPosition == Alignment.bottomRight) {
lineWidgetWrapper = Column(
children: [
Row(children: [lineWidget]),
Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
SizedBox(width: 25.0),
Flexible(child: labelWidgetWrapper),
],
),
],
);
}
And finally, we designed our horizontal divider completely:
Here is EmptyWidget:
Let’s create a few example of horizontal divider output, firstly create a widget that we put divider into:
Column(
children: [
Align(
child: Text(
'Example Divider Wrapper', style: TextStyle(fontSize: 18),
),
alignment: Alignment.centerLeft,
),
Container(
padding: EdgeInsets.symmetric(vertical: 20.0),
decoration: BoxDecoration(
border: Border.all(color: Colors.black26, width: 1.0),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
],
),
),
],
),
)
We will put dividers into a Column. Let’s start with unlabeled:
HorizontalDivider() // just enough for unlabeled divider
Lets try define some parameters:
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 48.0),
)
Add an icon as label:
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Icon(Icons.audiotrack_outlined, size: 30,),
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Icon(Icons.audiotrack_outlined, size: 30,),
labelAlignment: Alignment.centerLeft,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Icon(Icons.audiotrack_outlined, size: 30,),
labelAlignment: Alignment.centerRight,
)
Try to place label at top or bottom and see the raction:
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Icon(
Icons.audiotrack_outlined,
size: 30,
),
labelAlignment: Alignment.topLeft,
),
HorizontalDivider(
marginInsets: EdgeInsets.symmetric(vertical: 25.0),
color: Colors.black38,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Icon(
Icons.audiotrack_outlined,
size: 30,
),
labelAlignment: Alignment.topCenter,
),
HorizontalDivider(
marginInsets: EdgeInsets.symmetric(vertical: 25.0),
color: Colors.black38,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Icon(
Icons.audiotrack_outlined,
size: 30,
),
labelAlignment: Alignment.bottomRight,
labelMarginInsets: EdgeInsets.only(right: 36),
)
Lets design label as Text widget:
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Text('Text Label', style: TextStyle(fontSize: 20)),
),
HorizontalDivider(
marginInsets: EdgeInsets.symmetric(vertical: 25.0),
color: Colors.black38,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Text('Text Label', style: TextStyle(fontSize: 20)),
labelMarginInsets: EdgeInsets.symmetric(horizontal: 8.0),
),
HorizontalDivider(
marginInsets: EdgeInsets.symmetric(vertical: 25.0),
color: Colors.black38,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Text('Text Label', style: TextStyle(fontSize: 20)),
labelMarginInsets: EdgeInsets.symmetric(horizontal: 8.0),
labelAlignment: Alignment.topLeft,
),
HorizontalDivider(
marginInsets: EdgeInsets.symmetric(vertical: 25.0),
color: Colors.black38,
),
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Text('Text Label', style: TextStyle(fontSize: 20)),
labelAlignment: Alignment.bottomRight,
),
See the effect of second text labeled widget’s label margin insets.
Text label will cause an overflow problem, if it is too long (see how we will fix this):
HorizontalDivider(
color: Colors.redAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Text('Looooooong Long Long Looooonger Text Label',
style: TextStyle(fontSize: 20)),
) // will cause an overflow error, see how fix it below
// If we put Text widget into Container has BoxConstraint
// on maxWidth, problem fixed with customisable way
// win win
// dont forget define text overflow methodContainer(
constraints: BoxConstraints(maxWidth: 100.0),
child: Text(
'Looooooong Long Long Looooonger Text Label',
style: TextStyle(fontSize: 20),
overflow: TextOverflow.ellipsis,
),
)
Extra example:
HorizontalDivider(
color: Colors.lightBlueAccent,
thickness: 2.0,
leftIndent: 12.0,
marginInsets: EdgeInsets.only(right: 12.0),
label: Container(
child: Icon(
Icons.add,
color: Colors.lightBlueAccent,
),
decoration: BoxDecoration(
shape: BoxShape.circle,
border: Border.all(
width: 2.0,
color: Colors.blueAccent,
),
),
),
labelMarginInsets: EdgeInsets.symmetric(horizontal: 8.0),
)
I am using this widget on my flutter chat app project (still under development), you can see this in action.
https://github.com/icanseker/flutter_chapp
Best regards…