mod: pages

Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
bggRGjQaUbCoE
2025-03-04 21:20:18 +08:00
parent 56c5ad360a
commit ff2ed0421c
10 changed files with 1348 additions and 1320 deletions

View File

@@ -292,7 +292,7 @@ class MyApp extends StatelessWidget {
}
class _CustomHttpOverrides extends HttpOverrides {
static final badCertificateCallback =
final badCertificateCallback =
BuildConfig.isDebug || GStorage.badCertificateCallback;
@override

View File

@@ -1,5 +1,6 @@
import 'package:PiliPlus/pages/common/common_slide_page.dart';
import 'package:PiliPlus/pages/search/widgets/search_text.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/material.dart';
import 'package:PiliPlus/common/widgets/stat/danmu.dart';
import 'package:PiliPlus/common/widgets/stat/view.dart';
@@ -22,15 +23,46 @@ class IntroDetail extends CommonSlidePage {
}
class _IntroDetailState extends CommonSlidePageState<IntroDetail> {
late bool _isInit = true;
late final TextStyle smallTitle = TextStyle(
fontSize: 12,
color: Theme.of(context).colorScheme.onSurface,
);
@override
void initState() {
super.initState();
if (enableSlide && GStorage.collapsibleVideoPage) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_isInit = false;
});
}
});
}
}
@override
Widget build(BuildContext context) {
if (enableSlide && GStorage.collapsibleVideoPage && _isInit) {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),
);
}
return enableSlide
? Padding(
padding: EdgeInsets.only(top: padding),
child: buildPage,
)
: buildPage;
}
@override
Widget get buildPage {
return Padding(
padding: const EdgeInsets.only(left: 14, right: 14),
return Material(
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
GestureDetector(
@@ -61,95 +93,96 @@ class _IntroDetailState extends CommonSlidePageState<IntroDetail> {
}
@override
Widget get buildList => SingleChildScrollView(
Widget get buildList => ListView(
controller: ScrollController(),
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SelectableText(
widget.bangumiDetail!.title,
style: const TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 4),
Row(
children: [
statView(
context: context,
theme: 'gray',
view: widget.bangumiDetail!.stat!['views'],
size: 'medium',
),
const SizedBox(width: 6),
statDanMu(
context: context,
theme: 'gray',
danmu: widget.bangumiDetail!.stat!['danmakus'],
size: 'medium',
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.bangumiDetail!.areas!.first['name'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.bangumiDetail!.publish!['pub_time_show'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.bangumiDetail!.newEp!['desc'],
style: smallTitle,
),
],
),
const SizedBox(height: 20),
Text(
'简介:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
'${widget.bangumiDetail!.evaluate!}',
style: smallTitle.copyWith(fontSize: 13),
),
const SizedBox(height: 20),
Text(
'声优:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
widget.bangumiDetail.actors,
style: smallTitle.copyWith(fontSize: 13),
),
if (widget.videoTags is List && widget.videoTags.isNotEmpty) ...[
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: (widget.videoTags as List)
.map(
(item) => SearchText(
fontSize: 13,
text: item['tag_name'],
onTap: (_) => Get.toNamed('/searchResult',
parameters: {'keyword': item['tag_name']}),
onLongPress: (_) => Utils.copyText(item['tag_name']),
),
)
.toList(),
)
],
SizedBox(height: MediaQuery.of(context).padding.bottom + 20)
],
padding: EdgeInsets.only(
left: 14,
right: 14,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
children: [
SelectableText(
widget.bangumiDetail!.title,
style: const TextStyle(
fontSize: 16,
),
),
const SizedBox(height: 4),
Row(
children: [
statView(
context: context,
theme: 'gray',
view: widget.bangumiDetail!.stat!['views'],
size: 'medium',
),
const SizedBox(width: 6),
statDanMu(
context: context,
theme: 'gray',
danmu: widget.bangumiDetail!.stat!['danmakus'],
size: 'medium',
),
],
),
const SizedBox(height: 4),
Row(
children: [
Text(
widget.bangumiDetail!.areas!.first['name'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.bangumiDetail!.publish!['pub_time_show'],
style: smallTitle,
),
const SizedBox(width: 6),
Text(
widget.bangumiDetail!.newEp!['desc'],
style: smallTitle,
),
],
),
const SizedBox(height: 20),
Text(
'简介:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
'${widget.bangumiDetail!.evaluate!}',
style: smallTitle.copyWith(fontSize: 14),
),
const SizedBox(height: 20),
Text(
'声优:',
style: Theme.of(context).textTheme.titleMedium,
),
const SizedBox(height: 4),
SelectableText(
widget.bangumiDetail.actors,
style: smallTitle.copyWith(fontSize: 14),
),
if (widget.videoTags is List && widget.videoTags.isNotEmpty) ...[
const SizedBox(height: 10),
Wrap(
spacing: 8,
runSpacing: 8,
children: (widget.videoTags as List)
.map(
(item) => SearchText(
fontSize: 13,
text: item['tag_name'],
onTap: (_) => Get.toNamed('/searchResult',
parameters: {'keyword': item['tag_name']}),
onLongPress: (_) => Utils.copyText(item['tag_name']),
),
)
.toList(),
)
],
],
);
}

View File

@@ -395,8 +395,7 @@ class VideoDetailController extends GetxController
showMediaListPanel(context) {
if (mediaList.isNotEmpty) {
childKey.currentState?.showBottomSheet(
shape: const RoundedRectangleBorder(),
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: Colors.transparent,
(context) => MediaListPanel(
mediaList: mediaList,
changeMediaList: (bvid, cid, aid, cover) {

View File

@@ -19,8 +19,10 @@ class IntroDetail extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.only(left: 14, right: 14),
return Material(
color: Theme.of(context).colorScheme.surface,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Column(
children: [
InkWell(
@@ -133,7 +135,9 @@ class IntroDetail extends StatelessWidget {
),
)
],
));
),
),
);
}
InlineSpan buildContent(BuildContext context, content) {

View File

@@ -109,13 +109,15 @@ class _ViewPointsPageState extends CommonSlidePageState<ViewPointsPage> {
);
@override
Widget get buildList => SingleChildScrollView(
Widget get buildList => ListView(
controller: ScrollController(),
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
...List.generate(videoDetailController.viewPointList.length * 2 - 1,
(rawIndex) {
padding:
EdgeInsets.only(bottom: MediaQuery.paddingOf(context).bottom + 80),
children: [
...List.generate(
videoDetailController.viewPointList.length * 2 - 1,
(rawIndex) {
if (rawIndex % 2 == 1) {
return Divider(
height: 1,
@@ -191,9 +193,8 @@ class _ViewPointsPageState extends CommonSlidePageState<ViewPointsPage> {
),
),
);
}),
SizedBox(height: 25 + MediaQuery.paddingOf(context).bottom),
],
),
},
),
],
);
}

View File

@@ -2130,8 +2130,7 @@ class _VideoDetailPageVState extends State<VideoDetailPageV>
showIntroDetail(videoDetail, videoTags) {
videoDetailController.childKey.currentState?.showBottomSheet(
shape: const RoundedRectangleBorder(),
backgroundColor: Theme.of(context).colorScheme.surface,
backgroundColor: Colors.transparent,
(context) => videoDetail is BangumiInfoModel
? bangumi.IntroDetail(
bangumiDetail: videoDetail,

View File

@@ -1,6 +1,7 @@
import 'package:PiliPlus/common/constants.dart';
import 'package:PiliPlus/pages/common/common_slide_page.dart';
import 'package:PiliPlus/pages/video/detail/controller.dart';
import 'package:PiliPlus/utils/storage.dart';
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
@@ -21,6 +22,38 @@ class AiDetail extends CommonSlidePage {
}
class _AiDetailState extends CommonSlidePageState<AiDetail> {
late bool _isInit = true;
@override
void initState() {
super.initState();
if (enableSlide && GStorage.collapsibleVideoPage) {
WidgetsBinding.instance.addPostFrameCallback((_) {
if (mounted) {
setState(() {
_isInit = false;
});
}
});
}
}
@override
Widget build(BuildContext context) {
if (enableSlide && GStorage.collapsibleVideoPage && _isInit) {
return CustomScrollView(
physics: const NeverScrollableScrollPhysics(),
);
}
return enableSlide
? Padding(
padding: EdgeInsets.only(top: padding),
child: buildPage,
)
: buildPage;
}
InlineSpan buildContent(BuildContext context, content) {
List descV2 = content.descV2;
// type
@@ -89,12 +122,11 @@ class _AiDetailState extends CommonSlidePageState<AiDetail> {
}
@override
Widget get buildPage => Container(
Widget get buildPage => Material(
color: Theme.of(context).colorScheme.surface,
padding: const EdgeInsets.symmetric(horizontal: 14),
child: Column(
children: [
InkWell(
GestureDetector(
onTap: Get.back,
child: Container(
height: 35,
@@ -119,34 +151,46 @@ class _AiDetailState extends CommonSlidePageState<AiDetail> {
);
@override
Widget get buildList => SingleChildScrollView(
Widget get buildList => CustomScrollView(
controller: ScrollController(),
physics: const AlwaysScrollableScrollPhysics(),
child: Column(
children: [
if (widget.modelResult.summary?.isNotEmpty == true) ...[
SelectableText(
'总结: ${widget.modelResult.summary}',
style: const TextStyle(
fontSize: 15,
height: 1.5,
slivers: [
if (widget.modelResult.summary?.isNotEmpty == true) ...[
SliverToBoxAdapter(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 14),
child: SelectableText(
'总结: ${widget.modelResult.summary}',
style: const TextStyle(
fontSize: 15,
height: 1.5,
),
),
),
if (widget.modelResult.outline?.isNotEmpty == true)
Divider(
),
if (widget.modelResult.outline?.isNotEmpty == true)
SliverToBoxAdapter(
child: Divider(
height: 20,
color: Theme.of(context).dividerColor.withOpacity(0.1),
thickness: 6,
)
],
if (widget.modelResult.outline?.isNotEmpty == true)
ListView.builder(
shrinkWrap: true,
),
),
],
if (widget.modelResult.outline?.isNotEmpty == true)
SliverPadding(
padding: EdgeInsets.only(
left: 14,
right: 14,
bottom: MediaQuery.paddingOf(context).bottom + 80,
),
sliver: SliverList.builder(
itemCount: widget.modelResult.outline!.length,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, index) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
if (index != 0) const SizedBox(height: 10),
SelectableText(
widget.modelResult.outline![index].title!,
style: const TextStyle(
@@ -159,86 +203,59 @@ class _AiDetailState extends CommonSlidePageState<AiDetail> {
if (widget.modelResult.outline![index].partOutline
?.isNotEmpty ==
true)
ListView.builder(
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemCount: widget
.modelResult.outline![index].partOutline!.length,
itemBuilder: (context, i) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Wrap(
...widget.modelResult.outline![index].partOutline!.map(
(item) => Wrap(
children: [
SelectableText.rich(
TextSpan(
style: TextStyle(
fontSize: 14,
color:
Theme.of(context).colorScheme.onSurface,
height: 1.5,
),
children: [
SelectableText.rich(
TextSpan(
style: TextStyle(
fontSize: 13,
color: Theme.of(context)
.colorScheme
.onSurface,
height: 1.5,
),
children: [
TextSpan(
text: Utils.tampToSeektime(widget
.modelResult
.outline![index]
.partOutline![i]
.timestamp!),
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// 跳转到指定位置
try {
Get.find<VideoDetailController>(
tag: Get.arguments[
'heroTag'])
.plPlayerController
.seekTo(
Duration(
seconds:
Utils.duration(
Utils.tampToSeektime(widget
.modelResult
.outline![
index]
.partOutline![
i]
.timestamp!)
.toString(),
),
),
);
} catch (_) {}
},
),
const TextSpan(text: ' '),
TextSpan(
text: widget
.modelResult
.outline![index]
.partOutline![i]
.content!),
],
TextSpan(
text:
Utils.tampToSeektime(item.timestamp!),
style: TextStyle(
color: Theme.of(context)
.colorScheme
.primary,
),
recognizer: TapGestureRecognizer()
..onTap = () {
// 跳转到指定位置
try {
Get.find<VideoDetailController>(
tag: Get
.arguments['heroTag'])
.plPlayerController
.seekTo(
Duration(
seconds: Utils.duration(
Utils.tampToSeektime(
item.timestamp!)
.toString(),
),
),
);
} catch (_) {}
},
),
const TextSpan(text: ' '),
TextSpan(text: item.content!),
],
),
],
);
},
),
],
),
),
const SizedBox(height: 20),
],
);
},
)
],
),
),
),
],
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -64,38 +64,41 @@ class _MediaListPanelState extends CommonSlidePageState<MediaListPanel> {
@override
Widget get buildPage {
return Column(
children: [
AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
titleSpacing: 16,
title: Text(widget.panelTitle ?? '稍后再看'),
actions: [
Obx(
() => mediumButton(
tooltip: desc.value ? '顺序播放' : '倒序播放',
icon: desc.value
? MdiIcons.sortAscending
: MdiIcons.sortDescending,
onPressed: () {
widget.onReverse();
desc.value = !desc.value;
},
return Material(
color: Theme.of(context).colorScheme.surface,
child: Column(
children: [
AppBar(
toolbarHeight: 45,
automaticallyImplyLeading: false,
titleSpacing: 16,
title: Text(widget.panelTitle ?? '稍后再看'),
actions: [
Obx(
() => mediumButton(
tooltip: desc.value ? '顺序播放' : '倒序播放',
icon: desc.value
? MdiIcons.sortAscending
: MdiIcons.sortDescending,
onPressed: () {
widget.onReverse();
desc.value = !desc.value;
},
),
),
),
mediumButton(
tooltip: '关闭',
icon: Icons.close,
onPressed: Get.back,
),
const SizedBox(width: 14),
],
),
Expanded(
child: enableSlide ? slideList() : buildList,
),
],
mediumButton(
tooltip: '关闭',
icon: Icons.close,
onPressed: Get.back,
),
const SizedBox(width: 14),
],
),
Expanded(
child: enableSlide ? slideList() : buildList,
),
],
),
);
}

View File

@@ -78,273 +78,265 @@ class _WhisperPageState extends State<WhisperPage> {
_whisperController.onRefresh(),
]);
},
child: SingleChildScrollView(
child: ListView(
padding: EdgeInsets.only(bottom: 80),
controller: _scrollController,
child: Column(
children: [
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// 在这里根据父级容器的约束条件构建小部件树
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: SizedBox(
height: 90,
child: Obx(
() => Row(
children: Iterable<int>.generate(
_whisperController.msgFeedTop.length)
.map((idx) {
return Expanded(
child: GestureDetector(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Badge(
isLabelVisible: _whisperController
.msgFeedTop[idx]['value'] >
0,
label: Text(
" ${_whisperController.msgFeedTop[idx]['value']} "),
alignment: Alignment.topRight,
child: CircleAvatar(
radius: 22,
backgroundColor: Theme.of(context)
.colorScheme
.onInverseSurface,
child: Icon(
_whisperController.msgFeedTop[idx]
['icon'],
size: 20,
color:
Theme.of(context).colorScheme.primary,
),
children: [
LayoutBuilder(
builder: (BuildContext context, BoxConstraints constraints) {
// 在这里根据父级容器的约束条件构建小部件树
return Padding(
padding: const EdgeInsets.only(left: 20, right: 20),
child: SizedBox(
height: 90,
child: Obx(
() => Row(
children: Iterable<int>.generate(
_whisperController.msgFeedTop.length)
.map((idx) {
return Expanded(
child: GestureDetector(
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.center,
children: [
Badge(
isLabelVisible: _whisperController
.msgFeedTop[idx]['value'] >
0,
label: Text(
" ${_whisperController.msgFeedTop[idx]['value']} "),
alignment: Alignment.topRight,
child: CircleAvatar(
radius: 22,
backgroundColor: Theme.of(context)
.colorScheme
.onInverseSurface,
child: Icon(
_whisperController.msgFeedTop[idx]['icon'],
size: 20,
color:
Theme.of(context).colorScheme.primary,
),
),
const SizedBox(height: 6),
Text(_whisperController.msgFeedTop[idx]['name'],
style: const TextStyle(fontSize: 13))
],
),
onTap: () {
if (!_whisperController.msgFeedTop[idx]
['enabled']) {
SmartDialog.showToast('已禁用');
return;
}
setState(() {
_whisperController.msgFeedTop[idx]['value'] = 0;
});
Get.toNamed(
_whisperController.msgFeedTop[idx]['route']);
},
));
}).toList(),
),
),
const SizedBox(height: 6),
Text(_whisperController.msgFeedTop[idx]['name'],
style: const TextStyle(fontSize: 13))
],
),
onTap: () {
if (!_whisperController.msgFeedTop[idx]
['enabled']) {
SmartDialog.showToast('已禁用');
return;
}
setState(() {
_whisperController.msgFeedTop[idx]['value'] = 0;
});
Get.toNamed(
_whisperController.msgFeedTop[idx]['route']);
},
));
}).toList(),
),
),
);
}),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
// TODO: refactor
if (snapshot.data is! Map) {
return HttpError(
isSliver: false,
callback: () => setState(() {
_futureBuilderFuture =
_whisperController.querySessionList('init');
}),
);
}
Map data = snapshot.data as Map;
if (data['status']) {
List<SessionList> sessionList =
_whisperController.sessionList;
return Obx(
() => sessionList.isEmpty
? const SizedBox()
: ListView.separated(
itemCount: sessionList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, int i) {
dynamic content =
sessionList[i].lastMsg?.content;
if (content == null || content == "") {
content = '不支持的消息类型';
} else {
dynamic msg = content['text'] ??
content['content'] ??
content['title'] ??
content['reply_content'];
if (msg == null) {
if (content['imageType'] != null) {
msg = '[图片消息]';
}
),
);
}),
FutureBuilder(
future: _futureBuilderFuture,
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.data != null) {
// TODO: refactor
if (snapshot.data is! Map) {
return HttpError(
isSliver: false,
callback: () => setState(() {
_futureBuilderFuture =
_whisperController.querySessionList('init');
}),
);
}
Map data = snapshot.data as Map;
if (data['status']) {
List<SessionList> sessionList =
_whisperController.sessionList;
return Obx(
() => sessionList.isEmpty
? const SizedBox()
: ListView.separated(
itemCount: sessionList.length,
shrinkWrap: true,
physics: const NeverScrollableScrollPhysics(),
itemBuilder: (context, int i) {
dynamic content =
sessionList[i].lastMsg?.content;
if (content == null || content == "") {
content = '不支持的消息类型';
} else {
dynamic msg = content['text'] ??
content['content'] ??
content['title'] ??
content['reply_content'];
if (msg == null) {
if (content['imageType'] != null) {
msg = '[图片消息]';
}
content = msg ?? content.toString();
}
return ListTile(
tileColor: sessionList[i].topTs == 0
? null
: Theme.of(context)
.colorScheme
.onInverseSurface,
onLongPress: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
clipBehavior: Clip.hardEdge,
contentPadding:
const EdgeInsets.symmetric(
vertical: 12),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
dense: true,
onTap: () {
Get.back();
_whisperController
.onSetTop(i);
},
title: Text(
sessionList[i].topTs == 0
? '置顶'
: '移除置顶',
style: const TextStyle(
fontSize: 14),
),
content = msg ?? content.toString();
}
return ListTile(
tileColor: sessionList[i].topTs == 0
? null
: Theme.of(context)
.colorScheme
.onInverseSurface,
onLongPress: () {
showDialog(
context: context,
builder: (context) {
return AlertDialog(
clipBehavior: Clip.hardEdge,
contentPadding:
const EdgeInsets.symmetric(
vertical: 12),
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
ListTile(
dense: true,
onTap: () {
Get.back();
_whisperController
.onSetTop(i);
},
title: Text(
sessionList[i].topTs == 0
? '置顶'
: '移除置顶',
style: const TextStyle(
fontSize: 14),
),
ListTile(
dense: true,
onTap: () {
Get.back();
_whisperController
.onRemove(i);
},
title: const Text(
'删除',
style:
TextStyle(fontSize: 14),
),
),
ListTile(
dense: true,
onTap: () {
Get.back();
_whisperController
.onRemove(i);
},
title: const Text(
'删除',
style:
TextStyle(fontSize: 14),
),
],
),
);
},
);
},
onTap: () {
setState(() {
sessionList[i].unreadCount = 0;
});
Get.toNamed(
'/whisperDetail',
parameters: {
'talkerId':
'${sessionList[i].talkerId}',
'name': sessionList[i]
.accountInfo
?.name ??
'',
'face': sessionList[i]
),
],
),
);
},
);
},
onTap: () {
setState(() {
sessionList[i].unreadCount = 0;
});
Get.toNamed(
'/whisperDetail',
parameters: {
'talkerId':
'${sessionList[i].talkerId}',
'name':
sessionList[i].accountInfo?.name ??
'',
'face':
sessionList[i].accountInfo?.face ??
'',
if (sessionList[i].accountInfo?.mid !=
null)
'mid':
'${sessionList[i].accountInfo?.mid}',
},
);
},
leading: Builder(builder: (context) {
Widget buildAvatar() => NetworkImgLayer(
width: 45,
height: 45,
type: 'avatar',
src: sessionList[i]
.accountInfo
?.face ??
'',
if (sessionList[i].accountInfo?.mid !=
null)
'mid':
'${sessionList[i].accountInfo?.mid}',
},
);
},
leading: Builder(builder: (context) {
Widget buildAvatar() => NetworkImgLayer(
width: 45,
height: 45,
type: 'avatar',
src: sessionList[i]
.accountInfo
?.face ??
"",
);
return sessionList[i].unreadCount != null
? Badge(
isLabelVisible:
sessionList[i].unreadCount! >
0,
label: Text(
" ${sessionList[i].unreadCount} "),
alignment: Alignment.topRight,
child: buildAvatar(),
)
: buildAvatar();
}),
title: Text(
sessionList[i].accountInfo?.name ?? ""),
subtitle: Text(
'$content',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.outline),
),
trailing:
sessionList[i].lastMsg?.timestamp !=
null
? Text(
Utils.dateFormat(
sessionList[i]
.lastMsg!
.timestamp,
formatType: "day"),
style: Theme.of(context)
.textTheme
.labelSmall!
.copyWith(
color: Theme.of(context)
.colorScheme
.outline),
)
: null,
);
},
separatorBuilder:
(BuildContext context, int index) {
return Divider(
indent: 72,
endIndent: 20,
height: 6,
color: Colors.grey.withOpacity(0.1),
);
},
),
);
} else {
// 请求错误
return Center(
child: Text(data['msg'] ?? '请求异常'),
);
}
"",
);
return sessionList[i].unreadCount != null
? Badge(
isLabelVisible:
sessionList[i].unreadCount! > 0,
label: Text(
" ${sessionList[i].unreadCount} "),
alignment: Alignment.topRight,
child: buildAvatar(),
)
: buildAvatar();
}),
title: Text(
sessionList[i].accountInfo?.name ?? ""),
subtitle: Text(
'$content',
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: Theme.of(context)
.textTheme
.labelMedium!
.copyWith(
color: Theme.of(context)
.colorScheme
.outline),
),
trailing: sessionList[i].lastMsg?.timestamp !=
null
? Text(
Utils.dateFormat(
sessionList[i].lastMsg!.timestamp,
formatType: "day"),
style: Theme.of(context)
.textTheme
.labelSmall!
.copyWith(
color: Theme.of(context)
.colorScheme
.outline),
)
: null,
);
},
separatorBuilder:
(BuildContext context, int index) {
return Divider(
indent: 72,
endIndent: 20,
height: 6,
color: Colors.grey.withOpacity(0.1),
);
},
),
);
} else {
// 骨架屏
return const SizedBox();
// 请求错误
return Center(
child: Text(data['msg'] ?? '请求异常'),
);
}
},
)
],
),
} else {
// 骨架屏
return const SizedBox();
}
},
)
],
),
),
);