遇到問題
在使用PinCodeField驗證碼控件的時候遇到一個問題,在Android上邊,點擊返回或者點擊鍵盤右上角的關閉鍵盤是不會觸發PinCodeField失去焦點的所以導致關閉鍵盤之後,PinCodeField一直處於獲取焦點的狀態,無法再次點擊使PinCodeField獲取焦點彈出鍵盤,所以我們要監聽鍵盤的彈出和關閉去設置相應的焦點狀態。
解決方案
第一種使用keyboard_visibility三方庫
這個開發者已經很久沒更新了,所以我把代碼拉到本地,到時候不兼容的時候方便自己修改。
使用的話以拉到本地依賴爲準,把它放到和我們的主項目同一個目錄下用下邊的方式依賴:
keyboard_visibility:
path: ../flutter_keyboard_visibility
使用:
import 'package:keyboard_visibility/keyboard_visibility.dart';
int subscribeId;
@protected
void initState() {
super.initState();
subscribeId = KeyboardVisibilityNotification().addNewListener(
onChange: (bool visible) {
print(visible);
},
);
}
//及時移除防止內存泄漏問題 "A LoginInputCodeViewModel was used after being disposed."
@override
void dispose() {
KeyboardVisibilityNotification().removeListener(subscribeId);
super.dispose();
}
第二種使用WidgetsBindingObserver & didChangeMetrics
WidgetsBindingObserver這個組件可以監聽頁面的一些生命週期,並且其中有一個回調didChangeMetrics可以監聽界面高度的變化。其中鍵盤的彈出和收起這些其實都屬於高度的變化自然也是可以監聽到的。
使用:
class _PinCodeTextFieldState extends State<PinCodeTextField> with WidgetsBindingObserver {
bool isKeyboardActived = false;
@protected
void initState() {
//根據autoFocus賦予isKeyboardActived初值,自動獲取焦點的話鍵盤是自動彈出的需要賦值爲true正好和autoFocus對應
isKeyboardActived = widget.autoFocus;
WidgetsBinding.instance.addObserver(this);
super.initState();
}
@override
void didChangeMetrics() {
super.didChangeMetrics();
WidgetsBinding.instance.addPostFrameCallback((_) {
// 當前是安卓系統並且在焦點聚焦的情況下
if (Platform.isAndroid && _focusNode.hasFocus) {
if (isKeyboardActived) {
isKeyboardActived = false;
// 使輸入框失去焦點
_focusNode.unfocus();
return;
} else {
// showToast("鍵盤彈出 false");
}
isKeyboardActived = true;
} else {
isKeyboardActived = false;
// showToast("鍵盤消失");
}
});
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
具體和PinCodeTextField結合使用在下邊:
class PinCodeTextField extends StatefulWidget {
/// If the pin code field should be autofocused or not. Default is [false]
final bool autoFocus;
/// Should pass a [FocusNode] to manage it from the parent
final FocusNode focusNode;
PinCodeTextField({
Key key,
//省略代嗎...
this.autoFocus = false,
this.focusNode,
//省略代嗎...
}) : super(key: key);
}
class _PinCodeTextFieldState extends State<PinCodeTextField>
with WidgetsBindingObserver {//重點
FocusNode _focusNode;
bool isKeyboardActived = false;
@override
void initState() {
//重點
isKeyboardActived = widget.autoFocus;
//重點
WidgetsBinding.instance.addObserver(this);
_focusNode = widget.focusNode ?? FocusNode();
_focusNode.addListener(() {
setState(() {});
});
super.initState();
}
// validating all the values
void _checkForInvalidValues() {
//省略代嗎...
}
@override
void dispose() {
_textEditingController.dispose();
_focusNode.dispose();
WidgetsBinding.instance.removeObserver(this);
super.dispose();
}
@override
Widget build(BuildContext context) {
return Stack(
//省略代嗎...
GestureDetector(
onTap: _onFocus,
//省略代嗎...
child: Container(
//省略代嗎...
),
),
],
);
}
//重點
@override
void didChangeMetrics() {
super.didChangeMetrics();
WidgetsBinding.instance.addPostFrameCallback((_) {
// 當前是安卓系統並且在焦點聚焦的情況下
if (Platform.isAndroid && _focusNode.hasFocus) {
if (isKeyboardActived) {
isKeyboardActived = false;
// 使輸入框失去焦點
_focusNode.unfocus();
return;
} else {
// showToast("鍵盤彈出 false");
}
isKeyboardActived = true;
} else {
isKeyboardActived = false;
// showToast("鍵盤消失");
}
});
}
//重點
void _onFocus() {
if (_focusNode.hasFocus) {
// showToast("點擊 hasFocus");
_focusNode.unfocus();
} else {
// showToast("點擊 noFocus");
FocusScope.of(context).requestFocus(_focusNode);
}
}
}