import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; /// 手势 tap typedef GestureOnTapChangeCallback = void Function(T val); /// 扩展 Widget extension ExWidget on Widget { /// 对齐 Widget align( AlignmentGeometry alignment, { Key? key, }) => Align( key: key, alignment: alignment, child: this, ); /// 对齐 中间 Widget alignCenter() => align(Alignment.center); /// 对齐 左边 Widget alignLeftCenter() => align(Alignment.centerLeft); /// 对齐 右边 Widget alignRightCenter() => align(Alignment.centerRight); /// 对齐 顶部 Widget alignTopCenter() => align(Alignment.topCenter); /// 对齐 底部 Widget alignBottomCenter() => align(Alignment.bottomCenter); Widget alignLeftTop() => align(Alignment.topLeft); Widget alignRightTop() => align(Alignment.topRight); Widget alignLeftBottom() => align(Alignment.bottomLeft); Widget alignRightBottom() => align(Alignment.bottomRight); // 比例布局 Widget aspectRatio({ Key? key, required double aspectRatio, }) => AspectRatio( key: key, aspectRatio: aspectRatio, child: this, ); /// 背景颜色 Widget backgroundColor( Color color, { Key? key, }) => DecoratedBox( key: key, decoration: BoxDecoration(color: color), child: this, ); /// 背景图片 Widget backgroundImage( DecorationImage image, { Key? key, }) => DecoratedBox( key: key, decoration: BoxDecoration(image: image), child: this, ); /// 边框 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, decoration: decoration, child: this, ); } /// 圆角 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, decoration: decoration, child: this, ); } /// 阴影 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, decoration: decoration, child: this, ); } Widget card({ Key? key, double? radius, Color? color, Color? shadowColor, double? blurRadius, }) => Container( decoration: BoxDecoration( color: color ?? Colors.white, borderRadius: BorderRadius.all( Radius.circular(radius ?? 5), ), boxShadow: [ BoxShadow( // x偏移量 | y偏移量 offset: const Offset(0, 3), color: shadowColor ?? Colors.black.withOpacity(0.15), // 阴影模糊半径 blurRadius: blurRadius ?? 8, // 阴影扩散半径 spreadRadius: 0, blurStyle: BlurStyle.normal, ), ], ), 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, 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), ), child: this, ); /// 约束 // 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, // constraints: constraints, // child: this, // ); // } // 取消父级约束 // Widget unconstrained({ // Key? key, // TextDirection? textDirection, // AlignmentGeometry alignment = Alignment.center, // Axis? constrainedAxis, // Clip clipBehavior = Clip.none, // }) => // UnconstrainedBox( // key: key, // textDirection: textDirection, // alignment: alignment, // constrainedAxis: constrainedAxis, // clipBehavior: clipBehavior, // child: this, // ); /// 盒子装饰器 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, decoration: decoration, position: position, child: this, ); } /// elevation Widget elevation( double elevation, { Key? key, BorderRadiusGeometry borderRadius = BorderRadius.zero, Color shadowColor = const Color(0xFF000000), }) => Material( key: key, color: Colors.transparent, elevation: elevation, borderRadius: borderRadius, shadowColor: shadowColor, child: this, ); /// expanded 撑满 Widget expanded({ Key? key, int flex = 1, }) => Expanded( key: key, flex: flex, child: this, ); // Widget fittedBox({ // Key? key, // BoxFit fit = BoxFit.contain, // AlignmentGeometry alignment = Alignment.centerLeft, // Clip clipBehavior = Clip.none, // }) => // FittedBox( // key: key, // fit: fit, // alignment: alignment, // clipBehavior: clipBehavior, // child: this, // ); /// 弹性布局 flexible Widget flexible({ Key? key, int flex = 1, FlexFit fit = FlexFit.loose, }) => Flexible( key: key, flex: flex, fit: fit, child: this, ); // // 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, bool excludeFromSemantics = false, DragStartBehavior dragStartBehavior = DragStartBehavior.start, }) => GestureDetector( key: key, onTap: onTap, behavior: behavior ?? HitTestBehavior.opaque, excludeFromSemantics: excludeFromSemantics, dragStartBehavior: dragStartBehavior, 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, constraints: BoxConstraints.tightFor(height: height), child: this, ); /// 限制盒子 最大宽高 // 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 paddingAll(double val) => padding(all: val); /// 内间距 下 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); /// 内间距 Widget sliverPadding({ Key? key, EdgeInsetsGeometry? value, double? all, double? horizontal, double? vertical, double? top, double? bottom, double? left, double? right, }) => SliverPadding( 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, ), sliver: this, ); // /// 内间距 下 // Widget sliverPaddingBottom(double val) => sliverPadding(bottom: val); // // /// 内间距 横向 // Widget sliverPaddingHorizontal(double val) => sliverPadding(horizontal: val); // // /// 内间距 左 // Widget sliverPaddingLeft(double val) => sliverPadding(left: val); // // /// 内间距 右 // Widget sliverPaddingRight(double val) => sliverPadding(right: val); // // /// 内间距 上 // Widget sliverPaddingTop(double val) => sliverPadding(top: val); // // /// 内间距 纵向 // Widget sliverPaddingVertical(double val) => sliverPadding(vertical: val); 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); /// stack布局 位置 // Widget positioned({ // Key? key, // double? left, // double? top, // double? right, // double? bottom, // double? width, // double? height, // }) => // Positioned( // key: key, // left: left, // top: top, // right: right, // bottom: bottom, // width: width, // height: height, // child: this, // ); // 墨水纹 // Widget inkWell({ // Key? key, // Function()? onTap, // double? borderRadius, // }) => // Material( // color: Colors.transparent, // child: Ink( // child: InkWell( // borderRadius: borderRadius != null // ? BorderRadius.all( // Radius.circular(borderRadius), // ) // : null, // onTap: onTap ?? () {}, // child: this, // ), // ), // ); /// 涟漪 // Widget ripple({ // Key? key, // 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: 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, // origin: origin, // transformHitTests: transformHitTests, // child: this, // ); /// 滚动视图 // 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, // scrollDirection: scrollDirection, // reverse: reverse, // primary: primary, // physics: physics, // controller: controller, // dragStartBehavior: dragStartBehavior, // padding: padding, // child: this, // ); /// 语义调试 /// 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, // constraints: BoxConstraints.tightFor(width: width, height: height), // child: this, // ); /// 约束 宽高 size // Widget tightSize( // double size, { // Key? key, // }) => // ConstrainedBox( // key: key, // constraints: BoxConstraints.tightFor(width: size, height: size), // child: this, // ); /// 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, constraints: BoxConstraints.tightFor(width: width), child: this, ); /// SliverToBoxAdapter // Widget sliverToBoxAdapter({ // Key? key, // }) => // SliverToBoxAdapter(key: key, child: this); } class HabiPreventFastClick { static const Duration _duration = Duration(seconds: 1); static DateTime? _lastTime; static bool canClick(BuildContext context) { final now = DateTime.now(); if (_lastTime != null && now.difference(_lastTime!) < _duration) { // 如果上次点击的时间距离现在不超过1秒,则不允许点击 return false; } // 更新最后点击时间 _lastTime = now; return true; } }