feat: 粉丝页面
This commit is contained in:
@ -180,4 +180,9 @@ class Api {
|
|||||||
// 关注分类
|
// 关注分类
|
||||||
// https://api.bilibili.com/x/relation/tags
|
// https://api.bilibili.com/x/relation/tags
|
||||||
static const String followingsClass = '/x/relation/tags';
|
static const String followingsClass = '/x/relation/tags';
|
||||||
|
|
||||||
|
// 粉丝
|
||||||
|
// vmid 用户id pn 页码 ps 每页个数,最大50 order: desc
|
||||||
|
// order_type 排序规则 最近访问传空,最常访问传 attention
|
||||||
|
static const String fans = 'https://api.bilibili.com/x/relation/fans';
|
||||||
}
|
}
|
||||||
|
|||||||
23
lib/http/fan.dart
Normal file
23
lib/http/fan.dart
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import 'package:pilipala/http/index.dart';
|
||||||
|
import 'package:pilipala/models/fans/result.dart';
|
||||||
|
|
||||||
|
class FanHttp {
|
||||||
|
static Future fans({int? vmid, int? pn, int? ps, String? orderType}) async {
|
||||||
|
var res = await Request().get(Api.fans, data: {
|
||||||
|
'vmid': vmid,
|
||||||
|
'pn': pn,
|
||||||
|
'ps': ps,
|
||||||
|
'order': 'desc',
|
||||||
|
'order_type': orderType,
|
||||||
|
});
|
||||||
|
if (res.data['code'] == 0) {
|
||||||
|
return {'status': true, 'data': FansDataModel.fromJson(res.data['data'])};
|
||||||
|
} else {
|
||||||
|
return {
|
||||||
|
'status': false,
|
||||||
|
'data': [],
|
||||||
|
'msg': res.data['message'],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
52
lib/models/fans/result.dart
Normal file
52
lib/models/fans/result.dart
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
class FansDataModel {
|
||||||
|
FansDataModel({
|
||||||
|
this.total,
|
||||||
|
this.list,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? total;
|
||||||
|
List<FansItemModel>? list;
|
||||||
|
|
||||||
|
FansDataModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
total = json['total'];
|
||||||
|
list = json['list']
|
||||||
|
.map<FansItemModel>((e) => FansItemModel.fromJson(e))
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FansItemModel {
|
||||||
|
FansItemModel({
|
||||||
|
this.mid,
|
||||||
|
this.attribute,
|
||||||
|
this.mtime,
|
||||||
|
this.tag,
|
||||||
|
this.special,
|
||||||
|
this.uname,
|
||||||
|
this.face,
|
||||||
|
this.sign,
|
||||||
|
this.officialVerify,
|
||||||
|
});
|
||||||
|
|
||||||
|
int? mid;
|
||||||
|
int? attribute;
|
||||||
|
int? mtime;
|
||||||
|
List? tag;
|
||||||
|
int? special;
|
||||||
|
String? uname;
|
||||||
|
String? face;
|
||||||
|
String? sign;
|
||||||
|
Map? officialVerify;
|
||||||
|
|
||||||
|
FansItemModel.fromJson(Map<String, dynamic> json) {
|
||||||
|
mid = json['mid'];
|
||||||
|
attribute = json['attribute'];
|
||||||
|
mtime = json['mtime'];
|
||||||
|
tag = json['tag'];
|
||||||
|
special = json['special'];
|
||||||
|
uname = json['uname'];
|
||||||
|
face = json['face'];
|
||||||
|
sign = json['sign'] == '' ? '还没有签名' : json['sign'];
|
||||||
|
officialVerify = json['official_verify'];
|
||||||
|
}
|
||||||
|
}
|
||||||
36
lib/pages/fan/controller.dart
Normal file
36
lib/pages/fan/controller.dart
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:hive/hive.dart';
|
||||||
|
import 'package:pilipala/http/fan.dart';
|
||||||
|
import 'package:pilipala/models/fans/result.dart';
|
||||||
|
import 'package:pilipala/utils/storage.dart';
|
||||||
|
|
||||||
|
class FansController extends GetxController {
|
||||||
|
Box user = GStrorage.user;
|
||||||
|
int pn = 1;
|
||||||
|
int total = 0;
|
||||||
|
RxList<FansItemModel> fansList = [FansItemModel()].obs;
|
||||||
|
|
||||||
|
Future queryFans(type) async {
|
||||||
|
if (type == 'init') {
|
||||||
|
pn = 1;
|
||||||
|
}
|
||||||
|
var res = await FanHttp.fans(
|
||||||
|
vmid: user.get(UserBoxKey.userMid),
|
||||||
|
pn: pn,
|
||||||
|
ps: 20,
|
||||||
|
orderType: 'attention',
|
||||||
|
);
|
||||||
|
if (res['status']) {
|
||||||
|
if (type == 'init') {
|
||||||
|
fansList.value = res['data'].list;
|
||||||
|
total = res['data'].total;
|
||||||
|
} else if (type == 'onRefresh') {
|
||||||
|
fansList.insertAll(0, res['data'].list);
|
||||||
|
} else if (type == 'onLoad') {
|
||||||
|
fansList.addAll(res['data'].list);
|
||||||
|
}
|
||||||
|
pn += 1;
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
}
|
||||||
4
lib/pages/fan/index.dart
Normal file
4
lib/pages/fan/index.dart
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
library fan;
|
||||||
|
|
||||||
|
export './controller.dart';
|
||||||
|
export './view.dart';
|
||||||
84
lib/pages/fan/view.dart
Normal file
84
lib/pages/fan/view.dart
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:get/get.dart';
|
||||||
|
import 'package:pilipala/common/widgets/http_error.dart';
|
||||||
|
import 'package:pilipala/models/fans/result.dart';
|
||||||
|
|
||||||
|
import 'controller.dart';
|
||||||
|
import 'widgets/fan_item.dart';
|
||||||
|
|
||||||
|
class FansPage extends StatefulWidget {
|
||||||
|
const FansPage({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<FansPage> createState() => _FansPageState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _FansPageState extends State<FansPage> {
|
||||||
|
final FansController _fansController = Get.put(FansController());
|
||||||
|
final ScrollController scrollController = ScrollController();
|
||||||
|
Future? _futureBuilderFuture;
|
||||||
|
bool _isLoadingMore = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_futureBuilderFuture = _fansController.queryFans('init');
|
||||||
|
scrollController.addListener(
|
||||||
|
() async {
|
||||||
|
if (scrollController.position.pixels >=
|
||||||
|
scrollController.position.maxScrollExtent - 200) {
|
||||||
|
if (!_isLoadingMore) {
|
||||||
|
_isLoadingMore = true;
|
||||||
|
await _fansController.queryFans('onLoad');
|
||||||
|
_isLoadingMore = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Scaffold(
|
||||||
|
appBar: AppBar(
|
||||||
|
elevation: 0,
|
||||||
|
scrolledUnderElevation: 0,
|
||||||
|
centerTitle: false,
|
||||||
|
title: const Text('我的粉丝'),
|
||||||
|
),
|
||||||
|
body: RefreshIndicator(
|
||||||
|
onRefresh: () async => await _fansController.queryFans('init'),
|
||||||
|
child: FutureBuilder(
|
||||||
|
future: _futureBuilderFuture,
|
||||||
|
builder: (context, snapshot) {
|
||||||
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
|
var data = snapshot.data;
|
||||||
|
if (data['status']) {
|
||||||
|
List<FansItemModel> list = _fansController.fansList;
|
||||||
|
return Obx(
|
||||||
|
() => list.length == 1
|
||||||
|
? SizedBox()
|
||||||
|
: ListView.builder(
|
||||||
|
controller: scrollController,
|
||||||
|
itemCount: list.length,
|
||||||
|
itemBuilder: (BuildContext context, int index) {
|
||||||
|
return fanItem(item: list[index]);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return HttpError(
|
||||||
|
errMsg: data['msg'],
|
||||||
|
fn: () => _fansController.queryFans('init'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 骨架屏
|
||||||
|
return SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
22
lib/pages/fan/widgets/fan_item.dart
Normal file
22
lib/pages/fan/widgets/fan_item.dart
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:pilipala/common/widgets/network_img_layer.dart';
|
||||||
|
|
||||||
|
Widget fanItem({item}) {
|
||||||
|
return ListTile(
|
||||||
|
onTap: () {},
|
||||||
|
leading: NetworkImgLayer(
|
||||||
|
width: 38,
|
||||||
|
height: 38,
|
||||||
|
type: 'avatar',
|
||||||
|
src: item.face,
|
||||||
|
),
|
||||||
|
title: Text(item.uname),
|
||||||
|
subtitle: Text(
|
||||||
|
item.sign,
|
||||||
|
maxLines: 1,
|
||||||
|
overflow: TextOverflow.ellipsis,
|
||||||
|
),
|
||||||
|
dense: true,
|
||||||
|
trailing: const SizedBox(width: 6),
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -44,7 +44,7 @@ class _FollowPageState extends State<FollowPage> {
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
scrolledUnderElevation: 0,
|
scrolledUnderElevation: 0,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
title: const Text('关注的用户'),
|
title: const Text('我的关注'),
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: RefreshIndicator(
|
||||||
onRefresh: () async =>
|
onRefresh: () async =>
|
||||||
|
|||||||
@ -55,7 +55,8 @@ class MinePage extends StatelessWidget {
|
|||||||
builder: (context, snapshot) {
|
builder: (context, snapshot) {
|
||||||
if (snapshot.connectionState == ConnectionState.done) {
|
if (snapshot.connectionState == ConnectionState.done) {
|
||||||
if (snapshot.data['status']) {
|
if (snapshot.data['status']) {
|
||||||
return Obx(() => userInfoBuild(mineController, context));
|
return Obx(
|
||||||
|
() => userInfoBuild(mineController, context));
|
||||||
} else {
|
} else {
|
||||||
return userInfoBuild(mineController, context);
|
return userInfoBuild(mineController, context);
|
||||||
}
|
}
|
||||||
@ -250,7 +251,7 @@ class MinePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {},
|
onTap: () => Get.toNamed('/follow'),
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
@ -280,7 +281,7 @@ class MinePage extends StatelessWidget {
|
|||||||
),
|
),
|
||||||
),
|
),
|
||||||
InkWell(
|
InkWell(
|
||||||
onTap: () {},
|
onTap: () => Get.toNamed('/fan'),
|
||||||
borderRadius: StyleString.mdRadius,
|
borderRadius: StyleString.mdRadius,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
import 'package:pilipala/pages/dynamics/deatil/index.dart';
|
||||||
import 'package:pilipala/pages/dynamics/index.dart';
|
import 'package:pilipala/pages/dynamics/index.dart';
|
||||||
|
import 'package:pilipala/pages/fan/index.dart';
|
||||||
import 'package:pilipala/pages/fav/index.dart';
|
import 'package:pilipala/pages/fav/index.dart';
|
||||||
import 'package:pilipala/pages/favDetail/index.dart';
|
import 'package:pilipala/pages/favDetail/index.dart';
|
||||||
import 'package:pilipala/pages/follow/index.dart';
|
import 'package:pilipala/pages/follow/index.dart';
|
||||||
@ -56,5 +57,7 @@ class Routes {
|
|||||||
GetPage(name: '/dynamicDetail', page: () => const DynamicDetailPage()),
|
GetPage(name: '/dynamicDetail', page: () => const DynamicDetailPage()),
|
||||||
// 关注
|
// 关注
|
||||||
GetPage(name: '/follow', page: () => const FollowPage()),
|
GetPage(name: '/follow', page: () => const FollowPage()),
|
||||||
|
// 粉丝
|
||||||
|
GetPage(name: '/fan', page: () => const FansPage()),
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user