mod: 评论表情渲染
This commit is contained in:
@ -36,6 +36,7 @@ class NetworkImgLayer extends StatelessWidget {
|
||||
imageUrl: src!,
|
||||
width: width ?? double.infinity,
|
||||
height: height ?? double.infinity,
|
||||
alignment: Alignment.center,
|
||||
maxWidthDiskCache: ((cacheW ?? width!) * pr).toInt(),
|
||||
// maxHeightDiskCache: (cacheH ?? height!).toInt(),
|
||||
memCacheWidth: ((cacheW ?? width!) * pr).toInt(),
|
||||
|
@ -1,255 +0,0 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class ReplyItem extends StatelessWidget {
|
||||
ReplyItem({super.key, this.replyItem, required this.isUp});
|
||||
ReplyItemModel? replyItem;
|
||||
bool isUp = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 8, 8, 14),
|
||||
child: content(context),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget lfAvtar(context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 5),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.03)),
|
||||
),
|
||||
child: NetworkImgLayer(
|
||||
src: replyItem!.member!.avatar,
|
||||
width: 34,
|
||||
height: 34,
|
||||
type: 'avatar',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget content(context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 头像、昵称
|
||||
Row(
|
||||
// 两端对齐
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
// onTap: () =>
|
||||
// Get.toNamed('/member/${reply.userName}', parameters: {
|
||||
// 'memberAvatar': reply.avatar,
|
||||
// 'heroTag': reply.userName + heroTag,
|
||||
// }),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
lfAvtar(context),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
replyItem!.member!.uname!,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(
|
||||
color: isUp!
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: null,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${replyItem!.member!.level}.png',
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
),
|
||||
Text(
|
||||
Utils.dateFormat(replyItem!.ctime),
|
||||
style: Theme.of(context).textTheme.labelSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
// SizedBox(
|
||||
// width: 35,
|
||||
// height: 35,
|
||||
// child: IconButton(
|
||||
// padding: const EdgeInsets.all(2.0),
|
||||
// icon: const Icon(Icons.more_horiz_outlined, size: 18.0),
|
||||
// onPressed: () {},
|
||||
// ),
|
||||
// )
|
||||
],
|
||||
),
|
||||
// title
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 6, left: 45, right: 8),
|
||||
child: SelectionArea(
|
||||
child: Text(
|
||||
replyItem!.content!.message!,
|
||||
style: const TextStyle(height: 1.8),
|
||||
),
|
||||
),
|
||||
),
|
||||
bottonAction(context),
|
||||
// Text(replyItem!.replies!.length.toString()),
|
||||
if (replyItem!.replies!.isNotEmpty)
|
||||
ReplyItemRow(
|
||||
replies: replyItem!.replies,
|
||||
replyControl: replyItem!.replyControl,
|
||||
)
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 感谢、回复、复制
|
||||
Widget bottonAction(context) {
|
||||
var color = Theme.of(context).colorScheme.outline;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
const SizedBox(width: 42),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: TextButton(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.thumb_up_alt_outlined,
|
||||
size: 16,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
replyItem!.like.toString(),
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class ReplyItemRow extends StatelessWidget {
|
||||
ReplyItemRow({super.key, this.replies, this.replyControl});
|
||||
List? replies;
|
||||
var replyControl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isShow = replyControl.isShow;
|
||||
int extraRow = replyControl != null && isShow ? 1 : 0;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(left: 45, right: 10),
|
||||
padding: const EdgeInsets.only(top: 4, bottom: 4),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: replies!.length + extraRow,
|
||||
itemBuilder: (context, index) {
|
||||
if (extraRow == 1 && index == replies!.length) {
|
||||
return ListTile(
|
||||
onTap: () {},
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.only(left: 10, right: 6),
|
||||
title: Text.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
children: [
|
||||
if (replyControl.upReply) const TextSpan(text: 'up回复了'),
|
||||
if (replyControl.isUpTop) const TextSpan(text: 'up点赞了'),
|
||||
TextSpan(text: replyControl.entryText)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return ListTile(
|
||||
onTap: () {},
|
||||
dense: true,
|
||||
contentPadding: const EdgeInsets.only(left: 10, right: 6),
|
||||
title: Text.rich(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: replies![index].member.uname + ':',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => {print('跳转至用户主页')}),
|
||||
TextSpan(
|
||||
text: replies![index].content.message,
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
@ -1,10 +1,11 @@
|
||||
class ReplyContent {
|
||||
ReplyContent({
|
||||
this.message,
|
||||
this.atNameToMid, // @的用户的mid
|
||||
this.memebers, // 被@的用户List 如果有的话
|
||||
this.emote, // 表情包 如果有的话
|
||||
this.jumpUrl,
|
||||
this.atNameToMid, // @的用户的mid null
|
||||
this.memebers, // 被@的用户List 如果有的话 []
|
||||
this.emote, // 表情包 如果有的话 null
|
||||
this.jumpUrl, // {}
|
||||
this.pictures, // {}
|
||||
});
|
||||
|
||||
String? message;
|
||||
@ -12,12 +13,14 @@ class ReplyContent {
|
||||
List? memebers;
|
||||
Map? emote;
|
||||
Map? jumpUrl;
|
||||
List? pictures;
|
||||
|
||||
ReplyContent.fromJson(Map<String, dynamic> json) {
|
||||
message = json['message'];
|
||||
atNameToMid = json['at_name_to_mid'];
|
||||
memebers = json['memebers'];
|
||||
emote = json['emote'];
|
||||
jumpUrl = json['jumpUrl'];
|
||||
atNameToMid = json['at_name_to_mid'] ?? {};
|
||||
memebers = json['memebers'] ?? [];
|
||||
emote = json['emote'] ?? {};
|
||||
jumpUrl = json['jumpUrl'] ?? {};
|
||||
pictures = json['pictures'] ?? [];
|
||||
}
|
||||
}
|
||||
|
@ -15,18 +15,22 @@ class ReplyData {
|
||||
|
||||
ReplyPage? page;
|
||||
ReplyConfig? config;
|
||||
late List? replies;
|
||||
late List? topReplies;
|
||||
late List<ReplyItemModel>? replies;
|
||||
late List<ReplyItemModel>? topReplies;
|
||||
ReplyUpper? upper;
|
||||
|
||||
ReplyData.fromJson(Map<String, dynamic> json) {
|
||||
page = ReplyPage.fromJson(json['page']);
|
||||
config = ReplyConfig.fromJson(json['config']);
|
||||
replies =
|
||||
json['replies'].map((item) => ReplyItemModel.fromJson(item)).toList();
|
||||
replies = json['replies']
|
||||
.map<ReplyItemModel>(
|
||||
(item) => ReplyItemModel.fromJson(item, json['upper']['mid']))
|
||||
.toList();
|
||||
topReplies = json['top_replies'] != null
|
||||
? json['top_replies']
|
||||
.map((item) => ReplyItemModel.fromJson(item))
|
||||
.map<ReplyItemModel>((item) => ReplyItemModel.fromJson(
|
||||
item, json['upper']['mid'],
|
||||
isTopStatus: true))
|
||||
.toList()
|
||||
: [];
|
||||
upper = ReplyUpper.fromJson(json['upper']);
|
||||
|
@ -28,6 +28,8 @@ class ReplyItemModel {
|
||||
this.upAction,
|
||||
this.invisible,
|
||||
this.replyControl,
|
||||
this.isUp,
|
||||
this.isTop,
|
||||
});
|
||||
|
||||
int? rpid;
|
||||
@ -55,8 +57,11 @@ class ReplyItemModel {
|
||||
UpAction? upAction;
|
||||
bool? invisible;
|
||||
ReplyControl? replyControl;
|
||||
bool? isUp;
|
||||
bool? isTop = false;
|
||||
|
||||
ReplyItemModel.fromJson(Map<String, dynamic> json) {
|
||||
ReplyItemModel.fromJson(Map<String, dynamic> json, upperMid,
|
||||
{isTopStatus = false}) {
|
||||
rpid = json['rpid'];
|
||||
oid = json['oid'];
|
||||
type = json['type'];
|
||||
@ -78,7 +83,9 @@ class ReplyItemModel {
|
||||
member = ReplyMember.fromJson(json['member']);
|
||||
content = ReplyContent.fromJson(json['content']);
|
||||
replies = json['replies'] != null
|
||||
? json['replies'].map((item) => ReplyItemModel.fromJson(item)).toList()
|
||||
? json['replies']
|
||||
.map((item) => ReplyItemModel.fromJson(item, upperMid))
|
||||
.toList()
|
||||
: [];
|
||||
assist = json['assist'];
|
||||
upAction = UpAction.fromJson(json['up_action']);
|
||||
@ -86,6 +93,8 @@ class ReplyItemModel {
|
||||
replyControl = json['reply_control'] == null
|
||||
? null
|
||||
: ReplyControl.fromJson(json['reply_control']);
|
||||
isUp = upperMid.toString() == json['member']['mid'];
|
||||
isTop = isTopStatus;
|
||||
}
|
||||
}
|
||||
|
||||
@ -126,7 +135,7 @@ class ReplyControl {
|
||||
upReply = json['up_reply'] ?? false;
|
||||
isUpTop = json['is_up_top'] ?? false;
|
||||
upLike = json['up_like'] ?? false;
|
||||
if (json['sub_reply_entry_text'] == null) {
|
||||
if (json['sub_reply_entry_text'] != null) {
|
||||
final RegExp regex = RegExp(r"\d+");
|
||||
final RegExpMatch match = regex.firstMatch(
|
||||
json['sub_reply_entry_text'] == null
|
||||
|
@ -12,7 +12,7 @@ class ReplyUpper {
|
||||
ReplyUpper.fromJson(Map<String, dynamic> json) {
|
||||
mid = json['mid'];
|
||||
top = json['top'] != null
|
||||
? ReplyItemModel.fromJson(json['top'])
|
||||
? ReplyItemModel.fromJson(json['top'], json['mid'])
|
||||
: ReplyItemModel();
|
||||
}
|
||||
}
|
||||
|
@ -2,8 +2,9 @@ import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:pilipala/common/skeleton/video_card_h.dart';
|
||||
import 'package:pilipala/common/widgets/http_error.dart';
|
||||
import 'package:pilipala/common/widgets/reply_item.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'controller.dart';
|
||||
import 'widgets/reply_item.dart';
|
||||
|
||||
class VideoReplyPanel extends StatefulWidget {
|
||||
const VideoReplyPanel({super.key});
|
||||
@ -28,8 +29,24 @@ class _VideoReplyPanelState extends State<VideoReplyPanel>
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data['status']) {
|
||||
List<dynamic> replies = snapshot.data['data'].replies;
|
||||
replies.addAll(snapshot.data['data'].topReplies);
|
||||
List<ReplyItemModel> replies = snapshot.data['data'].replies;
|
||||
// 添加置顶回复
|
||||
if (snapshot.data['data'].upper.top != null) {
|
||||
bool flag = false;
|
||||
for (var i = 0;
|
||||
i < snapshot.data['data'].topReplies.length;
|
||||
i++) {
|
||||
if (snapshot.data['data'].topReplies[i].rpid ==
|
||||
snapshot.data['data'].upper.top.rpid) {
|
||||
flag = true;
|
||||
}
|
||||
}
|
||||
if (!flag) {
|
||||
replies.insert(0, snapshot.data['data'].upper.top);
|
||||
}
|
||||
}
|
||||
|
||||
replies.insertAll(0, snapshot.data['data'].topReplies);
|
||||
// 请求成功
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
|
448
lib/pages/video/detail/reply/widgets/reply_item.dart
Normal file
448
lib/pages/video/detail/reply/widgets/reply_item.dart
Normal file
@ -0,0 +1,448 @@
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||
import 'package:pilipala/models/video/reply/item.dart';
|
||||
import 'package:pilipala/utils/utils.dart';
|
||||
|
||||
class ReplyItem extends StatelessWidget {
|
||||
ReplyItem({super.key, this.replyItem, required this.isUp});
|
||||
ReplyItemModel? replyItem;
|
||||
bool isUp = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
child: Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 8, 8, 0),
|
||||
child: content(context),
|
||||
),
|
||||
Divider(
|
||||
height: 1,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.08),
|
||||
)
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget lfAvtar(context) {
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(top: 5),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.03)),
|
||||
),
|
||||
child: NetworkImgLayer(
|
||||
src: replyItem!.member!.avatar,
|
||||
width: 30,
|
||||
height: 30,
|
||||
type: 'avatar',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget content(context) {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
// 头像、昵称
|
||||
Row(
|
||||
// 两端对齐
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: <Widget>[
|
||||
GestureDetector(
|
||||
// onTap: () =>
|
||||
// Get.toNamed('/member/${reply.userName}', parameters: {
|
||||
// 'memberAvatar': reply.avatar,
|
||||
// 'heroTag': reply.userName + heroTag,
|
||||
// }),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
lfAvtar(context),
|
||||
const SizedBox(width: 12),
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(
|
||||
replyItem!.member!.uname!,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.copyWith(
|
||||
color: replyItem!.isUp!
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Image.asset(
|
||||
'assets/images/lv/lv${replyItem!.member!.level}.png',
|
||||
height: 13,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
// title
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 0, left: 45, right: 6),
|
||||
child: SelectableRegion(
|
||||
magnifierConfiguration: const TextMagnifierConfiguration(),
|
||||
focusNode: FocusNode(),
|
||||
selectionControls: MaterialTextSelectionControls(),
|
||||
child: Text.rich(
|
||||
style: const TextStyle(height: 1.6),
|
||||
TextSpan(
|
||||
children: [
|
||||
buildContent(context, replyItem!.content!),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// 操作区域
|
||||
bottonAction(context, replyItem!.replyControl),
|
||||
if (replyItem!.replies!.isNotEmpty) ...[
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 2, bottom: 12),
|
||||
child: ReplyItemRow(
|
||||
replies: replyItem!.replies,
|
||||
replyControl: replyItem!.replyControl,
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// 感谢、回复、复制
|
||||
Widget bottonAction(context, replyControl) {
|
||||
var color = Theme.of(context).colorScheme.outline;
|
||||
return Row(
|
||||
children: [
|
||||
const SizedBox(width: 48),
|
||||
Text(
|
||||
Utils.dateFormat(replyItem!.ctime),
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.copyWith(color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
if (replyItem!.isTop!) ...[
|
||||
Text(
|
||||
' • 置顶',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (replyControl!.isUpTop!) ...[
|
||||
Text(
|
||||
' • 超赞',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
// const SizedBox(width: 4),
|
||||
],
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
height: 35,
|
||||
child: TextButton(
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.thumb_up_alt_outlined,
|
||||
size: 16,
|
||||
color: color,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
replyItem!.like.toString(),
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelSmall!.fontSize),
|
||||
),
|
||||
],
|
||||
),
|
||||
onPressed: () {},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 5)
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ignore: must_be_immutable
|
||||
class ReplyItemRow extends StatelessWidget {
|
||||
ReplyItemRow({
|
||||
super.key,
|
||||
this.replies,
|
||||
this.replyControl,
|
||||
});
|
||||
List? replies;
|
||||
ReplyControl? replyControl;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
bool isShow = replyControl!.isShow!;
|
||||
int extraRow = replyControl != null && isShow ? 1 : 0;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(left: 42, right: 4, top: 0),
|
||||
child: Material(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(6),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
animationDuration: Duration.zero,
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.zero,
|
||||
shrinkWrap: true,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: replies!.length + extraRow,
|
||||
itemBuilder: (context, index) {
|
||||
if (extraRow == 1 && index == replies!.length) {
|
||||
// 有楼中楼回复,在最后显示
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||
child: Text.rich(
|
||||
TextSpan(
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
),
|
||||
children: [
|
||||
if (replyControl!.upReply!)
|
||||
const TextSpan(text: 'up主等人 '),
|
||||
TextSpan(
|
||||
text: replyControl!.entryText!,
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return InkWell(
|
||||
onTap: () {},
|
||||
child: Padding(
|
||||
padding: EdgeInsets.fromLTRB(8, index == 0 ? 8 : 4, 8, 4),
|
||||
child: Text.rich(
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 2,
|
||||
TextSpan(
|
||||
children: [
|
||||
if (replies![index].isUp)
|
||||
TextSpan(
|
||||
text: 'UP • ',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
),
|
||||
TextSpan(
|
||||
text: replies![index].member.uname + ' ',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.titleSmall!
|
||||
.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => {
|
||||
print('跳转至用户主页'),
|
||||
},
|
||||
),
|
||||
buildContent(context, replies![index].content),
|
||||
],
|
||||
),
|
||||
),
|
||||
));
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
InlineSpan buildContent(BuildContext context, content) {
|
||||
if (content.emote.isEmpty &&
|
||||
content.atNameToMid.isEmpty &&
|
||||
content.jumpUrl.isEmpty &&
|
||||
content.pictures.isEmpty) {
|
||||
return TextSpan(text: content.message);
|
||||
}
|
||||
List<InlineSpan> spanChilds = [];
|
||||
// if (content.atNameToMid.isNotEmpty) {
|
||||
// print(content.message);
|
||||
// content.atNameToMid.forEach((key, value) {
|
||||
// key = '@' + key;
|
||||
// int lastIndex = content.message.indexOf(key);
|
||||
// int endIndex = (lastIndex + key.length).toInt();
|
||||
// if (lastIndex >= 0) {
|
||||
// spanChilds.add(TextSpan(
|
||||
// text: '@' + key,
|
||||
// style: TextStyle(color: Theme.of(context).colorScheme.primary)));
|
||||
// content.message = content.message.replaceRange(lastIndex, endIndex, '');
|
||||
// spanChilds.add(TextSpan(text: content.message));
|
||||
// }
|
||||
// spanChilds.add(TextSpan(text: content.message.substring(lastIndex)));
|
||||
// });
|
||||
// // return TextSpan(children: spanChilds);
|
||||
// }
|
||||
// if (content.emote.isNotEmpty) {
|
||||
// content.emote.forEach((key, value) {
|
||||
// int lastIndex = content.message.indexOf(key);
|
||||
// int endIndex = content.message.indexOf(key) + key.length;
|
||||
// if (lastIndex >= 0) {
|
||||
// content.message = content.message.replaceRange(lastIndex, endIndex, '');
|
||||
// spanChilds.add(TextSpan(text: content.message.substring(0, lastIndex)));
|
||||
// }
|
||||
// spanChilds.add(WidgetSpan(
|
||||
// child: NetworkImgLayer(
|
||||
// src: value["url"],
|
||||
// width: 20,
|
||||
// height: 20,
|
||||
// )));
|
||||
// });
|
||||
// // return TextSpan(children: spanChilds);
|
||||
// }
|
||||
// if (content.pictures.isNotEmpty) {
|
||||
// spanChilds.add(TextSpan(text: content.message));
|
||||
// spanChilds.add(const WidgetSpan(
|
||||
// child: SizedBox(
|
||||
// height: 4,
|
||||
// )));
|
||||
// for (var i = 0; i < content.pictures.length; i++) {
|
||||
// spanChilds.add(
|
||||
// WidgetSpan(
|
||||
// child: SizedBox(
|
||||
// height: 180,
|
||||
// child: NetworkImgLayer(
|
||||
// src: content.pictures[i]['img_src'],
|
||||
// width: 200,
|
||||
// height: 200 *
|
||||
// content.pictures[i]['img_height'] /
|
||||
// content.pictures[i]['img_width'],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// return TextSpan(children: spanChilds);
|
||||
// }
|
||||
content.message.splitMapJoin(
|
||||
RegExp(r"\[.*?\]"),
|
||||
onMatch: (Match match) {
|
||||
String matchStr = match[0]!;
|
||||
if (content.emote.isNotEmpty) {
|
||||
if (content.emote.keys.contains(matchStr)) {
|
||||
spanChilds.add(
|
||||
WidgetSpan(
|
||||
child: NetworkImgLayer(
|
||||
src: content.emote[matchStr]['url'],
|
||||
width: 20,
|
||||
height: 20,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
spanChilds.add(TextSpan(text: matchStr));
|
||||
return matchStr;
|
||||
}
|
||||
}
|
||||
return matchStr;
|
||||
},
|
||||
onNonMatch: (String str) {
|
||||
try {
|
||||
if (content.atNameToMid.isNotEmpty) {
|
||||
return str.splitMapJoin(
|
||||
RegExp(r"@.*:"),
|
||||
onMatch: (Match match) {
|
||||
if (match[0] != null) {
|
||||
content.atNameToMid.forEach((key, value) {
|
||||
spanChilds.add(
|
||||
TextSpan(
|
||||
text: '@$key ',
|
||||
style: TextStyle(
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.titleSmall!.fontSize,
|
||||
color: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () => {
|
||||
print('跳转至用户主页'),
|
||||
},
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
return match[0]!;
|
||||
},
|
||||
onNonMatch: (String str) {
|
||||
spanChilds.add(TextSpan(text: str));
|
||||
return str;
|
||||
},
|
||||
);
|
||||
} else {
|
||||
spanChilds.add(TextSpan(text: str));
|
||||
return str;
|
||||
}
|
||||
} catch (e) {
|
||||
spanChilds.add(TextSpan(text: str));
|
||||
return str;
|
||||
}
|
||||
},
|
||||
);
|
||||
if (content.pictures.isNotEmpty) {
|
||||
spanChilds.add(const WidgetSpan(
|
||||
child: SizedBox(
|
||||
height: 4,
|
||||
)));
|
||||
for (var i = 0; i < content.pictures.length; i++) {
|
||||
spanChilds.add(
|
||||
WidgetSpan(
|
||||
child: SizedBox(
|
||||
height: 180,
|
||||
child: NetworkImgLayer(
|
||||
src: content.pictures[i]['img_src'],
|
||||
width: 200,
|
||||
height: 200 *
|
||||
content.pictures[i]['img_height'] /
|
||||
content.pictures[i]['img_width'],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
return TextSpan(children: spanChilds);
|
||||
}
|
Reference in New Issue
Block a user