Flutter 的垂直文本小部件
Vertical Text widget for Flutter
TextDirection 文档说:
Flutter is designed to address the needs of applications written in
any of the world's currently-used languages, whether they use a
right-to-left or left-to-right writing direction. Flutter does not
support other writing modes, such as vertical text or boustrophedon
text, as these are rarely used in computer programs. (emphasis added)
Flutter不仅不支持竖排文字,以后也不会正式支持。这是一个早期的设计决定(参见 here and here)。
即使如此,垂直脚本支持在今天也有实际用途。除了某些Chinese and Japanese uses, the traditional Mongolian script requires it. This script is very commonly used in computer programs in Inner Mongolia (example, example, example, example, example, example)。
从上到下书写,从左到右换行:
此外,表情符号和 CJK 字符在垂直显示时会保留其方向(不是绝对必要,但首选)。在下图中,顶部段落显示了当前的实现,底部段落显示了正确的垂直渲染:
// sample text
ᠨᠢᠭᠡ ᠬᠣᠶᠠᠷ ᠭᠣᠷᠪᠠ ᠳᠥᠷᠪᠡ ᠲᠠᠪᠤ ᠵᠢᠷᠭᠤᠭᠠ ᠳᠣᠯᠣᠭᠠ ᠨᠠᠢᠮᠠ ᠶᠢᠰᠦ ᠠᠷᠪᠠ one two three four five six seven eight nine ten 汉字 한국어 モンゴル語 English? ᠮᠣᠩᠭᠣᠯ︖
由于Flutter不支持垂直脚本,所以必须从头开始实现。所有实际的文本布局和绘制都是在 LibTxt 的 Flutter 引擎中完成的,所以我无法更改它。
创建从上到下的垂直文本小部件涉及什么?
更新
我还在寻找答案。 Rémi 的答案适用于单行文本,但不适用于多行文本。
我目前正在研究三种不同的可能解决方案:
- 创建一个支持类似于 RichText 小部件的自定义 StatelessWidget 的 RenderObject 子类(或者可能是 RenderParagraph 子类)。
- 使用 CustomPaint 小部件
- 制作 ListView(每行一行文本行)、文本小部件和 WidgetSpans 的复合小部件。
所有这些都涉及测量文本、对其进行布局以及获取行列表。
另一个更新
我目前倾向于制作一个自定义小部件和呈现对象来模仿 RichText 和 RenderParagraph。在引擎盖下,RenderParagraph 使用 TextPainter 来布局和绘制文本。我现在的问题是 TextPainter 与底层 Skia LibTxt 库紧密耦合。这就是所有实际布局和绘画发生的地方。在默认 ltr 和 rtl 之外的任何地方布置文本被证明是一个大问题。
又一个更新
接受的答案符合我在这个问题中提出的标准。我认为它将满足短期需求。我对应用文本样式之类的事情有所保留,但我需要做更多的测试。从长远来看,我可能仍会尝试进行自定义文本布局和绘画。
可以使用 RotatedBox
横向文本,这足以让文本按照您的预期正确换行。
Row(
children: <Widget>[
RotatedBox(
quarterTurns: 1,
child: Text(sample),
),
Expanded(child: Text(sample)),
RotatedBox(
quarterTurns: -1,
child: Text(sample),
),
],
),
同样,Flutter 现在支持文本内的内联小部件。这可用于在文本中旋转笑脸。
RotatedBox(
quarterTurns: 1,
child: RichText(
text: TextSpan(
text: 'Hello World',
style: DefaultTextStyle.of(context).style,
children: [
WidgetSpan(
child: RotatedBox(quarterTurns: -1, child: Text('')),
)
],
),
),
),
如果您制作自定义字体,所有符号都旋转 180°(困难部分),那么您可以简单地将 textDirection 更改为 TextDirection.rtl (right-to-left) 并将文本旋转四分之一(也不容易)。
想画什么就画什么
例子flutter-widget-positioning-guide
然后
Column(
children: [
MyWidget(),
MyWidget(),
MyWidget()
]
);
// or swap a Column for a Row to flip the axis
Row(children: [ ]);
// and this is all just sugar for the Flex widget
Flex(
direction: Axis.vertical<--------------
)
此解决方案基于 flex-box 布局。它将字符串转换为单词列表并将它们放入垂直旋转的文本小部件中。这些布局的 Wrap
小部件设置为 Axis.vertical
。换行小部件自动处理需要换行的单词,将它们放在下一列中。
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
direction: Axis.vertical,
children: _getWords(),
),
),
);
}
List<Widget> _getWords() {
const text =
"That's all 12345 One Two Three Four Five ᠸᠢᠺᠢᠫᠧᠳᠢᠶᠠ᠂ ᠴᠢᠯᠦᠭᠡᠲᠦ ᠨᠡᠪᠲᠡᠷᠬᠡᠢ ᠲᠣᠯᠢ ᠪᠢᠴᠢᠭ ᠪᠣᠯᠠᠢ᠃";
var emoji = RegExp(r"([\u2200-\u3300]|[\uD83C-\uD83E].)");
List<Widget> res = [];
var words = text.split(" ");
for (var word in words) {
var matches = emoji.allMatches(word);
if (matches.isEmpty) {
res.add(RotatedBox(quarterTurns: 1, child: Text(word + ' ')));
} else {
var parts = word.split(emoji);
int i = 0;
for (Match m in matches) {
res.add(RotatedBox(quarterTurns: 1, child: Text(parts[i++])));
res.add(Text(m.group(0)));
}
res.add(RotatedBox(quarterTurns: 1, child: Text(parts[i] + ' ')));
}
}
return res;
}
}
我最终创建了一个自定义小部件来处理文本呈现。
- 包裹在这里:https://pub.dev/packages/mongol
- 我在这里描述了我是如何制作包裹的:https://www.raywenderlich.com/4562681-flutter-text-rendering
这不是一项微不足道的任务,但它很灵活。如果其他答案不能满足您的需求,此问题的其他访问者可以遵循类似的模式。
如果您想在 Flutter 中更好地控制文本渲染,请阅读 My First Disappointment with Flutter and add a comment to this GitHub issue。
使用起来很简单RotatedBox
RotatedBox(
quarterTurns: 1,
child: //your text
),
TextDirection 文档说:
Flutter is designed to address the needs of applications written in any of the world's currently-used languages, whether they use a right-to-left or left-to-right writing direction. Flutter does not support other writing modes, such as vertical text or boustrophedon text, as these are rarely used in computer programs. (emphasis added)
Flutter不仅不支持竖排文字,以后也不会正式支持。这是一个早期的设计决定(参见 here and here)。
即使如此,垂直脚本支持在今天也有实际用途。除了某些Chinese and Japanese uses, the traditional Mongolian script requires it. This script is very commonly used in computer programs in Inner Mongolia (example, example, example, example, example, example)。
从上到下书写,从左到右换行:
此外,表情符号和 CJK 字符在垂直显示时会保留其方向(不是绝对必要,但首选)。在下图中,顶部段落显示了当前的实现,底部段落显示了正确的垂直渲染:
// sample text
ᠨᠢᠭᠡ ᠬᠣᠶᠠᠷ ᠭᠣᠷᠪᠠ ᠳᠥᠷᠪᠡ ᠲᠠᠪᠤ ᠵᠢᠷᠭᠤᠭᠠ ᠳᠣᠯᠣᠭᠠ ᠨᠠᠢᠮᠠ ᠶᠢᠰᠦ ᠠᠷᠪᠠ one two three four five six seven eight nine ten 汉字 한국어 モンゴル語 English? ᠮᠣᠩᠭᠣᠯ︖
由于Flutter不支持垂直脚本,所以必须从头开始实现。所有实际的文本布局和绘制都是在 LibTxt 的 Flutter 引擎中完成的,所以我无法更改它。
创建从上到下的垂直文本小部件涉及什么?
更新
我还在寻找答案。 Rémi 的答案适用于单行文本,但不适用于多行文本。
我目前正在研究三种不同的可能解决方案:
- 创建一个支持类似于 RichText 小部件的自定义 StatelessWidget 的 RenderObject 子类(或者可能是 RenderParagraph 子类)。
- 使用 CustomPaint 小部件
- 制作 ListView(每行一行文本行)、文本小部件和 WidgetSpans 的复合小部件。
所有这些都涉及测量文本、对其进行布局以及获取行列表。
另一个更新
我目前倾向于制作一个自定义小部件和呈现对象来模仿 RichText 和 RenderParagraph。在引擎盖下,RenderParagraph 使用 TextPainter 来布局和绘制文本。我现在的问题是 TextPainter 与底层 Skia LibTxt 库紧密耦合。这就是所有实际布局和绘画发生的地方。在默认 ltr 和 rtl 之外的任何地方布置文本被证明是一个大问题。
又一个更新
接受的答案符合我在这个问题中提出的标准。我认为它将满足短期需求。我对应用文本样式之类的事情有所保留,但我需要做更多的测试。从长远来看,我可能仍会尝试进行自定义文本布局和绘画。
可以使用 RotatedBox
横向文本,这足以让文本按照您的预期正确换行。
Row(
children: <Widget>[
RotatedBox(
quarterTurns: 1,
child: Text(sample),
),
Expanded(child: Text(sample)),
RotatedBox(
quarterTurns: -1,
child: Text(sample),
),
],
),
同样,Flutter 现在支持文本内的内联小部件。这可用于在文本中旋转笑脸。
RotatedBox(
quarterTurns: 1,
child: RichText(
text: TextSpan(
text: 'Hello World',
style: DefaultTextStyle.of(context).style,
children: [
WidgetSpan(
child: RotatedBox(quarterTurns: -1, child: Text('')),
)
],
),
),
),
如果您制作自定义字体,所有符号都旋转 180°(困难部分),那么您可以简单地将 textDirection 更改为 TextDirection.rtl (right-to-left) 并将文本旋转四分之一(也不容易)。
想画什么就画什么
例子flutter-widget-positioning-guide
然后
Column(
children: [
MyWidget(),
MyWidget(),
MyWidget()
]
);
// or swap a Column for a Row to flip the axis
Row(children: [ ]);
// and this is all just sugar for the Flex widget
Flex(
direction: Axis.vertical<--------------
)
此解决方案基于 flex-box 布局。它将字符串转换为单词列表并将它们放入垂直旋转的文本小部件中。这些布局的 Wrap
小部件设置为 Axis.vertical
。换行小部件自动处理需要换行的单词,将它们放在下一列中。
class HomePage extends StatelessWidget {
@override
Widget build(BuildContext context) {
return Scaffold(
body: Padding(
padding: const EdgeInsets.all(8.0),
child: Wrap(
direction: Axis.vertical,
children: _getWords(),
),
),
);
}
List<Widget> _getWords() {
const text =
"That's all 12345 One Two Three Four Five ᠸᠢᠺᠢᠫᠧᠳᠢᠶᠠ᠂ ᠴᠢᠯᠦᠭᠡᠲᠦ ᠨᠡᠪᠲᠡᠷᠬᠡᠢ ᠲᠣᠯᠢ ᠪᠢᠴᠢᠭ ᠪᠣᠯᠠᠢ᠃";
var emoji = RegExp(r"([\u2200-\u3300]|[\uD83C-\uD83E].)");
List<Widget> res = [];
var words = text.split(" ");
for (var word in words) {
var matches = emoji.allMatches(word);
if (matches.isEmpty) {
res.add(RotatedBox(quarterTurns: 1, child: Text(word + ' ')));
} else {
var parts = word.split(emoji);
int i = 0;
for (Match m in matches) {
res.add(RotatedBox(quarterTurns: 1, child: Text(parts[i++])));
res.add(Text(m.group(0)));
}
res.add(RotatedBox(quarterTurns: 1, child: Text(parts[i] + ' ')));
}
}
return res;
}
}
我最终创建了一个自定义小部件来处理文本呈现。
- 包裹在这里:https://pub.dev/packages/mongol
- 我在这里描述了我是如何制作包裹的:https://www.raywenderlich.com/4562681-flutter-text-rendering
这不是一项微不足道的任务,但它很灵活。如果其他答案不能满足您的需求,此问题的其他访问者可以遵循类似的模式。
如果您想在 Flutter 中更好地控制文本渲染,请阅读 My First Disappointment with Flutter and add a comment to this GitHub issue。
使用起来很简单RotatedBox
RotatedBox(
quarterTurns: 1,
child: //your text
),