天天看點

flutter 鍵盤遮擋輸入框問題_Flutter - 5 : 鍵盤遮擋輸入框問題,以及阻止系統鍵盤彈出...

Flutter - 5 : 鍵盤遮擋輸入框問題,以及阻止系統鍵盤彈出

Flutter中的輸入框控件TextField竟然在被鍵盤遮擋的時候沒有上移的行為,真是坑爹。

中間參考過某位大神的解決辦法,然而沒成功,可能是我看的不夠仔細,用的方法不對。

連結如下:點選跳轉某位大神的解決辦法

沒辦法,隻能自己解決,暴力的解決。效果如下圖:

解決辦法:

如果系統沒給向上滑動,那就自己控制它向上滑動,SingleChildScrollView可以随意控制想要滑動到的位置。

缺點就是麻煩。

1.

最好用的辦法是寫一個插件,在原生部分通過布局的變化來确定鍵盤的目前狀态,然後在狀态變化時通過插件通知flutter,這個辦法一般不會出錯。目前我也是在用這種辦法,雖然麻煩,不出錯就好,例子就不寫了,網上在原生端判斷鍵盤狀态的代碼很多,結合插件使用就好了。

2.

次級辦法是在flutter中判斷,然而壞處是,隻能通過監聽螢幕的矩陣回調來判斷,有些時候,會失效,甚至誤判,是以,不是很推薦這種方法。下面的例子是矩陣回調的例子,已确認過有些時候會發生誤判。

第一步:

Flutter中,焦點的擷取依賴于FocusNode這個類,這個類中提供了一系列與焦點相關的方法,尤其是其中的consumeKeyboardToken方法,傳回了目前系統鍵盤的狀态,如果想要不讓系統鍵盤彈出來,可以重寫這個方法,當然,裡面還是有坑。

是以首先需要寫一個繼承類:

class ScrollFocusNode extends FocusNode {

final bool _useSystemKeyBoard; // 是否使用系統鍵盤

final double _moveValue; // 上移距離

ScrollFocusNode(this._useSystemKeyBoard, this._moveValue);

@override

bool consumeKeyboardToken() {

if (_useSystemKeyBoard) {

return super.consumeKeyboardToken();

}

return false;

}

double get moveValue => _moveValue;

bool get useSystemKeyBoard => _useSystemKeyBoard;

}

第二步:

WidgetsBindingObserver 這個類,提供了很多回調的方法,這裡使用到的是對螢幕矩陣的回調(開始提到的參考文章中對此有相關介紹以及文檔),當然如果沒有用系統的鍵盤,也就沒有必要加它了。滾動的位置由傳入的focusNode所帶的參數來确定,直接繼承之後實作相關方法就行了。

abstract class BoardWidget extends State

with WidgetsBindingObserver {

final ScrollController _controller = ScrollController();

ScrollFocusNode _focusNode;

double _currentPosition = 0.0;

List initChild();

void bindNewInputer(ScrollFocusNode focusNode) {

_focusNode = focusNode;

_animateUp();

}

@override

void initState() {

super.initState();

WidgetsBinding.instance.addObserver(this);

}

@override

void dispose() {

super.dispose();

_controller.dispose();

WidgetsBinding.instance.removeObserver(this);

}

// 向上滾動

void _animateUp() {

_controller

.animateTo(_focusNode.moveValue,

duration: Duration(milliseconds: 250), curve: Curves.easeOut)

.then((Null) {

_currentPosition = _controller.offset;

});

}

// 向下滾動

void _animateDown() {

_controller

.animateTo(0.0,

duration: Duration(milliseconds: 250), curve: Curves.easeOut)

.then((Null) {

_currentPosition = 0.0;

});

}

@override

Widget build(BuildContext context) {

return Scaffold(

backgroundColor: Colors.white,

body: SingleChildScrollView(

controller: _controller,

physics: NeverScrollableScrollPhysics(),

child: Column(

children: initChild()..add(SizedBox(height: 400.0)),

),

),

);

}

// 使用系統鍵盤 ---> 矩陣變換 ---> 傳回原位置

@override

void didChangeMetrics() {

if (_currentPosition != 0.0) {

_focusNode.unfocus(); // 如果不加,收起鍵盤再點選會預設鍵盤還在。

_animateDown();

}

}

}

第三步:

使用的例子,在initChild()中傳回想要實作的布局,在TextField的點選事件onTap中傳入目前TextField綁定的ScrollFocusNode就能實作效果了,不過需要先确定滾動的距離。

class TestPage extends StatefulWidget {

@override

State createState() {

return _TestPageState();

}

}

class _TestPageState extends BoardWidget {

final bool _useSystemKeyBoard = true;

final TextStyle textStyle = TextStyle(

fontFamily: "hwxw",

fontSize: 20.0,

letterSpacing: 1.0,

fontWeight: FontWeight.bold,

fontStyle: FontStyle.normal,

color: Colors.black87);

final TextStyle lableStyle = TextStyle(

fontFamily: "hwxw",

fontSize: 20.0,

letterSpacing: 16.0,

fontWeight: FontWeight.bold,

fontStyle: FontStyle.normal);

final TextStyle helperStyle = TextStyle(

fontFamily: "hwxw", fontSize: 12.0, fontStyle: FontStyle.normal);

ScrollFocusNode _userNameFocusNode;

ScrollFocusNode _passWordFocusNode;

@override

void initState() {

super.initState();

_userNameFocusNode = ScrollFocusNode(_useSystemKeyBoard, 120.0); // 第二個參數是向上滾動的距離

_passWordFocusNode = ScrollFocusNode(_useSystemKeyBoard, 180.0); // 第二個參數是向上滾動的距離

}

@override

List initChild() {

return [

Padding(

padding: EdgeInsets.only(top: 350.0, left: 50.0, right: 50.0),

child: TextField(

focusNode: _userNameFocusNode,

autofocus: false,

maxLength: 12,

maxLines: 1,

style: textStyle,

decoration: InputDecoration(

contentPadding: EdgeInsets.only(top: 16.0, bottom: 16.0),

border: OutlineInputBorder(),

labelText: "賬号",

labelStyle: lableStyle,

helperStyle: helperStyle,

prefixIcon: Icon(Icons.account_box, size: 24.0),

),

onTap: () {

// 點選時綁定目前focusNode

bindNewInputer(_userNameFocusNode);

},

),

),

Padding(

padding: EdgeInsets.only(top: 20.0, left: 50.0, right: 50.0),

child: TextField(

obscureText: true,

focusNode: _passWordFocusNode,

autofocus: false,

maxLength: 12,

maxLines: 1,

style: textStyle,

decoration: InputDecoration(

contentPadding: EdgeInsets.only(top: 16.0, bottom: 16.0),

border: OutlineInputBorder(),

labelText: "密碼",

labelStyle: lableStyle,

helperStyle: helperStyle,

prefixIcon: Icon(Icons.https, size: 24.0),

),

onTap: () {

// 點選時綁定目前focusNode

bindNewInputer(_passWordFocusNode);

},

),

),

];

}

}

最後,阻止系統鍵盤彈出:

flutter的鍵盤是通過系統插件與原生通信,然後調起的,是以組織系統鍵盤的最簡單辦法就是,注釋掉調用代碼,如下:

修改flutter\packages\flutter\lib\src\services\text_input.dart路徑下的這個檔案的TextInputConnection方法下的show方法,注掉SystemChannels.textInput.invokeMethod(‘TextInput.show’);這一行就行了,這是調起系統鍵盤的系統通信頻道調用方法。

當然,這樣直接修改源代碼的後果就是,所有的textfield都不會彈出鍵盤了。

如果不彈系統鍵盤,那隻能自定義一個,适用于某些特殊狀況。

本集完!