"Fossies" - the Fresh Open Source Software Archive

Member "flutter-1.22.4/packages/flutter/lib/src/material/toggle_buttons.dart" (13 Nov 2020, 44350 Bytes) of package /linux/misc/flutter-1.22.4.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Dart source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file.

    1 // Copyright 2014 The Flutter Authors. All rights reserved.
    2 // Use of this source code is governed by a BSD-style license that can be
    3 // found in the LICENSE file.
    4 
    5 // @dart = 2.8
    6 
    7 import 'dart:math' as math;
    8 
    9 import 'package:flutter/foundation.dart';
   10 import 'package:flutter/rendering.dart';
   11 import 'package:flutter/widgets.dart';
   12 
   13 import 'button.dart';
   14 import 'constants.dart';
   15 import 'debug.dart';
   16 import 'theme.dart';
   17 import 'theme_data.dart';
   18 import 'toggle_buttons_theme.dart';
   19 
   20 /// A horizontal set of toggle buttons.
   21 ///
   22 /// The list of [children] are laid out in a row. The state of each button
   23 /// is controlled by [isSelected], which is a list of bools that determine
   24 /// if a button is in an unselected or selected state. They are both
   25 /// correlated by their index in the list. The length of [isSelected] has to
   26 /// match the length of the [children] list.
   27 ///
   28 /// {@youtube 560 315 https://www.youtube.com/watch?v=kVEguaQWGAY}
   29 ///
   30 /// ## Customizing toggle buttons
   31 /// Each toggle's behavior can be configured by the [onPressed] callback, which
   32 /// can update the [isSelected] list however it wants to.
   33 ///
   34 /// {@animation 700 150 https://flutter.github.io/assets-for-api-docs/assets/material/toggle_buttons_simple.mp4}
   35 ///
   36 /// Here is an implementation that allows for multiple buttons to be
   37 /// simultaneously selected, while requiring none of the buttons to be
   38 /// selected.
   39 /// ```dart
   40 /// ToggleButtons(
   41 ///   children: <Widget>[
   42 ///     Icon(Icons.ac_unit),
   43 ///     Icon(Icons.call),
   44 ///     Icon(Icons.cake),
   45 ///   ],
   46 ///   onPressed: (int index) {
   47 ///     setState(() {
   48 ///       isSelected[index] = !isSelected[index];
   49 ///     });
   50 ///   },
   51 ///   isSelected: isSelected,
   52 /// ),
   53 /// ```
   54 ///
   55 /// {@animation 700 150 https://flutter.github.io/assets-for-api-docs/assets/material/toggle_buttons_required_mutually_exclusive.mp4}
   56 ///
   57 /// Here is an implementation that requires mutually exclusive selection
   58 /// while requiring at least one selection. Note that this assumes that
   59 /// [isSelected] was properly initialized with one selection.
   60 /// ```dart
   61 /// ToggleButtons(
   62 ///   children: <Widget>[
   63 ///     Icon(Icons.ac_unit),
   64 ///     Icon(Icons.call),
   65 ///     Icon(Icons.cake),
   66 ///   ],
   67 ///   onPressed: (int index) {
   68 ///     setState(() {
   69 ///       for (int buttonIndex = 0; buttonIndex < isSelected.length; buttonIndex++) {
   70 ///         if (buttonIndex == index) {
   71 ///           isSelected[buttonIndex] = true;
   72 ///         } else {
   73 ///           isSelected[buttonIndex] = false;
   74 ///         }
   75 ///       }
   76 ///     });
   77 ///   },
   78 ///   isSelected: isSelected,
   79 /// ),
   80 /// ```
   81 ///
   82 /// {@animation 700 150 https://flutter.github.io/assets-for-api-docs/assets/material/toggle_buttons_mutually_exclusive.mp4}
   83 ///
   84 /// Here is an implementation that requires mutually exclusive selection,
   85 /// but allows for none of the buttons to be selected.
   86 /// ```dart
   87 /// ToggleButtons(
   88 ///   children: <Widget>[
   89 ///     Icon(Icons.ac_unit),
   90 ///     Icon(Icons.call),
   91 ///     Icon(Icons.cake),
   92 ///   ],
   93 ///   onPressed: (int index) {
   94 ///     setState(() {
   95 ///       for (int buttonIndex = 0; buttonIndex < isSelected.length; buttonIndex++) {
   96 ///         if (buttonIndex == index) {
   97 ///           isSelected[buttonIndex] = !isSelected[buttonIndex];
   98 ///         } else {
   99 ///           isSelected[buttonIndex] = false;
  100 ///         }
  101 ///       }
  102 ///     });
  103 ///   },
  104 ///   isSelected: isSelected,
  105 /// ),
  106 /// ```
  107 ///
  108 /// {@animation 700 150 https://flutter.github.io/assets-for-api-docs/assets/material/toggle_buttons_required.mp4}
  109 ///
  110 /// Here is an implementation that allows for multiple buttons to be
  111 /// simultaneously selected, while requiring at least one selection. Note
  112 /// that this assumes that [isSelected] was properly initialized with one
  113 /// selection.
  114 /// ```dart
  115 /// ToggleButtons(
  116 ///   children: <Widget>[
  117 ///     Icon(Icons.ac_unit),
  118 ///     Icon(Icons.call),
  119 ///     Icon(Icons.cake),
  120 ///   ],
  121 ///   onPressed: (int index) {
  122 ///     int count = 0;
  123 ///     isSelected.forEach((bool val) {
  124 ///       if (val) count++;
  125 ///     });
  126 ///
  127 ///     if (isSelected[index] && count < 2)
  128 ///       return;
  129 ///
  130 ///     setState(() {
  131 ///       isSelected[index] = !isSelected[index];
  132 ///     });
  133 ///   },
  134 ///   isSelected: isSelected,
  135 /// ),
  136 /// ```
  137 ///
  138 /// ## ToggleButton Borders
  139 /// The toggle buttons, by default, have a solid, 1 logical pixel border
  140 /// surrounding itself and separating each button. The toggle button borders'
  141 /// color, width, and corner radii are configurable.
  142 ///
  143 /// The [selectedBorderColor] determines the border's color when the button is
  144 /// selected, while [disabledBorderColor] determines the border's color when
  145 /// the button is disabled. [borderColor] is used when the button is enabled.
  146 ///
  147 /// To remove the border, set [borderWidth] to null. Setting [borderWidth] to
  148 /// 0.0 results in a hairline border. For more information on hairline borders,
  149 /// see [BorderSide.width].
  150 ///
  151 /// See also:
  152 ///
  153 ///  * <https://material.io/design/components/buttons.html#toggle-button>
  154 class ToggleButtons extends StatelessWidget {
  155   /// Creates a horizontal set of toggle buttons.
  156   ///
  157   /// It displays its widgets provided in a [List] of [children] horizontally.
  158   /// The state of each button is controlled by [isSelected], which is a list
  159   /// of bools that determine if a button is in an active, disabled, or
  160   /// selected state. They are both correlated by their index in the list.
  161   /// The length of [isSelected] has to match the length of the [children]
  162   /// list.
  163   ///
  164   /// Both [children] and [isSelected] properties arguments are required.
  165   ///
  166   /// [isSelected] values must be non-null. [focusNodes] must be null or a
  167   /// list of non-null nodes. [renderBorder] must not be null.
  168   const ToggleButtons({
  169     Key key,
  170     @required this.children,
  171     @required this.isSelected,
  172     this.onPressed,
  173     this.mouseCursor,
  174     this.textStyle,
  175     this.constraints,
  176     this.color,
  177     this.selectedColor,
  178     this.disabledColor,
  179     this.fillColor,
  180     this.focusColor,
  181     this.highlightColor,
  182     this.hoverColor,
  183     this.splashColor,
  184     this.focusNodes,
  185     this.renderBorder = true,
  186     this.borderColor,
  187     this.selectedBorderColor,
  188     this.disabledBorderColor,
  189     this.borderRadius,
  190     this.borderWidth,
  191   }) :
  192     assert(children != null),
  193     assert(isSelected != null),
  194     assert(children.length == isSelected.length),
  195     super(key: key);
  196 
  197   static const double _defaultBorderWidth = 1.0;
  198 
  199   /// The toggle button widgets.
  200   ///
  201   /// These are typically [Icon] or [Text] widgets. The boolean selection
  202   /// state of each widget is defined by the corresponding [isSelected]
  203   /// list item.
  204   ///
  205   /// The length of children has to match the length of [isSelected]. If
  206   /// [focusNodes] is not null, the length of children has to also match
  207   /// the length of [focusNodes].
  208   final List<Widget> children;
  209 
  210   /// The corresponding selection state of each toggle button.
  211   ///
  212   /// Each value in this list represents the selection state of the [children]
  213   /// widget at the same index.
  214   ///
  215   /// The length of [isSelected] has to match the length of [children].
  216   final List<bool> isSelected;
  217 
  218   /// The callback that is called when a button is tapped.
  219   ///
  220   /// The index parameter of the callback is the index of the button that is
  221   /// tapped or otherwise activated.
  222   ///
  223   /// When the callback is null, all toggle buttons will be disabled.
  224   final void Function(int index) onPressed;
  225 
  226   /// {@macro flutter.material.button.mouseCursor}
  227   final MouseCursor mouseCursor;
  228 
  229   /// The [TextStyle] to apply to any text in these toggle buttons.
  230   ///
  231   /// [TextStyle.color] will be ignored and substituted by [color],
  232   /// [selectedColor] or [disabledColor] depending on whether the buttons
  233   /// are active, selected, or disabled.
  234   final TextStyle textStyle;
  235 
  236   /// Defines the button's size.
  237   ///
  238   /// Typically used to constrain the button's minimum size.
  239   ///
  240   /// If this property is null, then
  241   /// BoxConstraints(minWidth: 48.0, minHeight: 48.0) is be used.
  242   final BoxConstraints constraints;
  243 
  244   /// The color for descendant [Text] and [Icon] widgets if the button is
  245   /// enabled and not selected.
  246   ///
  247   /// If [onPressed] is not null, this color will be used for values in
  248   /// [isSelected] that are false.
  249   ///
  250   /// If this property is null, then ToggleButtonTheme.of(context).color
  251   /// is used. If [ToggleButtonsThemeData.color] is also null, then
  252   /// Theme.of(context).colorScheme.onSurface is used.
  253   final Color color;
  254 
  255   /// The color for descendant [Text] and [Icon] widgets if the button is
  256   /// selected.
  257   ///
  258   /// If [onPressed] is not null, this color will be used for values in
  259   /// [isSelected] that are true.
  260   ///
  261   /// If this property is null, then
  262   /// ToggleButtonTheme.of(context).selectedColor is used. If
  263   /// [ToggleButtonsThemeData.selectedColor] is also null, then
  264   /// Theme.of(context).colorScheme.primary is used.
  265   final Color selectedColor;
  266 
  267   /// The color for descendant [Text] and [Icon] widgets if the button is
  268   /// disabled.
  269   ///
  270   /// If [onPressed] is null, this color will be used.
  271   ///
  272   /// If this property is null, then
  273   /// ToggleButtonTheme.of(context).disabledColor is used. If
  274   /// [ToggleButtonsThemeData.disabledColor] is also null, then
  275   /// Theme.of(context).colorScheme.onSurface.withOpacity(0.38) is used.
  276   final Color disabledColor;
  277 
  278   /// The fill color for selected toggle buttons.
  279   ///
  280   /// If this property is null, then
  281   /// ToggleButtonTheme.of(context).fillColor is used. If
  282   /// [ToggleButtonsThemeData.fillColor] is also null, then
  283   /// the fill color is null.
  284   final Color fillColor;
  285 
  286   /// The color to use for filling the button when the button has input focus.
  287   ///
  288   /// If this property is null, then
  289   /// ToggleButtonTheme.of(context).focusColor is used. If
  290   /// [ToggleButtonsThemeData.focusColor] is also null, then
  291   /// Theme.of(context).focusColor is used.
  292   final Color focusColor;
  293 
  294   /// The highlight color for the button's [InkWell].
  295   ///
  296   /// If this property is null, then
  297   /// ToggleButtonTheme.of(context).highlightColor is used. If
  298   /// [ToggleButtonsThemeData.highlightColor] is also null, then
  299   /// Theme.of(context).highlightColor is used.
  300   final Color highlightColor;
  301 
  302   /// The splash color for the button's [InkWell].
  303   ///
  304   /// If this property is null, then
  305   /// ToggleButtonTheme.of(context).splashColor is used. If
  306   /// [ToggleButtonsThemeData.splashColor] is also null, then
  307   /// Theme.of(context).splashColor is used.
  308   final Color splashColor;
  309 
  310   /// The color to use for filling the button when the button has a pointer
  311   /// hovering over it.
  312   ///
  313   /// If this property is null, then
  314   /// ToggleButtonTheme.of(context).hoverColor is used. If
  315   /// [ToggleButtonsThemeData.hoverColor] is also null, then
  316   /// Theme.of(context).hoverColor is used.
  317   final Color hoverColor;
  318 
  319   /// The list of [FocusNode]s, corresponding to each toggle button.
  320   ///
  321   /// Focus is used to determine which widget should be affected by keyboard
  322   /// events. The focus tree keeps track of which widget is currently focused
  323   /// on by the user.
  324   ///
  325   /// If not null, the length of focusNodes has to match the length of
  326   /// [children].
  327   ///
  328   /// See [FocusNode] for more information about how focus nodes are used.
  329   final List<FocusNode> focusNodes;
  330 
  331   /// Whether or not to render a border around each toggle button.
  332   ///
  333   /// When true, a border with [borderWidth], [borderRadius] and the
  334   /// appropriate border color will render. Otherwise, no border will be
  335   /// rendered.
  336   final bool renderBorder;
  337 
  338   /// The border color to display when the toggle button is enabled and not
  339   /// selected.
  340   ///
  341   /// If this property is null, then
  342   /// ToggleButtonTheme.of(context).borderColor is used. If
  343   /// [ToggleButtonsThemeData.borderColor] is also null, then
  344   /// Theme.of(context).colorScheme.onSurface is used.
  345   final Color borderColor;
  346 
  347   /// The border color to display when the toggle button is selected.
  348   ///
  349   /// If this property is null, then
  350   /// ToggleButtonTheme.of(context).selectedBorderColor is used. If
  351   /// [ToggleButtonsThemeData.selectedBorderColor] is also null, then
  352   /// Theme.of(context).colorScheme.primary is used.
  353   final Color selectedBorderColor;
  354 
  355   /// The border color to display when the toggle button is disabled.
  356   ///
  357   /// If this property is null, then
  358   /// ToggleButtonTheme.of(context).disabledBorderColor is used. If
  359   /// [ToggleButtonsThemeData.disabledBorderColor] is also null, then
  360   /// Theme.of(context).disabledBorderColor is used.
  361   final Color disabledBorderColor;
  362 
  363   /// The width of the border surrounding each toggle button.
  364   ///
  365   /// This applies to both the greater surrounding border, as well as the
  366   /// borders rendered between toggle buttons.
  367   ///
  368   /// To render a hairline border (one physical pixel), set borderWidth to 0.0.
  369   /// See [BorderSide.width] for more details on hairline borders.
  370   ///
  371   /// To omit the border entirely, set [renderBorder] to false.
  372   ///
  373   /// If this property is null, then
  374   /// ToggleButtonTheme.of(context).borderWidth is used. If
  375   /// [ToggleButtonsThemeData.borderWidth] is also null, then
  376   /// a width of 1.0 is used.
  377   final double borderWidth;
  378 
  379   /// The radii of the border's corners.
  380   ///
  381   /// If this property is null, then
  382   /// ToggleButtonTheme.of(context).borderRadius is used. If
  383   /// [ToggleButtonsThemeData.borderRadius] is also null, then
  384   /// the buttons default to non-rounded borders.
  385   final BorderRadius borderRadius;
  386 
  387   bool _isFirstIndex(int index, int length, TextDirection textDirection) {
  388     return index == 0 && textDirection == TextDirection.ltr
  389         || index == length - 1 && textDirection == TextDirection.rtl;
  390   }
  391 
  392   bool _isLastIndex(int index, int length, TextDirection textDirection) {
  393     return index == length - 1 && textDirection == TextDirection.ltr
  394         || index == 0 && textDirection == TextDirection.rtl;
  395   }
  396 
  397   BorderRadius _getEdgeBorderRadius(
  398     int index,
  399     int length,
  400     TextDirection textDirection,
  401     ToggleButtonsThemeData toggleButtonsTheme,
  402   ) {
  403     final BorderRadius resultingBorderRadius = borderRadius
  404       ?? toggleButtonsTheme.borderRadius
  405       ?? BorderRadius.zero;
  406 
  407     if (_isFirstIndex(index, length, textDirection)) {
  408       return BorderRadius.only(
  409         topLeft: resultingBorderRadius.topLeft,
  410         bottomLeft: resultingBorderRadius.bottomLeft,
  411       );
  412     } else if (_isLastIndex(index, length, textDirection)) {
  413       return BorderRadius.only(
  414         topRight: resultingBorderRadius.topRight,
  415         bottomRight: resultingBorderRadius.bottomRight,
  416       );
  417     }
  418     return BorderRadius.zero;
  419   }
  420 
  421   BorderRadius _getClipBorderRadius(
  422     int index,
  423     int length,
  424     TextDirection textDirection,
  425     ToggleButtonsThemeData toggleButtonsTheme,
  426   ) {
  427     final BorderRadius resultingBorderRadius = borderRadius
  428       ?? toggleButtonsTheme.borderRadius
  429       ?? BorderRadius.zero;
  430     final double resultingBorderWidth = borderWidth
  431       ?? toggleButtonsTheme.borderWidth
  432       ?? _defaultBorderWidth;
  433 
  434     if (_isFirstIndex(index, length, textDirection)) {
  435       return BorderRadius.only(
  436         topLeft: resultingBorderRadius.topLeft - Radius.circular(resultingBorderWidth / 2.0),
  437         bottomLeft: resultingBorderRadius.bottomLeft - Radius.circular(resultingBorderWidth / 2.0),
  438       );
  439     } else if (_isLastIndex(index, length, textDirection)) {
  440       return BorderRadius.only(
  441         topRight: resultingBorderRadius.topRight - Radius.circular(resultingBorderWidth / 2.0),
  442         bottomRight: resultingBorderRadius.bottomRight - Radius.circular(resultingBorderWidth / 2.0),
  443       );
  444     }
  445     return BorderRadius.zero;
  446   }
  447 
  448   BorderSide _getLeadingBorderSide(
  449     int index,
  450     ThemeData theme,
  451     ToggleButtonsThemeData toggleButtonsTheme,
  452   ) {
  453     if (!renderBorder)
  454       return BorderSide.none;
  455 
  456     final double resultingBorderWidth = borderWidth
  457       ?? toggleButtonsTheme.borderWidth
  458       ?? _defaultBorderWidth;
  459     if (onPressed != null && (isSelected[index] || (index != 0 && isSelected[index - 1]))) {
  460       return BorderSide(
  461         color: selectedBorderColor
  462           ?? toggleButtonsTheme.selectedBorderColor
  463           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  464         width: resultingBorderWidth,
  465       );
  466     } else if (onPressed != null && !isSelected[index]) {
  467       return BorderSide(
  468         color: borderColor
  469           ?? toggleButtonsTheme.borderColor
  470           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  471         width: resultingBorderWidth,
  472       );
  473     } else {
  474       return BorderSide(
  475         color: disabledBorderColor
  476           ?? toggleButtonsTheme.disabledBorderColor
  477           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  478         width: resultingBorderWidth,
  479       );
  480     }
  481   }
  482 
  483   BorderSide _getHorizontalBorderSide(
  484     int index,
  485     ThemeData theme,
  486     ToggleButtonsThemeData toggleButtonsTheme,
  487   ) {
  488     if (!renderBorder)
  489       return BorderSide.none;
  490 
  491     final double resultingBorderWidth = borderWidth
  492       ?? toggleButtonsTheme.borderWidth
  493       ?? _defaultBorderWidth;
  494     if (onPressed != null && isSelected[index]) {
  495       return BorderSide(
  496         color: selectedBorderColor
  497           ?? toggleButtonsTheme.selectedBorderColor
  498           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  499         width: resultingBorderWidth,
  500       );
  501     } else if (onPressed != null && !isSelected[index]) {
  502       return BorderSide(
  503         color: borderColor
  504           ?? toggleButtonsTheme.borderColor
  505           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  506         width: resultingBorderWidth,
  507       );
  508     } else {
  509       return BorderSide(
  510         color: disabledBorderColor
  511           ?? toggleButtonsTheme.disabledBorderColor
  512           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  513         width: resultingBorderWidth,
  514       );
  515     }
  516   }
  517 
  518   BorderSide _getTrailingBorderSide(
  519     int index,
  520     ThemeData theme,
  521     ToggleButtonsThemeData toggleButtonsTheme,
  522   ) {
  523     if (!renderBorder)
  524       return BorderSide.none;
  525 
  526     if (index != children.length - 1)
  527       return null;
  528 
  529     final double resultingBorderWidth = borderWidth
  530       ?? toggleButtonsTheme.borderWidth
  531       ?? _defaultBorderWidth;
  532     if (onPressed != null && (isSelected[index])) {
  533       return BorderSide(
  534         color: selectedBorderColor
  535           ?? toggleButtonsTheme.selectedBorderColor
  536           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  537         width: resultingBorderWidth,
  538       );
  539     } else if (onPressed != null && !isSelected[index]) {
  540       return BorderSide(
  541         color: borderColor
  542           ?? toggleButtonsTheme.borderColor
  543           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  544         width: resultingBorderWidth,
  545       );
  546     } else {
  547       return BorderSide(
  548         color: disabledBorderColor
  549           ?? toggleButtonsTheme.disabledBorderColor
  550           ?? theme.colorScheme.onSurface.withOpacity(0.12),
  551         width: resultingBorderWidth,
  552       );
  553     }
  554   }
  555 
  556   @override
  557   Widget build(BuildContext context) {
  558     assert(
  559       !isSelected.any((bool val) => val == null),
  560       'All elements of isSelected must be non-null.\n'
  561       'The current list of isSelected values is as follows:\n'
  562       '$isSelected'
  563     );
  564     assert(
  565       focusNodes == null || !focusNodes.any((FocusNode val) => val == null),
  566       'All elements of focusNodes must be non-null.\n'
  567       'The current list of focus node values is as follows:\n'
  568       '$focusNodes'
  569     );
  570     assert(
  571       () {
  572         if (focusNodes != null)
  573           return focusNodes.length == children.length;
  574         return true;
  575       }(),
  576       'focusNodes.length must match children.length.\n'
  577       'There are ${focusNodes.length} focus nodes, while '
  578       'there are ${children.length} children.'
  579     );
  580     final ThemeData theme = Theme.of(context);
  581     final ToggleButtonsThemeData toggleButtonsTheme = ToggleButtonsTheme.of(context);
  582     final TextDirection textDirection = Directionality.of(context);
  583 
  584     return IntrinsicHeight(
  585       child: Row(
  586         crossAxisAlignment: CrossAxisAlignment.stretch,
  587         mainAxisSize: MainAxisSize.min,
  588         children: List<Widget>.generate(children.length, (int index) {
  589           final BorderRadius edgeBorderRadius = _getEdgeBorderRadius(index, children.length, textDirection, toggleButtonsTheme);
  590           final BorderRadius clipBorderRadius = _getClipBorderRadius(index, children.length, textDirection, toggleButtonsTheme);
  591 
  592           final BorderSide leadingBorderSide = _getLeadingBorderSide(index, theme, toggleButtonsTheme);
  593           final BorderSide horizontalBorderSide = _getHorizontalBorderSide(index, theme, toggleButtonsTheme);
  594           final BorderSide trailingBorderSide = _getTrailingBorderSide(index, theme, toggleButtonsTheme);
  595 
  596           return _ToggleButton(
  597             selected: isSelected[index],
  598             textStyle: textStyle,
  599             constraints: constraints,
  600             color: color,
  601             selectedColor: selectedColor,
  602             disabledColor: disabledColor,
  603             fillColor: fillColor ?? toggleButtonsTheme.fillColor,
  604             focusColor: focusColor ?? toggleButtonsTheme.focusColor,
  605             highlightColor: highlightColor ?? toggleButtonsTheme.highlightColor,
  606             hoverColor: hoverColor ?? toggleButtonsTheme.hoverColor,
  607             splashColor: splashColor ?? toggleButtonsTheme.splashColor,
  608             focusNode: focusNodes != null ? focusNodes[index] : null,
  609             onPressed: onPressed != null
  610               ? () { onPressed(index); }
  611               : null,
  612             mouseCursor: mouseCursor,
  613             leadingBorderSide: leadingBorderSide,
  614             horizontalBorderSide: horizontalBorderSide,
  615             trailingBorderSide: trailingBorderSide,
  616             borderRadius: edgeBorderRadius,
  617             clipRadius: clipBorderRadius,
  618             isFirstButton: index == 0,
  619             isLastButton: index == children.length - 1,
  620             child: children[index],
  621           );
  622         }),
  623       ),
  624     );
  625   }
  626 
  627   @override
  628   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
  629     super.debugFillProperties(properties);
  630     properties.add(FlagProperty('disabled',
  631       value: onPressed == null,
  632       ifTrue: 'Buttons are disabled',
  633       ifFalse: 'Buttons are enabled',
  634     ));
  635     textStyle?.debugFillProperties(properties, prefix: 'textStyle.');
  636     properties.add(ColorProperty('color', color, defaultValue: null));
  637     properties.add(ColorProperty('selectedColor', selectedColor, defaultValue: null));
  638     properties.add(ColorProperty('disabledColor', disabledColor, defaultValue: null));
  639     properties.add(ColorProperty('fillColor', fillColor, defaultValue: null));
  640     properties.add(ColorProperty('focusColor', focusColor, defaultValue: null));
  641     properties.add(ColorProperty('highlightColor', highlightColor, defaultValue: null));
  642     properties.add(ColorProperty('hoverColor', hoverColor, defaultValue: null));
  643     properties.add(ColorProperty('splashColor', splashColor, defaultValue: null));
  644     properties.add(ColorProperty('borderColor', borderColor, defaultValue: null));
  645     properties.add(ColorProperty('selectedBorderColor', selectedBorderColor, defaultValue: null));
  646     properties.add(ColorProperty('disabledBorderColor', disabledBorderColor, defaultValue: null));
  647     properties.add(DiagnosticsProperty<BorderRadius>('borderRadius', borderRadius, defaultValue: null));
  648     properties.add(DoubleProperty('borderWidth', borderWidth, defaultValue: null));
  649   }
  650 }
  651 
  652 /// An individual toggle button, otherwise known as a segmented button.
  653 ///
  654 /// This button is used by [ToggleButtons] to implement a set of segmented controls.
  655 class _ToggleButton extends StatelessWidget {
  656   /// Creates a toggle button based on [RawMaterialButton].
  657   ///
  658   /// This class adds some logic to distinguish between enabled, active, and
  659   /// disabled states, to determine the appropriate colors to use.
  660   ///
  661   /// It takes in a [shape] property to modify the borders of the button,
  662   /// which is used by [ToggleButtons] to customize borders based on the
  663   /// order in which this button appears in the list.
  664   const _ToggleButton({
  665     Key key,
  666     this.selected = false,
  667     this.textStyle,
  668     this.constraints,
  669     this.color,
  670     this.selectedColor,
  671     this.disabledColor,
  672     this.fillColor,
  673     this.focusColor,
  674     this.highlightColor,
  675     this.hoverColor,
  676     this.splashColor,
  677     this.focusNode,
  678     this.onPressed,
  679     this.mouseCursor,
  680     this.leadingBorderSide,
  681     this.horizontalBorderSide,
  682     this.trailingBorderSide,
  683     this.borderRadius,
  684     this.clipRadius,
  685     this.isFirstButton,
  686     this.isLastButton,
  687     this.child,
  688   }) : super(key: key);
  689 
  690   /// Determines if the button is displayed as active/selected or enabled.
  691   final bool selected;
  692 
  693   /// The [TextStyle] to apply to any text that appears in this button.
  694   final TextStyle textStyle;
  695 
  696   /// Defines the button's size.
  697   ///
  698   /// Typically used to constrain the button's minimum size.
  699   final BoxConstraints constraints;
  700 
  701   /// The color for [Text] and [Icon] widgets if the button is enabled.
  702   ///
  703   /// If [selected] is false and [onPressed] is not null, this color will be used.
  704   final Color color;
  705 
  706   /// The color for [Text] and [Icon] widgets if the button is selected.
  707   ///
  708   /// If [selected] is true and [onPressed] is not null, this color will be used.
  709   final Color selectedColor;
  710 
  711   /// The color for [Text] and [Icon] widgets if the button is disabled.
  712   ///
  713   /// If [onPressed] is null, this color will be used.
  714   final Color disabledColor;
  715 
  716   /// The color of the button's [Material].
  717   final Color fillColor;
  718 
  719   /// The color for the button's [Material] when it has the input focus.
  720   final Color focusColor;
  721 
  722   /// The color for the button's [Material] when a pointer is hovering over it.
  723   final Color hoverColor;
  724 
  725   /// The highlight color for the button's [InkWell].
  726   final Color highlightColor;
  727 
  728   /// The splash color for the button's [InkWell].
  729   final Color splashColor;
  730 
  731   /// {@macro flutter.widgets.Focus.focusNode}
  732   final FocusNode focusNode;
  733 
  734   /// Called when the button is tapped or otherwise activated.
  735   ///
  736   /// If this is null, the button will be disabled, see [enabled].
  737   final VoidCallback onPressed;
  738 
  739   /// {@macro flutter.material.button.mouseCursor}
  740   final MouseCursor mouseCursor;
  741 
  742   /// The width and color of the button's leading side border.
  743   final BorderSide leadingBorderSide;
  744 
  745   /// The width and color of the button's top and bottom side borders.
  746   final BorderSide horizontalBorderSide;
  747 
  748   /// The width and color of the button's trailing side border.
  749   final BorderSide trailingBorderSide;
  750 
  751   /// The border radii of each corner of the button.
  752   final BorderRadius borderRadius;
  753 
  754   /// The corner radii used to clip the button's contents.
  755   ///
  756   /// This is used to have the button's contents be properly clipped taking
  757   /// the [borderRadius] and the border's width into account.
  758   final BorderRadius clipRadius;
  759 
  760   /// Whether or not this toggle button is the first button in the list.
  761   final bool isFirstButton;
  762 
  763   /// Whether or not this toggle button is the last button in the list.
  764   final bool isLastButton;
  765 
  766   /// The button's label, which is usually an [Icon] or a [Text] widget.
  767   final Widget child;
  768 
  769   @override
  770   Widget build(BuildContext context) {
  771     assert(debugCheckHasMaterial(context));
  772     Color currentColor;
  773     Color currentFillColor;
  774     Color currentFocusColor;
  775     Color currentHoverColor;
  776     Color currentSplashColor;
  777     final ThemeData theme = Theme.of(context);
  778     final ToggleButtonsThemeData toggleButtonsTheme = ToggleButtonsTheme.of(context);
  779 
  780     if (onPressed != null && selected) {
  781       currentColor = selectedColor
  782         ?? toggleButtonsTheme.selectedColor
  783         ?? theme.colorScheme.primary;
  784       currentFillColor = fillColor
  785         ?? theme.colorScheme.primary.withOpacity(0.12);
  786       currentFocusColor = focusColor
  787         ?? toggleButtonsTheme.focusColor
  788         ?? theme.colorScheme.primary.withOpacity(0.12);
  789       currentHoverColor = hoverColor
  790         ?? toggleButtonsTheme.hoverColor
  791         ?? theme.colorScheme.primary.withOpacity(0.04);
  792       currentSplashColor = splashColor
  793         ?? toggleButtonsTheme.splashColor
  794         ?? theme.colorScheme.primary.withOpacity(0.16);
  795     } else if (onPressed != null && !selected) {
  796       currentColor = color
  797         ?? toggleButtonsTheme.color
  798         ?? theme.colorScheme.onSurface.withOpacity(0.87);
  799       currentFillColor = theme.colorScheme.surface.withOpacity(0.0);
  800       currentFocusColor = focusColor
  801         ?? toggleButtonsTheme.focusColor
  802         ?? theme.colorScheme.onSurface.withOpacity(0.12);
  803       currentHoverColor = hoverColor
  804         ?? toggleButtonsTheme.hoverColor
  805         ?? theme.colorScheme.onSurface.withOpacity(0.04);
  806       currentSplashColor = splashColor
  807         ?? toggleButtonsTheme.splashColor
  808         ?? theme.colorScheme.onSurface.withOpacity(0.16);
  809     } else {
  810       currentColor = disabledColor
  811         ?? toggleButtonsTheme.disabledColor
  812         ?? theme.colorScheme.onSurface.withOpacity(0.38);
  813       currentFillColor = theme.colorScheme.surface.withOpacity(0.0);
  814     }
  815 
  816     final TextStyle currentTextStyle = textStyle ?? toggleButtonsTheme.textStyle ?? theme.textTheme.bodyText2;
  817     final BoxConstraints currentConstraints = constraints ?? toggleButtonsTheme.constraints ?? const BoxConstraints(minWidth: kMinInteractiveDimension, minHeight: kMinInteractiveDimension);
  818 
  819     final Widget result = ClipRRect(
  820       borderRadius: clipRadius,
  821       child: RawMaterialButton(
  822         textStyle: currentTextStyle.copyWith(
  823           color: currentColor,
  824         ),
  825         constraints: currentConstraints,
  826         elevation: 0.0,
  827         highlightElevation: 0.0,
  828         fillColor: currentFillColor,
  829         focusColor: currentFocusColor,
  830         highlightColor: highlightColor
  831           ?? theme.colorScheme.surface.withOpacity(0.0),
  832         hoverColor: currentHoverColor,
  833         splashColor: currentSplashColor,
  834         focusNode: focusNode,
  835         materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,
  836         onPressed: onPressed,
  837         mouseCursor: mouseCursor,
  838         child: child,
  839       ),
  840     );
  841 
  842     return _SelectToggleButton(
  843       key: key,
  844       leadingBorderSide: leadingBorderSide,
  845       horizontalBorderSide: horizontalBorderSide,
  846       trailingBorderSide: trailingBorderSide,
  847       borderRadius: borderRadius,
  848       isFirstButton: isFirstButton,
  849       isLastButton: isLastButton,
  850       child: result,
  851     );
  852   }
  853 
  854   @override
  855   void debugFillProperties(DiagnosticPropertiesBuilder properties) {
  856     super.debugFillProperties(properties);
  857     properties.add(FlagProperty('selected',
  858       value: selected,
  859       ifTrue: 'Button is selected',
  860       ifFalse: 'Button is unselected',
  861     ));
  862   }
  863 }
  864 
  865 class _SelectToggleButton extends SingleChildRenderObjectWidget {
  866   const _SelectToggleButton({
  867     Key key,
  868     Widget child,
  869     this.leadingBorderSide,
  870     this.horizontalBorderSide,
  871     this.trailingBorderSide,
  872     this.borderRadius,
  873     this.isFirstButton,
  874     this.isLastButton,
  875   }) : super(
  876     key: key,
  877     child: child,
  878   );
  879 
  880   // The width and color of the button's leading side border.
  881   final BorderSide leadingBorderSide;
  882 
  883   // The width and color of the button's top and bottom side borders.
  884   final BorderSide horizontalBorderSide;
  885 
  886   // The width and color of the button's trailing side border.
  887   final BorderSide trailingBorderSide;
  888 
  889   // The border radii of each corner of the button.
  890   final BorderRadius borderRadius;
  891 
  892   // Whether or not this toggle button is the first button in the list.
  893   final bool isFirstButton;
  894 
  895   // Whether or not this toggle button is the last button in the list.
  896   final bool isLastButton;
  897 
  898   @override
  899   _SelectToggleButtonRenderObject createRenderObject(BuildContext context) => _SelectToggleButtonRenderObject(
  900     leadingBorderSide,
  901     horizontalBorderSide,
  902     trailingBorderSide,
  903     borderRadius,
  904     isFirstButton,
  905     isLastButton,
  906     Directionality.of(context),
  907   );
  908 
  909   @override
  910   void updateRenderObject(BuildContext context, _SelectToggleButtonRenderObject renderObject) {
  911     renderObject
  912       ..leadingBorderSide = leadingBorderSide
  913       ..horizontalBorderSide = horizontalBorderSide
  914       ..trailingBorderSide = trailingBorderSide
  915       ..borderRadius = borderRadius
  916       ..isFirstButton = isFirstButton
  917       ..isLastButton = isLastButton
  918       ..textDirection = Directionality.of(context);
  919   }
  920 }
  921 
  922 class _SelectToggleButtonRenderObject extends RenderShiftedBox {
  923   _SelectToggleButtonRenderObject(
  924     this._leadingBorderSide,
  925     this._horizontalBorderSide,
  926     this._trailingBorderSide,
  927     this._borderRadius,
  928     this._isFirstButton,
  929     this._isLastButton,
  930     this._textDirection, [
  931     RenderBox child,
  932   ]) : super(child);
  933 
  934   // The width and color of the button's leading side border.
  935   BorderSide get leadingBorderSide => _leadingBorderSide;
  936   BorderSide _leadingBorderSide;
  937   set leadingBorderSide(BorderSide value) {
  938     if (_leadingBorderSide == value)
  939       return;
  940     _leadingBorderSide = value;
  941     markNeedsLayout();
  942   }
  943 
  944   // The width and color of the button's top and bottom side borders.
  945   BorderSide get horizontalBorderSide => _horizontalBorderSide;
  946   BorderSide _horizontalBorderSide;
  947   set horizontalBorderSide(BorderSide value) {
  948     if (_horizontalBorderSide == value)
  949       return;
  950     _horizontalBorderSide = value;
  951     markNeedsLayout();
  952   }
  953 
  954   // The width and color of the button's trailing side border.
  955   BorderSide get trailingBorderSide => _trailingBorderSide;
  956   BorderSide _trailingBorderSide;
  957   set trailingBorderSide(BorderSide value) {
  958     if (_trailingBorderSide == value)
  959       return;
  960     _trailingBorderSide = value;
  961     markNeedsLayout();
  962   }
  963 
  964   // The border radii of each corner of the button.
  965   BorderRadius get borderRadius => _borderRadius;
  966   BorderRadius _borderRadius;
  967   set borderRadius(BorderRadius value) {
  968     if (_borderRadius == value)
  969       return;
  970     _borderRadius = value;
  971     markNeedsLayout();
  972   }
  973 
  974   // Whether or not this toggle button is the first button in the list.
  975   bool get isFirstButton => _isFirstButton;
  976   bool _isFirstButton;
  977   set isFirstButton(bool value) {
  978     if (_isFirstButton == value)
  979       return;
  980     _isFirstButton = value;
  981     markNeedsLayout();
  982   }
  983 
  984   // Whether or not this toggle button is the last button in the list.
  985   bool get isLastButton => _isLastButton;
  986   bool _isLastButton;
  987   set isLastButton(bool value) {
  988     if (_isLastButton == value)
  989       return;
  990     _isLastButton = value;
  991     markNeedsLayout();
  992   }
  993 
  994   // The direction in which text flows for this application.
  995   TextDirection get textDirection => _textDirection;
  996   TextDirection _textDirection;
  997   set textDirection(TextDirection value) {
  998     if (_textDirection == value)
  999       return;
 1000     _textDirection = value;
 1001     markNeedsLayout();
 1002   }
 1003 
 1004   static double _maxHeight(RenderBox box, double width) {
 1005     return box == null ? 0.0 : box.getMaxIntrinsicHeight(width);
 1006   }
 1007 
 1008   static double _minWidth(RenderBox box, double height) {
 1009     return box == null ? 0.0 : box.getMinIntrinsicWidth(height);
 1010   }
 1011 
 1012   static double _maxWidth(RenderBox box, double height) {
 1013     return box == null ? 0.0 : box.getMaxIntrinsicWidth(height);
 1014   }
 1015 
 1016   @override
 1017   double computeDistanceToActualBaseline(TextBaseline baseline) {
 1018     // The baseline of this widget is the baseline of its child
 1019     return child.computeDistanceToActualBaseline(baseline) +
 1020       horizontalBorderSide.width;
 1021   }
 1022 
 1023   @override
 1024   double computeMaxIntrinsicHeight(double width) {
 1025     return horizontalBorderSide.width +
 1026       _maxHeight(child, width) +
 1027       horizontalBorderSide.width;
 1028   }
 1029 
 1030   @override
 1031   double computeMinIntrinsicHeight(double width) => computeMaxIntrinsicHeight(width);
 1032 
 1033   @override
 1034   double computeMaxIntrinsicWidth(double height) {
 1035     final double trailingWidth = trailingBorderSide == null ? 0.0 : trailingBorderSide.width;
 1036     return leadingBorderSide.width +
 1037            _maxWidth(child, height) +
 1038            trailingWidth;
 1039   }
 1040 
 1041   @override
 1042   double computeMinIntrinsicWidth(double height) {
 1043     final double trailingWidth = trailingBorderSide == null ? 0.0 : trailingBorderSide.width;
 1044     return leadingBorderSide.width +
 1045            _minWidth(child, height) +
 1046            trailingWidth;
 1047   }
 1048 
 1049   @override
 1050   void performLayout() {
 1051     if (child == null) {
 1052       size = constraints.constrain(Size(
 1053         leadingBorderSide.width + trailingBorderSide.width,
 1054         horizontalBorderSide.width * 2.0,
 1055       ));
 1056       return;
 1057     }
 1058 
 1059     final double trailingBorderOffset = isLastButton ? trailingBorderSide.width : 0.0;
 1060     double leftConstraint;
 1061     double rightConstraint;
 1062 
 1063     switch (textDirection) {
 1064       case TextDirection.ltr:
 1065         rightConstraint = trailingBorderOffset;
 1066         leftConstraint = leadingBorderSide.width;
 1067 
 1068         final BoxConstraints innerConstraints = constraints.deflate(
 1069           EdgeInsets.only(
 1070             left: leftConstraint,
 1071             top: horizontalBorderSide.width,
 1072             right: rightConstraint,
 1073             bottom: horizontalBorderSide.width,
 1074           ),
 1075         );
 1076 
 1077         child.layout(innerConstraints, parentUsesSize: true);
 1078         final BoxParentData childParentData = child.parentData as BoxParentData;
 1079         childParentData.offset = Offset(leadingBorderSide.width, leadingBorderSide.width);
 1080 
 1081         size = constraints.constrain(Size(
 1082           leftConstraint + child.size.width + rightConstraint,
 1083           horizontalBorderSide.width * 2.0 + child.size.height,
 1084         ));
 1085         break;
 1086       case TextDirection.rtl:
 1087         rightConstraint = leadingBorderSide.width;
 1088         leftConstraint = trailingBorderOffset;
 1089 
 1090         final BoxConstraints innerConstraints = constraints.deflate(
 1091           EdgeInsets.only(
 1092             left: leftConstraint,
 1093             top: horizontalBorderSide.width,
 1094             right: rightConstraint,
 1095             bottom: horizontalBorderSide.width,
 1096           ),
 1097         );
 1098 
 1099         child.layout(innerConstraints, parentUsesSize: true);
 1100         final BoxParentData childParentData = child.parentData as BoxParentData;
 1101 
 1102         if (isLastButton) {
 1103           childParentData.offset = Offset(trailingBorderOffset, trailingBorderOffset);
 1104         } else {
 1105           childParentData.offset = Offset(0, horizontalBorderSide.width);
 1106         }
 1107 
 1108         size = constraints.constrain(Size(
 1109           leftConstraint + child.size.width + rightConstraint,
 1110           horizontalBorderSide.width * 2.0 + child.size.height,
 1111         ));
 1112         break;
 1113     }
 1114   }
 1115 
 1116   @override
 1117   void paint(PaintingContext context, Offset offset) {
 1118     super.paint(context, offset);
 1119     final Offset bottomRight = size.bottomRight(offset);
 1120     final Rect outer = Rect.fromLTRB(offset.dx, offset.dy, bottomRight.dx, bottomRight.dy);
 1121     final Rect center = outer.deflate(horizontalBorderSide.width / 2.0);
 1122     const double sweepAngle = math.pi / 2.0;
 1123 
 1124     final RRect rrect = RRect.fromRectAndCorners(
 1125       center,
 1126       topLeft: borderRadius.topLeft,
 1127       topRight: borderRadius.topRight,
 1128       bottomLeft: borderRadius.bottomLeft,
 1129       bottomRight: borderRadius.bottomRight,
 1130     ).scaleRadii();
 1131 
 1132     final Rect tlCorner = Rect.fromLTWH(
 1133       rrect.left,
 1134       rrect.top,
 1135       rrect.tlRadiusX * 2.0,
 1136       rrect.tlRadiusY * 2.0,
 1137     );
 1138     final Rect blCorner = Rect.fromLTWH(
 1139       rrect.left,
 1140       rrect.bottom - (rrect.blRadiusY * 2.0),
 1141       rrect.blRadiusX * 2.0,
 1142       rrect.blRadiusY * 2.0,
 1143     );
 1144     final Rect trCorner = Rect.fromLTWH(
 1145       rrect.right - (rrect.trRadiusX * 2),
 1146       rrect.top,
 1147       rrect.trRadiusX * 2,
 1148       rrect.trRadiusY * 2,
 1149     );
 1150     final Rect brCorner = Rect.fromLTWH(
 1151       rrect.right - (rrect.brRadiusX * 2),
 1152       rrect.bottom - (rrect.brRadiusY * 2),
 1153       rrect.brRadiusX * 2,
 1154       rrect.brRadiusY * 2,
 1155     );
 1156 
 1157     final Paint leadingPaint = leadingBorderSide.toPaint();
 1158     switch (textDirection) {
 1159       case TextDirection.ltr:
 1160         if (isLastButton) {
 1161           final Path leftPath = Path()
 1162             ..moveTo(rrect.left, rrect.bottom + leadingBorderSide.width / 2)
 1163             ..lineTo(rrect.left, rrect.top - leadingBorderSide.width / 2);
 1164           context.canvas.drawPath(leftPath, leadingPaint);
 1165 
 1166           final Paint endingPaint = trailingBorderSide.toPaint();
 1167           final Path endingPath = Path()
 1168             ..moveTo(rrect.left + horizontalBorderSide.width / 2.0, rrect.top)
 1169             ..lineTo(rrect.right - rrect.trRadiusX, rrect.top)
 1170             ..addArc(trCorner, math.pi * 3.0 / 2.0, sweepAngle)
 1171             ..lineTo(rrect.right, rrect.bottom - rrect.brRadiusY)
 1172             ..addArc(brCorner, 0, sweepAngle)
 1173             ..lineTo(rrect.left + horizontalBorderSide.width / 2.0, rrect.bottom);
 1174           context.canvas.drawPath(endingPath, endingPaint);
 1175         } else if (isFirstButton) {
 1176           final Path leadingPath = Path()
 1177             ..moveTo(outer.right, rrect.bottom)
 1178             ..lineTo(rrect.left + rrect.blRadiusX, rrect.bottom)
 1179             ..addArc(blCorner, math.pi / 2.0, sweepAngle)
 1180             ..lineTo(rrect.left, rrect.top + rrect.tlRadiusY)
 1181             ..addArc(tlCorner, math.pi, sweepAngle)
 1182             ..lineTo(outer.right, rrect.top);
 1183           context.canvas.drawPath(leadingPath, leadingPaint);
 1184         } else {
 1185           final Path leadingPath = Path()
 1186             ..moveTo(rrect.left, rrect.bottom + leadingBorderSide.width / 2)
 1187             ..lineTo(rrect.left, rrect.top - leadingBorderSide.width / 2);
 1188           context.canvas.drawPath(leadingPath, leadingPaint);
 1189 
 1190           final Paint horizontalPaint = horizontalBorderSide.toPaint();
 1191           final Path horizontalPaths = Path()
 1192             ..moveTo(rrect.left + horizontalBorderSide.width / 2.0, rrect.top)
 1193             ..lineTo(outer.right - rrect.trRadiusX, rrect.top)
 1194             ..moveTo(rrect.left + horizontalBorderSide.width / 2.0 + rrect.tlRadiusX, rrect.bottom)
 1195             ..lineTo(outer.right - rrect.trRadiusX, rrect.bottom);
 1196           context.canvas.drawPath(horizontalPaths, horizontalPaint);
 1197         }
 1198         break;
 1199       case TextDirection.rtl:
 1200         if (isLastButton) {
 1201           final Path leadingPath = Path()
 1202             ..moveTo(rrect.right, rrect.bottom + leadingBorderSide.width / 2)
 1203             ..lineTo(rrect.right, rrect.top - leadingBorderSide.width / 2);
 1204           context.canvas.drawPath(leadingPath, leadingPaint);
 1205 
 1206           final Paint endingPaint = trailingBorderSide.toPaint();
 1207           final Path endingPath = Path()
 1208             ..moveTo(rrect.right - horizontalBorderSide.width / 2.0, rrect.top)
 1209             ..lineTo(rrect.left + rrect.tlRadiusX, rrect.top)
 1210             ..addArc(tlCorner, math.pi * 3.0 / 2.0, -sweepAngle)
 1211             ..lineTo(rrect.left, rrect.bottom - rrect.blRadiusY)
 1212             ..addArc(blCorner, math.pi, -sweepAngle)
 1213             ..lineTo(rrect.right - horizontalBorderSide.width / 2.0, rrect.bottom);
 1214           context.canvas.drawPath(endingPath, endingPaint);
 1215         } else if (isFirstButton) {
 1216           final Path leadingPath = Path()
 1217             ..moveTo(outer.left, rrect.bottom)
 1218             ..lineTo(rrect.right - rrect.brRadiusX, rrect.bottom)
 1219             ..addArc(brCorner, math.pi / 2.0, -sweepAngle)
 1220             ..lineTo(rrect.right, rrect.top + rrect.trRadiusY)
 1221             ..addArc(trCorner, 0, -sweepAngle)
 1222             ..lineTo(outer.left, rrect.top);
 1223           context.canvas.drawPath(leadingPath, leadingPaint);
 1224         } else {
 1225           final Path leadingPath = Path()
 1226             ..moveTo(rrect.right, rrect.bottom + leadingBorderSide.width / 2)
 1227             ..lineTo(rrect.right, rrect.top - leadingBorderSide.width / 2);
 1228           context.canvas.drawPath(leadingPath, leadingPaint);
 1229 
 1230           final Paint horizontalPaint = horizontalBorderSide.toPaint();
 1231           final Path horizontalPaths = Path()
 1232             ..moveTo(rrect.right - horizontalBorderSide.width / 2.0, rrect.top)
 1233             ..lineTo(outer.left - rrect.tlRadiusX, rrect.top)
 1234             ..moveTo(rrect.right - horizontalBorderSide.width / 2.0 + rrect.trRadiusX, rrect.bottom)
 1235             ..lineTo(outer.left - rrect.tlRadiusX, rrect.bottom);
 1236           context.canvas.drawPath(horizontalPaths, horizontalPaint);
 1237         }
 1238         break;
 1239     }
 1240   }
 1241 }