flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.4k stars 27.29k forks source link

Blockquotes hard wrap when they should soft wrap like a normal paragraph #156554

Open andrechalella opened 1 day ago

andrechalella commented 1 day ago

What package does this bug report belong to?

flutter_markdown

What target platforms are you seeing this bug on?

Android, Windows

Have you already upgraded your packages?

Yes

Dependency versions

pubspec.lock ```lock # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: args: dependency: transitive description: name: args sha256: "7cf60b9f0cc88203c5a190b4cd62a99feea42759a7fa695010eb5de1c0b2252a" url: "https://pub.dev" source: hosted version: "2.5.0" async: dependency: transitive description: name: async sha256: "947bfcf187f74dbc5e146c9eb9c0f10c9f8b30743e341481c1e2ed3ecc18c20c" url: "https://pub.dev" source: hosted version: "2.11.0" boolean_selector: dependency: transitive description: name: boolean_selector sha256: "6cfb5af12253eaf2b368f07bacc5a80d1301a071c73360d746b7f2e32d762c66" url: "https://pub.dev" source: hosted version: "2.1.1" characters: dependency: transitive description: name: characters sha256: "04a925763edad70e8443c99234dc3328f442e811f1d8fd1a72f1c8ad0f69a605" url: "https://pub.dev" source: hosted version: "1.3.0" clock: dependency: transitive description: name: clock sha256: cb6d7f03e1de671e34607e909a7213e31d7752be4fb66a86d29fe1eb14bfb5cf url: "https://pub.dev" source: hosted version: "1.1.1" collection: dependency: transitive description: name: collection sha256: ee67cb0715911d28db6bf4af1026078bd6f0128b07a5f66fb2ed94ec6783c09a url: "https://pub.dev" source: hosted version: "1.18.0" cross_file: dependency: transitive description: name: cross_file sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670" url: "https://pub.dev" source: hosted version: "0.3.4+2" fake_async: dependency: transitive description: name: fake_async sha256: "511392330127add0b769b75a987850d136345d9227c6b94c96a04cf4a391bf78" url: "https://pub.dev" source: hosted version: "1.3.1" ffi: dependency: transitive description: name: ffi sha256: "16ed7b077ef01ad6170a3d0c57caa4a112a38d7a2ed5602e0aca9ca6f3d98da6" url: "https://pub.dev" source: hosted version: "2.1.3" file: dependency: transitive description: name: file sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 url: "https://pub.dev" source: hosted version: "7.0.1" file_selector: dependency: "direct main" description: name: file_selector sha256: "5019692b593455127794d5718304ff1ae15447dea286cdda9f0db2a796a1b828" url: "https://pub.dev" source: hosted version: "1.0.3" file_selector_android: dependency: transitive description: name: file_selector_android sha256: "00aafa9ae05a8663d0b4f17abd2a02316911ca0f46f9b9dacb9578b324d99590" url: "https://pub.dev" source: hosted version: "0.5.1+9" file_selector_ios: dependency: transitive description: name: file_selector_ios sha256: "94b98ad950b8d40d96fee8fa88640c2e4bd8afcdd4817993bd04e20310f45420" url: "https://pub.dev" source: hosted version: "0.5.3+1" file_selector_linux: dependency: transitive description: name: file_selector_linux sha256: "712ce7fab537ba532c8febdb1a8f167b32441e74acd68c3ccb2e36dcb52c4ab2" url: "https://pub.dev" source: hosted version: "0.9.3" file_selector_macos: dependency: transitive description: name: file_selector_macos sha256: "271ab9986df0c135d45c3cdb6bd0faa5db6f4976d3e4b437cf7d0f258d941bfc" url: "https://pub.dev" source: hosted version: "0.9.4+2" file_selector_platform_interface: dependency: transitive description: name: file_selector_platform_interface sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b url: "https://pub.dev" source: hosted version: "2.6.2" file_selector_web: dependency: transitive description: name: file_selector_web sha256: c4c0ea4224d97a60a7067eca0c8fd419e708ff830e0c83b11a48faf566cec3e7 url: "https://pub.dev" source: hosted version: "0.9.4+2" file_selector_windows: dependency: transitive description: name: file_selector_windows sha256: "8f5d2f6590d51ecd9179ba39c64f722edc15226cc93dcc8698466ad36a4a85a4" url: "https://pub.dev" source: hosted version: "0.9.3+3" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" flutter_lints: dependency: "direct dev" description: name: flutter_lints sha256: "3f41d009ba7172d5ff9be5f6e6e6abb4300e263aab8866d2a0842ed2a70f8f0c" url: "https://pub.dev" source: hosted version: "4.0.0" flutter_markdown: dependency: "direct main" description: name: flutter_markdown sha256: e17575ca576a34b46c58c91f9948891117a1bd97815d2e661813c7f90c647a78 url: "https://pub.dev" source: hosted version: "0.7.3+2" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" flutter_web_plugins: dependency: transitive description: flutter source: sdk version: "0.0.0" http: dependency: transitive description: name: http sha256: b9c29a161230ee03d3ccf545097fccd9b87a5264228c5d348202e0f0c28f9010 url: "https://pub.dev" source: hosted version: "1.2.2" http_parser: dependency: transitive description: name: http_parser sha256: "2aa08ce0341cc9b354a498388e30986515406668dbcc4f7c950c3e715496693b" url: "https://pub.dev" source: hosted version: "4.0.2" leak_tracker: dependency: transitive description: name: leak_tracker sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05" url: "https://pub.dev" source: hosted version: "10.0.5" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806" url: "https://pub.dev" source: hosted version: "3.0.5" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted version: "3.0.1" lints: dependency: transitive description: name: lints sha256: "976c774dd944a42e83e2467f4cc670daef7eed6295b10b36ae8c85bcbf828235" url: "https://pub.dev" source: hosted version: "4.0.0" markdown: dependency: "direct main" description: name: markdown sha256: ef2a1298144e3f985cc736b22e0ccdaf188b5b3970648f2d9dc13efd1d9df051 url: "https://pub.dev" source: hosted version: "7.2.2" matcher: dependency: transitive description: name: matcher sha256: d2323aa2060500f906aa31a895b4030b6da3ebdcc5619d14ce1aada65cd161cb url: "https://pub.dev" source: hosted version: "0.12.16+1" material_color_utilities: dependency: transitive description: name: material_color_utilities sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec url: "https://pub.dev" source: hosted version: "0.11.1" meta: dependency: transitive description: name: meta sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7 url: "https://pub.dev" source: hosted version: "1.15.0" path: dependency: "direct main" description: name: path sha256: "087ce49c3f0dc39180befefc60fdb4acd8f8620e5682fe2476afd0b3688bb4af" url: "https://pub.dev" source: hosted version: "1.9.0" path_provider: dependency: "direct main" description: name: path_provider sha256: fec0d61223fba3154d87759e3cc27fe2c8dc498f6386c6d6fc80d1afdd1bf378 url: "https://pub.dev" source: hosted version: "2.1.4" path_provider_android: dependency: transitive description: name: path_provider_android sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a url: "https://pub.dev" source: hosted version: "2.2.12" path_provider_foundation: dependency: transitive description: name: path_provider_foundation sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 url: "https://pub.dev" source: hosted version: "2.4.0" path_provider_linux: dependency: transitive description: name: path_provider_linux sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 url: "https://pub.dev" source: hosted version: "2.2.1" path_provider_platform_interface: dependency: transitive description: name: path_provider_platform_interface sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" url: "https://pub.dev" source: hosted version: "2.1.2" path_provider_windows: dependency: transitive description: name: path_provider_windows sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 url: "https://pub.dev" source: hosted version: "2.3.0" platform: dependency: transitive description: name: platform sha256: "9b71283fc13df574056616011fb138fd3b793ea47cc509c189a6c3fa5f8a1a65" url: "https://pub.dev" source: hosted version: "3.1.5" plugin_platform_interface: dependency: transitive description: name: plugin_platform_interface sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02" url: "https://pub.dev" source: hosted version: "2.1.8" shared_preferences: dependency: "direct main" description: name: shared_preferences sha256: "746e5369a43170c25816cc472ee016d3a66bc13fcf430c0bc41ad7b4b2922051" url: "https://pub.dev" source: hosted version: "2.3.2" shared_preferences_android: dependency: transitive description: name: shared_preferences_android sha256: "3b9febd815c9ca29c9e3520d50ec32f49157711e143b7a4ca039eb87e8ade5ab" url: "https://pub.dev" source: hosted version: "2.3.3" shared_preferences_foundation: dependency: transitive description: name: shared_preferences_foundation sha256: "07e050c7cd39bad516f8d64c455f04508d09df104be326d8c02551590a0d513d" url: "https://pub.dev" source: hosted version: "2.5.3" shared_preferences_linux: dependency: transitive description: name: shared_preferences_linux sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" url: "https://pub.dev" source: hosted version: "2.4.1" shared_preferences_platform_interface: dependency: transitive description: name: shared_preferences_platform_interface sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" url: "https://pub.dev" source: hosted version: "2.4.1" shared_preferences_web: dependency: transitive description: name: shared_preferences_web sha256: d2ca4132d3946fec2184261726b355836a82c33d7d5b67af32692aff18a4684e url: "https://pub.dev" source: hosted version: "2.4.2" shared_preferences_windows: dependency: transitive description: name: shared_preferences_windows sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" url: "https://pub.dev" source: hosted version: "2.4.1" sky_engine: dependency: transitive description: flutter source: sdk version: "0.0.99" source_span: dependency: transitive description: name: source_span sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted version: "1.10.0" stack_trace: dependency: transitive description: name: stack_trace sha256: "73713990125a6d93122541237550ee3352a2d84baad52d375a4cad2eb9b7ce0b" url: "https://pub.dev" source: hosted version: "1.11.1" stream_channel: dependency: transitive description: name: stream_channel sha256: ba2aa5d8cc609d96bbb2899c28934f9e1af5cddbd60a827822ea467161eb54e7 url: "https://pub.dev" source: hosted version: "2.1.2" string_scanner: dependency: transitive description: name: string_scanner sha256: "556692adab6cfa87322a115640c11f13cb77b3f076ddcc5d6ae3c20242bedcde" url: "https://pub.dev" source: hosted version: "1.2.0" term_glyph: dependency: transitive description: name: term_glyph sha256: a29248a84fbb7c79282b40b8c72a1209db169a2e0542bce341da992fe1bc7e84 url: "https://pub.dev" source: hosted version: "1.2.1" test_api: dependency: transitive description: name: test_api sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb" url: "https://pub.dev" source: hosted version: "0.7.2" typed_data: dependency: transitive description: name: typed_data sha256: facc8d6582f16042dd49f2463ff1bd6e2c9ef9f3d5da3d9b087e244a7b564b3c url: "https://pub.dev" source: hosted version: "1.3.2" vector_math: dependency: transitive description: name: vector_math sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" url: "https://pub.dev" source: hosted version: "2.1.4" vm_service: dependency: transitive description: name: vm_service sha256: "5c5f338a667b4c644744b661f309fb8080bb94b18a7e91ef1dbd343bed00ed6d" url: "https://pub.dev" source: hosted version: "14.2.5" web: dependency: transitive description: name: web sha256: cd3543bd5798f6ad290ea73d210f423502e71900302dde696f8bff84bf89a1cb url: "https://pub.dev" source: hosted version: "1.1.0" xdg_directories: dependency: transitive description: name: xdg_directories sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" url: "https://pub.dev" source: hosted version: "1.1.0" sdks: dart: ">=3.5.2 <4.0.0" flutter: ">=3.24.0" ```

Steps to reproduce

Run Markdown() with any text that has a > blockquote) with multiple lines.

Expected results

Text in blockquote should be in the same line ("hello my friend").

Actual results

Text in blockquote is split in two lines like: hello my friend

Code sample

Code sample ```dart Markdown(data: '''lets test what happens when we use multiple lines > hello > my friend oops ''') ```

Screenshots or Videos

Screenshots / Video demonstration ![image](https://github.com/user-attachments/assets/c3031d5a-4a8d-4916-93f0-9988f966e80f)

Logs

No response

Flutter Doctor output

Doctor output ```console repository https://github.com/flutter/flutter.git • Framework revision 4cf269e36d (5 weeks ago), 2024-09-03 14:30:00 -0700 • Engine revision a6bd3f1de1 • Dart version 3.5.2 • DevTools version 2.37.2 [✓] Windows Version (Installed version of Windows is version 10 or higher) [!] Android toolchain - develop for Android devices (Android SDK version 35.0.0) • Android SDK at C:\Users\andre\AppData\Local\Android\sdk ✗ cmdline-tools component is missing Run `path/to/sdkmanager --install "cmdline-tools;latest"` See https://developer.android.com/studio/command-line for more details. ✗ Android license status unknown. Run `flutter doctor --android-licenses` to accept the SDK licenses. See https://flutter.dev/to/windows-android-setup for more details. [✓] Chrome - develop for the web • Chrome at C:\Program Files\Google\Chrome\Application\chrome.exe [✓] Visual Studio - develop Windows apps (Visual Studio Community 2022 17.11.5) • Visual Studio at C:\Program Files\Microsoft Visual Studio\2022\Community • Visual Studio Community 2022 version 17.11.35327.3 • Windows 10 SDK version 10.0.22621.0 [✓] Android Studio (version 2024.1) • Android Studio at C:\Program Files\Android\Android Studio • Flutter plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/9212-flutter • Dart plugin can be installed from: 🔨 https://plugins.jetbrains.com/plugin/6351-dart • Java version OpenJDK Runtime Environment (build 17.0.11+0--11852314) [✓] VS Code (version 1.94.2) • VS Code at C:\Users\andre\AppData\Local\Programs\Microsoft VS Code • Flutter extension version 3.98.0 [✓] Connected device (3 available) • Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.22631.4169] • Chrome (web) • chrome • web-javascript • Google Chrome 129.0.6668.90 • Edge (web) • edge • web-javascript • Microsoft Edge 128.0.2739.67 ! Device 192.168.0.195:5555 is offline. [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ```
andrechalella commented 1 day ago

Some notes:

  1. All online Markdown live previews will soft wrap blockquotes, including Dart Markdown Live Editor
  2. This problem is not on markdown's end. A quick test with only markdown showed it outputs the blockquote properly, like:
    <blockquote>
    <p>hello
    my friend</p>
    </blockquote>
  3. I used the Flutter Inspector in the Markdown widget and found that the "offending" paragraph is a single Text widget whose content DOES CONTAIN the line break, like hello\nmy friend.
  4. On the other hand, the correct paragraph ("lets test what happens when we use multiple lines") is a single Text widget WITHOUT any line breaks.
andrechalella commented 1 day ago

This marked line in builder.dart caught my eye:

      child = _buildRichText(
        TextSpan(
          style: _isInBlockquote
              ? styleSheet.blockquote!.merge(_inlines.last.style)
              : _inlines.last.style,
>>>       text: _isInBlockquote ? text.text : trimText(text.text),
          recognizer: _linkHandlers.isNotEmpty ? _linkHandlers.last : null,
        ),
        textAlign: _textAlignForBlockTag(_currentBlockTag),
      );

Why is text in a blockquote treated specially like that? Maybe <blockquote> is being mixed up with <pre> in this regard?

Seems to me text in a blockquote should be trimmed just like in a normal paragraph.

andrechalella commented 1 day ago

I changed the line to

text: trimText(text.text),

and now it works as expected. I wonder if that breaks something.

nate-thegrate commented 1 day ago

I changed the line to

text: trimText(text.text),

and now it works as expected. I wonder if that breaks something.

Glad to hear it! If you'd like to open a PR in the flutter/packages repo then I'd be happy to take a look. (The alternative would be to send this issue through the normal triage process and see if anybody else decides to pick it up.)