fix(comment): 修复 iOS 评论输入弹窗键盘避让
This commit is contained in:
1 parent
dfc33027d8
commit
349ea12404
3 files changed
+222
-121
No files matched your search
@@ -3,6 +3,7 @@
|
||||
import 'dart:async';
|
||||
import 'dart:math' as math;
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -54,6 +55,9 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
bool _useChannelInset = false;
|
||||
bool _keyboardVisible = false;
|
||||
|
||||
bool get _isAndroid =>
|
||||
!kIsWeb && defaultTargetPlatform == TargetPlatform.android;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
@@ -66,6 +70,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
@override
|
||||
void didChangeDependencies() {
|
||||
super.didChangeDependencies();
|
||||
if (!_isAndroid) return;
|
||||
if (!_pinnedInitialized) {
|
||||
_pinnedInitialized = true;
|
||||
_pinnedKeyboardInset =
|
||||
@@ -75,10 +80,12 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
}
|
||||
|
||||
Future<void> _prepareInput() async {
|
||||
await _keyboardInset.start();
|
||||
if (!mounted) return;
|
||||
_insetBaseline = _keyboardInset.value;
|
||||
_insetReady = true;
|
||||
if (_isAndroid) {
|
||||
await _keyboardInset.start();
|
||||
if (!mounted) return;
|
||||
_insetBaseline = _keyboardInset.value;
|
||||
_insetReady = true;
|
||||
}
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
_inputFocus.requestFocus();
|
||||
@@ -112,7 +119,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
}
|
||||
|
||||
void _onKeyboardInsetChanged() {
|
||||
if (!mounted || !_insetReady) return;
|
||||
if (!_isAndroid || !mounted || !_insetReady) return;
|
||||
if (_keyboardInset.value != _insetBaseline) {
|
||||
_useChannelInset = true;
|
||||
}
|
||||
@@ -121,6 +128,9 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
|
||||
double _keyboardHeight(BuildContext context) {
|
||||
final viewInset = MediaQuery.viewInsetsOf(context).bottom;
|
||||
if (!_isAndroid) {
|
||||
return _normalizeKeyboardInset(viewInset) ?? 0;
|
||||
}
|
||||
final channelInset = _useChannelInset
|
||||
? _keyboardInset.value / MediaQuery.devicePixelRatioOf(context)
|
||||
: 0.0;
|
||||
@@ -134,6 +144,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
}
|
||||
|
||||
void _trackKeyboardInset(double inset) {
|
||||
if (!_isAndroid) return;
|
||||
final visible = inset > 0;
|
||||
if (visible == _keyboardVisible) return;
|
||||
_keyboardVisible = visible;
|
||||
@@ -147,6 +158,7 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
}
|
||||
|
||||
void _captureSettledKeyboardInset() {
|
||||
if (!_isAndroid) return;
|
||||
_keyboardSettleTimer = null;
|
||||
if (!mounted) return;
|
||||
final inset = _keyboardHeight(context);
|
||||
@@ -193,6 +205,109 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
final hasContent = _input.text.trim().isNotEmpty;
|
||||
final canPost = signedIn && hasContent && !widget.controller.submitting;
|
||||
final multiline = _input.text.contains('\n');
|
||||
final bottomInset = _isAndroid
|
||||
? _pinnedKeyboardInset
|
||||
: _normalizeKeyboardInset(MediaQuery.viewInsetsOf(context).bottom) ?? 0;
|
||||
|
||||
final content = Padding(
|
||||
key: const Key('comment-composer-sheet'),
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
|
||||
child: TextFieldTapRegion(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (widget.replyTo != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
l10n.replyingTo(
|
||||
widget.replyTo!.nickname.isEmpty
|
||||
? l10n.anonymousUser
|
||||
: widget.replyTo!.nickname,
|
||||
),
|
||||
style: const TextStyle(color: AppColors.muted, fontSize: 12),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.commentInput,
|
||||
borderRadius: BorderRadius.circular(_inputRadius),
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key('comment-composer-input'),
|
||||
controller: _input,
|
||||
focusNode: _inputFocus,
|
||||
readOnly: !signedIn,
|
||||
canRequestFocus: signedIn,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
maxLength: _maxCommentLength,
|
||||
textAlignVertical: multiline
|
||||
? TextAlignVertical.top
|
||||
: TextAlignVertical.center,
|
||||
style: TextStyle(
|
||||
color: AppColors.cream,
|
||||
fontSize: 15,
|
||||
height: multiline ? 1.4 : null,
|
||||
),
|
||||
cursorColor: AppColors.blue,
|
||||
decoration: InputDecoration(
|
||||
hintText: signedIn
|
||||
? l10n.writeComment
|
||||
: l10n.commentLoginHint,
|
||||
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 : _authenticate,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
height: _postButtonHeight,
|
||||
child: TextButton(
|
||||
key: const Key('comment-composer-post'),
|
||||
onPressed: canPost ? _submit : 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
return FocusScope(
|
||||
key: const Key('comment-composer-focus-scope'),
|
||||
@@ -222,122 +337,14 @@ class _CommentComposerSheetState extends State<CommentComposerSheet> {
|
||||
borderRadius: const BorderRadius.vertical(
|
||||
top: Radius.circular(_sheetRadius),
|
||||
),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
bottom: false,
|
||||
child: Padding(
|
||||
padding: EdgeInsets.only(bottom: _pinnedKeyboardInset),
|
||||
child: Padding(
|
||||
key: const Key('comment-composer-sheet'),
|
||||
padding: const EdgeInsets.fromLTRB(16, 12, 16, 12),
|
||||
child: TextFieldTapRegion(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
if (widget.replyTo != null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: Text(
|
||||
l10n.replyingTo(
|
||||
widget.replyTo!.nickname.isEmpty
|
||||
? l10n.anonymousUser
|
||||
: widget.replyTo!.nickname,
|
||||
),
|
||||
style: const TextStyle(
|
||||
color: AppColors.muted,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.commentInput,
|
||||
borderRadius: BorderRadius.circular(
|
||||
_inputRadius,
|
||||
),
|
||||
),
|
||||
child: TextField(
|
||||
key: const Key('comment-composer-input'),
|
||||
controller: _input,
|
||||
focusNode: _inputFocus,
|
||||
readOnly: !signedIn,
|
||||
canRequestFocus: signedIn,
|
||||
minLines: 1,
|
||||
maxLines: 4,
|
||||
maxLength: _maxCommentLength,
|
||||
textAlignVertical: multiline
|
||||
? TextAlignVertical.top
|
||||
: TextAlignVertical.center,
|
||||
style: TextStyle(
|
||||
color: AppColors.cream,
|
||||
fontSize: 15,
|
||||
height: multiline ? 1.4 : null,
|
||||
),
|
||||
cursorColor: AppColors.blue,
|
||||
decoration: InputDecoration(
|
||||
hintText: signedIn
|
||||
? l10n.writeComment
|
||||
: l10n.commentLoginHint,
|
||||
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 : _authenticate,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: SizedBox(
|
||||
height: _postButtonHeight,
|
||||
child: TextButton(
|
||||
key: const Key('comment-composer-post'),
|
||||
onPressed: canPost ? _submit : 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,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
child: AnimatedPadding(
|
||||
key: const Key('comment-composer-keyboard-padding'),
|
||||
duration: const Duration(milliseconds: 160),
|
||||
curve: Curves.easeOutCubic,
|
||||
padding: EdgeInsets.only(bottom: bottomInset),
|
||||
child: _isAndroid
|
||||
? content
|
||||
: SafeArea(top: false, child: content),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
Reference in new issue
Block a user