fix(player): 评论聚焦态改为展开输入面板
This commit is contained in:
1 parent
8b51a7c18e
commit
519cab20fe
3 files changed
+225
-6
No files matched your search
@@ -31,6 +31,8 @@ const double _headerHeight = 44;
|
||||
const double _replyIndent = 52;
|
||||
const double _composerTopGap = 8;
|
||||
const double _replyBannerHeight = 46;
|
||||
const double _expandedInputRadius = 12;
|
||||
const double _postButtonHeight = 32;
|
||||
|
||||
/// 服务端评论底部弹窗:匿名列表、游标翻页、回复、举报和本人删除。
|
||||
class DramaCommentsSheet extends StatefulWidget {
|
||||
@@ -54,6 +56,8 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_inputFocus.addListener(_onFocusChanged);
|
||||
_input.addListener(_onInputChanged);
|
||||
_comments = context.read<CommentRepository>();
|
||||
_controller = CommentController(
|
||||
comments: _comments,
|
||||
@@ -66,12 +70,22 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_inputFocus.removeListener(_onFocusChanged);
|
||||
_input.removeListener(_onInputChanged);
|
||||
_controller.dispose();
|
||||
_input.dispose();
|
||||
_inputFocus.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _onFocusChanged() {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
void _onInputChanged() {
|
||||
if (mounted) setState(() {});
|
||||
}
|
||||
|
||||
Future<void> _post() async {
|
||||
if (_controller.submitting) return;
|
||||
if (!await ensureSignedIn(context) || !mounted) return;
|
||||
@@ -82,7 +96,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
if (result.isSuccess) {
|
||||
_input.clear();
|
||||
setState(() => _replyTo = null);
|
||||
FocusScope.of(context).unfocus();
|
||||
_inputFocus.requestFocus();
|
||||
await _refreshCount();
|
||||
return;
|
||||
}
|
||||
@@ -184,6 +198,9 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
final maxHeight = screenLimit < _maxSheetHeight
|
||||
? screenLimit
|
||||
: _maxSheetHeight;
|
||||
if (_inputFocus.hasFocus) {
|
||||
return _expandedComposer(context, maxHeight);
|
||||
}
|
||||
return Container(
|
||||
key: const Key('player-comments-sheet'),
|
||||
decoration: const BoxDecoration(
|
||||
@@ -215,6 +232,115 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _expandedComposer(BuildContext context, double maxHeight) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final signedIn = context.watch<AuthController>().isSignedIn;
|
||||
final hasContent = _input.text.trim().isNotEmpty;
|
||||
final canPost = signedIn && hasContent && !_controller.submitting;
|
||||
return AnimatedPadding(
|
||||
duration: const Duration(milliseconds: 150),
|
||||
curve: Curves.easeOut,
|
||||
padding: EdgeInsets.only(bottom: MediaQuery.viewInsetsOf(context).bottom),
|
||||
child: Container(
|
||||
key: const Key('player-comments-sheet'),
|
||||
decoration: const BoxDecoration(
|
||||
color: AppColors.card,
|
||||
borderRadius: BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(maxHeight: maxHeight),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
|
||||
child: Column(
|
||||
key: const Key('comment-expanded-composer'),
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (_replyTo != null) _replyBanner(l10n, _replyTo!),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.commentInput,
|
||||
borderRadius: BorderRadius.circular(_expandedInputRadius),
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key('comment-input'),
|
||||
controller: _input,
|
||||
focusNode: _inputFocus,
|
||||
autofocus: true,
|
||||
readOnly: !signedIn,
|
||||
canRequestFocus: signedIn,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
maxLength: _maxCommentLength,
|
||||
textAlignVertical: TextAlignVertical.top,
|
||||
style: const TextStyle(
|
||||
color: AppColors.cream,
|
||||
fontSize: 15,
|
||||
),
|
||||
cursorColor: AppColors.blue,
|
||||
decoration: InputDecoration(
|
||||
hintText: l10n.writeComment,
|
||||
hintStyle: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: 14,
|
||||
),
|
||||
counterText: '',
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 12,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
enabledBorder: InputBorder.none,
|
||||
focusedBorder: InputBorder.none,
|
||||
),
|
||||
onTap: signedIn ? null : _authenticateForComment,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
height: _postButtonHeight,
|
||||
child: TextButton(
|
||||
key: const Key('comment-post'),
|
||||
onPressed: canPost ? _post : null,
|
||||
style: TextButton.styleFrom(
|
||||
minimumSize: const Size(0, _postButtonHeight),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||
tapTargetSize: MaterialTapTargetSize.shrinkWrap,
|
||||
backgroundColor: hasContent
|
||||
? AppColors.blue.withValues(alpha: 0.12)
|
||||
: AppColors.raised,
|
||||
foregroundColor: AppColors.blue,
|
||||
disabledForegroundColor: AppColors.muted,
|
||||
disabledBackgroundColor: AppColors.raised,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
l10n.postComment,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _content(BuildContext context, double maxHeight) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final count = _serverCount ?? _controller.exactCount;
|
||||
@@ -277,9 +403,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
|
||||
child: _list(l10n),
|
||||
),
|
||||
if (_replyTo != null) _replyBanner(l10n, _replyTo!),
|
||||
const SizedBox(
|
||||
height: _composerHeight + _composerTopGap,
|
||||
),
|
||||
const SizedBox(height: _composerHeight + _composerTopGap),
|
||||
],
|
||||
),
|
||||
Positioned(
|
||||
|
||||
Reference in new issue
Block a user