diff --git a/tdesign-component/demo_tool/all_build.sh b/tdesign-component/demo_tool/all_build.sh index 51a48fcef..8aa4e780f 100644 --- a/tdesign-component/demo_tool/all_build.sh +++ b/tdesign-component/demo_tool/all_build.sh @@ -64,6 +64,7 @@ ./bin/demo_tool generate --file ../lib/src/components/tree/td_tree_select.dart --name TDTreeSelect,TDSelectOption --folder-name tree-select --output ../example/assets/api/ --only-api # upload +./bin/demo_tool generate --file ../lib/src/components/upload/td_upload.dart --name TDUpload --folder-name upload --output ../example/assets/api/ --only-api # 数据展示 diff --git a/tdesign-component/example/assets/api/cell_api.md b/tdesign-component/example/assets/api/cell_api.md index de0adc738..0ec7639a0 100644 --- a/tdesign-component/example/assets/api/cell_api.md +++ b/tdesign-component/example/assets/api/cell_api.md @@ -7,6 +7,7 @@ | 参数 | 类型 | 默认值 | 说明 | | --- | --- | --- | --- | | leftIconColor | Color? | - | 左侧图标颜色 | +| rightIconColor | Color? | - | 右侧图标颜色 | | titleStyle | TextStyle? | - | 标题文字样式 | | requiredStyle | TextStyle? | - | 必填星号文字样式 | | descriptionStyle | TextStyle? | - | 内容描述文字样式 | @@ -16,6 +17,9 @@ | groupBorderedColor | Color? | - | 单元格组边框颜色 | | backgroundColor | Color? | - | 默认状态背景颜色 | | padding | EdgeInsets? | - | 单元格内边距 | +| cardBorderRadius | BorderRadius? | - | 卡片模式边框圆角 | +| cardPadding | EdgeInsets? | - | 卡片模式内边距 | +| titlePadding | EdgeInsets? | - | 单元格组标题内边距 | #### 工厂构造方法 diff --git a/tdesign-component/example/assets/api/date-time-picker_api.md b/tdesign-component/example/assets/api/date-time-picker_api.md index 2dc188bec..6dc7e8f4b 100644 --- a/tdesign-component/example/assets/api/date-time-picker_api.md +++ b/tdesign-component/example/assets/api/date-time-picker_api.md @@ -25,7 +25,8 @@ | showTitle | bool | true | 是否展示标题 | | pickerHeight | double | 200 | 选择器List的视窗高度,默认200 | | pickerItemCount | int | - | 选择器List视窗中item个数,pickerHeight / pickerItemCount即item高度 | -| onSelectedItemChanged | void Function(int index)? | - | 选择器选中项改变回调 | +| isTimeUnit | bool? | - | 是否时间显示 | +| onSelectedItemChanged | void Function(int wheelIndex, int index)? | - | 选择器选中项改变回调 | | key | | - | | ``` @@ -36,6 +37,6 @@ | 名称 | 返回类型 | 参数 | 说明 | | --- | --- | --- | --- | -| showDatePicker | | required null context, required String title, required DatePickerCallback? onConfirm, DatePickerCallback? onCancel, bool useYear, bool useMonth, bool useDay, bool useHour, bool useMinute, bool useSecond, bool useWeekDay, Color? barrierColor, List dateStart, List? dateEnd, List? initialDate, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, Duration duration, double pickerHeight, int pickerItemCount, | 显示时间选择器 | +| showDatePicker | | required null context, required String title, required DatePickerCallback? onConfirm, DatePickerCallback? onCancel, bool useYear, bool useMonth, bool useDay, bool useHour, bool useMinute, bool useSecond, bool useWeekDay, Color? barrierColor, List dateStart, List? dateEnd, List? initialDate, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, Duration duration, double pickerHeight, bool isTimeUnit, Function(int wheelIndex, int index)? onSelectedItemChanged, int pickerItemCount, | 显示时间选择器 | | showMultiPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required List> data, List? initialIndexes, Duration duration, Color? barrierColor, double pickerHeight, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, double? topPadding, int pickerItemCount, | 显示多级选择器 | | showMultiLinkedPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required Map data, required int columnNum, required List initialData, Duration duration, Color? barrierColor, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, double pickerHeight, Color? titleDividerColor, double? topPadding, int pickerItemCount, | 显示多级联动选择器 | diff --git a/tdesign-component/example/assets/api/dialog_api.md b/tdesign-component/example/assets/api/dialog_api.md index 232fec0a2..36dc8c0c4 100644 --- a/tdesign-component/example/assets/api/dialog_api.md +++ b/tdesign-component/example/assets/api/dialog_api.md @@ -31,6 +31,7 @@ | title | String | - | 标题内容 | | action | Function()? | - | 点击操作 | | titleColor | Color? | - | 标题颜色 | +| titleSize | double? | - | 字体大小 | | style | TDButtonStyle? | - | 按钮样式 | | type | TDButtonType? | - | 按钮类型 | | theme | TDButtonTheme? | - | 按钮类型 | @@ -206,6 +207,7 @@ | key | | - | | | buttonText | String? | - | 按钮文字 | | buttonTextColor | Color? | - | 按钮文字颜色 | +| buttonTextSize | double? | - | 按钮文字大小 | | buttonTextFontWeight | FontWeight? | FontWeight.w600 | 按钮文字粗细 | | buttonStyle | TDButtonStyle? | - | 按钮样式 | | buttonType | TDButtonType? | - | 按钮类型 | diff --git a/tdesign-component/example/assets/api/dropdown-menu_api.md b/tdesign-component/example/assets/api/dropdown-menu_api.md index bdcd6a2a5..389c66c56 100644 --- a/tdesign-component/example/assets/api/dropdown-menu_api.md +++ b/tdesign-component/example/assets/api/dropdown-menu_api.md @@ -13,7 +13,7 @@ | direction | TDDropdownMenuDirection? | TDDropdownMenuDirection.auto | 菜单展开方向(down、up、auto) | | duration | double? | 200.0 | 动画时长,毫秒 | | showOverlay | bool? | true | 是否显示遮罩层 | -| isScrollable | bool | false | 是否开启滚动列表 | +| isScrollable | bool? | false | 是否开启滚动列表 | | arrowIcon | IconData? | - | 自定义箭头图标 | | labelBuilder | LabelBuilder? | - | 自定义标签内容 | | onMenuOpened | ValueChanged? | - | 展开菜单事件 | @@ -21,6 +21,7 @@ | width | double? | - | menu的宽度 | | height | double? | 48 | menu的高度 | | tabBarAlign | MainAxisAlignment? | MainAxisAlignment.center | [TDDropdownItem.label]和[arrowIcon]/[TDDropdownItem.arrowIcon]的对齐方式 | +| decoration | Decoration? | - | 下拉菜单的装饰器 | ``` ``` diff --git a/tdesign-component/example/assets/api/picker_api.md b/tdesign-component/example/assets/api/picker_api.md index 6d88cd8d3..c0ccadc4c 100644 --- a/tdesign-component/example/assets/api/picker_api.md +++ b/tdesign-component/example/assets/api/picker_api.md @@ -79,6 +79,6 @@ | 名称 | 返回类型 | 参数 | 说明 | | --- | --- | --- | --- | -| showDatePicker | | required null context, required String title, required DatePickerCallback? onConfirm, DatePickerCallback? onCancel, bool useYear, bool useMonth, bool useDay, bool useHour, bool useMinute, bool useSecond, bool useWeekDay, Color? barrierColor, List dateStart, List? dateEnd, List? initialDate, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, Duration duration, double pickerHeight, int pickerItemCount, | 显示时间选择器 | +| showDatePicker | | required null context, required String title, required DatePickerCallback? onConfirm, DatePickerCallback? onCancel, bool useYear, bool useMonth, bool useDay, bool useHour, bool useMinute, bool useSecond, bool useWeekDay, Color? barrierColor, List dateStart, List? dateEnd, List? initialDate, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, Duration duration, double pickerHeight, bool isTimeUnit, Function(int wheelIndex, int index)? onSelectedItemChanged, int pickerItemCount, | 显示时间选择器 | | showMultiPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required List> data, List? initialIndexes, Duration duration, Color? barrierColor, double pickerHeight, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, Color? titleDividerColor, double? topPadding, int pickerItemCount, | 显示多级选择器 | | showMultiLinkedPicker | | required null context, String? title, required MultiPickerCallback? onConfirm, MultiPickerCallback? onCancel, required Map data, required int columnNum, required List initialData, Duration duration, Color? barrierColor, String? rightText, String? leftText, TextStyle? leftTextStyle, TextStyle? centerTextStyle, TextStyle? rightTextStyle, double pickerHeight, Color? titleDividerColor, double? topPadding, int pickerItemCount, | 显示多级联动选择器 | diff --git a/tdesign-component/example/assets/api/popup_api.md b/tdesign-component/example/assets/api/popup_api.md index dd98f8b79..0f7e9b96d 100644 --- a/tdesign-component/example/assets/api/popup_api.md +++ b/tdesign-component/example/assets/api/popup_api.md @@ -20,6 +20,7 @@ | opened | VoidCallback? | - | 打开后事件 | | close | VoidCallback? | - | 关闭前事件 | | barrierClick | VoidCallback? | - | 蒙层点击事件,仅在[modalBarrierFull]为false时触发 | +| focusMove | bool | false | 是否有输入框获取焦点时整体平移避免输入框被遮挡 | ``` ``` diff --git a/tdesign-component/example/assets/api/search_api.md b/tdesign-component/example/assets/api/search_api.md index df881270e..69269aa0e 100644 --- a/tdesign-component/example/assets/api/search_api.md +++ b/tdesign-component/example/assets/api/search_api.md @@ -11,6 +11,7 @@ | onTextChanged | TDSearchBarEvent? | - | 文字改变回调 | | onSubmitted | TDSearchBarEvent? | - | 提交回调 | | onEditComplete | TDSearchBarCallBack? | - | 编辑完成回调 | +| onInputClick | GestureTapCallback? | - | 输入框点击事件 | | autoHeight | bool | false | 是否自动计算高度 | | padding | EdgeInsets | const EdgeInsets.fromLTRB(16, 8, 16, 8) | 内部填充 | | autoFocus | bool | false | 是否自动获取焦点 | @@ -23,3 +24,6 @@ | onActionClick | TDSearchBarEvent? | - | 自定义操作回调 | | onClearClick | TDSearchBarClearEvent? | - | 自定义操作回调 | | focusNode | FocusNode? | - | 自定义焦点 | +| inputAction | TextInputAction? | - | 键盘动作类型 | +| enabled | bool? | - | 是否禁用 | +| readOnly | bool? | - | 是否只读 | diff --git a/tdesign-component/example/assets/api/upload_api.md b/tdesign-component/example/assets/api/upload_api.md new file mode 100644 index 000000000..ac0d3e5cf --- /dev/null +++ b/tdesign-component/example/assets/api/upload_api.md @@ -0,0 +1,17 @@ +## API +### TDUpload +#### 默认构造方法 + +| 参数 | 类型 | 默认值 | 说明 | +| --- | --- | --- | --- | +| key | | - | | +| max | int | 0 | 用于控制文件上传数量,0为不限制,仅在multiple为true时有效 | +| mediaType | List | const [TDUploadMediaType.image, TDUploadMediaType.video] | 支持上传的文件类型,图片或视频 | +| sizeLimit | double? | - | 图片大小限制,单位为KB | +| onCancel | VoidCallback? | - | 监听取消上传 | +| onError | TDUploadErrorEvent? | - | 监听获取资源错误 | +| onValidate | TDUploadValidatorEvent? | - | 监听文件校验出错 | +| onClick | TDUploadClickEvent? | - | 监听点击图片位 | +| files | List | - | 控制展示的文件列表 | +| onChange | TDUploadValueChangedEvent? | - | 监听添加或删除照片 | +| multiple | bool | false | 是否多选上传,默认false | diff --git a/tdesign-component/lib/src/components/search/td_search_bar.dart b/tdesign-component/lib/src/components/search/td_search_bar.dart index 2fcb91986..fa5b15f75 100644 --- a/tdesign-component/lib/src/components/search/td_search_bar.dart +++ b/tdesign-component/lib/src/components/search/td_search_bar.dart @@ -119,32 +119,21 @@ class TDSearchBar extends StatefulWidget { State createState() => _TDSearchBarState(); } -enum _TDSearchBarStatus { - unFocus, - animatingToFocus, - focused, - animatingToUnFocus -} - class _TDSearchBarState extends State with TickerProviderStateMixin { late FocusNode focusNode = FocusNode(); final TextEditingController controller = TextEditingController(); final GlobalKey _textFieldKey = GlobalKey(); - final GlobalKey _phKey = GlobalKey(); - late AnimationController _animationController; - Animation? _animation; bool clearBtnHide = true; bool cancelBtnHide = true; - late _TDSearchBarStatus _status; @override void initState() { super.initState(); if(widget.controller==null){ controller.addListener(() { var clearVisible = controller.text.isNotEmpty; - _updateClearBtnVisible(clearVisible!); + _updateClearBtnVisible(clearVisible); }); }else{ widget.controller?.addListener(() { @@ -152,35 +141,15 @@ class _TDSearchBarState extends State _updateClearBtnVisible(clearVisible!); }); } - _animationController = AnimationController( - duration: const Duration(milliseconds: 200), vsync: this); _updateFocusNode(); } void _updateFocusNode() { focusNode = widget.focusNode ?? focusNode; - _status = widget.autoFocus - ? _TDSearchBarStatus.focused - : _TDSearchBarStatus.unFocus; focusNode.addListener(() { setState(() { - _status = focusNode.hasFocus - ? _TDSearchBarStatus.focused - : _TDSearchBarStatus.unFocus; + cancelBtnHide = !focusNode.hasFocus; }); - _updateCancelBtnVisible(focusNode.hasFocus); - }); - _animationController.addStatusListener((status) { - if (status == AnimationStatus.completed) { - setState(() { - if (_status == _TDSearchBarStatus.animatingToFocus) { - _status = _TDSearchBarStatus.focused; - focusNode.requestFocus(); - } else if (_status == _TDSearchBarStatus.animatingToUnFocus) { - _status = _TDSearchBarStatus.unFocus; - } - }); - } }); } @@ -196,12 +165,6 @@ class _TDSearchBarState extends State }); } - void _updateCancelBtnVisible(bool visible) { - setState(() { - cancelBtnHide = !visible; - }); - } - void _cleanInputText(){ if(!(widget.onClearClick?.call(controller.text) ?? false)){ // 如果外部没处理,则走默认清除逻辑 @@ -268,12 +231,13 @@ class _TDSearchBarState extends State child: Container( margin: const EdgeInsets.only(bottom: 1),// 为了适配TextField与Text的差异,后续需要做通用适配 child: TextField( + key: _textFieldKey, controller: widget.controller??controller, autofocus: widget.autoFocus, cursorColor: TDTheme.of(context).brandNormalColor, cursorWidth: 1, - cursorHeight:widget.cursorHeight ??(widget.mediumStyle ? 16 : 18), + cursorHeight:widget.cursorHeight, textAlign: widget.alignment == TDSearchAlignment.center ? TextAlign.center : TextAlign.left, @@ -283,15 +247,17 @@ class _TDSearchBarState extends State onSubmitted: widget.onSubmitted, onEditingComplete: widget.onEditComplete, style: TextStyle( + textBaseline: TextBaseline.ideographic, fontSize: getSize(context)?.size, color: TDTheme.of(context).fontGyColor1), decoration: InputDecoration( - hintText: (_status != _TDSearchBarStatus.focused) - ? '' - : widget.placeHolder, + hintText: widget.placeHolder, hintStyle: TextStyle( fontSize: getSize(context)?.size, - color: TDTheme.of(context).fontGyColor3), + color: TDTheme.of(context).fontGyColor3, + textBaseline: TextBaseline.ideographic, + overflow: TextOverflow.ellipsis,), + hintMaxLines: 1, border: InputBorder.none, isCollapsed: true, filled: true, @@ -301,6 +267,7 @@ class _TDSearchBarState extends State textInputAction: widget.inputAction, readOnly:widget.readOnly??false, enabled:widget.enabled, + cursorOpacityAnimates: false, ), ), ), @@ -340,19 +307,7 @@ class _TDSearchBarState extends State if (widget.onTextChanged != null) { widget.onTextChanged!(''); } - if (_animation == null) { - focusNode.unfocus(); - setState(() { - _status = _TDSearchBarStatus.unFocus; - }); - return; - } - setState(() { - _status = _TDSearchBarStatus.animatingToUnFocus; - }); focusNode.unfocus(); - _animationController.reverse( - from: _animationController.upperBound); }, child: Container( padding: const EdgeInsets.only(left: 16), @@ -365,139 +320,8 @@ class _TDSearchBarState extends State ), ], ), - Offstage( - offstage:widget.controller==null?(_status == _TDSearchBarStatus.focused || - controller.text.isNotEmpty):(_status == _TDSearchBarStatus.focused ||widget.controller!.text.isNotEmpty), - child: GestureDetector( - onTap: () { - if (_status == _TDSearchBarStatus.animatingToFocus || - _status == _TDSearchBarStatus.animatingToUnFocus) { - return; - } - if (_animation == null) { - focusNode.requestFocus(); - setState(() { - _status = _TDSearchBarStatus.focused; - }); - return; - } - setState(() { - _status = _TDSearchBarStatus.animatingToFocus; - _animationController.forward(); - }); - }, - child: Stack( - children: [ - Container( - decoration: BoxDecoration( - color: TDTheme.of(context).grayColor1, - borderRadius: BorderRadius.circular( - widget.style == TDSearchStyle.square ? 4 : 28), - ), - ), - Container( - margin: const EdgeInsets.only(left: 12, right: 12), - alignment: Alignment.centerLeft, - child: _getPlaceHolderWidgets(), - ) - ], - )), - ), ]), ); } - Widget _getPlaceHolderWidgets() { - return LayoutBuilder(builder: (BuildContext context, BoxConstraints box) { - if (_animation != null) { - return Row( - children: [ - Icon( - TDIcons.search, - size: widget.mediumStyle ? 20 : 24, - color: TDTheme.of(context).fontGyColor3, - ), - Expanded( - child: SlideTransition( - position: _animation!, - child: Row( - key: _phKey, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - SizedBox( - width: (widget.mediumStyle ? 20 : 24) / 2, - ), - Expanded( - child: Row( - mainAxisAlignment: - widget.alignment == TDSearchAlignment.left - ? MainAxisAlignment.start - : MainAxisAlignment.center, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: box.maxWidth - 51, - ), - child: TDText( - widget.placeHolder, - font: getSize(context), - textColor: TDTheme.of(context).fontGyColor3, - maxLines: 1, - overflow: TextOverflow.ellipsis, - forceVerticalCenter: true, - ), - ) - ], - )), - const SizedBox( - width: 6, - ), - ], - ), - )) - ], - ); - } else { - return Row( - key: _phKey, - mainAxisSize: MainAxisSize.min, - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Icon( - TDIcons.search, - size: widget.mediumStyle ? 20 : 24, - color: TDTheme.of(context).fontGyColor3, - ), - const SizedBox( - width: 3, - ), - Expanded( - child: Row( - mainAxisAlignment: widget.alignment == TDSearchAlignment.left - ? MainAxisAlignment.start - : MainAxisAlignment.center, - children: [ - ConstrainedBox( - constraints: BoxConstraints( - maxWidth: box.maxWidth - 51, - ), - child: TDText( - widget.placeHolder ?? '', - font: getSize(context), - textColor: TDTheme.of(context).fontGyColor3, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ) - ], - )), - const SizedBox( - width: 6, - ), - ], - ); - } - }); - } } diff --git a/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart b/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart index b64f4cc80..0c84af790 100644 --- a/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart +++ b/tdesign-component/lib/src/components/tabs/td_horizontal_tab_bar.dart @@ -14,7 +14,7 @@ import '../../../tdesign_flutter.dart'; const double _kTabHeight = 46.0; const double _kTextAndIconTabHeight = 72.0; - +const double _kStartOffset = 52.0; class _TabStyle extends AnimatedWidget { const _TabStyle({ Key? key, @@ -125,6 +125,7 @@ class TDHorizontalTabBar extends StatefulWidget implements PreferredSizeWidget { this.backgroundColor, this.selectedBgColor, this.unSelectedBgColor, + this.tabAlignment }) : assert(indicator != null || (indicatorWeight > 0.0)), super(key: key); @@ -321,6 +322,7 @@ class TDHorizontalTabBar extends StatefulWidget implements PreferredSizeWidget { /// 未选中背景色 final Color? unSelectedBgColor; + final TabAlignment? tabAlignment; /// A size whose height depends on if the tabs have both icons and text. /// /// [AppBar] uses this size to compute its own preferred size. @@ -799,9 +801,32 @@ class _TDHorizontalTabBarState extends State { } return null; } - + TabAlignment get _defaults { + return widget.isScrollable ? TabAlignment.start : TabAlignment.fill; + } + bool _debugTabAlignmentIsValid(TabAlignment tabAlignment) { + assert(() { + if (widget.isScrollable && tabAlignment == TabAlignment.fill) { + throw FlutterError( + '$tabAlignment is only valid for non-scrollable tab bars.', + ); + } + if (!widget.isScrollable + && (tabAlignment == TabAlignment.start + || tabAlignment == TabAlignment.startOffset)) { + throw FlutterError( + '$tabAlignment is only valid for scrollable tab bars.', + ); + } + return true; + }()); + return true; + } @override Widget build(BuildContext context) { + final tabBarTheme = TabBarTheme.of(context); + final TabAlignment effectiveTabAlignment = widget.tabAlignment ?? tabBarTheme.tabAlignment ?? _defaults; + assert(_debugTabAlignmentIsValid(effectiveTabAlignment)); assert(debugCheckHasMaterialLocalizations(context)); assert(() { if (_controller!.length != widget.tabs.length) { @@ -819,7 +844,7 @@ class _TDHorizontalTabBarState extends State { ); } - final tabBarTheme = TabBarTheme.of(context); + final wrappedTabs = List.generate(widget.tabs.length, (int index) { const verticalAdjustment = (_kTextAndIconTabHeight - _kTabHeight) / 2.0; @@ -927,7 +952,7 @@ class _TDHorizontalTabBarState extends State { ), ), ); - if (!widget.isScrollable) { + if (!widget.isScrollable && effectiveTabAlignment == TabAlignment.fill) { wrappedTabs[index] = Expanded(child: wrappedTabs[index]); } } @@ -943,18 +968,21 @@ class _TDHorizontalTabBarState extends State { unselectedLabelStyle: widget.unselectedLabelStyle, child: _TabLabelBar( onPerformLayout: _saveTabOffsets, + mainAxisSize: effectiveTabAlignment == TabAlignment.fill ? MainAxisSize.max : MainAxisSize.min, children: wrappedTabs, ), ), ); if (widget.isScrollable) { + final EdgeInsetsGeometry? effectivePadding = effectiveTabAlignment == TabAlignment.startOffset + ? const EdgeInsetsDirectional.only(start: _kStartOffset).add(widget.padding ?? EdgeInsets.zero): widget.padding; _scrollController ??= _TabBarScrollController(this); tdHorizontalTabBar = SingleChildScrollView( dragStartBehavior: widget.dragStartBehavior, scrollDirection: Axis.horizontal, controller: _scrollController, - padding: widget.padding, + padding: effectivePadding, physics: widget.physics, child: tdHorizontalTabBar, ); @@ -1087,11 +1115,11 @@ class _TabLabelBar extends Flex { Key? key, List children = const [], required this.onPerformLayout, + required super.mainAxisSize, }) : super( key: key, children: children, direction: Axis.horizontal, - mainAxisSize: MainAxisSize.max, mainAxisAlignment: MainAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.center, verticalDirection: VerticalDirection.down, diff --git a/tdesign-component/lib/src/components/tabs/td_tab_bar.dart b/tdesign-component/lib/src/components/tabs/td_tab_bar.dart index 281e94c4a..58d5a6375 100644 --- a/tdesign-component/lib/src/components/tabs/td_tab_bar.dart +++ b/tdesign-component/lib/src/components/tabs/td_tab_bar.dart @@ -42,6 +42,7 @@ class TDTabBar extends StatefulWidget { this.dividerHeight = 0.5, this.selectedBgColor, this.unSelectedBgColor, + this.tabAlignment, }) : assert( backgroundColor == null || decoration == null, 'Cannot provide both a backgroundColor and a decoration\n' @@ -124,6 +125,7 @@ class TDTabBar extends StatefulWidget { /// 未选中背景色,只有outlineType为capsule时有效 final Color? unSelectedBgColor; + final TabAlignment? tabAlignment; @override State createState() => _TDTabBarState(); } @@ -165,6 +167,7 @@ class _TDTabBarState extends State { backgroundColor: widget.backgroundColor, selectedBgColor: widget.selectedBgColor, unSelectedBgColor: widget.unSelectedBgColor, + tabAlignment:widget.tabAlignment, onTap: (index) { widget.onTap?.call(index); }, diff --git a/tdesign-component/lib/src/components/upload/td_upload.dart b/tdesign-component/lib/src/components/upload/td_upload.dart index e57f116f2..fbd489989 100644 --- a/tdesign-component/lib/src/components/upload/td_upload.dart +++ b/tdesign-component/lib/src/components/upload/td_upload.dart @@ -6,7 +6,6 @@ import 'package:image_picker/image_picker.dart'; import '../../../tdesign_flutter.dart'; - enum TDUploadMediaType { image, // 图片 video, // 视频 @@ -56,8 +55,7 @@ class TDUploadFile { typedef TDUploadErrorEvent = void Function(Object e); typedef TDUploadClickEvent = void Function(int value); -typedef TDUploadValueChangedEvent = void Function( - List files, TDUploadType type); +typedef TDUploadValueChangedEvent = void Function(List files, TDUploadType type); typedef TDUploadValidatorEvent = void Function(TDUploadValidatorError e); class TDUpload extends StatefulWidget { @@ -111,9 +109,8 @@ class TDUpload extends StatefulWidget { class _TDUploadState extends State { List fileList = []; - bool get canUpload => widget.multiple - ? (widget.max == 0 ? true : fileList.length < widget.max) - : fileList.isEmpty; + + bool get canUpload => widget.multiple ? (widget.max == 0 ? true : fileList.length < widget.max) : fileList.isEmpty; final ImagePicker _picker = ImagePicker(); @override @@ -132,8 +129,7 @@ class _TDUploadState extends State { try { if (widget.multiple) { - if (widget.mediaType.length == 1 && - widget.mediaType.contains(TDUploadMediaType.image)) { + if (widget.mediaType.length == 1 && widget.mediaType.contains(TDUploadMediaType.image)) { medias = await _picker.pickMultiImage(); } else { medias = await _picker.pickMultiImage(); @@ -186,15 +182,11 @@ class _TDUploadState extends State { return; } - var originMaxKeys = - fileList.isEmpty ? 0 : fileList.map((file) => file.key).reduce(max); + var originMaxKeys = fileList.isEmpty ? 0 : fileList.map((file) => file.key).reduce(max); var newFiles = []; for (var i = 0; i < files.length; i++) { - newFiles.add(TDUploadFile( - key: originMaxKeys + i + 1, - file: File(files[i].path), - assetPath: files[i].path)); + newFiles.add(TDUploadFile(key: originMaxKeys + i + 1, file: File(files[i].path), assetPath: files[i].path)); } if (widget.onChange != null) { @@ -254,8 +246,7 @@ class _TDUploadState extends State { ); } - Widget _buildUploadBox(BuildContext context, - {void Function()? onTap, bool shouldDisplay = true}) { + Widget _buildUploadBox(BuildContext context, {void Function()? onTap, bool shouldDisplay = true}) { return Visibility( visible: shouldDisplay, child: GestureDetector( @@ -263,9 +254,7 @@ class _TDUploadState extends State { child: Container( width: 80, height: 80, - decoration: BoxDecoration( - color: TDTheme.of(context).grayColor1, - borderRadius: BorderRadius.circular(6)), + decoration: BoxDecoration(color: TDTheme.of(context).grayColor1, borderRadius: BorderRadius.circular(6)), child: const Center( child: Icon( TDIcons.add, @@ -290,9 +279,7 @@ class _TDUploadState extends State { imgUrl: file.remotePath, assetUrl: file.assetPath, ), - Visibility( - visible: file.status != TDUploadFileStatus.success, - child: _buildShadowBox(file)), + Visibility(visible: file.status != TDUploadFileStatus.success, child: _buildShadowBox(file)), Visibility( visible: file.canDelete, child: Positioned( @@ -307,9 +294,8 @@ class _TDUploadState extends State { height: 20, decoration: const BoxDecoration( color: Color.fromRGBO(0, 0, 0, 0.6), - borderRadius: BorderRadius.only( - bottomLeft: Radius.circular(6), - topRight: Radius.circular(6))), + borderRadius: + BorderRadius.only(bottomLeft: Radius.circular(6), topRight: Radius.circular(6))), child: const Center( child: Icon( TDIcons.close, @@ -327,8 +313,7 @@ class _TDUploadState extends State { var displayText = ''; switch (file.status) { case TDUploadFileStatus.loading: - displayText = - file.progress != null ? '${file.progress!}%' : file.loadingText; + displayText = file.progress != null ? '${file.progress!}%' : file.loadingText; break; case TDUploadFileStatus.retry: displayText = file.retryText; @@ -342,9 +327,7 @@ class _TDUploadState extends State { return Container( width: 80, height: 80, - decoration: BoxDecoration( - color: const Color.fromRGBO(0, 0, 0, 0.4), - borderRadius: BorderRadius.circular(6)), + decoration: BoxDecoration(color: const Color.fromRGBO(0, 0, 0, 0.4), borderRadius: BorderRadius.circular(6)), child: Padding( padding: const EdgeInsets.symmetric(vertical: 16), child: Center( @@ -360,12 +343,9 @@ class _TDUploadState extends State { ), ), Visibility( - visible: file.status == TDUploadFileStatus.retry || - file.status == TDUploadFileStatus.error, + visible: file.status == TDUploadFileStatus.retry || file.status == TDUploadFileStatus.error, child: Icon( - file.status == TDUploadFileStatus.retry - ? TDIcons.refresh - : TDIcons.close_circle, + file.status == TDUploadFileStatus.retry ? TDIcons.refresh : TDIcons.close_circle, size: 24, color: Colors.white, )),