Merge remote-tracking branch 'origin/master'

This commit is contained in:
cuiyouliang 2025-09-05 10:56:15 +08:00
commit e90837a9b7
12 changed files with 410 additions and 212 deletions

View File

@ -1,6 +1,8 @@
/// /
library;
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:photo_view/photo_view.dart';
@ -31,6 +33,19 @@ class _ImageViewerState extends State<ImageViewer> {
currentIndex = widget.index;
}
ImageProvider getImageProvider(String path) {
if (Utils.isUrl(path)) {
//
return NetworkImage(path);
} else if (File(path).existsSync()) {
//
return FileImage(File(path));
} else {
// assets
return AssetImage(path);
}
}
@override
Widget build(BuildContext context) {
var imgCount = widget.images?.length;
@ -44,39 +59,40 @@ class _ImageViewerState extends State<ImageViewer> {
bottom: 0,
right: 0,
child: GestureDetector(
child: imgCount == 1 ? PhotoView(
imageProvider: Utils.isUrl(widget.images![0]) ? NetworkImage(widget.images![0]) : AssetImage(widget.images![0]),
backgroundDecoration: const BoxDecoration(
color: Colors.black,
),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
enableRotation: true,
)
:
PhotoViewGallery.builder(
itemCount: widget.images?.length,
builder: (context, index) {
return PhotoViewGalleryPageOptions(
imageProvider: Utils.isUrl(widget.images![index]) ? NetworkImage(widget.images![index]) : AssetImage(widget.images![index]),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
);
},
scrollPhysics: const BouncingScrollPhysics(),
backgroundDecoration: const BoxDecoration(
color: Colors.black,
),
pageController: PageController(initialPage: widget.index),
enableRotation: true,
onPageChanged: (index) {
setState(() {
currentIndex = index;
});
},
),
child: imgCount == 1
? PhotoView(
// imageProvider: Utils.isUrl(widget.images![0]) ? NetworkImage(widget.images![0]) : AssetImage(widget.images![0]),
imageProvider: getImageProvider(widget.images![0]),
backgroundDecoration: const BoxDecoration(
color: Colors.black,
),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![0]),
enableRotation: true,
)
: PhotoViewGallery.builder(
itemCount: widget.images?.length,
builder: (context, index) {
return PhotoViewGalleryPageOptions(
imageProvider: Utils.isUrl(widget.images![index]) ? NetworkImage(widget.images![index]) : AssetImage(widget.images![index]),
minScale: PhotoViewComputedScale.contained,
maxScale: PhotoViewComputedScale.covered * 2,
heroAttributes: PhotoViewHeroAttributes(tag: widget.images![index]),
);
},
scrollPhysics: const BouncingScrollPhysics(),
backgroundDecoration: const BoxDecoration(
color: Colors.black,
),
pageController: PageController(initialPage: widget.index),
enableRotation: true,
onPageChanged: (index) {
setState(() {
currentIndex = index;
});
},
),
onTap: () {
Get.back();
},
@ -87,11 +103,13 @@ class _ImageViewerState extends State<ImageViewer> {
top: MediaQuery.of(context).padding.top + 15,
width: MediaQuery.of(context).size.width,
child: Center(
child: Visibility(
visible: imgCount! > 1 ? true : false,
child: Text('${currentIndex+1} / ${widget.images?.length}', style: const TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'arial'),),
)
),
child: Visibility(
visible: imgCount! > 1 ? true : false,
child: Text(
'${currentIndex + 1} / ${widget.images?.length}',
style: const TextStyle(color: Colors.white, fontSize: 16, fontFamily: 'arial'),
),
)),
),
],
),

View File

@ -52,7 +52,7 @@ conversationTypeFromString(String? type) {
return ConversationType.order.name;
} else if (type.contains('groupNotify')) {
return ConversationType.groupNotify.name;
} else {
return null;
}
return null;
}

View File

@ -527,6 +527,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareTuangou) {
//price,title,url,sell
final obj = jsonDecode(item.customElem!.data!);
logger.e(obj);
final url = obj['url'];
final title = obj['title'];
final price = obj['price'];
@ -607,6 +608,7 @@ class _ChatState extends State<Chat> with SingleTickerProviderStateMixin {
else if (item.elemType == 2 && item.cloudCustomData == SummaryType.shareVideo) {
/// {imgUrl,videoUrl,width,height}
final obj = jsonDecode(item.customElem!.data!);
logger.e(obj);
final videoUrl = obj['videoUrl'];
final imgUrl = obj['imgUrl'];
final width = obj['width'] as num;

View File

@ -13,6 +13,7 @@ import 'package:loopin/IM/im_service.dart';
import 'package:loopin/api/shop_api.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/models/conversation_type.dart';
import 'package:loopin/models/share_type.dart';
import 'package:loopin/models/summary_type.dart';
import 'package:loopin/service/http.dart';
@ -48,7 +49,7 @@ class _GoodsState extends State<Goods> {
@override
void initState() {
super.initState();
final goodsId = Get.arguments['goodsId'];
final goodsId = Get.arguments['goodsId'] ?? '';
scrollController.addListener(() {
setState(() {
scrollOffset = scrollController.offset;
@ -78,25 +79,22 @@ class _GoodsState extends State<Goods> {
}
///
createOrder(String goodsId) async {
var params ={
"type": 1, // 1->2->;3->
"distribution": 1, // 1->2->;3->;
"skuItemBOList": [
{
"skuId": goodsId,
"quantity": 1
}
]
};
print('下单请求参数---->${params}');
createOrder(String goodsId) async {
var params = {
"type": 1, // 1->2->;3->
"distribution": 1, // 1->2->;3->;
"skuItemBOList": [
{"skuId": goodsId, "quantity": 1}
]
};
print('下单请求参数---->$params');
try {
final res = await Http.post('${ShopApi.createGoodsOrder}', data: params);
final res = await Http.post(ShopApi.createGoodsOrder, data: params);
var resData = res['data'];
print('1111111111111111111111111---->${res}');
if(resData['id'].isNotEmpty){
print('1111111111111111111111111---->$res');
if (resData['id'].isNotEmpty) {
return resData['id'];
}else{
} else {
return null;
}
} catch (e) {
@ -104,6 +102,7 @@ class _GoodsState extends State<Goods> {
return null;
}
}
void handleShareClick(int index) {
final description = shopObj['describe']; //
if (index == 1) {
@ -116,12 +115,13 @@ class _GoodsState extends State<Goods> {
}
void handlCoverClick(V2TimConversation conv) async {
// VideoMsg,
//
final userId = conv.userID;
//price,title,url,sell
logger.w(shopObj['name']);
final makeJson = jsonEncode({
"price": shopObj['price'],
"title": shopObj['describe'],
"title": shopObj['name'],
"url": shopObj['pic'],
"sell": Utils.graceNumber(int.parse(shopObj['sales'] ?? '0')),
});
@ -200,7 +200,8 @@ class _GoodsState extends State<Goods> {
//
Obx(() {
//
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
final filteredList = chatController.chatList.where((item) => conversationTypeFromString(item.isCustomAdmin) == null).toList();
if (filteredList.isEmpty) return SizedBox.shrink();
return SizedBox(
height: 110,
@ -219,10 +220,12 @@ class _GoodsState extends State<Goods> {
mainAxisSize: MainAxisSize.min,
children: [
// Image.asset('${chatController.chatList[index].faceUrl}', width: 48.0),
NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
ClipOval(
child: NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
),
),
SizedBox(height: 5),
Text(
@ -338,7 +341,12 @@ class _GoodsState extends State<Goods> {
activeColor: Colors.white,
)),
indicatorLayout: PageIndicatorLayout.SCALE,
children: swiperList.map((sw) => NetworkOrAssetImage(imageUrl: sw)).toList(),
children: swiperList
.map((sw) => NetworkOrAssetImage(
imageUrl: sw,
placeholderAsset: 'assets/images/bk.jpg',
))
.toList(),
),
),
),
@ -577,35 +585,46 @@ class _GoodsState extends State<Goods> {
// )
// ],
// ),
GestureDetector(
onTap: () async {
//
logger.i('联系客服');
final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}');
V2TimConversation conversation = res.data;
logger.i(conversation.toLogString());
if (res.success) {
//
conversation.showName = conversation.showName ?? shopObj['storeName'];
Get.toNamed('/chat', arguments: conversation);
} else {
MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
}
},
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.child_care_outlined,
size: 18.0,
),
Text(
'联系商家',
style: TextStyle(fontSize: 12.0),
)
],
),
)
//
// GestureDetector(
// onTap: () async {
// //
// logger.i('联系客服:$shopObj');
// final res = await ImService.instance.getConversation(conversationID: 'c2c_${shopObj['tenantId']}');
// V2TimConversation conversation = res.data;
// logger.i(conversation.toLogString());
// if (res.success) {
// //
// V2TimUserFullInfo? sellerInfo;
// final resIm = await ImService.instance.otherInfo(shopObj['tenantId']);
// if (resIm.success && resIm.data != null) {
// sellerInfo = resIm.data!;
// logger.i(sellerInfo!.toLogString());
// } else {
// logger.e(resIm.desc);
// }
// conversation.showName = conversation.showName ?? sellerInfo!.nickName;
// Get.toNamed('/chat', arguments: conversation);
// } else {
// MyDialog.toast(res.desc, icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
// }
// },
// child: Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
// Icon(
// Icons.child_care_outlined,
// size: 18.0,
// ),
// Text(
// '联系商家',
// style: TextStyle(fontSize: 12.0),
// )
// ],
// ),
// )
// Column(
// mainAxisAlignment: MainAxisAlignment.center,
// children: [
@ -650,11 +669,11 @@ class _GoodsState extends State<Goods> {
child: GestureDetector(
onTap: () async {
// orderId
// String orderId = '1958380183857659904'; //
// String orderId = '1958380183857659904'; //
String orderId = await createOrder(shopObj['skuList'][0]['id']);
if(orderId.isNotEmpty){
Get.toNamed('/order/detail', arguments: orderId);
}else{
if (orderId.isNotEmpty) {
Get.toNamed('/order/detail', arguments: orderId);
} else {
MyDialog.toast('生成订单失败', icon: const Icon(Icons.warning), style: ToastStyle(backgroundColor: Colors.red.withAlpha(200)));
}
},

View File

@ -182,7 +182,7 @@ class FansState extends State<Fans> with SingleTickerProviderStateMixin {
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
arguments: {'memberId': item.userInfo.userID},
);
},
child: Row(

View File

@ -182,7 +182,7 @@ class FlowingState extends State<Flowing> with SingleTickerProviderStateMixin {
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
arguments: {'memberId': item.userInfo.userID},
);
},
child: Row(

View File

@ -182,7 +182,7 @@ class MutualFollowersState extends State<MutualFollowers> with SingleTickerProvi
onTap: () {
Get.toNamed(
'/vloger',
arguments: item.userInfo.userID,
arguments: {'memberId': item.userInfo.userID},
);
},
child: Row(

View File

@ -1,12 +1,15 @@
import 'dart:io';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:loopin/IM/im_service.dart';
import 'package:loopin/api/common_api.dart';
import 'package:loopin/api/video_api.dart';
import 'package:loopin/components/image_viewer.dart';
import 'package:loopin/components/preview_video.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/utils/index.dart';
import 'package:loopin/utils/snapshot.dart';
import 'package:wechat_assets_picker/wechat_assets_picker.dart';
class UploadVideoPage extends StatefulWidget {
@ -19,15 +22,28 @@ class UploadVideoPage extends StatefulWidget {
class _UploadVideoPageState extends State<UploadVideoPage> {
final selectedVideo = Rxn<AssetEntity>();
final selectedCover = Rxn<AssetEntity>();
//
final uploading = false.obs;
final uploadProgress = 0.0.obs;
final status = ''.obs;
final descController = TextEditingController();
//
final uploading2 = false.obs;
final uploadProgress2 = 0.0.obs;
final status2 = ''.obs;
//
final uploadedVideoUrl = ''.obs; //
final uploadedImgUrl = ''.obs; //
final videoPath = ''.obs; //
final imgPath = ''.obs; //
final snapshot = ''.obs;
final FocusNode descFocusNode = FocusNode();
final uploadedVideoUrl = ''.obs;
final TextEditingController descriptionController = TextEditingController();
Future<void> pickVideo() async {
FocusScope.of(context).unfocus();
final result = await PhotoManager.requestPermissionExtend();
if (!result.isAuth) {
@ -60,10 +76,14 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
if (pickedAssets != null && pickedAssets.isNotEmpty) {
selectedVideo.value = pickedAssets.first;
status.value = '已选择视频';
uploadVideo();
}
}
//
Future<void> pickCoverImage() async {
FocusScope.of(context).unfocus();
final result = await PhotoManager.requestPermissionExtend();
if (!result.isAuth) {
@ -88,30 +108,73 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
if (pickedAssets != null && pickedAssets.isNotEmpty) {
selectedCover.value = pickedAssets.first;
//
uploadImg();
}
}
//
Future<void> uploadImg() async {
final coverFile = await selectedCover.value?.file;
if (coverFile == null) {
status.value = '未选择封面图';
return;
}
imgPath.value = coverFile.path;
uploading2.value = true;
uploadProgress2.value = 0;
status2.value = '上传中...';
try {
final res = await Http.upload(
CommonApi.uploadFile,
filePath: coverFile.path,
fileKey: 'file',
onSendProgress: (sent, total) {
if (total > 0) {
uploadProgress2.value = sent / total;
if (sent == total) uploading2.value = false;
}
},
);
uploadedImgUrl.value = res['data']['url'];
status2.value = '上传成功';
} catch (e) {
if (e is SocketException) {
status2.value = '网络错误,请检查连接';
} else {
status2.value = '上传失败: ${e.toString()}';
}
descFocusNode.unfocus();
uploading2.value = false;
} finally {
descFocusNode.unfocus();
uploading2.value = false;
}
}
//
Future<void> uploadVideo() async {
final file = await selectedVideo.value?.file;
final coverFile = await selectedCover.value?.file;
final desc = descController.text.trim();
if (file == null) {
status.value = '未选择视频';
return;
}
if (desc.isEmpty) {
Get.snackbar('请填写描述', '视频描述不能为空');
return;
}
uploading.value = true;
uploadProgress.value = 0;
status.value = '上传中...';
videoPath.value = file.path;
logger.w(videoPath.value);
snapshot.value = (await generateVideoThumbnail(file.path))!;
logger.w(snapshot.value);
try {
final data = await Http.upload(
final res = await Http.upload(
CommonApi.uploadFile,
filePath: file.path,
fileKey: 'file',
@ -121,60 +184,98 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
}
},
);
uploadedVideoUrl.value = data;
uploadedVideoUrl.value = res['data']['url'];
status.value = '上传成功';
//
descController.clear();
selectedVideo.value = null;
selectedCover.value = null;
uploadedVideoUrl.value = '';
} catch (e) {
descFocusNode.unfocus();
if (e is SocketException) {
status.value = '网络错误,请检查连接';
} else {
status.value = '上传失败: ${e.toString()}';
}
} finally {
descFocusNode.unfocus();
uploading.value = false;
}
}
//
Future<void> submitForm() async {
logger.w(descriptionController.text);
FocusScope.of(context).unfocus();
if (uploadedVideoUrl.value.isEmpty) {
Get.snackbar('请先上传视频', '未检测到上传的视频');
return;
}
if (descriptionController.text.trim().isEmpty) {
Get.snackbar('请填写视频描述', '描述是必填项');
return;
}
try {
await Http.post(VideoApi.publish, data: {
'video_url': uploadedVideoUrl.value,
'desc': descriptionController.text.trim(),
'cover': selectedCover.value,
});
Get.snackbar('发布成功', '视频已成功发布');
final data = {
'url': uploadedVideoUrl.value,
'title': descriptionController.text.trim(),
};
if (uploadedImgUrl.value.isNotEmpty) {
data['cover'] = uploadedImgUrl.value;
}
logger.w('发布内容:$data');
await Http.post(VideoApi.publish, data: data);
Get.snackbar('发布成功', '视频正在审核中');
clearForm();
} catch (e) {
Get.snackbar('发布失败', '$e');
}
} finally {}
}
void clearForm() {
selectedVideo.value = null;
selectedCover.value = null;
descriptionController.clear();
status.value = '';
uploadProgress.value = 0.0;
uploadedImgUrl.value = '';
uploadedVideoUrl.value = '';
descriptionController.clear();
descFocusNode.unfocus();
status.value = '';
status2.value = '';
uploadProgress.value = 0.0;
uploadProgress2.value = 0.0;
videoPath.value = '';
}
//
void openVd() {
FocusScope.of(context).unfocus();
showGeneralDialog(
context: context,
// barrierDismissible: true,
barrierColor: Colors.black.withAlpha((1.0 * 255).round()),
pageBuilder: (_, __, ___) {
return SafeArea(
child: PreviewVideo(
videoUrl: videoPath.value,
),
);
},
transitionBuilder: (_, anim, __, child) {
return FadeTransition(opacity: anim, child: child);
},
transitionDuration: const Duration(milliseconds: 200),
);
}
void openImg() {
//
Get.to(() => ImageViewer(
images: [imgPath.value],
index: 0,
));
}
@override
void dispose() {
descController.dispose();
descriptionController.dispose();
descFocusNode.dispose();
super.dispose();
}
@ -193,7 +294,8 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
children: [
TextField(
focusNode: descFocusNode,
controller: descController,
autofocus: false,
controller: descriptionController,
decoration: const InputDecoration(
labelText: '视频描述 *',
border: OutlineInputBorder(),
@ -201,72 +303,125 @@ class _UploadVideoPageState extends State<UploadVideoPage> {
maxLines: 2,
),
const SizedBox(height: 20),
//
Obx(() {
final video = selectedVideo.value;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
//
snapshot.value.isNotEmpty
? GestureDetector(
onTap: () => openVd(),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(snapshot.value),
width: 80,
fit: BoxFit.cover,
),
),
)
: const SizedBox(
height: 80,
child: Center(
child: Text(
'未选择视频',
style: TextStyle(fontSize: 16),
)),
),
const SizedBox(width: 24),
// /
Expanded(
child: Text(
video != null ? '已选择视频:${video.title}' : '未选择视频',
style: const TextStyle(fontSize: 16),
),
),
ElevatedButton(
onPressed: pickVideo,
child: const Text('选择视频'),
child: uploading.value
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 10),
LinearProgressIndicator(value: uploadProgress.value),
const SizedBox(height: 5),
Text('${(uploadProgress.value * 100).toStringAsFixed(1)}%'),
],
)
: ElevatedButton.icon(
onPressed: pickVideo,
icon: const Icon(Icons.upload),
label: Text(uploadedVideoUrl.value.isNotEmpty ? '重新选择' : '选择视频'),
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
),
),
],
);
}),
const SizedBox(height: 20),
//
Obx(() {
final cover = selectedCover.value;
return Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
cover != null
? FutureBuilder<Uint8List?>(
future: cover.thumbnailDataWithSize(const ThumbnailSize(80, 80)),
builder: (_, snapshot) {
if (snapshot.connectionState == ConnectionState.done && snapshot.hasData) {
return ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.memory(snapshot.data!, width: 80, height: 80),
);
} else {
return const SizedBox(width: 80, height: 80);
}
},
//
imgPath.value.isNotEmpty
? GestureDetector(
onTap: () => openImg(),
child: ClipRRect(
borderRadius: BorderRadius.circular(8),
child: Image.file(
File(imgPath.value),
width: 80,
fit: BoxFit.cover,
),
),
)
: const Text('未选择封面图'),
const SizedBox(width: 12),
ElevatedButton(
onPressed: pickCoverImage,
child: const Text('选择封面(可选)'),
: const SizedBox(
height: 80,
child: Center(
child: Text(
'未选择封面',
style: TextStyle(fontSize: 16),
)),
),
const SizedBox(width: 24),
// /
Expanded(
child: uploading2.value
? Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const CircularProgressIndicator(),
const SizedBox(height: 10),
LinearProgressIndicator(value: uploadProgress2.value),
const SizedBox(height: 5),
Text('${(uploadProgress2.value * 100).toStringAsFixed(1)}%'),
],
)
: ElevatedButton.icon(
onPressed: pickCoverImage,
icon: const Icon(Icons.upload),
label: Text(uploadedImgUrl.value.isNotEmpty ? '重新选择' : '选择封面(可选)'),
style: ElevatedButton.styleFrom(
minimumSize: const Size.fromHeight(50),
),
),
),
],
);
}),
const SizedBox(height: 30),
Obx(() => uploading.value
? Column(
children: [
const CircularProgressIndicator(),
const SizedBox(height: 10),
LinearProgressIndicator(value: uploadProgress.value),
const SizedBox(height: 5),
Text('${(uploadProgress.value * 100).toStringAsFixed(1)}%'),
],
)
: SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: uploadVideo,
icon: const Icon(Icons.upload),
label: const Text('上传'),
),
)),
const SizedBox(height: 20),
Obx(() => Text(status.value)),
//
SizedBox(
width: double.infinity,
child: ElevatedButton.icon(
onPressed: submitForm,
icon: const Icon(Icons.upload),
label: const Text('发布'),
),
),
],
),
),

View File

@ -21,6 +21,7 @@ import 'package:media_kit/media_kit.dart';
import 'package:media_kit_video/media_kit_video.dart';
import 'package:media_kit_video/media_kit_video_controls/src/controls/extensions/duration.dart';
import 'package:tencent_cloud_chat_sdk/models/v2_tim_conversation.dart';
import '../../../router/fade_route.dart';
import './components/popup_reply.dart';
@ -100,7 +101,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
});
try {
final res = await Http.get('${VideoApi.videoDetailApi}${videoId}');
final res = await Http.get('${VideoApi.videoDetailApi}$videoId');
logger.d('视频详情接口返回: ${json.encode(res)}');
@ -256,7 +257,6 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
Future<void> unfollowUser() async {
try {
final vlogerId = videoData['vlogerId'] ?? videoData['memberId'];
} catch (e) {
logger.e('取消关注用户异常: $e');
MyToast().tip(
@ -309,6 +309,8 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
context: context,
isScrollControlled: true,
builder: (context) {
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
return Material(
color: Colors.white,
child: Padding(
@ -348,15 +350,14 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
),
//
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
if (filteredList.isNotEmpty)
SizedBox(
height: 110,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
itemCount: filteredList.length,
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
itemBuilder: (context, index) {
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
return GestureDetector(
onTap: () => handlCoverClick(filteredList[index].conversation),
child: Container(
@ -365,10 +366,12 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
ClipOval(
child: NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
),
),
const SizedBox(height: 5),
Text(
@ -908,6 +911,7 @@ class _VideoDetailPageState extends State<VideoDetailPage> {
);
}
}
class CommentBottomSheet extends StatefulWidget {
final String videoId;
final Function(int) onCommentCountChanged; //

View File

@ -798,6 +798,8 @@ class _AttentionModuleState extends State<AttentionModule> {
context: context,
isScrollControlled: true,
builder: (context) {
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
return Material(
color: Colors.white,
child: Padding(
@ -837,15 +839,14 @@ class _AttentionModuleState extends State<AttentionModule> {
),
//
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
if (filteredList.isNotEmpty)
SizedBox(
height: 110,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
itemCount: filteredList.length,
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
itemBuilder: (context, index) {
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
return GestureDetector(
onTap: () => handlCoverClick(filteredList[index].conversation),
child: Container(
@ -1265,17 +1266,14 @@ class _AttentionModuleState extends State<AttentionModule> {
),
],
),
onTap: ()async {
player.pause();
//
final result = await Get.toNamed(
'/report',
arguments: videoList[videoModuleController
.videoPlayIndex.value]);
if (result != null) {
player.play();
};
},
onTap: () async {
player.pause();
//
final result = await Get.toNamed('/report', arguments: videoList[videoModuleController.videoPlayIndex.value]);
if (result != null) {
player.play();
}
},
),
],
),

View File

@ -15,6 +15,7 @@ import 'package:loopin/IM/im_service.dart' hide logger;
import 'package:loopin/api/video_api.dart';
import 'package:loopin/components/my_toast.dart';
import 'package:loopin/components/network_or_asset_image.dart';
import 'package:loopin/models/conversation_type.dart';
import 'package:loopin/models/summary_type.dart';
import 'package:loopin/service/http.dart';
import 'package:loopin/utils/download_video.dart';
@ -794,6 +795,7 @@ class _RecommendModuleState extends State<RecommendModule> {
context: context,
isScrollControlled: true,
builder: (context) {
final filteredList = chatController.chatList.where((item) => conversationTypeFromString(item.isCustomAdmin) == null).toList();
return Material(
color: Colors.white,
child: Padding(
@ -831,17 +833,15 @@ class _RecommendModuleState extends State<RecommendModule> {
},
),
),
//
if (chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).isNotEmpty)
if (filteredList.isNotEmpty)
SizedBox(
height: 110,
child: ListView.builder(
scrollDirection: Axis.horizontal,
itemCount: chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).length,
itemCount: filteredList.length,
padding: const EdgeInsets.symmetric(horizontal: 0, vertical: 20.0),
itemBuilder: (context, index) {
final filteredList = chatController.chatList.where((item) => item.conversation.conversationGroupList?.isEmpty == true).toList();
return GestureDetector(
onTap: () => handlCoverClick(filteredList[index].conversation),
child: Container(
@ -850,10 +850,12 @@ class _RecommendModuleState extends State<RecommendModule> {
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
ClipOval(
child: NetworkOrAssetImage(
imageUrl: filteredList[index].faceUrl,
width: 48.0,
height: 48.0,
),
),
const SizedBox(height: 5),
Text(

View File

@ -11,5 +11,5 @@ Future<String?> generateVideoThumbnail(String videoPath) async {
maxWidth: 120,
quality: 75,
);
return thumbnailPath;
return thumbnailPath ?? '';
}