import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/semantics.dart'; import 'package:shared/utils/ext_dart.dart'; /// 手势 tap typedef GestureOnTapChangeCallback = void Function(T val); //点击防抖类型 enum ClickType { none, throttle, debounce } extension WidgetPaddingX on Widget { Widget paddingAll(double padding) => Padding(padding: EdgeInsets.all(padding), child: this); Widget paddingSymmetric({double horizontal = 0.0, double vertical = 0.0}) => Padding( padding: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this); Widget paddingOnly({ double left = 0.0, double top = 0.0, double right = 0.0, double bottom = 0.0, }) => Padding( padding: EdgeInsets.only( top: top, left: left, right: right, bottom: bottom), child: this); Widget get paddingZero => Padding(padding: EdgeInsets.zero, child: this); } /// Add margin property to widget extension WidgetMarginX on Widget { Widget marginAll(double margin) => Container(margin: EdgeInsets.all(margin), child: this); Widget marginSymmetric({double horizontal = 0.0, double vertical = 0.0}) => Container( margin: EdgeInsets.symmetric(horizontal: horizontal, vertical: vertical), child: this); Widget marginOnly({ double left = 0.0, double top = 0.0, double right = 0.0, double bottom = 0.0, }) => Container( margin: EdgeInsets.only( top: top, left: left, right: right, bottom: bottom), child: this); Widget get marginZero => Container(margin: EdgeInsets.zero, child: this); } /// 扩展 Widget extension ExWidget on Widget { /// 对齐 Widget align( AlignmentGeometry alignment, { Key? key, }) => Align( key: key, alignment: alignment, child: this, ); /// 对齐 中间 Widget alignCenter() => align(Alignment.center); /// 对齐 左边 Widget alignLeft() => align(Alignment.centerLeft); /// 对齐 右边 Widget alignRight() => align(Alignment.centerRight); /// 对齐 顶部 Widget alignTop() => align(Alignment.topCenter); /// 对齐 底部 Widget alignBottom() => align(Alignment.bottomCenter); // 比例布局 Widget aspectRatio({ Key? key, required double aspectRatio, }) => AspectRatio( key: key, aspectRatio: aspectRatio, child: this, ); /// 背景颜色 Widget backgroundColor( Color? color, { Key? key, }) => DecoratedBox( key: key, child: this, decoration: BoxDecoration(color: color), ); /// 背景图片 Widget backgroundImage( DecorationImage image, { Key? key, }) => DecoratedBox( key: key, child: this, decoration: BoxDecoration(image: image), ); /// 边框 Widget border({ Key? key, double? all, double? left, double? right, double? top, double? bottom, Color color = const Color(0xFF000000), BorderStyle style = BorderStyle.solid, }) { BoxDecoration decoration = BoxDecoration( border: Border( left: (left ?? all) == null ? BorderSide.none : BorderSide(color: color, width: left ?? all ?? 0, style: style), right: (right ?? all) == null ? BorderSide.none : BorderSide(color: color, width: right ?? all ?? 0, style: style), top: (top ?? all) == null ? BorderSide.none : BorderSide(color: color, width: top ?? all ?? 0, style: style), bottom: (bottom ?? all) == null ? BorderSide.none : BorderSide(color: color, width: bottom ?? all ?? 0, style: style), ), ); return DecoratedBox( key: key, child: this, decoration: decoration, ); } /// 圆角 Widget borderRadius({ Key? key, double? all, double? topLeft, double? topRight, double? bottomLeft, double? bottomRight, }) { BoxDecoration decoration = BoxDecoration( borderRadius: BorderRadius.only( topLeft: Radius.circular(topLeft ?? all ?? 0.0), topRight: Radius.circular(topRight ?? all ?? 0.0), bottomLeft: Radius.circular(bottomLeft ?? all ?? 0.0), bottomRight: Radius.circular(bottomRight ?? all ?? 0.0), ), ); return DecoratedBox( key: key, child: this, decoration: decoration, ); } /// 阴影 Widget boxShadow({ Key? key, Color color = const Color(0xFF000000), Offset offset = Offset.zero, double blurRadius = 0.0, double spreadRadius = 0.0, }) { BoxDecoration decoration = BoxDecoration( boxShadow: [ BoxShadow( color: color, blurRadius: blurRadius, spreadRadius: spreadRadius, offset: offset, ), ], ); return DecoratedBox( key: key, child: this, decoration: decoration, ); } /// 卡片 Widget card({ Key? key, Color? color, double? elevation, ShapeBorder? shape, bool borderOnForeground = true, EdgeInsetsGeometry? margin, Clip? clipBehavior, bool semanticContainer = true, }) => Card( key: key, color: color, elevation: elevation, shape: shape, borderOnForeground: borderOnForeground, margin: margin, clipBehavior: clipBehavior, semanticContainer: semanticContainer, child: this, ); // 居中 Widget center({ Key? key, double? widthFactor, double? heightFactor, }) => Center( key: key, widthFactor: widthFactor, heightFactor: heightFactor, child: this, ); /// 裁剪 oval Widget clipOval({Key? key}) => ClipOval( key: key, child: this, ); /// 裁剪 rect Widget clipRect({ Key? key, CustomClipper? clipper, Clip clipBehavior = Clip.hardEdge, }) => ClipRect( key: key, clipper: clipper, clipBehavior: clipBehavior, child: this, ); /// 裁剪圆角 Widget clipRRect({ Key? key, double? all, double? topLeft, double? topRight, double? bottomLeft, double? bottomRight, CustomClipper? clipper, Clip clipBehavior = Clip.antiAlias, }) => ClipRRect( key: key, child: this, clipper: clipper, clipBehavior: clipBehavior, borderRadius: BorderRadius.only( topLeft: Radius.circular(topLeft ?? all ?? 0.0), topRight: Radius.circular(topRight ?? all ?? 0.0), bottomLeft: Radius.circular(bottomLeft ?? all ?? 0.0), bottomRight: Radius.circular(bottomRight ?? all ?? 0.0), ), ); /// 约束 Widget constrained({ Key? key, double? width, double? height, double minWidth = 0.0, double maxWidth = double.infinity, double minHeight = 0.0, double maxHeight = double.infinity, }) { BoxConstraints constraints = BoxConstraints( minWidth: minWidth, maxWidth: maxWidth, minHeight: minHeight, maxHeight: maxHeight, ); constraints = (width != null || height != null) ? constraints.tighten(width: width, height: height) : constraints; return ConstrainedBox( key: key, child: this, constraints: constraints, ); } /// 盒子装饰器 Widget decorated({ Key? key, Color? color, DecorationImage? image, BoxBorder? border, BorderRadius? borderRadius, List? boxShadow, Gradient? gradient, BlendMode? backgroundBlendMode, BoxShape shape = BoxShape.rectangle, DecorationPosition position = DecorationPosition.background, }) { BoxDecoration decoration = BoxDecoration( color: color, image: image, border: border, borderRadius: borderRadius, boxShadow: boxShadow, gradient: gradient, backgroundBlendMode: backgroundBlendMode, shape: shape, ); return DecoratedBox( key: key, child: this, decoration: decoration, position: position, ); } /// elevation Widget elevation( double elevation, { Key? key, BorderRadiusGeometry borderRadius = BorderRadius.zero, Color shadowColor = const Color(0xFF000000), }) => Material( key: key, child: this, color: Colors.transparent, elevation: elevation, borderRadius: borderRadius, shadowColor: shadowColor, ); /// expanded 撑满,用于线性布局 Widget expanded({ Key? key, int flex = 1, }) => Expanded( key: key, child: this, flex: flex, ); /// 调整子控件大小与位置 Widget fittedBox({ Key? key, BoxFit fit = BoxFit.contain, AlignmentGeometry alignment = Alignment.center, }) => FittedBox( key: key, fit: fit, alignment: alignment, child: this, ); /// 弹性布局 flexible,用于线性布局 Widget flexible({ Key? key, int flex = 1, FlexFit fit = FlexFit.loose, }) => Flexible( key: key, child: this, flex: flex, fit: fit, ); Widget fractionallySizedBox({ Key? key, AlignmentGeometry alignment = Alignment.center, double? widthFactor, double? heightFactor, }) => FractionallySizedBox( key: key, alignment: alignment, widthFactor: widthFactor, heightFactor: heightFactor, child: this, ); /// 手势 Widget gestures({ Key? key, GestureOnTapChangeCallback? onTapChange, GestureTapDownCallback? onTapDown, GestureTapUpCallback? onTapUp, GestureTapCallback? onTap, GestureTapCancelCallback? onTapCancel, GestureTapDownCallback? onSecondaryTapDown, GestureTapUpCallback? onSecondaryTapUp, GestureTapCancelCallback? onSecondaryTapCancel, GestureTapCallback? onDoubleTap, GestureLongPressCallback? onLongPress, GestureLongPressStartCallback? onLongPressStart, GestureLongPressMoveUpdateCallback? onLongPressMoveUpdate, GestureLongPressUpCallback? onLongPressUp, GestureLongPressEndCallback? onLongPressEnd, GestureDragDownCallback? onVerticalDragDown, GestureDragStartCallback? onVerticalDragStart, GestureDragUpdateCallback? onVerticalDragUpdate, GestureDragEndCallback? onVerticalDragEnd, GestureDragCancelCallback? onVerticalDragCancel, GestureDragDownCallback? onHorizontalDragDown, GestureDragStartCallback? onHorizontalDragStart, GestureDragUpdateCallback? onHorizontalDragUpdate, GestureDragEndCallback? onHorizontalDragEnd, GestureDragCancelCallback? onHorizontalDragCancel, GestureDragDownCallback? onPanDown, GestureDragStartCallback? onPanStart, GestureDragUpdateCallback? onPanUpdate, GestureDragEndCallback? onPanEnd, GestureDragCancelCallback? onPanCancel, GestureScaleStartCallback? onScaleStart, GestureScaleUpdateCallback? onScaleUpdate, GestureScaleEndCallback? onScaleEnd, GestureForcePressStartCallback? onForcePressStart, GestureForcePressPeakCallback? onForcePressPeak, GestureForcePressUpdateCallback? onForcePressUpdate, GestureForcePressEndCallback? onForcePressEnd, HitTestBehavior? behavior, bool excludeFromSemantics = false, DragStartBehavior dragStartBehavior = DragStartBehavior.start, }) => GestureDetector( key: key, onTapDown: (TapDownDetails tapDownDetails) { if (onTapDown != null) onTapDown(tapDownDetails); if (onTapChange != null) onTapChange(true); }, onTapCancel: () { if (onTapCancel != null) onTapCancel(); if (onTapChange != null) onTapChange(false); }, onTap: () { if (onTap != null) onTap(); if (onTapChange != null) onTapChange(false); }, onTapUp: onTapUp, onDoubleTap: onDoubleTap, onLongPress: onLongPress, onLongPressStart: onLongPressStart, onLongPressEnd: onLongPressEnd, onLongPressMoveUpdate: onLongPressMoveUpdate, onLongPressUp: onLongPressUp, onVerticalDragStart: onVerticalDragStart, onVerticalDragEnd: onVerticalDragEnd, onVerticalDragDown: onVerticalDragDown, onVerticalDragCancel: onVerticalDragCancel, onVerticalDragUpdate: onVerticalDragUpdate, onHorizontalDragStart: onHorizontalDragStart, onHorizontalDragEnd: onHorizontalDragEnd, onHorizontalDragCancel: onHorizontalDragCancel, onHorizontalDragUpdate: onHorizontalDragUpdate, onHorizontalDragDown: onHorizontalDragDown, onForcePressStart: onForcePressStart, onForcePressEnd: onForcePressEnd, onForcePressPeak: onForcePressPeak, onForcePressUpdate: onForcePressUpdate, onPanStart: onPanStart, onPanEnd: onPanEnd, onPanCancel: onPanCancel, onPanDown: onPanDown, onPanUpdate: onPanUpdate, onScaleStart: onScaleStart, onScaleEnd: onScaleEnd, onScaleUpdate: onScaleUpdate, behavior: behavior ?? HitTestBehavior.opaque, excludeFromSemantics: excludeFromSemantics, dragStartBehavior: dragStartBehavior, child: this, ); /// 手势 Widget onTap( GestureTapCallback onTap, { Key? key, HitTestBehavior? behavior, ClickType type = ClickType.none, int milliseconds = 500, bool excludeFromSemantics = false, DragStartBehavior dragStartBehavior = DragStartBehavior.start, double padding = 0, // 通过 padding 参数控制点击范围的扩展 }) => GestureDetector( key: key, onTap: type == ClickType.debounce ? onTap.debounce(milliseconds) : type == ClickType.throttle ? onTap.throttle(milliseconds) : onTap, behavior: behavior ?? HitTestBehavior.opaque, excludeFromSemantics: excludeFromSemantics, dragStartBehavior: dragStartBehavior, child: Padding( padding: EdgeInsets.all(padding), // 通过 Padding 扩大点击区域 child: this, ), ); /// 长按手势 Widget onLongPress( GestureTapCallback onLongPress, { Key? key, HitTestBehavior? behavior, bool excludeFromSemantics = false, DragStartBehavior dragStartBehavior = DragStartBehavior.start, }) => GestureDetector( key: key, onLongPress: onLongPress, behavior: behavior ?? HitTestBehavior.opaque, excludeFromSemantics: excludeFromSemantics, dragStartBehavior: dragStartBehavior, child: this, ); /// 约束 高度 Widget height( double height, { Key? key, }) => ConstrainedBox( key: key, child: this, constraints: BoxConstraints.tightFor(height: height), ); /// 限制盒子 最大宽高 Widget limitedBox({ Key? key, double maxWidth = double.infinity, double maxHeight = double.infinity, }) => LimitedBox( key: key, maxWidth: maxWidth, maxHeight: maxHeight, child: this, ); /// 偏移 Widget offstage({ Key? key, bool offstage = true, }) => Offstage( key: key, offstage: offstage, child: this, ); /// 透明度 Widget opacity( double opacity, { Key? key, bool alwaysIncludeSemantics = false, }) => Opacity( key: key, opacity: opacity, alwaysIncludeSemantics: alwaysIncludeSemantics, child: this, ); /// 溢出 Widget overflow({ Key? key, AlignmentGeometry alignment = Alignment.center, double? minWidth, double? maxWidth, double? minHeight, double? maxHeight, }) => OverflowBox( key: key, alignment: alignment, minWidth: minWidth, maxWidth: minWidth, minHeight: minHeight, maxHeight: maxHeight, child: this, ); /// 内间距 Widget padding({ Key? key, EdgeInsetsGeometry? value, double? all, double? horizontal, double? vertical, double? top, double? bottom, double? left, double? right, }) => Padding( key: key, padding: value ?? EdgeInsets.only( top: top ?? vertical ?? all ?? 0.0, bottom: bottom ?? vertical ?? all ?? 0.0, left: left ?? horizontal ?? all ?? 0.0, right: right ?? horizontal ?? all ?? 0.0, ), child: this, ); /// 内间距 下 Widget paddingBottom(double val) => padding(bottom: val); /// 内间距 横向 Widget paddingHorizontal(double val) => padding(horizontal: val); /// 内间距 左 Widget paddingLeft(double val) => padding(left: val); /// 内间距 右 Widget paddingRight(double val) => padding(right: val); /// 内间距 上 Widget paddingTop(double val) => padding(top: val); /// 内间距 纵向 Widget paddingVertical(double val) => padding(vertical: val); /// stack布局 位置 Widget positioned({ Key? key, double? left, double? top, double? right, double? bottom, double? width, double? height, }) => Positioned( key: key, child: this, left: left, top: top, right: right, bottom: bottom, width: width, height: height, ); /// 涟漪 Widget ripple({ Key? key, Color? bgColor, Color? focusColor, Color? hoverColor, Color? highlightColor, Color? splashColor, InteractiveInkFeatureFactory? splashFactory, double? radius, ShapeBorder? customBorder, bool enableFeedback = true, bool excludeFromSemantics = false, FocusNode? focusNode, bool canRequestFocus = true, bool autoFocus = false, bool enable = true, }) => enable ? Builder( key: key, builder: (BuildContext context) { GestureDetector? gestures = context.findAncestorWidgetOfExactType(); return Material( color: bgColor ?? Colors.transparent, child: InkWell( focusColor: focusColor, hoverColor: hoverColor, highlightColor: highlightColor, splashColor: splashColor, splashFactory: splashFactory, radius: radius, customBorder: customBorder, enableFeedback: enableFeedback, excludeFromSemantics: excludeFromSemantics, focusNode: focusNode, canRequestFocus: canRequestFocus, autofocus: autoFocus, onTap: gestures?.onTap, child: this, ), ); }, ) : Builder( key: key, builder: (context) => this, ); // 刘海屏 特殊屏幕 留白 Widget safeArea({ Key? key, bool top = true, bool bottom = true, bool left = true, bool right = true, }) => SafeArea( key: key, top: top, bottom: bottom, left: left, right: right, child: this, ); /// 比例缩放 Widget scale({ Key? key, double? all, double? x, double? y, Offset? origin, AlignmentGeometry alignment = Alignment.center, bool transformHitTests = true, }) => Transform( key: key, transform: Matrix4.diagonal3Values(x ?? all ?? 0, y ?? all ?? 0, 1.0), alignment: alignment, child: this, origin: origin, transformHitTests: transformHitTests, ); /// 滚动视图 Widget scrollable({ Key? key, Axis scrollDirection = Axis.vertical, bool reverse = false, bool? primary, ScrollPhysics? physics, ScrollController? controller, DragStartBehavior dragStartBehavior = DragStartBehavior.start, EdgeInsetsGeometry? padding, }) => SingleChildScrollView( key: key, child: this, scrollDirection: scrollDirection, reverse: reverse, primary: primary, physics: physics, controller: controller, dragStartBehavior: dragStartBehavior, padding: padding, ); /// 语义调试 /// MaterialApp.showSemanticsDebugger: true, Widget semanticsLabel( String label, { Key? key, }) => Semantics.fromProperties( key: key, properties: SemanticsProperties(label: label), child: this, ); /// 约束 宽高 Widget tight({ double? width, double? height, Key? key, }) => ConstrainedBox( key: key, child: this, constraints: BoxConstraints.tightFor(width: width, height: height), ); /// transforms Matrix4 Widget transform({ Key? key, required Matrix4 transform, Offset? origin, AlignmentGeometry? alignment, bool transformHitTests = true, }) => Transform( key: key, transform: transform, alignment: alignment, origin: origin, transformHitTests: transformHitTests, child: this, ); /// translate 变化位置 Widget translate({ Key? key, required Offset offset, bool transformHitTests = true, }) => Transform.translate( key: key, offset: offset, transformHitTests: transformHitTests, child: this, ); /// 约束 宽度 Widget width( double width, { Key? key, }) => ConstrainedBox( key: key, child: this, constraints: BoxConstraints.tightFor(width: width), ); }