mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-08-05 18:20:10 +08:00
@@ -43,13 +43,6 @@ class _ContactPageState extends State<ContactPage>
|
|||||||
return SimpleScaffold(
|
return SimpleScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: const Text('通讯录'),
|
title: const Text('通讯录'),
|
||||||
bottom: TabBar(
|
|
||||||
controller: _controller,
|
|
||||||
tabs: const [
|
|
||||||
Tab(text: '我的关注'),
|
|
||||||
Tab(text: '我的粉丝'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
onPressed: () async {
|
onPressed: () async {
|
||||||
@@ -70,16 +63,29 @@ class _ContactPageState extends State<ContactPage>
|
|||||||
const SizedBox(width: 16),
|
const SizedBox(width: 16),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: tabBarView(
|
body: Column(
|
||||||
controller: _controller,
|
|
||||||
children: [
|
children: [
|
||||||
FollowChildPage(
|
TabBar(
|
||||||
mid: mid,
|
controller: _controller,
|
||||||
onSelect: widget.isFromSelect ? onSelect : null,
|
tabs: const [
|
||||||
|
Tab(text: '我的关注'),
|
||||||
|
Tab(text: '我的粉丝'),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
FansPage(
|
Expanded(
|
||||||
showName: false,
|
child: tabBarView(
|
||||||
onSelect: widget.isFromSelect ? onSelect : null,
|
controller: _controller,
|
||||||
|
children: [
|
||||||
|
FollowChildPage(
|
||||||
|
mid: mid,
|
||||||
|
onSelect: widget.isFromSelect ? onSelect : null,
|
||||||
|
),
|
||||||
|
FansPage(
|
||||||
|
showName: false,
|
||||||
|
onSelect: widget.isFromSelect ? onSelect : null,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -51,32 +51,37 @@ class _DanmakuBlockPageState extends State<DanmakuBlockPage> {
|
|||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SimpleScaffold(
|
return SimpleScaffold(
|
||||||
appBar: AppBar(
|
appBar: AppBar(title: const Text('弹幕屏蔽')),
|
||||||
title: const Text('弹幕屏蔽'),
|
body: Column(
|
||||||
bottom: TabBar(
|
children: [
|
||||||
controller: _controller.tabController,
|
TabBar(
|
||||||
tabs: DmBlockType.values
|
controller: _controller.tabController,
|
||||||
.map(
|
tabs: DmBlockType.values
|
||||||
(e) => Obx(
|
.map(
|
||||||
() => Tab(
|
(e) => Obx(
|
||||||
text: '${e.label}(${_controller.rules[e.index].length})',
|
() => Tab(
|
||||||
|
text: '${e.label}(${_controller.rules[e.index].length})',
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
.toList(),
|
||||||
.toList(),
|
),
|
||||||
),
|
Expanded(
|
||||||
),
|
child: tabBarView(
|
||||||
body: tabBarView(
|
controller: _controller.tabController,
|
||||||
controller: _controller.tabController,
|
children: DmBlockType.values
|
||||||
children: DmBlockType.values
|
.map(
|
||||||
.map(
|
(e) => KeepAliveWrapper(
|
||||||
(e) => KeepAliveWrapper(
|
child: Obx(
|
||||||
child: Obx(
|
() =>
|
||||||
() => tabViewBuilder(e.index, _controller.rules[e.index]),
|
tabViewBuilder(e.index, _controller.rules[e.index]),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.toList(),
|
.toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
fab: Padding(
|
fab: Padding(
|
||||||
padding: .only(
|
padding: .only(
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:PiliPlus/http/loading_state.dart';
|
|||||||
import 'package:PiliPlus/models_new/fav/fav_note/list.dart';
|
import 'package:PiliPlus/models_new/fav/fav_note/list.dart';
|
||||||
import 'package:PiliPlus/pages/fav/note/controller.dart';
|
import 'package:PiliPlus/pages/fav/note/controller.dart';
|
||||||
import 'package:PiliPlus/pages/fav/note/widget/item.dart';
|
import 'package:PiliPlus/pages/fav/note/widget/item.dart';
|
||||||
|
import 'package:PiliPlus/pages/fav/pgc/pgc_layout.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:get/get.dart';
|
import 'package:get/get.dart';
|
||||||
@@ -37,109 +38,99 @@ class _FavNoteChildPageState extends State<FavNoteChildPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final padding = MediaQuery.viewPaddingOf(context);
|
final padding = MediaQuery.viewPaddingOf(context);
|
||||||
final bottomH = 50 + padding.bottom;
|
return PgcLayout(
|
||||||
return Stack(
|
body: refreshIndicator(
|
||||||
clipBehavior: Clip.none,
|
onRefresh: _favNoteController.onRefresh,
|
||||||
children: [
|
child: CustomScrollView(
|
||||||
refreshIndicator(
|
controller: _favNoteController.scrollController,
|
||||||
onRefresh: _favNoteController.onRefresh,
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
child: CustomScrollView(
|
slivers: [
|
||||||
controller: _favNoteController.scrollController,
|
SliverPadding(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
padding: EdgeInsets.only(bottom: padding.bottom + 100),
|
||||||
slivers: [
|
sliver: Obx(
|
||||||
SliverPadding(
|
() => _buildBody(_favNoteController.loadingState.value),
|
||||||
padding: EdgeInsets.only(bottom: padding.bottom + 100),
|
|
||||||
sliver: Obx(
|
|
||||||
() => _buildBody(_favNoteController.loadingState.value),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
Positioned(
|
),
|
||||||
left: 0,
|
toolbar: Obx(
|
||||||
right: 0,
|
() => AnimatedSlide(
|
||||||
bottom: -bottomH,
|
offset: _favNoteController.enableMultiSelect.value
|
||||||
child: Obx(
|
? const Offset(0, -1)
|
||||||
() => AnimatedSlide(
|
: Offset.zero,
|
||||||
offset: _favNoteController.enableMultiSelect.value
|
duration: const Duration(milliseconds: 150),
|
||||||
? const Offset(0, -1)
|
child: Container(
|
||||||
: Offset.zero,
|
padding: .only(bottom: padding.bottom),
|
||||||
duration: const Duration(milliseconds: 150),
|
decoration: BoxDecoration(
|
||||||
child: Container(
|
color: theme.colorScheme.onInverseSurface,
|
||||||
height: bottomH,
|
border: Border(
|
||||||
padding: padding,
|
top: BorderSide(
|
||||||
decoration: BoxDecoration(
|
width: 0.5,
|
||||||
color: theme.colorScheme.onInverseSurface,
|
color: theme.colorScheme.outline.withValues(alpha: 0.5),
|
||||||
border: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
width: 0.5,
|
|
||||||
color: theme.colorScheme.outline.withValues(alpha: 0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
iconButton(
|
|
||||||
size: 32,
|
|
||||||
tooltip: '取消',
|
|
||||||
context: context,
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: _favNoteController.onDisable,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Obx(
|
|
||||||
() => Checkbox(
|
|
||||||
value: _favNoteController.allSelected.value,
|
|
||||||
onChanged: (value) {
|
|
||||||
_favNoteController.handleSelect(
|
|
||||||
checked: !_favNoteController.allSelected.value,
|
|
||||||
disableSelect: false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => _favNoteController.handleSelect(
|
|
||||||
checked: !_favNoteController.allSelected.value,
|
|
||||||
disableSelect: false,
|
|
||||||
),
|
|
||||||
child: const Padding(
|
|
||||||
padding: EdgeInsets.only(
|
|
||||||
top: 14,
|
|
||||||
bottom: 14,
|
|
||||||
right: 12,
|
|
||||||
),
|
|
||||||
child: Text('全选'),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const Spacer(),
|
|
||||||
FilledButton.tonal(
|
|
||||||
style: TextButton.styleFrom(
|
|
||||||
visualDensity: VisualDensity.compact,
|
|
||||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
|
||||||
),
|
|
||||||
onPressed: () {
|
|
||||||
if (_favNoteController.checkedCount != 0) {
|
|
||||||
showConfirmDialog(
|
|
||||||
context: context,
|
|
||||||
title: const Text('确定删除已选中的笔记吗?'),
|
|
||||||
onConfirm: _favNoteController.onRemove,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: const Text('删除'),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
iconButton(
|
||||||
|
size: 32,
|
||||||
|
tooltip: '取消',
|
||||||
|
context: context,
|
||||||
|
icon: const Icon(Icons.clear),
|
||||||
|
onPressed: _favNoteController.onDisable,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 12),
|
||||||
|
Obx(
|
||||||
|
() => Checkbox(
|
||||||
|
value: _favNoteController.allSelected.value,
|
||||||
|
onChanged: (value) {
|
||||||
|
_favNoteController.handleSelect(
|
||||||
|
checked: !_favNoteController.allSelected.value,
|
||||||
|
disableSelect: false,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
GestureDetector(
|
||||||
|
behavior: HitTestBehavior.opaque,
|
||||||
|
onTap: () => _favNoteController.handleSelect(
|
||||||
|
checked: !_favNoteController.allSelected.value,
|
||||||
|
disableSelect: false,
|
||||||
|
),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: 14,
|
||||||
|
bottom: 14,
|
||||||
|
right: 12,
|
||||||
|
),
|
||||||
|
child: Text('全选'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const Spacer(),
|
||||||
|
FilledButton.tonal(
|
||||||
|
style: TextButton.styleFrom(
|
||||||
|
visualDensity: VisualDensity.compact,
|
||||||
|
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||||
|
),
|
||||||
|
onPressed: () {
|
||||||
|
if (_favNoteController.checkedCount != 0) {
|
||||||
|
showConfirmDialog(
|
||||||
|
context: context,
|
||||||
|
title: const Text('确定删除已选中的笔记吗?'),
|
||||||
|
onConfirm: _favNoteController.onRemove,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Text('删除'),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
|||||||
import 'package:PiliPlus/http/loading_state.dart';
|
import 'package:PiliPlus/http/loading_state.dart';
|
||||||
import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
|
import 'package:PiliPlus/models_new/fav/fav_pgc/list.dart';
|
||||||
import 'package:PiliPlus/pages/fav/pgc/controller.dart';
|
import 'package:PiliPlus/pages/fav/pgc/controller.dart';
|
||||||
|
import 'package:PiliPlus/pages/fav/pgc/pgc_layout.dart';
|
||||||
import 'package:PiliPlus/pages/fav/pgc/widget/item.dart';
|
import 'package:PiliPlus/pages/fav/pgc/widget/item.dart';
|
||||||
import 'package:PiliPlus/utils/grid.dart';
|
import 'package:PiliPlus/utils/grid.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
@@ -43,128 +44,118 @@ class _FavPgcChildPageState extends State<FavPgcChildPage>
|
|||||||
super.build(context);
|
super.build(context);
|
||||||
final theme = Theme.of(context);
|
final theme = Theme.of(context);
|
||||||
final padding = MediaQuery.viewPaddingOf(context);
|
final padding = MediaQuery.viewPaddingOf(context);
|
||||||
final bottomH = 50 + padding.bottom;
|
return PgcLayout(
|
||||||
return Stack(
|
body: refreshIndicator(
|
||||||
clipBehavior: Clip.none,
|
onRefresh: _favPgcController.onRefresh,
|
||||||
children: [
|
child: CustomScrollView(
|
||||||
refreshIndicator(
|
controller: _favPgcController.scrollController,
|
||||||
onRefresh: _favPgcController.onRefresh,
|
physics: const AlwaysScrollableScrollPhysics(),
|
||||||
child: CustomScrollView(
|
slivers: [
|
||||||
controller: _favPgcController.scrollController,
|
SliverPadding(
|
||||||
physics: const AlwaysScrollableScrollPhysics(),
|
padding: EdgeInsets.only(bottom: padding.bottom + 100),
|
||||||
slivers: [
|
sliver: Obx(
|
||||||
SliverPadding(
|
() => _buildBody(_favPgcController.loadingState.value),
|
||||||
padding: EdgeInsets.only(bottom: padding.bottom + 100),
|
),
|
||||||
sliver: Obx(
|
),
|
||||||
() => _buildBody(_favPgcController.loadingState.value),
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
toolbar: Obx(
|
||||||
|
() => AnimatedSlide(
|
||||||
|
offset: _favPgcController.enableMultiSelect.value
|
||||||
|
? const Offset(0, -1)
|
||||||
|
: Offset.zero,
|
||||||
|
duration: const Duration(milliseconds: 150),
|
||||||
|
child: Container(
|
||||||
|
padding: .only(bottom: padding.bottom),
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: theme.colorScheme.onInverseSurface,
|
||||||
|
border: Border(
|
||||||
|
top: BorderSide(
|
||||||
|
width: 0.5,
|
||||||
|
color: theme.colorScheme.outline.withValues(alpha: 0.5),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
child: Row(
|
||||||
),
|
children: [
|
||||||
Positioned(
|
const SizedBox(width: 16),
|
||||||
left: 0,
|
iconButton(
|
||||||
right: 0,
|
size: 32,
|
||||||
bottom: -bottomH,
|
tooltip: '取消',
|
||||||
child: Obx(
|
context: context,
|
||||||
() => AnimatedSlide(
|
icon: const Icon(Icons.clear),
|
||||||
offset: _favPgcController.enableMultiSelect.value
|
onPressed: _favPgcController.onDisable,
|
||||||
? const Offset(0, -1)
|
|
||||||
: Offset.zero,
|
|
||||||
duration: const Duration(milliseconds: 150),
|
|
||||||
child: Container(
|
|
||||||
height: bottomH,
|
|
||||||
padding: padding,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: theme.colorScheme.onInverseSurface,
|
|
||||||
border: Border(
|
|
||||||
top: BorderSide(
|
|
||||||
width: 0.5,
|
|
||||||
color: theme.colorScheme.outline.withValues(alpha: 0.5),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
child: Row(
|
const SizedBox(width: 12),
|
||||||
children: [
|
Obx(
|
||||||
const SizedBox(width: 16),
|
() => Checkbox(
|
||||||
iconButton(
|
value: _favPgcController.allSelected.value,
|
||||||
size: 32,
|
onChanged: (value) {
|
||||||
tooltip: '取消',
|
_favPgcController.handleSelect(
|
||||||
context: context,
|
|
||||||
icon: const Icon(Icons.clear),
|
|
||||||
onPressed: _favPgcController.onDisable,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 12),
|
|
||||||
Obx(
|
|
||||||
() => Checkbox(
|
|
||||||
value: _favPgcController.allSelected.value,
|
|
||||||
onChanged: (value) {
|
|
||||||
_favPgcController.handleSelect(
|
|
||||||
checked: !_favPgcController.allSelected.value,
|
|
||||||
disableSelect: false,
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
|
||||||
GestureDetector(
|
|
||||||
behavior: HitTestBehavior.opaque,
|
|
||||||
onTap: () => _favPgcController.handleSelect(
|
|
||||||
checked: !_favPgcController.allSelected.value,
|
checked: !_favPgcController.allSelected.value,
|
||||||
disableSelect: false,
|
disableSelect: false,
|
||||||
),
|
);
|
||||||
child: const Padding(
|
},
|
||||||
padding: EdgeInsets.only(
|
),
|
||||||
top: 14,
|
),
|
||||||
bottom: 14,
|
GestureDetector(
|
||||||
right: 12,
|
behavior: HitTestBehavior.opaque,
|
||||||
),
|
onTap: () => _favPgcController.handleSelect(
|
||||||
child: Text('全选'),
|
checked: !_favPgcController.allSelected.value,
|
||||||
),
|
disableSelect: false,
|
||||||
|
),
|
||||||
|
child: const Padding(
|
||||||
|
padding: EdgeInsets.only(
|
||||||
|
top: 14,
|
||||||
|
bottom: 14,
|
||||||
|
right: 12,
|
||||||
),
|
),
|
||||||
const Spacer(),
|
child: Text('全选'),
|
||||||
...const [
|
),
|
||||||
(followStatus: 1, title: '想看'),
|
),
|
||||||
(followStatus: 2, title: '在看'),
|
const Spacer(),
|
||||||
(followStatus: 3, title: '看过'),
|
...const [
|
||||||
]
|
(followStatus: 1, title: '想看'),
|
||||||
.where(
|
(followStatus: 2, title: '在看'),
|
||||||
(item) => item.followStatus != widget.followStatus,
|
(followStatus: 3, title: '看过'),
|
||||||
)
|
]
|
||||||
.map(
|
.where(
|
||||||
(item) => Padding(
|
(item) => item.followStatus != widget.followStatus,
|
||||||
padding: const EdgeInsets.only(left: 25),
|
)
|
||||||
child: GestureDetector(
|
.map(
|
||||||
behavior: HitTestBehavior.opaque,
|
(item) => Padding(
|
||||||
onTap: () {
|
padding: const EdgeInsets.only(left: 25),
|
||||||
if (_favPgcController.checkedCount != 0) {
|
child: GestureDetector(
|
||||||
_favPgcController.onUpdateList(
|
behavior: HitTestBehavior.opaque,
|
||||||
item.followStatus,
|
onTap: () {
|
||||||
);
|
if (_favPgcController.checkedCount != 0) {
|
||||||
}
|
_favPgcController.onUpdateList(
|
||||||
},
|
item.followStatus,
|
||||||
child: Padding(
|
);
|
||||||
padding: const EdgeInsets.symmetric(
|
}
|
||||||
vertical: 14,
|
},
|
||||||
horizontal: 5,
|
child: Padding(
|
||||||
),
|
padding: const EdgeInsets.symmetric(
|
||||||
child: Text(
|
vertical: 14,
|
||||||
'标记为${item.title}',
|
horizontal: 5,
|
||||||
style: TextStyle(
|
),
|
||||||
color: theme.colorScheme.onSurfaceVariant,
|
child: Text(
|
||||||
),
|
'标记为${item.title}',
|
||||||
),
|
style: TextStyle(
|
||||||
|
color: theme.colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
),
|
||||||
],
|
),
|
||||||
),
|
const SizedBox(width: 20),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
86
lib/pages/fav/pgc/pgc_layout.dart
Normal file
86
lib/pages/fav/pgc/pgc_layout.dart
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/rendering.dart' show BoxHitTestResult, BoxParentData;
|
||||||
|
|
||||||
|
enum PgcType { toolbar, body }
|
||||||
|
|
||||||
|
class PgcLayout
|
||||||
|
extends SlottedMultiChildRenderObjectWidget<PgcType, RenderBox> {
|
||||||
|
const PgcLayout({
|
||||||
|
super.key,
|
||||||
|
required this.body,
|
||||||
|
required this.toolbar,
|
||||||
|
});
|
||||||
|
|
||||||
|
final Widget body;
|
||||||
|
final Widget toolbar;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Iterable<PgcType> get slots => PgcType.values;
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget childForSlot(slot) => switch (slot) {
|
||||||
|
.toolbar => toolbar,
|
||||||
|
.body => body,
|
||||||
|
};
|
||||||
|
|
||||||
|
@override
|
||||||
|
SlottedContainerRenderObjectMixin<PgcType, RenderBox> createRenderObject(
|
||||||
|
BuildContext context,
|
||||||
|
) {
|
||||||
|
return _RenderPgcLayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class _RenderPgcLayout extends RenderBox
|
||||||
|
with SlottedContainerRenderObjectMixin<PgcType, RenderBox> {
|
||||||
|
RenderBox get body => childForSlot(.body)!;
|
||||||
|
RenderBox get toolbar => childForSlot(.toolbar)!;
|
||||||
|
|
||||||
|
Offset _getOffset(RenderBox child) {
|
||||||
|
return (child.parentData as BoxParentData).offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _setOffset(RenderBox child, Offset offset) {
|
||||||
|
(child.parentData as BoxParentData).offset = offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void performLayout() {
|
||||||
|
final constraints = this.constraints;
|
||||||
|
size = constraints.biggest;
|
||||||
|
|
||||||
|
final body = this.body..layout(constraints);
|
||||||
|
_setOffset(body, .zero);
|
||||||
|
|
||||||
|
final toolbar = this.toolbar
|
||||||
|
..layout(BoxConstraints.tightFor(width: constraints.maxWidth));
|
||||||
|
_setOffset(toolbar, Offset(0, constraints.maxHeight));
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void paint(PaintingContext context, Offset offset) {
|
||||||
|
void doPaint(RenderBox child) {
|
||||||
|
context.paintChild(child, _getOffset(child) + offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
doPaint(body);
|
||||||
|
doPaint(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {
|
||||||
|
for (final child in children) {
|
||||||
|
final bool isHit = result.addWithPaintOffset(
|
||||||
|
offset: _getOffset(child),
|
||||||
|
position: position,
|
||||||
|
hitTest: (BoxHitTestResult result, Offset transformed) {
|
||||||
|
return child.hitTest(result, position: transformed);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (isHit) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -126,36 +126,45 @@ class _FavPageState extends State<FavPage> with SingleTickerProviderStateMixin {
|
|||||||
),
|
),
|
||||||
const SizedBox(width: 6),
|
const SizedBox(width: 6),
|
||||||
],
|
],
|
||||||
bottom: TabBar(
|
|
||||||
controller: _tabController,
|
|
||||||
isScrollable: true,
|
|
||||||
tabAlignment: TabAlignment.start,
|
|
||||||
tabs: FavTabType.values.map((item) => Tab(text: item.title)).toList(),
|
|
||||||
onTap: (index) {
|
|
||||||
try {
|
|
||||||
if (!_tabController.indexIsChanging) {
|
|
||||||
switch (FavTabType.values[index]) {
|
|
||||||
case FavTabType.video:
|
|
||||||
_favController.scrollController.animToTop();
|
|
||||||
case FavTabType.article:
|
|
||||||
Get.find<FavArticleController>().scrollController
|
|
||||||
.animToTop();
|
|
||||||
case FavTabType.topic:
|
|
||||||
Get.find<FavTopicController>().scrollController.animToTop();
|
|
||||||
case FavTabType.cheese:
|
|
||||||
Get.find<FavCheeseController>().scrollController
|
|
||||||
.animToTop();
|
|
||||||
default:
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (_) {}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
body: ViewSafeArea(
|
body: ViewSafeArea(
|
||||||
child: tabBarView(
|
child: Column(
|
||||||
controller: _tabController,
|
children: [
|
||||||
children: FavTabType.values.map((item) => item.page).toList(),
|
TabBar(
|
||||||
|
controller: _tabController,
|
||||||
|
isScrollable: true,
|
||||||
|
tabAlignment: TabAlignment.start,
|
||||||
|
tabs: FavTabType.values
|
||||||
|
.map((item) => Tab(text: item.title))
|
||||||
|
.toList(),
|
||||||
|
onTap: (index) {
|
||||||
|
try {
|
||||||
|
if (!_tabController.indexIsChanging) {
|
||||||
|
switch (FavTabType.values[index]) {
|
||||||
|
case FavTabType.video:
|
||||||
|
_favController.scrollController.animToTop();
|
||||||
|
case FavTabType.article:
|
||||||
|
Get.find<FavArticleController>().scrollController
|
||||||
|
.animToTop();
|
||||||
|
case FavTabType.topic:
|
||||||
|
Get.find<FavTopicController>().scrollController
|
||||||
|
.animToTop();
|
||||||
|
case FavTabType.cheese:
|
||||||
|
Get.find<FavCheeseController>().scrollController
|
||||||
|
.animToTop();
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (_) {}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: tabBarView(
|
||||||
|
controller: _tabController,
|
||||||
|
children: FavTabType.values.map((item) => item.page).toList(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -104,51 +104,62 @@ class _HistoryPageState extends State<HistoryPage>
|
|||||||
left: padding.left,
|
left: padding.left,
|
||||||
right: padding.right,
|
right: padding.right,
|
||||||
),
|
),
|
||||||
child: Obx(() {
|
child: Column(
|
||||||
final tabs = _historyController.tabs;
|
children: [
|
||||||
if (tabs.isEmpty) {
|
?_buildPauseTip,
|
||||||
return child;
|
Expanded(
|
||||||
}
|
child: Obx(() {
|
||||||
return Column(
|
final tabs = _historyController.tabs;
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
if (tabs.isEmpty) {
|
||||||
children: [
|
return child;
|
||||||
TabBar(
|
}
|
||||||
controller: _historyController.tabController,
|
return Column(
|
||||||
onTap: (index) {
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
if (!_historyController
|
|
||||||
.tabController!
|
|
||||||
.indexIsChanging) {
|
|
||||||
currCtr().scrollController.animToTop();
|
|
||||||
} else {
|
|
||||||
if (enableMultiSelect) {
|
|
||||||
currCtr(
|
|
||||||
_historyController.tabController!.previousIndex,
|
|
||||||
).handleSelect();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tabs: [
|
|
||||||
const Tab(text: '全部'),
|
|
||||||
...tabs.map((item) => Tab(text: item.name)),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Expanded(
|
|
||||||
child: TabBarView(
|
|
||||||
physics: enableMultiSelect
|
|
||||||
? const NeverScrollableScrollPhysics()
|
|
||||||
: tabBarScrollPhysics,
|
|
||||||
controller: _historyController.tabController,
|
|
||||||
horizontalDragGestureRecognizer:
|
|
||||||
CustomHorizontalDragGestureRecognizer.new,
|
|
||||||
children: [
|
children: [
|
||||||
KeepAliveWrapper(child: child),
|
TabBar(
|
||||||
...tabs.map((item) => HistoryPage(type: item.type)),
|
controller: _historyController.tabController,
|
||||||
|
onTap: (index) {
|
||||||
|
if (!_historyController
|
||||||
|
.tabController!
|
||||||
|
.indexIsChanging) {
|
||||||
|
currCtr().scrollController.animToTop();
|
||||||
|
} else {
|
||||||
|
if (enableMultiSelect) {
|
||||||
|
currCtr(
|
||||||
|
_historyController
|
||||||
|
.tabController!
|
||||||
|
.previousIndex,
|
||||||
|
).handleSelect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tabs: [
|
||||||
|
const Tab(text: '全部'),
|
||||||
|
...tabs.map((item) => Tab(text: item.name)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Expanded(
|
||||||
|
child: TabBarView(
|
||||||
|
physics: enableMultiSelect
|
||||||
|
? const NeverScrollableScrollPhysics()
|
||||||
|
: tabBarScrollPhysics,
|
||||||
|
controller: _historyController.tabController,
|
||||||
|
horizontalDragGestureRecognizer:
|
||||||
|
CustomHorizontalDragGestureRecognizer.new,
|
||||||
|
children: [
|
||||||
|
KeepAliveWrapper(child: child),
|
||||||
|
...tabs.map(
|
||||||
|
(item) => HistoryPage(type: item.type),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
),
|
}),
|
||||||
],
|
),
|
||||||
);
|
],
|
||||||
}),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -158,7 +169,6 @@ class _HistoryPageState extends State<HistoryPage>
|
|||||||
|
|
||||||
AppBar get _buildAppBar => AppBar(
|
AppBar get _buildAppBar => AppBar(
|
||||||
title: const Text('观看记录'),
|
title: const Text('观看记录'),
|
||||||
bottom: _buildPauseTip,
|
|
||||||
actions: [
|
actions: [
|
||||||
IconButton(
|
IconButton(
|
||||||
tooltip: '搜索',
|
tooltip: '搜索',
|
||||||
|
|||||||
@@ -569,36 +569,41 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
bottom: !isLandscape
|
|
||||||
? TabBar(
|
|
||||||
tabs: const [
|
|
||||||
Tab(icon: Icon(Icons.password), text: '密码'),
|
|
||||||
Tab(icon: Icon(Icons.sms_outlined), text: '短信'),
|
|
||||||
Tab(icon: Icon(Icons.qr_code), text: '扫码'),
|
|
||||||
Tab(icon: Icon(Icons.cookie_outlined), text: 'Cookie'),
|
|
||||||
],
|
|
||||||
controller: _loginPageCtr.tabController,
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
body: NotificationListener<ScrollStartNotification>(
|
body: Column(
|
||||||
onNotification: (notification) {
|
children: [
|
||||||
if (notification.metrics.axis == Axis.horizontal) {
|
if (!isLandscape)
|
||||||
FocusScope.of(context).unfocus();
|
TabBar(
|
||||||
}
|
tabs: const [
|
||||||
return false;
|
Tab(icon: Icon(Icons.password), text: '密码'),
|
||||||
},
|
Tab(icon: Icon(Icons.sms_outlined), text: '短信'),
|
||||||
child: ViewInsetsSafeArea(
|
Tab(icon: Icon(Icons.qr_code), text: '扫码'),
|
||||||
child: tabBarView(
|
Tab(icon: Icon(Icons.cookie_outlined), text: 'Cookie'),
|
||||||
controller: _loginPageCtr.tabController,
|
],
|
||||||
children: [
|
controller: _loginPageCtr.tabController,
|
||||||
tabViewOuter(loginByPassword(theme)),
|
),
|
||||||
tabViewOuter(loginBySmS(theme)),
|
Expanded(
|
||||||
tabViewOuter(loginByQRCode(theme)),
|
child: NotificationListener<ScrollStartNotification>(
|
||||||
tabViewOuter(loginByCookie(theme)),
|
onNotification: (notification) {
|
||||||
],
|
if (notification.metrics.axis == Axis.horizontal) {
|
||||||
|
FocusScope.of(context).unfocus();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
child: ViewInsetsSafeArea(
|
||||||
|
child: tabBarView(
|
||||||
|
controller: _loginPageCtr.tabController,
|
||||||
|
children: [
|
||||||
|
tabViewOuter(loginByPassword(theme)),
|
||||||
|
tabViewOuter(loginBySmS(theme)),
|
||||||
|
tabViewOuter(loginByQRCode(theme)),
|
||||||
|
tabViewOuter(loginByCookie(theme)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user