Skip to content

[flutter_markdown] MarkdownElementBuilder Fails to Support WidgetSpans in Text.rich #144383

@pzierahn

Description

@pzierahn

Steps to reproduce

  1. Set up a custom MarkdownElementBuilder
  2. Inside the visitElementAfterWithContext function return a Text.rich with a WidgetSpan

Expected results

The markdown library should support WidgetSpan inside Text.rich

Actual results

A error is thrown: type 'WidgetSpan' is not a subtype of type 'TextSpan' in type cast

Code sample

Code sample
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:markdown/markdown.dart' as md;

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const MyHomePage(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Markdown Demo'),
      ),
      body: Markdown(
        data: 'The terms foobar ...',
        builders: <String, MarkdownElementBuilder>{
          'sub': SubscriptBuilder(),
        },
        extensionSet: md.ExtensionSet(
          <md.BlockSyntax>[],
          <md.InlineSyntax>[SubscriptSyntax()],
        ),
      ),
    );
  }
}

class SubscriptBuilder extends MarkdownElementBuilder {
  @override
  Widget visitElementAfterWithContext(
    BuildContext context,
    md.Element element,
    TextStyle? preferredStyle,
    TextStyle? parentStyle,
  ) {
    // This where the error happens
    return Text.rich(WidgetSpan(
      alignment: PlaceholderAlignment.middle,
      child: Text(
        element.textContent,
        style: const TextStyle(color: Colors.red),
      ),
    ));
  }
}

class SubscriptSyntax extends md.InlineSyntax {
  SubscriptSyntax() : super(_pattern);

  static const String _pattern = r'(foo)';

  @override
  bool onMatch(md.InlineParser parser, Match match) {
    parser.addNode(md.Element.text('sub', match[1]!));
    return true;
  }
}

Screenshots or Video

Screenshots / Video demonstration

[Upload media here]

Logs

Logs
Performing hot restart...
Syncing files to device macOS...
Restarted application in 403ms.

======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building KeyedSubtree-[GlobalKey#2c996]:
type 'WidgetSpan' is not a subtype of type 'TextSpan' in type cast

The relevant error-causing widget was: 
  Scaffold Scaffold:file:///Users/patrick/Code/markdown_demo/lib/main.dart:30:12
When the exception was thrown, this was the stack: 
#0      MarkdownBuilder._mergeInlineChildren (package:flutter_markdown/src/builder.dart:727:38)
#1      MarkdownBuilder._addAnonymousBlockIfNeeded (package:flutter_markdown/src/builder.dart:689:42)
#2      MarkdownBuilder.visitElementAfter (package:flutter_markdown/src/builder.dart:373:7)
#3      Element.accept (package:markdown/src/ast.dart:53:15)
#4      MarkdownBuilder.build (package:flutter_markdown/src/builder.dart:193:12)
#5      _MarkdownWidgetState._parseMarkdown (package:flutter_markdown/src/widget.dart:360:25)
#6      _MarkdownWidgetState.didChangeDependencies (package:flutter_markdown/src/widget.dart:304:5)
#7      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5630:11)
#8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:5457:5)
...     Normal element mounting (25 frames)
#33     Element.inflateWidget (package:flutter/src/widgets/framework.dart:4336:16)
#34     MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6896:36)
#35     MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6908:32)
...     Normal element mounting (134 frames)
#169    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4336:16)
#170    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6896:36)
#171    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6908:32)
...     Normal element mounting (178 frames)
#349    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4336:16)
#350    MultiChildRenderObjectElement.inflateWidget (package:flutter/src/widgets/framework.dart:6896:36)
#351    MultiChildRenderObjectElement.mount (package:flutter/src/widgets/framework.dart:6908:32)
...     Normal element mounting (514 frames)
#865    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4336:16)
#866    Element.updateChild (package:flutter/src/widgets/framework.dart:3847:18)
#867    _RawViewElement._updateChild (package:flutter/src/widgets/view.dart:291:16)
#868    _RawViewElement.mount (package:flutter/src/widgets/view.dart:314:5)
...     Normal element mounting (7 frames)
#875    Element.inflateWidget (package:flutter/src/widgets/framework.dart:4336:16)
#876    Element.updateChild (package:flutter/src/widgets/framework.dart:3847:18)
#877    RootElement._rebuild (package:flutter/src/widgets/binding.dart:1434:16)
#878    RootElement.mount (package:flutter/src/widgets/binding.dart:1403:5)
#879    RootWidget.attach.<anonymous closure> (package:flutter/src/widgets/binding.dart:1356:18)
#880    BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2845:19)
#881    RootWidget.attach (package:flutter/src/widgets/binding.dart:1355:13)
#882    WidgetsBinding.attachToBuildOwner (package:flutter/src/widgets/binding.dart:1092:27)
#883    WidgetsBinding.attachRootWidget (package:flutter/src/widgets/binding.dart:1074:5)
#884    WidgetsBinding.scheduleAttachRootWidget.<anonymous closure> (package:flutter/src/widgets/binding.dart:1060:7)
#888    _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
(elided 3 frames from class _Timer and dart:async-patch)
====================================================================================================

======== Exception caught by widgets library =======================================================
The following _TypeError was thrown building KeyedSubtree-[GlobalKey#2c996]:
type 'WidgetSpan' is not a subtype of type 'TextSpan' in type cast

The relevant error-causing widget was: 
  Scaffold Scaffold:file:///Users/patrick/Code/markdown_demo/lib/main.dart:30:12
When the exception was thrown, this was the stack: 
#0      MarkdownBuilder._mergeInlineChildren (package:flutter_markdown/src/builder.dart:727:38)
#1      MarkdownBuilder._addAnonymousBlockIfNeeded (package:flutter_markdown/src/builder.dart:689:42)
#2      MarkdownBuilder.visitElementAfter (package:flutter_markdown/src/builder.dart:373:7)
#3      Element.accept (package:markdown/src/ast.dart:53:15)
#4      MarkdownBuilder.build (package:flutter_markdown/src/builder.dart:193:12)
#5      _MarkdownWidgetState._parseMarkdown (package:flutter_markdown/src/widget.dart:360:25)
#6      _MarkdownWidgetState.didChangeDependencies (package:flutter_markdown/src/widget.dart:304:5)
#7      StatefulElement._firstBuild (package:flutter/src/widgets/framework.dart:5630:11)
#8      ComponentElement.mount (package:flutter/src/widgets/framework.dart:5457:5)
#9      Element.inflateWidget (package:flutter/src/widgets/framework.dart:4336:16)
#10     Element.updateChild (package:flutter/src/widgets/framework.dart:3841:20)
#11     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#12     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#13     StatelessElement.update (package:flutter/src/widgets/framework.dart:5557:5)
#14     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#15     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#16     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#17     StatelessElement.update (package:flutter/src/widgets/framework.dart:5557:5)
#18     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#19     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#20     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#21     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#22     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#23     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#24     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#25     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#26     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#27     Element.updateChildren (package:flutter/src/widgets/framework.dart:3974:32)
#28     MultiChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6921:17)
#29     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#30     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#31     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#32     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#33     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#34     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#35     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#36     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#37     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#38     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#39     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#40     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#41     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#42     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#43     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#44     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#45     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#46     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#47     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#48     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#49     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#50     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#51     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#52     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#53     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6768:14)
#54     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#55     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#56     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#57     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#58     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#59     SingleChildRenderObjectElement.update (package:flutter/src/widgets/framework.dart:6768:14)
#60     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#61     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#62     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#63     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#64     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#65     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#66     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#67     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#68     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#69     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#70     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#71     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#72     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#73     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#74     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#75     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#76     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#77     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#78     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#79     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#80     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#81     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#82     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#83     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#84     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#85     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#86     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#87     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#88     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#89     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#90     ProxyElement.update (package:flutter/src/widgets/framework.dart:5810:5)
#91     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#92     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#93     StatefulElement.performRebuild (package:flutter/src/widgets/framework.dart:5644:11)
#94     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#95     StatefulElement.update (package:flutter/src/widgets/framework.dart:5667:5)
#96     Element.updateChild (package:flutter/src/widgets/framework.dart:3825:15)
#97     ComponentElement.performRebuild (package:flutter/src/widgets/framework.dart:5506:16)
#98     Element.rebuild (package:flutter/src/widgets/framework.dart:5197:7)
#99     BuildOwner.buildScope (package:flutter/src/widgets/framework.dart:2905:19)
#100    WidgetsBinding.drawFrame (package:flutter/src/widgets/binding.dart:993:21)
#101    RendererBinding._handlePersistentFrameCallback (package:flutter/src/rendering/binding.dart:443:5)
#102    SchedulerBinding._invokeFrameCallback (package:flutter/src/scheduler/binding.dart:1387:15)
#103    SchedulerBinding.handleDrawFrame (package:flutter/src/scheduler/binding.dart:1312:9)
#104    SchedulerBinding.scheduleWarmUpFrame.<anonymous closure> (package:flutter/src/scheduler/binding.dart:1035:7)
#108    _RawReceivePort._handleMessage (dart:isolate-patch/isolate_patch.dart:184:12)
(elided 3 frames from class _Timer and dart:async-patch)
====================================================================================================

Flutter Doctor output

Doctor output
[✓] Flutter (Channel beta, 3.20.0-1.2.pre, on macOS 14.3.1 23D60 darwin-arm64, locale en-US)
    • Flutter version 3.20.0-1.2.pre on channel beta at /Users/patrick/Development/flutter
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 1751123cde (7 days ago), 2024-02-21 22:06:22 -0800
    • Engine revision 299e852cb9
    • Dart version 3.4.0 (build 3.4.0-99.1.beta)
    • DevTools version 2.31.0

[!] Android toolchain - develop for Android devices (Android SDK version 34.0.0)
    • Android SDK at /Users/patrick/Library/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/docs/get-started/install/macos#android-setup for more details.

[✓] Xcode - develop for iOS and macOS (Xcode 15.2)
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 15C500b
    • CocoaPods version 1.15.2

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • 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.7+0-17.0.7b1000.6-10550314)

[✓] VS Code (version 1.87.0)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension can be installed from:
      🔨 https://marketplace.visualstudio.com/items?itemName=Dart-Code.flutter

[✓] Connected device (5 available)
    • macOS (desktop)                 • macos                     • darwin-arm64   • macOS 14.3.1 23D60 darwin-arm64
    • Mac Designed for iPad (desktop) • mac-designed-for-ipad     • darwin         • macOS 14.3.1 23D60 darwin-arm64
    • Chrome (web)                    • chrome                    • web-javascript • Google Chrome 122.0.6261.94
    ! Error: Browsing on the local area network for Patrick’s Apple Watch. Ensure the device is unlocked and discoverable via Bluetooth. (code -27)

[✓] Network resources
    • All expected network resources are available.

! Doctor found issues in 1 category.

Metadata

Metadata

Assignees

No one assigned

    Labels

    found in release: 3.19Found to occur in 3.19found in release: 3.20Found to occur in 3.20has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: flutter_markdownflutter/packages flutter_markdownpackageflutter/packages repository. See also p: labels.r: fixedIssue is closed as already fixed in a newer versionteam-ecosystemOwned by Ecosystem team

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions