Flutter 的垂直文本小部件

37
TextDirection文档中写道:
Flutter设计用于满足使用世界上任何当前使用的语言编写的应用程序的需求,无论它们使用从右到左还是从左到右的书写方向。 Flutter不支持其他书写模式,例如竖排文字或“奇偶行转向”(boustrophedon)文本,因为这些在计算机程序中很少使用(强调添加) Flutter不仅不支持竖排文字,而且将来也不会得到官方支持。 这是早期的设计决策(请参见此处此处)。
即使如此,在今天,有真正需要垂直字母支持的用例。 除了某些中文和日文用途外,传统蒙古文字也需要垂直书写。 在内蒙古的计算机程序中非常普遍使用此脚本(示例示例示例示例示例示例)。
它是从上到下书写的,并且行从左到右换行: enter image description here 此外,当垂直显示时,表情符号和CJK字符保留其方向(不是绝对必要,但更好)。 在下面的图片中,上段显示当前实现,下段显示正确的垂直渲染: enter image description here
// sample text
ᠨᠢᠭᠡ ᠬᠣᠶᠠᠷ ᠭᠣᠷᠪᠠ ᠳᠥᠷᠪᠡ ᠲᠠᠪᠤ ᠵᠢᠷᠭᠤᠭ᠎ᠠ ᠳᠣᠯᠣᠭ᠎ᠠ ᠨᠠ‍ᠢᠮᠠ ᠶᠢᠰᠦ ᠠᠷᠪᠠ one two three four five six seven eight nine ten 汉字 한국어 モンゴル語 English? ᠮᠣᠩᠭᠣᠯ︖

由于Flutter不支持竖排文字,必须从头开始实现。所有实际的文本布局和绘画都在Flutter引擎中使用LibTxt完成,所以我不能改变这个。

创建一个自上而下的垂直文本widget需要涉及什么?

更新

我仍在寻找答案。Rémi的答案适用于单行文本,但对多行文本无效。

我目前正在研究三种不同的可能解决方案:

  • 创建一个RenderObject子类(或者可能是RenderParagraph子类),支持自定义StatelessWidget类似于RichText widget。
  • 使用CustomPaint widget
  • 制作一个复合widget,其中包含ListView(每行一个文本行),Text widget和WidgetSpans。

所有这些都涉及测量文本,布局文本并获取一组文本行。

另一个更新

我目前倾向于制作自定义widget和渲染对象,模仿RichText和RenderParagraph。在幕后,RenderParagraph使用TextPainter来布局和绘制文本。我的问题现在是TextPainter与底层的Skia LibTxt库紧密耦合。除了默认的从左到右和从右到左之外,将文本布局在其他任何位置都是一个大问题。

另一个更新

所接受的答案符合我在此问题中提出的标准。我认为它能够满足短期需求。对于应用文本样式等事项,我还有一些保留意见,但我需要进行更多测试。长期来看,我可能仍然会尝试进行自定义文本布局和绘画。


如果有人正在寻找英语语言,这个问题可能会有所帮助。 - CopsOnRoad
6个回答

56

使用 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('')),
        )
      ],
    ),
  ),
),

在此输入图片描述


关于新的 WidgetSpan 的想法非常棒。对于单行文本应该能够很好地工作。同样的概念可以通过使用小部件跨度列表来支持CJK字符。 - Suragch
确实。一个小脚本应该能够轻松完成这个转换。 - Rémi Rousselet
3
对于多行文本,仅仅旋转段落是不够的。蒙古文是从上到下书写,列从左到右换行。在您的第一个示例中,行的第一个子元素中的文本从右到左换行。这就像从书的底部开始向上阅读英语书籍一样。在行的第三个子元素中,文本是倒置的。 - Suragch
旁注:对于垂直的中文/日文布局,列通常会进行从右到左的换行。因此,简单地旋转段落(quarterTurns: 1)可能是一个有效的解决方案。由于小部件的创建很便宜,我想每个字符都有一个 WidgetSpan 可能是可行的,但需要进行测试。 - Suragch

24

这个解决方案基于flex-box布局。它将字符串转换为单词列表,并将它们放在垂直旋转的Text小部件中。这些小部件使用设置为Axis.verticalWrap小部件进行布局。Wrap小部件通过将需要换行的单词放入下一列来自动处理需要换行的单词。

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;
  }
}

输入图像描述


7

使用RotatedBox非常简单

RotatedBox(
  quarterTurns: 1,
  child: //your text
),


2
我最终创建了一个自定义小部件来处理文本呈现。

enter image description here

这不是一项微不足道的任务,但它非常灵活。如果其他答案不能满足您的需求,其他访问者可以按照类似的模式进行操作。

如果您想更加控制Flutter中的文本呈现,请阅读My First Disappointment with Flutter并在this GitHub issue上发表评论。


2
如果您制作了一个所有符号都旋转了180度的自定义字体(难点),那么您可以简单地将textDirection更改为TextDirection.rtl(从右到左),并将文本旋转一个四分之一(也不容易)。"最初的回答"

这个想法很好,但我认为这种特定的实现方式行不通,因为改变文本方向并不会改变字形绘制的顺序。一个类似的想法是在字体中垂直镜像字形,然后旋转和镜像Text小部件(请参见第二张图片此处)。这样做的缺点是任何未包含在字体中的字形都会被镜像,但这可能可以使用Remi答案中提到的WidgetSpan来处理。无论如何,这是一个可行的选择。 - Suragch
是的,双重翻转的想法看起来很完美,但在编辑模式下它会如何工作呢?顺便说一句,我的从右到左模式的实验也让我失望了。祝你好运,挑战很棒。 - Spatz
我曾经在安卓开发中使用双重翻转方法,但最终为了更灵活的需求选择了自定义布局和绘制。感谢您的建议。 - Suragch

-2

这很简单,随心所欲地绘制你想要的内容。

例如: Flutter小部件定位指南

然后:

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<--------------
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接