Вертикальный текстовый виджет для флаттера
Документы TextDirection говорят:
Flutter разработан для удовлетворения потребностей приложений, написанных на любом из используемых в настоящее время языков мира, независимо от того, используют ли они направление письма справа налево или слева направо. Flutter не поддерживает другие режимы письма, такие как вертикальный текст или бустрофедон, так как они редко используются в компьютерных программах. (выделение добавлено)
Flutter не только не поддерживает вертикальный текст, но и не будет официально поддерживаться в будущем. Это было раннее дизайнерское решение (см. Здесь и здесь).
Тем не менее, сегодня существует реальная поддержка вертикального сценария. В дополнение к определенному китайскому и японскому использованию, это требуется традиционному монгольскому письму. Этот сценарий очень часто используется в компьютерных программах во Внутренней Монголии ( пример, пример, пример, пример, пример).
Это написано сверху вниз, и строки переносятся слева направо:
Кроме того, символы эмодзи и CJK сохраняют свою ориентацию при вертикальном отображении (не обязательно, но желательно). На изображении ниже верхний абзац показывает текущую реализацию, а нижний абзац показывает правильную вертикальную визуализацию:
// sample text
ᠨᠢᠭᠡ ᠬᠣᠶᠠᠷ ᠭᠣᠷᠪᠠ ᠳᠥᠷᠪᠡ ᠲᠠᠪᠤ ᠵᠢᠷᠭᠤᠭᠠ ᠳᠣᠯᠣᠭᠠ ᠨᠠᠢᠮᠠ ᠶᠢᠰᠦ ᠠᠷᠪᠠ one two three four five six seven eight nine ten 汉字 한국어 モンゴル語 English? ᠮᠣᠩᠭᠣᠯ︖
Поскольку Flutter не поддерживает вертикальный скрипт, он должен быть реализован с нуля. Все фактическое расположение текста и рисование выполняется в движке Flutter с LibTxt, поэтому я не могу этого изменить.
Что потребуется для создания вертикального текстового виджета сверху вниз?
Обновить
Я все еще ищу ответ. Ответ Реми хорош для однострочного текста, но не работает для многострочного текста.
В настоящее время я исследую три различных возможных решения:
- Создайте подкласс RenderObject (или, возможно, подкласс RenderParagraph), который поддерживает пользовательский StatelessWidget, похожий на виджет RichText.
- Используйте виджет CustomPaint
- Создайте составной виджет для ListView (одна строка на текстовую строку), текстовых виджетов и WidgetSpans.
Все это включает измерение текста, его разметку и получение списка строк.
Еще одно обновление
В настоящее время я склоняюсь к созданию собственного виджета и визуализации объекта, который будет имитировать RichText и RenderParagraph. Под капотом RenderParagraph использует TextPainter для разметки и рисования текста. Моя проблема сейчас в том, что TextPainter тесно связан с базовой библиотекой Skia LibTxt. Вот где происходит вся фактическая планировка и покраска. Выкладывание текста во что угодно, кроме ltr и rtl по умолчанию, оказывается большой проблемой.
Еще одно обновление
Принятый ответ соответствует критериям, изложенным в этом вопросе. Я думаю, что это будет отвечать краткосрочным потребностям. У меня есть некоторые оговорки для таких вещей, как применение стиля текста, но мне нужно будет сделать больше тестов. В долгосрочной перспективе я все еще могу попытаться сделать пользовательский макет текста и рисование.
6 ответов
Это решение основано на компоновке 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;
}
}
Боковой текст возможен при использовании RotatedBox
, что достаточно для правильной переноски текста, как и следовало ожидать.
Row(
children: <Widget>[
RotatedBox(
quarterTurns: 1,
child: Text(sample),
),
Expanded(child: Text(sample)),
RotatedBox(
quarterTurns: -1,
child: Text(sample),
),
],
),
ht tps:https://stackru.com/images/9bb1d0b9b42b6c92d677592cf6d019c57dbafa94.gif
Точно так же Flutter теперь поддерживает встроенные виджеты внутри текста. Это можно использовать для поворота смайликов внутри текста.
RotatedBox(
quarterTurns: 1,
child: RichText(
text: TextSpan(
text: 'Hello World',
style: DefaultTextStyle.of(context).style,
children: [
WidgetSpan(
child: RotatedBox(quarterTurns: -1, child: Text('')),
)
],
),
),
),
ht tps:https://stackru.com/images/42e8fc702a2da58589f60ad5da1a79acc919b5cc.gif
В итоге я создал собственный виджет для обработки текста. На момент написания этой статьи он некорректно вращал символы эмодзи и CJK, но обрабатывал ориентацию и перенос строк.
- Пакет находится здесь: https://pub.dev/packages/mongol
- Я описал, как делал пакет здесь: https://www.raywenderlich.com/4562681-flutter-text-rendering
Задача нетривиальная, но гибкая. Другие посетители этого вопроса могут последовать аналогичной схеме, если другие ответы не соответствуют вашим потребностям.
Если вам нужен больший контроль над рендерингом текста во Flutter, прочтите Мое первое разочарование с Flutter и добавьте комментарий к этой проблеме GitHub.
Если вы делаете собственный шрифт со всеми символами, повернутыми на 180° (сложная часть), то вы можете просто изменить textDirection на TextDirection.rtl (справа налево) и повернуть текст на одну четверть (это тоже непросто).
Это просто нарисовать то, что вы хотите
Примеры https://fireship.io/lessons/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<--------------
)