fix(player): 评论面板保持高度并两段式收起

This commit is contained in:
Zhou Weidong committed 2026-10-21 13:04:29 +08:00
1 parent 9876772d06
commit d081cff3e4
3 files changed
+128 -51

No files matched your search

+2
View File
@@ -810,6 +810,8 @@ class PlayerLogic extends ChangeNotifier {
isScrollControlled: true, isScrollControlled: true,
backgroundColor: Colors.transparent, backgroundColor: Colors.transparent,
showDragHandle: false, showDragHandle: false,
isDismissible: false,
enableDrag: false,
builder: (_) => DramaCommentsSheet(drama: drama), builder: (_) => DramaCommentsSheet(drama: drama),
); );
} finally { } finally {
@@ -178,6 +178,14 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
).showSnackBar(SnackBar(content: Text(message))); ).showSnackBar(SnackBar(content: Text(message)));
} }
void _handleDismissLayerTap() {
if (_inputFocus.hasFocus || _keyboardHeight(context) > 0) {
_inputFocus.unfocus();
return;
}
Navigator.pop(context);
}
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
final screenLimit = MediaQuery.sizeOf(context).height - 24; final screenLimit = MediaQuery.sizeOf(context).height - 24;
@@ -194,61 +202,80 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
availableHeight, availableHeight,
); );
_measureSheetWithoutKeyboard(keyboardHeight); _measureSheetWithoutKeyboard(keyboardHeight);
return AnimatedPadding( return Stack(
duration: const Duration(milliseconds: 200), fit: StackFit.expand,
curve: Curves.easeOut, children: [
padding: EdgeInsets.only(bottom: keyboardHeight), Positioned.fill(
child: GestureDetector( child: GestureDetector(
behavior: HitTestBehavior.translucent, key: const Key('player-comments-dismiss-layer'),
onTap: inputFocused ? _inputFocus.unfocus : null, behavior: HitTestBehavior.opaque,
child: SizedBox( onTap: _handleDismissLayerTap,
key: _sheetMeasureKey, ),
child: Container( ),
key: const Key('player-comments-sheet'), Align(
decoration: const BoxDecoration( alignment: Alignment.bottomCenter,
color: AppColors.card, child: AnimatedPadding(
borderRadius: BorderRadius.vertical( duration: const Duration(milliseconds: 200),
top: Radius.circular(_sheetRadius), curve: Curves.easeOut,
), padding: EdgeInsets.only(bottom: keyboardHeight),
), child: GestureDetector(
child: SafeArea( behavior: HitTestBehavior.opaque,
top: false, onTap: () {},
child: ListenableBuilder( child: SizedBox(
listenable: _controller, key: _sheetMeasureKey,
builder: (context, _) { child: Container(
final minHeight = _controller.comments.isEmpty key: const Key('player-comments-sheet'),
? (_minSheetHeight < maxHeight decoration: const BoxDecoration(
? _minSheetHeight color: AppColors.card,
: maxHeight) borderRadius: BorderRadius.vertical(
: 0.0; top: Radius.circular(_sheetRadius),
return AnimatedSize(
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
alignment: Alignment.bottomCenter,
child: ConstrainedBox(
key: const Key('comment-sheet-constraints'),
constraints: keyboardOpen
? BoxConstraints.tightFor(height: keyboardPanelHeight)
: BoxConstraints(
minHeight: minHeight,
maxHeight: maxHeight,
),
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
child: _content(
context,
expanded: expanded,
keyboardHeight: keyboardHeight,
),
),
), ),
); ),
}, child: SafeArea(
top: false,
child: ListenableBuilder(
listenable: _controller,
builder: (context, _) {
final minHeight = _controller.comments.isEmpty
? (_minSheetHeight < maxHeight
? _minSheetHeight
: maxHeight)
: 0.0;
return AnimatedSize(
duration: const Duration(milliseconds: 200),
curve: Curves.easeOut,
alignment: Alignment.bottomCenter,
child: ConstrainedBox(
key: const Key('comment-sheet-constraints'),
constraints: keyboardOpen
? BoxConstraints.tightFor(
height: keyboardPanelHeight,
)
: BoxConstraints(
minHeight: minHeight,
maxHeight: maxHeight,
),
child: Padding(
padding: const EdgeInsets.symmetric(
horizontal: 16,
),
child: _content(
context,
expanded: expanded,
keyboardHeight: keyboardHeight,
),
),
),
);
},
),
),
),
), ),
), ),
), ),
), ),
), ],
); );
} }
@@ -509,7 +536,6 @@ class _DramaCommentsSheetState extends State<DramaCommentsSheet> {
focused: focused, focused: focused,
), ),
onTap: signedIn ? null : _authenticateForComment, onTap: signedIn ? null : _authenticateForComment,
onTapOutside: (_) => _inputFocus.unfocus(),
onSubmitted: focused ? null : (_) => _post(), onSubmitted: focused ? null : (_) => _post(),
), ),
), ),
@@ -307,12 +307,14 @@ void main() {
final sheet = find.byKey(const Key('player-comments-sheet')); final sheet = find.byKey(const Key('player-comments-sheet'));
final list = find.byKey(const Key('comment-list')); final list = find.byKey(const Key('comment-list'));
final heightBeforeKeyboard = tester.getSize(sheet).height;
tester.view.viewInsets = const FakeViewPadding(bottom: 180); tester.view.viewInsets = const FakeViewPadding(bottom: 180);
await tester.pump(); await tester.pump();
await tester.pump(const Duration(milliseconds: 200)); await tester.pump(const Duration(milliseconds: 200));
expect(sheet, findsOneWidget); expect(sheet, findsOneWidget);
expect(list, findsOneWidget); expect(list, findsOneWidget);
expect(tester.getSize(sheet).height, closeTo(heightBeforeKeyboard, 0.1));
expect(find.byKey(const Key('comment-count-label')), findsOneWidget); expect(find.byKey(const Key('comment-count-label')), findsOneWidget);
final field = tester.widget<TextField>( final field = tester.widget<TextField>(
find.byKey(const Key('comment-input')), find.byKey(const Key('comment-input')),
@@ -378,6 +380,7 @@ void main() {
); );
expect(collapsedField.maxLines, 1); expect(collapsedField.maxLines, 1);
expect(tester.getSize(find.byKey(const Key('comment-input'))).height, 44); expect(tester.getSize(find.byKey(const Key('comment-input'))).height, 44);
expect(tester.getSize(sheet).height, closeTo(heightBeforeKeyboard, 0.1));
expect(tester.getSize(sheet).height, lessThan(520)); expect(tester.getSize(sheet).height, lessThan(520));
final editableAfterKeyboard = tester.state<EditableTextState>( final editableAfterKeyboard = tester.state<EditableTextState>(
find.descendant( find.descendant(
@@ -402,6 +405,52 @@ void main() {
); );
}); });
testWidgets('should_dismiss_from_outside_in_two_steps', (tester) async {
tester.view.physicalSize = const Size(390, 844);
tester.view.devicePixelRatio = 1;
addTearDown(tester.view.resetPhysicalSize);
addTearDown(tester.view.resetDevicePixelRatio);
addTearDown(tester.view.resetViewInsets);
final repository = FakeCommentRepository(
pages: [
CommentPage(
comments: [commentFixture(id: '10', content: 'A comment')],
),
],
);
await _pumpSheet(tester, repository: repository);
final sheet = find.byKey(const Key('player-comments-sheet'));
final input = find.byKey(const Key('comment-input'));
expect(
find.byKey(const Key('player-comments-dismiss-layer')),
findsOneWidget,
);
tester.view.viewInsets = const FakeViewPadding(bottom: 180);
await tester.tap(input);
await tester.pumpAndSettle();
expect(sheet, findsOneWidget);
expect(tester.widget<TextField>(input).focusNode?.hasFocus, isTrue);
await tester.tap(find.byKey(const Key('comment-list')));
await tester.pump();
expect(sheet, findsOneWidget);
await tester.tapAt(const Offset(20, 20));
await tester.pump();
expect(sheet, findsOneWidget);
expect(tester.widget<TextField>(input).focusNode?.hasFocus, isFalse);
tester.view.viewInsets = const FakeViewPadding();
await tester.pumpAndSettle();
expect(sheet, findsOneWidget);
await tester.tapAt(const Offset(20, 20));
await tester.pumpAndSettle();
expect(sheet, findsNothing);
});
testWidgets('should_use_polished_comment_sizes', (tester) async { testWidgets('should_use_polished_comment_sizes', (tester) async {
final repository = FakeCommentRepository( final repository = FakeCommentRepository(
pages: [ pages: [