概要
どうも、@daiki1003です!約1年前に、TextField.decoration
の解体新書という記事をリリースしました。
割と好評をいただけているようなので、今回はSelectableText
について解説したいと思います。
このブログを始めとする解体新書シリーズを読めば
何を指定したら何が変わるのか、理解出来る様になります。
一度で理解できなくとも、辞書の様に困ったら何度でも見に来てください。
あなたの手助けをしてくれることでしょう。 (だといいな)
なお、今回のコードは全て以下で試すことが出来ますので
興味がある方は覗いてみてくださいね!
ちなみに、今後も解体新書シリーズは公開予定でしてこのレポジトリで更新していきます。
執筆時環境
Flutter 3.3.1 • channel stable • https://github.com/flutter/flutter.git Framework • revision 4f9d92fbbd (3 days ago) • 2022-09-06 17:54:53 -0700 Engine • revision 3efdf03e73 Tools • Dart 2.18.0 • DevTools 2.15.0
補足
僕自身、正直完全に理解出来ていないところもありそのような箇所は理解次第更新していきたいと思います。
初期設定
では、まずはSelectableText
を表示するところから始めます。
表示は至って簡単で、Text
ウィジェットの様に第一引数に表示する文字列を渡してあげます。
日本語と英語を混ぜていますが、後にこれがちょっと大事だったりします。
それでは、早速それぞれのプロパティについて変更していきましょう。
まず初めに
以下のプロパティに関しては、Text
ウィジェットと同じく割と良く使われるため割愛させていただきます。
focusNode
, style
, strutStyle
,textAlign
, textDirection
, textScaleFactor
,textHeightBehavior
bool showCursor = false
選択(タップ)した際にカーソルを表示するかどうかを指定します。
また、単語単位で最初か最後にしかカーソルは表示されません。
スクショの例で言うと、2行目において最初の文字であるa
か最後の文字であるz
の後ろにしか表示されず、
例えばm
をタップしたとしてもz
の後に表示されます。
double cursorWidth = 2.0
2.0 | 10.0 | 20.0 |
カーソルの横幅を指定します。
20の存在感すごいですね。
また、このcursorWidth
はテキストの表示に影響を与えるらしく20のスクショでは2行目に収まっていたz
が3行目にずれ込んでいますね。
double? cursorHeight
※見やすくするために、前項cursorWidth
を7に設定しています。
1.0 | 24.0(=fontSize) | 48.0 |
カーソルの縦幅を指定します。
50(フォントサイズ)以上ってどういう用途なんだろう…w
double? cursorRadius
0.0 | 5.0 | 10.0(=cursorWidth/2) |
角丸を指定します。
通常のカーソル幅は2.0
なことが多く、あまり指定することはなさそうですね。
また、borderRadius
やcursorRadius
は短辺の値の1/2以上を指定しても
見た目に変化が起こりません。
今回でいえば、横幅cursorWidth
を20に設定しているので最大値を10としています。
BoxWidthStyle selectionWidthStyle = ui.BoxWidthStyle.tight
tight(デフォルト) | max | max(右端) |
選択範囲の横幅を指定します。
デフォルトのtight
では、選択した単語と同じ幅になります。
一方、max
にすると面白いことが起こります。
基本は同じなのですが、選択範囲が行末であった場合、SelectableText
の描画範囲の端まで選択状態となります。
BoxHeightStyle selectionHeightStyle = ui.BoxHeightStyle.tight
tight | max | includeLineSpacingMiddle |
includeLineSpacingTop | includeLineSpacingBottom | strut |
選択範囲の縦幅を指定します。tight
の場合、文字の占有範囲ではなく描画範囲と同じ高さになるので英語や日本語などが混ざるとガタガタになってしまいますね。
なので、ユーザ自由入力の内容を表示するなど日英等複数の言語が混ざる場合は指定するのが良さそうに思いました。
なお、include
系の動作の差異が現状把握しきれいていないです。
DragStartBehavior dragStartBehavior
現在、僕の中でこれを変えることによってどういう影響がSelectableText
に出ているのかが把握できておりません🙏
基本的には、ドラッグ開始コールバックに渡す座標をタップ開始位置(.down
)とするかドラッグを開始後の次のフレームの位置(.start
)とするかの違いになります。(0, 0)
でタップして、(20, 20)
に素早く指やカーソルを動かした場合、.down
であれば(0, 0)
、.start
であれば(20, 20)
が渡されるイメージです。
どなたか、見識のある方教えてください!
bool enableInteractiveSelection = true
ユーザの操作によるテキスト選択の可否を設定します。false
とすると、ダブルタップや長押しでテキスト選択が出来なくなります。
TextSelectionControls? selectionControls
material | cupertino |
選択状態でのコントローラのUI(つまみや「Copy」などの部分)をMaterial UI
かCupertino (iOS)
とするかを指定します。
基本的には動作しているOS準拠になると思うので、未指定のままで良いかなとは思います。
これは余談ですが、実行中に動的に変更することは出来ないようです。
GestureTapCallback? onTap
SelectableText
が占有された領域がタップされたことだけが分かります。
引数などはありません。
typedef GestureTapCallback = void Function();
ScrollPhysics? scrollPhysics
スクロールの動作を指定します。
通常であれば、描画領域を超えてコンテンツが存在する場合はスクロールを行い、そうでなければスクロールが
出来ないようになっています。
Never
:どんな場合でもスクロールしません。Always
:常にスクロールします。Bouncing
: iOS
のような、スクロール領域をはみ出ようとした場合にバウンスするアニメーションを行います。Clamping
: Android
のような、スクロール領域をはみ出ようとした場合にすぐにピタッと止まる感じになります。
void Function(TextSelection, SelectionChangeCause?)? onSelectionChanged
選択範囲が変更された時に呼ばれるコールバックです。
TextSelection
こちらは、baseOffset
, extentOffset
, isDirectional
の三つのプロパティを持っています。
SelectionChangeCause
こちらは、enum
になっていて、ダブルタップやロングプレス、ドラッグなど選択範囲が変更した際の理由について返却されます。
例
1 | 2 | 3 | 4 |
例えば、
1. 「え」をロングタップして選択状態
2. 先頭のつまみをドラッグして「いうえ」を選択状態
3. 終端のつまみをドラッグして「いうえおか」を選択状態
4. 「こ」をタップして選択解除
と言うストーリーがあったとします。
コールバックは以下のように返って来ます。
1. flutter: TextSelection(baseOffset: 3, extentOffset: 4, isDirectional: false) flutter: SelectionChangedCause.longPress
2. flutter: TextSelection(baseOffset: 4, extentOffset: 1, isDirectional: false) flutter: SelectionChangedCause.drag
3. flutter: TextSelection(baseOffset: 1, extentOffset: 6, isDirectional: false) flutter: SelectionChangedCause.drag
4. flutter: TextSelection.collapsed(offset: 10, affinity: TextAffinity.downstream, isDirectional: false) flutter: SelectionChangedCause.tap
なんとなく想像付きましたでしょうか?
前回の値を覚えておいてoffset
を比較することでどちら側への移動なのかとかは分かりそうですね。
最後に
いかがでしたでしょうか?
基本的にアプリ上に出てくるテキストって選択してコピーとかしたくなることが多い
(Twitterの投稿内容とか)ので、意外とSelectableText
の利用頻度は多いんじゃないかなーなんて思います。
似たようなウィジェットでSelectionArea
と言うのがあるのですが、そちらはchild
として指定したウィジェット
を対象として選択可能にします。
頑張ったねって少しでも思っていただけたら↓からコーヒーを少し奢っていただけると嬉しいです!
もちろん、強制ではありません笑
Twitterフォローお願いします
「次回以降も記事を読んでみたい!」「この辺分からなかったから質問したい!」
そんな時は、是非Twitter (@daiki1003)やInstagram (@ashdik_flutter)のフォローお願いします♪
Twitterコミュニティ参加お願いします
Twitterコミュニティ「Flutter lovers」を開設しました!参加お待ちしております😁
☕️ Buy me a coffee
また、記事がとても役に立ったと思う人はコーヒーを奢っていただけると非常に嬉しいです!
コメント