Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Text widget with wrong emoji on initialization in Flutter

I want to display an emoji within a text widget, using Flutter.

When I copy an example emoji from the internet, some of them shows up with 2 characters in my IDE.

E.g:

static String dualCharEmoji = "⚔️";
static String singleCharEmoji = "🗡";

When I use this variable in the text widget, both of them work fine:

Text("⚔️",)
Text("🗡",)

However, only when first running the app, the dual character emoji shows up as its first character only.

i.e. Only when first opening the app, the sword icon shows up as instead of as ⚔️

After it gets reloaded it gets fixed, and hot reloading/hot restarting does not makes it bug again.

My question is:

Is this a bug? Am I missing some detail here? Why does it only happen when first opening the app?

How can I show a 2 sized emoji from the start?

I'm using the following Flutter version:

>flutter --version
Flutter 1.9.1+hotfix.4 • channel stable • https://github.com/flutter/flutter.git
Framework • revision cc949a8e8b (9 weeks ago) • 2019-09-27 15:04:59 -0700
Engine • revision b863200c37
Tools • Dart 2.5.0

See the minimum reproducible example below:

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: MyHomePage(),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key}) : super(key: key);

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  static String dualCharEmoji = "⚔️";
  static String singleCharEmoji = "🗡";
  String text = dualCharEmoji;
  int count = 0;
  void swapText() {
    setState(() {
      if (count % 2 == 0)
        text = singleCharEmoji;
      else
        text = dualCharEmoji;
      count++;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            Text(
              text,
              style: TextStyle(fontSize: 50),
            ),
          ],
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: swapText,
      ),
    );
  }
}
like image 402
Naslausky Avatar asked Nov 28 '19 03:11

Naslausky


People also ask

How do you add Emojis to text flutters?

You can insert emoji in the text field through following way: If you're on Mac, you can hit Control + Command + Space. Windows users can hit the "Windows key" + ; (semicolon).

How do I add icons before my text flutters?

The simplest way to create a button with icon and text in Flutter is to use the new Material button called ElevatedButton with an icon constructor. ElevatedButton. icon() gives you the ability to add the icon and label parameter to the button.

How do you use a new line character in text widget Flutter?

This can be achieved by adding \n into your text widget as below.


1 Answers

After creating this question, doing many unsuccessful tries, and no working answer, I've created an issue on Flutter's official Github repository.

After I showed my problem, an user explained that that is a bug related to how Flutter renders and caches the fonts, in case of the default font cannot handle properly some character:

The first time that dualCharEmoji is rendered Flutter sees that the default font does not handle the 2694 character. [...]it can render the basic 2694 crossed swords character but not the 2694 FE0F emoji.

When singleCharEmoji is rendered Flutter now includes the NotoSansSymbols font in the list of fonts given to the HarfBuzz text shaper.[...] and matchFamilyStyleCharacter returns NotoColorEmoji. Flutter also adds this font to the cache.

The next time the engine tries to render dualCharEmoji it provides both cached fonts (NotoSansSymbols and NotoColorEmoji) to HarfBuzz. Now that HarfBuzz is aware of the sequences defined in NotoColorEmoji it can recognize the 2694 FE0F emoji and return it to Flutter.

I've referenced here only some parts of the discussion. You can read the full discussion and explanation on the issue page I've linked above.

Users suggested some workarounds, namely two:

First, you can force the engine to pre-cache a font that handles emojis correctly. You can do this by adding the following code in the build, or the initState method (anywhere that runs before building the Text Widget):

import 'dart:ui';

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

This worked on my example project, but as stated on the Issue page:

However, this relies on implementation details of the current Flutter text engine that may change in the future.

So be aware that this workaround can stop working at any given time.

The second workaround is given as follow:

For now the work around is to list the desired font in the text style and it will be resolved correctly.

Which is simply to give a TextStyle property to the Text Widget, with its fontFamily (or the fontFamilyFallback) property set. The chosen font must already support all characters required. This also worked for me, however I had to include a custom font from my computer (or from a public online package).

like image 116
Naslausky Avatar answered Sep 24 '22 10:09

Naslausky