Sub6Resources / flutter_html

A Flutter widget for rendering static html as Flutter widgets (Will render over 80 different html tags!)
https://pub.dev/packages/flutter_html
MIT License
1.8k stars 875 forks source link

[BUG] [3.0.0-alpha.6] _RenderCSSBox cannot compute max intrinsic height #1165

Open tomk9 opened 2 years ago

tomk9 commented 2 years ago

Describe the bug:

I use Html widget nested in IntrinsicHeight and SingleChildScrollView as follows:

import 'package:flutter/material.dart';
import 'package:flutter_html/flutter_html.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      home: Scaffold(
        appBar: AppBar(),
        body: LayoutBuilder(
          builder: (context, constraints) {
            return SingleChildScrollView(
              child: ConstrainedBox(
                constraints: BoxConstraints(
                  minHeight: constraints.maxHeight,
                ),
                child: IntrinsicHeight(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.stretch,
                    children: [
                      Html(
                        data: 'Test',
                      ),
                      const Spacer(),
                      Container(
                        height: 200,
                        color: Colors.red,
                      ),
                    ],
                  ),
                ),
              ),
            );
          },
        ),
      ),
    );
  }
}

It works well with flutter_html: 3.0.0-alpha.5 but doesn't work with flutter_html: 3.0.0-alpha.6. Exception message is

Intrinsics are not available for PlaceholderAlignment.baseline, PlaceholderAlignment.aboveBaseline, or PlaceholderAlignment.belowBaseline.

Expected behavior:

Should render as on the screenshot from version 3.0.0-alpha.5.

Screenshots:

3.0.0-alpha.5 3.0.0-alpha.6
Simulator Screen Shot - iPhone 14 Pro Max - 2022-10-13 at 23 45 01 Simulator Screen Shot - iPhone 14 Pro Max - 2022-10-13 at 23 49 32

Device details and Flutter/Dart/flutter_html versions:

[✓] Flutter (Channel stable, 3.3.4, on macOS 12.6 21G115 darwin-arm, locale en-PL)
    • Flutter version 3.3.4 on channel stable at /Users/tomasz/development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision eb6d86ee27 (9 days ago), 2022-10-04 22:31:45 -0700
    • Engine revision c08d7d5efc
    • Dart version 2.18.2
    • DevTools version 2.15.0
Stacktrace/Logcat ``` _AssertionError._doThrowNew (errors_patch.dart:51) _AssertionError._throwNew (errors_patch.dart:40) RenderParagraph._canComputeIntrinsics (paragraph.dart:502) RenderParagraph._computeIntrinsicHeight (paragraph.dart:459) RenderParagraph.computeMaxIntrinsicHeight (paragraph.dart:474) RenderBox._computeIntrinsicDimension. (box.dart:1416) _LinkedHashMapMixin.putIfAbsent (compact_hash.dart:527) RenderBox._computeIntrinsicDimension (box.dart:1414) RenderBox.getMaxIntrinsicHeight (box.dart:1781) RenderPadding.computeMaxIntrinsicHeight (shifted_box.dart:213) RenderBox._computeIntrinsicDimension. (box.dart:1416) _LinkedHashMapMixin.putIfAbsent (compact_hash.dart:527) RenderBox._computeIntrinsicDimension (box.dart:1414) RenderBox.getMaxIntrinsicHeight (box.dart:1781) RenderProxyBoxMixin.computeMaxIntrinsicHeight (proxy_box.dart:96) RenderBox._computeIntrinsicDimension. (box.dart:1416) _LinkedHashMapMixin.putIfAbsent (compact_hash.dart:527) RenderBox._computeIntrinsicDimension (box.dart:1414) RenderBox.getMaxIntrinsicHeight (box.dart:1781) _RenderCSSBox.computeMaxIntrinsicHeight. (css_box_widget.dart:418) _RenderCSSBox.getIntrinsicDimension (css_box_widget.dart:390) _RenderCSSBox.computeMaxIntrinsicHeight (css_box_widget.dart:417) RenderBox._computeIntrinsicDimension. (box.dart:1416) _LinkedHashMapMixin.putIfAbsent (compact_hash.dart:527) RenderBox._computeIntrinsicDimension (box.dart:1414) RenderBox.getMaxIntrinsicHeight (box.dart:1781) RenderFlex.computeMaxIntrinsicHeight. (flex.dart:636) RenderFlex._getIntrinsicSize (flex.dart:544) RenderFlex.computeMaxIntrinsicHeight (flex.dart:633) RenderBox._computeIntrinsicDimension. (box.dart:1416) _LinkedHashMapMixin.putIfAbsent (compact_hash.dart:527) RenderBox._computeIntrinsicDimension (box.dart:1414) RenderBox.getMaxIntrinsicHeight (box.dart:1781) RenderIntrinsicHeight._computeSize (proxy_box.dart:833) RenderIntrinsicHeight.performLayout (proxy_box.dart:853) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderConstrainedBox.performLayout (proxy_box.dart:292) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) _RenderSingleChildViewport.performLayout (single_child_scroll_view.dart:517) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) _RenderLayoutBuilder.performLayout (layout_builder.dart:318) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) MultiChildLayoutDelegate.layoutChild (custom_layout.dart:171) _ScaffoldLayout.performLayout (scaffold.dart:1055) MultiChildLayoutDelegate._callPerformLayout (custom_layout.dart:240) RenderCustomMultiChildLayoutBox.performLayout (custom_layout.dart:410) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) _RenderCustomClip.performLayout (proxy_box.dart:1462) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) ChildLayoutHelper.layoutChild (layout_helper.dart:56) RenderStack._computeSize (stack.dart:595) RenderStack.performLayout (stack.dart:622) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderOffstage.performLayout (proxy_box.dart:3737) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) _RenderTheatre.performLayout (overlay.dart:804) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderCustomPaint.performLayout (custom_paint.dart:552) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderProxyBoxMixin.performLayout (proxy_box.dart:120) RenderObject.layout (object.dart:2135) RenderBox.layout (box.dart:2418) RenderView.performLayout (view.dart:170) RenderObject._layoutWithoutResize (object.dart:1973) PipelineOwner.flushLayout (object.dart:999) RendererBinding.drawFrame (binding.dart:513) WidgetsBinding.drawFrame (binding.dart:884) RendererBinding._handlePersistentFrameCallback (binding.dart:378) SchedulerBinding._invokeFrameCallback (binding.dart:1175) SchedulerBinding.handleDrawFrame (binding.dart:1104) SchedulerBinding.scheduleWarmUpFrame. (binding.dart:881) Timer._createTimer. (timer_patch.dart:18) _Timer._runTimers (timer_impl.dart:398) _Timer._handleMessage (timer_impl.dart:429) _RawReceivePortImpl._handleMessage (isolate_patch.dart:192) ```
Sub6Resources commented 2 years ago

Thank you. I'm able to reproduce the issue. Looking into it now

Sub6Resources commented 2 years ago

I've identified the issue as related to https://github.com/flutter/flutter/issues/65895. The assertion can safely be ignored in this case. I'll do some additional testing to ensure this is handled correctly in future versions, but a temporary workaround is to add RenderObject.debugCheckingIntrinsics = true; to the beginning of your build method.

darkstarx commented 2 years ago

@Sub6Resources just make it possible to change WidgetSpan.alignment outside the widget. It's hardcoded in your code now:

      return WidgetSpan(
        alignment: PlaceholderAlignment.baseline,
        baseline: TextBaseline.alphabetic,
        child: CssBoxWidget.withInlineSpanChildren(
          key: context.key,
...
darkstarx commented 2 years ago

Another easy way is to expose the property selectable outside the HtmlParser. It's hardcoded now, just make it customizable in the Html widget. So, making the widget selectable can help me workaround the issue with alphabetic alignment.

However, the most flexible and successful solution is to make both of this properties (selectable in Html and placeholderAlignment in Style) customizable.

erickok commented 2 years ago

I respectfully vote against making it an option. This should work out of the box and if that means not using baseline alignment than that's probably fine.

darkstarx commented 2 years ago

I respectfully vote against making it an option. This should work out of the box and if that means not using baseline alignment than that's probably fine.

Why? Optionality is always useful and can't be a problem anyway. Also it's impossible to use baseline alignment in the IntrinsicHeight due to the issue in the FlutterSDK which @Sub6Resources mentioned. Thus, you just have no choice. The only way is to help Flutter developers to fix the issue in the FlutterSDK. Can you? No? So, vote for optionality ;) If there is no any IntrinsicHeight widgets in a widget tree, why not to use baseline alignment? Somebody could need it.

And finally, Flutter developers afford themselves to have such optionality in the out-of-box widgets (Text, IntrinsicHeight are still exist in this world), so why not to have this optionality in other widgets? But I absolutely agree to have some notice about this issue in the documentation to this widget.

erickok commented 2 years ago

Optionally means more maintenance work and more support. Why not make sure IntrinsicHeight is supported out of the box so anyone can use it without having to raad documentation (best case scenario) on how to set up the right parameters?

It worked before we had CssBox so I'm sure we can work something out. Like using bottom alignment by default.

darkstarx commented 2 years ago

The work is already done. Your suggestion is to do additional work to remove already written code or replace it with another hardcoded values. I suggest just exposing a couple of properties for client code. Moreover, if there were optional properties, the widget would be ready to use when the Flutter issue is solved. If you're afraid of documentation, it's very easy to write assert in the Style constructor for the new placeholderAlignment property and make it bottom by default. What can be easier?

darkstarx commented 2 years ago

Anyway, here is my PR if you'd like the solution https://github.com/Sub6Resources/flutter_html/pull/1191

frankvollebregt commented 1 year ago

I was experiencing a similar issue, so I hadn't updated from 3.0.0-alpha.5 until now. Using the provided workaround didn't work for me

a temporary workaround is to add RenderObject.debugCheckingIntrinsics = true; to the beginning of your build method.

I had a Row wrapped in an IntrinsicHeight with two Columns, one of which contains an Html widget. If the height of the Html widget exceeded that of the other column, using this workaround would just make the Html widget overflow its contents outside of the Row instead of expanding the whole Row vertically until it fit. Without this workaround, it would not render at all.

Using the branch from the provided PR #1306, my issue has been resolved, and I can once again properly render my Widgets.

Is there any expectation for the timeline of this to be merged in a new beta version, or even a full release?

ThanhQuang1410 commented 5 months ago

@frankvollebregt can you show me how to use the branch from provided PR. I am using flutter_html: ^3.0.0-beta.2 and facing the similar issue

frankvollebregt commented 5 months ago

@ThanhQuang1410 in your pubspec.yaml, you can add a dependency override, so it looks like this:

dependencies:
  ...
  # This version is currently in your pubspec already
  flutter_html: 3.0.0-beta.2

dependency_overrides:
  # This is the version that is used for now (until, hopefully, this version gets released at some point)
  flutter_html:
    git:
      url: https://github.com/sub6resources/flutter_html.git
      ref: fix/intrinsics-and-table

See also the Flutter docs about dependency overrides

ThanhQuang1410 commented 5 months ago

Thank you @frankvollebregt . Now my app is rendering everything correctly

ilovesardine commented 1 week ago

@frankvollebregt thanks a lot, spent hours on this until i got to your reply.