29 changed files with 1810 additions and 172 deletions
Split View
Diff Options
-
BINassets/images/discover_nol.png
-
BINassets/images/discover_pre.png
-
BINassets/images/home_nol.png
-
BINassets/images/home_pre.png
-
BINassets/images/message_nol.png
-
BINassets/images/message_pre.png
-
BINassets/images/mine_nol.png
-
BINassets/images/mine_pre.png
-
13lib/extension/ex_context.dart
-
102lib/extension/ex_date.dart
-
44lib/extension/ex_int.dart
-
17lib/extension/ex_string.dart
-
936lib/extension/ex_widget.dart
-
17lib/generated/assets.dart
-
5lib/main.dart
-
21lib/pages/discover/discover_page.dart
-
24lib/pages/home/home_page.dart
-
91lib/pages/main/main_page.dart
-
182lib/pages/main/tabbar/main_tab_bar.dart
-
77lib/pages/main/tabbar/main_tab_btn.dart
-
13lib/pages/main/tabbar/main_tab_item.dart
-
45lib/pages/main_page.dart
-
22lib/pages/message/message_page.dart
-
2lib/pages/mine/login_controller.dart
-
21lib/pages/mine/mine_page.dart
-
2lib/pages/mine/user_info_controller.dart
-
39lib/widget/double_tap_to_exit_widget.dart
-
308pubspec.lock
-
1pubspec.yaml
@ -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; |
|||
} |
|||
@ -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(); |
|||
} |
|||
} |
|||
@ -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"; |
|||
} |
|||
} |
|||
|
|||
} |
|||
@ -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<String, String> get queryParameters { |
|||
return Uri.tryParse(this)?.queryParameters ?? {}; |
|||
} |
|||
} |
|||
@ -0,0 +1,936 @@ |
|||
import 'package:flutter/gestures.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
|
|||
/// 手势 tap |
|||
typedef GestureOnTapChangeCallback = void Function<T>(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<Rect>? 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<RRect>? 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>? 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<GestureDetector>(); |
|||
// 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; |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
import 'package:flutter/material.dart'; |
|||
|
|||
class DiscoverPage extends StatefulWidget { |
|||
const DiscoverPage({super.key}); |
|||
|
|||
@override |
|||
State<DiscoverPage> createState() => _DiscoverPageState(); |
|||
} |
|||
|
|||
class _DiscoverPageState extends State<DiscoverPage> with AutomaticKeepAliveClientMixin{ |
|||
@override |
|||
Widget build(BuildContext context) { |
|||
super.build(context); |
|||
return Container( |
|||
color: Colors.white, |
|||
); |
|||
} |
|||
|
|||
@override |
|||
bool get wantKeepAlive => true; |
|||
} |
|||
@ -0,0 +1,24 @@ |
|||
import 'package:flutter/cupertino.dart'; |
|||
import 'package:flutter/material.dart'; |
|||
|
|||
class HomePage extends StatefulWidget { |
|||
const HomePage({super.key}); |
|||
|
|||
@override |
|||
State<HomePage> createState() => _HomePageState(); |
|||
} |
|||
|
|||
class _HomePageState extends State<HomePage> with AutomaticKeepAliveClientMixin{ |
|||
|
|||
@override |
|||
Widget build(BuildContext context) { |
|||
super.build(context); |
|||
return Container( |
|||
color: Colors.white, |
|||
); |
|||
} |
|||
|
|||
@override |
|||
// TODO: implement wantKeepAlive |
|||
bool get wantKeepAlive => true; |
|||
} |
|||
@ -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<MainPage> createState() => _MainPageState(); |
|||
} |
|||
|
|||
class _MainPageState extends State<MainPage> { |
|||
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<UserApi>(); |
|||
|
|||
// 初始化页面实例 |
|||
homePage = HomePage(); |
|||
discoverPage = DiscoverPage(); |
|||
messagePage = MessagePage(); |
|||
minePage = MinePage(); |
|||
|
|||
// 检查token并调用获取婚姻信息详情的方法 |
|||
checkTokenAndFetchMarriageInfo(); |
|||
} |
|||
|
|||
// 检查token并获取婚姻信息详情 |
|||
Future<void> 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); |
|||
}, |
|||
), |
|||
], |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -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<MainTabBar> createState() => _MainTabBarState(); |
|||
} |
|||
|
|||
class _MainTabBarState extends State<MainTabBar> { |
|||
int _selecteIndex = 0; |
|||
|
|||
final List<MainTabItem> 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, |
|||
) |
|||
], |
|||
) |
|||
], |
|||
), |
|||
), |
|||
); |
|||
} |
|||
}); |
|||
} |
|||
} |
|||
@ -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<MainTabButton> createState() => MainTabButtonState(); |
|||
} |
|||
|
|||
class MainTabButtonState extends State<MainTabButton> { |
|||
@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 |
|||
),) |
|||
], |
|||
), |
|||
), |
|||
), |
|||
); |
|||
} |
|||
} |
|||
@ -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, |
|||
}); |
|||
} |
|||
@ -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<MainPage> createState() => _MainPageState(); |
|||
} |
|||
|
|||
class _MainPageState extends State<MainPage> { |
|||
late UserApi _userApi; |
|||
final storage = GetStorage(); |
|||
|
|||
@override |
|||
void initState() { |
|||
super.initState(); |
|||
// 获取UserApi实例 |
|||
_userApi = Get.find<UserApi>(); |
|||
|
|||
// 检查token并调用获取婚姻信息详情的方法 |
|||
checkTokenAndFetchMarriageInfo(); |
|||
} |
|||
|
|||
// 检查token并获取婚姻信息详情 |
|||
Future<void> 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(); |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
import 'package:flutter/material.dart'; |
|||
|
|||
class MessagePage extends StatefulWidget { |
|||
const MessagePage({super.key}); |
|||
|
|||
@override |
|||
State<MessagePage> createState() => _MessagePageState(); |
|||
} |
|||
|
|||
class _MessagePageState extends State<MessagePage> with AutomaticKeepAliveClientMixin{ |
|||
@override |
|||
Widget build(BuildContext context) { |
|||
super.build(context); |
|||
return Container( |
|||
color: Colors.white, |
|||
); |
|||
} |
|||
|
|||
@override |
|||
// TODO: implement wantKeepAlive |
|||
bool get wantKeepAlive => true; |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
import 'package:flutter/material.dart'; |
|||
|
|||
class MinePage extends StatefulWidget { |
|||
const MinePage({super.key}); |
|||
|
|||
@override |
|||
State<MinePage> createState() => _MinePageState(); |
|||
} |
|||
|
|||
class _MinePageState extends State<MinePage> with AutomaticKeepAliveClientMixin{ |
|||
@override |
|||
Widget build(BuildContext context) { |
|||
return Container( |
|||
color: Colors.white, |
|||
); |
|||
} |
|||
|
|||
@override |
|||
// TODO: implement wantKeepAlive |
|||
bool get wantKeepAlive => true; |
|||
} |
|||
@ -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<DoubleTapToExitWidget> createState() => DoubleTapToExitWidgetState(); |
|||
} |
|||
|
|||
class DoubleTapToExitWidgetState extends State<DoubleTapToExitWidget> { |
|||
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); |
|||
} |
|||
} |
|||
308
pubspec.lock
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save