Merge pull request #161 from flixcoo/bug/159-keyboard-behaviour-correction

Keyboard behaviour correction
This commit is contained in:
2025-08-17 22:19:28 +02:00
committed by GitHub
2 changed files with 278 additions and 238 deletions

View File

@@ -42,6 +42,8 @@ class _RoundViewState extends State<RoundView> {
(index) => FocusNode(), (index) => FocusNode(),
); );
late List<GlobalKey> _textFieldKeys;
@override @override
void initState() { void initState() {
print('=== Runde ${widget.roundNumber} geöffnet ==='); print('=== Runde ${widget.roundNumber} geöffnet ===');
@@ -62,6 +64,11 @@ class _RoundViewState extends State<RoundView> {
gameSession.roundList[widget.roundNumber - 1].kamikazePlayerIndex; gameSession.roundList[widget.roundNumber - 1].kamikazePlayerIndex;
} }
_textFieldKeys = List.generate(
widget.gameSession.players.length,
(index) => GlobalKey(),
);
super.initState(); super.initState();
} }
@@ -75,7 +82,6 @@ class _RoundViewState extends State<RoundView> {
return CupertinoPageScaffold( return CupertinoPageScaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
navigationBar: CupertinoNavigationBar( navigationBar: CupertinoNavigationBar(
transitionBetweenRoutes: true,
leading: CupertinoButton( leading: CupertinoButton(
padding: EdgeInsets.zero, padding: EdgeInsets.zero,
onPressed: () => { onPressed: () => {
@@ -91,11 +97,11 @@ class _RoundViewState extends State<RoundView> {
CupertinoIcons.lock, CupertinoIcons.lock,
size: 25, size: 25,
))), ))),
child: Stack( child: Column(
children: [ children: [
Positioned.fill( Expanded(
child: SingleChildScrollView( child: SingleChildScrollView(
padding: EdgeInsets.only(bottom: 100 + bottomInset), padding: EdgeInsets.only(bottom: 20 + bottomInset),
child: SafeArea( child: SafeArea(
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.center, crossAxisAlignment: CrossAxisAlignment.center,
@@ -195,6 +201,7 @@ class _RoundViewState extends State<RoundView> {
' ${AppLocalizations.of(context).points}'), ' ${AppLocalizations.of(context).points}'),
trailing: SizedBox( trailing: SizedBox(
width: 100, width: 100,
key: _textFieldKeys[originalIndex],
child: CupertinoTextField( child: CupertinoTextField(
maxLength: 3, maxLength: 3,
focusNode: _focusNodeList[originalIndex], focusNode: _focusNodeList[originalIndex],
@@ -248,11 +255,8 @@ class _RoundViewState extends State<RoundView> {
), ),
), ),
), ),
Positioned( KeyboardVisibilityBuilder(
left: 0, builder: (context, visible) {
right: 0,
bottom: bottomInset,
child: KeyboardVisibilityBuilder(builder: (context, visible) {
if (!visible) { if (!visible) {
return Container( return Container(
height: 80, height: 80,
@@ -284,8 +288,8 @@ class _RoundViewState extends State<RoundView> {
} else { } else {
return const SizedBox.shrink(); return const SizedBox.shrink();
} }
}), },
) ),
], ],
), ),
); );
@@ -371,8 +375,21 @@ class _RoundViewState extends State<RoundView> {
final currentPos = originalIndices.indexOf(index); final currentPos = originalIndices.indexOf(index);
if (currentPos < originalIndices.length - 1) { if (currentPos < originalIndices.length - 1) {
final nextIndex = originalIndices[currentPos + 1];
FocusScope.of(context) FocusScope.of(context)
.requestFocus(_focusNodeList[originalIndices[currentPos + 1]]); .requestFocus(_focusNodeList[originalIndices[currentPos + 1]]);
final scrollContext = _textFieldKeys[nextIndex].currentContext;
if (scrollContext != null) {
WidgetsBinding.instance.addPostFrameCallback((_) {
Scrollable.ensureVisible(
scrollContext,
duration: const Duration(milliseconds: 300),
curve: Curves.easeOut,
alignment: 0.55,
);
});
}
} else { } else {
_focusNodeList[index].unfocus(); _focusNodeList[index].unfocus();
} }

View File

@@ -77,7 +77,15 @@ class _CreateGameViewState extends State<CreateGameView> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return CupertinoPageScaffold( return PopScope(
canPop: false,
onPopInvokedWithResult: (bool didPop, dynamic result) async {
if (!didPop) {
await _keyboardDelay();
if (context.mounted) Navigator.pop(context);
}
},
child: CupertinoPageScaffold(
resizeToAvoidBottomInset: false, resizeToAvoidBottomInset: false,
navigationBar: CupertinoNavigationBar( navigationBar: CupertinoNavigationBar(
previousPageTitle: AppLocalizations.of(context).games, previousPageTitle: AppLocalizations.of(context).games,
@@ -129,6 +137,9 @@ class _CreateGameViewState extends State<CreateGameView> {
], ],
), ),
onTap: () async { onTap: () async {
await _keyboardDelay();
if (context.mounted) {
final selectedMode = await Navigator.push( final selectedMode = await Navigator.push(
context, context,
CupertinoPageRoute( CupertinoPageRoute(
@@ -142,6 +153,7 @@ class _CreateGameViewState extends State<CreateGameView> {
setState(() { setState(() {
gameMode = selectedMode ?? gameMode; gameMode = selectedMode ?? gameMode;
}); });
}
}, },
), ),
), ),
@@ -197,12 +209,13 @@ class _CreateGameViewState extends State<CreateGameView> {
'${AppLocalizations.of(context).player} ${index + 1}', '${AppLocalizations.of(context).player} ${index + 1}',
padding: const EdgeInsets.all(12), padding: const EdgeInsets.all(12),
decoration: const BoxDecoration(), decoration: const BoxDecoration(),
textInputAction: textInputAction: index + 1 <
index + 1 < _playerNameTextControllers.length _playerNameTextControllers.length
? TextInputAction.next ? TextInputAction.next
: TextInputAction.done, : TextInputAction.done,
onSubmitted: (_) { onSubmitted: (_) {
if (index + 1 < _playerNameFocusNodes.length) { if (index + 1 <
_playerNameFocusNodes.length) {
_playerNameFocusNodes[index + 1] _playerNameFocusNodes[index + 1]
.requestFocus(); .requestFocus();
} else { } else {
@@ -268,13 +281,15 @@ class _CreateGameViewState extends State<CreateGameView> {
], ],
), ),
onPressed: () { onPressed: () {
if (_playerNameTextControllers.length < maxPlayers) { if (_playerNameTextControllers.length <
maxPlayers) {
setState(() { setState(() {
_playerNameTextControllers _playerNameTextControllers
.add(TextEditingController()); .add(TextEditingController());
_playerNameFocusNodes.add(FocusNode()); _playerNameFocusNodes.add(FocusNode());
}); });
WidgetsBinding.instance.addPostFrameCallback((_) { WidgetsBinding.instance
.addPostFrameCallback((_) {
_playerNameFocusNodes.last.requestFocus(); _playerNameFocusNodes.last.requestFocus();
}); });
} else { } else {
@@ -296,13 +311,9 @@ class _CreateGameViewState extends State<CreateGameView> {
color: CustomTheme.primaryColor, color: CustomTheme.primaryColor,
), ),
), ),
onPressed: () { onPressed: () async {
FocusScope.of(context).unfocus(); await _keyboardDelay();
Future.delayed(
const Duration(
milliseconds: Constants.kKeyboardDelay), () {
_checkAllGameAttributes(); _checkAllGameAttributes();
});
}, },
), ),
), ),
@@ -319,7 +330,7 @@ class _CreateGameViewState extends State<CreateGameView> {
}) })
], ],
), ),
))); ))));
} }
/// Returns a widget that displays the currently selected game mode in the View. /// Returns a widget that displays the currently selected game mode in the View.
@@ -460,6 +471,18 @@ class _CreateGameViewState extends State<CreateGameView> {
); );
} }
/// If the keyboard is visible, this method will unfocus the current text field
/// to prevent the keyboard from interfering with the navigation bar.
Future<void> _keyboardDelay() async {
if (!KeyboardVisibilityController().isVisible) {
return;
} else {
FocusScope.of(context).unfocus();
await Future.delayed(
const Duration(milliseconds: Constants.kKeyboardDelay));
}
}
@override @override
void dispose() { void dispose() {
_gameTitleTextController.dispose(); _gameTitleTextController.dispose();