Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow text wrapping behavior to be controlled #61081

Open
forever-z-133 opened this issue Jul 8, 2020 · 16 comments
Open

Allow text wrapping behavior to be controlled #61081

forever-z-133 opened this issue Jul 8, 2020 · 16 comments
Labels
a: typography Text rendering, possibly libtxt c: new feature Nothing broken; request for a new capability engine flutter/engine repository. See also e: labels. framework flutter/packages/flutter repository. See also f: labels. P3 Issues that are less important to the Flutter project team-engine Owned by Engine team triaged-engine Triaged by Engine team

Comments

@forever-z-133
Copy link

forever-z-133 commented Jul 8, 2020

when the word is very long, the text wrap will be not good.
at web, we can use word-break: break-all; to solve it.
so, can flutter add the feature?

Use case

MaterialApp(
  theme: new ThemeData(),
  home: Scaffold(
    body: Center(
      child: Container(
        width: 100.0,
        child: Text.rich(
          TextSpan(text: '中文information spropaganda accomplishment accomplishment'),
        ),
      ),
    ),
  ),
)

e3a137c2018210683552c5be11bea583

like this, between two long word, there is very large spacing.

Proposal

please add new params wordBreak to TextSpan, let word wrap at end of line, instead of end of word.

@VladyslavBondarenko VladyslavBondarenko changed the title like css, use word-break: break-all; Word-break property for text Jul 8, 2020
@VladyslavBondarenko
Copy link

Reference how does it look in CSS https://developer.mozilla.org/en-US/docs/Web/CSS/word-break

@VladyslavBondarenko VladyslavBondarenko added a: text input Entering text in a text field or keyboard related problems framework flutter/packages/flutter repository. See also f: labels. c: new feature Nothing broken; request for a new capability and removed a: text input Entering text in a text field or keyboard related problems labels Jul 8, 2020
@goderbauer goderbauer added a: typography Text rendering, possibly libtxt engine flutter/engine repository. See also e: labels. labels Jul 8, 2020
@zanderso zanderso added the P6 label Dec 3, 2020
@zanderso
Copy link
Member

zanderso commented Dec 3, 2020

/cc @Hixie for thoughts.

@Hixie
Copy link
Contributor

Hixie commented Dec 3, 2020

@VladyslavBondarenko are you saying you want the text to overflow its box?

@VladyslavBondarenko
Copy link

@Hixie I attached the link to CSS docs to show what behavior author would like to achieve:
corresponding to word-break: break-all; instead of corresponding to word-break: break-word;

Screenshot from 2020-12-04 01-32-06

@Hixie Hixie changed the title Word-break property for text Allow text wrapping behavior to be controlled Dec 3, 2020
@Hixie
Copy link
Contributor

Hixie commented Dec 3, 2020

Ah, got it. FWIW, you should be able to do this today by just inserting a word separator between every character. It might make Thai and Arabic look a bit weird though.

To implement this we would probably start by adding a wrapStyle property to ui.TextStyle. Exactly what values it takes is a little tricky. We need to express both where line breaking opportunities happen, and how they take priority (e.g. break at the first of these opportunities, unless you find none, in which case break at these, etc). We could do this by having a hard-coded set of patterns, like CSS, or we could have some more elaborate mechanism. I suppose the ultimate solution would be to just pass in a closure that gets called to see where we should wrap. Probably too expensive though.

@llucax
Copy link

llucax commented Jan 20, 2021

I'm also in need for this. I was looking everywhere thinking there must be a way to do it (wrapping text by breaking words). My use case is showing long URLs in little space, I want them to wrap but then since random characters of it are considered braking (like -), I get ugly things like:

image

Where what I want is almost this (I'm replacing the hyphen with a non-breaking hyphen, \u2011, but it is still breaking at / and I can't find a non-breaking /):

image

The even if there were a non-breaking slash, replacing characters is not really a solution as I want the text to be selectable (and copyable) too, and then the text becomes an invalid URL.

Same could be applied to long UUIDs or similar long strings.

I even tried to look at Flutter's code to see how wrapping was done (as word-breaking wrapping is the simplest way of wrapping ever I guess it shouldn't be too hard to implement), but I failed to find where normal word wrapping is being done.

@WieFel
Copy link

WieFel commented Feb 25, 2021

+1

@cuirhong
Copy link

cuirhong commented Jul 7, 2021

// Use Extension
String get breakWord{
    String breakWord = '';
    runes.forEach((element){
      breakWord += String.fromCharCode(element);
      breakWord +='\u200B';
    });
    return breakWord;
}

Use case

Text("中文information spropaganda accomplishment accomplishment".breakWord)

Success,Perfect

@llucax
Copy link

llucax commented Jul 7, 2021

Thanks for the workaround, but again, this doesn't cover all use cases:

replacing characters is not really a solution as I want the text to be selectable (and copyable) too, and then the text becomes an invalid URL.

Same could be applied to long UUIDs or similar long strings.

Adding a breaking character between each character have the same issue about being able to copy the original string by selecting it.

@odoublu
Copy link

odoublu commented Oct 2, 2021

Any update on this?

@SalahAdDin
Copy link

Any advance on this?

@qb20nh
Copy link

qb20nh commented Apr 20, 2022

From #61081 (comment)

Ah, got it. FWIW, you should be able to do this today by just inserting a word separator between every character. It might make Thai and Arabic look a bit weird though.
...

  1. Replace space with NO-BREAK SPACE '\u00a0' where you don't want to break. This forces two characters around it to stay put together in the same line.
  2. Insert ZERO WIDTH NO-BREAK SPACE '\ufeff' between the characters you don't want to break. Same as the above but takes no space.
  3. Optionally insert ZERO WIDTH SPACE '\u200b' where you want to break. This allows two characters around it to break freely.

It's tedious but you can have total control of where to break or not

@EnriqueSanVic
Copy link

I have published this package in pub. The MagicText widget solves this problem.

The package is a auto-responsive text widget that supports a multitude of parameters to control text rendering behaviour. You can control line breaks that break words in half with a given character (default is '-').

Pub package: https://pub.dev/packages/magic_text
GitHub repo: https://github.com/EnriqueSanVic/magic_text

Instance widget code example:
const MagicText magicText = MagicText( "Your text here.", breakCharacter: '-', smartSizeMode: true, asyncMode: true, minFontSize: 20, maxFontSize: 40, textStyle: const TextStyle( fontSize: 20, //It is mandatory that the textStyle has a fontsize. fontWeight: FontWeight.bold ), );

@flutter-triage-bot flutter-triage-bot bot added P3 Issues that are less important to the Flutter project and removed P6 labels Jun 28, 2023
@flutter-triage-bot flutter-triage-bot bot added team-engine Owned by Engine team triaged-engine Triaged by Engine team labels Jul 8, 2023
@laeo
Copy link

laeo commented Aug 17, 2023

Facing same problem, zero-width char broken text copy in third-party libraries.

@EnriqueSanVic
Copy link

Facing same problem, zero-width char broken text copy in third-party libraries.

You can try magic_text widget to solve it. This widget wrap the text and handle word break character automatically.

https://pub.dev/packages/magic_text

@utopicnarwhal
Copy link

I came up with this solution.
What do you think?

It looks like that:

import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        body: Center(
          child: SingleChildScrollView(
            child: SelectionArea(
              child: Text.rich(
                BreakAllTextSpan(
                    '''Loremipsumdolorsitamet, consecteturadipiscingelit.Sedauctoreuismodplacerat.Crassuscipitatmassasedpulvina.Inpurusnisl,bibendumsitametdolorac,feugiat mattis risus. Maecenas vehicula lobortis justo, nec tincidunt nulla convallis ac. Nam nulla lacus, tincidunt id malesuada in, ultricies non enim. Morbi non sodales ex. Integer vitae odio sit amet nisi finibus malesuada ut in libero. Sed auctor tempus nisl. Donec et scelerisque nunc. Quisque rhoncus nunc quam. Aliquam erat volutpat. Integer condimentum sit amet quam ac accumsan. Donec porttitor faucibus vulputate. Ut sollicitudin vestibulum augue vel suscipit.'''),
              ),
            ),
          ),
        ),
      ),
    );
  }
}

class BreakAllTextSpan extends TextSpan {
  BreakAllTextSpan(String text) : super(text: text.breakString);

  @override
  void computeToPlainText(StringBuffer buffer,
      {bool includeSemanticsLabels = true, bool includePlaceholders = true}) {
    if (semanticsLabel != null && includeSemanticsLabels) {
      buffer.write(semanticsLabel);
    } else if (text != null) {
      buffer.write(text?.unbreakString);
    }
    if (children != null) {
      for (final child in children!) {
        child.computeToPlainText(
          buffer,
          includeSemanticsLabels: includeSemanticsLabels,
          includePlaceholders: includePlaceholders,
        );
      }
    }
  }
}

const zeroWidthSpace = '\u200B';

extension StringExtension on String {
  String get breakString {
    // Makes the string in [Text] widget behave like with CSS style `word-break: break-all;`
    // (src: https://github.com/flutter/flutter/issues/61081#issuecomment-1103330522)
    return runes
        .map<String>(
            (element) => '${String.fromCharCode(element)}$zeroWidthSpace')
        .join();
  }

  String get unbreakString {
    return replaceAll(zeroWidthSpace, String.fromCharCode(0xFFFC));
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
a: typography Text rendering, possibly libtxt c: new feature Nothing broken; request for a new capability engine flutter/engine repository. See also e: labels. framework flutter/packages/flutter repository. See also f: labels. P3 Issues that are less important to the Flutter project team-engine Owned by Engine team triaged-engine Triaged by Engine team
Projects
None yet
Development

No branches or pull requests