fix(player): 评论成功提示改黑色浮层并去除输入弹窗遮罩

This commit is contained in:
Zhou Weidong committed 2026-10-21 13:59:04 +08:00
1 parent c3e6e02e47
commit 5d459dd08a
3 files changed
+128 -54

No files matched your search

@@ -155,10 +155,28 @@ class _CommentComposerSheetState extends State<CommentComposerSheet>
final canPost = signedIn && hasContent && !widget.controller.submitting; final canPost = signedIn && hasContent && !widget.controller.submitting;
final multiline = _input.text.contains('\n'); final multiline = _input.text.contains('\n');
return AnimatedPadding( return Stack(
fit: StackFit.expand,
children: [
Positioned.fill(
child: GestureDetector(
key: const Key('comment-composer-dismiss-layer'),
behavior: HitTestBehavior.opaque,
onTap: () {
_inputFocus.unfocus();
_closeSheet();
},
),
),
Align(
alignment: Alignment.bottomCenter,
child: AnimatedPadding(
duration: const Duration(milliseconds: 200), duration: const Duration(milliseconds: 200),
curve: Curves.easeOut, curve: Curves.easeOut,
padding: EdgeInsets.only(bottom: keyboardHeight), padding: EdgeInsets.only(bottom: keyboardHeight),
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () {},
child: SizedBox( child: SizedBox(
key: const Key('comment-composer-sheet'), key: const Key('comment-composer-sheet'),
width: double.infinity, width: double.infinity,
@@ -195,7 +213,9 @@ class _CommentComposerSheetState extends State<CommentComposerSheet>
Container( Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.commentInput, color: AppColors.commentInput,
borderRadius: BorderRadius.circular(_inputRadius), borderRadius: BorderRadius.circular(
_inputRadius,
),
), ),
child: TextField( child: TextField(
key: const Key('comment-composer-input'), key: const Key('comment-composer-input'),
@@ -249,9 +269,15 @@ class _CommentComposerSheetState extends State<CommentComposerSheet>
key: const Key('comment-composer-post'), key: const Key('comment-composer-post'),
onPressed: canPost ? _submit : null, onPressed: canPost ? _submit : null,
style: TextButton.styleFrom( style: TextButton.styleFrom(
minimumSize: const Size(0, _postButtonHeight), minimumSize: const Size(
padding: const EdgeInsets.symmetric(horizontal: 16), 0,
tapTargetSize: MaterialTapTargetSize.shrinkWrap, _postButtonHeight,
),
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
tapTargetSize:
MaterialTapTargetSize.shrinkWrap,
backgroundColor: hasContent backgroundColor: hasContent
? AppColors.blue.withValues(alpha: 0.12) ? AppColors.blue.withValues(alpha: 0.12)
: AppColors.raised, : AppColors.raised,
@@ -279,6 +305,10 @@ class _CommentComposerSheetState extends State<CommentComposerSheet>
), ),
), ),
), ),
),
),
),
],
); );
} }
} }
@@ -41,7 +41,8 @@ class DramaCommentsSheet extends StatefulWidget {
class _DramaCommentsSheetState extends State<DramaCommentsSheet> { class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
late final CommentRepository _comments; late final CommentRepository _comments;
late final CommentController _controller; late final CommentController _controller;
final _messengerKey = GlobalKey<ScaffoldMessengerState>(); OverlayEntry? _successOverlay;
Timer? _successOverlayTimer;
int? _serverCount; int? _serverCount;
int _countRequest = 0; int _countRequest = 0;
@@ -60,6 +61,8 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
@override @override
void dispose() { void dispose() {
_successOverlayTimer?.cancel();
_successOverlay?.remove();
_controller.dispose(); _controller.dispose();
super.dispose(); super.dispose();
} }
@@ -88,6 +91,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
isScrollControlled: true, isScrollControlled: true,
isDismissible: true, isDismissible: true,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
barrierColor: Colors.transparent,
showDragHandle: false, showDragHandle: false,
builder: (_) => builder: (_) =>
CommentComposerSheet(controller: _controller, replyTo: replyTo), CommentComposerSheet(controller: _controller, replyTo: replyTo),
@@ -103,32 +107,19 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
} }
void _showPostSuccess() { void _showPostSuccess() {
final messenger = _messengerKey.currentState; _successOverlayTimer?.cancel();
if (messenger == null) return; _successOverlay?.remove();
final l10n = AppLocalizations.of(context)!; final l10n = AppLocalizations.of(context)!;
messenger final entry = OverlayEntry(
..hideCurrentSnackBar() builder: (_) =>
..showSnackBar( _CommentPostSuccessOverlay(message: l10n.commentPostSuccess),
SnackBar(
key: const Key('comment-post-success-snackbar'),
content: Text(
l10n.commentPostSuccess,
style: const TextStyle(
color: Colors.white,
fontSize: 14,
fontWeight: FontWeight.w500,
),
),
behavior: SnackBarBehavior.floating,
duration: const Duration(milliseconds: 1500),
backgroundColor: AppColors.ink,
elevation: 6,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(10),
),
margin: EdgeInsets.fromLTRB(16, 0, 16, _sheetHeight(context) + 12),
),
); );
_successOverlay = entry;
Overlay.of(context, rootOverlay: true).insert(entry);
_successOverlayTimer = Timer(const Duration(milliseconds: 1200), () {
if (entry.mounted) entry.remove();
if (identical(_successOverlay, entry)) _successOverlay = null;
});
} }
double _sheetHeight(BuildContext context) { double _sheetHeight(BuildContext context) {
@@ -142,12 +133,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final sheetHeight = _sheetHeight(context); final sheetHeight = _sheetHeight(context);
return ScaffoldMessenger( return Stack(
key: _messengerKey,
child: Scaffold(
backgroundColor: Colors.transparent,
resizeToAvoidBottomInset: false,
body: Stack(
fit: StackFit.expand, fit: StackFit.expand,
children: [ children: [
Positioned.fill( Positioned.fill(
@@ -180,9 +166,7 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
listenable: _controller, listenable: _controller,
builder: (context, _) { builder: (context, _) {
return Padding( return Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: 16),
horizontal: 16,
),
child: _content(context), child: _content(context),
); );
}, },
@@ -194,8 +178,6 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
), ),
), ),
], ],
),
),
); );
} }
@@ -372,6 +354,44 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
} }
} }
class _CommentPostSuccessOverlay extends StatelessWidget {
const _CommentPostSuccessOverlay({required this.message});
final String message;
@override
Widget build(BuildContext context) {
return Positioned.fill(
child: IgnorePointer(
child: SafeArea(
child: Align(
alignment: const Alignment(0, 0.55),
child: Semantics(
liveRegion: true,
child: Container(
key: const Key('comment-post-success-snackbar'),
padding: const EdgeInsets.symmetric(
horizontal: 16,
vertical: 10,
),
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.82),
borderRadius: BorderRadius.circular(8),
),
child: Text(
message,
textAlign: TextAlign.center,
style: const TextStyle(color: Colors.white, fontSize: 14),
),
),
),
),
),
),
);
}
}
class _CommentThread extends StatelessWidget { class _CommentThread extends StatelessWidget {
const _CommentThread({ const _CommentThread({
required this.comment, required this.comment,
+37 -13
View File
@@ -291,21 +291,32 @@ void main() {
await tester.pump(const Duration(milliseconds: 500)); await tester.pump(const Duration(milliseconds: 500));
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing); expect(find.byKey(const Key('comment-composer-sheet')), findsNothing);
final successSnackBar = find.byKey( final successOverlay = find.byKey(
const Key('comment-post-success-snackbar'), const Key('comment-post-success-snackbar'),
); );
expect(successSnackBar, findsOneWidget); expect(successOverlay, findsOneWidget);
expect(find.text('发表成功'), findsOneWidget); expect(find.text('发表成功'), findsOneWidget);
final snackBar = tester.widget<SnackBar>(successSnackBar); expect(find.byType(SnackBar), findsNothing);
expect(snackBar.action, isNull); final capsule = tester.widget<Container>(successOverlay);
expect(snackBar.behavior, SnackBarBehavior.floating); final decoration = capsule.decoration! as BoxDecoration;
expect(snackBar.duration, const Duration(milliseconds: 1500)); expect(decoration.color, Colors.black.withValues(alpha: 0.82));
expect(decoration.borderRadius, BorderRadius.circular(8));
expect( expect(
tester.getRect(find.text('发表成功')).bottom, capsule.padding,
lessThanOrEqualTo( const EdgeInsets.symmetric(horizontal: 16, vertical: 10),
tester.getRect(find.byKey(const Key('player-comments-sheet'))).top,
),
); );
final successText = tester.widget<Text>(find.text('发表成功'));
expect(successText.style?.color, Colors.white);
expect(successText.style?.fontSize, 14);
final placeholderRect = tester.getRect(
find.byKey(const Key('comment-input-placeholder')),
);
expect(
tester.getRect(successOverlay).bottom,
lessThan(placeholderRect.top),
);
await tester.pump(const Duration(milliseconds: 1200));
expect(successOverlay, findsNothing);
expect(find.byKey(const Key('comment-list')), findsOneWidget); expect(find.byKey(const Key('comment-list')), findsOneWidget);
expect(find.byKey(const Key('comment-count-label')), findsOneWidget); expect(find.byKey(const Key('comment-count-label')), findsOneWidget);
expect(find.byKey(const Key('comment-input-placeholder')), findsOneWidget); expect(find.byKey(const Key('comment-input-placeholder')), findsOneWidget);
@@ -341,6 +352,10 @@ void main() {
final composer = find.byKey(const Key('comment-composer-sheet')); final composer = find.byKey(const Key('comment-composer-sheet'));
final composerInput = find.byKey(const Key('comment-composer-input')); final composerInput = find.byKey(const Key('comment-composer-input'));
expect(composer, findsOneWidget); expect(composer, findsOneWidget);
expect(
ModalRoute.of(tester.element(composer))?.barrierColor,
Colors.transparent,
);
final compactHeight = tester.getSize(composer).height; final compactHeight = tester.getSize(composer).height;
expect(compactHeight, inInclusiveRange(100, 150)); expect(compactHeight, inInclusiveRange(100, 150));
expect(tester.widget<TextField>(composerInput).focusNode?.hasFocus, isTrue); expect(tester.widget<TextField>(composerInput).focusNode?.hasFocus, isTrue);
@@ -406,12 +421,21 @@ void main() {
await tester.tap(find.byKey(const Key('comment-input-placeholder'))); await tester.tap(find.byKey(const Key('comment-input-placeholder')));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(sheet, findsOneWidget); expect(sheet, findsOneWidget);
expect(find.byKey(const Key('comment-composer-sheet')), findsOneWidget); final composer = find.byKey(const Key('comment-composer-sheet'));
expect(composer, findsOneWidget);
expect(
find.byKey(const Key('comment-composer-dismiss-layer')),
findsOneWidget,
);
await tester.tapAt(const Offset(20, 20)); final sheetRect = tester.getRect(sheet);
final composerRect = tester.getRect(composer);
await tester.tapAt(
Offset(sheetRect.center.dx, (sheetRect.top + composerRect.top) / 2),
);
await tester.pumpAndSettle(); await tester.pumpAndSettle();
expect(sheet, findsOneWidget); expect(sheet, findsOneWidget);
expect(find.byKey(const Key('comment-composer-sheet')), findsNothing); expect(composer, findsNothing);
await tester.tapAt(const Offset(20, 20)); await tester.tapAt(const Offset(20, 20));
await tester.pumpAndSettle(); await tester.pumpAndSettle();