mirror of
https://github.com/bggRGjQaUbCoE/PiliPlus.git
synced 2026-07-13 21:00:12 +08:00
opt pub page
Signed-off-by: bggRGjQaUbCoE <githubaccount56556@proton.me>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
import 'dart:math' show max;
|
||||
|
||||
import 'package:PiliPlus/common/widgets/button/toolbar_icon_button.dart';
|
||||
import 'package:PiliPlus/common/widgets/text_field/controller.dart'
|
||||
@@ -12,10 +14,16 @@ import 'package:PiliPlus/models/common/publish_panel_type.dart';
|
||||
import 'package:PiliPlus/pages/common/publish/common_rich_text_pub_page.dart';
|
||||
import 'package:PiliPlus/pages/dynamics_mention/controller.dart';
|
||||
import 'package:PiliPlus/pages/emote/view.dart';
|
||||
import 'package:PiliPlus/pages/video/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/view.dart';
|
||||
import 'package:PiliPlus/utils/duration_util.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:PiliPlus/utils/storage_pref.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart' hide TextField;
|
||||
import 'package:flutter_smart_dialog/flutter_smart_dialog.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
|
||||
class ReplyPage extends CommonRichTextPubPage {
|
||||
final int oid;
|
||||
@@ -44,31 +52,7 @@ class ReplyPage extends CommonRichTextPubPage {
|
||||
|
||||
class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
final RxBool _syncToDynamic = false.obs;
|
||||
|
||||
Widget get child => SafeArea(
|
||||
bottom: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
),
|
||||
color: themeData.colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...buildInputView(),
|
||||
buildImagePreview(),
|
||||
buildPanelContainer(Colors.transparent),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
final heroTag = Get.arguments?['heroTag'];
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
@@ -90,6 +74,30 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = SafeArea(
|
||||
bottom: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 640),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
),
|
||||
color: themeData.colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
...buildInputView(),
|
||||
buildImagePreview(),
|
||||
buildPanelContainer(themeData, Colors.transparent),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return darkVideoPage ? Theme(data: themeData, child: child) : child;
|
||||
}
|
||||
|
||||
@@ -167,31 +175,7 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
padding: const EdgeInsets.only(left: 12, right: 12),
|
||||
child: Row(
|
||||
children: [
|
||||
Obx(
|
||||
() => ToolbarIconButton(
|
||||
tooltip: '输入',
|
||||
onPressed: () {
|
||||
if (panelType.value != PanelType.keyboard) {
|
||||
updatePanelType(PanelType.keyboard);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.keyboard, size: 22),
|
||||
selected: panelType.value == PanelType.keyboard,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Obx(
|
||||
() => ToolbarIconButton(
|
||||
tooltip: '表情',
|
||||
onPressed: () {
|
||||
if (panelType.value != PanelType.emoji) {
|
||||
updatePanelType(PanelType.emoji);
|
||||
}
|
||||
},
|
||||
icon: const Icon(Icons.emoji_emotions, size: 22),
|
||||
selected: panelType.value == PanelType.emoji,
|
||||
),
|
||||
),
|
||||
emojiBtn,
|
||||
if (widget.root == 0) ...[
|
||||
const SizedBox(width: 8),
|
||||
ToolbarIconButton(
|
||||
@@ -202,12 +186,9 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
),
|
||||
],
|
||||
const SizedBox(width: 8),
|
||||
ToolbarIconButton(
|
||||
onPressed: () => onMention(true),
|
||||
icon: const Icon(Icons.alternate_email, size: 22),
|
||||
tooltip: '@',
|
||||
selected: false,
|
||||
),
|
||||
atBtn,
|
||||
const SizedBox(width: 8),
|
||||
moreBtn,
|
||||
Expanded(
|
||||
child: Center(
|
||||
child: Obx(
|
||||
@@ -264,6 +245,144 @@ class _ReplyPageState extends CommonRichTextPubPageState<ReplyPage> {
|
||||
];
|
||||
}
|
||||
|
||||
@override
|
||||
Widget buildMorePanel(ThemeData theme) {
|
||||
double height = context.isTablet ? 300 : 170;
|
||||
final keyboardHeight = controller.keyboardHeight;
|
||||
if (keyboardHeight != 0) {
|
||||
height = max(height, keyboardHeight);
|
||||
}
|
||||
|
||||
Widget item({
|
||||
required VoidCallback onTap,
|
||||
required Icon icon,
|
||||
required String title,
|
||||
}) {
|
||||
return GestureDetector(
|
||||
onTap: onTap,
|
||||
child: Column(
|
||||
spacing: 5,
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: 1,
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: themeData.colorScheme.onInverseSurface,
|
||||
borderRadius: const BorderRadius.all(Radius.circular(6)),
|
||||
),
|
||||
alignment: Alignment.center,
|
||||
child: icon,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
style: const TextStyle(fontSize: 13),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
final isRoot = widget.root == 0;
|
||||
final color = themeData.colorScheme.onSurfaceVariant;
|
||||
|
||||
return SizedBox(
|
||||
height: height,
|
||||
child: GridView(
|
||||
padding: const EdgeInsets.only(left: 12, bottom: 12, right: 12),
|
||||
gridDelegate: const SliverGridDelegateWithExtentAndRatio(
|
||||
maxCrossAxisExtent: 65,
|
||||
mainAxisSpacing: 12,
|
||||
crossAxisSpacing: 12,
|
||||
mainAxisExtent: 25,
|
||||
),
|
||||
children: [
|
||||
item(
|
||||
onTap: () async {
|
||||
controller.keepChatPanel();
|
||||
({String title, String url})? res = await Get.to(
|
||||
ReplySearchPage(type: widget.replyType, oid: widget.oid));
|
||||
if (res != null) {
|
||||
onInsertText(
|
||||
'${res.title} ',
|
||||
RichTextType.common,
|
||||
rawText: '${res.url} ',
|
||||
);
|
||||
}
|
||||
controller.restoreChatPanel();
|
||||
},
|
||||
icon: Icon(Icons.post_add, size: 28, color: color),
|
||||
title: '插入内容',
|
||||
),
|
||||
if (heroTag != null) ...[
|
||||
// if (isRoot)
|
||||
// item(
|
||||
// onTap: () {
|
||||
// Get.back();
|
||||
// try {
|
||||
// Get.find<VideoDetailController>(tag: heroTag)
|
||||
// .showNoteList(context);
|
||||
// } catch (e) {
|
||||
// debugPrint(e.toString());
|
||||
// }
|
||||
// },
|
||||
// icon: Icon(Icons.edit_note, size: 28, color: color),
|
||||
// title: '笔记',
|
||||
// ),
|
||||
item(
|
||||
onTap: () {
|
||||
try {
|
||||
final plPlayerController =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
onInsertText(
|
||||
' ${DurationUtil.formatDuration((plPlayerController.playedTime ?? Duration.zero).inSeconds)} ',
|
||||
RichTextType.common,
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
},
|
||||
icon: Icon(Icons.my_location, size: 28, color: color),
|
||||
title: '视频进度',
|
||||
),
|
||||
if (isRoot)
|
||||
item(
|
||||
onTap: () async {
|
||||
if (pathList.length >= limit) {
|
||||
SmartDialog.showToast('最多选择$limit张图片');
|
||||
return;
|
||||
}
|
||||
try {
|
||||
final plPlayerController =
|
||||
Get.find<VideoDetailController>(tag: heroTag);
|
||||
final res = await plPlayerController
|
||||
.plPlayerController.videoPlayerController
|
||||
?.screenshot(format: 'image/png');
|
||||
if (res != null) {
|
||||
final tempDir = await getTemporaryDirectory();
|
||||
File file = File(
|
||||
'${tempDir.path}/${Utils.generateRandomString(8)}.png');
|
||||
await file.writeAsBytes(res);
|
||||
pathList.add(file.path);
|
||||
} else {
|
||||
debugPrint('null screenshot');
|
||||
}
|
||||
} catch (e) {
|
||||
debugPrint(e.toString());
|
||||
}
|
||||
},
|
||||
icon: Icon(Icons.enhance_photo_translate_outlined,
|
||||
size: 28, color: color),
|
||||
title: '视频截图',
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> onCustomPublish({List? pictures}) async {
|
||||
Map<String, int> atNameToMid = {};
|
||||
|
||||
36
lib/pages/video/reply_search_item/child/controller.dart
Normal file
36
lib/pages/video/reply_search_item/child/controller.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
|
||||
show SearchItemReply, SearchItem, SearchItemType;
|
||||
import 'package:PiliPlus/grpc/reply.dart';
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_search_type.dart';
|
||||
import 'package:PiliPlus/pages/common/common_list_controller.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/controller.dart';
|
||||
|
||||
class ReplySearchChildController
|
||||
extends CommonListController<SearchItemReply, SearchItem> {
|
||||
ReplySearchChildController(this.controller, this.searchType);
|
||||
|
||||
final ReplySearchController controller;
|
||||
final ReplySearchType searchType;
|
||||
|
||||
@override
|
||||
List<SearchItem>? getDataList(SearchItemReply response) {
|
||||
if (response.cursor.hasNext == false) {
|
||||
isEnd = true;
|
||||
}
|
||||
return response.items;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<LoadingState<SearchItemReply>> customGetData() {
|
||||
return ReplyGrpc.searchItem(
|
||||
page: page,
|
||||
itemType: searchType == ReplySearchType.video
|
||||
? SearchItemType.VIDEO
|
||||
: SearchItemType.ARTICLE,
|
||||
oid: controller.oid,
|
||||
type: controller.type,
|
||||
keyword: controller.editingController.text,
|
||||
);
|
||||
}
|
||||
}
|
||||
94
lib/pages/video/reply_search_item/child/view.dart
Normal file
94
lib/pages/video/reply_search_item/child/view.dart
Normal file
@@ -0,0 +1,94 @@
|
||||
import 'package:PiliPlus/common/skeleton/video_card_h.dart';
|
||||
import 'package:PiliPlus/common/widgets/loading_widget/http_error.dart';
|
||||
import 'package:PiliPlus/common/widgets/refresh_indicator.dart';
|
||||
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
|
||||
show SearchItem;
|
||||
import 'package:PiliPlus/http/loading_state.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_search_type.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/child/controller.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/child/widgets/item.dart';
|
||||
import 'package:PiliPlus/utils/grid.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ReplySearchChildPage extends StatefulWidget {
|
||||
const ReplySearchChildPage({
|
||||
super.key,
|
||||
required this.controller,
|
||||
required this.searchType,
|
||||
});
|
||||
|
||||
final ReplySearchChildController controller;
|
||||
final ReplySearchType searchType;
|
||||
|
||||
@override
|
||||
State<ReplySearchChildPage> createState() => _ReplySearchChildPageState();
|
||||
}
|
||||
|
||||
class _ReplySearchChildPageState extends State<ReplySearchChildPage>
|
||||
with AutomaticKeepAliveClientMixin {
|
||||
ReplySearchChildController get _controller => widget.controller;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
super.build(context);
|
||||
return refreshIndicator(
|
||||
onRefresh: _controller.onRefresh,
|
||||
child: CustomScrollView(
|
||||
controller: _controller.scrollController,
|
||||
physics: const AlwaysScrollableScrollPhysics(),
|
||||
slivers: [
|
||||
SliverPadding(
|
||||
padding: EdgeInsets.only(
|
||||
top: 7,
|
||||
bottom: MediaQuery.paddingOf(context).bottom + 80,
|
||||
),
|
||||
sliver: Obx(() => _buildBody(_controller.loadingState.value)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget get _buildLoading {
|
||||
return SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
return const VideoCardHSkeleton();
|
||||
},
|
||||
childCount: 10,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody(LoadingState<List<SearchItem>?> loadingState) {
|
||||
return switch (loadingState) {
|
||||
Loading() => _buildLoading,
|
||||
Success(:var response) => response?.isNotEmpty == true
|
||||
? SliverGrid(
|
||||
gridDelegate: Grid.videoCardHDelegate(context),
|
||||
delegate: SliverChildBuilderDelegate(
|
||||
(context, index) {
|
||||
if (index == response.length - 1) {
|
||||
_controller.onLoadMore();
|
||||
}
|
||||
return ReplySearchItem(
|
||||
item: response[index],
|
||||
type: widget.searchType,
|
||||
);
|
||||
},
|
||||
childCount: response!.length,
|
||||
),
|
||||
)
|
||||
: HttpError(onReload: _controller.onReload),
|
||||
Error(:var errMsg) => HttpError(
|
||||
errMsg: errMsg,
|
||||
onReload: _controller.onReload,
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
@override
|
||||
bool get wantKeepAlive => true;
|
||||
}
|
||||
125
lib/pages/video/reply_search_item/child/widgets/item.dart
Normal file
125
lib/pages/video/reply_search_item/child/widgets/item.dart
Normal file
@@ -0,0 +1,125 @@
|
||||
import 'package:PiliPlus/common/constants.dart';
|
||||
import 'package:PiliPlus/common/widgets/badge.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/image_save.dart';
|
||||
import 'package:PiliPlus/common/widgets/image/network_img_layer.dart';
|
||||
import 'package:PiliPlus/grpc/bilibili/main/community/reply/v1.pb.dart'
|
||||
show SearchItem, SearchItemVideoSubType;
|
||||
import 'package:PiliPlus/models/common/badge_type.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_search_type.dart';
|
||||
import 'package:PiliPlus/utils/duration_util.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ReplySearchItem extends StatelessWidget {
|
||||
const ReplySearchItem({
|
||||
super.key,
|
||||
required this.item,
|
||||
required this.type,
|
||||
});
|
||||
|
||||
final SearchItem item;
|
||||
final ReplySearchType type;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
String title = '';
|
||||
String cover = '';
|
||||
String? upNickname;
|
||||
String? category;
|
||||
int? duration;
|
||||
switch (type) {
|
||||
case ReplySearchType.video:
|
||||
if (item.video.type == SearchItemVideoSubType.UGC) {
|
||||
final ugc = item.video.ugc;
|
||||
title = ugc.title;
|
||||
cover = ugc.cover;
|
||||
upNickname = ugc.upNickname;
|
||||
duration = ugc.duration.toInt();
|
||||
} else {
|
||||
final pgc = item.video.pgc;
|
||||
title = pgc.title;
|
||||
cover = pgc.cover;
|
||||
category = pgc.category;
|
||||
}
|
||||
case ReplySearchType.article:
|
||||
final article = item.article;
|
||||
title = article.title;
|
||||
cover = article.covers.firstOrNull ?? '';
|
||||
upNickname = article.upNickname;
|
||||
}
|
||||
return Material(
|
||||
type: MaterialType.transparency,
|
||||
child: InkWell(
|
||||
onTap: () => Get.back(result: (title: title, url: item.url)),
|
||||
onLongPress: () => imageSaveDialog(
|
||||
title: title,
|
||||
cover: cover,
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: StyleString.safeSpace,
|
||||
vertical: 5,
|
||||
),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
AspectRatio(
|
||||
aspectRatio: StyleString.aspectRatio,
|
||||
child: LayoutBuilder(
|
||||
builder: (context, boxConstraints) {
|
||||
return Stack(
|
||||
children: [
|
||||
NetworkImgLayer(
|
||||
src: cover,
|
||||
width: boxConstraints.maxWidth,
|
||||
height: boxConstraints.maxHeight,
|
||||
),
|
||||
if (category != null)
|
||||
PBadge(
|
||||
right: 6,
|
||||
top: 6,
|
||||
text: category,
|
||||
),
|
||||
if (duration != null)
|
||||
PBadge(
|
||||
right: 6,
|
||||
bottom: 6,
|
||||
text: DurationUtil.formatDuration(duration),
|
||||
type: PBadgeType.gray,
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
),
|
||||
if (upNickname != null)
|
||||
Text(
|
||||
'UP: $upNickname',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Theme.of(context).colorScheme.outline,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
56
lib/pages/video/reply_search_item/controller.dart
Normal file
56
lib/pages/video/reply_search_item/controller.dart
Normal file
@@ -0,0 +1,56 @@
|
||||
import 'package:PiliPlus/models/common/reply/reply_search_type.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/child/controller.dart';
|
||||
import 'package:PiliPlus/utils/extension.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ReplySearchController extends GetxController
|
||||
with GetSingleTickerProviderStateMixin {
|
||||
ReplySearchController(this.type, this.oid);
|
||||
final int type;
|
||||
final int oid;
|
||||
|
||||
late final tabController = TabController(vsync: this, length: 2);
|
||||
final editingController = TextEditingController();
|
||||
final focusNode = FocusNode();
|
||||
|
||||
late final videoCtr = Get.put(
|
||||
ReplySearchChildController(this, ReplySearchType.video),
|
||||
tag: Utils.generateRandomString(8));
|
||||
late final articleCtr = Get.put(
|
||||
ReplySearchChildController(this, ReplySearchType.article),
|
||||
tag: Utils.generateRandomString(8));
|
||||
|
||||
void onClear() {
|
||||
if (editingController.value.text.isNotEmpty) {
|
||||
editingController.clear();
|
||||
focusNode.requestFocus();
|
||||
} else {
|
||||
Get.back();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
submit();
|
||||
}
|
||||
|
||||
void submit() {
|
||||
videoCtr
|
||||
..scrollController.jumpToTop()
|
||||
..onReload();
|
||||
articleCtr
|
||||
..scrollController.jumpToTop()
|
||||
..onReload();
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
editingController.dispose();
|
||||
focusNode.dispose();
|
||||
tabController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
99
lib/pages/video/reply_search_item/view.dart
Normal file
99
lib/pages/video/reply_search_item/view.dart
Normal file
@@ -0,0 +1,99 @@
|
||||
import 'package:PiliPlus/common/widgets/scroll_physics.dart';
|
||||
import 'package:PiliPlus/models/common/reply/reply_search_type.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/child/view.dart';
|
||||
import 'package:PiliPlus/pages/video/reply_search_item/controller.dart';
|
||||
import 'package:PiliPlus/utils/utils.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
class ReplySearchPage extends StatefulWidget {
|
||||
const ReplySearchPage({
|
||||
super.key,
|
||||
required this.type,
|
||||
required this.oid,
|
||||
});
|
||||
|
||||
final int type;
|
||||
final int oid;
|
||||
|
||||
@override
|
||||
State<ReplySearchPage> createState() => _ReplySearchPageState();
|
||||
}
|
||||
|
||||
class _ReplySearchPageState extends State<ReplySearchPage> {
|
||||
late final _controller = Get.put(
|
||||
ReplySearchController(widget.type, widget.oid),
|
||||
tag: Utils.generateRandomString(8));
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
actions: [
|
||||
IconButton(
|
||||
tooltip: '搜索',
|
||||
onPressed: _controller.submit,
|
||||
icon: const Icon(Icons.search, size: 22),
|
||||
),
|
||||
const SizedBox(width: 10)
|
||||
],
|
||||
title: TextField(
|
||||
autofocus: true,
|
||||
focusNode: _controller.focusNode,
|
||||
controller: _controller.editingController,
|
||||
textInputAction: TextInputAction.search,
|
||||
textAlignVertical: TextAlignVertical.center,
|
||||
decoration: InputDecoration(
|
||||
hintText: '搜索',
|
||||
border: InputBorder.none,
|
||||
suffixIcon: IconButton(
|
||||
tooltip: '清空',
|
||||
icon: const Icon(Icons.clear, size: 22),
|
||||
onPressed: _controller.onClear,
|
||||
),
|
||||
),
|
||||
onSubmitted: (value) => _controller.submit(),
|
||||
),
|
||||
),
|
||||
body: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Column(
|
||||
children: [
|
||||
TabBar(
|
||||
controller: _controller.tabController,
|
||||
tabs: [
|
||||
const Tab(text: '视频'),
|
||||
const Tab(text: '专栏'),
|
||||
],
|
||||
onTap: (index) {
|
||||
if (!_controller.tabController.indexIsChanging) {
|
||||
if (index == 0) {
|
||||
_controller.videoCtr.animateToTop();
|
||||
} else {
|
||||
_controller.articleCtr.animateToTop();
|
||||
}
|
||||
}
|
||||
},
|
||||
),
|
||||
Expanded(
|
||||
child: tabBarView(
|
||||
controller: _controller.tabController,
|
||||
children: [
|
||||
ReplySearchChildPage(
|
||||
controller: _controller.videoCtr,
|
||||
searchType: ReplySearchType.video,
|
||||
),
|
||||
ReplySearchChildPage(
|
||||
controller: _controller.articleCtr,
|
||||
searchType: ReplySearchType.article,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -134,30 +134,6 @@ class _SendDanmakuPanelState extends CommonTextPubPageState<SendDanmakuPanel> {
|
||||
),
|
||||
);
|
||||
|
||||
Widget get child => SafeArea(
|
||||
bottom: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 450),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
),
|
||||
color: themeData.colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildInputView(),
|
||||
buildPanelContainer(Colors.transparent),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
@@ -170,6 +146,29 @@ class _SendDanmakuPanelState extends CommonTextPubPageState<SendDanmakuPanel> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Widget child = SafeArea(
|
||||
bottom: false,
|
||||
child: Align(
|
||||
alignment: Alignment.bottomCenter,
|
||||
child: Container(
|
||||
constraints: const BoxConstraints(maxWidth: 450),
|
||||
decoration: BoxDecoration(
|
||||
borderRadius: const BorderRadius.only(
|
||||
topLeft: Radius.circular(12),
|
||||
topRight: Radius.circular(12),
|
||||
),
|
||||
color: themeData.colorScheme.surface,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
_buildInputView(),
|
||||
buildPanelContainer(themeData, Colors.transparent),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
return widget.darkVideoPage ? Theme(data: themeData, child: child) : child;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user