长按弹窗.
This commit is contained in:
parent
8d21b8755a
commit
ef31d9a2e6
BIN
assets/images/ic_copy.png
Normal file
BIN
assets/images/ic_copy.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 678 B |
BIN
assets/images/ic_delete.png
Normal file
BIN
assets/images/ic_delete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 746 B |
BIN
assets/images/ic_pearl.png
Normal file
BIN
assets/images/ic_pearl.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 33 KiB |
|
|
@ -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<String, dynamic> json) => _$MessageBeanFromJson(json);
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ MessageBean _$MessageBeanFromJson(Map<String, dynamic> json) => MessageBean(
|
|||
json['biography'] as String?,
|
||||
json['iconImage'] as String?,
|
||||
json['lastContactTime'] as String?,
|
||||
json['lastMessage'] as String?,
|
||||
);
|
||||
|
||||
Map<String, dynamic> _$MessageBeanToJson(MessageBean instance) =>
|
||||
|
|
@ -21,4 +22,5 @@ Map<String, dynamic> _$MessageBeanToJson(MessageBean instance) =>
|
|||
'biography': instance.biography,
|
||||
'iconImage': instance.iconImage,
|
||||
'lastContactTime': instance.lastContactTime,
|
||||
'lastMessage': instance.lastMessage,
|
||||
};
|
||||
|
|
|
|||
59
lib/common/func.dart
Normal file
59
lib/common/func.dart
Normal file
|
|
@ -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,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
39
lib/custom/DynamicText.dart
Normal file
39
lib/custom/DynamicText.dart
Normal file
|
|
@ -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<TextSpan> 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);
|
||||
}
|
||||
}
|
||||
409
lib/custom/custom_popup.dart
Normal file
409
lib/custom/custom_popup.dart
Normal file
|
|
@ -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<CustomPopup> {
|
||||
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: <Widget>[
|
||||
LayoutId(
|
||||
id: _MenuLayoutId.content,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
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<Path> {
|
||||
@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<Path> oldClipper) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,19 +23,22 @@ class _DeleteDialogState extends State<DeleteDialog> {
|
|||
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<DeleteDialog> {
|
|||
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(
|
||||
'确定',
|
||||
|
|
|
|||
|
|
@ -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<ChatApp> {
|
|||
'/ProblemPage': (BuildContext context) => const ProblemPage(),
|
||||
'/ShopPage': (BuildContext context) => const ShopPage(),
|
||||
'/SettingPage': (BuildContext context) => const SettingPage(),
|
||||
'/TestPage': (BuildContext context) => const TestPage(),
|
||||
},
|
||||
debugShowMaterialGrid: false,
|
||||
//显示网格
|
||||
|
|
|
|||
|
|
@ -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<ChatPage> {
|
|||
}
|
||||
|
||||
///删除消息
|
||||
delChat(int id, index) {
|
||||
deleteChat(int id, index) {
|
||||
EasyLoading.show(status: 'loading...');
|
||||
delIndex = index;
|
||||
List<int> ids = [id];
|
||||
|
|
@ -242,10 +245,13 @@ class _ChatPageState extends State<ChatPage> {
|
|||
///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<ChatPage> {
|
|||
controller: _scrollController,
|
||||
itemCount: chatList.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return _item(index);
|
||||
final Key key = ValueKey<int>(index);
|
||||
return _item(index, key);
|
||||
}),
|
||||
),
|
||||
)),
|
||||
|
|
@ -456,18 +463,18 @@ class _ChatPageState extends State<ChatPage> {
|
|||
)),
|
||||
|
||||
///智能输入
|
||||
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<ChatPage> {
|
|||
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<ChatPage> {
|
|||
}
|
||||
|
||||
///聊天条目
|
||||
_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<ChatPage> {
|
|||
///提示
|
||||
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<ChatPage> {
|
|||
|
||||
///聊天内容
|
||||
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<ChatPage> {
|
|||
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<ChatPage> {
|
|||
),
|
||||
);
|
||||
}
|
||||
|
||||
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)),
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<HomeChatPage> with AutomaticKeepAliveClie
|
|||
|
||||
bool isMore = false;
|
||||
|
||||
bool isHalf = true;
|
||||
bool isHalf = false;
|
||||
|
||||
///输入框内容
|
||||
String text = "";
|
||||
|
|
@ -140,10 +144,11 @@ class _HomeChatPageState extends State<HomeChatPage> with AutomaticKeepAliveClie
|
|||
_viewmodel.getCharacterInfo(widget.characterId);
|
||||
}
|
||||
|
||||
///删除消息
|
||||
///删除单条或多条消息
|
||||
deleteChat(int id, index) {
|
||||
EasyLoading.show(status: 'loading...');
|
||||
delIndex = index;
|
||||
print("delIndex==$delIndex");
|
||||
List<int> ids = [id];
|
||||
_viewmodel.delChatByIds(ids, widget.characterId);
|
||||
}
|
||||
|
|
@ -172,6 +177,11 @@ class _HomeChatPageState extends State<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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<HomeChatPage> 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;
|
||||
|
|
|
|||
60
lib/tools/home/test_page.dart
Normal file
60
lib/tools/home/test_page.dart
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../../custom/custom_popup.dart';
|
||||
|
||||
class TestPage extends StatefulWidget {
|
||||
const TestPage({super.key});
|
||||
|
||||
@override
|
||||
State<TestPage> createState() => _TestPageState();
|
||||
}
|
||||
|
||||
class _TestPageState extends State<TestPage> {
|
||||
@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,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
@ -155,21 +155,21 @@ class _HomePageState extends State<HomePage> 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<HomePage> 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<HomePage> 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;
|
||||
// }
|
||||
},
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class _LoginPageState extends State<LoginPage> {
|
|||
left: 36,
|
||||
top: 76,
|
||||
child: Text(
|
||||
'妙语',
|
||||
'妙语星河',
|
||||
style: TextStyle(color: Colors.white, fontSize: 33),
|
||||
)),
|
||||
Positioned(
|
||||
|
|
|
|||
|
|
@ -52,8 +52,8 @@ class _MePageState extends State<MePage> {
|
|||
context,
|
||||
MaterialPageRoute(
|
||||
builder: (context) => ChatPage(
|
||||
characterId: id,
|
||||
)),
|
||||
characterId: id,
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -78,6 +78,7 @@ class _MePageState extends State<MePage> {
|
|||
'',
|
||||
style: TextStyle(color: Colors.white, fontSize: 18),
|
||||
),
|
||||
|
||||
///设置按钮
|
||||
Positioned(
|
||||
right: 15,
|
||||
|
|
@ -129,21 +130,21 @@ class _MePageState extends State<MePage> {
|
|||
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<MePage> {
|
|||
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<MePage> {
|
|||
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<MePage> {
|
|||
);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ class _MessagePageState extends State<MessagePage> {
|
|||
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<MessagePage> {
|
|||
top: 6,
|
||||
child: Container(
|
||||
child: Text(
|
||||
'12:39',
|
||||
'${data.lastContactTime}',
|
||||
style: TextStyle(fontSize: 10, color: Color(0xFF8D8D8D)),
|
||||
),
|
||||
)),
|
||||
|
|
|
|||
|
|
@ -80,27 +80,23 @@ class _AccountPageState extends State<AccountPage> {
|
|||
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)),
|
||||
),
|
||||
),
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user