最近在研究,自己試着用Flutter編寫一個文本編輯的軟件,只要和系統自帶的記事本功能差不多就行。
心想,這不是很簡單嗎?直接使用TextField組件不就行了!
記事本的界面是占全屏,沒有下划線,用代碼設置一下樣式就行。然后編寫了如下代碼:
return const Scaffold(
body: SafeArea(
child: Padding(
padding: EdgeInsets.all(12.0),
child: TextField(
decoration: InputDecoration(
border: InputBorder.none,
),
maxLines: null,
),
),
),
);

哈哈,果然簡單!😄
然后我試着輸入內容:

誒?😦第一行輸入的字上移了。
碰到問題當然先百度一下,沒有結果。然后又必應一下,還是沒有。
🙁要不就找個現成的項目看看它是怎么寫的。然后從Flutter上克隆了一個項目到本地,直沖寫作的界面代碼,然后發現了新大陸。
代碼如下:
TextField(
controller: TextEditingController.fromValue(
TextEditingValue(
// 設置內容
text: notes,
selection: TextSelection.fromPosition(
TextPosition(
affinity: TextAffinity.downstream,
offset: notes.length,
),
),
),
),
onChanged: (text) {
setState(() {
notes = text;
});
},
maxLines: null,
style: const TextStyle(),
keyboardType: TextInputType.multiline,
decoration: const InputDecoration.collapsed(hintText: '請輸入您的內容'),
)
第一次發現TextEditingController還能這樣寫,以前都是先定義再初始化。
既然知道了,那就來好好研究研究這個fromValue方法。
TextEditingController.fromValue
TextEditingController.fromValue需要傳入一個可為空的TextEditingValue對象。既然能為空,那就傳入一個null先看看:
TextField(
controller: TextEditingController.fromValue(null),
),

效果和不寫這個參數是一樣的。如果我們傳入null值的話,這個值會被Flutter替換成TextEditingValue.empty。
TextEditingController.fromValue方法可以直接傳入一個TextEditingValue對象,也可以使用TextEditingValue.fromJson方法。
TextEditingValue
TextEditingValue有3個屬性:
String text:TextField顯示的默認值TextSelection selection:文本選中范圍TextRange composing:
text
TextEditingValue.text相當於``TextEditingController里的text參數。其實看源碼可以發現,TextEditingController里的text最終將會賦值給TextEditingValue.text。
TextEditingController({ String? text })
: super(text == null ? TextEditingValue.empty : TextEditingValue(text: text));
selection
selection屬性的默認值是TextSelection.collapsed(offset: -1),我們來看看是啥效果:

我們可以看到,光標瞬間移到最前面。我們修改一下數值看看:
TextSelection.collapsed(offset: 10)

通過上面的實驗我們可以發現,offset可以用來設置光標的位置(當offset=0時和-1一樣效果)。這還不算玩,我們試試刪除這些文字會發生什么。

😦竟然從第10個字符開始,把后面的全都刪除了。
TextSelection.collapsed中還有一個可選參數affinity,該參數可以傳入兩個不同的值:TextAffinity.upstream和TextAffinity.downstream(🤨下流???),其中 TextAffinity.downstream是默認值,我們改成TextAffinity.upstream一下看看是什么效果。。。試了一下,沒有效果😦。。。如果有效果的話,會是如下樣子:

該值用來設置,當字符串從offset處換行,光標應該顯示在上一行還是下一行。
selection還可以傳入TextSelection.fromPosition方法,該方法需要傳入一個TextPosition對象的參數,該對象也有兩個參數,和TextSelection.collapsed一模一樣,效果也一樣。
現在來說說TextSelection對象本身。
TextSelection
int baseOffset:開始的位置int extentOffset:結束的位置TextAffinity affinity:光標的位置bool isDirectional:是否消除了其基礎和范圍的歧義(不懂可以直接忽略)
該對象可以用來設置文本的選擇范圍:
selection: const TextSelection(
extentOffset: 5,
baseOffset: 12,
),

其實,TextSelection.fromPosition是把TextSelection對象的 extentOffset 和 baseOffset賦值給了同一個數,而TextSelection.collapsed又是TextSelection.fromPosition的簡化版。
composing
該值傳入一個TextRange對象,該對象有兩個屬性:
int start:開始的位置int end:結束的位置
我們來試試有什么效果:
composing: const TextRange(start: 8, end: 16),

我們設定的范圍會有一條下划線。
composing的默認值為TextRange.empty,相當於把start和end的值設置成-1,而TextRange.collapsed方法是把start和end設置成相同的數值,所以顯示效果回合TextRange.empty一樣。
