|
@@ -0,0 +1,153 @@
|
|
|
+import 'package:fis_ui/index.dart';
|
|
|
+import 'package:flutter/material.dart';
|
|
|
+
|
|
|
+/// ### 按钮组容器
|
|
|
+/// - 用于将一组按钮设置为等宽
|
|
|
+///
|
|
|
+/// 限制:
|
|
|
+/// - 只能放入 Button 组件,当前支持的有:ElevatedButton、FOutlinedButton、ElevatedButtonWithIcon、FDelayedTiriggerButton
|
|
|
+/// - 按钮的 child 当前只支持单个 FText 组件
|
|
|
+/// - 按钮的 padding 需要设为0,如下:
|
|
|
+/// ```dart
|
|
|
+/// FOutlinedButton(
|
|
|
+/// style: ButtonStyle(
|
|
|
+/// padding: MaterialStatePropertyAll(EdgeInsets.all(0)),
|
|
|
+/// ),
|
|
|
+/// child: FText("Text..."),
|
|
|
+/// ...
|
|
|
+/// );
|
|
|
+/// ```
|
|
|
+/// e.g.
|
|
|
+class ButtonGroup extends StatelessWidget {
|
|
|
+ static const List<Type> supportedButtonTypes = [
|
|
|
+ FElevatedButtonWithIcon,
|
|
|
+ ElevatedButton,
|
|
|
+ OutlinedButton,
|
|
|
+ ];
|
|
|
+ ButtonGroup(
|
|
|
+ {super.key,
|
|
|
+ required this.children,
|
|
|
+ this.spacing = 10.0,
|
|
|
+ this.buttonPadding = 16.0})
|
|
|
+ : assert(
|
|
|
+ children.every((e) => supportedButtonTypes.contains(e.runtimeType)),
|
|
|
+ 'FButtonGroup children must be Supported');
|
|
|
+ final List<Widget> children;
|
|
|
+
|
|
|
+ /// 按钮间距
|
|
|
+ final double spacing;
|
|
|
+
|
|
|
+ /// 按钮水平内边距
|
|
|
+ final double buttonPadding;
|
|
|
+
|
|
|
+ /// 按钮组件自带的边框宽度
|
|
|
+ static const double _defaultBorderWidth = 1.0;
|
|
|
+
|
|
|
+ /// 遍历获取到每个按钮的宽度,然后取最大值
|
|
|
+ double getMaxWidth() {
|
|
|
+ double maxWidth = 0;
|
|
|
+ for (var i = 0; i < children.length; i++) {
|
|
|
+ var child = children[i];
|
|
|
+ if (child is FElevatedButtonWithIcon) {
|
|
|
+ final double innerSpacing = 8.0;
|
|
|
+ var text = child.label;
|
|
|
+ var icon = child.icon;
|
|
|
+ var iconWidth = 24.0;
|
|
|
+ if (icon is Icon) {
|
|
|
+ iconWidth = getIconWidth(icon);
|
|
|
+ }
|
|
|
+ if (text is Text) {
|
|
|
+ var width = getTextWidth(text) +
|
|
|
+ iconWidth +
|
|
|
+ innerSpacing +
|
|
|
+ _defaultBorderWidth * 2;
|
|
|
+ if (width > maxWidth) {
|
|
|
+ /// 一般情况下 Icon 与 Text 之间存在间距,所以这里需要加上间距
|
|
|
+ maxWidth = width;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (child is ElevatedButton) {
|
|
|
+ var text = child.child;
|
|
|
+ if (text is Text) {
|
|
|
+ var width = getTextWidth(text) + _defaultBorderWidth * 2;
|
|
|
+ if (width > maxWidth) {
|
|
|
+ maxWidth = width;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (child is FOutlinedButton) {
|
|
|
+ var text = child.child;
|
|
|
+ if (text is Text) {
|
|
|
+ var width = getTextWidth(text) + _defaultBorderWidth * 2;
|
|
|
+ if (width > maxWidth) {
|
|
|
+ maxWidth = width;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } else if (child is FDelayedTiriggerButton) {
|
|
|
+ var text = child.child;
|
|
|
+ if (text is Text) {
|
|
|
+ var width = getTextWidth(text) + _defaultBorderWidth * 2 + 16;
|
|
|
+ if (width > maxWidth) {
|
|
|
+ maxWidth = width;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return maxWidth;
|
|
|
+ }
|
|
|
+
|
|
|
+ double getTextWidth(Text text) {
|
|
|
+ final TextStyle style = text.style ?? TextStyle();
|
|
|
+ var textPainter = TextPainter(
|
|
|
+ text: TextSpan(
|
|
|
+ style: style,
|
|
|
+ // style.copyWith(fontFamily: FTheme.ins.localeSetting.fontFamily),
|
|
|
+ text: text.data),
|
|
|
+ textDirection: TextDirection.ltr,
|
|
|
+ maxLines: 1,
|
|
|
+ textScaleFactor: 1);
|
|
|
+ textPainter.layout();
|
|
|
+ var width = textPainter.width;
|
|
|
+ return width;
|
|
|
+ }
|
|
|
+
|
|
|
+ double getIconWidth(Icon icon) {
|
|
|
+ final double width = icon.size ?? 24.0;
|
|
|
+ return width;
|
|
|
+ }
|
|
|
+
|
|
|
+ double get totalWidth =>
|
|
|
+ getMaxWidth() * children.length +
|
|
|
+ buttonPadding * 2 * children.length +
|
|
|
+ spacing * (children.length - 1);
|
|
|
+
|
|
|
+ @override
|
|
|
+ build(BuildContext context) {
|
|
|
+ return ConstrainedBox(
|
|
|
+ constraints: BoxConstraints.tightFor(width: totalWidth),
|
|
|
+ child: Row(
|
|
|
+ mainAxisAlignment: MainAxisAlignment.center,
|
|
|
+ mainAxisSize: MainAxisSize.max,
|
|
|
+ children: children.map((e) {
|
|
|
+ // ignore: unnecessary_cast
|
|
|
+ return Expanded(child: e) as Widget;
|
|
|
+ }).toList()
|
|
|
+ // .superJoin(FSizedBox(width: spacing)),
|
|
|
+ ),
|
|
|
+ );
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+extension ListExt<Widget> on List<Widget> {
|
|
|
+ List<Widget> superJoin(Widget separator) {
|
|
|
+ final iterator = this.iterator;
|
|
|
+ if (!iterator.moveNext()) return [];
|
|
|
+
|
|
|
+ final l = [iterator.current];
|
|
|
+ while (iterator.moveNext()) {
|
|
|
+ l
|
|
|
+ ..add(separator)
|
|
|
+ ..add(iterator.current);
|
|
|
+ }
|
|
|
+ return l;
|
|
|
+ }
|
|
|
+}
|