fix(player): 评论成功提示改黑色浮层并去除输入弹窗遮罩
This commit is contained in:
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,
|
||||||
|
|||||||
@@ -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();
|
||||||
|
|||||||
Reference in new issue
Block a user