mirror of
https://github.com/HChaZZY/PiliPlus.git
synced 2025-12-06 09:13:48 +08:00
mod: 侧边栏、动态重构,排行改为首页分区,平板、折叠屏、竖屏视频新适配,播放页可隐藏黑边、截图、点踩,弹幕粗细调整,默认关闭后台播放,弹窗接受返回
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
// ignore_for_file: avoid_print
|
||||
|
||||
import 'package:PiliPalaX/pages/dynamics/tab/index.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -17,39 +18,17 @@ import 'package:PiliPalaX/utils/id_utils.dart';
|
||||
import 'package:PiliPalaX/utils/storage.dart';
|
||||
import 'package:PiliPalaX/utils/utils.dart';
|
||||
|
||||
class DynamicsController extends GetxController {
|
||||
int page = 1;
|
||||
class DynamicsController extends GetxController
|
||||
with GetTickerProviderStateMixin {
|
||||
String? offset = '';
|
||||
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs;
|
||||
Rx<DynamicsType> dynamicsType = DynamicsType.values[0].obs;
|
||||
RxString dynamicsTypeLabel = '全部'.obs;
|
||||
final ScrollController scrollController = ScrollController();
|
||||
Rx<FollowUpModel> upData = FollowUpModel().obs;
|
||||
// 默认获取全部动态
|
||||
RxInt mid = (-1).obs;
|
||||
Rx<UpItem> upInfo = UpItem().obs;
|
||||
List filterTypeList = [
|
||||
{
|
||||
'label': DynamicsType.all.labels,
|
||||
'value': DynamicsType.all,
|
||||
'enabled': true
|
||||
},
|
||||
{
|
||||
'label': DynamicsType.video.labels,
|
||||
'value': DynamicsType.video,
|
||||
'enabled': true
|
||||
},
|
||||
{
|
||||
'label': DynamicsType.pgc.labels,
|
||||
'value': DynamicsType.pgc,
|
||||
'enabled': true
|
||||
},
|
||||
{
|
||||
'label': DynamicsType.article.labels,
|
||||
'value': DynamicsType.article,
|
||||
'enabled': true
|
||||
},
|
||||
];
|
||||
late TabController tabController;
|
||||
RxList<int> tempBannedList = <int>[].obs;
|
||||
late List<Widget> tabsPageList;
|
||||
bool flag = false;
|
||||
RxInt initialValue = 0.obs;
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
@@ -63,53 +42,23 @@ class DynamicsController extends GetxController {
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
userLogin.value = userInfo != null;
|
||||
super.onInit();
|
||||
initialValue.value =
|
||||
setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0);
|
||||
dynamicsType = DynamicsType.values[initialValue.value].obs;
|
||||
|
||||
tabController = TabController(
|
||||
length: tabsConfig.length,
|
||||
vsync: this,
|
||||
initialIndex:
|
||||
setting.get(SettingBoxKey.defaultDynamicType, defaultValue: 0));
|
||||
tabsPageList = tabsConfig.map((e) {
|
||||
return e['page'] as Widget;
|
||||
}).toList();
|
||||
}
|
||||
|
||||
Future queryFollowDynamic({type = 'init'}) async {
|
||||
if (!userLogin.value) {
|
||||
return {'status': false, 'msg': '账号未登录'};
|
||||
}
|
||||
if (type == 'init') {
|
||||
dynamicsList.clear();
|
||||
}
|
||||
// 下拉刷新数据渲染时会触发onLoad
|
||||
if (type == 'onLoad' && page == 1) {
|
||||
return;
|
||||
}
|
||||
isLoadingDynamic.value = true;
|
||||
var res = await DynamicsHttp.followDynamic(
|
||||
page: type == 'init' ? 1 : page,
|
||||
type: dynamicsType.value.values,
|
||||
offset: offset,
|
||||
mid: mid.value,
|
||||
);
|
||||
isLoadingDynamic.value = false;
|
||||
if (res['status']) {
|
||||
if (type == 'onLoad' && res['data'].items.isEmpty) {
|
||||
SmartDialog.showToast('没有更多了');
|
||||
return;
|
||||
}
|
||||
if (type == 'init') {
|
||||
dynamicsList.value = res['data'].items;
|
||||
} else {
|
||||
dynamicsList.addAll(res['data'].items);
|
||||
}
|
||||
offset = res['data'].offset;
|
||||
page++;
|
||||
}
|
||||
return res;
|
||||
void refreshNotifier() {
|
||||
queryFollowUp();
|
||||
}
|
||||
|
||||
onSelectType(value) async {
|
||||
dynamicsType.value = filterTypeList[value]['value'];
|
||||
dynamicsList.value = [DynamicItemModel()];
|
||||
page = 1;
|
||||
initialValue.value = value;
|
||||
await queryFollowDynamic();
|
||||
scrollController.jumpTo(0);
|
||||
}
|
||||
|
||||
pushDetail(item, floor, {action = 'all'}) async {
|
||||
@@ -276,21 +225,27 @@ class DynamicsController extends GetxController {
|
||||
}
|
||||
|
||||
onSelectUp(mid) async {
|
||||
dynamicsType.value = DynamicsType.values[0];
|
||||
dynamicsList.value = [DynamicItemModel()];
|
||||
page = 1;
|
||||
queryFollowDynamic();
|
||||
if (mid == this.mid.value) {
|
||||
this.mid.refresh();
|
||||
return;
|
||||
}
|
||||
this.mid.value = mid;
|
||||
tabController.index = (mid == -1 ? 0 : 4);
|
||||
}
|
||||
|
||||
onRefresh() async {
|
||||
page = 1;
|
||||
print('onRefresh');
|
||||
await queryFollowUp();
|
||||
await queryFollowDynamic();
|
||||
print(tabsConfig[tabController.index]['ctr']);
|
||||
await Future.wait(<Future>[
|
||||
queryFollowUp(),
|
||||
tabsConfig[tabController.index]['ctr'].onRefresh()
|
||||
]);
|
||||
}
|
||||
|
||||
// 返回顶部并刷新
|
||||
void animateToTop() async {
|
||||
tabsConfig[tabController.index]['ctr'].animateToTop();
|
||||
if (!scrollController.hasClients) return;
|
||||
if (scrollController.offset >=
|
||||
MediaQuery.of(Get.context!).size.height * 5) {
|
||||
scrollController.jumpTo(0);
|
||||
@@ -299,14 +254,4 @@ class DynamicsController extends GetxController {
|
||||
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||
}
|
||||
}
|
||||
|
||||
// 重置搜索
|
||||
void resetSearch() {
|
||||
mid.value = -1;
|
||||
dynamicsType.value = DynamicsType.values[0];
|
||||
initialValue.value = 0;
|
||||
SmartDialog.showToast('还原默认加载');
|
||||
dynamicsList.value = [DynamicItemModel()];
|
||||
queryFollowDynamic();
|
||||
}
|
||||
}
|
||||
|
||||
76
lib/pages/dynamics/tab/controller.dart
Normal file
76
lib/pages/dynamics/tab/controller.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
// import 'package:hive/hive.dart';
|
||||
|
||||
import '../../../http/dynamics.dart';
|
||||
import '../../../models/dynamics/result.dart';
|
||||
// import '../../../utils/storage.dart';
|
||||
|
||||
class DynamicsTabController extends GetxController {
|
||||
String offset = '';
|
||||
ScrollController scrollController = ScrollController();
|
||||
RxList<DynamicItemModel> dynamicsList = <DynamicItemModel>[].obs;
|
||||
RxBool isLoadingMore = false.obs;
|
||||
String dynamicsType = 'all';
|
||||
// Box userInfoCache = GStrorage.userInfo;
|
||||
// bool userLogin = false;
|
||||
int mid = -1;
|
||||
|
||||
Future queryFollowDynamic(String type, String dynamicsType, int? mid) async {
|
||||
this.dynamicsType = dynamicsType;
|
||||
if (mid != null) this.mid = mid;
|
||||
if (type != 'onLoad') {
|
||||
dynamicsList.clear();
|
||||
offset = '';
|
||||
}
|
||||
// // 下拉刷新数据渲染时会触发onLoad
|
||||
// if (type == 'onLoad' && page == 1) {
|
||||
// return;
|
||||
// }
|
||||
isLoadingMore.value = true;
|
||||
var res = await DynamicsHttp.followDynamic(
|
||||
type: dynamicsType == "up" ? "all" : dynamicsType,
|
||||
offset: offset,
|
||||
mid: dynamicsType == "up" ? mid : -1,
|
||||
);
|
||||
isLoadingMore.value = false;
|
||||
if (res['status']) {
|
||||
if (type == 'onLoad' && res['data'].items.isEmpty) {
|
||||
SmartDialog.showToast('没有更多了');
|
||||
return;
|
||||
}
|
||||
if (type == 'onLoad') {
|
||||
dynamicsList.addAll(res['data'].items);
|
||||
} else {
|
||||
dynamicsList.value = res['data'].items;
|
||||
}
|
||||
// print('dynamicsList: $dynamicsList');
|
||||
dynamicsList.refresh();
|
||||
offset = res['data'].offset;
|
||||
// print("page: $page[dynamicsType]!");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// 下拉刷新
|
||||
Future onRefresh() async {
|
||||
await queryFollowDynamic('onRefresh', dynamicsType, mid);
|
||||
}
|
||||
|
||||
// 上拉加载
|
||||
Future onLoad() async {
|
||||
await queryFollowDynamic('onLoad', dynamicsType, mid);
|
||||
}
|
||||
|
||||
// 返回顶部并刷新
|
||||
void animateToTop() async {
|
||||
if (scrollController.offset >=
|
||||
MediaQuery.of(Get.context!).size.height * 5) {
|
||||
scrollController.jumpTo(0);
|
||||
} else {
|
||||
await scrollController.animateTo(0,
|
||||
duration: const Duration(milliseconds: 500), curve: Curves.easeInOut);
|
||||
}
|
||||
}
|
||||
}
|
||||
4
lib/pages/dynamics/tab/index.dart
Normal file
4
lib/pages/dynamics/tab/index.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
library dynamics.tab;
|
||||
|
||||
export './controller.dart';
|
||||
export './view.dart';
|
||||
234
lib/pages/dynamics/tab/view.dart
Normal file
234
lib/pages/dynamics/tab/view.dart
Normal file
@@ -0,0 +1,234 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:PiliPalaX/utils/storage.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPalaX/common/constants.dart';
|
||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||
import 'package:PiliPalaX/pages/home/index.dart';
|
||||
import 'package:PiliPalaX/pages/main/index.dart';
|
||||
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||
import 'package:waterfall_flow/waterfall_flow.dart';
|
||||
|
||||
import '../../../common/skeleton/dynamic_card.dart';
|
||||
import '../../../models/dynamics/result.dart';
|
||||
import '../../../utils/grid.dart';
|
||||
|
||||
import '../index.dart';
|
||||
import '../widgets/dynamic_panel.dart';
|
||||
import 'controller.dart';
|
||||
|
||||
class DynamicsTabPage extends StatefulWidget {
|
||||
const DynamicsTabPage({Key? key, required this.dynamicsType})
|
||||
: super(key: key);
|
||||
|
||||
final String dynamicsType;
|
||||
|
||||
@override
|
||||
State<DynamicsTabPage> createState() => _DynamicsTabPageState();
|
||||
}
|
||||
|
||||
class _DynamicsTabPageState extends State<DynamicsTabPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
late DynamicsTabController _dynamicsTabController;
|
||||
late Future _futureBuilderFuture;
|
||||
late ScrollController scrollController;
|
||||
late bool dynamicsWaterfallFlow;
|
||||
late final DynamicsController dynamicsController;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_dynamicsTabController =
|
||||
Get.put(DynamicsTabController(), tag: widget.dynamicsType);
|
||||
dynamicsController = Get.find<DynamicsController>();
|
||||
|
||||
_futureBuilderFuture = _dynamicsTabController.queryFollowDynamic(
|
||||
'init', widget.dynamicsType, dynamicsController.mid.value);
|
||||
scrollController = _dynamicsTabController.scrollController
|
||||
..addListener(() {
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 200) {
|
||||
if (!_dynamicsTabController.isLoadingMore.value) {
|
||||
EasyThrottle.throttle('_dynamicsTabController_onLoad',
|
||||
const Duration(milliseconds: 500), () {
|
||||
_dynamicsTabController.isLoadingMore.value = true;
|
||||
_dynamicsTabController.onLoad();
|
||||
_dynamicsTabController.isLoadingMore.value = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
dynamicsController.mid.listen((mid) {
|
||||
print('midListen: $mid');
|
||||
scrollController.jumpTo(0);
|
||||
_futureBuilderFuture = _dynamicsTabController.queryFollowDynamic(
|
||||
'init', widget.dynamicsType, mid);
|
||||
});
|
||||
dynamicsWaterfallFlow = GStrorage.setting
|
||||
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.removeListener(() {});
|
||||
dynamicsController.mid.close();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
// print(widget.dynamicsType + widget.mid.value.toString());
|
||||
return RefreshIndicator(
|
||||
// key:
|
||||
// ValueKey<String>(widget.dynamicsType + widget.mid.value.toString()),
|
||||
onRefresh: () async {
|
||||
dynamicsWaterfallFlow = GStrorage.setting
|
||||
.get(SettingBoxKey.dynamicsWaterfallFlow, defaultValue: true);
|
||||
await Future.wait(<Future>[
|
||||
_dynamicsTabController.onRefresh(),
|
||||
dynamicsController.queryFollowUp()
|
||||
]);
|
||||
},
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _dynamicsTabController.scrollController,
|
||||
slivers: [
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
// print(snapshot);
|
||||
// print(widget.dynamicsType + "${widget.mid?.value}");
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const NoData();
|
||||
}
|
||||
Map data = snapshot.data;
|
||||
// print('data: $data');
|
||||
if (data['status']) {
|
||||
List<DynamicItemModel> list =
|
||||
_dynamicsTabController.dynamicsList;
|
||||
// print('list: $list');
|
||||
return Obx(
|
||||
() {
|
||||
if (list.isEmpty) {
|
||||
if (_dynamicsTabController.isLoadingMore.value) {
|
||||
return skeleton();
|
||||
}
|
||||
return const NoData();
|
||||
}
|
||||
if (!dynamicsWaterfallFlow) {
|
||||
return SliverCrossAxisGroup(
|
||||
slivers: [
|
||||
const SliverFillRemaining(),
|
||||
SliverConstrainedCrossAxis(
|
||||
maxExtent: Grid.maxRowWidth * 2,
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if ((dynamicsController
|
||||
.tabController.index ==
|
||||
4 &&
|
||||
dynamicsController.mid.value !=
|
||||
-1) ||
|
||||
!dynamicsController.tempBannedList
|
||||
.contains(list[index]
|
||||
.modules
|
||||
?.moduleAuthor
|
||||
?.mid)) {
|
||||
return DynamicPanel(
|
||||
item: list[index]);
|
||||
}
|
||||
return const SizedBox();
|
||||
},
|
||||
childCount: list.length,
|
||||
),
|
||||
)),
|
||||
const SliverFillRemaining(),
|
||||
],
|
||||
);
|
||||
}
|
||||
return SliverWaterfallFlow.extent(
|
||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||
//cacheExtent: 0.0,
|
||||
crossAxisSpacing: StyleString.cardSpace / 2,
|
||||
mainAxisSpacing: StyleString.cardSpace / 2,
|
||||
|
||||
lastChildLayoutTypeBuilder: (index) =>
|
||||
index == list.length
|
||||
? LastChildLayoutType.foot
|
||||
: LastChildLayoutType.none,
|
||||
children: [
|
||||
if (dynamicsController.tabController.index == 4 &&
|
||||
dynamicsController.mid.value != -1) ...[
|
||||
for (var i in list) DynamicPanel(item: i),
|
||||
] else ...[
|
||||
for (var i in list)
|
||||
if (!dynamicsController.tempBannedList
|
||||
.contains(i.modules?.moduleAuthor?.mid))
|
||||
DynamicPanel(item: i),
|
||||
]
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () {
|
||||
// setState(() {
|
||||
_futureBuilderFuture =
|
||||
_dynamicsTabController.queryFollowDynamic(
|
||||
'init',
|
||||
widget.dynamicsType,
|
||||
dynamicsController.mid.value);
|
||||
// });
|
||||
dynamicsController.onRefresh();
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return skeleton();
|
||||
}
|
||||
},
|
||||
)
|
||||
],
|
||||
));
|
||||
}
|
||||
|
||||
Widget skeleton() {
|
||||
if (!dynamicsWaterfallFlow) {
|
||||
return SliverCrossAxisGroup(slivers: [
|
||||
const SliverFillRemaining(),
|
||||
SliverConstrainedCrossAxis(
|
||||
maxExtent: Grid.maxRowWidth * 2,
|
||||
sliver: SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return const DynamicCardSkeleton();
|
||||
}, childCount: 10)),
|
||||
),
|
||||
const SliverFillRemaining()
|
||||
]);
|
||||
}
|
||||
return SliverGrid(
|
||||
gridDelegate: SliverGridDelegateWithExtentAndRatio(
|
||||
crossAxisSpacing: StyleString.cardSpace / 2,
|
||||
mainAxisSpacing: StyleString.cardSpace / 2,
|
||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||
childAspectRatio: StyleString.aspectRatio,
|
||||
mainAxisExtent: 50),
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return const DynamicCardSkeleton();
|
||||
}, childCount: 10),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,24 +1,15 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:custom_sliding_segmented_control/custom_sliding_segmented_control.dart';
|
||||
import 'package:easy_debounce/easy_throttle.dart';
|
||||
import 'package:PiliPalaX/models/common/dynamics_type.dart';
|
||||
import 'package:PiliPalaX/models/common/up_panel_position.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/rendering.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:hive/hive.dart';
|
||||
import 'package:waterfall_flow/waterfall_flow.dart';
|
||||
import 'package:PiliPalaX/common/skeleton/dynamic_card.dart';
|
||||
import 'package:PiliPalaX/common/widgets/http_error.dart';
|
||||
import 'package:PiliPalaX/common/widgets/no_data.dart';
|
||||
import 'package:PiliPalaX/models/dynamics/result.dart';
|
||||
import 'package:PiliPalaX/pages/main/index.dart';
|
||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||
import 'package:PiliPalaX/utils/storage.dart';
|
||||
|
||||
import '../../common/constants.dart';
|
||||
import '../../utils/grid.dart';
|
||||
import 'controller.dart';
|
||||
import 'widgets/dynamic_panel.dart';
|
||||
import 'widgets/up_panel.dart';
|
||||
|
||||
class DynamicsPage extends StatefulWidget {
|
||||
@@ -29,12 +20,12 @@ class DynamicsPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _DynamicsPageState extends State<DynamicsPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
with AutomaticKeepAliveClientMixin, SingleTickerProviderStateMixin {
|
||||
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
||||
late Future _futureBuilderFuture;
|
||||
late Future _futureBuilderFutureUp;
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
late ScrollController scrollController;
|
||||
late UpPanelPosition upPanelPosition;
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
@@ -42,269 +33,125 @@ class _DynamicsPageState extends State<DynamicsPage>
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_futureBuilderFuture = _dynamicsController.queryFollowDynamic();
|
||||
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
|
||||
scrollController = _dynamicsController.scrollController;
|
||||
StreamController<bool> mainStream =
|
||||
Get.find<MainController>().bottomBarStream;
|
||||
scrollController.addListener(
|
||||
() async {
|
||||
if (scrollController.position.pixels >=
|
||||
scrollController.position.maxScrollExtent - 200) {
|
||||
EasyThrottle.throttle(
|
||||
'queryFollowDynamic', const Duration(seconds: 1), () {
|
||||
_dynamicsController.queryFollowDynamic(type: 'onLoad');
|
||||
});
|
||||
}
|
||||
|
||||
final ScrollDirection direction =
|
||||
scrollController.position.userScrollDirection;
|
||||
if (direction == ScrollDirection.forward) {
|
||||
mainStream.add(true);
|
||||
} else if (direction == ScrollDirection.reverse) {
|
||||
mainStream.add(false);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// _dynamicsController.tabController =
|
||||
// TabController(vsync: this, length: DynamicsType.values.length);
|
||||
// ..addListener(() {
|
||||
// if (!_dynamicsController.tabController.indexIsChanging) {
|
||||
// // if (!mounted) return;
|
||||
// // print('indexChanging: ${_dynamicsController.tabController.index}');
|
||||
// _dynamicsController
|
||||
// .onSelectType(_dynamicsController.tabController.index);
|
||||
// }
|
||||
// });
|
||||
_dynamicsController.userLogin.listen((status) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_futureBuilderFuture = _dynamicsController.queryFollowDynamic();
|
||||
_futureBuilderFutureUp = _dynamicsController.queryFollowUp();
|
||||
});
|
||||
}
|
||||
});
|
||||
upPanelPosition = UpPanelPosition.values[setting.get(
|
||||
SettingBoxKey.upPanelPosition,
|
||||
defaultValue: UpPanelPosition.leftFixed.code)];
|
||||
print('upPanelPosition: $upPanelPosition');
|
||||
scrollController = _dynamicsController.scrollController;
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
scrollController.removeListener(() {});
|
||||
_dynamicsController.tabController.removeListener(() {});
|
||||
_dynamicsController.tabController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Widget upPanelPart() {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
child: Container(
|
||||
//抽屉模式增加底色
|
||||
color: upPanelPosition.code > 1? Theme.of(context).colorScheme.surface: Colors.transparent,
|
||||
width: 56,
|
||||
child: FutureBuilder(
|
||||
future: _futureBuilderFutureUp,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SizedBox();
|
||||
}
|
||||
Map data = snapshot.data;
|
||||
if (data['status']) {
|
||||
return Obx(() => UpPanel(
|
||||
_dynamicsController.upData.value,
|
||||
scrollController));
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
} else {
|
||||
return const SizedBox(
|
||||
width: 56,
|
||||
child: UpPanelSkeleton(),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
));
|
||||
}
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
print('upPanelPosition1: $upPanelPosition');
|
||||
super.build(context);
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
elevation: 0,
|
||||
scrolledUnderElevation: 0,
|
||||
titleSpacing: 0,
|
||||
title: SizedBox(
|
||||
height: 34,
|
||||
child: Stack(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Obx(() {
|
||||
if (_dynamicsController.mid.value != -1 &&
|
||||
_dynamicsController.upInfo.value.uname != null) {
|
||||
return SizedBox(
|
||||
height: 36,
|
||||
child: AnimatedSwitcher(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
transitionBuilder:
|
||||
(Widget child, Animation<double> animation) {
|
||||
return ScaleTransition(
|
||||
scale: animation, child: child);
|
||||
},
|
||||
child: Text(
|
||||
'${_dynamicsController.upInfo.value.uname!}的动态',
|
||||
key: ValueKey<String>(
|
||||
_dynamicsController.upInfo.value.uname!),
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelLarge!
|
||||
.fontSize,
|
||||
)),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox();
|
||||
}
|
||||
}),
|
||||
Obx(
|
||||
() => _dynamicsController.userLogin.value
|
||||
? Visibility(
|
||||
visible: _dynamicsController.mid.value == -1,
|
||||
child: Theme(
|
||||
data: ThemeData(
|
||||
splashColor:
|
||||
Colors.transparent, // 点击时的水波纹颜色设置为透明
|
||||
highlightColor:
|
||||
Colors.transparent, // 点击时的背景高亮颜色设置为透明
|
||||
),
|
||||
child: CustomSlidingSegmentedControl<int>(
|
||||
initialValue:
|
||||
_dynamicsController.initialValue.value,
|
||||
children: {
|
||||
0: Text(
|
||||
'全部',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize),
|
||||
),
|
||||
1: Text('投稿',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize)),
|
||||
2: Text('番剧',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize)),
|
||||
3: Text('专栏',
|
||||
style: TextStyle(
|
||||
fontSize: Theme.of(context)
|
||||
.textTheme
|
||||
.labelMedium!
|
||||
.fontSize)),
|
||||
},
|
||||
padding: 13.0,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.surfaceVariant
|
||||
.withOpacity(0.7),
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
thumbDecoration: BoxDecoration(
|
||||
color:
|
||||
Theme.of(context).colorScheme.background,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeInOut,
|
||||
onValueChanged: (v) {
|
||||
feedBack();
|
||||
_dynamicsController.onSelectType(v);
|
||||
},
|
||||
),
|
||||
),
|
||||
)
|
||||
: Text('动态',
|
||||
style: Theme.of(context).textTheme.titleMedium),
|
||||
)
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
body: RefreshIndicator(
|
||||
onRefresh: () => _dynamicsController.onRefresh(),
|
||||
child: CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _dynamicsController.scrollController,
|
||||
slivers: [
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFutureUp,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SliverToBoxAdapter(child: SizedBox());
|
||||
}
|
||||
Map data = snapshot.data;
|
||||
if (data['status']) {
|
||||
return Obx(() => UpPanel(_dynamicsController.upData.value));
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
child: SizedBox(height: 80),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
return const SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 90,
|
||||
child: UpPanelSkeleton(),
|
||||
));
|
||||
backgroundColor: Colors.transparent,
|
||||
appBar: AppBar(
|
||||
toolbarHeight: 50,
|
||||
elevation: 0,
|
||||
backgroundColor: Colors.transparent,
|
||||
title: SizedBox(
|
||||
height: 50,
|
||||
child: TabBar(
|
||||
controller: _dynamicsController.tabController,
|
||||
isScrollable: true,
|
||||
dividerColor: Colors.transparent,
|
||||
dividerHeight: 0,
|
||||
tabAlignment: TabAlignment.center,
|
||||
indicatorColor: Theme.of(context).colorScheme.primary,
|
||||
labelColor: Theme.of(context).colorScheme.primary,
|
||||
unselectedLabelColor: Theme.of(context).colorScheme.onSurface,
|
||||
labelStyle: TextStyle(
|
||||
fontSize: Theme.of(context).textTheme.labelMedium!.fontSize,
|
||||
),
|
||||
tabs: DynamicsType.values
|
||||
.map((e) => Tab(text: e.labels))
|
||||
.toList(),
|
||||
onTap: (index) {
|
||||
print('index: $index');
|
||||
feedBack();
|
||||
tabsConfig[_dynamicsController.tabController.index]['ctr'].animateToTop();
|
||||
// _dynamicsController.tabController
|
||||
// _dynamicsController.tabController.index = index;
|
||||
// _dynamicsController.onSelectType(index);
|
||||
// _
|
||||
}
|
||||
},
|
||||
),
|
||||
FutureBuilder(
|
||||
future: _futureBuilderFuture,
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.done) {
|
||||
if (snapshot.data == null) {
|
||||
return const SliverToBoxAdapter(child: SizedBox());
|
||||
}
|
||||
Map data = snapshot.data;
|
||||
if (data['status']) {
|
||||
List<DynamicItemModel> list =
|
||||
_dynamicsController.dynamicsList;
|
||||
return Obx(
|
||||
() {
|
||||
if (list.isEmpty) {
|
||||
if (_dynamicsController.isLoadingDynamic.value) {
|
||||
return skeleton();
|
||||
} else {
|
||||
return const NoData();
|
||||
}
|
||||
} else {
|
||||
return SliverWaterfallFlow.extent(
|
||||
maxCrossAxisExtent: Grid.maxRowWidth * 2,
|
||||
//cacheExtent: 0.0,
|
||||
crossAxisSpacing: StyleString.safeSpace,
|
||||
mainAxisSpacing: StyleString.cardSpace,
|
||||
|
||||
/// follow max child trailing layout offset and layout with full cross axis extend
|
||||
/// last child as loadmore item/no more item in [GridView] and [WaterfallFlow]
|
||||
/// with full cross axis extend
|
||||
// LastChildLayoutType.fullCrossAxisExtend,
|
||||
|
||||
/// as foot at trailing and layout with full cross axis extend
|
||||
/// show no more item at trailing when children are not full of viewport
|
||||
/// if children is full of viewport, it's the same as fullCrossAxisExtend
|
||||
// LastChildLayoutType.foot,
|
||||
lastChildLayoutTypeBuilder: (index) =>
|
||||
index == list.length
|
||||
? LastChildLayoutType.foot
|
||||
: LastChildLayoutType.none,
|
||||
children: [
|
||||
for (var i in list) DynamicPanel(item: i),
|
||||
]);
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
return HttpError(
|
||||
errMsg: data['msg'],
|
||||
fn: () {
|
||||
setState(() {
|
||||
_futureBuilderFuture =
|
||||
_dynamicsController.queryFollowDynamic();
|
||||
_futureBuilderFutureUp =
|
||||
_dynamicsController.queryFollowUp();
|
||||
});
|
||||
},
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// 骨架屏
|
||||
return skeleton();
|
||||
}
|
||||
},
|
||||
),
|
||||
const SliverToBoxAdapter(child: SizedBox(height: 40))
|
||||
],
|
||||
)),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget skeleton() {
|
||||
return SliverList(
|
||||
delegate: SliverChildBuilderDelegate((context, index) {
|
||||
return const DynamicCardSkeleton();
|
||||
}, childCount: 5),
|
||||
);
|
||||
drawer: upPanelPosition == UpPanelPosition.leftDrawer ?
|
||||
upPanelPart(): null,
|
||||
drawerEnableOpenDragGesture: true,
|
||||
endDrawer: upPanelPosition == UpPanelPosition.rightDrawer ?
|
||||
upPanelPart(): null,
|
||||
endDrawerEnableOpenDragGesture: true,
|
||||
body: Row(children: [
|
||||
if (upPanelPosition == UpPanelPosition.leftFixed)
|
||||
upPanelPart(),
|
||||
Expanded(
|
||||
child: TabBarView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: _dynamicsController.tabController,
|
||||
children: _dynamicsController.tabsPageList,
|
||||
)),
|
||||
if (upPanelPosition == UpPanelPosition.rightFixed)
|
||||
upPanelPart(),
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,15 @@ import 'package:PiliPalaX/common/widgets/network_img_layer.dart';
|
||||
import 'package:PiliPalaX/http/user.dart';
|
||||
import 'package:PiliPalaX/utils/feed_back.dart';
|
||||
import 'package:PiliPalaX/utils/utils.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../../http/constants.dart';
|
||||
import '../controller.dart';
|
||||
|
||||
class AuthorPanel extends StatelessWidget {
|
||||
final dynamic item;
|
||||
const AuthorPanel({super.key, required this.item});
|
||||
final Function? addBannedList;
|
||||
const AuthorPanel({super.key, required this.item, this.addBannedList});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
@@ -77,28 +82,27 @@ class AuthorPanel extends StatelessWidget {
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
if (item.type == 'DYNAMIC_TYPE_AV')
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
tooltip: '更多',
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return MorePanel(item: item);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
||||
SizedBox(
|
||||
width: 32,
|
||||
height: 32,
|
||||
child: IconButton(
|
||||
tooltip: '更多',
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(EdgeInsets.zero),
|
||||
),
|
||||
onPressed: () {
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
useRootNavigator: true,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return MorePanel(item: item);
|
||||
},
|
||||
);
|
||||
},
|
||||
icon: const Icon(Icons.more_vert_outlined, size: 18),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
@@ -132,24 +136,56 @@ class MorePanel extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
),
|
||||
if (item.type == 'DYNAMIC_TYPE_AV')
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
try {
|
||||
String bvid = item.modules.moduleDynamic.major.archive.bvid;
|
||||
var res = await UserHttp.toViewLater(bvid: bvid);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
Get.back();
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('出错了:${err.toString()}');
|
||||
}
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
// dense: true,
|
||||
leading: const Icon(Icons.watch_later_outlined, size: 19),
|
||||
title: Text(
|
||||
'稍后再看',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
),
|
||||
ListTile(
|
||||
onTap: () async {
|
||||
try {
|
||||
String bvid = item.modules.moduleDynamic.major.archive.bvid;
|
||||
var res = await UserHttp.toViewLater(bvid: bvid);
|
||||
SmartDialog.showToast(res['msg']);
|
||||
Get.back();
|
||||
} catch (err) {
|
||||
SmartDialog.showToast('出错了:${err.toString()}');
|
||||
}
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
// dense: true,
|
||||
leading: const Icon(Icons.watch_later_outlined, size: 19),
|
||||
title: Text(
|
||||
'稍后再看',
|
||||
'分享动态',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
leading: const Icon(Icons.share_outlined, size: 19),
|
||||
onTap: () async {
|
||||
var result = await Share.share(
|
||||
'${HttpString.baseUrl}/dynamic/${item.idStr} UP主: ${item.modules.moduleAuthor.name}')
|
||||
.whenComplete(() {});
|
||||
return result;
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
ListTile(
|
||||
title: Text(
|
||||
'临时屏蔽:${item.modules.moduleAuthor.name}',
|
||||
style: Theme.of(context).textTheme.titleSmall,
|
||||
),
|
||||
leading: const Icon(Icons.visibility_off_outlined, size: 19),
|
||||
onTap: () {
|
||||
Get.back();
|
||||
DynamicsController dynamicsController =
|
||||
Get.find<DynamicsController>();
|
||||
dynamicsController.tempBannedList
|
||||
.add(item.modules.moduleAuthor.mid);
|
||||
SmartDialog.showToast(
|
||||
'已临时屏蔽${item.modules.moduleAuthor.name}(${item.modules.moduleAuthor.mid}),重启恢复');
|
||||
},
|
||||
minLeadingWidth: 0,
|
||||
),
|
||||
const Divider(thickness: 0.1, height: 1),
|
||||
ListTile(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:PiliPalaX/models/dynamics/result.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:PiliPalaX/pages/dynamics/index.dart';
|
||||
@@ -9,7 +10,7 @@ import 'forward_panel.dart';
|
||||
class DynamicPanel extends StatelessWidget {
|
||||
final dynamic item;
|
||||
final String? source;
|
||||
DynamicPanel({this.item, this.source, Key? key}) : super(key: key);
|
||||
DynamicPanel({required this.item, this.source, Key? key}) : super(key: key);
|
||||
final DynamicsController _dynamicsController = Get.put(DynamicsController());
|
||||
|
||||
@override
|
||||
@@ -18,19 +19,20 @@ class DynamicPanel extends StatelessWidget {
|
||||
padding: source == 'detail'
|
||||
? const EdgeInsets.only(bottom: 12)
|
||||
: EdgeInsets.zero,
|
||||
decoration: BoxDecoration(
|
||||
border: Border(
|
||||
bottom: BorderSide(
|
||||
width: 8,
|
||||
color: Theme.of(context).dividerColor.withOpacity(0.05),
|
||||
),
|
||||
),
|
||||
),
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border(
|
||||
// bottom: BorderSide(
|
||||
// width: 8,
|
||||
// color: Theme.of(context).dividerColor.withOpacity(0.05),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
child: Material(
|
||||
elevation: 0,
|
||||
clipBehavior: Clip.hardEdge,
|
||||
color: Theme.of(context).cardColor.withOpacity(0.5),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(0),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
),
|
||||
child: InkWell(
|
||||
onTap: () => _dynamicsController.pushDetail(item, 1),
|
||||
@@ -38,7 +40,7 @@ class DynamicPanel extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 8),
|
||||
padding: const EdgeInsets.fromLTRB(12, 12, 12, 6),
|
||||
child: AuthorPanel(item: item),
|
||||
),
|
||||
if (item!.modules!.moduleDynamic!.desc != null ||
|
||||
|
||||
@@ -11,26 +11,27 @@ import 'package:PiliPalaX/utils/utils.dart';
|
||||
|
||||
class UpPanel extends StatefulWidget {
|
||||
final FollowUpModel? upData;
|
||||
const UpPanel(this.upData, {Key? key}) : super(key: key);
|
||||
final ScrollController scrollController;
|
||||
const UpPanel(this.upData, this.scrollController, {Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
State<UpPanel> createState() => _UpPanelState();
|
||||
}
|
||||
|
||||
class _UpPanelState extends State<UpPanel> {
|
||||
final ScrollController scrollController = ScrollController();
|
||||
int currentMid = -1;
|
||||
late double contentWidth = 56;
|
||||
List<UpItem> upList = [];
|
||||
List<LiveUserItem> liveList = [];
|
||||
static const itemPadding = EdgeInsets.symmetric(horizontal: 5, vertical: 0);
|
||||
Box userInfoCache = GStrorage.userInfo;
|
||||
var userInfo;
|
||||
bool _showLiveItems = false;
|
||||
late DynamicsController dynamicsController;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
userInfo = userInfoCache.get('userInfoCache');
|
||||
dynamicsController = Get.find<DynamicsController>();
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -39,95 +40,81 @@ class _UpPanelState extends State<UpPanel> {
|
||||
if (widget.upData!.liveUsers != null) {
|
||||
liveList = widget.upData!.liveUsers!.items!;
|
||||
}
|
||||
return SliverPersistentHeader(
|
||||
floating: true,
|
||||
pinned: false,
|
||||
delegate: _SliverHeaderDelegate(
|
||||
height: 126,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.start,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
padding: const EdgeInsets.only(left: 16, right: 16),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
// return const SizedBox();
|
||||
return CustomScrollView(
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
controller: widget.scrollController,
|
||||
slivers: [
|
||||
SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 45,
|
||||
child: TextButton(
|
||||
style: ButtonStyle(
|
||||
padding: MaterialStateProperty.all(const EdgeInsets.only()),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
const Text('最新关注'),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
feedBack();
|
||||
Get.toNamed('/follow?mid=${userInfo.mid}');
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.only(top: 5, bottom: 5),
|
||||
child: Text(
|
||||
'查看全部',
|
||||
style: TextStyle(
|
||||
color: Theme.of(context).colorScheme.outline),
|
||||
),
|
||||
const Spacer(),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Live(${liveList.length})',
|
||||
style: const TextStyle(
|
||||
fontSize: 13,
|
||||
),
|
||||
semanticsLabel:
|
||||
'${_showLiveItems ? '展开' : '收起'}直播中的${liveList.length}个Up',
|
||||
),
|
||||
Icon(_showLiveItems ? Icons.expand_less : Icons.expand_more,
|
||||
size: 12),
|
||||
const Spacer(),
|
||||
],
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_showLiveItems = !_showLiveItems;
|
||||
});
|
||||
},
|
||||
),
|
||||
Container(
|
||||
height: 90,
|
||||
color: Theme.of(context).colorScheme.background,
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
physics: const BouncingScrollPhysics(),
|
||||
scrollDirection: Axis.horizontal,
|
||||
controller: scrollController,
|
||||
children: [
|
||||
const SizedBox(width: 10),
|
||||
if (liveList.isNotEmpty) ...[
|
||||
for (int i = 0; i < liveList.length; i++) ...[
|
||||
upItemBuild(liveList[i], i)
|
||||
],
|
||||
VerticalDivider(
|
||||
indent: 20,
|
||||
endIndent: 40,
|
||||
width: 26,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.primary
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
],
|
||||
upItemBuild(
|
||||
UpItem(face: '', uname: '全部动态', mid: -1), 0),
|
||||
upItemBuild(
|
||||
UpItem(
|
||||
face: userInfo.face,
|
||||
uname: '我',
|
||||
mid: userInfo.mid,
|
||||
),
|
||||
1),
|
||||
for (int i = 0; i < upList.length; i++) ...[
|
||||
upItemBuild(upList[i], i + 2)
|
||||
],
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 10,
|
||||
),
|
||||
),
|
||||
SliverGrid(
|
||||
gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
|
||||
crossAxisCount: 1,
|
||||
mainAxisExtent: 76,
|
||||
crossAxisSpacing: 0,
|
||||
mainAxisSpacing: 0,
|
||||
),
|
||||
delegate: SliverChildListDelegate(
|
||||
[
|
||||
if (_showLiveItems && liveList.isNotEmpty) ...[
|
||||
for (int i = 0; i < liveList.length; i++) ...[
|
||||
upItemBuild(liveList[i], i)
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
Container(
|
||||
height: 6,
|
||||
color: Theme.of(context)
|
||||
.colorScheme
|
||||
.onInverseSurface
|
||||
.withOpacity(0.5),
|
||||
),
|
||||
],
|
||||
)),
|
||||
);
|
||||
upItemBuild(UpItem(face: '', uname: '全部动态', mid: -1), 0),
|
||||
upItemBuild(
|
||||
UpItem(
|
||||
face: userInfo.face,
|
||||
uname: '我',
|
||||
mid: userInfo.mid,
|
||||
),
|
||||
1),
|
||||
for (int i = 0; i < upList.length; i++) ...[
|
||||
upItemBuild(upList[i], i + 2)
|
||||
],
|
||||
],
|
||||
)),
|
||||
const SliverToBoxAdapter(
|
||||
child: SizedBox(
|
||||
height: 200,
|
||||
),
|
||||
),
|
||||
]);
|
||||
}
|
||||
|
||||
Widget upItemBuild(data, i) {
|
||||
@@ -137,28 +124,27 @@ class _UpPanelState extends State<UpPanel> {
|
||||
feedBack();
|
||||
if (data.type == 'up') {
|
||||
currentMid = data.mid;
|
||||
Get.find<DynamicsController>().mid.value = data.mid;
|
||||
Get.find<DynamicsController>().upInfo.value = data;
|
||||
Get.find<DynamicsController>().onSelectUp(data.mid);
|
||||
int liveLen = liveList.length;
|
||||
int upLen = upList.length;
|
||||
double itemWidth = contentWidth + itemPadding.horizontal;
|
||||
double screenWidth = MediaQuery.sizeOf(context).width;
|
||||
double moveDistance = 0.0;
|
||||
if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
||||
} else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
||||
moveDistance =
|
||||
(i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
||||
} else {
|
||||
moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
||||
}
|
||||
// dynamicsController.mid.value = data.mid;
|
||||
dynamicsController.upInfo.value = data;
|
||||
dynamicsController.onSelectUp(data.mid);
|
||||
// int liveLen = liveList.length;
|
||||
// int upLen = upList.length;
|
||||
// double itemWidth = contentWidth + itemPadding.horizontal;
|
||||
// double screenWidth = MediaQuery.sizeOf(context).width;
|
||||
// double moveDistance = 0.0;
|
||||
// if (itemWidth * (upList.length + liveList.length) <= screenWidth) {
|
||||
// } else if ((upLen - i - 0.5) * itemWidth > screenWidth / 2) {
|
||||
// moveDistance =
|
||||
// (i + liveLen + 0.5) * itemWidth + 46 - screenWidth / 2;
|
||||
// } else {
|
||||
// moveDistance = (upLen + liveLen) * itemWidth + 46 - screenWidth;
|
||||
// }
|
||||
data.hasUpdate = false;
|
||||
scrollController.animateTo(
|
||||
moveDistance,
|
||||
duration: const Duration(milliseconds: 500),
|
||||
curve: Curves.easeInOut,
|
||||
);
|
||||
|
||||
// scrollController.animateTo(
|
||||
// moveDistance,
|
||||
// duration: const Duration(milliseconds: 500),
|
||||
// curve: Curves.easeInOut,
|
||||
// );
|
||||
setState(() {});
|
||||
} else if (data.type == 'live') {
|
||||
LiveItemModel liveItem = LiveItemModel.fromJson({
|
||||
@@ -182,63 +168,59 @@ class _UpPanelState extends State<UpPanel> {
|
||||
Get.toNamed('/member?mid=${data.mid}',
|
||||
arguments: {'face': data.face, 'heroTag': heroTag});
|
||||
},
|
||||
child: Padding(
|
||||
padding: itemPadding,
|
||||
child: AnimatedOpacity(
|
||||
opacity: isCurrent ? 1 : 0.3,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Badge(
|
||||
smallSize: 8,
|
||||
label: data.type == 'live' ? const Text('Live') : null,
|
||||
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
alignment: data.type == 'live'
|
||||
? AlignmentDirectional.topCenter
|
||||
: AlignmentDirectional.topEnd,
|
||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||
isLabelVisible: data.type == 'live' ||
|
||||
(data.type == 'up' && (data.hasUpdate ?? false)),
|
||||
backgroundColor: data.type == 'live'
|
||||
? Theme.of(context).colorScheme.secondaryContainer
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
child: data.face != ''
|
||||
? NetworkImgLayer(
|
||||
width: 50,
|
||||
height: 50,
|
||||
src: data.face,
|
||||
type: 'avatar',
|
||||
)
|
||||
: const CircleAvatar(
|
||||
radius: 25,
|
||||
backgroundImage: AssetImage(
|
||||
'assets/images/noface.jpeg',
|
||||
),
|
||||
child: AnimatedOpacity(
|
||||
opacity: isCurrent ? 1 : 0.6,
|
||||
duration: const Duration(milliseconds: 200),
|
||||
curve: Curves.easeInOut,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Badge(
|
||||
smallSize: 8,
|
||||
label: data.type == 'live' ? const Text('Live') : null,
|
||||
textColor: Theme.of(context).colorScheme.onSecondaryContainer,
|
||||
alignment: data.type == 'live'
|
||||
? AlignmentDirectional.topCenter
|
||||
: AlignmentDirectional.topEnd,
|
||||
padding: const EdgeInsets.only(left: 6, right: 6),
|
||||
isLabelVisible: data.type == 'live' ||
|
||||
(data.type == 'up' && (data.hasUpdate ?? false)),
|
||||
backgroundColor: data.type == 'live'
|
||||
? Theme.of(context)
|
||||
.colorScheme
|
||||
.secondaryContainer
|
||||
.withOpacity(0.7)
|
||||
: Theme.of(context).colorScheme.primary,
|
||||
child: data.face != ''
|
||||
? NetworkImgLayer(
|
||||
width: 38,
|
||||
height: 38,
|
||||
src: data.face,
|
||||
type: 'avatar',
|
||||
)
|
||||
: const CircleAvatar(
|
||||
radius: 19,
|
||||
backgroundImage: AssetImage(
|
||||
'assets/images/logo/logo_android_2.png',
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 4),
|
||||
child: SizedBox(
|
||||
width: contentWidth,
|
||||
child: Text(
|
||||
data.uname,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
softWrap: false,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: currentMid == data.mid
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
fontSize:
|
||||
Theme.of(context).textTheme.labelMedium!.fontSize),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 3),
|
||||
Text(
|
||||
data.uname,
|
||||
overflow: TextOverflow.clip,
|
||||
maxLines: 2,
|
||||
softWrap: true,
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(
|
||||
color: currentMid == data.mid
|
||||
? Theme.of(context).colorScheme.primary
|
||||
: Theme.of(context).colorScheme.outline,
|
||||
height: 1.1,
|
||||
fontSize: 12.5),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -273,35 +255,25 @@ class UpPanelSkeleton extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return ListView.builder(
|
||||
scrollDirection: Axis.horizontal,
|
||||
physics: const NeverScrollableScrollPhysics(),
|
||||
itemCount: 10,
|
||||
itemBuilder: ((context, index) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 0),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
width: 45,
|
||||
height: 12,
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
],
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
width: 50,
|
||||
height: 50,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
borderRadius: BorderRadius.circular(50),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
Container(
|
||||
margin: const EdgeInsets.only(top: 6),
|
||||
width: 45,
|
||||
height: 12,
|
||||
color: Theme.of(context).colorScheme.onInverseSurface,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,8 +105,8 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
||||
right: 0,
|
||||
bottom: 0,
|
||||
child: Container(
|
||||
height: 80,
|
||||
padding: const EdgeInsets.fromLTRB(12, 0, 10, 10),
|
||||
height: 70,
|
||||
padding: const EdgeInsets.fromLTRB(10, 0, 8, 8),
|
||||
clipBehavior: Clip.hardEdge,
|
||||
decoration: BoxDecoration(
|
||||
gradient: const LinearGradient(
|
||||
@@ -139,17 +139,17 @@ Widget videoSeasonWidget(item, context, type, {floor = 1}) {
|
||||
'时长${Utils.durationReadFormat(content.durationText)}',
|
||||
),
|
||||
if (content.durationText != null)
|
||||
const SizedBox(width: 10),
|
||||
const SizedBox(width: 6),
|
||||
Text(content.stat.play + '次围观'),
|
||||
const SizedBox(width: 10),
|
||||
const SizedBox(width: 6),
|
||||
Text(content.stat.danmu + '条弹幕')
|
||||
],
|
||||
),
|
||||
),
|
||||
Image.asset(
|
||||
'assets/images/play.png',
|
||||
width: 60,
|
||||
height: 60,
|
||||
width: 50,
|
||||
height: 50,
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
Reference in New Issue
Block a user