Files
PiliPlus/lib/pages/member_opus/view.dart
My-Responsitories 9d94c72e95 tweaks (#2426)
* opt: danmaku weight

* opt: cache clean

* opt: level img

* opt: play icon

* opt: svg big-vip

* opt: webview ua

* opt: simple dialog

* feat: export vtt

* tweak

* opt: mapIndexed

* feat: more subtitle

* refa: settings page

* feat: codec list options

* drawPath

Signed-off-by: dom <githubaccount56556@proton.me>

* custom dialog option

Signed-off-by: dom <githubaccount56556@proton.me>

* update

Signed-off-by: dom <githubaccount56556@proton.me>

* Revert "drawPath"

This reverts commit e8a4b19f0f.

* opt: _initStreamIndex

* fix: avoid gap

* fix: scale [skip ci]

* fix: hide repost menu not login

* tweaks

Signed-off-by: dom <githubaccount56556@proton.me>

---------

Co-authored-by: dom <githubaccount56556@proton.me>
2026-06-26 10:51:41 +08:00

184 lines
6.1 KiB
Dart

import 'package:PiliPlus/common/skeleton/space_opus.dart';
import 'package:PiliPlus/common/style.dart';
import 'package:PiliPlus/common/widgets/flutter/refresh_indicator.dart';
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
import 'package:PiliPlus/http/loading_state.dart';
import 'package:PiliPlus/models_new/space/space_opus/item.dart';
import 'package:PiliPlus/pages/common/fab_mixin.dart';
import 'package:PiliPlus/pages/member_opus/controller.dart';
import 'package:PiliPlus/pages/member_opus/widgets/space_opus_item.dart';
import 'package:PiliPlus/utils/grid.dart';
import 'package:PiliPlus/utils/waterfall.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:waterfall_flow/waterfall_flow.dart'
hide SliverWaterfallFlowDelegateWithMaxCrossAxisExtent;
class MemberOpus extends StatefulWidget {
const MemberOpus({
super.key,
this.isSingle = false,
required this.heroTag,
required this.mid,
});
final bool isSingle;
final String? heroTag;
final int mid;
@override
State<MemberOpus> createState() => _MemberOpusState();
}
class _MemberOpusState extends State<MemberOpus>
with
AutomaticKeepAliveClientMixin,
SingleTickerProviderStateMixin,
BaseFabMixin,
LazyFabMixin {
late final MemberOpusController _controller;
@override
void initState() {
super.initState();
_controller = Get.put(
MemberOpusController(
mid: widget.mid,
heroTag: widget.heroTag,
),
tag: widget.heroTag,
);
}
@override
Widget build(BuildContext context) {
super.build(context);
final bottom = MediaQuery.viewPaddingOf(context).bottom;
return Stack(
clipBehavior: .none,
children: [
refreshIndicator(
onRefresh: _controller.onRefresh,
child: NotificationListener<UserScrollNotification>(
onNotification: (notification) {
final direction = notification.direction;
if (direction == .forward) {
showFab();
} else if (direction == .reverse) {
hideFab();
}
return false;
},
child: CustomScrollView(
physics: const AlwaysScrollableScrollPhysics(),
slivers: [
SliverPadding(
padding: EdgeInsets.only(
top: widget.isSingle ? 12 : 0,
left: Style.safeSpace,
right: Style.safeSpace,
bottom: bottom + 100,
),
sliver: Obx(() => _buildBody(_controller.loadingState.value)),
),
],
),
),
),
if (_controller.filter?.isNotEmpty == true)
Positioned(
right: kFloatingActionButtonMargin,
bottom: 0,
child: SlideTransition(
position: fabAnimation,
child: Padding(
padding: .only(
bottom: bottom + kFloatingActionButtonMargin,
),
child: FloatingActionButton.extended(
onPressed: () => showDialog(
context: context,
builder: (context) => SimpleDialog(
clipBehavior: Clip.hardEdge,
contentPadding: const EdgeInsets.symmetric(vertical: 12),
children: _controller.filter!
.map(
(e) => ListTile(
dense: true,
onTap: () {
if (e == _controller.type.value) {
return;
}
Get.back();
_controller
..type.value = e
..onReload();
},
tileColor: e == _controller.type.value
? ColorScheme.of(context).onInverseSurface
: null,
title: Text(
e.text ?? e.tabName!,
style: const TextStyle(fontSize: 14),
),
),
)
.toList(),
),
),
icon: const Icon(size: 20, Icons.sort),
label: Obx(
() {
final type = _controller.type.value;
return Text(type.text ?? type.tabName!);
},
),
),
),
),
),
],
);
}
late final gridDelegate = SliverWaterfallFlowDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: Grid.smallCardWidth,
mainAxisSpacing: Style.safeSpace,
crossAxisSpacing: Style.safeSpace,
);
Widget _buildBody(LoadingState<List<SpaceOpusItemModel>?> loadingState) {
return switch (loadingState) {
Loading() => SliverWaterfallFlow(
gridDelegate: gridDelegate,
delegate: SliverChildBuilderDelegate(
(context, index) => const SpaceOpusSkeleton(),
childCount: 10,
),
),
Success(:final response) =>
response != null && response.isNotEmpty
? SliverWaterfallFlow(
gridDelegate: gridDelegate,
delegate: SliverChildBuilderDelegate(
(_, index) {
if (index == response.length - 1) {
_controller.onLoadMore();
}
return SpaceOpusItem(item: response[index]);
},
childCount: response.length,
),
)
: HttpError(onReload: _controller.onReload),
Error(:final errMsg) => HttpError(
errMsg: errMsg,
onReload: _controller.onReload,
),
};
}
@override
bool get wantKeepAlive => true;
}