Browse Source

增加首页tab

ios
Jolie 4 months ago
parent
commit
52017524a6
29 changed files with 1810 additions and 172 deletions
  1. BIN
      assets/images/discover_nol.png
  2. BIN
      assets/images/discover_pre.png
  3. BIN
      assets/images/home_nol.png
  4. BIN
      assets/images/home_pre.png
  5. BIN
      assets/images/message_nol.png
  6. BIN
      assets/images/message_pre.png
  7. BIN
      assets/images/mine_nol.png
  8. BIN
      assets/images/mine_pre.png
  9. 13
      lib/extension/ex_context.dart
  10. 102
      lib/extension/ex_date.dart
  11. 44
      lib/extension/ex_int.dart
  12. 17
      lib/extension/ex_string.dart
  13. 936
      lib/extension/ex_widget.dart
  14. 17
      lib/generated/assets.dart
  15. 5
      lib/main.dart
  16. 21
      lib/pages/discover/discover_page.dart
  17. 24
      lib/pages/home/home_page.dart
  18. 91
      lib/pages/main/main_page.dart
  19. 182
      lib/pages/main/tabbar/main_tab_bar.dart
  20. 77
      lib/pages/main/tabbar/main_tab_btn.dart
  21. 13
      lib/pages/main/tabbar/main_tab_item.dart
  22. 45
      lib/pages/main_page.dart
  23. 22
      lib/pages/message/message_page.dart
  24. 2
      lib/pages/mine/login_controller.dart
  25. 21
      lib/pages/mine/mine_page.dart
  26. 2
      lib/pages/mine/user_info_controller.dart
  27. 39
      lib/widget/double_tap_to_exit_widget.dart
  28. 308
      pubspec.lock
  29. 1
      pubspec.yaml

BIN
assets/images/discover_nol.png

Before After
Width: 186  |  Height: 186  |  Size: 7.0 KiB

BIN
assets/images/discover_pre.png

Before After
Width: 186  |  Height: 186  |  Size: 6.4 KiB

BIN
assets/images/home_nol.png

Before After
Width: 198  |  Height: 198  |  Size: 7.3 KiB

BIN
assets/images/home_pre.png

Before After
Width: 198  |  Height: 198  |  Size: 7.3 KiB

BIN
assets/images/message_nol.png

Before After
Width: 186  |  Height: 186  |  Size: 7.1 KiB

BIN
assets/images/message_pre.png

Before After
Width: 186  |  Height: 186  |  Size: 7.3 KiB

BIN
assets/images/mine_nol.png

Before After
Width: 198  |  Height: 198  |  Size: 6.1 KiB

BIN
assets/images/mine_pre.png

Before After
Width: 198  |  Height: 198  |  Size: 7.4 KiB

13
lib/extension/ex_context.dart

@ -0,0 +1,13 @@
import 'package:flutter/material.dart';
extension BuildContextExtension on BuildContext {
MediaQueryData get mediaQuery => MediaQuery.of(this);
double get topPadding => mediaQuery.padding.top;
double get bottomPadding => mediaQuery.padding.bottom;
double get bottomInsets => mediaQuery.viewInsets.bottom;
EdgeInsets get viewInsets => mediaQuery.viewInsets;
}

102
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();
}
}

44
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";
}
}
}

17
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<String, String> get queryParameters {
return Uri.tryParse(this)?.queryParameters ?? {};
}
}

936
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>(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;
}
}

17
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';
}

5
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();
}

21
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<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;
}

24
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<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;
}

91
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<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);
},
),
],
),
),
);
}
}

182
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<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,
)
],
)
],
),
),
);
}
});
}
}

77
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<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
),)
],
),
),
),
);
}
}

13
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,
});
}

45
lib/pages/main_page.dart

@ -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();
}
}

22
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<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;
}

2
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';

21
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<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;
}

2
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 {
//

39
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<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

1
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:

Loading…
Cancel
Save