From ef31d9a2e658b4e2ce0956ba939f8865734a6310 Mon Sep 17 00:00:00 2001 From: 18631081161 <2088094923@qq.com> Date: Tue, 23 Jul 2024 17:57:57 +0800 Subject: [PATCH] =?UTF-8?q?=E9=95=BF=E6=8C=89=E5=BC=B9=E7=AA=97.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- assets/images/ic_copy.png | Bin 0 -> 678 bytes assets/images/ic_delete.png | Bin 0 -> 746 bytes assets/images/ic_pearl.png | Bin 0 -> 34173 bytes lib/beans/message_bean.dart | 3 +- lib/beans/message_bean.g.dart | 2 + lib/common/func.dart | 59 ++++ lib/custom/DynamicText.dart | 39 +++ lib/custom/custom_popup.dart | 409 ++++++++++++++++++++++++++++ lib/dialog/delete_dialog.dart | 36 +-- lib/main.dart | 2 + lib/tools/chat/chat_page.dart | 182 +++++++++---- lib/tools/home/home_chat_page.dart | 311 +++++++++++++-------- lib/tools/home/test_page.dart | 60 ++++ lib/tools/home_page.dart | 51 ++-- lib/tools/login/login_page.dart | 2 +- lib/tools/me/me_page.dart | 219 ++++++++------- lib/tools/message/message_page.dart | 4 +- lib/tools/shop/account_page.dart | 16 +- pubspec.yaml | 1 - 19 files changed, 1069 insertions(+), 327 deletions(-) create mode 100644 assets/images/ic_copy.png create mode 100644 assets/images/ic_delete.png create mode 100644 assets/images/ic_pearl.png create mode 100644 lib/common/func.dart create mode 100644 lib/custom/DynamicText.dart create mode 100644 lib/custom/custom_popup.dart create mode 100644 lib/tools/home/test_page.dart diff --git a/assets/images/ic_copy.png b/assets/images/ic_copy.png new file mode 100644 index 0000000000000000000000000000000000000000..c8eaa895ae9ad1027e98c1fb72a7a785ba7788d0 GIT binary patch literal 678 zcmV;X0$KfuP)Px%U`a$lR9HvtmrICFVHn4Mf4P@?Eaw|3ltw9~*_e&Bl^EB;&ZXRnjm?yW>q24U z)|4VqvQ)B?L@0`E+_Dj)upr~_eZJ18F@5u$^Ue49CN*!J&f>h!`~1%TJmDIQreg-!Zw7jEA#Cpjjsb6gO~A_r zA;to$faAa`U}+JEJHT>ayQDJ>0{k(zeG6Ctl!`z+0p>?cNV?fXh)_3FZZ87y7+3&w zON!WN%Ca4wV^2L03xPE)fv_EsJ3WiwK1uzyLy6Wv*ggbo1SSBH7rG=pY6*nxi75e{ z2i5}pz>f@~Q_{UwL97EV0q+vTLEs}WF8!9bC4Fuc#Pclrmq?25|JQ02gfp*glHQIq zL`hPl<`Gq56rvInqe}cYN<?>DM+j&Yn06KsTlCCzTjsv#OrEYCYQHWE( zwghr5yMHa(o7MoHm0G-~z<6M85hZ2=&w#kU0A3GjR_hTbR1e3?fGC`2fgQOJ;lcJi z;Ci|i*W)o~?*(A1Bo~6H9A>)&Obf0CU}9u=FR8C`MqLmMIId~>&me5?Of$-Q;IyRP zS`-5zs+RKMzP;@e!0yzg9hP(;58@jzIT6p1^kKM8tF!E=r2o zu;FF99f&S1g1AFcy7S(u?8tUk zN=Bi`qCA6w&%dNu<#IU@NzuL6{yPTbfEihA3`c>%PJRNPiU5S+Z^$NgQMaYDy#N3J M07*qoM6N<$f)lJQ5&!@I literal 0 HcmV?d00001 diff --git a/assets/images/ic_delete.png b/assets/images/ic_delete.png new file mode 100644 index 0000000000000000000000000000000000000000..39c3934cf745ded6e7b22d3934a94a11316f77e2 GIT binary patch literal 746 zcmVPx%q)9|UR9HvtmrIC_VHC%If3uO0g-fAKlyX_JkOeZ-%T^9HqZCY_w~&veP{LdK9}=*&U4Or&JhJ- zW@CWWz%gKY(frJ~fG@x);D)5%skJ__teDwh;84-Lf^pyDh@?M5%_|FGW>bLIz|>%c z%fM}*5IidS1*`>j0)4<-NuR2LSPDD{mhclSm-MO@Gh=tl=Pt*h{ZrR;P<&i zQg4M2(}2?HXG;2*gotKem@~DXegSYYT4EdUKHRN4>Ttxt-I4Fc$?n`g0n03I0D5I+ zljFTR^}mpQhnNW548-$ul%oOUR((6*>1H+#7&{tTb2?oA_hh(vl1jyO!B|5oF{RcG z31MavfYZPucdRUonau_c0+%Gc$ezs1RsidOE=fJPF%S3kz*R{vvtucUHNYLxq-h@5pIYbECt&%*M4M+-NGjIiX3N%adj5i== zwm2-Jk)C~A(y}8LMnh~D=Wx#z&Uq=igyKyDoHMK+~iW8RT z3RQdCa9+{2@b+H=o`vVUNz%7!m1i8+n;>t7h7IARnAZ|6t+6HWqNN(~{$)|DPsRgh zLtD64(qR8luFx%!SED$!=5s6;pJykOgP0Rd=clNJcnjGs>0W-bqORPl?nyNua6Ntb ckS^`}7gMA2_gA*(p8x;=07*qoM6N<$f(QR#kN^Mx literal 0 HcmV?d00001 diff --git a/assets/images/ic_pearl.png b/assets/images/ic_pearl.png new file mode 100644 index 0000000000000000000000000000000000000000..6f6b91e440cd68b04d5c1700a523217232146850 GIT binary patch literal 34173 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 literal 0 HcmV?d00001 diff --git a/lib/beans/message_bean.dart b/lib/beans/message_bean.dart index 8ee332c..f82651e 100644 --- a/lib/beans/message_bean.dart +++ b/lib/beans/message_bean.dart @@ -10,8 +10,9 @@ class MessageBean { String? biography; String? iconImage; String? lastContactTime; + String? lastMessage; - MessageBean(this.id, this.name, this.biography, this.iconImage, this.lastContactTime); + MessageBean(this.id, this.name, this.biography, this.iconImage, this.lastContactTime, this.lastMessage); factory MessageBean.fromJson(Map json) => _$MessageBeanFromJson(json); diff --git a/lib/beans/message_bean.g.dart b/lib/beans/message_bean.g.dart index 8cd17c8..f59dc05 100644 --- a/lib/beans/message_bean.g.dart +++ b/lib/beans/message_bean.g.dart @@ -12,6 +12,7 @@ MessageBean _$MessageBeanFromJson(Map json) => MessageBean( json['biography'] as String?, json['iconImage'] as String?, json['lastContactTime'] as String?, + json['lastMessage'] as String?, ); Map _$MessageBeanToJson(MessageBean instance) => @@ -21,4 +22,5 @@ Map _$MessageBeanToJson(MessageBean instance) => 'biography': instance.biography, 'iconImage': instance.iconImage, 'lastContactTime': instance.lastContactTime, + 'lastMessage': instance.lastMessage, }; diff --git a/lib/common/func.dart b/lib/common/func.dart new file mode 100644 index 0000000..c993f40 --- /dev/null +++ b/lib/common/func.dart @@ -0,0 +1,59 @@ +import 'package:flutter/material.dart'; + +class FunctionUtil { + static BuildContext? dialogContext; + + //显示中间弹窗 + static void popDialog(BuildContext context, Widget widget) { + showDialog( + context: context, + barrierDismissible: true, + builder: (BuildContext context) { + dialogContext = context; + return widget; + }); + } + + //显示底部弹窗 + static void bottomSheetDialog(BuildContext context, Widget widget) { + showModalBottomSheet( + backgroundColor: const Color(0x00FFFFFF), + context: context, + /* isDismissible: false,*/ + isScrollControlled: true, + builder: (BuildContext context) { + dialogContext = context; + return widget; + }, + ); + } + + //显示底部弹窗 + static void bottomNoSheetDialog(BuildContext context, Widget widget) { + showModalBottomSheet( + backgroundColor: Color(0x00FFFFFF), + context: context, + isDismissible: false, + isScrollControlled: true, + enableDrag: false, + builder: (BuildContext context) { + dialogContext = context; + return WillPopScope(onWillPop: () async => false, child: widget); + }); + } + + //返回上一级 + static void pop() { + Navigator.pop(dialogContext!); + } + + //push到下一级 + static Future push(BuildContext context, Widget widget) { + return Navigator.push( + context, + MaterialPageRoute( + builder: (context) => widget, + ), + ); + } +} diff --git a/lib/custom/DynamicText.dart b/lib/custom/DynamicText.dart new file mode 100644 index 0000000..343f005 --- /dev/null +++ b/lib/custom/DynamicText.dart @@ -0,0 +1,39 @@ +import 'package:flutter/cupertino.dart'; + +class DynamicText extends StatelessWidget { + final String text; + final TextStyle highlightedStyle; + final TextStyle normalStyle; + + DynamicText({required this.text, required this.highlightedStyle, required this.normalStyle}); + + @override + Widget build(BuildContext context) { + return RichText( + text: _buildTextSpan(text, highlightedStyle, normalStyle), + ); + } + + TextSpan _buildTextSpan(String text, TextStyle highlightedStyle, TextStyle normalStyle) { + List spans = []; + final RegExp regExp = RegExp(r'\*([^*]+)\*'); + int lastIndex = 0; + + for (final match in regExp.allMatches(text)) { + if (match.start > lastIndex) { + spans.add(TextSpan(text: text.substring(lastIndex, match.start), style: normalStyle)); + } + spans.add(TextSpan( + text: '(${match.group(1)})', + style: highlightedStyle, + )); + lastIndex = match.end; + } + + if (lastIndex < text.length) { + spans.add(TextSpan(text: text.substring(lastIndex), style: normalStyle)); + } + + return TextSpan(children: spans); + } +} \ No newline at end of file diff --git a/lib/custom/custom_popup.dart b/lib/custom/custom_popup.dart new file mode 100644 index 0000000..8b0d548 --- /dev/null +++ b/lib/custom/custom_popup.dart @@ -0,0 +1,409 @@ +import 'dart:io'; +import 'dart:math' as math; + +import 'package:flutter/material.dart'; + +enum PressType { + longPress, + singleClick, +} + +enum PreferredPosition { + top, + bottom, +} + +class CustomPopupMenuController extends ChangeNotifier { + bool menuIsShowing = false; + + void showMenu() { + menuIsShowing = true; + notifyListeners(); + } + + void hideMenu() { + menuIsShowing = false; + notifyListeners(); + } + + void toggleMenu() { + menuIsShowing = !menuIsShowing; + notifyListeners(); + } +} + +Rect _menuRect = Rect.zero; + +class CustomPopup extends StatefulWidget { + CustomPopup({ + required this.child, + required this.menuBuilder, + required this.pressType, + this.controller, + this.arrowColor = const Color(0xFFF14343), + this.showArrow = true, + this.barrierColor = Colors.black12, + this.arrowSize = 10.0, + this.horizontalMargin = 10.0, + this.verticalMargin = 1.0, + this.position, + this.menuOnChange, + this.enablePassEvent = true, + }); + + final Widget child; + final PressType pressType; + final bool showArrow; + final Color arrowColor; + final Color barrierColor; + final double horizontalMargin; + final double verticalMargin; + final double arrowSize; + final CustomPopupMenuController? controller; + final Widget Function() menuBuilder; + final PreferredPosition? position; + final void Function(bool)? menuOnChange; + + /// Pass tap event to the widgets below the mask. + /// It only works when [barrierColor] is transparent. + final bool enablePassEvent; + + @override + _CustomPopupState createState() => _CustomPopupState(); +} + +class _CustomPopupState extends State { + RenderBox? _childBox; + RenderBox? _parentBox; + OverlayEntry? _overlayEntry; + CustomPopupMenuController? _controller; + bool _canResponse = true; + + _showMenu() { + _overlayEntry = OverlayEntry( + builder: (context) { + Widget menu = Center( + child: Container( + constraints: BoxConstraints( + maxWidth: _parentBox!.size.width - 2 * widget.horizontalMargin, + minWidth: 0, + ), + child: CustomMultiChildLayout( + delegate: _MenuLayoutDelegate( + anchorSize: _childBox!.size, + anchorOffset: _childBox!.localToGlobal( + Offset(-widget.horizontalMargin, 0), + ), + verticalMargin: widget.verticalMargin, + position: widget.position, + ), + children: [ + LayoutId( + id: _MenuLayoutId.content, + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Material( + child: widget.menuBuilder(), + color: Colors.transparent, + ), + ], + ), + ), + ], + ), + ), + ); + return Listener( + behavior: widget.enablePassEvent + ? HitTestBehavior.translucent + : HitTestBehavior.opaque, + onPointerDown: (PointerDownEvent event) { + Offset offset = event.localPosition; + // If tap position in menu + if (_menuRect.contains( + Offset(offset.dx - widget.horizontalMargin, offset.dy))) { + return; + } + _controller?.hideMenu(); + // When [enablePassEvent] works and we tap the [child] to [hideMenu], + // but the passed event would trigger [showMenu] again. + // So, we use time threshold to solve this bug. + _canResponse = false; + Future.delayed(Duration(milliseconds: 300)) + .then((_) => _canResponse = true); + }, + child: widget.barrierColor == Colors.transparent + ? menu + : Container( + color: widget.barrierColor, + child: menu, + ), + ); + }, + ); + if (_overlayEntry != null) { + Overlay.of(context)!.insert(_overlayEntry!); + } + } + + _hideMenu() { + if (_overlayEntry != null) { + _overlayEntry?.remove(); + _overlayEntry = null; + } + } + + _updateView() { + bool menuIsShowing = _controller?.menuIsShowing ?? false; + widget.menuOnChange?.call(menuIsShowing); + if (menuIsShowing) { + _showMenu(); + } else { + _hideMenu(); + } + } + + @override + void initState() { + super.initState(); + _controller = widget.controller; + if (_controller == null) _controller = CustomPopupMenuController(); + _controller?.addListener(_updateView); + WidgetsBinding.instance.addPostFrameCallback((call) { + if (mounted) { + _childBox = context.findRenderObject() as RenderBox?; + _parentBox = + Overlay.of(context)?.context.findRenderObject() as RenderBox?; + } + }); + } + + @override + void dispose() { + _hideMenu(); + _controller?.removeListener(_updateView); + super.dispose(); + } + + @override + Widget build(BuildContext context) { + var child = Material( + child: InkWell( + hoverColor: Colors.transparent, + focusColor: Colors.transparent, + splashColor: Colors.transparent, + highlightColor: Colors.transparent, + child: widget.child, + onTap: () { + if (widget.pressType == PressType.singleClick && _canResponse) { + _controller?.showMenu(); + } + }, + onLongPress: () { + if (widget.pressType == PressType.longPress && _canResponse) { + _controller?.showMenu(); + } + }, + ), + color: Colors.transparent, + ); + if (Platform.isIOS) { + return child; + } else { + return WillPopScope( + onWillPop: () { + _hideMenu(); + return Future.value(true); + }, + child: child, + ); + } + } +} + +enum _MenuLayoutId { + arrow, + downArrow, + content, +} + +enum _MenuPosition { + bottomLeft, + bottomCenter, + bottomRight, + topLeft, + topCenter, + topRight, +} + +class _MenuLayoutDelegate extends MultiChildLayoutDelegate { + _MenuLayoutDelegate({ + required this.anchorSize, + required this.anchorOffset, + required this.verticalMargin, + this.position, + }); + + final Size anchorSize; + final Offset anchorOffset; + final double verticalMargin; + final PreferredPosition? position; + + @override + void performLayout(Size size) { + Size contentSize = Size.zero; + Size arrowSize = Size.zero; + Offset contentOffset = Offset(0, 0); + // Offset arrowOffset = Offset(0, 0); + + double anchorCenterX = anchorOffset.dx + anchorSize.width / 2; + double anchorTopY = anchorOffset.dy; + double anchorBottomY = anchorTopY + anchorSize.height; + _MenuPosition menuPosition = _MenuPosition.bottomCenter; + + if (hasChild(_MenuLayoutId.content)) { + contentSize = layoutChild( + _MenuLayoutId.content, + BoxConstraints.loose(size), + ); + } + if (hasChild(_MenuLayoutId.arrow)) { + arrowSize = layoutChild( + _MenuLayoutId.arrow, + BoxConstraints.loose(size), + ); + } + if (hasChild(_MenuLayoutId.downArrow)) { + layoutChild( + _MenuLayoutId.downArrow, + BoxConstraints.loose(size), + ); + } + + bool isTop = false; + if (position == null) { + // auto calculate position + isTop = anchorBottomY > size.height / 2; + } else { + isTop = position == PreferredPosition.top; + } + if (anchorCenterX - contentSize.width / 2 < 0) { + menuPosition = isTop ? _MenuPosition.topLeft : _MenuPosition.bottomLeft; + } else if (anchorCenterX + contentSize.width / 2 > size.width) { + menuPosition = isTop ? _MenuPosition.topRight : _MenuPosition.bottomRight; + } else { + menuPosition = + isTop ? _MenuPosition.topCenter : _MenuPosition.bottomCenter; + } + + switch (menuPosition) { + case _MenuPosition.bottomCenter: + // arrowOffset = Offset( + // anchorCenterX - arrowSize.width / 2, + // anchorBottomY + verticalMargin, + // ); + contentOffset = Offset( + anchorCenterX - contentSize.width / 2, + anchorBottomY + verticalMargin + arrowSize.height, + ); + break; + case _MenuPosition.bottomLeft: + // arrowOffset = Offset(anchorCenterX - arrowSize.width / 2, + // anchorBottomY + verticalMargin); + contentOffset = Offset( + 0, + anchorBottomY + verticalMargin + arrowSize.height, + ); + break; + case _MenuPosition.bottomRight: + // arrowOffset = Offset(anchorCenterX - arrowSize.width / 2, + // anchorBottomY + verticalMargin); + contentOffset = Offset( + size.width - contentSize.width, + anchorBottomY + verticalMargin + arrowSize.height, + ); + break; + case _MenuPosition.topCenter: + // arrowOffset = Offset( + // anchorCenterX - arrowSize.width / 2, + // anchorTopY - verticalMargin - arrowSize.height, + // ); + contentOffset = Offset( + anchorCenterX - contentSize.width / 2, + anchorTopY - verticalMargin - arrowSize.height - contentSize.height, + ); + break; + case _MenuPosition.topLeft: + // arrowOffset = Offset( + // anchorCenterX - arrowSize.width / 2, + // anchorTopY - verticalMargin - arrowSize.height, + // ); + contentOffset = Offset( + 0, + anchorTopY - verticalMargin - arrowSize.height - contentSize.height, + ); + break; + case _MenuPosition.topRight: + // arrowOffset = Offset( + // anchorCenterX - arrowSize.width / 2, + // anchorTopY - verticalMargin - arrowSize.height, + // ); + contentOffset = Offset( + size.width - contentSize.width, + anchorTopY - verticalMargin - arrowSize.height - contentSize.height, + ); + break; + } + if (hasChild(_MenuLayoutId.content)) { + positionChild(_MenuLayoutId.content, contentOffset); + } + + _menuRect = Rect.fromLTWH( + contentOffset.dx, + contentOffset.dy, + contentSize.width, + contentSize.height, + ); + bool isBottom = false; + if (_MenuPosition.values.indexOf(menuPosition) < 3) { + // bottom + isBottom = true; + } + // if (hasChild(_MenuLayoutId.arrow)) { + // positionChild( + // _MenuLayoutId.arrow, + // isBottom + // ? Offset(arrowOffset.dx, arrowOffset.dy + 0.1) + // : Offset(-100, 0), + // ); + // } + // if (hasChild(_MenuLayoutId.downArrow)) { + // positionChild( + // _MenuLayoutId.downArrow, + // !isBottom + // ? Offset(arrowOffset.dx, arrowOffset.dy - 0.1) + // : Offset(-100, 0), + // ); + // } + } + + @override + bool shouldRelayout(MultiChildLayoutDelegate oldDelegate) => false; +} + +class _ArrowClipper extends CustomClipper { + @override + Path getClip(Size size) { + Path path = Path(); + path.moveTo(0, size.height); + path.lineTo(size.width / 2, size.height / 2); + path.lineTo(size.width, size.height); + return path; + } + + @override + bool shouldReclip(CustomClipper oldClipper) { + return true; + } +} diff --git a/lib/dialog/delete_dialog.dart b/lib/dialog/delete_dialog.dart index 8335ca5..ef9f23f 100644 --- a/lib/dialog/delete_dialog.dart +++ b/lib/dialog/delete_dialog.dart @@ -23,19 +23,22 @@ class _DeleteDialogState extends State { color: Color(0x1A000000), child: Center( child: ClipRRect( - borderRadius: BorderRadius.circular(10.0), + borderRadius: BorderRadius.circular(7.0), child: Container( - width: 280, - color: Colors.white, + width: 247, + color: Color(0xFF272727), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( - margin: EdgeInsets.only(top: 34), - child: Text('确认删除该聊天记录吗'), + margin: EdgeInsets.only(top: 28), + child: Text( + '确认删除该条聊天记录吗?', + style: TextStyle(fontSize: 15, color: Color(0xFFBABABA)), + ), ), Container( - margin: EdgeInsets.only(top: 36, left: 22, right: 22, bottom: 19), + margin: EdgeInsets.only(top: 38, left: 22, right: 22, bottom: 19), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ @@ -44,30 +47,31 @@ class _DeleteDialogState extends State { Navigator.pop(context); }, child: Container( - width: 108, - height: 43, + width: 80, + height: 28, alignment: Alignment.center, decoration: BoxDecoration( - borderRadius: BorderRadius.all(Radius.circular(12)), - border: Border.all(color: Color(0xFFD9D9D9), width: 1), + borderRadius: BorderRadius.all(Radius.circular(14)), + border: Border.all(color: Color(0xFFFF9000), width: 1), ), child: Text( '取消', - style: TextStyle(color: Color(0xFFD9D9D9), fontSize: 12), + style: TextStyle(color: Color(0xFFFF9000), fontSize: 12), ), ), ), GestureDetector( onTap: () { - widget.onTap(1); + Navigator.pop(context); + widget.onTap(); }, child: Container( - width: 108, - height: 43, + width: 80, + height: 28, alignment: Alignment.center, decoration: BoxDecoration( - color: Color(0xFFD9D9D9), - borderRadius: BorderRadius.all(Radius.circular(12)), + color: Color(0xFFFF9000), + borderRadius: BorderRadius.all(Radius.circular(14)), ), child: Text( '确定', diff --git a/lib/main.dart b/lib/main.dart index d425882..d3b3dc8 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -4,6 +4,7 @@ import 'dart:io'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; +import 'package:talk/tools/home/test_page.dart'; import 'package:talk/tools/home_page.dart'; import 'package:talk/tools/login/login_page.dart'; import 'package:talk/tools/me/setting_page.dart'; @@ -60,6 +61,7 @@ class _ChatAppState extends State { '/ProblemPage': (BuildContext context) => const ProblemPage(), '/ShopPage': (BuildContext context) => const ShopPage(), '/SettingPage': (BuildContext context) => const SettingPage(), + '/TestPage': (BuildContext context) => const TestPage(), }, debugShowMaterialGrid: false, //显示网格 diff --git a/lib/tools/chat/chat_page.dart b/lib/tools/chat/chat_page.dart index 2b65ae5..eefd79f 100644 --- a/lib/tools/chat/chat_page.dart +++ b/lib/tools/chat/chat_page.dart @@ -3,12 +3,15 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import '../../beans/character_info_bean.dart'; import '../../beans/chat_info_bean.dart'; import '../../beans/send_message_bean.dart'; +import '../../custom/custom_popup.dart'; +import 'chat_info_page.dart'; import 'chat_model.dart'; class ChatPage extends StatefulWidget { @@ -150,7 +153,7 @@ class _ChatPageState extends State { } ///删除消息 - delChat(int id, index) { + deleteChat(int id, index) { EasyLoading.show(status: 'loading...'); delIndex = index; List ids = [id]; @@ -242,10 +245,13 @@ class _ChatPageState extends State { ///AI头像 GestureDetector( onTap: () { - // Navigator.push( - // context, - // MaterialPageRoute(builder: (context) => ChatInfoPage()), - // ); + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => ChatInfoPage( + data: characterInfoBean!, + )), + ); }, child: Container( margin: EdgeInsets.only(left: 2), @@ -373,7 +379,8 @@ class _ChatPageState extends State { controller: _scrollController, itemCount: chatList.length, itemBuilder: (BuildContext context, int index) { - return _item(index); + final Key key = ValueKey(index); + return _item(index, key); }), ), )), @@ -456,18 +463,18 @@ class _ChatPageState extends State { )), ///智能输入 - Container( - margin: const EdgeInsets.only(left: 14, right: 14), - child: GestureDetector( - onTap: () { - Navigator.of(context).pushNamed('/LoginPage'); - }, - child: Image( - width: 27, - image: AssetImage('assets/images/ic_smart_chat.png'), - ), - ), - ), + // Container( + // margin: const EdgeInsets.only(left: 14, right: 14), + // child: GestureDetector( + // onTap: () { + // Navigator.of(context).pushNamed('/LoginPage'); + // }, + // child: Image( + // width: 27, + // image: AssetImage('assets/images/ic_smart_chat.png'), + // ), + // ), + // ), ///功能 更多 GestureDetector( @@ -475,9 +482,12 @@ class _ChatPageState extends State { isMore = !isMore; setState(() {}); }, - child: Image( - width: 27, - image: AssetImage('assets/images/ic_more.png'), + child: Container( + margin: EdgeInsets.only(left: 14), + child: Image( + width: 27, + image: AssetImage('assets/images/ic_more.png'), + ), ), ), ], @@ -571,16 +581,18 @@ class _ChatPageState extends State { } ///聊天条目 - _item(index) { + _item(index, key) { // if (index == 0) { // return Container(); // } + CustomPopupMenuController customController = CustomPopupMenuController(); ///简介 if (chatList[index].role == 'profile') { return Center( + key: key, child: Container( - margin: EdgeInsets.only(left: 16, right: 16), + margin: EdgeInsets.only(left: 16, right: 16, bottom: 20), padding: EdgeInsets.only(left: 20, right: 20, top: 12, bottom: 12), decoration: BoxDecoration(color: Color(0x99000000), borderRadius: BorderRadius.all(Radius.circular(13))), child: ExpandableText( @@ -609,21 +621,26 @@ class _ChatPageState extends State { ///提示 if (chatList[index].role == 'tips') { return Center( - child: Container( - padding: const EdgeInsets.only(left: 9, right: 9, top: 6, bottom: 6), - decoration: const BoxDecoration( - color: Color(0xCC000000), - borderRadius: BorderRadius.all(Radius.circular(15)), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(chatList[index].content!, style: TextStyle(color: Color(0xFFB6B6B6), fontSize: 10)), - Container( - margin: const EdgeInsets.only(left: 10), - child: const Text("立即增加", style: TextStyle(color: Color(0xFFFF9000), fontSize: 10)), - ), - ], + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/AccountPage'); + }, + child: Container( + padding: const EdgeInsets.only(left: 9, right: 9, top: 6, bottom: 6), + decoration: const BoxDecoration( + color: Color(0xCC000000), + borderRadius: BorderRadius.all(Radius.circular(15)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(chatList[index].content!, style: TextStyle(color: Color(0xFFB6B6B6), fontSize: 10)), + Container( + margin: const EdgeInsets.only(left: 10), + child: const Text("立即增加", style: TextStyle(color: Color(0xFFFF9000), fontSize: 10)), + ), + ], + ), ), ), ); @@ -631,10 +648,11 @@ class _ChatPageState extends State { ///聊天内容 return Padding( - padding: const EdgeInsets.symmetric(vertical: 8.0), + padding: const EdgeInsets.only(bottom: 8), child: chatList[index].role != 'user' ? Container( ///AI + key: key, alignment: Alignment.centerLeft, child: Stack( children: [ @@ -645,19 +663,22 @@ class _ChatPageState extends State { constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 50, // 确保不超过屏幕宽度 ), - child: Container( - margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), - padding: const EdgeInsets.all(11.0), - decoration: const BoxDecoration( - color: Color(0x99FF9000), - borderRadius: BorderRadius.only( - topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), - ), - child: GestureDetector( - onTap: () { - EasyLoading.showToast("status${chatList[index].id}"); - delChat(chatList[index].id!, index); - }, + child: CustomPopup( + controller: customController, + menuBuilder: () { + return popupView(chatList[index].id!, chatList[index].content!, index, customController); + }, + barrierColor: Colors.transparent, + //触发方式 + pressType: PressType.longPress, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), + padding: const EdgeInsets.all(11.0), + decoration: const BoxDecoration( + color: Color(0x99FF9000), + borderRadius: BorderRadius.only( + topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), + ), child: Text( chatList[index].content!, style: const TextStyle( @@ -730,4 +751,61 @@ class _ChatPageState extends State { ), ); } + + popupView(int id, String content, index, customController) { + return Container( + width: 135, + height: 40, + decoration: BoxDecoration( + color: Color(0xFF222222), + borderRadius: BorderRadius.all(Radius.circular(2)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: GestureDetector( + onTap: () { + EasyLoading.showToast("内容已复制"); + Clipboard.setData(ClipboardData(text: content)); + customController.hideMenu(); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image(width: 11, height: 11, image: AssetImage('assets/images/ic_copy.png')), + Container( + child: Text( + '复制', + style: TextStyle(fontSize: 8, color: Color(0xFF9D9D9D)), + ), + ) + ], + ), + ), + ), + Expanded( + child: GestureDetector( + onTap: () { + customController.hideMenu(); + deleteChat(id, index); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image(width: 11, height: 11, image: AssetImage('assets/images/ic_delete.png')), + Container( + child: Text( + '删除', + style: TextStyle(fontSize: 8, color: Color(0xFF9D9D9D)), + ), + ) + ], + ), + ), + ), + ], + ), + ); + } } diff --git a/lib/tools/home/home_chat_page.dart b/lib/tools/home/home_chat_page.dart index 64443c8..a5bae2b 100644 --- a/lib/tools/home/home_chat_page.dart +++ b/lib/tools/home/home_chat_page.dart @@ -3,12 +3,16 @@ import 'dart:async'; import 'package:cached_network_image/cached_network_image.dart'; import 'package:expandable_text/expandable_text.dart'; import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_easyloading/flutter_easyloading.dart'; import 'package:flutter_keyboard_visibility/flutter_keyboard_visibility.dart'; import '../../beans/character_info_bean.dart'; import '../../beans/chat_info_bean.dart'; import '../../beans/send_message_bean.dart'; +import '../../common/func.dart'; +import '../../custom/custom_popup.dart'; +import '../../dialog/delete_dialog.dart'; import '../../network/NetworkConfig.dart'; import '../chat/chat_info_page.dart'; import '../chat/chat_model.dart'; @@ -44,7 +48,7 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie bool isMore = false; - bool isHalf = true; + bool isHalf = false; ///输入框内容 String text = ""; @@ -140,10 +144,11 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie _viewmodel.getCharacterInfo(widget.characterId); } - ///删除消息 + ///删除单条或多条消息 deleteChat(int id, index) { EasyLoading.show(status: 'loading...'); delIndex = index; + print("delIndex==$delIndex"); List ids = [id]; _viewmodel.delChatByIds(ids, widget.characterId); } @@ -172,6 +177,11 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie @override Widget build(BuildContext context) { + final size = MediaQuery.of(context).size; + final h311 = size.width / 1.1575562700964; + final h30 = size.width / 12; + final h35 = size.width / 10.285714285714; + super.build(context); return Scaffold( resizeToAvoidBottomInset: false, @@ -189,7 +199,7 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie bottom: 0, child: Container( width: MediaQuery.of(context).size.width, - height: 311.67, + height: h311, decoration: BoxDecoration( gradient: LinearGradient( colors: [Color(0x00000000), Color(0x00000000), Color(0x00000000), Color(0xFF0C0909), Color(0xFF0C0909)], // 三色渐变数组 @@ -204,7 +214,7 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie ///title Container( width: double.infinity, - height: 30, + height: h30, margin: EdgeInsets.only(top: MediaQuery.of(context).padding.top + 9, left: 16, right: 16), child: Stack( alignment: Alignment.center, @@ -219,20 +229,15 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie Positioned( left: 0, child: Container( - width: 101, - height: 30, + height: h30, decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(14))), child: Row( crossAxisAlignment: CrossAxisAlignment.center, - mainAxisAlignment: MainAxisAlignment.spaceBetween, + mainAxisSize: MainAxisSize.min, children: [ ///AI头像 GestureDetector( onTap: () { - if (NetworkConfig.userId == "") { - Navigator.of(context).pushNamed('/LoginPage'); - return; - } Navigator.push( context, MaterialPageRoute( @@ -247,7 +252,7 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie child: CachedNetworkImage( width: 23, height: 23, - imageUrl: characterInfoBean.icon!, + imageUrl: '${characterInfoBean.icon}', errorWidget: (context, url, error) => const Icon(Icons.error), ), ), @@ -256,85 +261,90 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie ///AI名 Container( - margin: EdgeInsets.only(left: 5), + margin: EdgeInsets.only(left: 5, right: 10), child: Column( crossAxisAlignment: CrossAxisAlignment.start, mainAxisSize: MainAxisSize.min, children: [ SizedBox( - width: 35, + width: h35, child: Text( - characterInfoBean.characterName!, + "${characterInfoBean.characterName}", overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 10, color: Colors.white), ), ), - Text( - '${characterInfoBean.lookCount} 聊过', - style: TextStyle(fontSize: 7, color: Colors.white), + SizedBox( + width: h35, + child: Text( + '${characterInfoBean.lookCount} 聊过', + style: TextStyle(fontSize: 7, color: Colors.white), + ), ), ], ), ), ///心动值 - Container( - margin: EdgeInsets.only(left: 6, right: 6), - child: GestureDetector( - onTap: () { - // Navigator.push( - // context, - // MaterialPageRoute( - // builder: (context) => ChatPage( - // characterId: characterInfoBean.characterId.toString(), - // )), - // ); - }, - child: Stack( - alignment: Alignment.center, - children: [ - Image( - width: 24, - height: 21, - image: AssetImage('assets/images/ic_beckoning.png'), - ), - Text( - characterInfoBean.intimacy.toString(), - style: TextStyle(fontSize: 8, color: Colors.white), - ) - ], - ), - ), - ), + // Container( + // margin: EdgeInsets.only(left: 6, right: 6), + // child: GestureDetector( + // onTap: () { + // // Navigator.push( + // // context, + // // MaterialPageRoute( + // // builder: (context) => ChatPage( + // // id: '123', + // // )), + // // ); + // }, + // child: Stack( + // alignment: Alignment.center, + // children: [ + // Image( + // width: 24, + // height: 21, + // image: AssetImage('assets/images/ic_beckoning.png'), + // ), + // characterInfoBean != null + // ? Text( + // characterInfoBean!.intimacy.toString(), + // style: TextStyle(fontSize: 8, color: Colors.white), + // ) + // : Container(), + // ], + // ), + // ), + // ), ], ), ), ), ///关注 - Positioned( - right: 0, - child: GestureDetector( - onTap: () { - if (NetworkConfig.userId == "") { - Navigator.of(context).pushNamed('/LoginPage'); - return; - } - isHalf = !isHalf; - setState(() {}); - }, - child: Container( - width: 50, - height: 24, - alignment: Alignment.center, - decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(12))), - child: Text( - '+ 关注', - style: TextStyle(fontSize: 12, color: Colors.white), - ), - ), - ), - ), + // Positioned( + // right: 0, + // child: GestureDetector( + // onTap: () { + // if (NetworkConfig.userId == "") { + // Navigator.of(context).pushNamed('/LoginPage'); + // return; + // } + // isHalf = !isHalf; + // setState(() {}); + // }, + // child: Container( + // width: 50, + // height: 24, + // alignment: Alignment.center, + // decoration: BoxDecoration(color: Color(0x33000000), borderRadius: BorderRadius.all(Radius.circular(12))), + // child: Text( + // '+ 关注', + // style: TextStyle(fontSize: 12, color: Colors.white), + // ), + // ), + // ), + // ), ], ), ), @@ -453,18 +463,18 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie )), ///智能输入 - Container( - margin: const EdgeInsets.only(left: 14, right: 14), - child: GestureDetector( - onTap: () { - // Navigator.of(context).pushNamed('/OverlayView'); - }, - child: Image( - width: 27, - image: AssetImage('assets/images/ic_smart_chat.png'), - ), - ), - ), + // Container( + // margin: const EdgeInsets.only(left: 14, right: 14), + // child: GestureDetector( + // onTap: () { + // Navigator.of(context).pushNamed('/TestPage'); + // }, + // child: Image( + // width: 27, + // image: AssetImage('assets/images/ic_smart_chat.png'), + // ), + // ), + // ), ///功能 更多 GestureDetector( @@ -476,9 +486,12 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie isMore = !isMore; setState(() {}); }, - child: Image( - width: 27, - image: !isMore ? AssetImage('assets/images/ic_more.png') : AssetImage('assets/images/ic_more_no.png'), + child: Container( + margin: EdgeInsets.only(left: 14), + child: Image( + width: 27, + image: !isMore ? AssetImage('assets/images/ic_more.png') : AssetImage('assets/images/ic_more_no.png'), + ), ), ), ], @@ -579,12 +592,13 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie // if (index == 0) { // return Container(); // } + CustomPopupMenuController customController = CustomPopupMenuController(); ///简介 if (chatList[index].role == 'profile') { return Center( child: Container( - margin: EdgeInsets.only(left: 16, right: 16), + margin: EdgeInsets.only(left: 12, right: 12, bottom: 20), padding: EdgeInsets.only(left: 20, right: 20, top: 12, bottom: 12), decoration: BoxDecoration(color: Color(0x99000000), borderRadius: BorderRadius.all(Radius.circular(13))), child: ExpandableText( @@ -613,21 +627,26 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie ///提示 if (chatList[index].role == 'tips') { return Center( - child: Container( - padding: const EdgeInsets.only(left: 9, right: 9, top: 6, bottom: 6), - decoration: const BoxDecoration( - color: Color(0xCC000000), - borderRadius: BorderRadius.all(Radius.circular(15)), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(chatList[index].content!, style: TextStyle(color: Color(0xFFB6B6B6), fontSize: 10)), - Container( - margin: const EdgeInsets.only(left: 10), - child: const Text("立即增加", style: TextStyle(color: Color(0xFFFF9000), fontSize: 10)), - ), - ], + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/AccountPage'); + }, + child: Container( + padding: const EdgeInsets.only(left: 9, right: 9, top: 6, bottom: 6), + decoration: const BoxDecoration( + color: Color(0xCC000000), + borderRadius: BorderRadius.all(Radius.circular(15)), + ), + child: Row( + mainAxisSize: MainAxisSize.min, + children: [ + Text(chatList[index].content!, style: TextStyle(color: Color(0xFFB6B6B6), fontSize: 10)), + Container( + margin: const EdgeInsets.only(left: 10), + child: const Text("立即增加", style: TextStyle(color: Color(0xFFFF9000), fontSize: 10)), + ), + ], + ), ), ), ); @@ -649,19 +668,22 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie constraints: BoxConstraints( maxWidth: MediaQuery.of(context).size.width - 50, // 确保不超过屏幕宽度 ), - child: Container( - margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), - padding: const EdgeInsets.all(11.0), - decoration: const BoxDecoration( - color: Color(0x99FF9000), - borderRadius: BorderRadius.only( - topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), - ), - child: GestureDetector( - onTap: () { - EasyLoading.showToast("status${chatList[index].id}"); - // delChat(chatList[index].id!, index); - }, + child: CustomPopup( + controller: customController, + menuBuilder: () { + return popupView(chatList[index].id!, chatList[index].content!, index, customController); + }, + barrierColor: Colors.transparent, + //触发方式 + pressType: PressType.longPress, + child: Container( + margin: const EdgeInsets.symmetric(vertical: 4, horizontal: 12), + padding: const EdgeInsets.all(11.0), + decoration: const BoxDecoration( + color: Color(0x99FF9000), + borderRadius: BorderRadius.only( + topRight: Radius.circular(16.0), bottomLeft: Radius.circular(16.0), bottomRight: Radius.circular(16.0)), + ), child: Text( chatList[index].content!, style: const TextStyle( @@ -735,6 +757,69 @@ class _HomeChatPageState extends State with AutomaticKeepAliveClie ); } + popupView(int id, String content, index, customController) { + return Container( + width: 135, + height: 40, + decoration: BoxDecoration( + color: Color(0xFF222222), + borderRadius: BorderRadius.all(Radius.circular(2)), + ), + child: Row( + mainAxisAlignment: MainAxisAlignment.spaceAround, + children: [ + Expanded( + child: GestureDetector( + onTap: () { + customController.hideMenu(); + + EasyLoading.showToast("内容已复制"); + Clipboard.setData(ClipboardData(text: content)); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image(width: 11, height: 11, image: AssetImage('assets/images/ic_copy.png')), + Container( + child: Text( + '复制', + style: TextStyle(fontSize: 8, color: Color(0xFF9D9D9D)), + ), + ) + ], + ), + ), + ), + Expanded( + child: GestureDetector( + onTap: () { + customController.hideMenu(); + + FunctionUtil.popDialog(context, DeleteDialog( + onTap: () { + deleteChat(id, index); + }, + )); + }, + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Image(width: 11, height: 11, image: AssetImage('assets/images/ic_delete.png')), + Container( + child: Text( + '删除', + style: TextStyle(fontSize: 8, color: Color(0xFF9D9D9D)), + ), + ) + ], + ), + ), + ), + ], + ), + ); + } + @override // TODO: implement wantKeepAlive bool get wantKeepAlive => true; diff --git a/lib/tools/home/test_page.dart b/lib/tools/home/test_page.dart new file mode 100644 index 0000000..c38e9c7 --- /dev/null +++ b/lib/tools/home/test_page.dart @@ -0,0 +1,60 @@ +import 'package:flutter/material.dart'; + +import '../../custom/custom_popup.dart'; + +class TestPage extends StatefulWidget { + const TestPage({super.key}); + + @override + State createState() => _TestPageState(); +} + +class _TestPageState extends State { + @override + Widget build(BuildContext context) { + return Scaffold( + body: Stack( + children: [ + Positioned( + top: 400, + left: 50, + child: CustomPopup( + menuBuilder: () { + return Container( + width: 50, + height: 150, + color: Colors.lightBlue, + ); + }, + pressType: PressType.singleClick, + child: Container( + width: 50, + height: 50, + color: Colors.black, + ), + ), + ), + Positioned( + top: 400, + left: 150, + child: CustomPopup( + menuBuilder: () { + return Container( + width: 50, + height: 150, + color: Colors.lightBlue, + ); + }, + pressType: PressType.singleClick, + child: Container( + width: 50, + height: 50, + color: Colors.black, + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/tools/home_page.dart b/lib/tools/home_page.dart index 278e5e6..5a13732 100644 --- a/lib/tools/home_page.dart +++ b/lib/tools/home_page.dart @@ -155,21 +155,21 @@ class _HomePageState extends State with SingleTickerProviderStateMixin ), label: '', ), - BottomNavigationBarItem( - icon: Container( - // margin: EdgeInsets.only(top: 10), - child: Image( - width: 32, - image: AssetImage('assets/images/ic_create.png'), - ), - ), - label: '', - ), + // BottomNavigationBarItem( + // icon: Container( + // // margin: EdgeInsets.only(top: 10), + // child: Image( + // width: 32, + // image: AssetImage('assets/images/ic_create.png'), + // ), + // ), + // label: '', + // ), BottomNavigationBarItem( icon: Container( child: Text( '消息', - style: TextStyle(color: Color(currentIndex == 3 ? 0xFFFFFFFF : 0xFF7E7E7E), fontSize: 15), + style: TextStyle(color: Color(currentIndex == 2 ? 0xFFFFFFFF : 0xFF7E7E7E), fontSize: 15), ), ), label: '', @@ -178,7 +178,7 @@ class _HomePageState extends State with SingleTickerProviderStateMixin icon: Container( child: Text( '我的', - style: TextStyle(color: Color(currentIndex == 4 ? 0xFFFFFFFF : 0xFF7E7E7E), fontSize: 15), + style: TextStyle(color: Color(currentIndex == 3 ? 0xFFFFFFFF : 0xFF7E7E7E), fontSize: 15), ), ), label: '', @@ -203,20 +203,21 @@ class _HomePageState extends State with SingleTickerProviderStateMixin setState(() { currentIndex = index; }); + _tabController.animateTo(index); - switch (index) { - case 2: - break; - case 3: - _tabController.animateTo(2); - break; - case 4: - _tabController.animateTo(3); - break; - default: - _tabController.animateTo(index); - break; - } + // switch (index) { + // case 2: + // break; + // case 3: + // _tabController.animateTo(2); + // break; + // case 4: + // _tabController.animateTo(3); + // break; + // default: + // _tabController.animateTo(index); + // break; + // } }, ), ), diff --git a/lib/tools/login/login_page.dart b/lib/tools/login/login_page.dart index 81587ba..f57b479 100644 --- a/lib/tools/login/login_page.dart +++ b/lib/tools/login/login_page.dart @@ -130,7 +130,7 @@ class _LoginPageState extends State { left: 36, top: 76, child: Text( - '妙语', + '妙语星河', style: TextStyle(color: Colors.white, fontSize: 33), )), Positioned( diff --git a/lib/tools/me/me_page.dart b/lib/tools/me/me_page.dart index b86d361..2b7fe43 100644 --- a/lib/tools/me/me_page.dart +++ b/lib/tools/me/me_page.dart @@ -52,8 +52,8 @@ class _MePageState extends State { context, MaterialPageRoute( builder: (context) => ChatPage( - characterId: id, - )), + characterId: id, + )), ); } @@ -78,6 +78,7 @@ class _MePageState extends State { '', style: TextStyle(color: Colors.white, fontSize: 18), ), + ///设置按钮 Positioned( right: 15, @@ -129,21 +130,21 @@ class _MePageState extends State { margin: EdgeInsets.only(left: 16, top: 17), child: Row( children: [ - Text( - '9', - style: TextStyle(color: Colors.white, fontSize: 16), - ), + // Text( + // '9', + // style: TextStyle(color: Colors.white, fontSize: 16), + // ), + // Container( + // margin: EdgeInsets.only(left: 6), + // child: Text( + // '相册', + // style: TextStyle(color: Color(0xFF4D4D4D), fontSize: 12), + // ), + // ), Container( - margin: EdgeInsets.only(left: 6), + // margin: EdgeInsets.only(left: 18), child: Text( - '相册', - style: TextStyle(color: Color(0xFF4D4D4D), fontSize: 12), - ), - ), - Container( - margin: EdgeInsets.only(left: 22), - child: Text( - '9', + '${userInfoBean.remainingChatCount}', style: TextStyle(color: Colors.white, fontSize: 16), ), ), @@ -164,62 +165,64 @@ class _MePageState extends State { child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - GestureDetector( - onTap: () { - Navigator.pushNamed(context, '/AccountPage'); - }, - child: Container( - width: 151, - height: 38, - decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), - child: Row( - children: [ - Container( - margin: EdgeInsets.only(left: 15), - child: Image( - width: 23, - height: 20, - image: AssetImage('assets/images/ic_currency.png'), + Expanded( + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/AccountPage'); + }, + child: Container( + height: 38, + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 15), + child: Image( + width: 23, + height: 20, + image: AssetImage('assets/images/ic_currency.png'), + ), ), - ), - Container( - margin: EdgeInsets.only(left: 17), - child: Text( - '货币:${userInfoBean.currency}', - style: TextStyle(color: Color(0xFFE1E1E1), fontSize: 13), + Container( + margin: EdgeInsets.only(left: 17), + child: Text( + '货币:${userInfoBean.currency}', + style: TextStyle(color: Color(0xFFE1E1E1), fontSize: 13), + ), ), - ), - ], + ], + ), ), ), ), - GestureDetector( - onTap: () { - Navigator.pushNamed(context, '/ShopPage'); - }, - child: Container( - width: 151, - height: 38, - margin: EdgeInsets.only(left: 26), - decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), - child: Row( - children: [ - Container( - margin: EdgeInsets.only(left: 15), - child: Image( - width: 23, - height: 20, - image: AssetImage('assets/images/ic_mall.png'), + Container(width: 15), + Expanded( + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/ShopPage'); + }, + child: Container( + height: 38, + decoration: BoxDecoration(color: Color(0xFF2A2A2A), borderRadius: BorderRadius.all(Radius.circular(13))), + child: Row( + children: [ + Container( + margin: EdgeInsets.only(left: 15), + child: Image( + width: 23, + height: 20, + image: AssetImage('assets/images/ic_mall.png'), + ), ), - ), - Container( - margin: EdgeInsets.only(left: 24), - child: Text( - '货币商城', - style: TextStyle(color: Color(0xFFE1E1E1), fontSize: 13), + Container( + margin: EdgeInsets.only(left: 24), + child: Text( + '货币商城', + style: TextStyle(color: Color(0xFFE1E1E1), fontSize: 13), + ), ), - ), - ], + ], + ), ), ), ), @@ -230,50 +233,55 @@ class _MePageState extends State { Container( height: 50, margin: EdgeInsets.symmetric(horizontal: 16), - child: Image( - image: AssetImage('assets/images/ic_web.png'), + child: GestureDetector( + onTap: () { + Navigator.pushNamed(context, '/LoginPage'); + }, + child: Image( + image: AssetImage('assets/images/ic_web.png'), + ), ), ), - Container( - margin: EdgeInsets.only(left: 16, top: 33), - child: Row( - children: [ - Text( - '创作中心', - style: TextStyle(color: Color(0xFFE1E1E1)), - ), - Container( - margin: EdgeInsets.only(left: 9), - child: Text( - '0', - style: TextStyle(color: Color(0xFF4D4D4D)), - ), - ), - ], - ), - ), - - Container( - margin: EdgeInsets.symmetric(horizontal: 16, vertical: 17), - child: GridView.count( - shrinkWrap: true, - //水平子Widget之间间距 - crossAxisSpacing: 12.0, - //垂直子Widget之间间距 - mainAxisSpacing: 9.0, - //GridView内边距 - padding: EdgeInsets.zero, - //一行的Widget数量 - crossAxisCount: 3, - //子Widget宽高比例 - childAspectRatio: 0.7, - //子Widget列表 - children: _item(userInfoBean.characterInfo!), - physics: NeverScrollableScrollPhysics(), - //类似 cellForRow 函数 - scrollDirection: Axis.vertical), - ), + // Container( + // margin: EdgeInsets.only(left: 16, top: 33), + // child: Row( + // children: [ + // Text( + // '创作中心', + // style: TextStyle(color: Color(0xFFE1E1E1)), + // ), + // Container( + // margin: EdgeInsets.only(left: 9), + // child: Text( + // '0', + // style: TextStyle(color: Color(0xFF4D4D4D)), + // ), + // ), + // ], + // ), + // ), + // + // Container( + // margin: EdgeInsets.symmetric(horizontal: 16, vertical: 17), + // child: GridView.count( + // shrinkWrap: true, + // //水平子Widget之间间距 + // crossAxisSpacing: 12.0, + // //垂直子Widget之间间距 + // mainAxisSpacing: 9.0, + // //GridView内边距 + // padding: EdgeInsets.zero, + // //一行的Widget数量 + // crossAxisCount: 3, + // //子Widget宽高比例 + // childAspectRatio: 0.7, + // //子Widget列表 + // children: _item(userInfoBean.characterInfo!), + // physics: NeverScrollableScrollPhysics(), + // //类似 cellForRow 函数 + // scrollDirection: Axis.vertical), + // ), ], ), ) @@ -330,5 +338,4 @@ class _MePageState extends State { ); }).toList(); } - } diff --git a/lib/tools/message/message_page.dart b/lib/tools/message/message_page.dart index 3828f33..07743d0 100644 --- a/lib/tools/message/message_page.dart +++ b/lib/tools/message/message_page.dart @@ -160,7 +160,7 @@ class _MessagePageState extends State { child: Container( width: 160, child: Text( - '你好', + '${data.lastMessage}', maxLines: 1, overflow: TextOverflow.ellipsis, style: TextStyle(fontSize: 12, color: Color(0xFF777777)), @@ -171,7 +171,7 @@ class _MessagePageState extends State { top: 6, child: Container( child: Text( - '12:39', + '${data.lastContactTime}', style: TextStyle(fontSize: 10, color: Color(0xFF8D8D8D)), ), )), diff --git a/lib/tools/shop/account_page.dart b/lib/tools/shop/account_page.dart index 3be9f4f..dbe48ac 100644 --- a/lib/tools/shop/account_page.dart +++ b/lib/tools/shop/account_page.dart @@ -80,27 +80,23 @@ class _AccountPageState extends State { child: Column( mainAxisSize: MainAxisSize.min, children: [ + Image( + width: 45, + image: AssetImage('assets/images/ic_pearl.png'), + ), Container( alignment: Alignment.center, - margin: EdgeInsets.only(top: 40), + margin: EdgeInsets.only(top: 13), child: Text( '${accountBean.currency}', style: TextStyle(fontSize: 24, color: Color(0xFFE1E1E1)), ), ), - Container( - alignment: Alignment.center, - margin: EdgeInsets.only(top: 11), - child: Text( - '货币余额', - style: TextStyle(fontSize: 10, color: Color(0xFF686868)), - ), - ), Container( alignment: Alignment.centerLeft, margin: EdgeInsets.only(top: 39), child: Text( - '货币余额', + '账户充值', style: TextStyle(fontSize: 13, color: Color(0xFFACACAC)), ), ), diff --git a/pubspec.yaml b/pubspec.yaml index 49f981e..001a31f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -47,7 +47,6 @@ dependencies: card_swiper: ^3.0.1 flutter_slidable: ^3.1.0 flutter_keyboard_visibility: ^6.0.0 - custom_pop_up_menu: ^1.2.4 dev_dependencies: flutter_test: