mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
opt: member page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -95,6 +95,7 @@ class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
|
|||||||
// As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used
|
// As long as the height is 0 instead of the sliver app bar a sliver to box adapter will be used
|
||||||
// to calculate dynamically the size for the sliver app bar
|
// to calculate dynamically the size for the sliver app bar
|
||||||
double _height = 0;
|
double _height = 0;
|
||||||
|
Orientation? _orientation;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -105,6 +106,7 @@ class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
|
|||||||
@override
|
@override
|
||||||
void didUpdateWidget(covariant DynamicSliverAppBar oldWidget) {
|
void didUpdateWidget(covariant DynamicSliverAppBar oldWidget) {
|
||||||
super.didUpdateWidget(oldWidget);
|
super.didUpdateWidget(oldWidget);
|
||||||
|
|
||||||
_updateHeight();
|
_updateHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +126,11 @@ class _DynamicSliverAppBarState extends State<DynamicSliverAppBar> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
//Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height
|
//Needed to lay out the flexibleSpace the first time, so we can calculate its intrinsic height
|
||||||
|
Orientation orientation = MediaQuery.orientationOf(context);
|
||||||
|
if (_orientation != orientation) {
|
||||||
|
_orientation = orientation;
|
||||||
|
_height = 0;
|
||||||
|
}
|
||||||
if (_height == 0) {
|
if (_height == 0) {
|
||||||
return SliverToBoxAdapter(
|
return SliverToBoxAdapter(
|
||||||
child: Stack(
|
child: Stack(
|
||||||
|
|||||||
@@ -50,7 +50,8 @@ class VideoCardH extends StatelessWidget {
|
|||||||
excludeSemantics: true,
|
excludeSemantics: true,
|
||||||
customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
customSemanticsActions: <CustomSemanticsAction, void Function()>{
|
||||||
for (var item in actions)
|
for (var item in actions)
|
||||||
CustomSemanticsAction(label: item.title): item.onTap!,
|
CustomSemanticsAction(
|
||||||
|
label: item.title.isEmpty ? 'label' : item.title): item.onTap!,
|
||||||
},
|
},
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
borderRadius: BorderRadius.circular(12),
|
borderRadius: BorderRadius.circular(12),
|
||||||
|
|||||||
@@ -24,19 +24,20 @@ class VideoCustomActions {
|
|||||||
late List<VideoCustomAction> actions;
|
late List<VideoCustomAction> actions;
|
||||||
VideoCustomActions(this.videoItem, this.context) {
|
VideoCustomActions(this.videoItem, this.context) {
|
||||||
actions = [
|
actions = [
|
||||||
VideoCustomAction(
|
if ((videoItem.bvid as String?)?.isNotEmpty == true)
|
||||||
videoItem.bvid,
|
VideoCustomAction(
|
||||||
'copy',
|
videoItem.bvid,
|
||||||
Stack(
|
'copy',
|
||||||
children: [
|
Stack(
|
||||||
Icon(MdiIcons.identifier, size: 16),
|
children: [
|
||||||
Icon(MdiIcons.circleOutline, size: 16),
|
Icon(MdiIcons.identifier, size: 16),
|
||||||
],
|
Icon(MdiIcons.circleOutline, size: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
() {
|
||||||
|
Utils.copyText(videoItem.bvid);
|
||||||
|
},
|
||||||
),
|
),
|
||||||
() {
|
|
||||||
Utils.copyText(videoItem.bvid);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
VideoCustomAction(
|
VideoCustomAction(
|
||||||
'稍后再看',
|
'稍后再看',
|
||||||
'pause',
|
'pause',
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ class _MemberPageNewState extends State<MemberPageNew>
|
|||||||
);
|
);
|
||||||
_userController.scrollController.addListener(() {
|
_userController.scrollController.addListener(() {
|
||||||
_userController.scrollRatio.value =
|
_userController.scrollRatio.value =
|
||||||
min(1.0, _userController.scrollController.offset.round() / 150);
|
min(1.0, _userController.scrollController.offset.round() / 120);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -53,71 +53,73 @@ class _MemberPageNewState extends State<MemberPageNew>
|
|||||||
() => _userController.loadingState.value is Success
|
() => _userController.loadingState.value is Success
|
||||||
? LayoutBuilder(
|
? LayoutBuilder(
|
||||||
builder: (_, constraints) {
|
builder: (_, constraints) {
|
||||||
if (constraints.maxHeight > constraints.maxWidth) {
|
// if (constraints.maxHeight > constraints.maxWidth) {
|
||||||
return ExtendedNestedScrollView(
|
return ExtendedNestedScrollView(
|
||||||
controller: _userController.scrollController,
|
controller: _userController.scrollController,
|
||||||
onlyOneScrollInBody: true,
|
onlyOneScrollInBody: true,
|
||||||
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
headerSliverBuilder: (context, innerBoxIsScrolled) {
|
||||||
return [
|
return [
|
||||||
SliverOverlapAbsorber(
|
SliverOverlapAbsorber(
|
||||||
handle: ExtendedNestedScrollView
|
handle: ExtendedNestedScrollView
|
||||||
.sliverOverlapAbsorberHandleFor(context),
|
.sliverOverlapAbsorberHandleFor(context),
|
||||||
sliver: _buildAppBar(),
|
sliver: _buildAppBar(
|
||||||
),
|
isV: constraints.maxHeight > constraints.maxWidth,
|
||||||
];
|
|
||||||
},
|
|
||||||
body: _userController.tab2?.isNotEmpty == true
|
|
||||||
? LayoutBuilder(
|
|
||||||
builder: (context, _) {
|
|
||||||
return Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: ExtendedNestedScrollView
|
|
||||||
.sliverOverlapAbsorberHandleFor(
|
|
||||||
context)
|
|
||||||
.layoutExtent ??
|
|
||||||
0,
|
|
||||||
),
|
|
||||||
child: _buildBody,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
)
|
|
||||||
: Center(child: const Text('EMPTY')),
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: CustomScrollView(
|
|
||||||
slivers: [
|
|
||||||
_buildAppBar(false),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
];
|
||||||
child: SafeArea(
|
},
|
||||||
top: false,
|
body: _userController.tab2?.isNotEmpty == true
|
||||||
left: false,
|
? LayoutBuilder(
|
||||||
bottom: false,
|
builder: (context, _) {
|
||||||
child: Column(
|
return Padding(
|
||||||
children: [
|
padding: EdgeInsets.only(
|
||||||
SizedBox(height: _userController.top),
|
top: ExtendedNestedScrollView
|
||||||
if ((_userController.tab2?.length ?? -1) > 1)
|
.sliverOverlapAbsorberHandleFor(
|
||||||
_buildTab,
|
context)
|
||||||
Expanded(
|
.layoutExtent ??
|
||||||
child:
|
0,
|
||||||
_userController.tab2?.isNotEmpty == true
|
|
||||||
? _buildBody
|
|
||||||
: Center(
|
|
||||||
child: const Text('EMPTY'),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
child: _buildBody,
|
||||||
),
|
);
|
||||||
),
|
},
|
||||||
),
|
)
|
||||||
],
|
: Center(child: const Text('EMPTY')),
|
||||||
);
|
);
|
||||||
}
|
// } else {
|
||||||
|
// return Row(
|
||||||
|
// children: [
|
||||||
|
// Expanded(
|
||||||
|
// child: CustomScrollView(
|
||||||
|
// slivers: [
|
||||||
|
// _buildAppBar(false),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// Expanded(
|
||||||
|
// child: SafeArea(
|
||||||
|
// top: false,
|
||||||
|
// left: false,
|
||||||
|
// bottom: false,
|
||||||
|
// child: Column(
|
||||||
|
// children: [
|
||||||
|
// SizedBox(height: _userController.top),
|
||||||
|
// if ((_userController.tab2?.length ?? -1) > 1)
|
||||||
|
// _buildTab,
|
||||||
|
// Expanded(
|
||||||
|
// child:
|
||||||
|
// _userController.tab2?.isNotEmpty == true
|
||||||
|
// ? _buildBody
|
||||||
|
// : Center(
|
||||||
|
// child: const Text('EMPTY'),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ),
|
||||||
|
// ],
|
||||||
|
// );
|
||||||
|
// }
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
: Center(
|
: Center(
|
||||||
@@ -132,37 +134,44 @@ class _MemberPageNewState extends State<MemberPageNew>
|
|||||||
tabs: _userController.tabs,
|
tabs: _userController.tabs,
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget get _buildBody => TabBarView(
|
Widget get _buildBody => Padding(
|
||||||
controller: _userController.tabController,
|
padding: EdgeInsets.only(
|
||||||
children: _userController.tab2!.map((item) {
|
left: MediaQuery.paddingOf(context).left,
|
||||||
return switch (item.param!) {
|
right: MediaQuery.paddingOf(context).right,
|
||||||
'home' => MemberHome(heroTag: _heroTag),
|
),
|
||||||
// 'dynamic' => MemberDynamic(mid: _mid ?? -1),
|
child: TabBarView(
|
||||||
'dynamic' => MemberDynamicsPage(mid: _mid),
|
controller: _userController.tabController,
|
||||||
'contribute' => Obx(
|
children: _userController.tab2!.map((item) {
|
||||||
() => MemberContribute(
|
return switch (item.param!) {
|
||||||
|
'home' => MemberHome(heroTag: _heroTag),
|
||||||
|
// 'dynamic' => MemberDynamic(mid: _mid ?? -1),
|
||||||
|
'dynamic' => MemberDynamicsPage(mid: _mid),
|
||||||
|
'contribute' => Obx(
|
||||||
|
() => MemberContribute(
|
||||||
|
heroTag: _heroTag,
|
||||||
|
initialIndex: _userController.contributeInitialIndex.value,
|
||||||
|
mid: _mid ?? -1,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
'bangumi' => MemberBangumi(
|
||||||
heroTag: _heroTag,
|
heroTag: _heroTag,
|
||||||
initialIndex: _userController.contributeInitialIndex.value,
|
|
||||||
mid: _mid ?? -1,
|
mid: _mid ?? -1,
|
||||||
),
|
),
|
||||||
),
|
'favorite' => MemberFavorite(
|
||||||
'bangumi' => MemberBangumi(
|
heroTag: _heroTag,
|
||||||
heroTag: _heroTag,
|
mid: _mid ?? -1,
|
||||||
mid: _mid ?? -1,
|
),
|
||||||
),
|
_ => Center(child: Text(item.title ?? '')),
|
||||||
'favorite' => MemberFavorite(
|
};
|
||||||
heroTag: _heroTag,
|
}).toList(),
|
||||||
mid: _mid ?? -1,
|
),
|
||||||
),
|
|
||||||
_ => Center(child: Text(item.title ?? '')),
|
|
||||||
};
|
|
||||||
}).toList(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
Widget _buildAppBar([bool needTab = true]) => MediaQuery.removePadding(
|
Widget _buildAppBar({bool needTab = true, bool isV = true}) =>
|
||||||
|
MediaQuery.removePadding(
|
||||||
context: context,
|
context: context,
|
||||||
removeTop: true,
|
removeTop: true,
|
||||||
removeRight: true,
|
// removeRight: true,
|
||||||
child: DynamicSliverAppBar(
|
child: DynamicSliverAppBar(
|
||||||
leading: Padding(
|
leading: Padding(
|
||||||
padding: EdgeInsets.only(top: _userController.top ?? 0),
|
padding: EdgeInsets.only(top: _userController.top ?? 0),
|
||||||
@@ -178,7 +187,8 @@ class _MemberPageNewState extends State<MemberPageNew>
|
|||||||
pinned: true,
|
pinned: true,
|
||||||
backgroundColor: Theme.of(context).colorScheme.surface,
|
backgroundColor: Theme.of(context).colorScheme.surface,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
flexibleSpace: _buildUserInfo(_userController.loadingState.value),
|
flexibleSpace:
|
||||||
|
_buildUserInfo(_userController.loadingState.value, isV),
|
||||||
bottom: needTab && (_userController.tab2?.length ?? -1) > 1
|
bottom: needTab && (_userController.tab2?.length ?? -1) > 1
|
||||||
? PreferredSize(
|
? PreferredSize(
|
||||||
preferredSize: Size.fromHeight(48),
|
preferredSize: Size.fromHeight(48),
|
||||||
@@ -291,7 +301,7 @@ class _MemberPageNewState extends State<MemberPageNew>
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget _buildUserInfo(LoadingState userState) {
|
Widget _buildUserInfo(LoadingState userState, [bool isV = true]) {
|
||||||
switch (userState) {
|
switch (userState) {
|
||||||
case Empty():
|
case Empty():
|
||||||
return _errorWidget('EMPTY');
|
return _errorWidget('EMPTY');
|
||||||
@@ -303,6 +313,7 @@ class _MemberPageNewState extends State<MemberPageNew>
|
|||||||
padding: EdgeInsets.only(
|
padding: EdgeInsets.only(
|
||||||
bottom: (_userController.tab2?.length ?? 0) > 1 ? 48 : 0),
|
bottom: (_userController.tab2?.length ?? 0) > 1 ? 48 : 0),
|
||||||
child: UserInfoCard(
|
child: UserInfoCard(
|
||||||
|
isV: isV,
|
||||||
isOwner: _userController.mid == _userController.ownerMid,
|
isOwner: _userController.mid == _userController.ownerMid,
|
||||||
relation: _userController.relation.value,
|
relation: _userController.relation.value,
|
||||||
isFollow: _userController.isFollow.value,
|
isFollow: _userController.isFollow.value,
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import 'package:get/get.dart';
|
|||||||
class UserInfoCard extends StatelessWidget {
|
class UserInfoCard extends StatelessWidget {
|
||||||
const UserInfoCard({
|
const UserInfoCard({
|
||||||
super.key,
|
super.key,
|
||||||
|
required this.isV,
|
||||||
required this.isOwner,
|
required this.isOwner,
|
||||||
required this.card,
|
required this.card,
|
||||||
required this.images,
|
required this.images,
|
||||||
@@ -20,6 +21,7 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
required this.onFollow,
|
required this.onFollow,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
final bool isV;
|
||||||
final bool isOwner;
|
final bool isOwner;
|
||||||
final int relation;
|
final int relation;
|
||||||
final bool isFollow;
|
final bool isFollow;
|
||||||
@@ -29,245 +31,79 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
return isV ? _buildV(context) : _buildH(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
Widget _countWidget({
|
||||||
|
required String title,
|
||||||
|
required int count,
|
||||||
|
required VoidCallback onTap,
|
||||||
|
}) {
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: onTap,
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
Utils.numFormat(count),
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 14,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
title,
|
||||||
|
style: TextStyle(
|
||||||
|
height: 1,
|
||||||
|
fontSize: 11,
|
||||||
|
color: Theme.of(Get.context!).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildHeader(BuildContext context) {
|
||||||
bool darken = GStorage.brightness == Brightness.dark;
|
bool darken = GStorage.brightness == Brightness.dark;
|
||||||
String? imgUrl = darken
|
String? imgUrl = darken
|
||||||
? (images.nightImgurl?.isEmpty == true
|
? (images.nightImgurl?.isEmpty == true
|
||||||
? images.imgUrl?.http2https
|
? images.imgUrl?.http2https
|
||||||
: images.nightImgurl?.http2https)
|
: images.nightImgurl?.http2https)
|
||||||
: images.imgUrl?.http2https;
|
: images.imgUrl?.http2https;
|
||||||
return Column(
|
return GestureDetector(
|
||||||
mainAxisAlignment: MainAxisAlignment.start,
|
onTap: () {
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
showDialog(
|
||||||
children: [
|
useSafeArea: false,
|
||||||
Stack(
|
context: context,
|
||||||
children: [
|
builder: (context) {
|
||||||
Column(
|
return ImagePreview(
|
||||||
mainAxisSize: MainAxisSize.min,
|
initialPage: 0,
|
||||||
children: [
|
imgList: [imgUrl ?? ''],
|
||||||
GestureDetector(
|
);
|
||||||
onTap: () {
|
},
|
||||||
showDialog(
|
);
|
||||||
useSafeArea: false,
|
},
|
||||||
context: context,
|
child: CachedNetworkImage(
|
||||||
builder: (context) {
|
imageUrl: imgUrl ?? '',
|
||||||
return ImagePreview(
|
width: double.infinity,
|
||||||
initialPage: 0,
|
height: 135,
|
||||||
imgList: [imgUrl ?? ''],
|
imageBuilder: (context, imageProvider) => Container(
|
||||||
);
|
decoration: BoxDecoration(
|
||||||
},
|
image: DecorationImage(
|
||||||
);
|
image: imageProvider,
|
||||||
},
|
fit: BoxFit.cover,
|
||||||
child: CachedNetworkImage(
|
colorFilter: ColorFilter.mode(
|
||||||
imageUrl: imgUrl ?? '',
|
darken ? const Color(0x8D000000) : const Color(0x5DFFFFFF),
|
||||||
width: double.infinity,
|
darken ? BlendMode.darken : BlendMode.lighten,
|
||||||
height: 135,
|
|
||||||
imageBuilder: (context, imageProvider) => Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
image: DecorationImage(
|
|
||||||
image: imageProvider,
|
|
||||||
fit: BoxFit.cover,
|
|
||||||
colorFilter: ColorFilter.mode(
|
|
||||||
darken
|
|
||||||
? const Color(0x8D000000)
|
|
||||||
: const Color(0x5DFFFFFF),
|
|
||||||
darken ? BlendMode.darken : BlendMode.lighten,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: double.infinity, height: 85)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: 110,
|
|
||||||
left: 20,
|
|
||||||
child: GestureDetector(
|
|
||||||
onTap: () {
|
|
||||||
showDialog(
|
|
||||||
useSafeArea: false,
|
|
||||||
context: context,
|
|
||||||
builder: (context) {
|
|
||||||
return ImagePreview(
|
|
||||||
initialPage: 0,
|
|
||||||
imgList: [card.face ?? ''],
|
|
||||||
);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
border: Border.all(
|
|
||||||
width: 2.5,
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
),
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
),
|
|
||||||
child: NetworkImgLayer(
|
|
||||||
src: card.face,
|
|
||||||
type: 'avatar',
|
|
||||||
width: 80,
|
|
||||||
height: 80,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (card.officialVerify?.icon?.isNotEmpty == true ||
|
),
|
||||||
(card.vip?.vipStatus ?? -1) > 0)
|
|
||||||
Positioned(
|
|
||||||
top: 170,
|
|
||||||
left: 80,
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(0.01),
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
shape: BoxShape.circle,
|
|
||||||
color: Theme.of(context).colorScheme.surface,
|
|
||||||
),
|
|
||||||
child: card.officialVerify?.icon?.isNotEmpty == true
|
|
||||||
? NetworkImgLayer(
|
|
||||||
src: card.officialVerify?.icon,
|
|
||||||
radius: null,
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
quality: 100,
|
|
||||||
)
|
|
||||||
: Image.asset(
|
|
||||||
'assets/images/big-vip.png',
|
|
||||||
width: 24,
|
|
||||||
height: 24,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Positioned(
|
|
||||||
top: 140,
|
|
||||||
right: 20,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: List.generate(
|
|
||||||
5,
|
|
||||||
(index) => index % 2 == 0
|
|
||||||
? Padding(
|
|
||||||
padding:
|
|
||||||
const EdgeInsets.symmetric(horizontal: 20),
|
|
||||||
child: _countWidget(
|
|
||||||
title: ['粉丝', '关注', '获赞'][index ~/ 2],
|
|
||||||
count: index == 0
|
|
||||||
? card.fans
|
|
||||||
: index == 2
|
|
||||||
? card.attention
|
|
||||||
: card.likes?.likeNum ?? 0,
|
|
||||||
onTap: () {
|
|
||||||
if (index == 0) {
|
|
||||||
Get.toNamed(
|
|
||||||
'/fan?mid=${card.mid}&name=${card.name}');
|
|
||||||
} else if (index == 2) {
|
|
||||||
Get.toNamed(
|
|
||||||
'/follow?mid=${card.mid}&name=${card.name}');
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
: SizedBox(
|
|
||||||
height: 15,
|
|
||||||
width: 1,
|
|
||||||
child: VerticalDivider(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 5),
|
|
||||||
Row(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
if (!isOwner)
|
|
||||||
IconButton.outlined(
|
|
||||||
onPressed: () {
|
|
||||||
if (GStorage.userInfo.get('userInfoCache') !=
|
|
||||||
null) {
|
|
||||||
Get.toNamed(
|
|
||||||
'/whisperDetail',
|
|
||||||
parameters: {
|
|
||||||
'talkerId': card.mid ?? '',
|
|
||||||
'name': card.name ?? '',
|
|
||||||
'face': card.face ?? '',
|
|
||||||
'mid': card.mid ?? '',
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.mail_outline, size: 21),
|
|
||||||
style: IconButton.styleFrom(
|
|
||||||
side: BorderSide(
|
|
||||||
width: 1.0,
|
|
||||||
color: Theme.of(context)
|
|
||||||
.colorScheme
|
|
||||||
.outline
|
|
||||||
.withOpacity(0.5),
|
|
||||||
),
|
|
||||||
padding: EdgeInsets.zero,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
visualDensity: const VisualDensity(
|
|
||||||
horizontal: -2,
|
|
||||||
vertical: -2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
FilledButton.tonal(
|
|
||||||
onPressed: onFollow,
|
|
||||||
style: FilledButton.styleFrom(
|
|
||||||
backgroundColor: relation == -1 || isFollow
|
|
||||||
? Theme.of(context).colorScheme.onInverseSurface
|
|
||||||
: null,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 50),
|
|
||||||
visualDensity: const VisualDensity(
|
|
||||||
horizontal: -2,
|
|
||||||
vertical: -2,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Text.rich(
|
|
||||||
style: TextStyle(
|
|
||||||
color: relation == -1 || isFollow
|
|
||||||
? Theme.of(context).colorScheme.outline
|
|
||||||
: null,
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
children: [
|
|
||||||
if (isFollow)
|
|
||||||
WidgetSpan(
|
|
||||||
alignment: PlaceholderAlignment.top,
|
|
||||||
child: Icon(
|
|
||||||
Icons.sort,
|
|
||||||
size: 16,
|
|
||||||
color:
|
|
||||||
Theme.of(context).colorScheme.outline,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
TextSpan(
|
|
||||||
text: isOwner
|
|
||||||
? '编辑资料'
|
|
||||||
: relation == -1
|
|
||||||
? '移除黑名单'
|
|
||||||
: relation == 2
|
|
||||||
? ' 特别关注'
|
|
||||||
: isFollow
|
|
||||||
? ' 已关注'
|
|
||||||
: '关注',
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
_buildLeft(BuildContext context) => [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
child: Wrap(
|
child: Wrap(
|
||||||
@@ -434,37 +270,255 @@ class UserInfoCard extends StatelessWidget {
|
|||||||
// .toList(),
|
// .toList(),
|
||||||
// ),
|
// ),
|
||||||
// ),
|
// ),
|
||||||
const SizedBox(height: 5),
|
];
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget _countWidget({
|
_buildRight(BuildContext context) => Column(
|
||||||
required String title,
|
|
||||||
required int count,
|
|
||||||
required VoidCallback onTap,
|
|
||||||
}) {
|
|
||||||
return GestureDetector(
|
|
||||||
onTap: onTap,
|
|
||||||
child: Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
mainAxisSize: MainAxisSize.min,
|
||||||
children: [
|
children: [
|
||||||
Text(
|
Row(
|
||||||
Utils.numFormat(count),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
style: TextStyle(
|
children: List.generate(
|
||||||
fontSize: 14,
|
5,
|
||||||
|
(index) => index % 2 == 0
|
||||||
|
? Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: _countWidget(
|
||||||
|
title: ['粉丝', '关注', '获赞'][index ~/ 2],
|
||||||
|
count: index == 0
|
||||||
|
? card.fans
|
||||||
|
: index == 2
|
||||||
|
? card.attention
|
||||||
|
: card.likes?.likeNum ?? 0,
|
||||||
|
onTap: () {
|
||||||
|
if (index == 0) {
|
||||||
|
Get.toNamed(
|
||||||
|
'/fan?mid=${card.mid}&name=${card.name}');
|
||||||
|
} else if (index == 2) {
|
||||||
|
Get.toNamed(
|
||||||
|
'/follow?mid=${card.mid}&name=${card.name}');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
: SizedBox(
|
||||||
|
height: 15,
|
||||||
|
width: 1,
|
||||||
|
child: VerticalDivider(),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Text(
|
const SizedBox(height: 5),
|
||||||
title,
|
Row(
|
||||||
style: TextStyle(
|
mainAxisSize: MainAxisSize.min,
|
||||||
height: 1,
|
children: [
|
||||||
fontSize: 11,
|
if (!isOwner)
|
||||||
color: Theme.of(Get.context!).colorScheme.outline,
|
IconButton.outlined(
|
||||||
),
|
onPressed: () {
|
||||||
|
if (GStorage.userInfo.get('userInfoCache') != null) {
|
||||||
|
Get.toNamed(
|
||||||
|
'/whisperDetail',
|
||||||
|
parameters: {
|
||||||
|
'talkerId': card.mid ?? '',
|
||||||
|
'name': card.name ?? '',
|
||||||
|
'face': card.face ?? '',
|
||||||
|
'mid': card.mid ?? '',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.mail_outline, size: 21),
|
||||||
|
style: IconButton.styleFrom(
|
||||||
|
side: BorderSide(
|
||||||
|
width: 1.0,
|
||||||
|
color: Theme.of(context)
|
||||||
|
.colorScheme
|
||||||
|
.outline
|
||||||
|
.withOpacity(0.5),
|
||||||
|
),
|
||||||
|
padding: EdgeInsets.zero,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
visualDensity: const VisualDensity(
|
||||||
|
horizontal: -2,
|
||||||
|
vertical: -2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
FilledButton.tonal(
|
||||||
|
onPressed: onFollow,
|
||||||
|
style: FilledButton.styleFrom(
|
||||||
|
backgroundColor: relation == -1 || isFollow
|
||||||
|
? Theme.of(context).colorScheme.onInverseSurface
|
||||||
|
: null,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 50),
|
||||||
|
visualDensity: const VisualDensity(
|
||||||
|
horizontal: -2,
|
||||||
|
vertical: -2,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
child: Text.rich(
|
||||||
|
style: TextStyle(
|
||||||
|
color: relation == -1 || isFollow
|
||||||
|
? Theme.of(context).colorScheme.outline
|
||||||
|
: null,
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
children: [
|
||||||
|
if (isFollow)
|
||||||
|
WidgetSpan(
|
||||||
|
alignment: PlaceholderAlignment.top,
|
||||||
|
child: Icon(
|
||||||
|
Icons.sort,
|
||||||
|
size: 16,
|
||||||
|
color: Theme.of(context).colorScheme.outline,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
TextSpan(
|
||||||
|
text: isOwner
|
||||||
|
? '编辑资料'
|
||||||
|
: relation == -1
|
||||||
|
? '移除黑名单'
|
||||||
|
: relation == 2
|
||||||
|
? ' 特别关注'
|
||||||
|
: isFollow
|
||||||
|
? ' 已关注'
|
||||||
|
: '关注',
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
);
|
|
||||||
}
|
_buildBadge(BuildContext context) => Container(
|
||||||
|
padding: const EdgeInsets.all(0.01),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
child: card.officialVerify?.icon?.isNotEmpty == true
|
||||||
|
? NetworkImgLayer(
|
||||||
|
src: card.officialVerify?.icon,
|
||||||
|
radius: null,
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
quality: 100,
|
||||||
|
)
|
||||||
|
: Image.asset(
|
||||||
|
'assets/images/big-vip.png',
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
_buildAvatar(BuildContext context) => GestureDetector(
|
||||||
|
onTap: () {
|
||||||
|
showDialog(
|
||||||
|
useSafeArea: false,
|
||||||
|
context: context,
|
||||||
|
builder: (context) {
|
||||||
|
return ImagePreview(
|
||||||
|
initialPage: 0,
|
||||||
|
imgList: [card.face ?? ''],
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
border: Border.all(
|
||||||
|
width: 2.5,
|
||||||
|
color: Theme.of(context).colorScheme.surface,
|
||||||
|
),
|
||||||
|
shape: BoxShape.circle,
|
||||||
|
),
|
||||||
|
child: NetworkImgLayer(
|
||||||
|
src: card.face,
|
||||||
|
type: 'avatar',
|
||||||
|
width: 80,
|
||||||
|
height: 80,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
_buildV(BuildContext context) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
const SizedBox(width: double.infinity, height: 85)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 110,
|
||||||
|
left: 20,
|
||||||
|
child: _buildAvatar(context),
|
||||||
|
),
|
||||||
|
if (card.officialVerify?.icon?.isNotEmpty == true ||
|
||||||
|
(card.vip?.vipStatus ?? -1) > 0)
|
||||||
|
Positioned(
|
||||||
|
top: 170,
|
||||||
|
left: 80,
|
||||||
|
child: _buildBadge(context),
|
||||||
|
),
|
||||||
|
Positioned(
|
||||||
|
top: 140,
|
||||||
|
right: 20,
|
||||||
|
child: _buildRight(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
..._buildLeft(context),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
_buildH(BuildContext context) => Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.start,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
_buildHeader(context),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(width: MediaQuery.paddingOf(context).left),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Stack(
|
||||||
|
children: [
|
||||||
|
_buildAvatar(context),
|
||||||
|
if (card.officialVerify?.icon?.isNotEmpty == true ||
|
||||||
|
(card.vip?.vipStatus ?? -1) > 0)
|
||||||
|
Positioned(
|
||||||
|
right: 0,
|
||||||
|
bottom: 0,
|
||||||
|
child: _buildBadge(context),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
..._buildLeft(context),
|
||||||
|
const SizedBox(height: 5),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Expanded(child: _buildRight(context)),
|
||||||
|
SizedBox(width: MediaQuery.paddingOf(context).right),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user