Flutter TextField輸入框如何優(yōu)雅的禁止彈出軟鍵盤

在學(xué)習(xí)Flutter的過程中經(jīng)常會(huì)遇到一些小坑,由于目前Flutter還不夠完善,有些問題還是要我們自己想辦法解決的,下面就分享一個(gè)關(guān)于TextField的問題,如有錯(cuò)誤,請(qǐng)指出。

首先先上個(gè)被軟鍵盤遮蔽的問題

TextField彈出系統(tǒng)軟鍵盤時(shí),如果被遮蔽是不會(huì)自動(dòng)上移的。這對(duì)于開發(fā)慣了原生的同學(xué)來說簡(jiǎn)直是不能原諒的,網(wǎng)上有一些很復(fù)雜的自定義插件方案可以完美解決,其實(shí)我們偷懶的話只需要在最外層嵌套一個(gè)ScrollView組件里就可以了,無論是ListView還是SingleChildScrollView,這樣基本解決了被遮蔽的問題,如果還需要控制滑動(dòng)多少則需要對(duì)ScrollView的controller進(jìn)行設(shè)置了。

開胃菜吃完了,現(xiàn)在進(jìn)入正餐

在一些業(yè)務(wù)場(chǎng)景里我們有時(shí)候是不希望彈出系統(tǒng)的軟鍵盤的,比如輸入密碼、搜索等情況,但是Flutter并沒有給我們提供這樣的屬性,那我們要如何做到呢?
首先,我們先觀察一下TextField是怎么彈出鍵盤的。從最直觀的手機(jī)界面看,當(dāng)TextField獲得焦點(diǎn)時(shí)彈出了,失去焦點(diǎn)時(shí)消失了,而且彈出的鍵盤是系統(tǒng)原生的鍵盤。現(xiàn)在我們猜測(cè)一下,他是通過焦點(diǎn)監(jiān)聽來決定系統(tǒng)鍵盤狀態(tài)的,帶著這2個(gè)猜想我們看看TextField的代碼是怎么寫的。
由于源碼比較多,我就直接上圖了


下面源碼的分析都是基于1.2.1版本

隨便定義一個(gè)TextField組件點(diǎn)進(jìn)去,來到text_field.dart文件中,構(gòu)造函數(shù)中一大串設(shè)置屬性,不急,先看下這個(gè)類有什么繼承,實(shí)現(xiàn)什么的


1

哦,很好,很簡(jiǎn)單,那么我們可以往下找他的_state類了


2

繼續(xù)點(diǎn)進(jìn)_TextFieldState類中,一路翻下來,看到了很多私有方法,還有生命周期的重寫,我們先不關(guān)心里面的邏輯,先找到最重要的build方法


3

嗯,很長(zhǎng),在return里找到最里層的child


4

點(diǎn)過去

5

這里他建了一個(gè)EditableText類,有點(diǎn)熟悉的名字了,點(diǎn)過去,來到editable_text.dart文件中


6

focusNode通常是flutter里處理焦點(diǎn)的,是不是和我們觀察界面時(shí)總結(jié)的焦點(diǎn)監(jiān)聽有點(diǎn)聯(lián)系呢?帶著疑問,我們追蹤一下這個(gè)屬性,最后我們?cè)赺state類里找到了


7
PS:這里插隊(duì)一條小知識(shí)

在追蹤的過程里發(fā)現(xiàn)了這個(gè),回過去看了一下

class EditableTextState extends State<EditableText> with AutomaticKeepAliveClientMixin<EditableText>, WidgetsBindingObserver, TickerProviderStateMixin<EditableText> implements TextInputClient, TextSelectionDelegate {}

這是mixin了AutomaticKeepAliveClientMixin需要重寫的方法,這樣用來保證輸入框在有焦點(diǎn)時(shí)狀態(tài)不會(huì)被UI刷新時(shí)重置,是不是聯(lián)想到了TabBarView在切換時(shí)又被重置的問題了~方法已經(jīng)教你了,剩下的你懂的

繼續(xù)看焦點(diǎn)的問題,從widget類中拿到了focusNode對(duì)象,進(jìn)行了焦點(diǎn)監(jiān)聽,通過_handleFocusChanged方法實(shí)現(xiàn)


8

這么接地氣的方法名,大概猜出來了吧,繼續(xù)點(diǎn)...


9

當(dāng)獲得焦點(diǎn)的時(shí)候open當(dāng)沒焦點(diǎn)時(shí)close,我們是想阻止他在獲得焦點(diǎn)時(shí)不彈鍵盤,所以我們只需要看open這個(gè)方法
10

上面一堆我們也不知道啥意思,我們也看不懂,但是他最后都會(huì)調(diào)用show方法,是不是show鍵盤呢??我們過去看看

/// An interface for interacting with a text input control.
///
/// See also:
///
///  * [TextInput.attach]
class TextInputConnection {
  TextInputConnection._(this._client)
    : assert(_client != null),
      _id = _nextId++;

  static int _nextId = 1;
  final int _id;

  final TextInputClient _client;

  /// Whether this connection is currently interacting with the text input control.
  bool get attached => _clientHandler._currentConnection == this;

  /// Requests that the text input control become visible.
  void show() {
    assert(attached);
    SystemChannels.textInput.invokeMethod<void>('TextInput.show');
  }

  /// Requests that the text input control change its internal state to match the given state.
  void setEditingState(TextEditingValue value) {
    assert(attached);
    SystemChannels.textInput.invokeMethod<void>(
      'TextInput.setEditingState',
      value.toJSON(),
    );
  }

  /// Stop interacting with the text input control.
  ///
  /// After calling this method, the text input control might disappear if no
  /// other client attaches to it within this animation frame.
  void close() {
    if (attached) {
      SystemChannels.textInput.invokeMethod<void>('TextInput.clearClient');
      _clientHandler
        .._currentConnection = null
        .._scheduleHide();
    }
    assert(!attached);
  }
}

這是text_input.dart文件里的TextInputConnection類,一共就這么多內(nèi)容,從我們點(diǎn)過來的show方法可以看出他調(diào)用了SystemChannels.textInput里的方法,看到System是不是菊花一緊,這是要和系統(tǒng)打交道了嗎??點(diǎn)過去看看吧


11

這個(gè)方法在platform_channel.dart文件下,看過原生混合開發(fā)的同學(xué)大概都猜出了,這是在和原生系統(tǒng)進(jìn)行交互了,到這一步我們觀察UI表現(xiàn)時(shí)的2個(gè)猜想都被證實(shí)了。
看到這里已經(jīng)有同學(xué)知道怎么做了,要禁止彈出鍵盤只需要把show方法里的調(diào)用給禁掉就好啦
和java不同的是,flutter的源碼是可以直接修改的,你在源碼文件中隨便敲個(gè)字就會(huì)彈出


選擇一個(gè)你想要的方式就可以直接修改源碼了,我們現(xiàn)在選擇第一個(gè),再把show方法里的實(shí)現(xiàn)屏蔽掉來驗(yàn)證下是不是真的可以不彈出鍵盤

  /// Requests that the text input control become visible.
  void show() {
    assert(attached);
//    SystemChannels.textInput.invokeMethod<void>('TextInput.show');
  }

測(cè)試發(fā)現(xiàn)真的不彈了!但是我不是想讓所有輸入框都不彈鍵盤啊,我的登陸框怎么辦??難道我要把這么多文件復(fù)制出來自定義一個(gè)輸入框?qū)iT用來不彈??當(dāng)然這是可以的,但是這一點(diǎn)也不優(yōu)雅!

通過上面這么多源碼的分析,我們大概已經(jīng)知道了flutter是怎么控制的了,那么我們可以自己定義一個(gè)屬性給TextField,按著上面的流程一步一步傳遞過去,用來控制最終的show方法,我們想彈就彈,不想彈就禁止,一勞永逸。

最后的調(diào)用代碼:

void show({bool needshow = true}) {
    assert(attached);
    if(needshow){
    SystemChannels.textInput.invokeMethod<void>('TextInput.show');}else {close();}
  }

注意需要在else的邏輯里調(diào)用原本類中的close()方法,這樣就算一個(gè)要彈鍵盤,一個(gè)不要彈鍵盤的輸入框在一個(gè)界面也不會(huì)出問題啦

自學(xué)flutter的路很坎坷,如果大家有更好的建議,歡迎指出。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容