From 52017524a619d78863ee2a3771126088b7164903 Mon Sep 17 00:00:00 2001 From: Jolie <412895109@qq.com> Date: Thu, 6 Nov 2025 09:39:06 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E9=A6=96=E9=A1=B5tab?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/discover_nol.png | Bin 0 -> 7143 bytes assets/images/discover_pre.png | Bin 0 -> 6565 bytes assets/images/home_nol.png | Bin 0 -> 7468 bytes assets/images/home_pre.png | Bin 0 -> 7434 bytes assets/images/message_nol.png | Bin 0 -> 7288 bytes assets/images/message_pre.png | Bin 0 -> 7425 bytes assets/images/mine_nol.png | Bin 0 -> 6255 bytes assets/images/mine_pre.png | Bin 0 -> 7557 bytes lib/extension/ex_context.dart | 13 + lib/extension/ex_date.dart | 102 +++ lib/extension/ex_int.dart | 44 + lib/extension/ex_string.dart | 17 + lib/extension/ex_widget.dart | 936 ++++++++++++++++++++++ lib/generated/assets.dart | 17 +- lib/main.dart | 5 +- lib/pages/discover/discover_page.dart | 21 + lib/pages/home/home_page.dart | 24 + lib/pages/main/main_page.dart | 91 +++ lib/pages/main/tabbar/main_tab_bar.dart | 182 +++++ lib/pages/main/tabbar/main_tab_btn.dart | 77 ++ lib/pages/main/tabbar/main_tab_item.dart | 13 + lib/pages/main_page.dart | 45 -- lib/pages/message/message_page.dart | 22 + lib/pages/mine/login_controller.dart | 2 +- lib/pages/mine/mine_page.dart | 21 + lib/pages/mine/user_info_controller.dart | 2 +- lib/widget/double_tap_to_exit_widget.dart | 39 + pubspec.lock | 308 ++++--- pubspec.yaml | 1 + 29 files changed, 1810 insertions(+), 172 deletions(-) create mode 100644 assets/images/discover_nol.png create mode 100644 assets/images/discover_pre.png create mode 100644 assets/images/home_nol.png create mode 100644 assets/images/home_pre.png create mode 100644 assets/images/message_nol.png create mode 100644 assets/images/message_pre.png create mode 100644 assets/images/mine_nol.png create mode 100644 assets/images/mine_pre.png create mode 100644 lib/extension/ex_context.dart create mode 100644 lib/extension/ex_date.dart create mode 100644 lib/extension/ex_int.dart create mode 100644 lib/extension/ex_string.dart create mode 100644 lib/extension/ex_widget.dart create mode 100644 lib/pages/discover/discover_page.dart create mode 100644 lib/pages/home/home_page.dart create mode 100644 lib/pages/main/main_page.dart create mode 100644 lib/pages/main/tabbar/main_tab_bar.dart create mode 100644 lib/pages/main/tabbar/main_tab_btn.dart create mode 100644 lib/pages/main/tabbar/main_tab_item.dart delete mode 100644 lib/pages/main_page.dart create mode 100644 lib/pages/message/message_page.dart create mode 100644 lib/pages/mine/mine_page.dart create mode 100644 lib/widget/double_tap_to_exit_widget.dart diff --git a/assets/images/discover_nol.png b/assets/images/discover_nol.png new file mode 100644 index 0000000000000000000000000000000000000000..a11a12037258ae0f6bb74cd5912ca444cea37920 GIT binary patch literal 7143 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 a978<2ag-E?8$sY^9blkqqEoBo68C={kMdjq literal 0 HcmV?d00001 diff --git a/assets/images/discover_pre.png b/assets/images/discover_pre.png new file mode 100644 index 0000000000000000000000000000000000000000..d8699f891e1ad626d9bb042ffa17386d59b00fca GIT binary patch literal 6565 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 E0HA6&mH+?% literal 0 HcmV?d00001 diff --git a/assets/images/home_pre.png b/assets/images/home_pre.png new file mode 100644 index 0000000000000000000000000000000000000000..b7a5f67b0d0345255aa0ff7d9ce1c757a7081e55 GIT binary patch literal 7434 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 MO;5F2*(&V+0pi(G4gdfE literal 0 HcmV?d00001 diff --git a/assets/images/message_nol.png b/assets/images/message_nol.png new file mode 100644 index 0000000000000000000000000000000000000000..9627ff8397620ba01f64219bc2b96b2c625a0697 GIT binary patch literal 7288 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 DQYH-w literal 0 HcmV?d00001 diff --git a/assets/images/message_pre.png b/assets/images/message_pre.png new file mode 100644 index 0000000000000000000000000000000000000000..a5ffa80776c0f595119c3e1b135155d3b015e309 GIT binary patch literal 7425 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 literal 0 HcmV?d00001 diff --git a/assets/images/mine_nol.png b/assets/images/mine_nol.png new file mode 100644 index 0000000000000000000000000000000000000000..f9574ad29ea991b143c195dbfbe69c5a96a90bde GIT binary patch literal 6255 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 aOfmV;4Aa~vX5gO&fU$u&vicf2@&5qJqS>wh literal 0 HcmV?d00001 diff --git a/assets/images/mine_pre.png b/assets/images/mine_pre.png new file mode 100644 index 0000000000000000000000000000000000000000..cfeda9dc7eee344c2075bd4e6dc266420f8d0c0b GIT binary patch literal 7557 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 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: