@@ -141,6 +141,7 @@ class SegmentedButton<T> extends StatefulWidget {
141141 this .style,
142142 this .showSelectedIcon = true ,
143143 this .selectedIcon,
144+ this .direction = Axis .horizontal,
144145 }) : assert (segments.length > 0 ),
145146 assert (selected.length > 0 || emptySelectionAllowed),
146147 assert (selected.length < 2 || multiSelectionEnabled);
@@ -154,6 +155,14 @@ class SegmentedButton<T> extends StatefulWidget {
154155 /// [ChoiceChip] widgets.
155156 final List <ButtonSegment <T >> segments;
156157
158+ /// The orientation of the button's [segments] .
159+ ///
160+ /// If this is [Axis.vertical] , the segments will be aligned vertically
161+ /// and the first item in [segments] will be on the top.
162+ ///
163+ /// Defaults to [Axis.horizontal] .
164+ final Axis direction;
165+
157166 /// The set of [ButtonSegment.value] s that indicate which [segments] are
158167 /// selected.
159168 ///
@@ -449,7 +458,7 @@ class SegmentedButtonState<T> extends State<SegmentedButton<T>> {
449458 Widget build (BuildContext context) {
450459 final SegmentedButtonThemeData theme = SegmentedButtonTheme .of (context);
451460 final SegmentedButtonThemeData defaults = _SegmentedButtonDefaultsM3 (context);
452- final TextDirection direction = Directionality .of (context);
461+ final TextDirection textDirection = Directionality .of (context);
453462
454463 const Set <MaterialState > enabledState = < MaterialState > {};
455464 const Set <MaterialState > disabledState = < MaterialState > { MaterialState .disabled };
@@ -576,7 +585,8 @@ class SegmentedButtonState<T> extends State<SegmentedButton<T>> {
576585 segments: widget.segments,
577586 enabledBorder: _enabled ? enabledBorder : disabledBorder,
578587 disabledBorder: disabledBorder,
579- direction: direction,
588+ direction: widget.direction,
589+ textDirection: textDirection,
580590 isExpanded: widget.expandedInsets != null ,
581591 children: buttons,
582592 ),
@@ -601,6 +611,7 @@ class _SegmentedButtonRenderWidget<T> extends MultiChildRenderObjectWidget {
601611 required this .enabledBorder,
602612 required this .disabledBorder,
603613 required this .direction,
614+ required this .textDirection,
604615 required this .tapTargetVerticalPadding,
605616 required this .isExpanded,
606617 required super .children,
@@ -609,7 +620,8 @@ class _SegmentedButtonRenderWidget<T> extends MultiChildRenderObjectWidget {
609620 final List <ButtonSegment <T >> segments;
610621 final OutlinedBorder enabledBorder;
611622 final OutlinedBorder disabledBorder;
612- final TextDirection direction;
623+ final Axis direction;
624+ final TextDirection textDirection;
613625 final double tapTargetVerticalPadding;
614626 final bool isExpanded;
615627
@@ -619,7 +631,8 @@ class _SegmentedButtonRenderWidget<T> extends MultiChildRenderObjectWidget {
619631 segments: segments,
620632 enabledBorder: enabledBorder,
621633 disabledBorder: disabledBorder,
622- textDirection: direction,
634+ textDirection: textDirection,
635+ direction: direction,
623636 tapTargetVerticalPadding: tapTargetVerticalPadding,
624637 isExpanded: isExpanded,
625638 );
@@ -631,7 +644,8 @@ class _SegmentedButtonRenderWidget<T> extends MultiChildRenderObjectWidget {
631644 ..segments = segments
632645 ..enabledBorder = enabledBorder
633646 ..disabledBorder = disabledBorder
634- ..textDirection = direction;
647+ ..direction = direction
648+ ..textDirection = textDirection;
635649 }
636650}
637651
@@ -651,10 +665,12 @@ class _RenderSegmentedButton<T> extends RenderBox with
651665 required TextDirection textDirection,
652666 required double tapTargetVerticalPadding,
653667 required bool isExpanded,
668+ required Axis direction,
654669 }) : _segments = segments,
655670 _enabledBorder = enabledBorder,
656671 _disabledBorder = disabledBorder,
657672 _textDirection = textDirection,
673+ _direction = direction,
658674 _tapTargetVerticalPadding = tapTargetVerticalPadding,
659675 _isExpanded = isExpanded;
660676
@@ -698,6 +714,16 @@ class _RenderSegmentedButton<T> extends RenderBox with
698714 markNeedsLayout ();
699715 }
700716
717+ Axis get direction => _direction;
718+ Axis _direction;
719+ set direction (Axis value) {
720+ if (value == _direction) {
721+ return ;
722+ }
723+ _direction = value;
724+ markNeedsLayout ();
725+ }
726+
701727 double get tapTargetVerticalPadding => _tapTargetVerticalPadding;
702728 double _tapTargetVerticalPadding;
703729 set tapTargetVerticalPadding (double value) {
@@ -787,17 +813,28 @@ class _RenderSegmentedButton<T> extends RenderBox with
787813 double start = 0.0 ;
788814 while (child != null ) {
789815 final _SegmentedButtonContainerBoxParentData childParentData = child.parentData! as _SegmentedButtonContainerBoxParentData ;
790- final Offset childOffset = Offset (start, 0.0 );
791- childParentData.offset = childOffset;
792- final Rect childRect = Rect .fromLTWH (start, 0.0 , child.size.width, child.size.height);
793- final RRect rChildRect = RRect .fromRectAndCorners (childRect);
816+ late final RRect rChildRect;
817+ if (direction == Axis .vertical) {
818+ childParentData.offset = Offset (0.0 , start);
819+ final Rect childRect = Rect .fromLTWH (0.0 , childParentData.offset.dy, child.size.width, child.size.height);
820+ rChildRect = RRect .fromRectAndCorners (childRect);
821+ start += child.size.height;
822+ } else {
823+ childParentData.offset = Offset (start, 0.0 );
824+ final Rect childRect = Rect .fromLTWH (start, 0.0 , child.size.width, child.size.height);
825+ rChildRect = RRect .fromRectAndCorners (childRect);
826+ start += child.size.width;
827+ }
794828 childParentData.surroundingRect = rChildRect;
795- start += child.size.width;
796829 child = nextChild (child);
797830 }
798831 }
799832
800833 Size _calculateChildSize (BoxConstraints constraints) {
834+ return direction == Axis .horizontal ? _calculateHorizontalChildSize (constraints) : _calculateVerticalChildSize (constraints);
835+ }
836+
837+ Size _calculateHorizontalChildSize (BoxConstraints constraints) {
801838 double maxHeight = 0 ;
802839 RenderBox ? child = firstChild;
803840 double childWidth;
@@ -820,7 +857,33 @@ class _RenderSegmentedButton<T> extends RenderBox with
820857 return Size (childWidth, maxHeight);
821858 }
822859
860+ Size _calculateVerticalChildSize (BoxConstraints constraints) {
861+ double maxWidth = 0 ;
862+ RenderBox ? child = firstChild;
863+ double childHeight;
864+ if (_isExpanded) {
865+ childHeight = constraints.maxHeight / childCount;
866+ } else {
867+ childHeight = constraints.minHeight / childCount;
868+ while (child != null ) {
869+ childHeight = math.max (childHeight, child.getMaxIntrinsicHeight (double .infinity));
870+ child = childAfter (child);
871+ }
872+ childHeight = math.min (childHeight, constraints.maxHeight / childCount);
873+ }
874+ child = firstChild;
875+ while (child != null ) {
876+ final double boxWidth = child.getMaxIntrinsicWidth (maxWidth);
877+ maxWidth = math.max (maxWidth, boxWidth);
878+ child = childAfter (child);
879+ }
880+ return Size (maxWidth, childHeight);
881+ }
882+
823883 Size _computeOverallSizeFromChildSize (Size childSize) {
884+ if (direction == Axis .vertical) {
885+ return constraints.constrain (Size (childSize.width, childSize.height * childCount));
886+ }
824887 return constraints.constrain (Size (childSize.width * childCount, childSize.height));
825888 }
826889
@@ -926,9 +989,17 @@ class _RenderSegmentedButton<T> extends RenderBox with
926989 final BorderSide divider = segments[index - 1 ].enabled || segments[index].enabled
927990 ? enabledBorder.side.copyWith (strokeAlign: 0.0 )
928991 : disabledBorder.side.copyWith (strokeAlign: 0.0 );
929- final Offset top = Offset (dividerPos, borderRect.top);
930- final Offset bottom = Offset (dividerPos, borderRect.bottom);
931- context.canvas.drawLine (top, bottom, divider.toPaint ());
992+ if (direction == Axis .horizontal) {
993+ final Offset top = Offset (dividerPos, borderRect.top);
994+ final Offset bottom = Offset (dividerPos, borderRect.bottom);
995+ context.canvas.drawLine (top, bottom, divider.toPaint ());
996+ } else if (direction == Axis .vertical) {
997+ final Offset start = Offset (borderRect.left, childRect.top);
998+ final Offset end = Offset (borderRect.right, childRect.top);
999+ context.canvas..save ()..clipPath (borderClipPath);
1000+ context.canvas.drawLine (start, end, divider.toPaint ());
1001+ context.canvas.restore ();
1002+ }
9321003 }
9331004
9341005 previousChild = child;
0 commit comments