diff --git a/assets/images/discover_nol.png b/assets/images/discover_nol.png new file mode 100644 index 0000000..a11a120 Binary files /dev/null and b/assets/images/discover_nol.png differ diff --git a/assets/images/discover_pre.png b/assets/images/discover_pre.png new file mode 100644 index 0000000..d8699f8 Binary files /dev/null and b/assets/images/discover_pre.png differ diff --git a/assets/images/home_nol.png b/assets/images/home_nol.png new file mode 100644 index 0000000..67c8843 Binary files /dev/null and b/assets/images/home_nol.png differ diff --git a/assets/images/home_pre.png b/assets/images/home_pre.png new file mode 100644 index 0000000..b7a5f67 Binary files /dev/null and b/assets/images/home_pre.png differ diff --git a/assets/images/message_nol.png b/assets/images/message_nol.png new file mode 100644 index 0000000..9627ff8 Binary files /dev/null and b/assets/images/message_nol.png differ diff --git a/assets/images/message_pre.png b/assets/images/message_pre.png new file mode 100644 index 0000000..a5ffa80 Binary files /dev/null and b/assets/images/message_pre.png differ diff --git a/assets/images/mine_nol.png b/assets/images/mine_nol.png new file mode 100644 index 0000000..f9574ad Binary files /dev/null and b/assets/images/mine_nol.png differ diff --git a/assets/images/mine_pre.png b/assets/images/mine_pre.png new file mode 100644 index 0000000..cfeda9d Binary files /dev/null and b/assets/images/mine_pre.png differ diff --git a/lib/extension/ex_context.dart b/lib/extension/ex_context.dart new file mode 100644 index 0000000..8c81cd2 --- /dev/null +++ b/lib/extension/ex_context.dart @@ -0,0 +1,13 @@ +import 'package:flutter/material.dart'; + +extension BuildContextExtension on BuildContext { + MediaQueryData get mediaQuery => MediaQuery.of(this); + + double get topPadding => mediaQuery.padding.top; + + double get bottomPadding => mediaQuery.padding.bottom; + + double get bottomInsets => mediaQuery.viewInsets.bottom; + + EdgeInsets get viewInsets => mediaQuery.viewInsets; +} diff --git a/lib/extension/ex_date.dart b/lib/extension/ex_date.dart new file mode 100644 index 0000000..f4d9ec0 --- /dev/null +++ b/lib/extension/ex_date.dart @@ -0,0 +1,102 @@ +import 'package:easy_localization/easy_localization.dart'; +extension DateTimeIntExt on int { + String get formatExpired { + if (this == -1) { + return "Perpetually valid".tr(); + } + final now = DateTime.now(); + final date = DateTime.fromMillisecondsSinceEpoch(this); + final difference = date.difference(now); + if (difference.inHours > 24) { + return "{} Days".tr(args: ["${difference.inDays}"]); + } + if (difference.inHours > 1) { + return "{} Hours".tr(args: ["${difference.inHours}"]); + } + return "<1 Hours".tr(); + } + + String get formatTimeago { + int value = (this >= 1000000000 && this <= 9999999999) ? this * 1000 : this; + // 将时间戳转换为DateTime对象 + final date = DateTime.fromMillisecondsSinceEpoch(value, isUtc: true); + final now = DateTime.now(); + final difference = now.difference(date); + if ((date.year - 1) >= now.year) { + return "${date.year}-${date.month}-${date.day}"; + } else if (difference.inDays > 1) { + return "${date.month}-${date.day}"; + } else if (difference.inDays == 1) { + return 'yesterday'.tr(); + } else if (difference.inDays < 1 && difference.inHours >= 3) { + return "${date.hour}:${date.minute}"; + } else if (difference.inHours >= 1) { + return '{} hours ago'.tr(args: ["${difference.inHours}"]); + } else if (difference.inMinutes >= 1) { + return '{} minute ago'.tr(args: ["${difference.inMinutes}"]); + } else { + return 'just now'.tr(); + } + } + + /// hours 时区偏移, 0 为本地时间 + String formatString({ + String format = "yyyy.MM.dd HH:mm:ss", + int hours = 0, + bool isUtc = false, + String? locale, + }) { + int value = (this >= 1000000000 && this <= 9999999999) ? this * 1000 : this; + // 将时间戳转换为DateTime对象 + final date = DateTime.fromMillisecondsSinceEpoch(value, + isUtc: hours > 0 ? true : isUtc); + if (hours != 0) { + Duration utcMinus3Offset = Duration(hours: hours); + return DateFormat(format, locale ?? "en") + .format(date.add(utcMinus3Offset)); + } + return DateFormat(format, locale ?? "en").format(date); + } + + /// hours 时区偏移, 0 为本地时间 + // String formatStringToLoacl( + // {String format = "yyyy-MM-dd HH:mm:ss", int hours = 3}) { + // int value = (this >= 1000000000 && this <= 9999999999) ? this * 1000 : this; + // // 将时间戳转换为DateTime对象 + // // final date = DateTime.fromMillisecondsSinceEpoch(value, isUtc: true); + // final date = DateTime.fromMicrosecondsSinceEpoch(value * 1000); + // // if (hours != 0) { + // // Duration utcMinus3Offset = Duration(hours: hours); + // // return DateFormat(format).format(date.add(utcMinus3Offset)); + // // } + // return SpUtil().dateFormatOfLocal2(DateFormat(format).format(date)); + // } +} + +extension DateTimeExt on DateTime { + String get formatUTC3String { + DateFormat dateFormat = DateFormat('yyyy.MM.dd HH:mm:ss', "en"); + + // 格式化 DateTime 对象 + String formattedDate = + dateFormat.format(toUtc().add(const Duration(hours: 3))); + + // 返回格式化后的字符串 + return '$formattedDate (UTC+3)'; + } + + String formatStringWithFormat(String fromat) { + DateFormat dateFormat = DateFormat(fromat, "en"); + return dateFormat.format(this); + } +} + +extension DateTimeStrExt on String { + DateTime get formatUtcTime { + return DateFormat('yyyy-MM-dd HH:mm', "en").parse(this).toUtc(); + } + + DateTime get formatLocalTime { + return DateFormat('yyyy-MM-dd HH:mm', "en").parse(this).toLocal(); + } +} diff --git a/lib/extension/ex_int.dart b/lib/extension/ex_int.dart new file mode 100644 index 0000000..0a6d20d --- /dev/null +++ b/lib/extension/ex_int.dart @@ -0,0 +1,44 @@ +extension IntExt on int { + String get formatRank { + if (this < 1000) { + return toString(); + } else if (this < 1000000) { + return '${_formatResult((this / 1000))}K'; + } else if (this < 1000000000) { + return '${_formatResult(this / 1000000)}M'; + } + return '${_formatResult(this / 1000000000)}B'; + } + + String get formatUSDString { + return (this / 100).toStringAsFixed(2); + } + + String _formatResult(double value) { + value.toStringAsFixed(2); + final String resultStr = value.toString(); //value.toStringAsFixed(2); + final index = resultStr.lastIndexOf('.') + 3; + String resultVlueStr = resultStr; + if (index < resultStr.length) { + resultVlueStr = resultStr.substring(0, index); + } + return resultVlueStr.endsWith('.0') + ? value.toStringAsFixed(0) + : resultVlueStr; + } + + String get formatDurationFromSeconds{ + Duration duration = Duration(seconds: this); + String twoDigits(int n) => n.toString().padLeft(2, '0'); + String minutes = twoDigits(duration.inMinutes.remainder(60)); + String seconds = twoDigits(duration.inSeconds.remainder(60)); + + if (duration.inHours > 0) { + String hours = duration.inHours.toString(); + return "$hours:$minutes:$seconds"; + } else { + return "$minutes:$seconds"; + } + } + +} diff --git a/lib/extension/ex_string.dart b/lib/extension/ex_string.dart new file mode 100644 index 0000000..ed9edbf --- /dev/null +++ b/lib/extension/ex_string.dart @@ -0,0 +1,17 @@ +import 'package:easy_localization/easy_localization.dart'; + +extension DateTimeStringExt on String { + String formatTimeString({String format = "yyyy.MM.dd HH:mm:ss"}) { + final DateTime? time = DateTime.tryParse(this); + if (time == null) return ""; + return DateFormat(format, "en").format(time); + } + + int get toInt { + return int.tryParse(this) ?? 0; + } + + Map get queryParameters { + return Uri.tryParse(this)?.queryParameters ?? {}; + } +} diff --git a/lib/extension/ex_widget.dart b/lib/extension/ex_widget.dart new file mode 100644 index 0000000..4d9c81e --- /dev/null +++ b/lib/extension/ex_widget.dart @@ -0,0 +1,936 @@ +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; + } +} diff --git a/lib/generated/assets.dart b/lib/generated/assets.dart index c112028..ddf36c3 100644 --- a/lib/generated/assets.dart +++ b/lib/generated/assets.dart @@ -2,12 +2,21 @@ class Assets { Assets._(); + static const String imagesAvatarsExample = 'assets/images/avatars_example.png'; + static const String imagesBgEditAvatars = 'assets/images/bg_edit_avatars.png'; + static const String imagesBgInformation = 'assets/images/bg_information.png'; + static const String imagesDiscoverNol = 'assets/images/discover_nol.png'; + static const String imagesDiscoverPre = 'assets/images/discover_pre.png'; + static const String imagesEditAvatarsIcon = 'assets/images/edit_avatars_icon.png'; + static const String imagesHomeNol = 'assets/images/home_nol.png'; + static const String imagesHomePre = 'assets/images/home_pre.png'; static const String imagesLoginBg = 'assets/images/login_bg.png'; static const String imagesLoginLogo = 'assets/images/login_logo.png'; static const String imagesManIcon = 'assets/images/man_icon.png'; + static const String imagesMessageNol = 'assets/images/message_nol.png'; + static const String imagesMessagePre = 'assets/images/message_pre.png'; + static const String imagesMineNol = 'assets/images/mine_nol.png'; + static const String imagesMinePre = 'assets/images/mine_pre.png'; static const String imagesWomenIcon = 'assets/images/women_icon.png'; - static const String imagesBgInformation = 'assets/images/bg_information.png'; - static const String imagesAvatarsExample = 'assets/images/avatars_example.png'; - static const String imagesBgEditAvatars = 'assets/images/bg_edit_avatars.png'; - static const String imagesEditAvatarsIcon = 'assets/images/edit_avatars_icon.png'; + } diff --git a/lib/main.dart b/lib/main.dart index 28b8722..5fdf5bd 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -2,7 +2,7 @@ import 'dart:io'; import 'package:dating_touchme_app/config/env_config.dart'; import 'package:dating_touchme_app/network/network_service.dart'; -import 'package:dating_touchme_app/pages/main_page.dart'; +import 'package:dating_touchme_app/pages/main/main_page.dart'; import 'package:dating_touchme_app/pages/mine/login_page.dart'; import 'package:dating_touchme_app/pages/mine/user_info_page.dart'; import 'package:flutter/material.dart'; @@ -74,8 +74,7 @@ class MyApp extends StatelessWidget { // 如果token不为空,显示主页;如果token为空,显示登录页面 if (token != null && token.isNotEmpty) { - // return MainPage(); - return UserInfoPage(); + return MainPage(); } else { return LoginPage(); } diff --git a/lib/pages/discover/discover_page.dart b/lib/pages/discover/discover_page.dart new file mode 100644 index 0000000..32e5db8 --- /dev/null +++ b/lib/pages/discover/discover_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class DiscoverPage extends StatefulWidget { + const DiscoverPage({super.key}); + + @override + State createState() => _DiscoverPageState(); +} + +class _DiscoverPageState extends State with AutomaticKeepAliveClientMixin{ + @override + Widget build(BuildContext context) { + super.build(context); + return Container( + color: Colors.white, + ); + } + + @override + bool get wantKeepAlive => true; +} diff --git a/lib/pages/home/home_page.dart b/lib/pages/home/home_page.dart new file mode 100644 index 0000000..cb4516f --- /dev/null +++ b/lib/pages/home/home_page.dart @@ -0,0 +1,24 @@ +import 'package:flutter/cupertino.dart'; +import 'package:flutter/material.dart'; + +class HomePage extends StatefulWidget { + const HomePage({super.key}); + + @override + State createState() => _HomePageState(); +} + +class _HomePageState extends State with AutomaticKeepAliveClientMixin{ + + @override + Widget build(BuildContext context) { + super.build(context); + return Container( + color: Colors.white, + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/pages/main/main_page.dart b/lib/pages/main/main_page.dart new file mode 100644 index 0000000..a2a76f1 --- /dev/null +++ b/lib/pages/main/main_page.dart @@ -0,0 +1,91 @@ +import 'package:dating_touchme_app/pages/main/tabbar/main_tab_bar.dart'; +import 'package:dating_touchme_app/pages/message/message_page.dart'; +import 'package:dating_touchme_app/pages/mine/mine_page.dart'; +import 'package:flutter/material.dart'; +import 'package:get/get.dart'; +import 'package:get_storage/get_storage.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; +import 'package:dating_touchme_app/network/user_api.dart'; + +import '../../widget/double_tap_to_exit_widget.dart'; +import '../discover/discover_page.dart'; +import '../home/home_page.dart'; +// 移除未使用的导入 + +class MainPage extends StatefulWidget { + const MainPage({super.key}); + + @override + State createState() => _MainPageState(); +} + +class _MainPageState extends State { + late UserApi _userApi; + final storage = GetStorage(); + PageController pageController = PageController(initialPage: 0); + int currentIndex = 0; // 使用普通int替代RxInt + + // 将页面实例存储为成员变量,避免每次build都重新创建 + late HomePage homePage; + late DiscoverPage discoverPage; + late MessagePage messagePage; + late MinePage minePage; + + @override + void initState() { + super.initState(); + // 获取UserApi实例 + _userApi = Get.find(); + + // 初始化页面实例 + homePage = HomePage(); + discoverPage = DiscoverPage(); + messagePage = MessagePage(); + minePage = MinePage(); + + // 检查token并调用获取婚姻信息详情的方法 + checkTokenAndFetchMarriageInfo(); + } + + // 检查token并获取婚姻信息详情 + Future checkTokenAndFetchMarriageInfo() async { + final response = await _userApi.getMarriageInformationDetail(); + if (response.data.isSuccess) { + if (response.data.data == null) { + + } + } + } + + @override + Widget build(BuildContext context) { + return DoubleTapToExitWidget( + child: Scaffold( + backgroundColor: Colors.transparent, + resizeToAvoidBottomInset: false, + body: Stack( + alignment: Alignment.bottomCenter, + children: [ + PageView( + physics: const NeverScrollableScrollPhysics(), + controller: pageController, + children: [ + homePage, // 使用成员变量引用 + discoverPage, + messagePage, + minePage, + ], + ), + MainTabBar( + initialIndex: currentIndex, + onTabChanged: (index) { + currentIndex = index; + pageController.jumpToPage(index); + }, + ), + ], + ), + ), + ); + } +} diff --git a/lib/pages/main/tabbar/main_tab_bar.dart b/lib/pages/main/tabbar/main_tab_bar.dart new file mode 100644 index 0000000..d1fbece --- /dev/null +++ b/lib/pages/main/tabbar/main_tab_bar.dart @@ -0,0 +1,182 @@ +import 'dart:io'; +import 'package:collection/collection.dart'; +import 'package:dating_touchme_app/extension/ex_context.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +import '../../../generated/assets.dart'; +import 'main_tab_btn.dart'; +import 'main_tab_item.dart'; + +class MainTabBar extends StatefulWidget { + const MainTabBar({super.key, this.initialIndex = 0, this.onTabChanged}); + + final int initialIndex; + final void Function(int index)? onTabChanged; + + @override + State createState() => _MainTabBarState(); +} + +class _MainTabBarState extends State { + int _selecteIndex = 0; + + final List items = const [ + MainTabItem( + title: "首页", + icon: Assets.imagesHomeNol, + selectedIcon: Assets.imagesHomePre, + ), + MainTabItem( + title: "找对象", + icon: Assets.imagesDiscoverNol, + selectedIcon: Assets.imagesDiscoverPre, + ), + MainTabItem( + title: "消息", + icon: Assets.imagesMessageNol, + selectedIcon: Assets.imagesMessagePre, + ), + MainTabItem( + title: "我的", + icon: Assets.imagesMineNol, + selectedIcon: Assets.imagesMinePre, + ), + ]; + + @override + void initState() { + _selecteIndex = widget.initialIndex; + super.initState(); + } + + @override + void didUpdateWidget(covariant MainTabBar oldWidget) { + super.didUpdateWidget(oldWidget); + if (_selecteIndex != widget.initialIndex) { + _selecteIndex = widget.initialIndex; + } + } + + @override + Widget build(BuildContext context) { + return Builder(builder: (context) { + if (Platform.isIOS) { + if (context.bottomPadding > 0) { + return Container( + height: 64.w, + color: Colors.white, + child: Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: items.mapIndexed((index, item) { + return MainTabButton( + selected: index == _selecteIndex, + isRedDot: item.showRedDot, + title: item.title, + onTap: () { + if (_selecteIndex != index) { + _selecteIndex = index; + setState(() {}); + widget.onTabChanged?.call(index); + } + }, + icon: item.icon, + selectedIcon: item.selectedIcon, + ); + }).toList()), + SizedBox( + height: 22.w, + ) + ], + ) + ], + ), + ); + } else { + return SafeArea( + top: false, + child: Container( + height: 64.w, + color: Colors.white, + child: Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: items.mapIndexed((index, item) { + return MainTabButton( + selected: index == _selecteIndex, + isRedDot: item.showRedDot, + title: item.title, + onTap: () { + if (_selecteIndex != index) { + _selecteIndex = index; + setState(() {}); + widget.onTabChanged?.call(index); + } + }, + icon: item.icon, + selectedIcon: item.selectedIcon, + ); + }).toList()), + SizedBox( + height: 14.w, + ) + ], + ) + ], + ), + ), + ); + } + } else { + return SafeArea( + top: false, + child: Container( + // padding: EdgeInsets.only(bottom: 14.w), + // margin: EdgeInsets.only(left: 16.w, right: 16.w, bottom: 14.w), + height: 64.w, + color: Colors.white, + child: Stack( + children: [ + Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: items.mapIndexed((index, item) { + return MainTabButton( + selected: index == _selecteIndex, + isRedDot: item.showRedDot, + title: item.title, + onTap: () { + if (_selecteIndex != index) { + _selecteIndex = index; + setState(() {}); + widget.onTabChanged?.call(index); + } + }, + icon: item.icon, + selectedIcon: item.selectedIcon, + ); + }).toList()), + SizedBox( + height: 14.w, + ) + ], + ) + ], + ), + ), + ); + } + }); + } +} diff --git a/lib/pages/main/tabbar/main_tab_btn.dart b/lib/pages/main/tabbar/main_tab_btn.dart new file mode 100644 index 0000000..5b61c36 --- /dev/null +++ b/lib/pages/main/tabbar/main_tab_btn.dart @@ -0,0 +1,77 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_screenutil/flutter_screenutil.dart'; + +class MainTabButton extends StatefulWidget { + final VoidCallback? onTap; + final bool isRedDot; + final bool selected; + final String icon; + final String title; + final String selectedIcon; + + const MainTabButton({ + super.key, + required this.selected, + required this.icon, + required this.selectedIcon, + this.isRedDot = false, + this.onTap, + required this.title + }); + + @override + State createState() => MainTabButtonState(); +} + +class MainTabButtonState extends State { + @override + Widget build(BuildContext context) { + if (widget.isRedDot) { + return Expanded( + child: GestureDetector( + onTap: widget.onTap, + child: Container( + color: Colors.transparent, + alignment: Alignment.center, + child: Stack( + clipBehavior: Clip.none, + children: [ + Column( + children: [ + SizedBox(height: 5.w,), + widget.selected + ? Image.asset(widget.selectedIcon, width: 25.w, height: 25.w) + : Image.asset(widget.icon, width: 25.w, height: 25.w), + Text(widget.title, style: const TextStyle( + fontSize: 12 + ),) + ], + ), + ], + ), + ), + )); + } + + return Expanded( + child: GestureDetector( + onTap: widget.onTap, + child: Container( + alignment: Alignment.center, + color: Colors.transparent, + child: Column( + children: [ + SizedBox(height: 5.w,), + widget.selected + ? Image.asset(widget.selectedIcon, width: 25.w, height: 25.w) + : Image.asset(widget.icon, width: 25.w, height: 25.w), + Text(widget.title, style: const TextStyle( + fontSize: 12 + ),) + ], + ), + ), + ), + ); + } +} diff --git a/lib/pages/main/tabbar/main_tab_item.dart b/lib/pages/main/tabbar/main_tab_item.dart new file mode 100644 index 0000000..3c79969 --- /dev/null +++ b/lib/pages/main/tabbar/main_tab_item.dart @@ -0,0 +1,13 @@ +class MainTabItem { + final String title; + final String icon; + final String selectedIcon; + final bool showRedDot; + + const MainTabItem({ + required this.title, + required this.icon, + required this.selectedIcon, + this.showRedDot = false, + }); +} diff --git a/lib/pages/main_page.dart b/lib/pages/main_page.dart deleted file mode 100644 index fb2baf8..0000000 --- a/lib/pages/main_page.dart +++ /dev/null @@ -1,45 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:get/get.dart'; -import 'package:get_storage/get_storage.dart'; -import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; -import 'package:dating_touchme_app/network/user_api.dart'; -// 移除未使用的导入 - -class MainPage extends StatefulWidget { - const MainPage({super.key}); - - @override - State createState() => _MainPageState(); -} - -class _MainPageState extends State { - late UserApi _userApi; - final storage = GetStorage(); - - @override - void initState() { - super.initState(); - // 获取UserApi实例 - _userApi = Get.find(); - - // 检查token并调用获取婚姻信息详情的方法 - checkTokenAndFetchMarriageInfo(); - } - - // 检查token并获取婚姻信息详情 - Future checkTokenAndFetchMarriageInfo() async { - final response = await _userApi.getMarriageInformationDetail(); - if (response.data.isSuccess) { - if (response.data.data == null) { - // 跳转到完善信息页面 - SmartDialog.showToast('跳转到完善信息'); - // 这里可以添加跳转到完善信息页面的逻辑 - } - } - } - - @override - Widget build(BuildContext context) { - return const Placeholder(); - } -} diff --git a/lib/pages/message/message_page.dart b/lib/pages/message/message_page.dart new file mode 100644 index 0000000..98c390d --- /dev/null +++ b/lib/pages/message/message_page.dart @@ -0,0 +1,22 @@ +import 'package:flutter/material.dart'; + +class MessagePage extends StatefulWidget { + const MessagePage({super.key}); + + @override + State createState() => _MessagePageState(); +} + +class _MessagePageState extends State with AutomaticKeepAliveClientMixin{ + @override + Widget build(BuildContext context) { + super.build(context); + return Container( + color: Colors.white, + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/pages/mine/login_controller.dart b/lib/pages/mine/login_controller.dart index a98fd8c..c192281 100644 --- a/lib/pages/mine/login_controller.dart +++ b/lib/pages/mine/login_controller.dart @@ -1,5 +1,5 @@ import 'dart:async'; -import 'package:dating_touchme_app/pages/main_page.dart'; +import 'package:dating_touchme_app/pages/main/main_page.dart'; import 'package:get/get.dart'; import 'package:get_storage/get_storage.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; diff --git a/lib/pages/mine/mine_page.dart b/lib/pages/mine/mine_page.dart new file mode 100644 index 0000000..91b48f3 --- /dev/null +++ b/lib/pages/mine/mine_page.dart @@ -0,0 +1,21 @@ +import 'package:flutter/material.dart'; + +class MinePage extends StatefulWidget { + const MinePage({super.key}); + + @override + State createState() => _MinePageState(); +} + +class _MinePageState extends State with AutomaticKeepAliveClientMixin{ + @override + Widget build(BuildContext context) { + return Container( + color: Colors.white, + ); + } + + @override + // TODO: implement wantKeepAlive + bool get wantKeepAlive => true; +} diff --git a/lib/pages/mine/user_info_controller.dart b/lib/pages/mine/user_info_controller.dart index 97d415f..a0bbf58 100644 --- a/lib/pages/mine/user_info_controller.dart +++ b/lib/pages/mine/user_info_controller.dart @@ -4,7 +4,7 @@ import 'package:get_storage/get_storage.dart'; import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; import 'package:image_picker/image_picker.dart'; import 'package:dating_touchme_app/network/user_api.dart'; -import 'package:dating_touchme_app/pages/main_page.dart'; +import 'package:dating_touchme_app/pages/main/main_page.dart'; class UserInfoController extends GetxController { // 用户信息表单字段 diff --git a/lib/widget/double_tap_to_exit_widget.dart b/lib/widget/double_tap_to_exit_widget.dart new file mode 100644 index 0000000..4951912 --- /dev/null +++ b/lib/widget/double_tap_to_exit_widget.dart @@ -0,0 +1,39 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:flutter_smart_dialog/flutter_smart_dialog.dart'; + +class DoubleTapToExitWidget extends StatefulWidget { + final Widget child; + final Duration duration; + + const DoubleTapToExitWidget( + {super.key, + required this.child, + this.duration = const Duration(seconds: 1)}); + + @override + State createState() => DoubleTapToExitWidgetState(); +} + +class DoubleTapToExitWidgetState extends State { + DateTime? lastPressedAt; + + @override + Widget build(BuildContext context) { + return PopScope( + canPop: false, + onPopInvokedWithResult: (bool didPop, Object? result) async { + final now = DateTime.now(); + if (lastPressedAt == null || + now.difference(lastPressedAt!) >= widget.duration) { + setState(() { + lastPressedAt = now; + }); + SmartDialog.showToast('再按一次退出'); + } else { + SystemNavigator.pop(); + } + }, + child: widget.child); + } +} diff --git a/pubspec.lock b/pubspec.lock index 1473b31..6e1e082 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -6,7 +6,7 @@ packages: description: name: _fe_analyzer_shared sha256: f0bb5d1648339c8308cc0b9838d8456b3cfe5c91f9dc1a735b4d003269e5da9a - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "88.0.0" analyzer: @@ -14,7 +14,7 @@ packages: description: name: analyzer sha256: "0b7b9c329d2879f8f05d6c05b32ee9ec025f39b077864bdb5ac9a7b63418a98f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "8.1.1" ansicolor: @@ -22,7 +22,7 @@ packages: description: name: ansicolor sha256: "50e982d500bc863e1d703448afdbf9e5a72eb48840a4f766fa361ffd6877055f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.3" archive: @@ -30,7 +30,7 @@ packages: description: name: archive sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.7" args: @@ -38,7 +38,7 @@ packages: description: name: args sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.7.0" async: @@ -46,7 +46,7 @@ packages: description: name: async sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.13.0" boolean_selector: @@ -54,7 +54,7 @@ packages: description: name: boolean_selector sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" build: @@ -62,7 +62,7 @@ packages: description: name: build sha256: dfb67ccc9a78c642193e0c2d94cb9e48c2c818b3178a86097d644acdcde6a8d9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.2" build_config: @@ -70,7 +70,7 @@ packages: description: name: build_config sha256: "4f64382b97504dc2fcdf487d5aae33418e08b4703fc21249e4db6d804a4d0187" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" build_daemon: @@ -78,7 +78,7 @@ packages: description: name: build_daemon sha256: "409002f1adeea601018715d613115cfaf0e31f512cb80ae4534c79867ae2363d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.0" build_runner: @@ -86,7 +86,7 @@ packages: description: name: build_runner sha256: a9461b8e586bf018dd4afd2e13b49b08c6a844a4b226c8d1d10f3a723cdd78c3 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.10.1" built_collection: @@ -94,7 +94,7 @@ packages: description: name: built_collection sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.1" built_value: @@ -102,7 +102,7 @@ packages: description: name: built_value sha256: a30f0a0e38671e89a492c44d005b5545b830a961575bbd8336d42869ff71066d - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "8.12.0" characters: @@ -110,7 +110,7 @@ packages: description: name: characters sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" checked_yaml: @@ -118,7 +118,7 @@ packages: description: name: checked_yaml sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.4" clock: @@ -126,7 +126,7 @@ packages: description: name: clock sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.2" code_builder: @@ -134,7 +134,7 @@ packages: description: name: code_builder sha256: "11654819532ba94c34de52ff5feb52bd81cba1de00ef2ed622fd50295f9d4243" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.11.0" collection: @@ -142,7 +142,7 @@ packages: description: name: collection sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.19.1" convert: @@ -150,7 +150,7 @@ packages: description: name: convert sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" cross_file: @@ -158,7 +158,7 @@ packages: description: name: cross_file sha256: "942a4791cd385a68ccb3b32c71c427aba508a1bb949b86dff2adbe4049f16239" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.3.5" crypto: @@ -166,7 +166,7 @@ packages: description: name: crypto sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.7" csslib: @@ -174,7 +174,7 @@ packages: description: name: csslib sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.2" cupertino_icons: @@ -182,7 +182,7 @@ packages: description: name: cupertino_icons sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.8" dart_style: @@ -190,7 +190,7 @@ packages: description: name: dart_style sha256: c87dfe3d56f183ffe9106a18aebc6db431fc7c98c31a54b952a77f3d54a85697 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.2" dio: @@ -198,7 +198,7 @@ packages: description: name: dio sha256: d90ee57923d1828ac14e492ca49440f65477f4bb1263575900be731a3dac66a9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.9.0" dio_web_adapter: @@ -206,15 +206,31 @@ packages: description: name: dio_web_adapter sha256: "7586e476d70caecaf1686d21eee7247ea43ef5c345eab9e0cc3583ff13378d78" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" + easy_localization: + dependency: "direct main" + description: + name: easy_localization + sha256: "2ccdf9db8fe4d9c5a75c122e6275674508fd0f0d49c827354967b8afcc56bbed" + url: "https://pub.flutter-io.cn" + source: hosted + version: "3.0.8" + easy_logger: + dependency: transitive + description: + name: easy_logger + sha256: c764a6e024846f33405a2342caf91c62e357c24b02c04dbc712ef232bf30ffb7 + url: "https://pub.flutter-io.cn" + source: hosted + version: "0.0.2" event_bus: dependency: "direct main" description: name: event_bus sha256: "1a55e97923769c286d295240048fc180e7b0768902c3c2e869fe059aafa15304" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.1" fake_async: @@ -222,7 +238,7 @@ packages: description: name: fake_async sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.3" ffi: @@ -230,7 +246,7 @@ packages: description: name: ffi sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" file: @@ -238,7 +254,7 @@ packages: description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" file_selector_linux: @@ -246,7 +262,7 @@ packages: description: name: file_selector_linux sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.3+2" file_selector_macos: @@ -254,7 +270,7 @@ packages: description: name: file_selector_macos sha256: "88707a3bec4b988aaed3b4df5d7441ee4e987f20b286cddca5d6a8270cab23f2" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.4+5" file_selector_platform_interface: @@ -262,7 +278,7 @@ packages: description: name: file_selector_platform_interface sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.6.2" file_selector_windows: @@ -270,7 +286,7 @@ packages: description: name: file_selector_windows sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.9.3+4" fixnum: @@ -278,7 +294,7 @@ packages: description: name: fixnum sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" flutter: @@ -291,7 +307,7 @@ packages: description: name: flutter_lints sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.0.0" flutter_localizations: @@ -304,7 +320,7 @@ packages: description: name: flutter_native_splash sha256: "4fb9f4113350d3a80841ce05ebf1976a36de622af7d19aca0ca9a9911c7ff002" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.7" flutter_plugin_android_lifecycle: @@ -312,7 +328,7 @@ packages: description: name: flutter_plugin_android_lifecycle sha256: "306f0596590e077338312f38837f595c04f28d6cdeeac392d3d74df2f0003687" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.32" flutter_screenutil: @@ -320,7 +336,7 @@ packages: description: name: flutter_screenutil sha256: "8239210dd68bee6b0577aa4a090890342d04a136ce1c81f98ee513fc0ce891de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.9.3" flutter_smart_dialog: @@ -328,7 +344,7 @@ packages: description: name: flutter_smart_dialog sha256: "0852df132cb03fd8fc5144eb404c31eb7eb50c22aecb1cc2504f2f598090d756" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.8+9" flutter_test: @@ -346,7 +362,7 @@ packages: description: name: frontend_server_client sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.0" get: @@ -354,7 +370,7 @@ packages: description: name: get sha256: c79eeb4339f1f3deffd9ec912f8a923834bec55f7b49c9e882b8fef2c139d425 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.7.2" get_storage: @@ -362,7 +378,7 @@ packages: description: name: get_storage sha256: "39db1fffe779d0c22b3a744376e86febe4ade43bf65e06eab5af707dc84185a2" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" glob: @@ -370,7 +386,7 @@ packages: description: name: glob sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.3" graphs: @@ -378,7 +394,7 @@ packages: description: name: graphs sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.2" hotreloader: @@ -386,7 +402,7 @@ packages: description: name: hotreloader sha256: bc167a1163807b03bada490bfe2df25b0d744df359227880220a5cbd04e5734b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.3.0" html: @@ -394,7 +410,7 @@ packages: description: name: html sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.15.6" http: @@ -402,7 +418,7 @@ packages: description: name: http sha256: bb2ce4590bc2667c96f318d68cac1b5a7987ec819351d32b1c987239a815e007 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" http_multi_server: @@ -410,7 +426,7 @@ packages: description: name: http_multi_server sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.2.2" http_parser: @@ -418,7 +434,7 @@ packages: description: name: http_parser sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.1.2" im_flutter_sdk: @@ -426,7 +442,7 @@ packages: description: name: im_flutter_sdk sha256: "952bd7a4846d9645adf6bf67c781573aba2825e12bbedfa7bb9f8b234bb1410b" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.1" im_flutter_sdk_android: @@ -434,7 +450,7 @@ packages: description: name: im_flutter_sdk_android sha256: d809f8091b24bb70f55c8a1a040f7e0a27aef4756ac71549ce62e460f2151de6 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.1" im_flutter_sdk_interface: @@ -442,7 +458,7 @@ packages: description: name: im_flutter_sdk_interface sha256: "82aef6f78bc7e4afc26768631262535b641cc885220981cc9deb871abf14efa8" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.1" im_flutter_sdk_ios: @@ -450,7 +466,7 @@ packages: description: name: im_flutter_sdk_ios sha256: "5455ecd4e5877dd289051ca280ffb435d8f7f477a46f32bbb1bfc903dc03952a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.15.1" image: @@ -458,7 +474,7 @@ packages: description: name: image sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.5.4" image_picker: @@ -466,7 +482,7 @@ packages: description: name: image_picker sha256: "736eb56a911cf24d1859315ad09ddec0b66104bc41a7f8c5b96b4e2620cf5041" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" image_picker_android: @@ -474,7 +490,7 @@ packages: description: name: image_picker_android sha256: ca2a3b04d34e76157e9ae680ef16014fb4c2d20484e78417eaed6139330056f6 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.8.13+7" image_picker_for_web: @@ -482,7 +498,7 @@ packages: description: name: image_picker_for_web sha256: "40c2a6a0da15556dc0f8e38a3246064a971a9f512386c3339b89f76db87269b6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.0" image_picker_ios: @@ -490,7 +506,7 @@ packages: description: name: image_picker_ios sha256: e675c22790bcc24e9abd455deead2b7a88de4b79f7327a281812f14de1a56f58 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.8.13+1" image_picker_linux: @@ -498,7 +514,7 @@ packages: description: name: image_picker_linux sha256: "1f81c5f2046b9ab724f85523e4af65be1d47b038160a8c8deed909762c308ed4" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.2" image_picker_macos: @@ -506,7 +522,7 @@ packages: description: name: image_picker_macos sha256: "86f0f15a309de7e1a552c12df9ce5b59fe927e71385329355aec4776c6a8ec91" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.2+1" image_picker_platform_interface: @@ -514,7 +530,7 @@ packages: description: name: image_picker_platform_interface sha256: "567e056716333a1647c64bb6bd873cff7622233a5c3f694be28a583d4715690c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.11.1" image_picker_windows: @@ -522,7 +538,7 @@ packages: description: name: image_picker_windows sha256: d248c86554a72b5495a31c56f060cf73a41c7ff541689327b1a7dbccc33adfae - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.2.2" intl: @@ -530,7 +546,7 @@ packages: description: name: intl sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.20.2" io: @@ -538,7 +554,7 @@ packages: description: name: io sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.5" json_annotation: @@ -546,7 +562,7 @@ packages: description: name: json_annotation sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.0" json_serializable: @@ -554,7 +570,7 @@ packages: description: name: json_serializable sha256: "33a040668b31b320aafa4822b7b1e177e163fc3c1e835c6750319d4ab23aa6fe" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.11.1" leak_tracker: @@ -562,7 +578,7 @@ packages: description: name: leak_tracker sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "11.0.2" leak_tracker_flutter_testing: @@ -570,7 +586,7 @@ packages: description: name: leak_tracker_flutter_testing sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.10" leak_tracker_testing: @@ -578,7 +594,7 @@ packages: description: name: leak_tracker_testing sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.2" lean_builder: @@ -586,7 +602,7 @@ packages: description: name: lean_builder sha256: ef5cd5f907157eb7aa87d1704504b5a6386d2cbff88a3c2b3344477bab323ee9 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.1.2" lints: @@ -594,7 +610,7 @@ packages: description: name: lints sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.1.1" logging: @@ -602,7 +618,7 @@ packages: description: name: logging sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.0" matcher: @@ -610,7 +626,7 @@ packages: description: name: matcher sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.12.17" material_color_utilities: @@ -618,7 +634,7 @@ packages: description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.11.1" meta: @@ -626,7 +642,7 @@ packages: description: name: meta sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.16.0" mime: @@ -634,7 +650,7 @@ packages: description: name: mime sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.0.0" package_config: @@ -642,7 +658,7 @@ packages: description: name: package_config sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" path: @@ -650,7 +666,7 @@ packages: description: name: path sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.9.1" path_provider: @@ -658,7 +674,7 @@ packages: description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.5" path_provider_android: @@ -666,7 +682,7 @@ packages: description: name: path_provider_android sha256: e122c5ea805bb6773bb12ce667611265980940145be920cd09a4b0ec0285cb16 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.20" path_provider_foundation: @@ -674,7 +690,7 @@ packages: description: name: path_provider_foundation sha256: efaec349ddfc181528345c56f8eda9d6cccd71c177511b132c6a0ddaefaa2738 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.4.3" path_provider_linux: @@ -682,7 +698,7 @@ packages: description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.1" path_provider_platform_interface: @@ -690,7 +706,7 @@ packages: description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.2" path_provider_windows: @@ -698,7 +714,7 @@ packages: description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.3.0" petitparser: @@ -706,7 +722,7 @@ packages: description: name: petitparser sha256: "1a97266a94f7350d30ae522c0af07890c70b8e62c71e8e3920d1db4d23c057d1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "7.0.1" platform: @@ -714,7 +730,7 @@ packages: description: name: platform sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.6" plugin_platform_interface: @@ -722,7 +738,7 @@ packages: description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.8" pool: @@ -730,7 +746,7 @@ packages: description: name: pool sha256: "978783255c543aa3586a1b3c21f6e9d720eb315376a915872c61ef8b5c20177d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.2" posix: @@ -738,7 +754,7 @@ packages: description: name: posix sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.0.3" protobuf: @@ -746,7 +762,7 @@ packages: description: name: protobuf sha256: "826d6a306be26f29e5cd9faeb0c97aad5897270341dab6dbd7b8acd675937006" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "5.0.0" pub_semver: @@ -754,7 +770,7 @@ packages: description: name: pub_semver sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" pubspec_parse: @@ -762,7 +778,7 @@ packages: description: name: pubspec_parse sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.5.0" retrofit: @@ -770,7 +786,7 @@ packages: description: name: retrofit sha256: "7d78824afa6eeeaf6ac58220910ee7a97597b39e93360d4bda230b7c6df45089" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.9.0" retrofit_generator: @@ -778,15 +794,71 @@ packages: description: name: retrofit_generator sha256: "47998fb9f214935e4eb00741aebc636ffcb62cb8ae73b474ac88127ce2744428" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "10.1.2" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.3" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "34266009473bf71d748912da4bf62d439185226c03e01e2d9687bc65bbfcb713" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.15" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "1c33a907142607c40a7542768ec9badfd16293bac51da3a4482623d15845f88b" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.5.5" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.flutter-io.cn" + source: hosted + version: "2.4.1" shelf: dependency: transitive description: name: shelf sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.2" shelf_web_socket: @@ -794,7 +866,7 @@ packages: description: name: shelf_web_socket sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.0" sky_engine: @@ -807,7 +879,7 @@ packages: description: name: source_gen sha256: "9098ab86015c4f1d8af6486b547b11100e73b193e1899015033cb3e14ad20243" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "4.0.2" source_helper: @@ -815,7 +887,7 @@ packages: description: name: source_helper sha256: "6a3c6cc82073a8797f8c4dc4572146114a39652851c157db37e964d9c7038723" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.3.8" source_span: @@ -823,7 +895,7 @@ packages: description: name: source_span sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.10.1" stack_trace: @@ -831,7 +903,7 @@ packages: description: name: stack_trace sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.12.1" stream_channel: @@ -839,7 +911,7 @@ packages: description: name: stream_channel sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.4" stream_transform: @@ -847,7 +919,7 @@ packages: description: name: stream_transform sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.1.1" string_scanner: @@ -855,7 +927,7 @@ packages: description: name: string_scanner sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.1" term_glyph: @@ -863,7 +935,7 @@ packages: description: name: term_glyph sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.2" test_api: @@ -871,7 +943,7 @@ packages: description: name: test_api sha256: "522f00f556e73044315fa4585ec3270f1808a4b186c936e612cab0b565ff1e00" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "0.7.6" typed_data: @@ -879,7 +951,7 @@ packages: description: name: typed_data sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.4.0" universal_io: @@ -887,7 +959,7 @@ packages: description: name: universal_io sha256: "1722b2dcc462b4b2f3ee7d188dad008b6eb4c40bbd03a3de451d82c78bba9aad" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.2" vector_math: @@ -895,7 +967,7 @@ packages: description: name: vector_math sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "2.2.0" vm_service: @@ -903,7 +975,7 @@ packages: description: name: vm_service sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "15.0.2" watcher: @@ -911,7 +983,7 @@ packages: description: name: watcher sha256: "592ab6e2892f67760543fb712ff0177f4ec76c031f02f5b4ff8d3fc5eb9fb61a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.4" web: @@ -919,7 +991,7 @@ packages: description: name: web sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.1" web_socket: @@ -927,7 +999,7 @@ packages: description: name: web_socket sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.0.1" web_socket_channel: @@ -935,7 +1007,7 @@ packages: description: name: web_socket_channel sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8 - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.0.3" xdg_directories: @@ -943,7 +1015,7 @@ packages: description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.1.0" xml: @@ -951,7 +1023,7 @@ packages: description: name: xml sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "6.6.1" xxh3: @@ -959,7 +1031,7 @@ packages: description: name: xxh3 sha256: "399a0438f5d426785723c99da6b16e136f4953fb1e9db0bf270bd41dd4619916" - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "1.2.0" yaml: @@ -967,7 +1039,7 @@ packages: description: name: yaml sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce - url: "https://pub.dev" + url: "https://pub.flutter-io.cn" source: hosted version: "3.1.3" sdks: diff --git a/pubspec.yaml b/pubspec.yaml index e7ca007..beae3f7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -48,6 +48,7 @@ dependencies: retrofit: ^4.9.0 flutter_native_splash: ^2.4.7 im_flutter_sdk: ^4.15.1 + easy_localization: ^3.0.8 dev_dependencies: flutter_test: