初始化
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/hj_model/video_model.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/common_service.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
|
||||
import 'package:hgdj/tools_base/toast.dart';
|
||||
import 'package:hgdj/tools_base/widget/swipe_action_item.dart';
|
||||
import 'package:pull_to_refresh/pull_to_refresh.dart';
|
||||
|
||||
import '../../../hj_model/mine/follow_user_list_model.dart';
|
||||
import 'mine_follow_sub_page.dart';
|
||||
import 'widget/follow_list_items.dart';
|
||||
|
||||
abstract class MineFollowSubLogic extends GetxController
|
||||
with GetTickerProviderStateMixin {
|
||||
final int? uid;
|
||||
MineFollowSubLogic(this.uid);
|
||||
|
||||
RxInt loadCount = 0.obs;
|
||||
|
||||
final dataSource = [];
|
||||
|
||||
RefreshController? refreshController;
|
||||
int pageNumber = 1;
|
||||
bool isLoading = true;
|
||||
bool isGridStyle = false;
|
||||
|
||||
@override
|
||||
void onReady() {
|
||||
super.onReady();
|
||||
fetchPageData();
|
||||
}
|
||||
|
||||
@mustCallSuper
|
||||
fetchPageData({bool isRefresh = true}) {
|
||||
if (isRefresh) {
|
||||
pageNumber = 1;
|
||||
}
|
||||
}
|
||||
|
||||
Widget instanceChildItem(int index);
|
||||
}
|
||||
|
||||
class ActressController extends MineFollowSubLogic {
|
||||
ActressController(super.uid);
|
||||
|
||||
@override
|
||||
fetchPageData({bool isRefresh = true}) async {
|
||||
super.fetchPageData(isRefresh: isRefresh);
|
||||
final retModel = await MineService.fetchCollectList<FollowUserModel>(
|
||||
'actress',
|
||||
page: pageNumber,
|
||||
size: 10,
|
||||
uid: uid,
|
||||
);
|
||||
|
||||
isLoading = false;
|
||||
if (isRefresh) {
|
||||
dataSource.clear();
|
||||
refreshController?.refreshCompleted();
|
||||
}
|
||||
retModel?.hasNext ?? false
|
||||
? refreshController?.loadComplete()
|
||||
: refreshController?.loadNoData();
|
||||
dataSource.addAll(retModel?.list ?? []);
|
||||
pageNumber += 1;
|
||||
update();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget instanceChildItem(int index) {
|
||||
return FollowActressItem(dataSource[index]);
|
||||
}
|
||||
}
|
||||
|
||||
class BloggerController extends MineFollowSubLogic {
|
||||
BloggerController(super.uid);
|
||||
|
||||
@override
|
||||
fetchPageData({bool isRefresh = true}) async {
|
||||
super.fetchPageData(isRefresh: isRefresh);
|
||||
final retModel = await CommonService.getFollowUsers(
|
||||
pageNumber: pageNumber,
|
||||
pageSize: 10,
|
||||
uid: uid,
|
||||
);
|
||||
|
||||
isLoading = false;
|
||||
if (isRefresh) {
|
||||
dataSource.clear();
|
||||
refreshController?.refreshCompleted();
|
||||
}
|
||||
retModel?.hasNext ?? false
|
||||
? refreshController?.loadComplete()
|
||||
: refreshController?.loadNoData();
|
||||
dataSource.addAll(retModel?.list ?? []);
|
||||
pageNumber += 1;
|
||||
update();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget instanceChildItem(int index) {
|
||||
final model = dataSource[index] as FollowUserModel;
|
||||
// 用 ObjectKey 而不是 ValueKey(uid):uid 是可空的,两条 uid 都为 null 就是重复 key,
|
||||
// Flutter 会直接抛 Duplicate keys 白屏。绑对象身份天然唯一,且能让展开状态不串项
|
||||
return SwipeActionItem(
|
||||
key: ObjectKey(model),
|
||||
actionText: '取消关注',
|
||||
onAction: () => _unfollow(model),
|
||||
child: FollowBloggerItem(model),
|
||||
);
|
||||
}
|
||||
|
||||
/// 正在取关的项:按钮收起动画那 200ms 里还能再点一次,防重复请求
|
||||
final _unfollowing = <FollowUserModel>{};
|
||||
|
||||
/// 左滑取关:成功才从列表摘掉(接口内部会 emit CollectStatusModel 同步其他页面)
|
||||
Future<void> _unfollow(FollowUserModel model) async {
|
||||
if (!_unfollowing.add(model)) return;
|
||||
try {
|
||||
final ok = await MineService.getFollow(model.uid, false);
|
||||
if (!ok) return showToast('取消关注失败,请重试');
|
||||
dataSource.remove(model); // 按对象移除,不用 index——异步期间列表可能已刷新
|
||||
update();
|
||||
} finally {
|
||||
_unfollowing.remove(model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class TopicController extends MineFollowSubLogic {
|
||||
TopicController(super.uid);
|
||||
@override
|
||||
bool get isGridStyle => true;
|
||||
|
||||
@override
|
||||
fetchPageData({bool isRefresh = true}) async {
|
||||
super.fetchPageData(isRefresh: isRefresh);
|
||||
final retModel = await MineService.fetchCollectList<TagsBean>("tag",
|
||||
page: pageNumber, size: 10, uid: uid);
|
||||
isLoading = false;
|
||||
if (isRefresh) {
|
||||
dataSource.clear();
|
||||
refreshController?.refreshCompleted();
|
||||
}
|
||||
retModel?.hasNext ?? false
|
||||
? refreshController?.loadComplete()
|
||||
: refreshController?.loadNoData();
|
||||
dataSource.addAll(retModel?.list ?? []);
|
||||
pageNumber += 1;
|
||||
update();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget instanceChildItem(int index) {
|
||||
return FollowTopicItem(dataSource[index]);
|
||||
}
|
||||
}
|
||||
|
||||
MineFollowSubLogic instanceController(FollowType type, {int? uid}) {
|
||||
switch (type) {
|
||||
case FollowType.user:
|
||||
return BloggerController(uid);
|
||||
case FollowType.topic:
|
||||
return TopicController(uid);
|
||||
case FollowType.actress:
|
||||
return ActressController(uid);
|
||||
default:
|
||||
throw 'type 没有定义';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../../tools_base/loading/loading_center_widget.dart';
|
||||
import '../../../tools_base/refresh/pull_refresh.dart';
|
||||
import '../../../tools_base/widget/swipe_action_item.dart';
|
||||
import 'mine_follow_sub_logic.dart';
|
||||
|
||||
enum FollowType {
|
||||
user,
|
||||
topic,
|
||||
actress;
|
||||
}
|
||||
|
||||
class MineFollowSubPage extends StatelessWidget {
|
||||
final FollowType pageType; // 0 博主, 1 粉丝, 2 话题
|
||||
final int? uid;
|
||||
|
||||
MineFollowSubPage({super.key, required this.pageType, this.uid});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
// 用默认 global:global:false 下 widget 销毁不会 Get.delete,onClose 永远不触发,
|
||||
// 往 logic 里加 Timer/订阅就会静默泄漏。本页单实例(入口只在「我的」,链路不成环)故不加 tag;
|
||||
// 将来若做成多 pageType 并存的 TabBarView,得按 pageType 补 tag,否则三个 tab 抢同一个实例
|
||||
body: GetBuilder<MineFollowSubLogic>(
|
||||
init: instanceController(pageType, uid: uid),
|
||||
builder: (logic) {
|
||||
return pullYsRefresh(
|
||||
onRefresh: (refreshController) => logic.fetchPageData(),
|
||||
onLoading: (refreshController) => logic.fetchPageData(isRefresh: false),
|
||||
onInit: (ctr) => logic.refreshController = ctr,
|
||||
child: () {
|
||||
if (logic.isLoading) return const LoadingCenterWidget();
|
||||
if (logic.dataSource.isEmpty)
|
||||
return CErrorWidget(
|
||||
retryOnTap: () {
|
||||
logic.fetchPageData();
|
||||
},
|
||||
);
|
||||
if (logic.isGridStyle)
|
||||
return GridView.builder(
|
||||
padding: EdgeInsets.only(left: 16, right: 16, top: 12),
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 2,
|
||||
mainAxisSpacing: 10,
|
||||
crossAxisSpacing: 5,
|
||||
childAspectRatio: 165 / 143,
|
||||
),
|
||||
itemCount: logic.dataSource.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return logic.instanceChildItem(index);
|
||||
},
|
||||
);
|
||||
// 一滚动就收起左滑展开的那项,免得滑走了还留着个张开的
|
||||
return NotificationListener<ScrollStartNotification>(
|
||||
onNotification: (_) {
|
||||
SwipeActionItem.closeOpened();
|
||||
return false;
|
||||
},
|
||||
child: ListView.builder(
|
||||
padding: EdgeInsets.only(top: 12),
|
||||
itemCount: logic.dataSource.length,
|
||||
itemBuilder: (BuildContext context, int index) {
|
||||
return logic.instanceChildItem(index);
|
||||
},
|
||||
),
|
||||
);
|
||||
}());
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'mine_follow_sub_page.dart';
|
||||
|
||||
class MineFollowingPage extends StatelessWidget {
|
||||
const MineFollowingPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: const Text('我的关注')),
|
||||
body: MineFollowSubPage(pageType: FollowType.user),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,294 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_screenutil/flutter_screenutil.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hgdj/assets_tool/images.dart';
|
||||
import 'package:hgdj/hj_model/mine/follow_user_list_model.dart';
|
||||
import 'package:hgdj/hj_model/video_model.dart';
|
||||
import 'package:hgdj/hj_utils/api_service/mine_service.dart';
|
||||
import 'package:hgdj/hj_utils/screen.dart';
|
||||
import 'package:hgdj/routers/jump_router.dart';
|
||||
import 'package:hgdj/tools_base/widget/net_image_widget.dart';
|
||||
|
||||
import '../../../community/community_tag_page/community_tag_page.dart';
|
||||
import 'package:hgdj/extension/extensions.dart';
|
||||
|
||||
/// 关注列表 - 女优项
|
||||
class FollowActressItem extends StatefulWidget {
|
||||
final FollowUserModel model;
|
||||
const FollowActressItem(this.model, {super.key});
|
||||
|
||||
@override
|
||||
State<FollowActressItem> createState() => _FollowActressItemState();
|
||||
}
|
||||
|
||||
class _FollowActressItemState extends State<FollowActressItem> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
// type:1 原跳女优主页 ActressMainPage(已下线),pushToPersonCenter 内 type==1 已 return null
|
||||
// → 当前点击不跳转,待产品确认替代页(objcId 与用户中心 uid 非同一体系,不能直接跳 UserCenterPage)
|
||||
pushToPersonCenter(widget.model.objcId, type: 1);
|
||||
},
|
||||
child: Container(
|
||||
margin: EdgeInsets.symmetric(horizontal: 18.w),
|
||||
padding: EdgeInsets.symmetric(horizontal: 13.w, vertical: 16.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xff242424),
|
||||
borderRadius: BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: widget.model.portrait ?? "",
|
||||
width: 43,
|
||||
height: 43,
|
||||
borderRadius: 6,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
widget.model.name ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
"粉丝: ${widget.model.fans?.countStr}",
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
color: Color(0xff666666),
|
||||
fontWeight: FontWeight.w400,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final isFollow = widget.model.hasFollow ?? false;
|
||||
final res = await MineService.postCollect(
|
||||
widget.model.objcId, 'actresss', !isFollow);
|
||||
if (res) {
|
||||
widget.model.hasFollow = !isFollow;
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.symmetric(horizontal: 11.w, vertical: 5.h),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white.withValues(alpha: .3),
|
||||
borderRadius: BorderRadius.circular(40)),
|
||||
child: Row(
|
||||
children: [
|
||||
if (widget.model.hasFollow == true) ...[
|
||||
Image.asset('collect_red.png'.commonImgPath, width: 16.w),
|
||||
7.w.sizeBoxW,
|
||||
],
|
||||
Text(
|
||||
widget.model.hasFollow == true ? '已关注' : '关注',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 关注列表 - 博主项
|
||||
class FollowBloggerItem extends StatefulWidget {
|
||||
final FollowUserModel model;
|
||||
const FollowBloggerItem(this.model, {super.key});
|
||||
|
||||
@override
|
||||
State<FollowBloggerItem> createState() => _FollowBloggerItemState();
|
||||
}
|
||||
|
||||
class _FollowBloggerItemState extends State<FollowBloggerItem> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
pushToPersonCenter(widget.model.uid ?? 0);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.only(bottom: 18, left: 16, right: 16),
|
||||
child: Row(
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: widget.model.portrait ?? "",
|
||||
width: 60,
|
||||
height: 60,
|
||||
borderRadius: 30,
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
widget.model.name ?? "",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Color(0xE5FFFFFF),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
6.sizeBoxH,
|
||||
Row(
|
||||
children: [
|
||||
Flexible(
|
||||
child: Text(
|
||||
'${widget.model.totalWorks ?? 0} 作品',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0x8CFFFFFF),
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Flexible(
|
||||
child: Text(
|
||||
'${widget.model.fans?.countStr} 粉丝',
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0x8CFFFFFF),
|
||||
fontWeight: FontWeight.w400),
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(width: 12),
|
||||
Image.asset(
|
||||
'arrow_right_grey.webp'.commonImgPath,
|
||||
color: Color(0xffDCDCDC),
|
||||
width: 24,
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// 关注列表 - 话题项
|
||||
class FollowTopicItem extends StatefulWidget {
|
||||
final TagsBean model;
|
||||
const FollowTopicItem(this.model, {super.key});
|
||||
|
||||
@override
|
||||
State<FollowTopicItem> createState() => _FollowTopicItemState();
|
||||
}
|
||||
|
||||
class _FollowTopicItemState extends State<FollowTopicItem> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
enableFeedback: false,
|
||||
onTap: () {
|
||||
Get.to(() => CommunityTagDetailPage(model: widget.model));
|
||||
},
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
alignment: Alignment.center,
|
||||
children: [
|
||||
NetworkImageLoader(
|
||||
imageUrl: widget.model.coverImg ?? "",
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
borderRadius: 8,
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withValues(alpha: .6),
|
||||
borderRadius: BorderRadius.circular(8)),
|
||||
),
|
||||
Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
Text(
|
||||
"#${widget.model.name}",
|
||||
overflow: TextOverflow.ellipsis,
|
||||
maxLines: 1,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${widget.model.videoCount.countStr}个帖子',
|
||||
style: TextStyle(color: Color(0xffcccccc), fontSize: 10),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
SizedBox(height: 12),
|
||||
GestureDetector(
|
||||
onTap: () async {
|
||||
final isCollected = widget.model.hasCollected ?? false;
|
||||
final res = await MineService.postCollect(
|
||||
widget.model.id, "tag", !isCollected);
|
||||
if (res) {
|
||||
widget.model.hasCollected = !isCollected;
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
width: 108,
|
||||
height: 27,
|
||||
alignment: Alignment.center,
|
||||
decoration: BoxDecoration(
|
||||
color: Color(0xFFF68804),
|
||||
borderRadius: BorderRadius.circular(6)),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
widget.model.hasCollected == true ? '已关注' : '关注',
|
||||
style: TextStyle(color: Colors.white, fontSize: 14),
|
||||
)
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user