Flutter 初始化时带有错误表情符号的文本小部件

Nas*_*sky 5 emoji dart flutter

我想使用 Flutter 在文本小部件中显示表情符号。

\n\n

当我从互联网复制示例表情符号时,其中一些在我的 IDE 中显示为 2 个字符。

\n\n

例如:

\n\n
static String dualCharEmoji = "\xe2\x9a\x94\xef\xb8\x8f";\nstatic String singleCharEmoji = "";\n
Run Code Online (Sandbox Code Playgroud)\n\n

当我在文本小部件中使用此变量时,它们都工作正常:

\n\n
Text("\xe2\x9a\x94\xef\xb8\x8f",)\nText("",)\n
Run Code Online (Sandbox Code Playgroud)\n\n

但是,仅在首次运行应用程序时,双字符表情符号仅显示为其第一个字符。

\n\n

即仅当第一次打开应用程序时,剑图标显示为\xe2\x9a\x94而不是\xe2\x9a\x94\xef\xb8\x8f

\n\n

重新加载后,它会得到修复,并且热重新加载/热重启不会再次导致错误。

\n\n

我的问题是:

\n\n

这是一个错误吗?我在这里遗漏了一些细节吗?为什么只有第一次打开应用程序时才会出现这种情况?

\n\n

如何从一开始就显示 2 尺​​寸的表情符号?

\n\n

我正在使用以下 Flutter 版本:

\n\n
>flutter --version\nFlutter 1.9.1+hotfix.4 \xe2\x80\xa2 channel stable \xe2\x80\xa2 https://github.com/flutter/flutter.git\nFramework \xe2\x80\xa2 revision cc949a8e8b (9 weeks ago) \xe2\x80\xa2 2019-09-27 15:04:59 -0700\nEngine \xe2\x80\xa2 revision b863200c37\nTools \xe2\x80\xa2 Dart 2.5.0\n
Run Code Online (Sandbox Code Playgroud)\n\n

请参阅下面的最小可重现示例:

\n\n
import \'package:flutter/material.dart\';\n\nvoid main() => runApp(MyApp());\n\nclass MyApp extends StatelessWidget {\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: \'Flutter Demo\',\n      home: MyHomePage(),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  MyHomePage({Key key}) : super(key: key);\n\n  @override\n  _MyHomePageState createState() => _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n  static String dualCharEmoji = "\xe2\x9a\x94\xef\xb8\x8f";\n  static String singleCharEmoji = "";\n  String text = dualCharEmoji;\n  int count = 0;\n  void swapText() {\n    setState(() {\n      if (count % 2 == 0)\n        text = singleCharEmoji;\n      else\n        text = dualCharEmoji;\n      count++;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Scaffold(\n      body: Center(\n        child: Column(\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            Text(\n              text,\n              style: TextStyle(fontSize: 50),\n            ),\n          ],\n        ),\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: swapText,\n      ),\n    );\n  }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

Nas*_*sky 5

在创建这个问题、进行多次不成功的尝试并且没有有效答案之后,我在 Flutter 的官方 Github 存储库上创建了一个问题。

在我展示了我的问题后,一位用户解释说,这是与 Flutter 如何渲染和缓存字体相关的错误,以防默认字体无法正确处理某些字符:

第一次渲染 DualCharEmoji 时,Flutter 发现默认字体无法处理 2694 个字符。[...]它可以渲染基本的 2694 交叉剑角色,但不能渲染 2694 FE0F 表情符号。

当渲染 singleCharEmoji 时,Flutter 现在将 NotoSansSymbols 字体包含在提供给 HarfBuzz 文本整形器的字体列表中。[...] 并且 matchFamilyStyleCharacter 返回 NotoColorEmoji。Flutter 也会将此字体添加到缓存中。

下次引擎尝试渲染 DualCharEmoji 时,它会向 HarfBuzz 提供两种缓存字体(NotoSansSymbols 和 NotoColorEmoji)。现在 HarfBuzz 知道了 NotoColorEmoji 中定义的序列,它可以识别 2694 FE0F 表情符号并将其返回给 Flutter。

我在这里仅引用了讨论的某些部分。您可以在我上面链接的问题页面上阅读完整的讨论和解释。

用户提出了一些解决方法,即两个:

首先,您可以强制引擎预先缓存正确处理表情符号的字体。build您可以通过在、 或方法中添加以下代码initState(在构建文本小部件之前运行的任何位置)来完成此操作:

import 'dart:ui';

ParagraphBuilder pb = ParagraphBuilder(ParagraphStyle(locale: window.locale));
pb.addText('\ud83d\ude01');  // smiley face emoji
pb.build().layout(ParagraphConstraints(width: 100));
Run Code Online (Sandbox Code Playgroud)

这适用于我的示例项目,但如问题页面所述:

然而,这依赖于当前 Flutter 文本引擎的实现细节,这些细节将来可能会发生变化。

因此请注意,此解决方法可能会在任何给定时间停止工作。

第二种解决方法如下:

目前的解决方法是在文本样式中列出所需的字体,它将被正确解析。

这只是TextStyle为 Text Widget 提供一个属性,并设置其fontFamily(或fontFamilyFallback)属性。所选字体必须已经支持所需的所有字符。这也对我有用,但是我必须包含来自我的计算机(或来自公共在线包)的自定义字体。