diff --git a/README.md b/README.md
index bbe62b20b4..49f73985d7 100644
--- a/README.md
+++ b/README.md
@@ -37,6 +37,7 @@ These are the available packages in this repository.
| [css\_colors](./packages/css_colors/) | [](https://pub.dev/packages/css_colors) |
| [extension\_google\_sign\_in\_as\_googleapis\_auth](./packages/extension_google_sign_in_as_googleapis_auth/) | [](https://pub.dev/packages/extension_google_sign_in_as_googleapis_auth) |
| [fuchsia\_ctl](./packages/fuchsia_ctl/) | [](https://pub.dev/packages/fuchsia_ctl) |
+| [flutter\_markdown](./packages/flutter_markdown/) | [](https://pub.dev/packages/flutter_markdown) |
| [multicast\_dns](./packages/multicast_dns/) | [](https://pub.dev/packages/multicast_dns) |
| [palette\_generator](./packages/palette_generator/) | [](https://pub.dartlang.org/packages/palette_generator) |
| [pigeon](./packages/pigeon/) | [](https://pub.dev/packages/pigeon) |
diff --git a/packages/flutter_markdown/.github/ISSUE_TEMPLATE/bug_report.md b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 0000000000..4e9254e777
--- /dev/null
+++ b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,24 @@
+---
+name: Bug report
+about: Create an issue to report a bug
+title: 'Bug: '
+labels: ''
+assignees: ''
+
+---
+
+**Describe the bug**
+Please provide a clear and concise description of the bug.
+
+**How to reproduce**
+Steps to reproduce the behavior:
+1.
+
+**Expected behavior**
+A description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain the problem.
+
+**Additional context**
+Add any other context about the problem here.
diff --git a/packages/flutter_markdown/.github/ISSUE_TEMPLATE/config.yml b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/config.yml
new file mode 100644
index 0000000000..09d8e31088
--- /dev/null
+++ b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/config.yml
@@ -0,0 +1,3 @@
+# Use one of the three issue templates provided. The "Other issue"
+# template should be used instead of a blank issue.
+blank_issues_enabled: false
\ No newline at end of file
diff --git a/packages/flutter_markdown/.github/ISSUE_TEMPLATE/feature_request.md b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 0000000000..93e105c34f
--- /dev/null
+++ b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an feature enhancement or new functionality
+title: 'Feature: '
+labels: ''
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of the problem.
+
+**Describe a possible solution.**
+Do you have a possible solution or approach you think should be considered?
+
+**Describe possible alternatives.**
+Are there possible alternative solutions or features.
+
+**Additional context.**
+List any additional context or screenshots about the feature request here.
diff --git a/packages/flutter_markdown/.github/ISSUE_TEMPLATE/other_issue.md b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/other_issue.md
new file mode 100644
index 0000000000..92d3fda17a
--- /dev/null
+++ b/packages/flutter_markdown/.github/ISSUE_TEMPLATE/other_issue.md
@@ -0,0 +1,11 @@
+---
+name: Other issue
+about: Issues that are not bugs or feature requests.
+title: ''
+labels: ''
+assignees: ''
+
+---
+
+**Describe the issue**
+Please provide a clear and concise description of the issue.
diff --git a/packages/flutter_markdown/.github/pull_request_template.md b/packages/flutter_markdown/.github/pull_request_template.md
new file mode 100644
index 0000000000..e819a48d1c
--- /dev/null
+++ b/packages/flutter_markdown/.github/pull_request_template.md
@@ -0,0 +1,23 @@
+**IMPORTANT: Please do not create a Pull Request without creating an issue first.**
+
+*Any change needs to be discussed before proceeding. Failure to do so may result in the rejection of the pull request.*
+
+Please provide enough information so that others can review your pull request:
+
+
+
+**Test plan (required)**
+
+
+
+**Closing issues**
+
+
+
+**Additional considerations**
+
+
\ No newline at end of file
diff --git a/packages/flutter_markdown/.github/workflows/flutter_ci.yml b/packages/flutter_markdown/.github/workflows/flutter_ci.yml
new file mode 100644
index 0000000000..12e1facb5c
--- /dev/null
+++ b/packages/flutter_markdown/.github/workflows/flutter_ci.yml
@@ -0,0 +1,35 @@
+name: flutter_markdown
+
+on:
+ push:
+ branches: [ master ]
+ pull_request:
+ branches: [ master ]
+ workflow_dispatch:
+ schedule:
+ - cron: '0 0 * * *' # Every day at midnight
+
+defaults:
+ run:
+ shell: bash
+
+jobs:
+ flutter-tests:
+ name: Test Flutter ${{ matrix.flutter_version }} on ${{ matrix.os }}
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ flutter_version: [dev, beta] # Disable stable until Null Safety goes stable
+ os: [ubuntu-latest, macos-latest, windows-latest]
+ steps:
+ - uses: actions/checkout@v2
+ - uses: actions/setup-java@v1
+ with:
+ java-version: '12.x'
+ - uses: subosito/flutter-action@v1
+ with:
+ channel: ${{ matrix.flutter_version }}
+ - run: flutter analyze
+ - run: flutter format --dry-run --set-exit-if-changed .
+ - run: flutter test
diff --git a/packages/flutter_markdown/.gitignore b/packages/flutter_markdown/.gitignore
new file mode 100644
index 0000000000..437cb45872
--- /dev/null
+++ b/packages/flutter_markdown/.gitignore
@@ -0,0 +1,36 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Exceptions to above rules.
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/packages/flutter_markdown/.vscode/launch.json b/packages/flutter_markdown/.vscode/launch.json
new file mode 100644
index 0000000000..1c3102dbd3
--- /dev/null
+++ b/packages/flutter_markdown/.vscode/launch.json
@@ -0,0 +1,19 @@
+{
+ // Use IntelliSense to learn about possible attributes.
+ // Hover to view descriptions of existing attributes.
+ // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+ "version": "0.2.0",
+ "configurations": [
+ {
+ "name": "flutter_markdown",
+ "request": "launch",
+ "type": "dart"
+ },
+ {
+ "name": "example",
+ "cwd": "example",
+ "request": "launch",
+ "type": "dart"
+ }
+ ]
+}
\ No newline at end of file
diff --git a/packages/flutter_markdown/CHANGELOG.md b/packages/flutter_markdown/CHANGELOG.md
new file mode 100644
index 0000000000..cf6752919a
--- /dev/null
+++ b/packages/flutter_markdown/CHANGELOG.md
@@ -0,0 +1,159 @@
+## 0.6.2
+
+ * Updated metadata for new source location
+ * Style changes to conform to flutter/packages analyzer settings
+
+ ## 0.6.1
+
+ * Added builder option bulletBuilder
+
+## 0.6.0
+
+ * Null safety release
+ * Added stylesheet option listBulletPadding
+ * Fixed blockquote inline styling
+ * Added onTapText handler for selectable text
+
+## 0.6.0-nullsafety.2
+
+ * Dependencies updated for null safety
+
+## 0.6.0-nullsafety.1
+
+ * Fix null safety on web
+ * Image test mocks fixed for null safety
+
+## 0.6.0-nullsafety.0
+
+ * Initial null safety migration.
+
+## 0.5.2
+
+ * Added `MarkdownListItemCrossAxisAlignment` to allow for intrinsic height
+ measurements of lists.
+
+## 0.5.1
+
+ * Fix user defined builders
+
+## 0.5.0
+
+ * BREAKING CHANGE: `MarkdownTapLinkCallback` now has three parameters, not one, exposing more
+ information about a tapped link.
+ * Note for upgraders, the old single parameter `href` is now the second parameter to match the specification.
+ * Android example upgraded
+ * Test coverage updated to match GitHub Flavoured Markdown and CommonMark
+ * Handle links with empty descriptions
+ * Handle empty rows in tables
+
+## 0.4.4
+
+ * Fix handling of newline character in blockquote
+ * Add new example demo
+ * Use the start attribute in ordered list to set the first number
+ * Revert changes made in PR #235 (which broke newline handling)
+
+## 0.4.3
+
+ * Fix merging of `MarkdownStyleSheets`
+ * Fix `MarkdownStyleSheet` textScaleFactor to use default value of 1.0, if not provided, instead using the textScaleFactor of the nearest MediaQuery
+
+## 0.4.2
+
+ * Fix parsing of image caption & alt attributes
+ * Fix baseline alignment in lists
+ * Support `LineBreakSyntax`
+
+## 0.4.1
+
+ * Downgrade Flutter minimum from 1.17.1 to 1.17.0 for Pub
+
+## 0.4.0
+
+ * Updated for Flutter 1.17
+ * Ignore newlines in paragraphs
+ * Improve handling of horizontal rules
+
+## 0.3.5
+
+ * Fix hardcoded colors and improve Darktheme
+ * Fix text alignment when formatting is involved
+
+## 0.3.4
+
+ * Add support for text paragraphs and blockquotes.
+
+## 0.3.3
+
+ * Add the ability to control the scroll position of the `MarkdownWidget`.
+
+## 0.3.2
+
+ * Uplift `package:markdown` dependency version to enable deleting HTML unescape URI workaround
+ * Explictly state that Flutter 1.10.7 is the minimum supported Flutter version in the library `pubspec.yaml`.
+
+## 0.3.1
+
+ * Expose `tableColumnWidth`
+ * Add `MarkdownStyleSheet.fromCupertinoTheme`
+ * Fix `MarkdownStyleSheet.blockquote`
+ * Flutter for web support
+ * Add physic and shrinkWrap to Markdown widget
+ * Add MarkdownBody.fitContent
+ * Support select text to copy
+ * Fix list bullet alignment
+ * HTML unescape URIs (temporary workaround for [dart-lang/markdown #272](https://github.com/dart-lang/markdown/issues/272))
+ * Rebuilt `example/android` and `example/ios` directories
+
+**Note:** this version has an implicit minimum supported version of Flutter 1.10.7.
+See [flutter/flutter_markdown issue #156](https://github.com/flutter/flutter_markdown/issues/156) for more detail.
+
+## 0.3.0
+
+ * Support GitHub flavoured Markdown
+ * Support strikethrough
+ * Convert TextSpan to use new InlineSpan API
+
+## 0.2.0
+
+ * Updated environment sdk constraints to make the package
+ Dart 2 compatible. As a result, usage of this version and higher
+ requires a Dart 2 SDK.
+
+## 0.1.6
+
+ * Updated `markdown` dependency.
+
+## 0.1.5
+
+ * Add `mockito` as a dev dependency. Eliminate use of `package:http`, which
+ is no longer part of Flutter.
+
+## 0.1.4
+
+ * Add `li` style to bullets
+
+## 0.1.3
+
+ * Add `path` and `http` as declared dependencies in `pubspec.yaml`
+
+## 0.1.2
+
+ * Add support for horizontal rules.
+ * Fix the `onTap` callback on images nested in hyperlinks
+
+## 0.1.1
+
+ * Add support for local file paths in image links. Make sure to set the
+ `imageDirectory` property to specify the base directory containing the image
+ files.
+
+## 0.1.0
+
+ * Roll the dependency on `markdown` to 1.0.0
+ * Add a test and example for image links
+ * Fix the `onTap` callback on hyperlinks
+
+## 0.0.9
+
+ * First published version
diff --git a/packages/flutter_markdown/LICENSE b/packages/flutter_markdown/LICENSE
new file mode 100644
index 0000000000..e7892520aa
--- /dev/null
+++ b/packages/flutter_markdown/LICENSE
@@ -0,0 +1,27 @@
+// Copyright 2017 Google, Inc. All rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following disclaimer
+// in the documentation and/or other materials provided with the
+// distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived from
+// this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/packages/flutter_markdown/README.md b/packages/flutter_markdown/README.md
new file mode 100644
index 0000000000..2852bf330a
--- /dev/null
+++ b/packages/flutter_markdown/README.md
@@ -0,0 +1,148 @@
+# Flutter Markdown
+[](https://pub.dartlang.org/packages/flutter_markdown)
+[](https://github.com/flutter/flutter_markdown/actions?workflow=flutter_markdown)
+
+
+A markdown renderer for Flutter. It supports the
+[original format](https://daringfireball.net/projects/markdown/), but no inline
+HTML.
+
+## Overview
+
+The [`flutter_markdown`](https://pub.dev/packages/flutter_markdown) package
+renders Markdown, a lightweight markup language, into a Flutter widget
+containing a rich text representation.
+
+`flutter_markdown` is built on top of the Dart
+[`markdown`](https://pub.dev/packages/markdown) package, which parses
+the Markdown into an abstract syntax tree (AST). The nodes of the AST are an
+HTML representation of the Markdown data.
+
+## Flutter Isn't a HTML Renderer
+
+While this approach to creating a rich text representation of Markdown source
+text in Flutter works well, Flutter isn't a HTML renderer like a web browser.
+Markdown was developed by John Gruber in 2004 to allow users to turn readable,
+plain text content into rich text HTML. This close association with HTML allows
+for the injection of HTML into the Markdown source data. Markdown parsers
+generally ignore hand-tuned HTML and pass it through to be included in the
+generated HTML. This *trick* has allowed users to perform some customization
+or tweaking of the HTML output. A common HTML tweak is to insert HTML line-break
+elements **\
** in Markdown source to force additional line breaks not
+supported by the Markdown syntax. This HTML *trick* doesn't apply to Flutter. The
+`flutter_markdown` package doesn't support inline HTML.
+
+## Markdown Specifications and `flutter_markdown` Compliance
+
+There are three seminal documents regarding Markdown syntax; the
+[original Markdown syntax documentation](https://daringfireball.net/projects/markdown/syntax)
+specified by John Gruber, the
+[CommonMark specification](https://spec.commonmark.org/0.29/), and the
+[GitHub Flavored Markdown specification](https://github.github.com/gfm/).
+
+The CommonMark specification brings order to and clarifies the Markdown syntax
+cases left ambiguous or unclear in the Gruber document. GitHub Flavored
+Markdown (GFM) is a strict superset of CommonMark used by GitHub.
+
+The `markdown` package, and in extension, the `flutter_markdown` package, supports
+four levels of Markdown syntax; basic, CommonMark, GitHub Flavored, and GitHub
+Web. Basic, CommonMark, and GitHub Flavored adhere to the three Markdown
+documents, respectively. GitHub Web adds header ID and emoji support. The
+`flutter_markdown` package defaults to GitHub Flavored Markdown.
+
+## Getting Started
+
+Using the Markdown widget is simple, just pass in the source markdown as a
+string:
+
+ Markdown(data: markdownSource);
+
+If you do not want the padding or scrolling behavior, use the MarkdownBody
+instead:
+
+ MarkdownBody(data: markdownSource);
+
+By default, Markdown uses the formatting from the current material design theme,
+but it's possible to create your own custom styling. Use the MarkdownStyle class
+to pass in your own style. If you don't want to use Markdown outside of material
+design, use the MarkdownRaw class.
+
+## Emoji Support
+
+Emoji glyphs can be included in the formatted text displayed by the Markdown
+widget by either inserting the emoji glyph directly or using the inline emoji
+tag syntax in the source Markdown document.
+
+Markdown documents using UTF-8 encoding can insert emojis, symbols, and other
+Unicode characters directly in the source document. Emoji glyphs inserted
+directly in the Markdown source data are treated as text and preserved in the
+formatted output of the Markdown widget. For example, in the following Markdown
+widget constructor, a text string with a smiley face emoji is passed in as the
+source Markdown data.
+
+```
+Markdown(
+ controller: controller,
+ selectable: true,
+ data: 'Insert emoji here😀 ',
+)
+```
+
+The resulting Markdown widget will contain a single line of text with the
+emoji preserved in the formatted text output.
+
+The second method for including emoji glyphs is to provide the Markdown
+widget with a syntax extension for inline emoji tags. The Markdown
+package includes a syntax extension for emojis, EmojiSyntax. The default
+extension set used by the Markdown widget is the GitHub flavored extension
+set. This pre-defined extension set approximates the GitHub supported
+Markdown tags, providing syntax handlers for fenced code blocks, tables,
+auto-links, and strike-through. To include the inline emoji tag syntax
+while maintaining the default GitHub flavored Markdown behavior, define
+an extension set that combines EmojiSyntax with ExtensionSet.gitHubFlavored.
+
+```
+import 'package:markdown/markdown.dart' as md;
+
+Markdown(
+ controller: controller,
+ selectable: true,
+ data: 'Insert emoji :smiley: here',
+ extensionSet: md.ExtensionSet(
+ md.ExtensionSet.gitHubFlavored.blockSyntaxes,
+ [md.EmojiSyntax(), ...md.ExtensionSet.gitHubFlavored.inlineSyntaxes],
+ ),
+)
+```
+
+## Image Support
+
+The `Img` tag only supports the following image locations:
+
+* From the network: Use a URL prefixed by either `http://` or `https://`.
+
+* From local files on the device: Use an absolute path to the file, for example by
+ concatenating the file name with the path returned by a known storage location,
+ such as those provided by the [`path_provider`](https://pub.dartlang.org/packages/path_provider)
+ plugin.
+
+* From image locations referring to bundled assets: Use an asset name prefixed by `resource:`.
+ like `resource:assets/image.png`.
+
+## Verifying Markdown Behavior
+
+Verifying Markdown behavior in other applications can often be useful to track
+down or identify unexpected output from the `flutter_markdown` package. Two
+valuable resources are the
+[Dart Markdown Live Editor](https://dart-lang.github.io/markdown/) and the
+[Markdown Live Preview](https://markdownlivepreview.com/). These two resources
+are dynamic, online Markdown viewers.
+
+## Markdown Resources
+
+Here are some additional Markdown syntax resources:
+
+- [Markdown Guide](https://www.markdownguide.org/)
+- [CommonMark Markdown Reference](https://commonmark.org/help/)
+- [GitHub Guides - Mastering Markdown](https://guides.github.com/features/mastering-markdown/#GitHub-flavored-markdown)
+ - [Download PDF cheatsheet version](https://guides.github.com/pdfs/markdown-cheatsheet-online.pdf)
diff --git a/packages/flutter_markdown/example/.gitignore b/packages/flutter_markdown/example/.gitignore
new file mode 100644
index 0000000000..437cb45872
--- /dev/null
+++ b/packages/flutter_markdown/example/.gitignore
@@ -0,0 +1,36 @@
+# Miscellaneous
+*.class
+*.log
+*.pyc
+*.swp
+.DS_Store
+.atom/
+.buildlog/
+.history
+.svn/
+
+# IntelliJ related
+*.iml
+*.ipr
+*.iws
+.idea/
+
+# The .vscode folder contains launch configuration and tasks you configure in
+# VS Code which you may wish to be included in version control, so this line
+# is commented out by default.
+#.vscode/
+
+# Flutter/Dart/Pub related
+**/doc/api/
+.dart_tool/
+.flutter-plugins
+.packages
+.pub-cache/
+.pub/
+/build/
+
+# Web related
+lib/generated_plugin_registrant.dart
+
+# Exceptions to above rules.
+!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
diff --git a/packages/flutter_markdown/example/.metadata b/packages/flutter_markdown/example/.metadata
new file mode 100644
index 0000000000..962d678e2d
--- /dev/null
+++ b/packages/flutter_markdown/example/.metadata
@@ -0,0 +1,10 @@
+# This file tracks properties of this Flutter project.
+# Used by Flutter tool to assess capabilities and perform upgrades etc.
+#
+# This file should be version controlled and should not be manually edited.
+
+version:
+ revision: 532a8fed41a4f6595965f02f3edf9666ba5ebf44
+ channel: master
+
+project_type: app
diff --git a/packages/flutter_markdown/example/README.md b/packages/flutter_markdown/example/README.md
new file mode 100644
index 0000000000..1529200b10
--- /dev/null
+++ b/packages/flutter_markdown/example/README.md
@@ -0,0 +1,16 @@
+# flutter_markdown_example
+
+Demonstrates how to use the flutter_markdown package.
+
+## Getting Started
+
+This project is a starting point for a Flutter application.
+
+A few resources to get you started if this is your first Flutter project:
+
+- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab)
+- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook)
+
+For help getting started with Flutter, view our
+[online documentation](https://flutter.dev/docs), which offers tutorials,
+samples, guidance on mobile development, and a full API reference.
diff --git a/packages/flutter_markdown/example/android/.gitignore b/packages/flutter_markdown/example/android/.gitignore
new file mode 100644
index 0000000000..0a741cb43d
--- /dev/null
+++ b/packages/flutter_markdown/example/android/.gitignore
@@ -0,0 +1,11 @@
+gradle-wrapper.jar
+/.gradle
+/captures/
+/gradlew
+/gradlew.bat
+/local.properties
+GeneratedPluginRegistrant.java
+
+# Remember to never publicly share your keystore.
+# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app
+key.properties
diff --git a/packages/flutter_markdown/example/android/app/build.gradle b/packages/flutter_markdown/example/android/app/build.gradle
new file mode 100644
index 0000000000..e1a9001e29
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/build.gradle
@@ -0,0 +1,59 @@
+def localProperties = new Properties()
+def localPropertiesFile = rootProject.file('local.properties')
+if (localPropertiesFile.exists()) {
+ localPropertiesFile.withReader('UTF-8') { reader ->
+ localProperties.load(reader)
+ }
+}
+
+def flutterRoot = localProperties.getProperty('flutter.sdk')
+if (flutterRoot == null) {
+ throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.")
+}
+
+def flutterVersionCode = localProperties.getProperty('flutter.versionCode')
+if (flutterVersionCode == null) {
+ flutterVersionCode = '1'
+}
+
+def flutterVersionName = localProperties.getProperty('flutter.versionName')
+if (flutterVersionName == null) {
+ flutterVersionName = '1.0'
+}
+
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
+
+android {
+ compileSdkVersion 30
+
+ sourceSets {
+ main.java.srcDirs += 'src/main/kotlin'
+ }
+
+ defaultConfig {
+ // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
+ applicationId "io.flutter.packages.flutter_markdown_example"
+ minSdkVersion 16
+ targetSdkVersion 30
+ versionCode flutterVersionCode.toInteger()
+ versionName flutterVersionName
+ }
+
+ buildTypes {
+ release {
+ // TODO: Add your own signing config for the release build.
+ // Signing with the debug keys for now, so `flutter run --release` works.
+ signingConfig signingConfigs.debug
+ }
+ }
+}
+
+flutter {
+ source '../..'
+}
+
+dependencies {
+ implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
+}
diff --git a/packages/flutter_markdown/example/android/app/src/debug/AndroidManifest.xml b/packages/flutter_markdown/example/android/app/src/debug/AndroidManifest.xml
new file mode 100644
index 0000000000..a29a9b499a
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/debug/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/app/src/main/AndroidManifest.xml b/packages/flutter_markdown/example/android/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..6d02f614dc
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/main/AndroidManifest.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/app/src/main/kotlin/io/flutter/packages/flutter_markdown_example/MainActivity.kt b/packages/flutter_markdown/example/android/app/src/main/kotlin/io/flutter/packages/flutter_markdown_example/MainActivity.kt
new file mode 100644
index 0000000000..d55943d22c
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/main/kotlin/io/flutter/packages/flutter_markdown_example/MainActivity.kt
@@ -0,0 +1,6 @@
+package io.flutter.packages.flutter_markdown_example
+
+import io.flutter.embedding.android.FlutterActivity
+
+class MainActivity: FlutterActivity() {
+}
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/drawable-v21/launch_background.xml b/packages/flutter_markdown/example/android/app/src/main/res/drawable-v21/launch_background.xml
new file mode 100644
index 0000000000..f74085f3f6
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/main/res/drawable-v21/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/drawable/launch_background.xml b/packages/flutter_markdown/example/android/app/src/main/res/drawable/launch_background.xml
new file mode 100644
index 0000000000..304732f884
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/main/res/drawable/launch_background.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 0000000000..db77bb4b7b
Binary files /dev/null and b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 0000000000..17987b79bb
Binary files /dev/null and b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 0000000000..09d4391482
Binary files /dev/null and b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..d5f1c8d34e
Binary files /dev/null and b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000000..4d6372eebd
Binary files /dev/null and b/packages/flutter_markdown/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/values-night/styles.xml b/packages/flutter_markdown/example/android/app/src/main/res/values-night/styles.xml
new file mode 100644
index 0000000000..449a9f9308
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/main/res/values-night/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/app/src/main/res/values/styles.xml b/packages/flutter_markdown/example/android/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000000..d74aa35c28
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/main/res/values/styles.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/app/src/profile/AndroidManifest.xml b/packages/flutter_markdown/example/android/app/src/profile/AndroidManifest.xml
new file mode 100644
index 0000000000..a29a9b499a
--- /dev/null
+++ b/packages/flutter_markdown/example/android/app/src/profile/AndroidManifest.xml
@@ -0,0 +1,7 @@
+
+
+
+
diff --git a/packages/flutter_markdown/example/android/build.gradle b/packages/flutter_markdown/example/android/build.gradle
new file mode 100644
index 0000000000..c505a86352
--- /dev/null
+++ b/packages/flutter_markdown/example/android/build.gradle
@@ -0,0 +1,31 @@
+buildscript {
+ ext.kotlin_version = '1.3.50'
+ repositories {
+ google()
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:4.1.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+rootProject.buildDir = '../build'
+subprojects {
+ project.buildDir = "${rootProject.buildDir}/${project.name}"
+}
+subprojects {
+ project.evaluationDependsOn(':app')
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/packages/flutter_markdown/example/android/gradle.properties b/packages/flutter_markdown/example/android/gradle.properties
new file mode 100644
index 0000000000..94adc3a3f9
--- /dev/null
+++ b/packages/flutter_markdown/example/android/gradle.properties
@@ -0,0 +1,3 @@
+org.gradle.jvmargs=-Xmx1536M
+android.useAndroidX=true
+android.enableJetifier=true
diff --git a/packages/flutter_markdown/example/android/gradle/wrapper/gradle-wrapper.properties b/packages/flutter_markdown/example/android/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000000..bc6a58afdd
--- /dev/null
+++ b/packages/flutter_markdown/example/android/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Fri Jun 23 08:50:38 CEST 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-6.7-all.zip
diff --git a/packages/flutter_markdown/example/android/settings.gradle b/packages/flutter_markdown/example/android/settings.gradle
new file mode 100644
index 0000000000..44e62bcf06
--- /dev/null
+++ b/packages/flutter_markdown/example/android/settings.gradle
@@ -0,0 +1,11 @@
+include ':app'
+
+def localPropertiesFile = new File(rootProject.projectDir, "local.properties")
+def properties = new Properties()
+
+assert localPropertiesFile.exists()
+localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) }
+
+def flutterSdkPath = properties.getProperty("flutter.sdk")
+assert flutterSdkPath != null, "flutter.sdk not set in local.properties"
+apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle"
diff --git a/packages/flutter_markdown/example/assets/logo.png b/packages/flutter_markdown/example/assets/logo.png
new file mode 100644
index 0000000000..00357cb9c9
Binary files /dev/null and b/packages/flutter_markdown/example/assets/logo.png differ
diff --git a/packages/flutter_markdown/example/assets/markdown_test_page.md b/packages/flutter_markdown/example/assets/markdown_test_page.md
new file mode 100644
index 0000000000..f550015a0f
--- /dev/null
+++ b/packages/flutter_markdown/example/assets/markdown_test_page.md
@@ -0,0 +1,199 @@
+# Markdown Test Page
+
+# Basic Syntax
+
+## Headings
+---
+# Heading One
+
+Sint sit cillum pariatur eiusmod nulla pariatur ipsum. Sit laborum anim qui mollit tempor pariatur nisi minim dolor. Aliquip et adipisicing sit sit fugiat commodo id sunt. Nostrud enim ad commodo incididunt cupidatat in ullamco ullamco Lorem cupidatat velit enim et Lorem.
+
+## Heading Two
+
+Aute officia nulla deserunt do deserunt cillum velit magna. Officia veniam culpa anim minim dolore labore pariatur voluptate id ad est duis quis velit dolor pariatur enim. Incididunt enim excepteur do veniam consequat culpa do voluptate dolor fugiat ad adipisicing sit.
+
+### Heading Three
+
+Voluptate cupidatat cillum elit quis ipsum eu voluptate fugiat consectetur enim. Quis ut voluptate culpa ex anim aute consectetur dolore proident voluptate exercitation eiusmod. Esse in do anim magna minim culpa sint.
+
+#### Heading Four
+
+Commodo fugiat aliqua minim quis pariatur mollit id tempor. Non occaecat minim esse enim aliqua adipisicing nostrud duis consequat eu adipisicing qui. Minim aliquip sit excepteur ipsum consequat laborum pariatur excepteur.
+
+##### Heading Five
+
+Veniam enim esse amet veniam deserunt laboris amet enim consequat. Minim nostrud deserunt cillum consectetur commodo eu enim nostrud ullamco occaecat excepteur. Aliquip et ut est commodo enim dolor amet sint excepteur.
+
+###### Heading Six
+
+Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
+
+## Paragraphs
+---
+Incididunt ex adipisicing ea ullamco consectetur in voluptate proident fugiat tempor deserunt reprehenderit ullamco id dolore laborum. Do laboris laboris minim incididunt qui consectetur exercitation adipisicing dolore et magna consequat magna anim sunt. Officia fugiat Lorem sunt pariatur incididunt Lorem reprehenderit proident irure.
+
+Officia dolore laborum aute incididunt commodo nisi velit est est elit et dolore elit exercitation. Enim aliquip magna id ipsum aliquip consectetur ad nulla quis. Incididunt pariatur dolor consectetur cillum enim velit cupidatat laborum quis ex.
+
+Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in.
+
+## Inline Emphasis - Bold and Italic
+---
+Sint ea do **exercitation** incididunt et minim ad labore sunt. Minim deserunt labore laboris velit nulla incididunt ipsum nulla. **Consequat commodo** reprehenderit duis esse esse ex**ercita**tion minim enim Lorem dolore duis irure. Et ad ipsum labore enim ipsum **cupidatat consequat**. Commodo non ea __cupidatat magna__ deserunt dolore ipsum velit nulla elit veniam nulla eiusmod proident officia.
+
+*"Proident sit veniam in est proident officia adipisicing"* ea tempor cillum non cillum velit deserunt. Sit tempor ad*ipisic*ing ea laboris anim aute reprehenderit id eu ea. Aute Lorem minim _adipisicing nostrud mollit_ ad ut voluptate do nulla esse occaecat aliqua sint anim.
+
+## Blockquotes
+---
+Ad nisi laborum aute cupidatat magna deserunt eu id laboris id. Aliquip nulla cupidatat sint ex Lorem mollit laborum dolor amet est ut esse aute. Nostrud ex consequat id incididunt proident ipsum minim duis aliqua ut ex et ad quis.
+
+> Ipsum et cupidatat mollit exercitation enim duis sunt irure aliqua reprehenderit mollit. Pariatur Lorem pariatur laboris do culpa do elit irure.
+
+Labore ea magna Lorem consequat aliquip consectetur cillum duis dolore. Et veniam dolor qui incididunt minim amet laboris sit.
+
+> Qui est sit et reprehenderit aute est esse enim aliqua id aliquip ea anim. Pariatur sint reprehenderit mollit velit voluptate enim consectetur sint enim. Quis exercitation proident elit non id qui culpa dolore esse aliquip consequat.
+
+## Lists
+---
+### Ordered List
+
+1. Longan
+2. Lychee
+3. Excepteur ad cupidatat do elit laborum amet cillum reprehenderit consequat quis.
+ Deserunt officia esse aliquip consectetur duis ut labore laborum commodo aliquip aliquip velit pariatur dolore.
+4. Marionberry
+4. Melon
+ - Cantaloupe
+ - Honeydew
+ - Watermelon
+6. Miracle fruit
+7. Mulberry
+
+
+9. Strawberry
+12. Plum
+
+### Unordered List
+
+- Olive
+- Orange
+ - Blood orange
+ - Clementine
+- Papaya
+- Ut aute ipsum occaecat nisi culpa Lorem id occaecat cupidatat id id magna laboris ad duis. Fugiat cillum dolore veniam nostrud proident sint consectetur eiusmod irure adipisicing.
+- Passionfruit
+
+## Inline Code
+---
+Duis duis est `code in text` velit velit aute culpa ex quis pariatur pariatur laborum aute pariatur duis tempor sunt ad. Irure magna voluptate dolore consectetur consectetur irure esse. Anim magna `md.ExtensionSet.GitHub` dolor.
+
+## Horizontal Rule
+---
+In dolore velit aliquip labore mollit minim tempor veniam eu veniam ad in sint aliquip mollit mollit. Ex occaecat non deserunt elit laborum sunt tempor sint consequat culpa culpa qui sit.
+
+***
+or
+
+---
+or
+____________
+
+In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi.
+
+## Link
+---
+[Google]: https://www.google.com
+
+Excepteur ad cupidatat do elit laborum amet cillum reprehenderit consequat quis.
+Deserunt officia esse [Flutter](https://www.flutter.dev) aliquip consectetur duis ut labore laborum commodo aliquip aliquip velit pariatur dolore.
+
+[Flutter](https://www.flutter.dev)
+
+Excepteur ad cupidatat do *[Flutter](https://www.flutter.dev)* elit laborum amet cillum reprehenderit **[Dart](https://www.dart.dev)** aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi.
+
+In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi [Google's Home Page][Google].
+
+
+## Image
+---
+
+Minim id consequat adipisicing cupidatat laborum culpa veniam non consectetur et duis pariatur reprehenderit eu ex consectetur. Sunt nisi qui eiusmod ut cillum laborum Lorem officia aliquip laboris ullamco nostrud laboris non irure laboris.
+
+
+
+In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi.
+
+
+
+Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in.
+
+
+
+In laboris eiusmod reprehenderit aliquip sit proident occaecat. Non sit labore anim elit veniam Lorem minim commodo eiusmod irure do minim nisi.
+
+
+
+# Extended Syntax
+
+# Table
+---
+Duis sunt ut pariatur reprehenderit mollit mollit magna dolore in pariatur nulla commodo sit dolor ad fugiat.
+
+| Table Heading 1 | Table Heading 2 | Table Heading 3 |
+| --------------- | --------------- | --------------- |
+| Item 1 | Item 2 | Item 3 |
+| Item 1 | Item 2 | Item 3 |
+| Item 1 | Item 2 | Item 3 |
+
+Ex amet id ex aliquip id do laborum excepteur exercitation elit sint commodo occaecat nostrud est. Nostrud pariatur esse veniam laborum non sint magna sit laboris minim in id.
+
+## Fenced Code Block
+---
+
+Et fugiat ad nisi amet magna labore do cillum fugiat occaecat cillum Lorem proident. In sint dolor ullamco ad do adipisicing amet.
+
+```
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import 'package:markdown/markdown.dart' as md;
+
+const String _markdownData = """
+# Markdown Example
+Markdown allows you to easily include formatted text, images,
+and even formatted Dart code in your app.
+""";
+
+void main() {
+ runApp(
+ MaterialApp(
+ title: "Markdown Demo",
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text('Markdown Demo'),
+ ),
+ body: SafeArea(
+ child: Markdown(
+ data: _markdownData,
+ ),
+ ),
+ ),
+ ),
+ );
+}
+```
+
+## Strikethrough
+---
+
+~~Voluptate cupidatat cillum elit quis ipsum eu voluptate fugiat consectetur enim. Quis ut voluptate culpa ex anim aute consectetur dolore proident voluptate exercitation eiusmod. Esse in do anim magna minim culpa sint.~~
+
+Commodo ***~~fugiat aliqua minim quis pariatur mollit~~*** id tempor. Non occaecat **~~minim~~** anim aute ~~**consectetur esse**~~ enim aliqua *~~adipisicing~~* nostrud duis consequat eu ~~*adipisicing*~~ qui. Minim aliquip ~~***sit excepteur ipsum consequat***~~ laborum pariatur excepteur.
+
+## Task List
+---
+Officia irure in non voluptate adipisicing sit amet tempor duis dolore deserunt enim ut. Reprehenderit incididunt in ad anim et deserunt deserunt Lorem laborum quis. Enim aute anim labore proident laboris voluptate elit excepteur in.
+
+- [ ] laboris voluptate
+- [x] consectetur
+- [ ] eiusmod irure do
+
+Labore ea magna Lorem consequat aliquip consectetur cillum duis dolore. Et veniam dolor qui incididunt minim amet laboris sit.
diff --git a/packages/flutter_markdown/example/assets/original_markdown_example_data.md b/packages/flutter_markdown/example/assets/original_markdown_example_data.md
new file mode 100644
index 0000000000..56e3f66263
--- /dev/null
+++ b/packages/flutter_markdown/example/assets/original_markdown_example_data.md
@@ -0,0 +1,112 @@
+# Markdown Example
+Markdown allows you to easily include formatted text, images, and even formatted Dart code in your app.
+
+## Titles
+
+Setext-style
+
+```
+This is an H1
+=============
+
+This is an H2
+-------------
+```
+
+Atx-style
+
+```
+# This is an H1
+
+## This is an H2
+
+###### This is an H6
+```
+
+Select the valid headers:
+
+- [x] `# hello`
+- [ ] `#hello`
+
+## Links
+
+[Google's Homepage][Google]
+
+```
+[inline-style](https://www.google.com)
+
+[reference-style][Google]
+```
+
+## Images
+
+
+
+## Tables
+
+|Syntax |Result |
+|---------------------------------------|-------------------------------------|
+|`*italic 1*` |*italic 1* |
+|`_italic 2_` | _italic 2_ |
+|`**bold 1**` |**bold 1** |
+|`__bold 2__` |__bold 2__ |
+|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ |
+|`***italic bold 1***` |***italic bold 1*** |
+|`___italic bold 2___` |___italic bold 2___ |
+|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***|
+|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~|
+
+## Styling
+Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`.
+
+- Use bulleted lists
+- To better clarify
+- Your points
+
+## Code blocks
+Formatted Dart code looks really pretty too:
+
+```
+void main() {
+ runApp(MaterialApp(
+ home: Scaffold(
+ body: Markdown(data: markdownData),
+ ),
+ ));
+}
+```
+
+## Center Title
+
+###### ※ ※ ※
+
+_* How to implement it see main.dart#L129 in example._
+
+## Custom Syntax
+
+NaOH + Al_2O_3 = NaAlO_2 + H_2O
+
+C_4H_10 = C_2H_6 + C_2H_4
+
+## Markdown widget
+
+This is an example of how to create your own Markdown widget:
+
+ Markdown(data: 'Hello _world_!');
+
+Enjoy!
+
+[Google]: https://www.google.com/
+
+## Line Breaks
+
+This is an example of how to create line breaks (tab or two whitespaces):
+
+line 1
+
+
+line 2
+
+
+
+line 3
diff --git a/packages/flutter_markdown/example/fonts/RobotoMono-Regular.ttf b/packages/flutter_markdown/example/fonts/RobotoMono-Regular.ttf
new file mode 100644
index 0000000000..7c4ce36a44
Binary files /dev/null and b/packages/flutter_markdown/example/fonts/RobotoMono-Regular.ttf differ
diff --git a/packages/flutter_markdown/example/ios/.gitignore b/packages/flutter_markdown/example/ios/.gitignore
new file mode 100644
index 0000000000..e96ef602b8
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/.gitignore
@@ -0,0 +1,32 @@
+*.mode1v3
+*.mode2v3
+*.moved-aside
+*.pbxuser
+*.perspectivev3
+**/*sync/
+.sconsign.dblite
+.tags*
+**/.vagrant/
+**/DerivedData/
+Icon?
+**/Pods/
+**/.symlinks/
+profile
+xcuserdata
+**/.generated/
+Flutter/App.framework
+Flutter/Flutter.framework
+Flutter/Flutter.podspec
+Flutter/Generated.xcconfig
+Flutter/app.flx
+Flutter/app.zip
+Flutter/flutter_assets/
+Flutter/flutter_export_environment.sh
+ServiceDefinitions.json
+Runner/GeneratedPluginRegistrant.*
+
+# Exceptions to above rules.
+!default.mode1v3
+!default.mode2v3
+!default.pbxuser
+!default.perspectivev3
diff --git a/packages/flutter_markdown/example/ios/Flutter/AppFrameworkInfo.plist b/packages/flutter_markdown/example/ios/Flutter/AppFrameworkInfo.plist
new file mode 100644
index 0000000000..9367d483e4
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Flutter/AppFrameworkInfo.plist
@@ -0,0 +1,26 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ en
+ CFBundleExecutable
+ App
+ CFBundleIdentifier
+ io.flutter.flutter.app
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ App
+ CFBundlePackageType
+ FMWK
+ CFBundleShortVersionString
+ 1.0
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ 1.0
+ MinimumOSVersion
+ 8.0
+
+
diff --git a/packages/flutter_markdown/example/ios/Flutter/Debug.xcconfig b/packages/flutter_markdown/example/ios/Flutter/Debug.xcconfig
new file mode 100644
index 0000000000..592ceee85b
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Flutter/Debug.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/flutter_markdown/example/ios/Flutter/Release.xcconfig b/packages/flutter_markdown/example/ios/Flutter/Release.xcconfig
new file mode 100644
index 0000000000..592ceee85b
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Flutter/Release.xcconfig
@@ -0,0 +1 @@
+#include "Generated.xcconfig"
diff --git a/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.pbxproj b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..e757aaeab9
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,471 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 46;
+ objects = {
+
+/* Begin PBXBuildFile section */
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 9705A1C41CF9048500538489 /* Embed Frameworks */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Embed Frameworks";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; };
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; };
+ 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
+ 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
+ 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
+ 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 97C146EB1CF9000F007C117D /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 9740EEB11CF90186004384FC /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 9740EEB31CF90195004384FC /* Generated.xcconfig */,
+ );
+ name = Flutter;
+ sourceTree = "";
+ };
+ 97C146E51CF9000F007C117D = {
+ isa = PBXGroup;
+ children = (
+ 9740EEB11CF90186004384FC /* Flutter */,
+ 97C146F01CF9000F007C117D /* Runner */,
+ 97C146EF1CF9000F007C117D /* Products */,
+ );
+ sourceTree = "";
+ };
+ 97C146EF1CF9000F007C117D /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146EE1CF9000F007C117D /* Runner.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 97C146F01CF9000F007C117D /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 97C146FA1CF9000F007C117D /* Main.storyboard */,
+ 97C146FD1CF9000F007C117D /* Assets.xcassets */,
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
+ 97C147021CF9000F007C117D /* Info.plist */,
+ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
+ 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
+ 74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
+ 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 97C146ED1CF9000F007C117D /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 9740EEB61CF901F6004384FC /* Run Script */,
+ 97C146EA1CF9000F007C117D /* Sources */,
+ 97C146EB1CF9000F007C117D /* Frameworks */,
+ 97C146EC1CF9000F007C117D /* Resources */,
+ 9705A1C41CF9048500538489 /* Embed Frameworks */,
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 97C146E61CF9000F007C117D /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1020;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 97C146ED1CF9000F007C117D = {
+ CreatedOnToolsVersion = 7.3.1;
+ LastSwiftMigration = 1100;
+ };
+ };
+ };
+ buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 97C146E51CF9000F007C117D;
+ productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 97C146ED1CF9000F007C117D /* Runner */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 97C146EC1CF9000F007C117D /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
+ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
+ 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
+ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Thin Binary";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
+ };
+ 9740EEB61CF901F6004384FC /* Run Script */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ );
+ name = "Run Script";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 97C146EA1CF9000F007C117D /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
+ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXVariantGroup section */
+ 97C146FA1CF9000F007C117D /* Main.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C146FB1CF9000F007C117D /* Base */,
+ );
+ name = Main.storyboard;
+ sourceTree = "";
+ };
+ 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 97C147001CF9000F007C117D /* Base */,
+ );
+ name = LaunchScreen.storyboard;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 249021D3217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Profile;
+ };
+ 249021D4217E4FDB00AE95B9 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = io.flutter.packages.flutterMarkdownExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Profile;
+ };
+ 97C147031CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = iphoneos;
+ TARGETED_DEVICE_FAMILY = "1,2";
+ };
+ name = Debug;
+ };
+ 97C147041CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu99;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ IPHONEOS_DEPLOYMENT_TARGET = 9.0;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = iphoneos;
+ SUPPORTED_PLATFORMS = iphoneos;
+ SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
+ TARGETED_DEVICE_FAMILY = "1,2";
+ VALIDATE_PRODUCT = YES;
+ };
+ name = Release;
+ };
+ 97C147061CF9000F007C117D /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = io.flutter.packages.flutterMarkdownExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Debug;
+ };
+ 97C147071CF9000F007C117D /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
+ ENABLE_BITCODE = NO;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
+ PRODUCT_BUNDLE_IDENTIFIER = io.flutter.packages.flutterMarkdownExample;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
+ SWIFT_VERSION = 5.0;
+ VERSIONING_SYSTEM = "apple-generic";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147031CF9000F007C117D /* Debug */,
+ 97C147041CF9000F007C117D /* Release */,
+ 249021D3217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 97C147061CF9000F007C117D /* Debug */,
+ 97C147071CF9000F007C117D /* Release */,
+ 249021D4217E4FDB00AE95B9 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 97C146E61CF9000F007C117D /* Project object */;
+}
diff --git a/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..919434a625
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000000..18d981003d
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
new file mode 100644
index 0000000000..f9b0d7c5ea
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings
@@ -0,0 +1,8 @@
+
+
+
+
+ PreviewsEnabled
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter_markdown/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000000..a28140cfdb
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/packages/flutter_markdown/example/ios/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..1d526a16ed
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter_markdown/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000000..18d981003d
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner/AppDelegate.swift b/packages/flutter_markdown/example/ios/Runner/AppDelegate.swift
new file mode 100644
index 0000000000..70693e4a8c
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/AppDelegate.swift
@@ -0,0 +1,13 @@
+import UIKit
+import Flutter
+
+@UIApplicationMain
+@objc class AppDelegate: FlutterAppDelegate {
+ override func application(
+ _ application: UIApplication,
+ didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
+ ) -> Bool {
+ GeneratedPluginRegistrant.register(with: self)
+ return super.application(application, didFinishLaunchingWithOptions: launchOptions)
+ }
+}
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..d36b1fab2d
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,122 @@
+{
+ "images" : [
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-20x20@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-29x29@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-40x40@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "60x60",
+ "idiom" : "iphone",
+ "filename" : "Icon-App-60x60@3x.png",
+ "scale" : "3x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "20x20",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-20x20@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "29x29",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-29x29@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "40x40",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-40x40@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@1x.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "76x76",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-76x76@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "83.5x83.5",
+ "idiom" : "ipad",
+ "filename" : "Icon-App-83.5x83.5@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "1024x1024",
+ "idiom" : "ios-marketing",
+ "filename" : "Icon-App-1024x1024@1x.png",
+ "scale" : "1x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png
new file mode 100644
index 0000000000..dc9ada4725
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png
new file mode 100644
index 0000000000..28c6bf0301
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
new file mode 100644
index 0000000000..2ccbfd967d
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
new file mode 100644
index 0000000000..f091b6b0bc
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
new file mode 100644
index 0000000000..4cde12118d
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
new file mode 100644
index 0000000000..d0ef06e7ed
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
new file mode 100644
index 0000000000..dcdc2306c2
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png
new file mode 100644
index 0000000000..2ccbfd967d
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
new file mode 100644
index 0000000000..c8f9ed8f5c
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
new file mode 100644
index 0000000000..a6d6b8609d
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
new file mode 100644
index 0000000000..a6d6b8609d
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
new file mode 100644
index 0000000000..75b2d164a5
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png
new file mode 100644
index 0000000000..c4df70d39d
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png
new file mode 100644
index 0000000000..6a84f41e14
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png
new file mode 100644
index 0000000000..d0e1f58536
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
new file mode 100644
index 0000000000..0bedcf2fd4
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
@@ -0,0 +1,23 @@
+{
+ "images" : [
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage.png",
+ "scale" : "1x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@2x.png",
+ "scale" : "2x"
+ },
+ {
+ "idiom" : "universal",
+ "filename" : "LaunchImage@3x.png",
+ "scale" : "3x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
new file mode 100644
index 0000000000..9da19eacad
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
new file mode 100644
index 0000000000..9da19eacad
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
new file mode 100644
index 0000000000..9da19eacad
Binary files /dev/null and b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png differ
diff --git a/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
new file mode 100644
index 0000000000..89c2725b70
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
@@ -0,0 +1,5 @@
+# Launch Screen Assets
+
+You can customize the launch screen with your own desired assets by replacing the image files in this directory.
+
+You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
diff --git a/packages/flutter_markdown/example/ios/Runner/Base.lproj/LaunchScreen.storyboard b/packages/flutter_markdown/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
new file mode 100644
index 0000000000..f2e259c7c9
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Base.lproj/LaunchScreen.storyboard
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner/Base.lproj/Main.storyboard b/packages/flutter_markdown/example/ios/Runner/Base.lproj/Main.storyboard
new file mode 100644
index 0000000000..f3c28516fb
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Base.lproj/Main.storyboard
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner/Info.plist b/packages/flutter_markdown/example/ios/Runner/Info.plist
new file mode 100644
index 0000000000..2fb29c6052
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Info.plist
@@ -0,0 +1,45 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ flutter_markdown_example
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleSignature
+ ????
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSRequiresIPhoneOS
+
+ UILaunchStoryboardName
+ LaunchScreen
+ UIMainStoryboardFile
+ Main
+ UISupportedInterfaceOrientations
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UISupportedInterfaceOrientations~ipad
+
+ UIInterfaceOrientationPortrait
+ UIInterfaceOrientationPortraitUpsideDown
+ UIInterfaceOrientationLandscapeLeft
+ UIInterfaceOrientationLandscapeRight
+
+ UIViewControllerBasedStatusBarAppearance
+
+
+
diff --git a/packages/flutter_markdown/example/ios/Runner/Runner-Bridging-Header.h b/packages/flutter_markdown/example/ios/Runner/Runner-Bridging-Header.h
new file mode 100644
index 0000000000..308a2a560b
--- /dev/null
+++ b/packages/flutter_markdown/example/ios/Runner/Runner-Bridging-Header.h
@@ -0,0 +1 @@
+#import "GeneratedPluginRegistrant.h"
diff --git a/packages/flutter_markdown/example/lib/demos/basic_markdown_demo.dart b/packages/flutter_markdown/example/lib/demos/basic_markdown_demo.dart
new file mode 100644
index 0000000000..41363e7230
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/basic_markdown_demo.dart
@@ -0,0 +1,170 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+
+import '../shared/dropdown_menu.dart';
+import '../shared/markdown_demo_widget.dart';
+import '../shared/markdown_extensions.dart';
+
+// ignore_for_file: public_member_api_docs
+
+const String _notes = """
+# Basic Markdown Demo
+---
+The Basic Markdown Demo shows the effect of the four Markdown extension sets
+on formatting basic and extended Markdown tags.
+
+## Overview
+
+The Dart [markdown](https://pub.dev/packages/markdown) package parses Markdown
+into HTML. The flutter_markdown package builds on this package using the
+abstract syntax tree generated by the parser to make a tree of widgets instead
+of HTML elements.
+
+The markdown package supports the basic block and inline Markdown syntax
+specified in the original Markdown implementation as well as a few Markdown
+extensions. The markdown package uses extension sets to make extension
+management easy. There are four pre-defined extension sets; none, Common Mark,
+GitHub Flavored, and GitHub Web. The default extension set used by the
+flutter_markdown package is GitHub Flavored.
+
+The Basic Markdown Demo shows the effect each of the pre-defined extension sets
+has on a test Markdown document with basic and extended Markdown tags. Use the
+Extension Set dropdown menu to select an extension set and view the Markdown
+widget's output.
+
+## Comments
+
+Since GitHub Flavored is the default extension set, it is the initial setting
+for the formatted Markdown view in the demo.
+""";
+
+class BasicMarkdownDemo extends StatefulWidget implements MarkdownDemoWidget {
+ const BasicMarkdownDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Basic Markdown Demo';
+
+ @override
+ String get title => BasicMarkdownDemo._title;
+
+ @override
+ String get description => 'Shows the effect the four Markdown extension sets '
+ 'have on basic and extended Markdown tagged elements.';
+
+ @override
+ Future get data =>
+ rootBundle.loadString('assets/markdown_test_page.md');
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ @override
+ _BasicMarkdownDemoState createState() => _BasicMarkdownDemoState();
+}
+
+class _BasicMarkdownDemoState extends State {
+ MarkdownExtensionSet _extensionSet = MarkdownExtensionSet.githubFlavored;
+
+ final Map _menuItems =
+ Map.fromIterables(
+ MarkdownExtensionSet.values.map((MarkdownExtensionSet e) => e.displayTitle),
+ MarkdownExtensionSet.values,
+ );
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: widget.data,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return Column(
+ children: [
+ DropdownMenu(
+ items: _menuItems,
+ label: 'Extension Set:',
+ initialValue: _extensionSet,
+ onChanged: (MarkdownExtensionSet? value) {
+ if (value != _extensionSet) {
+ setState(() {
+ _extensionSet = value!;
+ });
+ }
+ },
+ ),
+ Expanded(
+ child: Markdown(
+ key: Key(_extensionSet.name),
+ data: snapshot.data!,
+ imageDirectory: 'https://raw.githubusercontent.com',
+ extensionSet: _extensionSet.value,
+ onTapLink: (String text, String? href, String title) =>
+ linkOnTapHandler(context, text, href, title),
+ ),
+ ),
+ ],
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+
+ // Handle the link. The [href] in the callback contains information
+ // from the link. The url_launcher package or other similar package
+ // can be used to execute the link.
+ Future linkOnTapHandler(
+ BuildContext context,
+ String text,
+ String? href,
+ String title,
+ ) async {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) =>
+ _createDialog(context, text, href, title),
+ );
+ }
+
+ Widget _createDialog(
+ BuildContext context, String text, String? href, String title) =>
+ AlertDialog(
+ title: const Text('Reference Link'),
+ content: SingleChildScrollView(
+ child: ListBody(
+ children: [
+ Text(
+ 'See the following link for more information:',
+ style: Theme.of(context).textTheme.bodyText1,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Link text: $text',
+ style: Theme.of(context).textTheme.bodyText2,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Link destination: $href',
+ style: Theme.of(context).textTheme.bodyText2,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Link title: $title',
+ style: Theme.of(context).textTheme.bodyText2,
+ ),
+ ],
+ ),
+ ),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Text('OK'),
+ )
+ ],
+ );
+}
diff --git a/packages/flutter_markdown/example/lib/demos/centered_header_demo.dart b/packages/flutter_markdown/example/lib/demos/centered_header_demo.dart
new file mode 100644
index 0000000000..ad3632c6eb
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/centered_header_demo.dart
@@ -0,0 +1,77 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import 'package:markdown/markdown.dart' as md;
+import '../shared/markdown_demo_widget.dart';
+
+// ignore_for_file: public_member_api_docs
+
+const String _data = '''
+## Centered Title
+
+###### ※ ※ ※
+
+''';
+
+const String _notes = '''
+# Centered Title Demo
+---
+
+## Overview
+This example demonstrates how to implement a centered headline using a custom builder.
+
+''';
+
+class CenteredHeaderDemo extends StatelessWidget implements MarkdownDemoWidget {
+ const CenteredHeaderDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Centered Header Demo';
+
+ @override
+ String get title => CenteredHeaderDemo._title;
+
+ @override
+ String get description =>
+ 'An example of using a user defined builder to implement a centered headline';
+
+ @override
+ Future get data => Future.value(_data);
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: data,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return Markdown(
+ data: snapshot.data!,
+ builders: {
+ 'h2': CenteredHeaderBuilder(),
+ 'h6': CenteredHeaderBuilder(),
+ },
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+}
+
+class CenteredHeaderBuilder extends MarkdownElementBuilder {
+ @override
+ Widget visitText(md.Text text, TextStyle? preferredStyle) {
+ return Row(
+ mainAxisAlignment: MainAxisAlignment.center,
+ children: [
+ Text(text.text, style: preferredStyle),
+ ],
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/demos/extended_emoji_demo.dart b/packages/flutter_markdown/example/lib/demos/extended_emoji_demo.dart
new file mode 100644
index 0000000000..edf9b9397a
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/extended_emoji_demo.dart
@@ -0,0 +1,122 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import 'package:markdown/markdown.dart' as md;
+import '../shared/markdown_demo_widget.dart';
+import '../shared/markdown_extensions.dart';
+
+// ignore_for_file: public_member_api_docs
+
+const String _notes = """
+# Extended Emoji Demo
+---
+
+## Overview
+
+This simple example demonstrates how to subclass an existing inline syntax
+parser to extend, enhance, or modify its behavior. This example shows how to
+subclass the EmojiSyntax inline syntax parser to support alternative names for
+the thumbs up and thumbs down emoji characters. The emoji character map used by
+EmojiSyntax has the keys "+1" and "-1" associated with the thumbs up and thumbs
+down emoji characters, respectively. The ExtendedEmojiSyntax subclass extends
+the EmojiSyntax class by overriding the onMatch method to intercept the call
+from the parser. ExtendedEmojiSyntax either handles the matched tag or passes
+the match along to its parent for processing.
+
+```
+class ExtendedEmojiSyntax extends md.EmojiSyntax {
+ static const alternateTags = {
+ 'thumbsup': '👍',
+ 'thumbsdown': '👎',
+ };
+
+ @override
+ bool onMatch(md.InlineParser parser, Match match) {
+ var emoji = alternateTags[match[1]];
+ if (emoji != null) {
+ parser.addNode(md.Text(emoji));
+ return true;
+ }
+ return super.onMatch(parser, match);
+ }
+}
+```
+""";
+
+class ExtendedEmojiDemo extends StatelessWidget implements MarkdownDemoWidget {
+ const ExtendedEmojiDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Extended Emoji Demo';
+
+ @override
+ String get title => ExtendedEmojiDemo._title;
+
+ @override
+ String get description => 'Demonstrates how to extend an existing inline'
+ ' syntax parser by intercepting the parser onMatch routine.';
+
+ @override
+ Future get data =>
+ Future.value('Simple test :smiley: :thumbsup:!');
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ static const String _notExtended = '# Using Emoji Syntax\n';
+
+ static const String _extended = '# Using Extened Emoji Syntax\n';
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: data,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return Container(
+ margin: const EdgeInsets.all(12),
+ constraints: const BoxConstraints.expand(),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ MarkdownBody(
+ data: _notExtended + snapshot.data!,
+ extensionSet: MarkdownExtensionSet.githubWeb.value,
+ ),
+ const SizedBox(
+ height: 24,
+ ),
+ MarkdownBody(
+ data: _extended + snapshot.data!,
+ extensionSet: md.ExtensionSet([],
+ [ExtendedEmojiSyntax()]),
+ ),
+ ],
+ ),
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+}
+
+class ExtendedEmojiSyntax extends md.EmojiSyntax {
+ static const Map alternateTags = {
+ 'thumbsup': '👍',
+ 'thumbsdown': '👎',
+ };
+
+ @override
+ bool onMatch(md.InlineParser parser, Match match) {
+ final String? emoji = alternateTags[match[1]!];
+ if (emoji != null) {
+ parser.addNode(md.Text(emoji));
+ return true;
+ }
+ return super.onMatch(parser, match);
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/demos/minimal_markdown_demo.dart b/packages/flutter_markdown/example/lib/demos/minimal_markdown_demo.dart
new file mode 100644
index 0000000000..8bf9bea67a
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/minimal_markdown_demo.dart
@@ -0,0 +1,112 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import '../shared/markdown_demo_widget.dart';
+
+// ignore_for_file: public_member_api_docs
+
+const String _data = '''
+# Minimal Markdown Test
+---
+This is a simple Markdown test. Provide a text string with Markdown tags
+to the Markdown widget and it will display the formatted output in a scrollable
+widget.
+
+## Section 1
+Maecenas eget **arcu egestas**, mollis ex vitae, posuere magna. Nunc eget
+ aliquam tortor. Vestibulum porta sodales efficitur. Mauris interdum turpis
+ eget est condimentum, vitae porttitor diam ornare.
+
+### Subsection A
+Sed et massa finibus, blandit massa vel, vulputate velit. Vestibulum vitae
+venenatis libero. ***Curabitur sem lectus, feugiat eu justo in, eleifend
+accumsan ante.*** Sed a fermentum elit. Curabitur sodales metus id mi ornare,
+in ullamcorper magna congue.
+''';
+
+const String _notes = """
+# Minimal Markdown Demo
+---
+
+## Overview
+
+The simplest use case that illustrates how to make use of the
+flutter_markdown package is to include a Markdown widget in a widget tree
+and supply it with a character string of text containing Markdown formatting
+syntax. Here is a simple Flutter app that creates a Markdown widget that
+formats and displays the text in the string _markdownData. The resulting
+Flutter app demonstrates the use of headers, rules, and emphasis text from
+plain text Markdown syntax.
+
+## Usage
+
+The code sample below demonstrates a simple Flutter app with a Markdown widget.
+
+```
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+
+const String _markdownData = \"""
+# Minimal Markdown Test
+---
+This is a simple Markdown test. Provide a text string with Markdown tags
+to the Markdown widget and it will display the formatted output in a
+scrollable widget.
+
+## Section 1
+Maecenas eget **arcu egestas**, mollis ex vitae, posuere magna. Nunc eget
+aliquam tortor. Vestibulum porta sodales efficitur. Mauris interdum turpis
+eget est condimentum, vitae porttitor diam ornare.
+
+### Subsection A
+Sed et massa finibus, blandit massa vel, vulputate velit. Vestibulum vitae
+venenatis libero. **__Curabitur sem lectus, feugiat eu justo in, eleifend
+accumsan ante.__** Sed a fermentum elit. Curabitur sodales metus id mi
+ornare, in ullamcorper magna congue.
+\""";
+
+void main() {
+ runApp(
+ MaterialApp(
+ title: "Markdown Demo",
+ home: Scaffold(
+ appBar: AppBar(
+ title: const Text('Simple Markdown Demo'),
+ ),
+ body: SafeArea(
+ child: Markdown(
+ data: _markdownData,
+ ),
+ ),
+ ),
+ ),
+ );
+}
+```
+""";
+
+class MinimalMarkdownDemo extends StatelessWidget
+ implements MarkdownDemoWidget {
+ const MinimalMarkdownDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Minimal Markdown Demo';
+
+ @override
+ String get title => MinimalMarkdownDemo._title;
+
+ @override
+ String get description => 'A minimal example of how to use the Markdown '
+ 'widget in a Flutter app.';
+
+ @override
+ Future get data => Future.value(_data);
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ @override
+ Widget build(BuildContext context) {
+ return const Markdown(
+ data: _data,
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/demos/original_demo.dart b/packages/flutter_markdown/example/lib/demos/original_demo.dart
new file mode 100644
index 0000000000..1af3c08a51
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/original_demo.dart
@@ -0,0 +1,183 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import '../shared/markdown_demo_widget.dart';
+
+// ignore_for_file: public_member_api_docs
+
+const String _markdownData = """
+# Markdown Example
+Markdown allows you to easily include formatted text, images, and even formatted
+Dart code in your app.
+
+## Titles
+
+Setext-style
+
+```
+This is an H1
+=============
+
+This is an H2
+-------------
+```
+
+Atx-style
+
+```
+# This is an H1
+
+## This is an H2
+
+###### This is an H6
+```
+
+Select the valid headers:
+
+- [x] `# hello`
+- [ ] `#hello`
+
+## Links
+
+[Google's Homepage][Google]
+
+```
+[inline-style](https://www.google.com)
+
+[reference-style][Google]
+```
+
+## Images
+
+
+
+## Tables
+
+|Syntax |Result |
+|---------------------------------------|-------------------------------------|
+|`*italic 1*` |*italic 1* |
+|`_italic 2_` | _italic 2_ |
+|`**bold 1**` |**bold 1** |
+|`__bold 2__` |__bold 2__ |
+|`This is a ~~strikethrough~~` |This is a ~~strikethrough~~ |
+|`***italic bold 1***` |***italic bold 1*** |
+|`___italic bold 2___` |___italic bold 2___ |
+|`***~~italic bold strikethrough 1~~***`|***~~italic bold strikethrough 1~~***|
+|`~~***italic bold strikethrough 2***~~`|~~***italic bold strikethrough 2***~~|
+
+## Styling
+Style text as _italic_, __bold__, ~~strikethrough~~, or `inline code`.
+
+- Use bulleted lists
+- To better clarify
+- Your points
+
+## Code blocks
+Formatted Dart code looks really pretty too:
+
+```
+void main() {
+ runApp(MaterialApp(
+ home: Scaffold(
+ body: Markdown(data: markdownData),
+ ),
+ ));
+}
+```
+
+## Center Title
+
+###### ※ ※ ※
+
+_* How to implement it see main.dart#L129 in example._
+
+## Custom Syntax
+
+NaOH + Al_2O_3 = NaAlO_2 + H_2O
+
+C_4H_10 = C_2H_6 + C_2H_4
+
+## Markdown widget
+
+This is an example of how to create your own Markdown widget:
+
+ Markdown(data: 'Hello _world_!');
+
+Enjoy!
+
+[Google]: https://www.google.com/
+
+## Line Breaks
+
+This is an example of how to create line breaks (tab or two whitespaces):
+
+line 1
+
+
+line 2
+
+
+
+line 3
+""";
+
+const String _notes = """
+# Original Markdown Demo
+---
+
+## Overview
+
+This is the original Flutter Markdown demo example that was created to
+show how to use the flutter_markdown package. There were limitations in
+the implementation of this demo example that didn't show the full potential
+or extensibility of using the flutter_markdown package. This demo example
+is being preserved for reference purposes.
+
+## Comments
+
+This demo example is being preserved for reference purposes.
+""";
+
+class OriginalMarkdownDemo extends StatelessWidget
+ implements MarkdownDemoWidget {
+ OriginalMarkdownDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Original Markdown Demo';
+
+ @override
+ String get title => OriginalMarkdownDemo._title;
+
+ @override
+ String get description => 'The original demo example. This demo was '
+ 'include with versions of the package prior to version 0.4.4.';
+
+ @override
+ Future get data => Future.value(_markdownData);
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ final ScrollController controller = ScrollController();
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ body: SafeArea(
+ child: Markdown(
+ controller: controller,
+ selectable: true,
+ data: _markdownData,
+ imageDirectory: 'https://raw.githubusercontent.com',
+ ),
+ ),
+ floatingActionButton: FloatingActionButton(
+ child: const Icon(Icons.arrow_upward),
+ onPressed: () => controller.animateTo(0,
+ duration: const Duration(seconds: 1), curve: Curves.easeOut),
+ ),
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/demos/subscript_syntax_demo.dart b/packages/flutter_markdown/example/lib/demos/subscript_syntax_demo.dart
new file mode 100644
index 0000000000..3995237961
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/subscript_syntax_demo.dart
@@ -0,0 +1,169 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import 'package:markdown/markdown.dart' as md;
+import '../shared/markdown_demo_widget.dart';
+
+// ignore_for_file: public_member_api_docs
+
+// Markdown source data showing the use of subscript tags.
+const String _data = '''
+## Subscript Syntax
+
+NaOH + Al_2O_3 = NaAlO_2 + H_2O
+
+C_4H_10 = C_2H_6 + C_2H_4
+''';
+
+const String _notes = """
+# Subscript Syntax Demo
+---
+
+## Overview
+
+This is an example of how to create an inline syntax parser with an
+associated element builder. This example defines an inline syntax parser that
+matches instances of an underscore character "**_**" followed by a integer
+numerical value. When the parser finds a match for this syntax sequence,
+a '**sub**' element is inserted into the abstract syntac tree. The supplied
+builder for the '**sub**' element, SubscriptBuilder, is then called to create
+an appropriate RichText widget for the formatted output.
+
+## Usage
+
+To support a new custom inline Markdown tag, an inline syntax object needs to be
+defined for the Markdown parser and an element builder which is deligated the
+task of building the appropriate Flutter widgets for the resulting Markdown
+output. Instances of these objects need to be provided to the Markdown widget.
+
+```
+ Markdown(
+ data: _data,
+ builders: {
+ 'sub': SubscriptBuilder(),
+ },
+ extensionSet: md.ExtensionSet([], [SubscriptSyntax()]),
+ );
+```
+
+### Inline Syntax Class
+
+```
+class SubscriptSyntax extends md.InlineSyntax {
+ static final _pattern = r'_([0-9]+)';
+
+ SubscriptSyntax() : super(_pattern);
+
+ @override
+ bool onMatch(md.InlineParser parser, Match match) {
+ parser.addNode(md.Element.text('sub', match[1]));
+ return true;
+ }
+```
+
+### Markdown Element Builder
+
+```
+class SubscriptBuilder extends MarkdownElementBuilder {
+ static const List _subscripts = [
+ '₀', '₁', '₂', '₃', '₄', '₅', '₆', '₇', '₈', '₉'
+ ];
+
+ @override
+ Widget visitElementAfter(md.Element element, TextStyle preferredStyle) {
+ String textContent = element.textContent;
+ String text = '';
+ for (int i = 0; i < textContent.length; i++) {
+ text += _subscripts[int.parse(textContent[i])];
+ }
+ return SelectableText.rich(TextSpan(text: text));
+ }
+}
+```
+""";
+
+/// The subscript syntax demo provides an example of creating an inline syntax
+/// object which defines the syntax for the Markdown inline parser and an
+/// accompanying Markdown element builder object to handle subscript tags.
+class SubscriptSyntaxDemo extends StatelessWidget
+ implements MarkdownDemoWidget {
+ const SubscriptSyntaxDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Subscript Syntax Demo';
+
+ @override
+ String get title => SubscriptSyntaxDemo._title;
+
+ @override
+ String get description => 'An example of how to create a custom inline '
+ 'syntax parser and element builder for numerical subscripts.';
+
+ @override
+ Future get data => Future.value(_data);
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: data,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return Markdown(
+ data: snapshot.data!,
+ builders: {
+ 'sub': SubscriptBuilder(),
+ },
+ extensionSet: md.ExtensionSet(
+ [], [SubscriptSyntax()]),
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+}
+
+class SubscriptBuilder extends MarkdownElementBuilder {
+ static const List _subscripts = [
+ '₀',
+ '₁',
+ '₂',
+ '₃',
+ '₄',
+ '₅',
+ '₆',
+ '₇',
+ '₈',
+ '₉'
+ ];
+
+ @override
+ Widget visitElementAfter(md.Element element, TextStyle? preferredStyle) {
+ // We don't currently have a way to control the vertical alignment of text spans.
+ // See https://github.com/flutter/flutter/issues/10906#issuecomment-385723664
+ final String textContent = element.textContent;
+ String text = '';
+ for (int i = 0; i < textContent.length; i++) {
+ text += _subscripts[int.parse(textContent[i])];
+ }
+ return SelectableText.rich(TextSpan(text: text));
+ }
+}
+
+class SubscriptSyntax extends md.InlineSyntax {
+ SubscriptSyntax() : super(_pattern);
+
+ static const String _pattern = r'_([0-9]+)';
+
+ @override
+ bool onMatch(md.InlineParser parser, Match match) {
+ parser.addNode(md.Element.text('sub', match[1]!));
+ return true;
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart b/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart
new file mode 100644
index 0000000000..52db4c7095
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/demos/wrap_alignment_demo.dart
@@ -0,0 +1,133 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/services.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+import '../shared/dropdown_menu.dart';
+import '../shared/markdown_demo_widget.dart';
+import '../shared/markdown_extensions.dart';
+
+// ignore_for_file: public_member_api_docs
+
+const String _notes = '''
+# Wrap Alignment Demo
+---
+The Wrap Alignment Demo shows the effect of defining a wrap alignment for
+various Markdown elements. Wrap alignments for the block elements text
+paragraphs, headers, ordered and unordered lists, blockquotes, and code blocks
+are set in the **MarkdownStyleSheet**. This demo shows the effect of setting
+this parameter universally on these block elements for illustration purposes,
+but they are independent settings.
+
+This demo also shows the effect of setting the **MarkdownStyleSheet** block
+spacing parameter. The Markdown widget lays out block elements in a column using
+**SizedBox** widgets to separate widgets with formatted output. The block
+spacing parameter sets the height of the **SizedBox**.
+''';
+
+class WrapAlignmentDemo extends StatefulWidget implements MarkdownDemoWidget {
+ const WrapAlignmentDemo({Key? key}) : super(key: key);
+
+ static const String _title = 'Wrap Alignment Demo';
+
+ @override
+ String get title => WrapAlignmentDemo._title;
+
+ @override
+ String get description => 'Shows the effect the wrap alignment and block '
+ 'spacing parameters have on various Markdown tagged elements.';
+
+ @override
+ Future get data =>
+ rootBundle.loadString('assets/markdown_test_page.md');
+
+ @override
+ Future get notes => Future.value(_notes);
+
+ @override
+ _WrapAlignmentDemoState createState() => _WrapAlignmentDemoState();
+}
+
+class _WrapAlignmentDemoState extends State {
+ double _blockSpacing = 8.0;
+
+ WrapAlignment _wrapAlignment = WrapAlignment.start;
+
+ final Map _wrapAlignmentMenuItems =
+ Map.fromIterables(
+ WrapAlignment.values.map((WrapAlignment e) => e.displayTitle),
+ WrapAlignment.values,
+ );
+
+ static const List _spacing = [4.0, 8.0, 16.0, 24.0, 32.0];
+ final Map _blockSpacingMenuItems =
+ Map.fromIterables(
+ _spacing.map((double e) => e.toString()),
+ _spacing,
+ );
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: widget.data,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return Column(
+ children: [
+ DropdownMenu(
+ items: _wrapAlignmentMenuItems,
+ label: 'Wrap Alignment:',
+ initialValue: _wrapAlignment,
+ onChanged: (WrapAlignment? value) {
+ if (value != _wrapAlignment) {
+ setState(() {
+ _wrapAlignment = value!;
+ });
+ }
+ },
+ ),
+ DropdownMenu(
+ items: _blockSpacingMenuItems,
+ label: 'Block Spacing:',
+ initialValue: _blockSpacing,
+ onChanged: (double? value) {
+ if (value != _blockSpacing) {
+ setState(() {
+ _blockSpacing = value!;
+ });
+ }
+ },
+ ),
+ Expanded(
+ child: Markdown(
+ key: Key(_wrapAlignment.toString()),
+ data: snapshot.data!,
+ imageDirectory: 'https://raw.githubusercontent.com',
+ styleSheet:
+ MarkdownStyleSheet.fromTheme(Theme.of(context)).copyWith(
+ blockSpacing: _blockSpacing,
+ textAlign: _wrapAlignment,
+ h1Align: _wrapAlignment,
+ h2Align: _wrapAlignment,
+ h3Align: _wrapAlignment,
+ h4Align: _wrapAlignment,
+ h5Align: _wrapAlignment,
+ h6Align: _wrapAlignment,
+ unorderedListAlign: _wrapAlignment,
+ orderedListAlign: _wrapAlignment,
+ blockquoteAlign: _wrapAlignment,
+ codeblockAlign: _wrapAlignment,
+ ),
+ ),
+ ),
+ ],
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/main.dart b/packages/flutter_markdown/example/lib/main.dart
new file mode 100644
index 0000000000..bddea46eda
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/main.dart
@@ -0,0 +1,78 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+///
+/// The simplest use case that illustrates how to make use of the
+/// flutter_markdown package is to include a Markdown widget in a widget tree
+/// and supply it with a character string of text containing Markdown formatting
+/// syntax. Here is a simple Flutter app that creates a Markdown widget that
+/// formats and displays the text in the string _markdownData. The resulting
+/// Flutter app demonstrates the use of headers, rules, and emphasis text from
+/// plain text Markdown syntax.
+///
+/// import 'package:flutter/material.dart';
+/// import 'package:flutter_markdown/flutter_markdown.dart';
+///
+/// const String _markdownData = """
+/// # Minimal Markdown Test
+/// ---
+/// This is a simple Markdown test. Provide a text string with Markdown tags
+/// to the Markdown widget and it will display the formatted output in a
+/// scrollable widget.
+///
+/// ## Section 1
+/// Maecenas eget **arcu egestas**, mollis ex vitae, posuere magna. Nunc eget
+/// aliquam tortor. Vestibulum porta sodales efficitur. Mauris interdum turpis
+/// eget est condimentum, vitae porttitor diam ornare.
+///
+/// ### Subsection A
+/// Sed et massa finibus, blandit massa vel, vulputate velit. Vestibulum vitae
+/// venenatis libero. **__Curabitur sem lectus, feugiat eu justo in, eleifend
+/// accumsan ante.__** Sed a fermentum elit. Curabitur sodales metus id mi
+/// ornare, in ullamcorper magna congue.
+/// """;
+///
+/// void main() {
+/// runApp(
+/// MaterialApp(
+/// title: "Markdown Demo",
+/// home: Scaffold(
+/// appBar: AppBar(
+/// title: const Text('Simple Markdown Demo'),
+/// ),
+/// body: SafeArea(
+/// child: Markdown(
+/// data: _markdownData,
+/// ),
+/// ),
+/// ),
+/// ),
+/// );
+/// }
+///
+/// The flutter_markdown package has options for customizing and extending the
+/// parsing of Markdown syntax and building of the formatted output. The demos
+/// in this example app illustrate some of the potentials of the
+/// flutter_markdown package.
+
+import 'package:flutter/material.dart';
+import 'screens/demo_screen.dart';
+import 'screens/home_screen.dart';
+import 'shared/markdown_demo_widget.dart';
+
+void main() {
+ runApp(
+ MaterialApp(
+ title: 'Markdown Demos',
+ initialRoute: '/',
+ home: HomeScreen(),
+ onGenerateRoute: (RouteSettings settings) {
+ return MaterialPageRoute(
+ builder: (_) => DemoScreen(
+ child: settings.arguments as MarkdownDemoWidget?,
+ ),
+ );
+ },
+ ),
+ );
+}
diff --git a/packages/flutter_markdown/example/lib/screens/demo_card.dart b/packages/flutter_markdown/example/lib/screens/demo_card.dart
new file mode 100644
index 0000000000..e54bdb5f23
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/screens/demo_card.dart
@@ -0,0 +1,56 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../screens/demo_screen.dart';
+import '../shared/markdown_demo_widget.dart';
+
+// ignore_for_file: public_member_api_docs
+
+class DemoCard extends StatelessWidget {
+ const DemoCard({Key? key, required this.widget}) : super(key: key);
+
+ final MarkdownDemoWidget widget;
+
+ @override
+ Widget build(BuildContext context) {
+ return GestureDetector(
+ onTap: () => Navigator.pushNamed(
+ context,
+ DemoScreen.routeName,
+ arguments: widget,
+ ),
+ child: Container(
+ alignment: Alignment.center,
+ child: ConstrainedBox(
+ constraints:
+ const BoxConstraints(minHeight: 50, minWidth: 425, maxWidth: 425),
+ child: Card(
+ color: Colors.blue,
+ margin: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
+ child: Container(
+ padding:
+ const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ child: Column(
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ widget.title,
+ style: Theme.of(context).primaryTextTheme.headline5,
+ ),
+ const SizedBox(
+ height: 6,
+ ),
+ Text(
+ widget.description,
+ style: Theme.of(context).primaryTextTheme.bodyText1,
+ ),
+ ],
+ ),
+ )),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/screens/demo_screen.dart b/packages/flutter_markdown/example/lib/screens/demo_screen.dart
new file mode 100644
index 0000000000..169f873a0c
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/screens/demo_screen.dart
@@ -0,0 +1,178 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import 'package:flutter_markdown/flutter_markdown.dart';
+
+import '../shared/markdown_demo_widget.dart';
+import '../shared/markdown_extensions.dart';
+
+// ignore_for_file: public_member_api_docs
+
+class DemoScreen extends StatelessWidget {
+ const DemoScreen({Key? key, required this.child}) : super(key: key);
+
+ static const String routeName = '/demoScreen';
+
+ final MarkdownDemoWidget? child;
+
+ static const List _tabLabels = ['Formatted', 'Raw', 'Notes'];
+
+ @override
+ Widget build(BuildContext context) {
+ return DefaultTabController(
+ length: 3,
+ child: Scaffold(
+ appBar: AppBar(
+ title: Text(child!.title),
+ bottom: TabBar(
+ indicatorPadding: const EdgeInsets.only(bottom: 8),
+ indicatorSize: TabBarIndicatorSize.label,
+ tabs: [
+ for (String label in _tabLabels) Tab(text: label),
+ ],
+ ),
+ ),
+ body: TabBarView(
+ children: [
+ DemoFormattedView(child: child),
+ DemoRawDataView(data: child!.data),
+ DemoNotesView(notes: child!.notes), //child.notes as String),
+ ],
+ ),
+ ),
+ );
+ }
+}
+
+class DemoFormattedView extends StatelessWidget {
+ const DemoFormattedView({Key? key, required this.child}) : super(key: key);
+
+ final Widget? child;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ alignment: Alignment.center,
+ child: ConstrainedBox(
+ constraints: const BoxConstraints(maxWidth: 1250),
+ child: child,
+ ),
+ );
+ }
+}
+
+class DemoRawDataView extends StatelessWidget {
+ const DemoRawDataView({Key? key, required this.data}) : super(key: key);
+
+ final Future data;
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: data,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return SingleChildScrollView(
+ child: Container(
+ padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ child: Text(
+ snapshot.data!,
+ softWrap: true,
+ style: Theme.of(context)
+ .primaryTextTheme
+ .bodyText1!
+ .copyWith(fontFamily: 'Roboto Mono', color: Colors.black),
+ ),
+ ),
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+}
+
+class DemoNotesView extends StatelessWidget {
+ const DemoNotesView({Key? key, required this.notes}) : super(key: key);
+
+ final Future notes;
+
+ // Handle the link. The [href] in the callback contains information
+ // from the link. The url_launcher package or other similar package
+ // can be used to execute the link.
+ Future linkOnTapHandler(
+ BuildContext context,
+ String text,
+ String? href,
+ String title,
+ ) async {
+ showDialog(
+ context: context,
+ builder: (BuildContext context) =>
+ _createDialog(context, text, href, title),
+ );
+ }
+
+ Widget _createDialog(
+ BuildContext context,
+ String text,
+ String? href,
+ String title,
+ ) =>
+ AlertDialog(
+ title: const Text('Reference Link'),
+ content: SingleChildScrollView(
+ child: ListBody(
+ children: [
+ Text(
+ 'See the following link for more information:',
+ style: Theme.of(context).textTheme.bodyText1,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Link text: $text',
+ style: Theme.of(context).textTheme.bodyText2,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Link destination: $href',
+ style: Theme.of(context).textTheme.bodyText2,
+ ),
+ const SizedBox(height: 8),
+ Text(
+ 'Link title: $title',
+ style: Theme.of(context).textTheme.bodyText2,
+ ),
+ ],
+ ),
+ ),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.of(context).pop(),
+ child: const Text('OK'),
+ )
+ ],
+ );
+
+ @override
+ Widget build(BuildContext context) {
+ return FutureBuilder(
+ future: notes,
+ builder: (BuildContext context, AsyncSnapshot snapshot) {
+ if (snapshot.connectionState == ConnectionState.done) {
+ return Markdown(
+ data: snapshot.data!,
+ extensionSet: MarkdownExtensionSet.githubFlavored.value,
+ onTapLink: (String text, String? href, String title) =>
+ linkOnTapHandler(context, text, href, title),
+ );
+ } else {
+ return const CircularProgressIndicator();
+ }
+ },
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/screens/home_screen.dart b/packages/flutter_markdown/example/lib/screens/home_screen.dart
new file mode 100644
index 0000000000..ed96c94eeb
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/screens/home_screen.dart
@@ -0,0 +1,52 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+import '../demos/basic_markdown_demo.dart';
+import '../demos/centered_header_demo.dart';
+import '../demos/extended_emoji_demo.dart';
+import '../demos/minimal_markdown_demo.dart';
+import '../demos/original_demo.dart';
+import '../demos/subscript_syntax_demo.dart';
+import '../demos/wrap_alignment_demo.dart';
+import '../screens/demo_card.dart';
+import '../shared/markdown_demo_widget.dart';
+
+// ignore_for_file: public_member_api_docs
+
+class HomeScreen extends StatelessWidget {
+ HomeScreen({Key? key}) : super(key: key);
+
+ static const String routeName = '/homeScreen';
+
+ final List _demos = [
+ const MinimalMarkdownDemo(),
+ const BasicMarkdownDemo(),
+ const WrapAlignmentDemo(),
+ const SubscriptSyntaxDemo(),
+ const ExtendedEmojiDemo(),
+ OriginalMarkdownDemo(),
+ const CenteredHeaderDemo(),
+ ];
+
+ @override
+ Widget build(BuildContext context) {
+ return Scaffold(
+ backgroundColor: Colors.white,
+ appBar: AppBar(
+ title: const Text('Markdown Demos'),
+ ),
+ body: SafeArea(
+ child: Container(
+ color: Colors.black12,
+ child: ListView(
+ children: [
+ for (MarkdownDemoWidget demo in _demos) DemoCard(widget: demo),
+ ],
+ ),
+ ),
+ ),
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/shared/dropdown_menu.dart b/packages/flutter_markdown/example/lib/shared/dropdown_menu.dart
new file mode 100644
index 0000000000..db1371bb98
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/shared/dropdown_menu.dart
@@ -0,0 +1,82 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/material.dart';
+
+// ignore_for_file: public_member_api_docs
+
+class DropdownMenu extends StatelessWidget {
+ DropdownMenu({
+ Key? key,
+ required this.items,
+ required this.initialValue,
+ required this.label,
+ this.labelStyle,
+ Color? background,
+ EdgeInsetsGeometry? padding,
+ Color? menuItemBackground,
+ EdgeInsetsGeometry? menuItemMargin,
+ this.onChanged,
+ }) : assert(
+ items.isNotEmpty, 'The items map must contain at least one entry'),
+ background = background ?? Colors.black12,
+ padding =
+ padding ?? const EdgeInsets.symmetric(horizontal: 12, vertical: 8),
+ menuItemBackground = menuItemBackground ?? Colors.white,
+ menuItemMargin = menuItemMargin ?? const EdgeInsets.only(left: 4),
+ super(key: key);
+
+ final Map items;
+
+ final T initialValue;
+
+ final String label;
+
+ final TextStyle? labelStyle;
+
+ final ValueChanged? onChanged;
+
+ final Color background;
+
+ final EdgeInsetsGeometry padding;
+
+ final Color menuItemBackground;
+
+ final EdgeInsetsGeometry menuItemMargin;
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ color: background,
+ padding: padding,
+ child: Row(
+ children: [
+ Text(
+ label,
+ style: labelStyle ?? Theme.of(context).textTheme.subtitle1,
+ ),
+ Container(
+ color: menuItemBackground,
+ margin: menuItemMargin,
+ child: DropdownButton(
+ isDense: true,
+ value: initialValue,
+ items: >[
+ for (String item in items.keys)
+ DropdownMenuItem(
+ child: Container(
+ padding: const EdgeInsets.only(left: 4),
+ child: Text(item),
+ ),
+ value: items[item],
+ ),
+ ],
+ onChanged: (T? value) => onChanged!(value),
+ ),
+ ),
+ ],
+ ),
+ );
+ }
+}
diff --git a/packages/flutter_markdown/example/lib/shared/markdown_demo_widget.dart b/packages/flutter_markdown/example/lib/shared/markdown_demo_widget.dart
new file mode 100644
index 0000000000..5fa6d0ad42
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/shared/markdown_demo_widget.dart
@@ -0,0 +1,35 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/widgets.dart';
+
+// ignore_for_file: public_member_api_docs
+
+abstract class MarkdownDemoWidget extends Widget {
+ const MarkdownDemoWidget({Key? key}) : super(key: key);
+
+ // The title property should be a short name to uniquely identify the example
+ // demo. The title will be displayed at the top of the card in the home screen
+ // to identify the demo and as the banner title on the demo screen.
+ String get title;
+
+ // The description property should be a short explanation to provide
+ // additional information to clarify the actions performed by the demo. This
+ // should be a terse explanation of no more than three sentences.
+ String get description;
+
+ // The data property is the sample Markdown source text data to be displayed
+ // in the Formatted and Raw tabs of the demo screen. This data will be used by
+ // the demo widget that implements MarkdownDemoWidget to format the Markdown
+ // data to be displayed in the Formatted tab. The raw source text of data is
+ // used by the Raw tab of the demo screen. The data can be as short or as long
+ // as needed for demonstration purposes.
+ Future get data;
+
+ // The notes property is a detailed explanation of the syntax, concepts,
+ // comments, notes, or other additional information useful in explaining the
+ // demo. The notes are displayed in the Notes tab of the demo screen. Notes
+ // supports Markdown data to allow for rich text formatting.
+ Future get notes;
+}
diff --git a/packages/flutter_markdown/example/lib/shared/markdown_extensions.dart b/packages/flutter_markdown/example/lib/shared/markdown_extensions.dart
new file mode 100644
index 0000000000..feb0e47c02
--- /dev/null
+++ b/packages/flutter_markdown/example/lib/shared/markdown_extensions.dart
@@ -0,0 +1,62 @@
+// Copyright 2020 Quiverware LLC. Open source contribution. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/material.dart';
+import 'package:markdown/markdown.dart' as md;
+
+// ignore_for_file: public_member_api_docs
+
+enum MarkdownExtensionSet { none, commonMark, githubFlavored, githubWeb }
+
+extension MarkdownExtensionSetExtension on MarkdownExtensionSet {
+ String get name => describeEnum(this);
+
+ String get displayTitle => () {
+ switch (this) {
+ case MarkdownExtensionSet.none:
+ return 'None';
+ case MarkdownExtensionSet.commonMark:
+ return 'Common Mark';
+ case MarkdownExtensionSet.githubFlavored:
+ return 'GitHub Flavored';
+ case MarkdownExtensionSet.githubWeb:
+ return 'GitHub Web';
+ }
+ }();
+
+ md.ExtensionSet get value => () {
+ switch (this) {
+ case MarkdownExtensionSet.none:
+ return md.ExtensionSet.none;
+ case MarkdownExtensionSet.commonMark:
+ return md.ExtensionSet.commonMark;
+ case MarkdownExtensionSet.githubFlavored:
+ return md.ExtensionSet.gitHubFlavored;
+ case MarkdownExtensionSet.githubWeb:
+ return md.ExtensionSet.gitHubWeb;
+ }
+ }();
+}
+
+extension WrapAlignmentExtension on WrapAlignment {
+ String get name => describeEnum(this);
+
+ String get displayTitle => () {
+ switch (this) {
+ case WrapAlignment.center:
+ return 'Center';
+ case WrapAlignment.end:
+ return 'End';
+ case WrapAlignment.spaceAround:
+ return 'Space Around';
+ case WrapAlignment.spaceBetween:
+ return 'Space Between';
+ case WrapAlignment.spaceEvenly:
+ return 'Space Evenly';
+ case WrapAlignment.start:
+ return 'Start';
+ }
+ }();
+}
diff --git a/packages/flutter_markdown/example/linux/.gitignore b/packages/flutter_markdown/example/linux/.gitignore
new file mode 100644
index 0000000000..d3896c9844
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/.gitignore
@@ -0,0 +1 @@
+flutter/ephemeral
diff --git a/packages/flutter_markdown/example/linux/CMakeLists.txt b/packages/flutter_markdown/example/linux/CMakeLists.txt
new file mode 100644
index 0000000000..76cc6c58c8
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/CMakeLists.txt
@@ -0,0 +1,106 @@
+cmake_minimum_required(VERSION 3.10)
+project(runner LANGUAGES CXX)
+
+set(BINARY_NAME "flutter_markdown_example")
+set(APPLICATION_ID "io.flutter.packages.flutter_markdown_example")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+endif()
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_14)
+ target_compile_options(${TARGET} PRIVATE -Wall -Werror)
+ target_compile_options(${TARGET} PRIVATE "$<$>:-O3>")
+ target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+
+add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}")
+
+# Application build
+add_executable(${BINARY_NAME}
+ "main.cc"
+ "my_application.cc"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+)
+apply_standard_settings(${BINARY_NAME})
+target_link_libraries(${BINARY_NAME} PRIVATE flutter)
+target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)
+add_dependencies(${BINARY_NAME} flutter_assemble)
+# Only the install-generated bundle's copy of the executable will launch
+# correctly, since the resources must in the right relative locations. To avoid
+# people trying to run the unbundled copy, put it in a subdirectory instead of
+# the default top-level location.
+set_target_properties(${BINARY_NAME}
+ PROPERTIES
+ RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run"
+)
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# By default, "installing" just makes a relocatable bundle in the build
+# directory.
+set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle")
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+# Start with a clean build bundle directory every time.
+install(CODE "
+ file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\")
+ " COMPONENT Runtime)
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+if(NOT CMAKE_BUILD_TYPE MATCHES "Debug")
+ install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
diff --git a/packages/flutter_markdown/example/linux/flutter/CMakeLists.txt b/packages/flutter_markdown/example/linux/flutter/CMakeLists.txt
new file mode 100644
index 0000000000..a1da1b9e53
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/flutter/CMakeLists.txt
@@ -0,0 +1,91 @@
+cmake_minimum_required(VERSION 3.10)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+
+# Serves the same purpose as list(TRANSFORM ... PREPEND ...),
+# which isn't available in 3.10.
+function(list_prepend LIST_NAME PREFIX)
+ set(NEW_LIST "")
+ foreach(element ${${LIST_NAME}})
+ list(APPEND NEW_LIST "${PREFIX}${element}")
+ endforeach(element)
+ set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE)
+endfunction()
+
+# === Flutter Library ===
+# System-level dependencies.
+find_package(PkgConfig REQUIRED)
+pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)
+pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)
+pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)
+pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid)
+pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma)
+
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "fl_basic_message_channel.h"
+ "fl_binary_codec.h"
+ "fl_binary_messenger.h"
+ "fl_dart_project.h"
+ "fl_engine.h"
+ "fl_json_message_codec.h"
+ "fl_json_method_codec.h"
+ "fl_message_codec.h"
+ "fl_method_call.h"
+ "fl_method_channel.h"
+ "fl_method_codec.h"
+ "fl_method_response.h"
+ "fl_plugin_registrar.h"
+ "fl_plugin_registry.h"
+ "fl_standard_message_codec.h"
+ "fl_standard_method_codec.h"
+ "fl_string_codec.h"
+ "fl_value.h"
+ "fl_view.h"
+ "flutter_linux.h"
+)
+list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}")
+target_link_libraries(flutter INTERFACE
+ PkgConfig::GTK
+ PkgConfig::GLIB
+ PkgConfig::GIO
+ PkgConfig::BLKID
+ PkgConfig::LZMA
+)
+add_dependencies(flutter flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CMAKE_CURRENT_BINARY_DIR}/_phony_
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh"
+ linux-x64 ${CMAKE_BUILD_TYPE}
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+)
diff --git a/packages/flutter_markdown/example/linux/flutter/generated_plugin_registrant.cc b/packages/flutter_markdown/example/linux/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000000..890de29bba
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,7 @@
+//
+// Generated file. Do not edit.
+//
+
+#include "generated_plugin_registrant.h"
+
+void fl_register_plugins(FlPluginRegistry* registry) {}
diff --git a/packages/flutter_markdown/example/linux/flutter/generated_plugin_registrant.h b/packages/flutter_markdown/example/linux/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000000..9bf7478940
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/flutter/generated_plugin_registrant.h
@@ -0,0 +1,13 @@
+//
+// Generated file. Do not edit.
+//
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void fl_register_plugins(FlPluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/packages/flutter_markdown/example/linux/flutter/generated_plugins.cmake b/packages/flutter_markdown/example/linux/flutter/generated_plugins.cmake
new file mode 100644
index 0000000000..51436ae8c9
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/flutter_markdown/example/linux/main.cc b/packages/flutter_markdown/example/linux/main.cc
new file mode 100644
index 0000000000..e7c5c54370
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/main.cc
@@ -0,0 +1,6 @@
+#include "my_application.h"
+
+int main(int argc, char** argv) {
+ g_autoptr(MyApplication) app = my_application_new();
+ return g_application_run(G_APPLICATION(app), argc, argv);
+}
diff --git a/packages/flutter_markdown/example/linux/my_application.cc b/packages/flutter_markdown/example/linux/my_application.cc
new file mode 100644
index 0000000000..a18de4cde7
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/my_application.cc
@@ -0,0 +1,106 @@
+#include "my_application.h"
+
+#include
+#ifdef GDK_WINDOWING_X11
+#include
+#endif
+
+#include "flutter/generated_plugin_registrant.h"
+
+struct _MyApplication {
+ GtkApplication parent_instance;
+ char** dart_entrypoint_arguments;
+};
+
+G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)
+
+// Implements GApplication::activate.
+static void my_application_activate(GApplication* application) {
+ MyApplication* self = MY_APPLICATION(application);
+ GtkWindow* window =
+ GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));
+
+ // Use a header bar when running in GNOME as this is the common style used
+ // by applications and is the setup most users will be using (e.g. Ubuntu
+ // desktop).
+ // If running on X and not using GNOME then just use a traditional title bar
+ // in case the window manager does more exotic layout, e.g. tiling.
+ // If running on Wayland assume the header bar will work (may need changing
+ // if future cases occur).
+ gboolean use_header_bar = TRUE;
+#ifdef GDK_WINDOWING_X11
+ GdkScreen* screen = gtk_window_get_screen(window);
+ if (GDK_IS_X11_SCREEN(screen)) {
+ const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);
+ if (g_strcmp0(wm_name, "GNOME Shell") != 0) {
+ use_header_bar = FALSE;
+ }
+ }
+#endif
+ if (use_header_bar) {
+ GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
+ gtk_widget_show(GTK_WIDGET(header_bar));
+ gtk_header_bar_set_title(header_bar, "flutter_markdown_example");
+ gtk_header_bar_set_show_close_button(header_bar, TRUE);
+ gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
+ } else {
+ gtk_window_set_title(window, "flutter_markdown_example");
+ }
+
+ gtk_window_set_default_size(window, 1280, 720);
+ gtk_widget_show(GTK_WIDGET(window));
+
+ g_autoptr(FlDartProject) project = fl_dart_project_new();
+ fl_dart_project_set_dart_entrypoint_arguments(
+ project, self->dart_entrypoint_arguments);
+
+ FlView* view = fl_view_new(project);
+ gtk_widget_show(GTK_WIDGET(view));
+ gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));
+
+ fl_register_plugins(FL_PLUGIN_REGISTRY(view));
+
+ gtk_widget_grab_focus(GTK_WIDGET(view));
+}
+
+// Implements GApplication::local_command_line.
+static gboolean my_application_local_command_line(GApplication* application,
+ gchar*** arguments,
+ int* exit_status) {
+ MyApplication* self = MY_APPLICATION(application);
+ // Strip out the first argument as it is the binary name.
+ self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);
+
+ g_autoptr(GError) error = nullptr;
+ if (!g_application_register(application, nullptr, &error)) {
+ g_warning("Failed to register: %s", error->message);
+ *exit_status = 1;
+ return TRUE;
+ }
+
+ g_application_activate(application);
+ *exit_status = 0;
+
+ return TRUE;
+}
+
+// Implements GObject::dispose.
+static void my_application_dispose(GObject* object) {
+ MyApplication* self = MY_APPLICATION(object);
+ g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);
+ G_OBJECT_CLASS(my_application_parent_class)->dispose(object);
+}
+
+static void my_application_class_init(MyApplicationClass* klass) {
+ G_APPLICATION_CLASS(klass)->activate = my_application_activate;
+ G_APPLICATION_CLASS(klass)->local_command_line =
+ my_application_local_command_line;
+ G_OBJECT_CLASS(klass)->dispose = my_application_dispose;
+}
+
+static void my_application_init(MyApplication* self) {}
+
+MyApplication* my_application_new() {
+ return MY_APPLICATION(g_object_new(
+ my_application_get_type(), "application-id", APPLICATION_ID, nullptr));
+}
diff --git a/packages/flutter_markdown/example/linux/my_application.h b/packages/flutter_markdown/example/linux/my_application.h
new file mode 100644
index 0000000000..72271d5e41
--- /dev/null
+++ b/packages/flutter_markdown/example/linux/my_application.h
@@ -0,0 +1,18 @@
+#ifndef FLUTTER_MY_APPLICATION_H_
+#define FLUTTER_MY_APPLICATION_H_
+
+#include
+
+G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,
+ GtkApplication)
+
+/**
+ * my_application_new:
+ *
+ * Creates a new Flutter-based application.
+ *
+ * Returns: a new #MyApplication.
+ */
+MyApplication* my_application_new();
+
+#endif // FLUTTER_MY_APPLICATION_H_
diff --git a/packages/flutter_markdown/example/macos/.gitignore b/packages/flutter_markdown/example/macos/.gitignore
new file mode 100644
index 0000000000..d2fd377230
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/.gitignore
@@ -0,0 +1,6 @@
+# Flutter-related
+**/Flutter/ephemeral/
+**/Pods/
+
+# Xcode-related
+**/xcuserdata/
diff --git a/packages/flutter_markdown/example/macos/Flutter/Flutter-Debug.xcconfig b/packages/flutter_markdown/example/macos/Flutter/Flutter-Debug.xcconfig
new file mode 100644
index 0000000000..c2efd0b608
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Flutter/Flutter-Debug.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/flutter_markdown/example/macos/Flutter/Flutter-Release.xcconfig b/packages/flutter_markdown/example/macos/Flutter/Flutter-Release.xcconfig
new file mode 100644
index 0000000000..c2efd0b608
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Flutter/Flutter-Release.xcconfig
@@ -0,0 +1 @@
+#include "ephemeral/Flutter-Generated.xcconfig"
diff --git a/packages/flutter_markdown/example/macos/Flutter/GeneratedPluginRegistrant.swift b/packages/flutter_markdown/example/macos/Flutter/GeneratedPluginRegistrant.swift
new file mode 100644
index 0000000000..cccf817a52
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Flutter/GeneratedPluginRegistrant.swift
@@ -0,0 +1,10 @@
+//
+// Generated file. Do not edit.
+//
+
+import FlutterMacOS
+import Foundation
+
+
+func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
+}
diff --git a/packages/flutter_markdown/example/macos/Runner.xcodeproj/project.pbxproj b/packages/flutter_markdown/example/macos/Runner.xcodeproj/project.pbxproj
new file mode 100644
index 0000000000..39be746092
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner.xcodeproj/project.pbxproj
@@ -0,0 +1,572 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 51;
+ objects = {
+
+/* Begin PBXAggregateTarget section */
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {
+ isa = PBXAggregateTarget;
+ buildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */;
+ buildPhases = (
+ 33CC111E2044C6BF0003C045 /* ShellScript */,
+ );
+ dependencies = (
+ );
+ name = "Flutter Assemble";
+ productName = FLX;
+ };
+/* End PBXAggregateTarget section */
+
+/* Begin PBXBuildFile section */
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXContainerItemProxy section */
+ 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {
+ isa = PBXContainerItemProxy;
+ containerPortal = 33CC10E52044A3C60003C045 /* Project object */;
+ proxyType = 1;
+ remoteGlobalIDString = 33CC111A2044C6BA0003C045;
+ remoteInfo = FLX;
+ };
+/* End PBXContainerItemProxy section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+ 33CC110E2044A8840003C045 /* Bundle Framework */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ );
+ name = "Bundle Framework";
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; };
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; };
+ 33CC10ED2044A3C60003C045 /* flutter_markdown_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "flutter_markdown_example.app"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; };
+ 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; };
+ 33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = ""; };
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = ""; };
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Debug.xcconfig"; sourceTree = ""; };
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = "Flutter-Release.xcconfig"; sourceTree = ""; };
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = "Flutter-Generated.xcconfig"; path = "ephemeral/Flutter-Generated.xcconfig"; sourceTree = ""; };
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = ""; };
+ 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = ""; };
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = ""; };
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = ""; };
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+ 33CC10EA2044A3C60003C045 /* Frameworks */ = {
+ isa = PBXFrameworksBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+ 33BA886A226E78AF003329D5 /* Configs */ = {
+ isa = PBXGroup;
+ children = (
+ 33E5194F232828860026EE4D /* AppInfo.xcconfig */,
+ 9740EEB21CF90195004384FC /* Debug.xcconfig */,
+ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
+ 333000ED22D3DE5D00554162 /* Warnings.xcconfig */,
+ );
+ path = Configs;
+ sourceTree = "";
+ };
+ 33CC10E42044A3C60003C045 = {
+ isa = PBXGroup;
+ children = (
+ 33FAB671232836740065AC1E /* Runner */,
+ 33CEB47122A05771004F2AC0 /* Flutter */,
+ 33CC10EE2044A3C60003C045 /* Products */,
+ D73912EC22F37F3D000D13A0 /* Frameworks */,
+ );
+ sourceTree = "";
+ };
+ 33CC10EE2044A3C60003C045 /* Products */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10ED2044A3C60003C045 /* flutter_markdown_example.app */,
+ );
+ name = Products;
+ sourceTree = "";
+ };
+ 33CC11242044D66E0003C045 /* Resources */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F22044A3C60003C045 /* Assets.xcassets */,
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */,
+ 33CC10F72044A3C60003C045 /* Info.plist */,
+ );
+ name = Resources;
+ path = ..;
+ sourceTree = "";
+ };
+ 33CEB47122A05771004F2AC0 /* Flutter */ = {
+ isa = PBXGroup;
+ children = (
+ 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,
+ 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,
+ 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,
+ 33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,
+ );
+ path = Flutter;
+ sourceTree = "";
+ };
+ 33FAB671232836740065AC1E /* Runner */ = {
+ isa = PBXGroup;
+ children = (
+ 33CC10F02044A3C60003C045 /* AppDelegate.swift */,
+ 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,
+ 33E51913231747F40026EE4D /* DebugProfile.entitlements */,
+ 33E51914231749380026EE4D /* Release.entitlements */,
+ 33CC11242044D66E0003C045 /* Resources */,
+ 33BA886A226E78AF003329D5 /* Configs */,
+ );
+ path = Runner;
+ sourceTree = "";
+ };
+ D73912EC22F37F3D000D13A0 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+ 33CC10EC2044A3C60003C045 /* Runner */ = {
+ isa = PBXNativeTarget;
+ buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */;
+ buildPhases = (
+ 33CC10E92044A3C60003C045 /* Sources */,
+ 33CC10EA2044A3C60003C045 /* Frameworks */,
+ 33CC10EB2044A3C60003C045 /* Resources */,
+ 33CC110E2044A8840003C045 /* Bundle Framework */,
+ 3399D490228B24CF009A79C7 /* ShellScript */,
+ );
+ buildRules = (
+ );
+ dependencies = (
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */,
+ );
+ name = Runner;
+ productName = Runner;
+ productReference = 33CC10ED2044A3C60003C045 /* flutter_markdown_example.app */;
+ productType = "com.apple.product-type.application";
+ };
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+ 33CC10E52044A3C60003C045 /* Project object */ = {
+ isa = PBXProject;
+ attributes = {
+ LastSwiftUpdateCheck = 0920;
+ LastUpgradeCheck = 0930;
+ ORGANIZATIONNAME = "";
+ TargetAttributes = {
+ 33CC10EC2044A3C60003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ LastSwiftMigration = 1100;
+ ProvisioningStyle = Automatic;
+ SystemCapabilities = {
+ com.apple.Sandbox = {
+ enabled = 1;
+ };
+ };
+ };
+ 33CC111A2044C6BA0003C045 = {
+ CreatedOnToolsVersion = 9.2;
+ ProvisioningStyle = Manual;
+ };
+ };
+ };
+ buildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */;
+ compatibilityVersion = "Xcode 9.3";
+ developmentRegion = en;
+ hasScannedForEncodings = 0;
+ knownRegions = (
+ en,
+ Base,
+ );
+ mainGroup = 33CC10E42044A3C60003C045;
+ productRefGroup = 33CC10EE2044A3C60003C045 /* Products */;
+ projectDirPath = "";
+ projectRoot = "";
+ targets = (
+ 33CC10EC2044A3C60003C045 /* Runner */,
+ 33CC111A2044C6BA0003C045 /* Flutter Assemble */,
+ );
+ };
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+ 33CC10EB2044A3C60003C045 /* Resources */ = {
+ isa = PBXResourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,
+ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+ 3399D490228B24CF009A79C7 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ );
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "echo \"$PRODUCT_NAME.app\" > \"$PROJECT_DIR\"/Flutter/ephemeral/.app_filename && \"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh embed\n";
+ };
+ 33CC111E2044C6BF0003C045 /* ShellScript */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ Flutter/ephemeral/FlutterInputs.xcfilelist,
+ );
+ inputPaths = (
+ Flutter/ephemeral/tripwire,
+ );
+ outputFileListPaths = (
+ Flutter/ephemeral/FlutterOutputs.xcfilelist,
+ );
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire";
+ };
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+ 33CC10E92044A3C60003C045 /* Sources */ = {
+ isa = PBXSourcesBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,
+ 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,
+ 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXSourcesBuildPhase section */
+
+/* Begin PBXTargetDependency section */
+ 33CC11202044C79F0003C045 /* PBXTargetDependency */ = {
+ isa = PBXTargetDependency;
+ target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;
+ targetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;
+ };
+/* End PBXTargetDependency section */
+
+/* Begin PBXVariantGroup section */
+ 33CC10F42044A3C60003C045 /* MainMenu.xib */ = {
+ isa = PBXVariantGroup;
+ children = (
+ 33CC10F52044A3C60003C045 /* Base */,
+ );
+ name = MainMenu.xib;
+ path = Runner;
+ sourceTree = "";
+ };
+/* End PBXVariantGroup section */
+
+/* Begin XCBuildConfiguration section */
+ 338D0CE9231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Profile;
+ };
+ 338D0CEA231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Profile;
+ };
+ 338D0CEB231458BD00FA5F75 /* Profile */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Profile;
+ };
+ 33CC10F92044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = dwarf;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_DYNAMIC_NO_PIC = NO;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_OPTIMIZATION_LEVEL = 0;
+ GCC_PREPROCESSOR_DEFINITIONS = (
+ "DEBUG=1",
+ "$(inherited)",
+ );
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = YES;
+ ONLY_ACTIVE_ARCH = YES;
+ SDKROOT = macosx;
+ SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ };
+ name = Debug;
+ };
+ 33CC10FA2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
+ buildSettings = {
+ ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ANALYZER_NONNULL = YES;
+ CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+ CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+ CLANG_CXX_LIBRARY = "libc++";
+ CLANG_ENABLE_MODULES = YES;
+ CLANG_ENABLE_OBJC_ARC = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+ CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CODE_SIGN_IDENTITY = "-";
+ COPY_PHASE_STRIP = NO;
+ DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+ ENABLE_NS_ASSERTIONS = NO;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ GCC_C_LANGUAGE_STANDARD = gnu11;
+ GCC_NO_COMMON_BLOCKS = YES;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+ GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+ GCC_WARN_UNUSED_FUNCTION = YES;
+ GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.11;
+ MTL_ENABLE_DEBUG_INFO = NO;
+ SDKROOT = macosx;
+ SWIFT_COMPILATION_MODE = wholemodule;
+ SWIFT_OPTIMIZATION_LEVEL = "-O";
+ };
+ name = Release;
+ };
+ 33CC10FC2044A3C60003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_OPTIMIZATION_LEVEL = "-Onone";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Debug;
+ };
+ 33CC10FD2044A3C60003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ baseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;
+ buildSettings = {
+ ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
+ CLANG_ENABLE_MODULES = YES;
+ CODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;
+ CODE_SIGN_STYLE = Automatic;
+ COMBINE_HIDPI_IMAGES = YES;
+ INFOPLIST_FILE = Runner/Info.plist;
+ LD_RUNPATH_SEARCH_PATHS = (
+ "$(inherited)",
+ "@executable_path/../Frameworks",
+ );
+ PROVISIONING_PROFILE_SPECIFIER = "";
+ SWIFT_VERSION = 5.0;
+ };
+ name = Release;
+ };
+ 33CC111C2044C6BA0003C045 /* Debug */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Manual;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Debug;
+ };
+ 33CC111D2044C6BA0003C045 /* Release */ = {
+ isa = XCBuildConfiguration;
+ buildSettings = {
+ CODE_SIGN_STYLE = Automatic;
+ PRODUCT_NAME = "$(TARGET_NAME)";
+ };
+ name = Release;
+ };
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+ 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10F92044A3C60003C045 /* Debug */,
+ 33CC10FA2044A3C60003C045 /* Release */,
+ 338D0CE9231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC10FC2044A3C60003C045 /* Debug */,
+ 33CC10FD2044A3C60003C045 /* Release */,
+ 338D0CEA231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+ 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget "Flutter Assemble" */ = {
+ isa = XCConfigurationList;
+ buildConfigurations = (
+ 33CC111C2044C6BA0003C045 /* Debug */,
+ 33CC111D2044C6BA0003C045 /* Release */,
+ 338D0CEB231458BD00FA5F75 /* Profile */,
+ );
+ defaultConfigurationIsVisible = 0;
+ defaultConfigurationName = Release;
+ };
+/* End XCConfigurationList section */
+ };
+ rootObject = 33CC10E52044A3C60003C045 /* Project object */;
+}
diff --git a/packages/flutter_markdown/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter_markdown/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000000..18d981003d
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/packages/flutter_markdown/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
new file mode 100644
index 0000000000..74c450c90f
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme
@@ -0,0 +1,89 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner.xcworkspace/contents.xcworkspacedata b/packages/flutter_markdown/example/macos/Runner.xcworkspace/contents.xcworkspacedata
new file mode 100644
index 0000000000..1d526a16ed
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner.xcworkspace/contents.xcworkspacedata
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/packages/flutter_markdown/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
new file mode 100644
index 0000000000..18d981003d
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist
@@ -0,0 +1,8 @@
+
+
+
+
+ IDEDidComputeMac32BitWarning
+
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner/AppDelegate.swift b/packages/flutter_markdown/example/macos/Runner/AppDelegate.swift
new file mode 100644
index 0000000000..d53ef64377
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/AppDelegate.swift
@@ -0,0 +1,9 @@
+import Cocoa
+import FlutterMacOS
+
+@NSApplicationMain
+class AppDelegate: FlutterAppDelegate {
+ override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
+ return true
+ }
+}
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
new file mode 100644
index 0000000000..a2ec33f19f
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
@@ -0,0 +1,68 @@
+{
+ "images" : [
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_16.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "16x16",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_32.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "32x32",
+ "idiom" : "mac",
+ "filename" : "app_icon_64.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_128.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "128x128",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_256.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "256x256",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "2x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_512.png",
+ "scale" : "1x"
+ },
+ {
+ "size" : "512x512",
+ "idiom" : "mac",
+ "filename" : "app_icon_1024.png",
+ "scale" : "2x"
+ }
+ ],
+ "info" : {
+ "version" : 1,
+ "author" : "xcode"
+ }
+}
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png
new file mode 100644
index 0000000000..3c4935a7ca
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png
new file mode 100644
index 0000000000..ed4cc16421
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png
new file mode 100644
index 0000000000..483be61389
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png
new file mode 100644
index 0000000000..bcbf36df2f
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png
new file mode 100644
index 0000000000..9c0a652864
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png
new file mode 100644
index 0000000000..e71a726136
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png
new file mode 100644
index 0000000000..8a31fe2dd3
Binary files /dev/null and b/packages/flutter_markdown/example/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ
diff --git a/packages/flutter_markdown/example/macos/Runner/Base.lproj/MainMenu.xib b/packages/flutter_markdown/example/macos/Runner/Base.lproj/MainMenu.xib
new file mode 100644
index 0000000000..537341abf9
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Base.lproj/MainMenu.xib
@@ -0,0 +1,339 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner/Configs/AppInfo.xcconfig b/packages/flutter_markdown/example/macos/Runner/Configs/AppInfo.xcconfig
new file mode 100644
index 0000000000..206ecf51aa
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Configs/AppInfo.xcconfig
@@ -0,0 +1,14 @@
+// Application-level settings for the Runner target.
+//
+// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the
+// future. If not, the values below would default to using the project name when this becomes a
+// 'flutter create' template.
+
+// The application's name. By default this is also the title of the Flutter window.
+PRODUCT_NAME = flutter_markdown_example
+
+// The application's bundle identifier
+PRODUCT_BUNDLE_IDENTIFIER = io.flutter.packages.flutterMarkdownExample
+
+// The copyright displayed in application information
+PRODUCT_COPYRIGHT = Copyright © 2021 io.flutter.packages. All rights reserved.
diff --git a/packages/flutter_markdown/example/macos/Runner/Configs/Debug.xcconfig b/packages/flutter_markdown/example/macos/Runner/Configs/Debug.xcconfig
new file mode 100644
index 0000000000..36b0fd9464
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Configs/Debug.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Debug.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/flutter_markdown/example/macos/Runner/Configs/Release.xcconfig b/packages/flutter_markdown/example/macos/Runner/Configs/Release.xcconfig
new file mode 100644
index 0000000000..dff4f49561
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Configs/Release.xcconfig
@@ -0,0 +1,2 @@
+#include "../../Flutter/Flutter-Release.xcconfig"
+#include "Warnings.xcconfig"
diff --git a/packages/flutter_markdown/example/macos/Runner/Configs/Warnings.xcconfig b/packages/flutter_markdown/example/macos/Runner/Configs/Warnings.xcconfig
new file mode 100644
index 0000000000..42bcbf4780
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Configs/Warnings.xcconfig
@@ -0,0 +1,13 @@
+WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings
+GCC_WARN_UNDECLARED_SELECTOR = YES
+CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES
+CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE
+CLANG_WARN__DUPLICATE_METHOD_MATCH = YES
+CLANG_WARN_PRAGMA_PACK = YES
+CLANG_WARN_STRICT_PROTOTYPES = YES
+CLANG_WARN_COMMA = YES
+GCC_WARN_STRICT_SELECTOR_MATCH = YES
+CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES
+CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES
+GCC_WARN_SHADOW = YES
+CLANG_WARN_UNREACHABLE_CODE = YES
diff --git a/packages/flutter_markdown/example/macos/Runner/DebugProfile.entitlements b/packages/flutter_markdown/example/macos/Runner/DebugProfile.entitlements
new file mode 100644
index 0000000000..08c3ab17cc
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/DebugProfile.entitlements
@@ -0,0 +1,14 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.network.server
+
+ com.apple.security.network.client
+
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner/Info.plist b/packages/flutter_markdown/example/macos/Runner/Info.plist
new file mode 100644
index 0000000000..4789daa6a4
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Info.plist
@@ -0,0 +1,32 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ $(DEVELOPMENT_LANGUAGE)
+ CFBundleExecutable
+ $(EXECUTABLE_NAME)
+ CFBundleIconFile
+
+ CFBundleIdentifier
+ $(PRODUCT_BUNDLE_IDENTIFIER)
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundleName
+ $(PRODUCT_NAME)
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $(FLUTTER_BUILD_NAME)
+ CFBundleVersion
+ $(FLUTTER_BUILD_NUMBER)
+ LSMinimumSystemVersion
+ $(MACOSX_DEPLOYMENT_TARGET)
+ NSHumanReadableCopyright
+ $(PRODUCT_COPYRIGHT)
+ NSMainNibFile
+ MainMenu
+ NSPrincipalClass
+ NSApplication
+
+
diff --git a/packages/flutter_markdown/example/macos/Runner/MainFlutterWindow.swift b/packages/flutter_markdown/example/macos/Runner/MainFlutterWindow.swift
new file mode 100644
index 0000000000..2722837ec9
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/MainFlutterWindow.swift
@@ -0,0 +1,15 @@
+import Cocoa
+import FlutterMacOS
+
+class MainFlutterWindow: NSWindow {
+ override func awakeFromNib() {
+ let flutterViewController = FlutterViewController.init()
+ let windowFrame = self.frame
+ self.contentViewController = flutterViewController
+ self.setFrame(windowFrame, display: true)
+
+ RegisterGeneratedPlugins(registry: flutterViewController)
+
+ super.awakeFromNib()
+ }
+}
diff --git a/packages/flutter_markdown/example/macos/Runner/Release.entitlements b/packages/flutter_markdown/example/macos/Runner/Release.entitlements
new file mode 100644
index 0000000000..ee95ab7e58
--- /dev/null
+++ b/packages/flutter_markdown/example/macos/Runner/Release.entitlements
@@ -0,0 +1,10 @@
+
+
+
+
+ com.apple.security.app-sandbox
+
+ com.apple.security.network.client
+
+
+
diff --git a/packages/flutter_markdown/example/pubspec.yaml b/packages/flutter_markdown/example/pubspec.yaml
new file mode 100644
index 0000000000..5221a12bea
--- /dev/null
+++ b/packages/flutter_markdown/example/pubspec.yaml
@@ -0,0 +1,28 @@
+name: flutter_markdown_example
+description: Demonstrates how to use the flutter_markdown package.
+publish_to: "none"
+
+environment:
+ sdk: '>=2.12.0-0 <3.0.0'
+ flutter: ">=1.17.0"
+
+dependencies:
+ flutter:
+ sdk: flutter
+
+dev_dependencies:
+ flutter_markdown:
+ path: ../
+ flutter_test:
+ sdk: flutter
+
+flutter:
+ assets:
+ - assets/
+
+ fonts:
+ - family: 'Roboto Mono'
+ fonts:
+ - asset: fonts/RobotoMono-Regular.ttf
+
+ uses-material-design: true
diff --git a/packages/flutter_markdown/example/web/favicon.png b/packages/flutter_markdown/example/web/favicon.png
new file mode 100644
index 0000000000..8aaa46ac1a
Binary files /dev/null and b/packages/flutter_markdown/example/web/favicon.png differ
diff --git a/packages/flutter_markdown/example/web/icons/Icon-192.png b/packages/flutter_markdown/example/web/icons/Icon-192.png
new file mode 100644
index 0000000000..b749bfef07
Binary files /dev/null and b/packages/flutter_markdown/example/web/icons/Icon-192.png differ
diff --git a/packages/flutter_markdown/example/web/icons/Icon-512.png b/packages/flutter_markdown/example/web/icons/Icon-512.png
new file mode 100644
index 0000000000..88cfd48dff
Binary files /dev/null and b/packages/flutter_markdown/example/web/icons/Icon-512.png differ
diff --git a/packages/flutter_markdown/example/web/index.html b/packages/flutter_markdown/example/web/index.html
new file mode 100644
index 0000000000..e4e333b77c
--- /dev/null
+++ b/packages/flutter_markdown/example/web/index.html
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ flutter_markdown_example
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/web/manifest.json b/packages/flutter_markdown/example/web/manifest.json
new file mode 100644
index 0000000000..0b30ee999b
--- /dev/null
+++ b/packages/flutter_markdown/example/web/manifest.json
@@ -0,0 +1,23 @@
+{
+ "name": "flutter_markdown_example",
+ "short_name": "flutter_markdown_example",
+ "start_url": ".",
+ "display": "standalone",
+ "background_color": "#0175C2",
+ "theme_color": "#0175C2",
+ "description": "A new Flutter project.",
+ "orientation": "portrait-primary",
+ "prefer_related_applications": false,
+ "icons": [
+ {
+ "src": "icons/Icon-192.png",
+ "sizes": "192x192",
+ "type": "image/png"
+ },
+ {
+ "src": "icons/Icon-512.png",
+ "sizes": "512x512",
+ "type": "image/png"
+ }
+ ]
+}
diff --git a/packages/flutter_markdown/example/windows/.gitignore b/packages/flutter_markdown/example/windows/.gitignore
new file mode 100644
index 0000000000..d492d0d98c
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/.gitignore
@@ -0,0 +1,17 @@
+flutter/ephemeral/
+
+# Visual Studio user-specific files.
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# Visual Studio build-related files.
+x64/
+x86/
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!*.[Cc]ache/
diff --git a/packages/flutter_markdown/example/windows/CMakeLists.txt b/packages/flutter_markdown/example/windows/CMakeLists.txt
new file mode 100644
index 0000000000..52880babfc
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/CMakeLists.txt
@@ -0,0 +1,95 @@
+cmake_minimum_required(VERSION 3.15)
+project(flutter_markdown_example LANGUAGES CXX)
+
+set(BINARY_NAME "flutter_markdown_example")
+
+cmake_policy(SET CMP0063 NEW)
+
+set(CMAKE_INSTALL_RPATH "$ORIGIN/lib")
+
+# Configure build options.
+get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
+if(IS_MULTICONFIG)
+ set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release"
+ CACHE STRING "" FORCE)
+else()
+ if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+ set(CMAKE_BUILD_TYPE "Debug" CACHE
+ STRING "Flutter build mode" FORCE)
+ set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS
+ "Debug" "Profile" "Release")
+ endif()
+endif()
+
+set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}")
+set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}")
+set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}")
+set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}")
+
+# Use Unicode for all projects.
+add_definitions(-DUNICODE -D_UNICODE)
+
+# Compilation settings that should be applied to most targets.
+function(APPLY_STANDARD_SETTINGS TARGET)
+ target_compile_features(${TARGET} PUBLIC cxx_std_17)
+ target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100")
+ target_compile_options(${TARGET} PRIVATE /EHsc)
+ target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0")
+ target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>")
+endfunction()
+
+set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter")
+
+# Flutter library and tool build rules.
+add_subdirectory(${FLUTTER_MANAGED_DIR})
+
+# Application build
+add_subdirectory("runner")
+
+# Generated plugin build rules, which manage building the plugins and adding
+# them to the application.
+include(flutter/generated_plugins.cmake)
+
+
+# === Installation ===
+# Support files are copied into place next to the executable, so that it can
+# run in place. This is done instead of making a separate bundle (as on Linux)
+# so that building and running from within Visual Studio will work.
+set(BUILD_BUNDLE_DIR "$")
+# Make the "install" step default, as it's required to run.
+set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)
+if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)
+ set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE)
+endif()
+
+set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data")
+set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}")
+
+install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ COMPONENT Runtime)
+
+install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+
+if(PLUGIN_BUNDLED_LIBRARIES)
+ install(FILES "${PLUGIN_BUNDLED_LIBRARIES}"
+ DESTINATION "${INSTALL_BUNDLE_LIB_DIR}"
+ COMPONENT Runtime)
+endif()
+
+# Fully re-copy the assets directory on each build to avoid having stale files
+# from a previous install.
+set(FLUTTER_ASSET_DIR_NAME "flutter_assets")
+install(CODE "
+ file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\")
+ " COMPONENT Runtime)
+install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}"
+ DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime)
+
+# Install the AOT library on non-Debug builds only.
+install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}"
+ CONFIGURATIONS Profile;Release
+ COMPONENT Runtime)
diff --git a/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt b/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt
new file mode 100644
index 0000000000..744f08a938
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/flutter/CMakeLists.txt
@@ -0,0 +1,102 @@
+cmake_minimum_required(VERSION 3.15)
+
+set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral")
+
+# Configuration provided via flutter tool.
+include(${EPHEMERAL_DIR}/generated_config.cmake)
+
+# TODO: Move the rest of this into files in ephemeral. See
+# https://github.com/flutter/flutter/issues/57146.
+set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper")
+
+# === Flutter Library ===
+set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll")
+
+# Published to parent scope for install step.
+set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)
+set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE)
+set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE)
+set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE)
+
+list(APPEND FLUTTER_LIBRARY_HEADERS
+ "flutter_export.h"
+ "flutter_windows.h"
+ "flutter_messenger.h"
+ "flutter_plugin_registrar.h"
+)
+list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/")
+add_library(flutter INTERFACE)
+target_include_directories(flutter INTERFACE
+ "${EPHEMERAL_DIR}"
+)
+target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib")
+add_dependencies(flutter flutter_assemble)
+
+# === Wrapper ===
+list(APPEND CPP_WRAPPER_SOURCES_CORE
+ "core_implementations.cc"
+ "standard_codec.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_PLUGIN
+ "plugin_registrar.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/")
+list(APPEND CPP_WRAPPER_SOURCES_APP
+ "flutter_engine.cc"
+ "flutter_view_controller.cc"
+)
+list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/")
+
+# Wrapper sources needed for a plugin.
+add_library(flutter_wrapper_plugin STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+)
+apply_standard_settings(flutter_wrapper_plugin)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ POSITION_INDEPENDENT_CODE ON)
+set_target_properties(flutter_wrapper_plugin PROPERTIES
+ CXX_VISIBILITY_PRESET hidden)
+target_link_libraries(flutter_wrapper_plugin PUBLIC flutter)
+target_include_directories(flutter_wrapper_plugin PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_plugin flutter_assemble)
+
+# Wrapper sources needed for the runner.
+add_library(flutter_wrapper_app STATIC
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
+apply_standard_settings(flutter_wrapper_app)
+target_link_libraries(flutter_wrapper_app PUBLIC flutter)
+target_include_directories(flutter_wrapper_app PUBLIC
+ "${WRAPPER_ROOT}/include"
+)
+add_dependencies(flutter_wrapper_app flutter_assemble)
+
+# === Flutter tool backend ===
+# _phony_ is a non-existent file to force this command to run every time,
+# since currently there's no way to get a full input/output list from the
+# flutter tool.
+set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_")
+set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE)
+add_custom_command(
+ OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+ ${PHONY_OUTPUT}
+ COMMAND ${CMAKE_COMMAND} -E env
+ ${FLUTTER_TOOL_ENVIRONMENT}
+ "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat"
+ windows-x64 $
+ VERBATIM
+)
+add_custom_target(flutter_assemble DEPENDS
+ "${FLUTTER_LIBRARY}"
+ ${FLUTTER_LIBRARY_HEADERS}
+ ${CPP_WRAPPER_SOURCES_CORE}
+ ${CPP_WRAPPER_SOURCES_PLUGIN}
+ ${CPP_WRAPPER_SOURCES_APP}
+)
diff --git a/packages/flutter_markdown/example/windows/flutter/generated_plugin_registrant.cc b/packages/flutter_markdown/example/windows/flutter/generated_plugin_registrant.cc
new file mode 100644
index 0000000000..a6177ab0b7
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/flutter/generated_plugin_registrant.cc
@@ -0,0 +1,7 @@
+//
+// Generated file. Do not edit.
+//
+
+#include "generated_plugin_registrant.h"
+
+void RegisterPlugins(flutter::PluginRegistry* registry) {}
diff --git a/packages/flutter_markdown/example/windows/flutter/generated_plugin_registrant.h b/packages/flutter_markdown/example/windows/flutter/generated_plugin_registrant.h
new file mode 100644
index 0000000000..9846246b4d
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/flutter/generated_plugin_registrant.h
@@ -0,0 +1,13 @@
+//
+// Generated file. Do not edit.
+//
+
+#ifndef GENERATED_PLUGIN_REGISTRANT_
+#define GENERATED_PLUGIN_REGISTRANT_
+
+#include
+
+// Registers Flutter plugins.
+void RegisterPlugins(flutter::PluginRegistry* registry);
+
+#endif // GENERATED_PLUGIN_REGISTRANT_
diff --git a/packages/flutter_markdown/example/windows/flutter/generated_plugins.cmake b/packages/flutter_markdown/example/windows/flutter/generated_plugins.cmake
new file mode 100644
index 0000000000..4d10c25186
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/flutter/generated_plugins.cmake
@@ -0,0 +1,15 @@
+#
+# Generated file, do not edit.
+#
+
+list(APPEND FLUTTER_PLUGIN_LIST
+)
+
+set(PLUGIN_BUNDLED_LIBRARIES)
+
+foreach(plugin ${FLUTTER_PLUGIN_LIST})
+ add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})
+ target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES $)
+ list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})
+endforeach(plugin)
diff --git a/packages/flutter_markdown/example/windows/runner/CMakeLists.txt b/packages/flutter_markdown/example/windows/runner/CMakeLists.txt
new file mode 100644
index 0000000000..977e38b5d1
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/CMakeLists.txt
@@ -0,0 +1,18 @@
+cmake_minimum_required(VERSION 3.15)
+project(runner LANGUAGES CXX)
+
+add_executable(${BINARY_NAME} WIN32
+ "flutter_window.cpp"
+ "main.cpp"
+ "run_loop.cpp"
+ "utils.cpp"
+ "win32_window.cpp"
+ "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc"
+ "Runner.rc"
+ "runner.exe.manifest"
+)
+apply_standard_settings(${BINARY_NAME})
+target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX")
+target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)
+target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}")
+add_dependencies(${BINARY_NAME} flutter_assemble)
diff --git a/packages/flutter_markdown/example/windows/runner/Runner.rc b/packages/flutter_markdown/example/windows/runner/Runner.rc
new file mode 100644
index 0000000000..7b61799edc
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/Runner.rc
@@ -0,0 +1,121 @@
+// Microsoft Visual C++ generated resource script.
+//
+#pragma code_page(65001)
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (United States) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""winres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDI_APP_ICON ICON "resources\\app_icon.ico"
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+#ifdef FLUTTER_BUILD_NUMBER
+#define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER
+#else
+#define VERSION_AS_NUMBER 1,0,0
+#endif
+
+#ifdef FLUTTER_BUILD_NAME
+#define VERSION_AS_STRING #FLUTTER_BUILD_NAME
+#else
+#define VERSION_AS_STRING "1.0.0"
+#endif
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VERSION_AS_NUMBER
+ PRODUCTVERSION VERSION_AS_NUMBER
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+#ifdef _DEBUG
+ FILEFLAGS VS_FF_DEBUG
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS VOS__WINDOWS32
+ FILETYPE VFT_APP
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904e4"
+ BEGIN
+ VALUE "CompanyName", "io.flutter.packages" "\0"
+ VALUE "FileDescription", "A new Flutter project." "\0"
+ VALUE "FileVersion", VERSION_AS_STRING "\0"
+ VALUE "InternalName", "flutter_markdown_example" "\0"
+ VALUE "LegalCopyright", "Copyright (C) 2021 io.flutter.packages. All rights reserved." "\0"
+ VALUE "OriginalFilename", "flutter_markdown_example.exe" "\0"
+ VALUE "ProductName", "flutter_markdown_example" "\0"
+ VALUE "ProductVersion", VERSION_AS_STRING "\0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1252
+ END
+END
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
diff --git a/packages/flutter_markdown/example/windows/runner/flutter_window.cpp b/packages/flutter_markdown/example/windows/runner/flutter_window.cpp
new file mode 100644
index 0000000000..c422723045
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/flutter_window.cpp
@@ -0,0 +1,64 @@
+#include "flutter_window.h"
+
+#include
+
+#include "flutter/generated_plugin_registrant.h"
+
+FlutterWindow::FlutterWindow(RunLoop* run_loop,
+ const flutter::DartProject& project)
+ : run_loop_(run_loop), project_(project) {}
+
+FlutterWindow::~FlutterWindow() {}
+
+bool FlutterWindow::OnCreate() {
+ if (!Win32Window::OnCreate()) {
+ return false;
+ }
+
+ RECT frame = GetClientArea();
+
+ // The size here must match the window dimensions to avoid unnecessary surface
+ // creation / destruction in the startup path.
+ flutter_controller_ = std::make_unique(
+ frame.right - frame.left, frame.bottom - frame.top, project_);
+ // Ensure that basic setup of the controller was successful.
+ if (!flutter_controller_->engine() || !flutter_controller_->view()) {
+ return false;
+ }
+ RegisterPlugins(flutter_controller_->engine());
+ run_loop_->RegisterFlutterInstance(flutter_controller_->engine());
+ SetChildContent(flutter_controller_->view()->GetNativeWindow());
+ return true;
+}
+
+void FlutterWindow::OnDestroy() {
+ if (flutter_controller_) {
+ run_loop_->UnregisterFlutterInstance(flutter_controller_->engine());
+ flutter_controller_ = nullptr;
+ }
+
+ Win32Window::OnDestroy();
+}
+
+LRESULT
+FlutterWindow::MessageHandler(HWND hwnd, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ // Give Flutter, including plugins, an opporutunity to handle window messages.
+ if (flutter_controller_) {
+ std::optional result =
+ flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,
+ lparam);
+ if (result) {
+ return *result;
+ }
+ }
+
+ switch (message) {
+ case WM_FONTCHANGE:
+ flutter_controller_->engine()->ReloadSystemFonts();
+ break;
+ }
+
+ return Win32Window::MessageHandler(hwnd, message, wparam, lparam);
+}
diff --git a/packages/flutter_markdown/example/windows/runner/flutter_window.h b/packages/flutter_markdown/example/windows/runner/flutter_window.h
new file mode 100644
index 0000000000..b663ddd501
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/flutter_window.h
@@ -0,0 +1,39 @@
+#ifndef RUNNER_FLUTTER_WINDOW_H_
+#define RUNNER_FLUTTER_WINDOW_H_
+
+#include
+#include
+
+#include
+
+#include "run_loop.h"
+#include "win32_window.h"
+
+// A window that does nothing but host a Flutter view.
+class FlutterWindow : public Win32Window {
+ public:
+ // Creates a new FlutterWindow driven by the |run_loop|, hosting a
+ // Flutter view running |project|.
+ explicit FlutterWindow(RunLoop* run_loop,
+ const flutter::DartProject& project);
+ virtual ~FlutterWindow();
+
+ protected:
+ // Win32Window:
+ bool OnCreate() override;
+ void OnDestroy() override;
+ LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept override;
+
+ private:
+ // The run loop driving events for this window.
+ RunLoop* run_loop_;
+
+ // The project to run.
+ flutter::DartProject project_;
+
+ // The Flutter instance hosted by this window.
+ std::unique_ptr flutter_controller_;
+};
+
+#endif // RUNNER_FLUTTER_WINDOW_H_
diff --git a/packages/flutter_markdown/example/windows/runner/main.cpp b/packages/flutter_markdown/example/windows/runner/main.cpp
new file mode 100644
index 0000000000..19b45edf35
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/main.cpp
@@ -0,0 +1,41 @@
+#include
+#include
+#include
+
+#include "flutter_window.h"
+#include "run_loop.h"
+#include "utils.h"
+
+int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
+ _In_ wchar_t *command_line, _In_ int show_command) {
+ // Attach to console when present (e.g., 'flutter run') or create a
+ // new console when running with a debugger.
+ if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {
+ CreateAndAttachConsole();
+ }
+
+ // Initialize COM, so that it is available for use in the library and/or
+ // plugins.
+ ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
+
+ RunLoop run_loop;
+
+ flutter::DartProject project(L"data");
+
+ std::vector command_line_arguments = GetCommandLineArguments();
+
+ project.set_dart_entrypoint_arguments(std::move(command_line_arguments));
+
+ FlutterWindow window(&run_loop, project);
+ Win32Window::Point origin(10, 10);
+ Win32Window::Size size(1280, 720);
+ if (!window.CreateAndShow(L"flutter_markdown_example", origin, size)) {
+ return EXIT_FAILURE;
+ }
+ window.SetQuitOnClose(true);
+
+ run_loop.Run();
+
+ ::CoUninitialize();
+ return EXIT_SUCCESS;
+}
diff --git a/packages/flutter_markdown/example/windows/runner/resource.h b/packages/flutter_markdown/example/windows/runner/resource.h
new file mode 100644
index 0000000000..d5d958dc42
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/resource.h
@@ -0,0 +1,16 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Runner.rc
+//
+#define IDI_APP_ICON 101
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 102
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif
diff --git a/packages/flutter_markdown/example/windows/runner/resources/app_icon.ico b/packages/flutter_markdown/example/windows/runner/resources/app_icon.ico
new file mode 100644
index 0000000000..c04e20caf6
Binary files /dev/null and b/packages/flutter_markdown/example/windows/runner/resources/app_icon.ico differ
diff --git a/packages/flutter_markdown/example/windows/runner/run_loop.cpp b/packages/flutter_markdown/example/windows/runner/run_loop.cpp
new file mode 100644
index 0000000000..2d6636ab6b
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/run_loop.cpp
@@ -0,0 +1,66 @@
+#include "run_loop.h"
+
+#include
+
+#include
+
+RunLoop::RunLoop() {}
+
+RunLoop::~RunLoop() {}
+
+void RunLoop::Run() {
+ bool keep_running = true;
+ TimePoint next_flutter_event_time = TimePoint::clock::now();
+ while (keep_running) {
+ std::chrono::nanoseconds wait_duration =
+ std::max(std::chrono::nanoseconds(0),
+ next_flutter_event_time - TimePoint::clock::now());
+ ::MsgWaitForMultipleObjects(
+ 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000),
+ QS_ALLINPUT);
+ bool processed_events = false;
+ MSG message;
+ // All pending Windows messages must be processed; MsgWaitForMultipleObjects
+ // won't return again for items left in the queue after PeekMessage.
+ while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) {
+ processed_events = true;
+ if (message.message == WM_QUIT) {
+ keep_running = false;
+ break;
+ }
+ ::TranslateMessage(&message);
+ ::DispatchMessage(&message);
+ // Allow Flutter to process messages each time a Windows message is
+ // processed, to prevent starvation.
+ next_flutter_event_time =
+ std::min(next_flutter_event_time, ProcessFlutterMessages());
+ }
+ // If the PeekMessage loop didn't run, process Flutter messages.
+ if (!processed_events) {
+ next_flutter_event_time =
+ std::min(next_flutter_event_time, ProcessFlutterMessages());
+ }
+ }
+}
+
+void RunLoop::RegisterFlutterInstance(
+ flutter::FlutterEngine* flutter_instance) {
+ flutter_instances_.insert(flutter_instance);
+}
+
+void RunLoop::UnregisterFlutterInstance(
+ flutter::FlutterEngine* flutter_instance) {
+ flutter_instances_.erase(flutter_instance);
+}
+
+RunLoop::TimePoint RunLoop::ProcessFlutterMessages() {
+ TimePoint next_event_time = TimePoint::max();
+ for (auto instance : flutter_instances_) {
+ std::chrono::nanoseconds wait_duration = instance->ProcessMessages();
+ if (wait_duration != std::chrono::nanoseconds::max()) {
+ next_event_time =
+ std::min(next_event_time, TimePoint::clock::now() + wait_duration);
+ }
+ }
+ return next_event_time;
+}
diff --git a/packages/flutter_markdown/example/windows/runner/run_loop.h b/packages/flutter_markdown/example/windows/runner/run_loop.h
new file mode 100644
index 0000000000..5f2c4a9ad7
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/run_loop.h
@@ -0,0 +1,38 @@
+#ifndef RUNNER_RUN_LOOP_H_
+#define RUNNER_RUN_LOOP_H_
+
+#include
+
+#include
+#include
+
+// A runloop that will service events for Flutter instances as well
+// as native messages.
+class RunLoop {
+ public:
+ RunLoop();
+ ~RunLoop();
+
+ // Prevent copying
+ RunLoop(RunLoop const&) = delete;
+ RunLoop& operator=(RunLoop const&) = delete;
+
+ // Runs the run loop until the application quits.
+ void Run();
+
+ // Registers the given Flutter instance for event servicing.
+ void RegisterFlutterInstance(flutter::FlutterEngine* flutter_instance);
+
+ // Unregisters the given Flutter instance from event servicing.
+ void UnregisterFlutterInstance(flutter::FlutterEngine* flutter_instance);
+
+ private:
+ using TimePoint = std::chrono::steady_clock::time_point;
+
+ // Processes all currently pending messages for registered Flutter instances.
+ TimePoint ProcessFlutterMessages();
+
+ std::set flutter_instances_;
+};
+
+#endif // RUNNER_RUN_LOOP_H_
diff --git a/packages/flutter_markdown/example/windows/runner/runner.exe.manifest b/packages/flutter_markdown/example/windows/runner/runner.exe.manifest
new file mode 100644
index 0000000000..c977c4a425
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/runner.exe.manifest
@@ -0,0 +1,20 @@
+
+
+
+
+ PerMonitorV2
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/flutter_markdown/example/windows/runner/utils.cpp b/packages/flutter_markdown/example/windows/runner/utils.cpp
new file mode 100644
index 0000000000..afa363b236
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/utils.cpp
@@ -0,0 +1,63 @@
+#include "utils.h"
+
+#include
+#include
+#include
+#include
+
+#include
+
+void CreateAndAttachConsole() {
+ if (::AllocConsole()) {
+ FILE* unused;
+ if (freopen_s(&unused, "CONOUT$", "w", stdout)) {
+ _dup2(_fileno(stdout), 1);
+ }
+ if (freopen_s(&unused, "CONOUT$", "w", stderr)) {
+ _dup2(_fileno(stdout), 2);
+ }
+ std::ios::sync_with_stdio();
+ FlutterDesktopResyncOutputStreams();
+ }
+}
+
+std::vector GetCommandLineArguments() {
+ // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.
+ int argc;
+ wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);
+ if (argv == nullptr) {
+ return std::vector();
+ }
+
+ std::vector command_line_arguments;
+
+ // Skip the first argument as it's the binary name.
+ for (int i = 1; i < argc; i++) {
+ command_line_arguments.push_back(Utf8FromUtf16(argv[i]));
+ }
+
+ ::LocalFree(argv);
+
+ return command_line_arguments;
+}
+
+std::string Utf8FromUtf16(const wchar_t* utf16_string) {
+ if (utf16_string == nullptr) {
+ return std::string();
+ }
+ int target_length =
+ ::WideCharToMultiByte(CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1,
+ nullptr, 0, nullptr, nullptr);
+ if (target_length == 0) {
+ return std::string();
+ }
+ std::string utf8_string;
+ utf8_string.resize(target_length);
+ int converted_length = ::WideCharToMultiByte(
+ CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, -1, utf8_string.data(),
+ target_length, nullptr, nullptr);
+ if (converted_length == 0) {
+ return std::string();
+ }
+ return utf8_string;
+}
diff --git a/packages/flutter_markdown/example/windows/runner/utils.h b/packages/flutter_markdown/example/windows/runner/utils.h
new file mode 100644
index 0000000000..3879d54755
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/utils.h
@@ -0,0 +1,19 @@
+#ifndef RUNNER_UTILS_H_
+#define RUNNER_UTILS_H_
+
+#include
+#include
+
+// Creates a console for the process, and redirects stdout and stderr to
+// it for both the runner and the Flutter library.
+void CreateAndAttachConsole();
+
+// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string
+// encoded in UTF-8. Returns an empty std::string on failure.
+std::string Utf8FromUtf16(const wchar_t* utf16_string);
+
+// Gets the command line arguments passed in as a std::vector,
+// encoded in UTF-8. Returns an empty std::vector on failure.
+std::vector GetCommandLineArguments();
+
+#endif // RUNNER_UTILS_H_
diff --git a/packages/flutter_markdown/example/windows/runner/win32_window.cpp b/packages/flutter_markdown/example/windows/runner/win32_window.cpp
new file mode 100644
index 0000000000..44091b3f3c
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/win32_window.cpp
@@ -0,0 +1,237 @@
+#include "win32_window.h"
+
+#include
+
+#include "resource.h"
+
+namespace {
+
+constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW";
+
+// The number of Win32Window objects that currently exist.
+static int g_active_window_count = 0;
+
+using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);
+
+// Scale helper to convert logical scaler values to physical using passed in
+// scale factor
+int Scale(int source, double scale_factor) {
+ return static_cast(source * scale_factor);
+}
+
+// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.
+// This API is only needed for PerMonitor V1 awareness mode.
+void EnableFullDpiSupportIfAvailable(HWND hwnd) {
+ HMODULE user32_module = LoadLibraryA("User32.dll");
+ if (!user32_module) {
+ return;
+ }
+ auto enable_non_client_dpi_scaling =
+ reinterpret_cast(
+ GetProcAddress(user32_module, "EnableNonClientDpiScaling"));
+ if (enable_non_client_dpi_scaling != nullptr) {
+ enable_non_client_dpi_scaling(hwnd);
+ FreeLibrary(user32_module);
+ }
+}
+
+} // namespace
+
+// Manages the Win32Window's window class registration.
+class WindowClassRegistrar {
+ public:
+ ~WindowClassRegistrar() = default;
+
+ // Returns the singleton registar instance.
+ static WindowClassRegistrar* GetInstance() {
+ if (!instance_) {
+ instance_ = new WindowClassRegistrar();
+ }
+ return instance_;
+ }
+
+ // Returns the name of the window class, registering the class if it hasn't
+ // previously been registered.
+ const wchar_t* GetWindowClass();
+
+ // Unregisters the window class. Should only be called if there are no
+ // instances of the window.
+ void UnregisterWindowClass();
+
+ private:
+ WindowClassRegistrar() = default;
+
+ static WindowClassRegistrar* instance_;
+
+ bool class_registered_ = false;
+};
+
+WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;
+
+const wchar_t* WindowClassRegistrar::GetWindowClass() {
+ if (!class_registered_) {
+ WNDCLASS window_class{};
+ window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);
+ window_class.lpszClassName = kWindowClassName;
+ window_class.style = CS_HREDRAW | CS_VREDRAW;
+ window_class.cbClsExtra = 0;
+ window_class.cbWndExtra = 0;
+ window_class.hInstance = GetModuleHandle(nullptr);
+ window_class.hIcon =
+ LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));
+ window_class.hbrBackground = 0;
+ window_class.lpszMenuName = nullptr;
+ window_class.lpfnWndProc = Win32Window::WndProc;
+ RegisterClass(&window_class);
+ class_registered_ = true;
+ }
+ return kWindowClassName;
+}
+
+void WindowClassRegistrar::UnregisterWindowClass() {
+ UnregisterClass(kWindowClassName, nullptr);
+ class_registered_ = false;
+}
+
+Win32Window::Win32Window() { ++g_active_window_count; }
+
+Win32Window::~Win32Window() {
+ --g_active_window_count;
+ Destroy();
+}
+
+bool Win32Window::CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size) {
+ Destroy();
+
+ const wchar_t* window_class =
+ WindowClassRegistrar::GetInstance()->GetWindowClass();
+
+ const POINT target_point = {static_cast(origin.x),
+ static_cast(origin.y)};
+ HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);
+ UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);
+ double scale_factor = dpi / 96.0;
+
+ HWND window = CreateWindow(
+ window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,
+ Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),
+ Scale(size.width, scale_factor), Scale(size.height, scale_factor),
+ nullptr, nullptr, GetModuleHandle(nullptr), this);
+
+ if (!window) {
+ return false;
+ }
+
+ return OnCreate();
+}
+
+// static
+LRESULT CALLBACK Win32Window::WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ if (message == WM_NCCREATE) {
+ auto window_struct = reinterpret_cast(lparam);
+ SetWindowLongPtr(window, GWLP_USERDATA,
+ reinterpret_cast(window_struct->lpCreateParams));
+
+ auto that = static_cast(window_struct->lpCreateParams);
+ EnableFullDpiSupportIfAvailable(window);
+ that->window_handle_ = window;
+ } else if (Win32Window* that = GetThisFromHandle(window)) {
+ return that->MessageHandler(window, message, wparam, lparam);
+ }
+
+ return DefWindowProc(window, message, wparam, lparam);
+}
+
+LRESULT
+Win32Window::MessageHandler(HWND hwnd, UINT const message, WPARAM const wparam,
+ LPARAM const lparam) noexcept {
+ switch (message) {
+ case WM_DESTROY:
+ window_handle_ = nullptr;
+ Destroy();
+ if (quit_on_close_) {
+ PostQuitMessage(0);
+ }
+ return 0;
+
+ case WM_DPICHANGED: {
+ auto newRectSize = reinterpret_cast(lparam);
+ LONG newWidth = newRectSize->right - newRectSize->left;
+ LONG newHeight = newRectSize->bottom - newRectSize->top;
+
+ SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,
+ newHeight, SWP_NOZORDER | SWP_NOACTIVATE);
+
+ return 0;
+ }
+ case WM_SIZE: {
+ RECT rect = GetClientArea();
+ if (child_content_ != nullptr) {
+ // Size and position the child window.
+ MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,
+ rect.bottom - rect.top, TRUE);
+ }
+ return 0;
+ }
+
+ case WM_ACTIVATE:
+ if (child_content_ != nullptr) {
+ SetFocus(child_content_);
+ }
+ return 0;
+ }
+
+ return DefWindowProc(window_handle_, message, wparam, lparam);
+}
+
+void Win32Window::Destroy() {
+ OnDestroy();
+
+ if (window_handle_) {
+ DestroyWindow(window_handle_);
+ window_handle_ = nullptr;
+ }
+ if (g_active_window_count == 0) {
+ WindowClassRegistrar::GetInstance()->UnregisterWindowClass();
+ }
+}
+
+Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {
+ return reinterpret_cast(
+ GetWindowLongPtr(window, GWLP_USERDATA));
+}
+
+void Win32Window::SetChildContent(HWND content) {
+ child_content_ = content;
+ SetParent(content, window_handle_);
+ RECT frame = GetClientArea();
+
+ MoveWindow(content, frame.left, frame.top, frame.right - frame.left,
+ frame.bottom - frame.top, true);
+
+ SetFocus(child_content_);
+}
+
+RECT Win32Window::GetClientArea() {
+ RECT frame;
+ GetClientRect(window_handle_, &frame);
+ return frame;
+}
+
+HWND Win32Window::GetHandle() { return window_handle_; }
+
+void Win32Window::SetQuitOnClose(bool quit_on_close) {
+ quit_on_close_ = quit_on_close;
+}
+
+bool Win32Window::OnCreate() {
+ // No-op; provided for subclasses.
+ return true;
+}
+
+void Win32Window::OnDestroy() {
+ // No-op; provided for subclasses.
+}
diff --git a/packages/flutter_markdown/example/windows/runner/win32_window.h b/packages/flutter_markdown/example/windows/runner/win32_window.h
new file mode 100644
index 0000000000..4ae64a12b4
--- /dev/null
+++ b/packages/flutter_markdown/example/windows/runner/win32_window.h
@@ -0,0 +1,95 @@
+#ifndef RUNNER_WIN32_WINDOW_H_
+#define RUNNER_WIN32_WINDOW_H_
+
+#include
+
+#include
+#include
+#include
+
+// A class abstraction for a high DPI-aware Win32 Window. Intended to be
+// inherited from by classes that wish to specialize with custom
+// rendering and input handling
+class Win32Window {
+ public:
+ struct Point {
+ unsigned int x;
+ unsigned int y;
+ Point(unsigned int x, unsigned int y) : x(x), y(y) {}
+ };
+
+ struct Size {
+ unsigned int width;
+ unsigned int height;
+ Size(unsigned int width, unsigned int height)
+ : width(width), height(height) {}
+ };
+
+ Win32Window();
+ virtual ~Win32Window();
+
+ // Creates and shows a win32 window with |title| and position and size using
+ // |origin| and |size|. New windows are created on the default monitor. Window
+ // sizes are specified to the OS in physical pixels, hence to ensure a
+ // consistent size to will treat the width height passed in to this function
+ // as logical pixels and scale to appropriate for the default monitor. Returns
+ // true if the window was created successfully.
+ bool CreateAndShow(const std::wstring& title, const Point& origin,
+ const Size& size);
+
+ // Release OS resources associated with window.
+ void Destroy();
+
+ // Inserts |content| into the window tree.
+ void SetChildContent(HWND content);
+
+ // Returns the backing Window handle to enable clients to set icon and other
+ // window properties. Returns nullptr if the window has been destroyed.
+ HWND GetHandle();
+
+ // If true, closing this window will quit the application.
+ void SetQuitOnClose(bool quit_on_close);
+
+ // Return a RECT representing the bounds of the current client area.
+ RECT GetClientArea();
+
+ protected:
+ // Processes and route salient window messages for mouse handling,
+ // size change and DPI. Delegates handling of these to member overloads that
+ // inheriting classes can handle.
+ virtual LRESULT MessageHandler(HWND window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Called when CreateAndShow is called, allowing subclass window-related
+ // setup. Subclasses should return false if setup fails.
+ virtual bool OnCreate();
+
+ // Called when Destroy is called.
+ virtual void OnDestroy();
+
+ private:
+ friend class WindowClassRegistrar;
+
+ // OS callback called by message pump. Handles the WM_NCCREATE message which
+ // is passed when the non-client area is being created and enables automatic
+ // non-client DPI scaling so that the non-client area automatically
+ // responsponds to changes in DPI. All other messages are handled by
+ // MessageHandler.
+ static LRESULT CALLBACK WndProc(HWND const window, UINT const message,
+ WPARAM const wparam,
+ LPARAM const lparam) noexcept;
+
+ // Retrieves a class instance pointer for |window|
+ static Win32Window* GetThisFromHandle(HWND const window) noexcept;
+
+ bool quit_on_close_ = false;
+
+ // window handle for top level window.
+ HWND window_handle_ = nullptr;
+
+ // window handle for hosted content.
+ HWND child_content_ = nullptr;
+};
+
+#endif // RUNNER_WIN32_WINDOW_H_
diff --git a/packages/flutter_markdown/flutter_markdown.iml b/packages/flutter_markdown/flutter_markdown.iml
new file mode 100644
index 0000000000..4f0181093d
--- /dev/null
+++ b/packages/flutter_markdown/flutter_markdown.iml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/flutter_markdown/lib/flutter_markdown.dart b/packages/flutter_markdown/lib/flutter_markdown.dart
new file mode 100644
index 0000000000..8d7ed6ea08
--- /dev/null
+++ b/packages/flutter_markdown/lib/flutter_markdown.dart
@@ -0,0 +1,10 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/// A library to render markdown formatted text.
+library flutter_markdown;
+
+export 'src/builder.dart';
+export 'src/style_sheet.dart';
+export 'src/widget.dart';
diff --git a/packages/flutter_markdown/lib/src/_functions_io.dart b/packages/flutter_markdown/lib/src/_functions_io.dart
new file mode 100644
index 0000000000..2fbf2145b6
--- /dev/null
+++ b/packages/flutter_markdown/lib/src/_functions_io.dart
@@ -0,0 +1,85 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:io';
+
+import 'package:flutter/cupertino.dart' show CupertinoTheme;
+import 'package:flutter/material.dart' show Theme;
+import 'package:flutter/widgets.dart';
+
+import 'style_sheet.dart';
+import 'widget.dart';
+
+/// Type for a function that creates image widgets.
+typedef ImageBuilder = Widget Function(
+ Uri uri, String? imageDirectory, double? width, double? height);
+
+/// A default image builder handling http/https, resource, and file URLs.
+// ignore: prefer_function_declarations_over_variables
+final ImageBuilder kDefaultImageBuilder = (
+ Uri uri,
+ String? imageDirectory,
+ double? width,
+ double? height,
+) {
+ if (uri.scheme == 'http' || uri.scheme == 'https') {
+ return Image.network(uri.toString(), width: width, height: height);
+ } else if (uri.scheme == 'data') {
+ return _handleDataSchemeUri(uri, width, height);
+ } else if (uri.scheme == 'resource') {
+ return Image.asset(uri.path, width: width, height: height);
+ } else {
+ final Uri fileUri = imageDirectory != null
+ ? Uri.parse(imageDirectory + uri.toString())
+ : uri;
+ if (fileUri.scheme == 'http' || fileUri.scheme == 'https') {
+ return Image.network(fileUri.toString(), width: width, height: height);
+ } else {
+ return Image.file(File.fromUri(fileUri), width: width, height: height);
+ }
+ }
+};
+
+/// A default style sheet generator.
+final MarkdownStyleSheet Function(BuildContext, MarkdownStyleSheetBaseTheme?)
+// ignore: prefer_function_declarations_over_variables
+ kFallbackStyle = (
+ BuildContext context,
+ MarkdownStyleSheetBaseTheme? baseTheme,
+) {
+ MarkdownStyleSheet result;
+ switch (baseTheme) {
+ case MarkdownStyleSheetBaseTheme.platform:
+ result = (Platform.isIOS || Platform.isMacOS)
+ ? MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context))
+ : MarkdownStyleSheet.fromTheme(Theme.of(context));
+ break;
+ case MarkdownStyleSheetBaseTheme.cupertino:
+ result =
+ MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context));
+ break;
+ case MarkdownStyleSheetBaseTheme.material:
+ default:
+ result = MarkdownStyleSheet.fromTheme(Theme.of(context));
+ }
+
+ return result.copyWith(
+ textScaleFactor: MediaQuery.textScaleFactorOf(context),
+ );
+};
+
+Widget _handleDataSchemeUri(
+ Uri uri, final double? width, final double? height) {
+ final String mimeType = uri.data!.mimeType;
+ if (mimeType.startsWith('image/')) {
+ return Image.memory(
+ uri.data!.contentAsBytes(),
+ width: width,
+ height: height,
+ );
+ } else if (mimeType.startsWith('text/')) {
+ return Text(uri.data!.contentAsString());
+ }
+ return const SizedBox();
+}
diff --git a/packages/flutter_markdown/lib/src/_functions_web.dart b/packages/flutter_markdown/lib/src/_functions_web.dart
new file mode 100644
index 0000000000..018d71e7c4
--- /dev/null
+++ b/packages/flutter_markdown/lib/src/_functions_web.dart
@@ -0,0 +1,88 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'dart:html'; // ignore: avoid_web_libraries_in_flutter
+
+import 'package:flutter/cupertino.dart' show CupertinoTheme;
+import 'package:flutter/material.dart' show Theme;
+import 'package:flutter/widgets.dart';
+import 'package:path/path.dart' as p;
+
+import 'style_sheet.dart';
+import 'widget.dart';
+
+/// Type for a function that creates image widgets.
+typedef ImageBuilder = Widget Function(
+ Uri uri, String? imageDirectory, double? width, double? height);
+
+/// A default image builder handling http/https, resource, data, and file URLs.
+// ignore: prefer_function_declarations_over_variables
+final ImageBuilder kDefaultImageBuilder = (
+ Uri uri,
+ String? imageDirectory,
+ double? width,
+ double? height,
+) {
+ if (uri.scheme == 'http' || uri.scheme == 'https') {
+ return Image.network(uri.toString(), width: width, height: height);
+ } else if (uri.scheme == 'data') {
+ return _handleDataSchemeUri(uri, width, height);
+ } else if (uri.scheme == 'resource') {
+ return Image.asset(uri.path, width: width, height: height);
+ } else {
+ final Uri fileUri = imageDirectory != null
+ ? Uri.parse(p.join(imageDirectory, uri.toString()))
+ : uri;
+ if (fileUri.scheme == 'http' || fileUri.scheme == 'https') {
+ return Image.network(fileUri.toString(), width: width, height: height);
+ } else {
+ final String src = p.join(p.current, fileUri.toString());
+ return Image.network(src, width: width, height: height);
+ }
+ }
+};
+
+/// A default style sheet generator.
+final MarkdownStyleSheet Function(BuildContext, MarkdownStyleSheetBaseTheme?)
+// ignore: prefer_function_declarations_over_variables
+ kFallbackStyle = (
+ BuildContext context,
+ MarkdownStyleSheetBaseTheme? baseTheme,
+) {
+ MarkdownStyleSheet result;
+ switch (baseTheme) {
+ case MarkdownStyleSheetBaseTheme.platform:
+ final String userAgent = window.navigator.userAgent;
+ result = userAgent.contains('Mac OS X')
+ ? MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context))
+ : MarkdownStyleSheet.fromTheme(Theme.of(context));
+ break;
+ case MarkdownStyleSheetBaseTheme.cupertino:
+ result =
+ MarkdownStyleSheet.fromCupertinoTheme(CupertinoTheme.of(context));
+ break;
+ case MarkdownStyleSheetBaseTheme.material:
+ default:
+ result = MarkdownStyleSheet.fromTheme(Theme.of(context));
+ }
+
+ return result.copyWith(
+ textScaleFactor: MediaQuery.textScaleFactorOf(context),
+ );
+};
+
+Widget _handleDataSchemeUri(
+ Uri uri, final double? width, final double? height) {
+ final String mimeType = uri.data!.mimeType;
+ if (mimeType.startsWith('image/')) {
+ return Image.memory(
+ uri.data!.contentAsBytes(),
+ width: width,
+ height: height,
+ );
+ } else if (mimeType.startsWith('text/')) {
+ return Text(uri.data!.contentAsString());
+ }
+ return const SizedBox();
+}
diff --git a/packages/flutter_markdown/lib/src/builder.dart b/packages/flutter_markdown/lib/src/builder.dart
new file mode 100644
index 0000000000..c19d577bc8
--- /dev/null
+++ b/packages/flutter_markdown/lib/src/builder.dart
@@ -0,0 +1,761 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/gestures.dart';
+import 'package:flutter/material.dart';
+import 'package:markdown/markdown.dart' as md;
+
+import '_functions_io.dart' if (dart.library.html) '_functions_web.dart';
+import 'style_sheet.dart';
+import 'widget.dart';
+
+const List _kBlockTags = [
+ 'p',
+ 'h1',
+ 'h2',
+ 'h3',
+ 'h4',
+ 'h5',
+ 'h6',
+ 'li',
+ 'blockquote',
+ 'pre',
+ 'ol',
+ 'ul',
+ 'hr',
+ 'table',
+ 'thead',
+ 'tbody',
+ 'tr'
+];
+
+const List _kListTags = ['ul', 'ol'];
+
+bool _isBlockTag(String? tag) => _kBlockTags.contains(tag);
+
+bool _isListTag(String tag) => _kListTags.contains(tag);
+
+class _BlockElement {
+ _BlockElement(this.tag);
+
+ final String? tag;
+ final List children = [];
+
+ int nextListIndex = 0;
+}
+
+class _TableElement {
+ final List rows = [];
+}
+
+/// A collection of widgets that should be placed adjacent to (inline with)
+/// other inline elements in the same parent block.
+///
+/// Inline elements can be textual (a/em/strong) represented by [RichText]
+/// widgets or images (img) represented by [Image.network] widgets.
+///
+/// Inline elements can be nested within other inline elements, inheriting their
+/// parent's style along with the style of the block they are in.
+///
+/// When laying out inline widgets, first, any adjacent RichText widgets are
+/// merged, then, all inline widgets are enclosed in a parent [Wrap] widget.
+class _InlineElement {
+ _InlineElement(this.tag, {this.style});
+
+ final String? tag;
+
+ /// Created by merging the style defined for this element's [tag] in the
+ /// delegate's [MarkdownStyleSheet] with the style of its parent.
+ final TextStyle? style;
+
+ final List children = [];
+}
+
+/// A delegate used by [MarkdownBuilder] to control the widgets it creates.
+abstract class MarkdownBuilderDelegate {
+ /// Returns a gesture recognizer to use for an `a` element with the given
+ /// text, `href` attribute, and title.
+ GestureRecognizer createLink(String text, String? href, String title);
+
+ /// Returns formatted text to use to display the given contents of a `pre`
+ /// element.
+ ///
+ /// The `styleSheet` is the value of [MarkdownBuilder.styleSheet].
+ TextSpan formatText(MarkdownStyleSheet styleSheet, String code);
+}
+
+/// Builds a [Widget] tree from parsed Markdown.
+///
+/// See also:
+///
+/// * [Markdown], which is a widget that parses and displays Markdown.
+class MarkdownBuilder implements md.NodeVisitor {
+ /// Creates an object that builds a [Widget] tree from parsed Markdown.
+ MarkdownBuilder({
+ required this.delegate,
+ required this.selectable,
+ required this.styleSheet,
+ required this.imageDirectory,
+ required this.imageBuilder,
+ required this.checkboxBuilder,
+ required this.bulletBuilder,
+ required this.builders,
+ required this.listItemCrossAxisAlignment,
+ this.fitContent = false,
+ this.onTapText,
+ });
+
+ /// A delegate that controls how link and `pre` elements behave.
+ final MarkdownBuilderDelegate delegate;
+
+ /// If true, the text is selectable.
+ ///
+ /// Defaults to false.
+ final bool selectable;
+
+ /// Defines which [TextStyle] objects to use for each type of element.
+ final MarkdownStyleSheet styleSheet;
+
+ /// The base directory holding images referenced by Img tags with local or network file paths.
+ final String? imageDirectory;
+
+ /// Call when build an image widget.
+ final MarkdownImageBuilder? imageBuilder;
+
+ /// Call when build a checkbox widget.
+ final MarkdownCheckboxBuilder? checkboxBuilder;
+
+ /// Called when building a custom bullet.
+ final MarkdownBulletBuilder? bulletBuilder;
+
+ /// Call when build a custom widget.
+ final Map builders;
+
+ /// Whether to allow the widget to fit the child content.
+ final bool fitContent;
+
+ /// Controls the cross axis alignment for the bullet and list item content
+ /// in lists.
+ ///
+ /// Defaults to [MarkdownListItemCrossAxisAlignment.baseline], which
+ /// does not allow for intrinsic height measurements.
+ final MarkdownListItemCrossAxisAlignment listItemCrossAxisAlignment;
+
+ /// Default tap handler used when [selectable] is set to true
+ final VoidCallback? onTapText;
+
+ final List _listIndents = [];
+ final List<_BlockElement> _blocks = <_BlockElement>[];
+ final List<_TableElement> _tables = <_TableElement>[];
+ final List<_InlineElement> _inlines = <_InlineElement>[];
+ final List _linkHandlers = [];
+ String? _currentBlockTag;
+ String? _lastTag;
+ bool _isInBlockquote = false;
+
+ /// Returns widgets that display the given Markdown nodes.
+ ///
+ /// The returned widgets are typically used as children in a [ListView].
+ List build(List nodes) {
+ _listIndents.clear();
+ _blocks.clear();
+ _tables.clear();
+ _inlines.clear();
+ _linkHandlers.clear();
+ _isInBlockquote = false;
+
+ _blocks.add(_BlockElement(null));
+
+ for (final md.Node node in nodes) {
+ assert(_blocks.length == 1);
+ node.accept(this);
+ }
+
+ assert(_tables.isEmpty);
+ assert(_inlines.isEmpty);
+ assert(!_isInBlockquote);
+ return _blocks.single.children;
+ }
+
+ @override
+ bool visitElementBefore(md.Element element) {
+ final String tag = element.tag;
+ _currentBlockTag ??= tag;
+
+ if (builders.containsKey(tag)) {
+ builders[tag]!.visitElementBefore(element);
+ }
+
+ int? start;
+ if (_isBlockTag(tag)) {
+ _addAnonymousBlockIfNeeded();
+ if (_isListTag(tag)) {
+ _listIndents.add(tag);
+ if (element.attributes['start'] != null)
+ start = int.parse(element.attributes['start']!) - 1;
+ } else if (tag == 'blockquote') {
+ _isInBlockquote = true;
+ } else if (tag == 'table') {
+ _tables.add(_TableElement());
+ } else if (tag == 'tr') {
+ final int length = _tables.single.rows.length;
+ BoxDecoration? decoration =
+ styleSheet.tableCellsDecoration as BoxDecoration?;
+ if (length == 0 || length.isOdd) {
+ decoration = null;
+ }
+ _tables.single.rows.add(TableRow(
+ decoration: decoration,
+ // TODO(stuartmorgan): This should be fixed, not suppressed; enabling
+ // this lint warning exposed that the builder is modifying the
+ // children of TableRows, even though they are @immutable.
+ // ignore: prefer_const_literals_to_create_immutables
+ children: [],
+ ));
+ }
+ final _BlockElement bElement = _BlockElement(tag);
+ if (start != null) {
+ bElement.nextListIndex = start;
+ }
+ _blocks.add(bElement);
+ } else {
+ if (tag == 'a') {
+ final String? text = extractTextFromElement(element);
+ // Don't add empty links
+ if (text == null) {
+ return false;
+ }
+ final String? destination = element.attributes['href'];
+ final String title = element.attributes['title'] ?? '';
+
+ _linkHandlers.add(
+ delegate.createLink(text, destination, title),
+ );
+ }
+
+ _addParentInlineIfNeeded(_blocks.last.tag);
+
+ // The Markdown parser passes empty table data tags for blank
+ // table cells. Insert a text node with an empty string in this
+ // case for the table cell to get properly created.
+ if (element.tag == 'td' &&
+ element.children != null &&
+ element.children!.isEmpty) {
+ element.children!.add(md.Text(''));
+ }
+
+ final TextStyle parentStyle = _inlines.last.style!;
+ _inlines.add(_InlineElement(
+ tag,
+ style: parentStyle.merge(styleSheet.styles[tag]),
+ ));
+ }
+
+ return true;
+ }
+
+ /// Returns the text, if any, from [element] and its descendants.
+ String? extractTextFromElement(md.Node element) {
+ return element is md.Element && (element.children?.isNotEmpty ?? false)
+ ? element.children!
+ .map((md.Node e) =>
+ e is md.Text ? e.text : extractTextFromElement(e))
+ .join('')
+ : (element is md.Element && (element.attributes.isNotEmpty)
+ ? element.attributes['alt']
+ : '');
+ }
+
+ @override
+ void visitText(md.Text text) {
+ // Don't allow text directly under the root.
+ if (_blocks.last.tag == null) {
+ return;
+ }
+
+ _addParentInlineIfNeeded(_blocks.last.tag);
+
+ // Define trim text function to remove spaces from text elements in
+ // accordance with Markdown specifications.
+ String trimText(String text) {
+ // The leading spaces pattern is used to identify spaces
+ // at the beginning of a line of text.
+ final RegExp _leadingSpacesPattern = RegExp(r'^ *');
+
+ // The soft line break pattern is used to identify the spaces at the end of a
+ // line of text and the leading spaces in the immediately following the line
+ // of text. These spaces are removed in accordance with the Markdown
+ // specification on soft line breaks when lines of text are joined.
+ final RegExp _softLineBreakPattern = RegExp(r' ?\n *');
+
+ // Leading spaces following a hard line break are ignored.
+ // https://github.github.com/gfm/#example-657
+ if (_lastTag == 'br') {
+ text = text.replaceAll(_leadingSpacesPattern, '');
+ }
+
+ // Spaces at end of the line and beginning of the next line are removed.
+ // https://github.github.com/gfm/#example-670
+ return text.replaceAll(_softLineBreakPattern, ' ');
+ }
+
+ Widget? child;
+ if (_blocks.isNotEmpty && builders.containsKey(_blocks.last.tag)) {
+ child = builders[_blocks.last.tag!]!
+ .visitText(text, styleSheet.styles[_blocks.last.tag!]);
+ } else if (_blocks.last.tag == 'pre') {
+ child = Scrollbar(
+ child: SingleChildScrollView(
+ scrollDirection: Axis.horizontal,
+ padding: styleSheet.codeblockPadding,
+ child: _buildRichText(delegate.formatText(styleSheet, text.text)),
+ ),
+ );
+ } else {
+ 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),
+ );
+ }
+ if (child != null) {
+ _inlines.last.children.add(child);
+ }
+ }
+
+ @override
+ void visitElementAfter(md.Element element) {
+ final String tag = element.tag;
+
+ if (_isBlockTag(tag)) {
+ _addAnonymousBlockIfNeeded();
+
+ final _BlockElement current = _blocks.removeLast();
+ Widget child;
+
+ if (current.children.isNotEmpty) {
+ child = Column(
+ crossAxisAlignment: fitContent
+ ? CrossAxisAlignment.start
+ : CrossAxisAlignment.stretch,
+ children: current.children,
+ );
+ } else {
+ child = const SizedBox();
+ }
+
+ if (_isListTag(tag)) {
+ assert(_listIndents.isNotEmpty);
+ _listIndents.removeLast();
+ } else if (tag == 'li') {
+ if (_listIndents.isNotEmpty) {
+ if (element.children!.isEmpty) {
+ element.children!.add(md.Text(''));
+ }
+ Widget bullet;
+ final dynamic el = element.children![0];
+ if (el is md.Element && el.attributes['type'] == 'checkbox') {
+ final bool val = el.attributes['checked'] != 'false';
+ bullet = _buildCheckbox(val);
+ } else {
+ bullet = _buildBullet(_listIndents.last);
+ }
+ child = Row(
+ textBaseline: listItemCrossAxisAlignment ==
+ MarkdownListItemCrossAxisAlignment.start
+ ? null
+ : TextBaseline.alphabetic,
+ crossAxisAlignment: listItemCrossAxisAlignment ==
+ MarkdownListItemCrossAxisAlignment.start
+ ? CrossAxisAlignment.start
+ : CrossAxisAlignment.baseline,
+ children: [
+ SizedBox(
+ width: styleSheet.listIndent! +
+ styleSheet.listBulletPadding!.left +
+ styleSheet.listBulletPadding!.right,
+ child: bullet,
+ ),
+ Expanded(child: child)
+ ],
+ );
+ }
+ } else if (tag == 'table') {
+ child = Table(
+ defaultColumnWidth: styleSheet.tableColumnWidth!,
+ defaultVerticalAlignment: TableCellVerticalAlignment.middle,
+ border: styleSheet.tableBorder,
+ children: _tables.removeLast().rows,
+ );
+ } else if (tag == 'blockquote') {
+ _isInBlockquote = false;
+ child = DecoratedBox(
+ decoration: styleSheet.blockquoteDecoration!,
+ child: Padding(
+ padding: styleSheet.blockquotePadding!,
+ child: child,
+ ),
+ );
+ } else if (tag == 'pre') {
+ child = DecoratedBox(
+ decoration: styleSheet.codeblockDecoration!,
+ child: child,
+ );
+ } else if (tag == 'hr') {
+ child = Container(decoration: styleSheet.horizontalRuleDecoration);
+ }
+
+ _addBlockChild(child);
+ } else {
+ final _InlineElement current = _inlines.removeLast();
+ final _InlineElement parent = _inlines.last;
+
+ if (builders.containsKey(tag)) {
+ final Widget? child =
+ builders[tag]!.visitElementAfter(element, styleSheet.styles[tag]);
+ if (child != null) {
+ current.children[0] = child;
+ }
+ } else if (tag == 'img') {
+ // create an image widget for this image
+ current.children.add(_buildImage(
+ element.attributes['src']!,
+ element.attributes['title'],
+ element.attributes['alt'],
+ ));
+ } else if (tag == 'br') {
+ current.children.add(_buildRichText(const TextSpan(text: '\n')));
+ } else if (tag == 'th' || tag == 'td') {
+ TextAlign? align;
+ final String? style = element.attributes['style'];
+ if (style == null) {
+ align = tag == 'th' ? styleSheet.tableHeadAlign : TextAlign.left;
+ } else {
+ final RegExp regExp = RegExp(r'text-align: (left|center|right)');
+ final Match match = regExp.matchAsPrefix(style)!;
+ switch (match[1]) {
+ case 'left':
+ align = TextAlign.left;
+ break;
+ case 'center':
+ align = TextAlign.center;
+ break;
+ case 'right':
+ align = TextAlign.right;
+ break;
+ }
+ }
+ final Widget child = _buildTableCell(
+ _mergeInlineChildren(current.children, align),
+ textAlign: align,
+ );
+ _tables.single.rows.last.children!.add(child);
+ } else if (tag == 'a') {
+ _linkHandlers.removeLast();
+ }
+
+ if (current.children.isNotEmpty) {
+ parent.children.addAll(current.children);
+ }
+ }
+ if (_currentBlockTag == tag) {
+ _currentBlockTag = null;
+ }
+ _lastTag = tag;
+ }
+
+ Widget _buildImage(String src, String? title, String? alt) {
+ final List parts = src.split('#');
+ if (parts.isEmpty) {
+ return const SizedBox();
+ }
+
+ final String path = parts.first;
+ double? width;
+ double? height;
+ if (parts.length == 2) {
+ final List dimensions = parts.last.split('x');
+ if (dimensions.length == 2) {
+ width = double.parse(dimensions[0]);
+ height = double.parse(dimensions[1]);
+ }
+ }
+
+ final Uri uri = Uri.parse(path);
+ Widget child;
+ if (imageBuilder != null) {
+ child = imageBuilder!(uri, title, alt);
+ } else {
+ child = kDefaultImageBuilder(uri, imageDirectory, width, height);
+ }
+
+ if (_linkHandlers.isNotEmpty) {
+ final TapGestureRecognizer recognizer =
+ _linkHandlers.last as TapGestureRecognizer;
+ return GestureDetector(child: child, onTap: recognizer.onTap);
+ } else {
+ return child;
+ }
+ }
+
+ Widget _buildCheckbox(bool checked) {
+ if (checkboxBuilder != null) {
+ return checkboxBuilder!(checked);
+ }
+ return Padding(
+ padding: styleSheet.listBulletPadding!,
+ child: Icon(
+ checked ? Icons.check_box : Icons.check_box_outline_blank,
+ size: styleSheet.checkbox!.fontSize,
+ color: styleSheet.checkbox!.color,
+ ),
+ );
+ }
+
+ Widget _buildBullet(String listTag) {
+ final int index = _blocks.last.nextListIndex;
+ final bool isUnordered = listTag == 'ul';
+
+ if (bulletBuilder != null) {
+ return Padding(
+ padding: styleSheet.listBulletPadding!,
+ child: bulletBuilder!(index,
+ isUnordered ? BulletStyle.unorderedList : BulletStyle.orderedList),
+ );
+ }
+
+ if (isUnordered) {
+ return Padding(
+ padding: styleSheet.listBulletPadding!,
+ child: Text(
+ '•',
+ textAlign: TextAlign.center,
+ style: styleSheet.listBullet,
+ ),
+ );
+ }
+
+ return Padding(
+ padding: styleSheet.listBulletPadding!,
+ child: Text(
+ '${index + 1}.',
+ textAlign: TextAlign.right,
+ style: styleSheet.listBullet,
+ ),
+ );
+ }
+
+ Widget _buildTableCell(List children, {TextAlign? textAlign}) {
+ return TableCell(
+ child: Padding(
+ padding: styleSheet.tableCellsPadding!,
+ child: DefaultTextStyle(
+ style: styleSheet.tableBody!,
+ textAlign: textAlign,
+ child: Wrap(children: children as List),
+ ),
+ ),
+ );
+ }
+
+ void _addParentInlineIfNeeded(String? tag) {
+ if (_inlines.isEmpty) {
+ _inlines.add(_InlineElement(
+ tag,
+ style: styleSheet.styles[tag!],
+ ));
+ }
+ }
+
+ void _addBlockChild(Widget child) {
+ final _BlockElement parent = _blocks.last;
+ if (parent.children.isNotEmpty) {
+ parent.children.add(SizedBox(height: styleSheet.blockSpacing));
+ }
+ parent.children.add(child);
+ parent.nextListIndex += 1;
+ }
+
+ void _addAnonymousBlockIfNeeded() {
+ if (_inlines.isEmpty) {
+ return;
+ }
+
+ WrapAlignment blockAlignment = WrapAlignment.start;
+ TextAlign textAlign = TextAlign.start;
+ if (_isBlockTag(_currentBlockTag)) {
+ blockAlignment = _wrapAlignmentForBlockTag(_currentBlockTag);
+ textAlign = _textAlignForBlockTag(_currentBlockTag);
+ }
+
+ final _InlineElement inline = _inlines.single;
+ if (inline.children.isNotEmpty) {
+ final List mergedInlines = _mergeInlineChildren(
+ inline.children,
+ textAlign,
+ );
+ final Wrap wrap = Wrap(
+ crossAxisAlignment: WrapCrossAlignment.center,
+ children: mergedInlines,
+ alignment: blockAlignment,
+ );
+ _addBlockChild(wrap);
+ _inlines.clear();
+ }
+ }
+
+ /// Merges adjacent [TextSpan] children
+ List _mergeInlineChildren(
+ List children,
+ TextAlign? textAlign,
+ ) {
+ final List mergedTexts = [];
+ for (final Widget child in children) {
+ if (mergedTexts.isNotEmpty &&
+ mergedTexts.last is RichText &&
+ child is RichText) {
+ final RichText previous = mergedTexts.removeLast() as RichText;
+ final TextSpan previousTextSpan = previous.text as TextSpan;
+ final List children = previousTextSpan.children != null
+ ? List.from(previousTextSpan.children!)
+ : [previousTextSpan];
+ children.add(child.text as TextSpan);
+ final TextSpan? mergedSpan = _mergeSimilarTextSpans(children);
+ mergedTexts.add(_buildRichText(
+ mergedSpan,
+ textAlign: textAlign,
+ ));
+ } else if (mergedTexts.isNotEmpty &&
+ mergedTexts.last is SelectableText &&
+ child is SelectableText) {
+ final SelectableText previous =
+ mergedTexts.removeLast() as SelectableText;
+ final TextSpan previousTextSpan = previous.textSpan!;
+ final List children = previousTextSpan.children != null
+ ? List.from(previousTextSpan.children!)
+ : [previousTextSpan];
+ if (child.textSpan != null) {
+ children.add(child.textSpan!);
+ }
+ final TextSpan? mergedSpan = _mergeSimilarTextSpans(children);
+ mergedTexts.add(
+ _buildRichText(
+ mergedSpan,
+ textAlign: textAlign,
+ ),
+ );
+ } else {
+ mergedTexts.add(child);
+ }
+ }
+ return mergedTexts;
+ }
+
+ TextAlign _textAlignForBlockTag(String? blockTag) {
+ final WrapAlignment wrapAlignment = _wrapAlignmentForBlockTag(blockTag);
+ switch (wrapAlignment) {
+ case WrapAlignment.start:
+ return TextAlign.start;
+ case WrapAlignment.center:
+ return TextAlign.center;
+ case WrapAlignment.end:
+ return TextAlign.end;
+ case WrapAlignment.spaceAround:
+ return TextAlign.justify;
+ case WrapAlignment.spaceBetween:
+ return TextAlign.justify;
+ case WrapAlignment.spaceEvenly:
+ return TextAlign.justify;
+ }
+ }
+
+ WrapAlignment _wrapAlignmentForBlockTag(String? blockTag) {
+ switch (blockTag) {
+ case 'p':
+ return styleSheet.textAlign;
+ case 'h1':
+ return styleSheet.h1Align;
+ case 'h2':
+ return styleSheet.h2Align;
+ case 'h3':
+ return styleSheet.h3Align;
+ case 'h4':
+ return styleSheet.h4Align;
+ case 'h5':
+ return styleSheet.h5Align;
+ case 'h6':
+ return styleSheet.h6Align;
+ case 'ul':
+ return styleSheet.unorderedListAlign;
+ case 'ol':
+ return styleSheet.orderedListAlign;
+ case 'blockquote':
+ return styleSheet.blockquoteAlign;
+ case 'pre':
+ return styleSheet.codeblockAlign;
+ case 'hr':
+ print('Markdown did not handle hr for alignment');
+ break;
+ case 'li':
+ print('Markdown did not handle li for alignment');
+ break;
+ }
+ return WrapAlignment.start;
+ }
+
+ /// Combine text spans with equivalent properties into a single span.
+ TextSpan? _mergeSimilarTextSpans(List? textSpans) {
+ if (textSpans == null || textSpans.length < 2) {
+ return TextSpan(children: textSpans);
+ }
+
+ final List mergedSpans = [textSpans.first];
+
+ for (int index = 1; index < textSpans.length; index++) {
+ final TextSpan nextChild = textSpans[index];
+ if (nextChild is TextSpan &&
+ nextChild.recognizer == mergedSpans.last.recognizer &&
+ nextChild.semanticsLabel == mergedSpans.last.semanticsLabel &&
+ nextChild.style == mergedSpans.last.style) {
+ final TextSpan previous = mergedSpans.removeLast();
+ mergedSpans.add(TextSpan(
+ text: previous.toPlainText() + nextChild.toPlainText(),
+ recognizer: previous.recognizer,
+ semanticsLabel: previous.semanticsLabel,
+ style: previous.style,
+ ));
+ } else {
+ mergedSpans.add(nextChild);
+ }
+ }
+
+ // When the mergered spans compress into a single TextSpan return just that
+ // TextSpan, otherwise bundle the set of TextSpans under a single parent.
+ return mergedSpans.length == 1
+ ? mergedSpans.first
+ : TextSpan(children: mergedSpans);
+ }
+
+ Widget _buildRichText(TextSpan? text, {TextAlign? textAlign}) {
+ if (selectable) {
+ return SelectableText.rich(
+ text!,
+ textScaleFactor: styleSheet.textScaleFactor,
+ textAlign: textAlign ?? TextAlign.start,
+ onTap: onTapText,
+ );
+ } else {
+ return RichText(
+ text: text!,
+ textScaleFactor: styleSheet.textScaleFactor!,
+ textAlign: textAlign ?? TextAlign.start,
+ );
+ }
+ }
+}
diff --git a/packages/flutter_markdown/lib/src/style_sheet.dart b/packages/flutter_markdown/lib/src/style_sheet.dart
new file mode 100644
index 0000000000..f815047143
--- /dev/null
+++ b/packages/flutter_markdown/lib/src/style_sheet.dart
@@ -0,0 +1,688 @@
+// Copyright 2016 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'package:flutter/cupertino.dart';
+import 'package:flutter/material.dart';
+
+/// Defines which [TextStyle] objects to use for which Markdown elements.
+class MarkdownStyleSheet {
+ /// Creates an explicit mapping of [TextStyle] objects to Markdown elements.
+ MarkdownStyleSheet({
+ this.a,
+ this.p,
+ this.code,
+ this.h1,
+ this.h2,
+ this.h3,
+ this.h4,
+ this.h5,
+ this.h6,
+ this.em,
+ this.strong,
+ this.del,
+ this.blockquote,
+ this.img,
+ this.checkbox,
+ this.blockSpacing,
+ this.listIndent,
+ this.listBullet,
+ this.listBulletPadding,
+ this.tableHead,
+ this.tableBody,
+ this.tableHeadAlign,
+ this.tableBorder,
+ this.tableColumnWidth,
+ this.tableCellsPadding,
+ this.tableCellsDecoration,
+ this.blockquotePadding,
+ this.blockquoteDecoration,
+ this.codeblockPadding,
+ this.codeblockDecoration,
+ this.horizontalRuleDecoration,
+ this.textAlign = WrapAlignment.start,
+ this.h1Align = WrapAlignment.start,
+ this.h2Align = WrapAlignment.start,
+ this.h3Align = WrapAlignment.start,
+ this.h4Align = WrapAlignment.start,
+ this.h5Align = WrapAlignment.start,
+ this.h6Align = WrapAlignment.start,
+ this.unorderedListAlign = WrapAlignment.start,
+ this.orderedListAlign = WrapAlignment.start,
+ this.blockquoteAlign = WrapAlignment.start,
+ this.codeblockAlign = WrapAlignment.start,
+ this.textScaleFactor,
+ }) : _styles = {
+ 'a': a,
+ 'p': p,
+ 'li': p,
+ 'code': code,
+ 'pre': p,
+ 'h1': h1,
+ 'h2': h2,
+ 'h3': h3,
+ 'h4': h4,
+ 'h5': h5,
+ 'h6': h6,
+ 'em': em,
+ 'strong': strong,
+ 'del': del,
+ 'blockquote': blockquote,
+ 'img': img,
+ 'table': p,
+ 'th': tableHead,
+ 'tr': tableBody,
+ 'td': tableBody,
+ };
+
+ /// Creates a [MarkdownStyleSheet] from the [TextStyle]s in the provided [ThemeData].
+ factory MarkdownStyleSheet.fromTheme(ThemeData theme) {
+ assert(theme.textTheme.bodyText2?.fontSize != null);
+ return MarkdownStyleSheet(
+ a: const TextStyle(color: Colors.blue),
+ p: theme.textTheme.bodyText2,
+ code: theme.textTheme.bodyText2!.copyWith(
+ backgroundColor: theme.cardTheme.color ?? theme.cardColor,
+ fontFamily: 'monospace',
+ fontSize: theme.textTheme.bodyText2!.fontSize! * 0.85,
+ ),
+ h1: theme.textTheme.headline5,
+ h2: theme.textTheme.headline6,
+ h3: theme.textTheme.subtitle1,
+ h4: theme.textTheme.bodyText1,
+ h5: theme.textTheme.bodyText1,
+ h6: theme.textTheme.bodyText1,
+ em: const TextStyle(fontStyle: FontStyle.italic),
+ strong: const TextStyle(fontWeight: FontWeight.bold),
+ del: const TextStyle(decoration: TextDecoration.lineThrough),
+ blockquote: theme.textTheme.bodyText2,
+ img: theme.textTheme.bodyText2,
+ checkbox: theme.textTheme.bodyText2!.copyWith(
+ color: theme.primaryColor,
+ ),
+ blockSpacing: 8.0,
+ listIndent: 24.0,
+ listBullet: theme.textTheme.bodyText2,
+ listBulletPadding: const EdgeInsets.only(right: 4),
+ tableHead: const TextStyle(fontWeight: FontWeight.w600),
+ tableBody: theme.textTheme.bodyText2,
+ tableHeadAlign: TextAlign.center,
+ tableBorder: TableBorder.all(
+ color: theme.dividerColor,
+ width: 1,
+ ),
+ tableColumnWidth: const FlexColumnWidth(),
+ tableCellsPadding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
+ tableCellsDecoration: const BoxDecoration(),
+ blockquotePadding: const EdgeInsets.all(8.0),
+ blockquoteDecoration: BoxDecoration(
+ color: Colors.blue.shade100,
+ borderRadius: BorderRadius.circular(2.0),
+ ),
+ codeblockPadding: const EdgeInsets.all(8.0),
+ codeblockDecoration: BoxDecoration(
+ color: theme.cardTheme.color ?? theme.cardColor,
+ borderRadius: BorderRadius.circular(2.0),
+ ),
+ horizontalRuleDecoration: BoxDecoration(
+ border: Border(
+ top: BorderSide(
+ width: 5.0,
+ color: theme.dividerColor,
+ ),
+ ),
+ ),
+ );
+ }
+
+ /// Creates a [MarkdownStyleSheet] from the [TextStyle]s in the provided [CupertinoThemeData].
+ factory MarkdownStyleSheet.fromCupertinoTheme(CupertinoThemeData theme) {
+ assert(theme.textTheme.textStyle.fontSize != null);
+ return MarkdownStyleSheet(
+ a: theme.textTheme.textStyle.copyWith(
+ color: theme.brightness == Brightness.dark
+ ? CupertinoColors.link.darkColor
+ : CupertinoColors.link.color,
+ ),
+ p: theme.textTheme.textStyle,
+ code: theme.textTheme.textStyle.copyWith(
+ backgroundColor: theme.brightness == Brightness.dark
+ ? CupertinoColors.systemGrey6.darkColor
+ : CupertinoColors.systemGrey6.color,
+ fontFamily: 'monospace',
+ fontSize: theme.textTheme.textStyle.fontSize! * 0.85,
+ ),
+ h1: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w500,
+ fontSize: theme.textTheme.textStyle.fontSize! + 10,
+ ),
+ h2: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w500,
+ fontSize: theme.textTheme.textStyle.fontSize! + 8,
+ ),
+ h3: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w500,
+ fontSize: theme.textTheme.textStyle.fontSize! + 6,
+ ),
+ h4: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w500,
+ fontSize: theme.textTheme.textStyle.fontSize! + 4,
+ ),
+ h5: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w500,
+ fontSize: theme.textTheme.textStyle.fontSize! + 2,
+ ),
+ h6: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w500,
+ ),
+ em: theme.textTheme.textStyle.copyWith(
+ fontStyle: FontStyle.italic,
+ ),
+ strong: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.bold,
+ ),
+ del: theme.textTheme.textStyle.copyWith(
+ decoration: TextDecoration.lineThrough,
+ ),
+ blockquote: theme.textTheme.textStyle,
+ img: theme.textTheme.textStyle,
+ checkbox: theme.textTheme.textStyle.copyWith(
+ color: theme.primaryColor,
+ ),
+ blockSpacing: 8,
+ listIndent: 24,
+ listBullet: theme.textTheme.textStyle,
+ listBulletPadding: const EdgeInsets.only(right: 4),
+ tableHead: theme.textTheme.textStyle.copyWith(
+ fontWeight: FontWeight.w600,
+ ),
+ tableBody: theme.textTheme.textStyle,
+ tableHeadAlign: TextAlign.center,
+ tableBorder: TableBorder.all(color: CupertinoColors.separator, width: 0),
+ tableColumnWidth: const FlexColumnWidth(),
+ tableCellsPadding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
+ tableCellsDecoration: BoxDecoration(
+ color: theme.brightness == Brightness.dark
+ ? CupertinoColors.systemGrey6.darkColor
+ : CupertinoColors.systemGrey6.color,
+ ),
+ blockquotePadding: const EdgeInsets.all(16),
+ blockquoteDecoration: BoxDecoration(
+ color: theme.brightness == Brightness.dark
+ ? CupertinoColors.systemGrey6.darkColor
+ : CupertinoColors.systemGrey6.color,
+ border: Border(
+ left: BorderSide(
+ color: theme.brightness == Brightness.dark
+ ? CupertinoColors.systemGrey4.darkColor
+ : CupertinoColors.systemGrey4.color,
+ width: 4,
+ ),
+ ),
+ ),
+ codeblockPadding: const EdgeInsets.all(8),
+ codeblockDecoration: BoxDecoration(
+ color: theme.brightness == Brightness.dark
+ ? CupertinoColors.systemGrey6.darkColor
+ : CupertinoColors.systemGrey6.color,
+ ),
+ horizontalRuleDecoration: BoxDecoration(
+ border: Border(
+ top: BorderSide(
+ color: theme.brightness == Brightness.dark
+ ? CupertinoColors.systemGrey4.darkColor
+ : CupertinoColors.systemGrey4.color,
+ width: 1,
+ ),
+ ),
+ ),
+ );
+ }
+
+ /// Creates a [MarkdownStyle] from the [TextStyle]s in the provided [ThemeData].
+ ///
+ /// This constructor uses larger fonts for the headings than in
+ /// [MarkdownStyle.fromTheme].
+ factory MarkdownStyleSheet.largeFromTheme(ThemeData theme) {
+ return MarkdownStyleSheet(
+ a: const TextStyle(color: Colors.blue),
+ p: theme.textTheme.bodyText2,
+ code: theme.textTheme.bodyText2!.copyWith(
+ backgroundColor: theme.cardTheme.color ?? theme.cardColor,
+ fontFamily: 'monospace',
+ fontSize: theme.textTheme.bodyText2!.fontSize! * 0.85,
+ ),
+ h1: theme.textTheme.headline2,
+ h2: theme.textTheme.headline3,
+ h3: theme.textTheme.headline4,
+ h4: theme.textTheme.headline5,
+ h5: theme.textTheme.headline6,
+ h6: theme.textTheme.subtitle1,
+ em: const TextStyle(fontStyle: FontStyle.italic),
+ strong: const TextStyle(fontWeight: FontWeight.bold),
+ del: const TextStyle(decoration: TextDecoration.lineThrough),
+ blockquote: theme.textTheme.bodyText2,
+ img: theme.textTheme.bodyText2,
+ checkbox: theme.textTheme.bodyText2!.copyWith(
+ color: theme.primaryColor,
+ ),
+ blockSpacing: 8.0,
+ listIndent: 24.0,
+ listBullet: theme.textTheme.bodyText2,
+ listBulletPadding: const EdgeInsets.only(right: 4),
+ tableHead: const TextStyle(fontWeight: FontWeight.w600),
+ tableBody: theme.textTheme.bodyText2,
+ tableHeadAlign: TextAlign.center,
+ tableBorder: TableBorder.all(
+ color: theme.dividerColor,
+ ),
+ tableColumnWidth: const FlexColumnWidth(),
+ tableCellsPadding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
+ tableCellsDecoration: const BoxDecoration(),
+ blockquotePadding: const EdgeInsets.all(8.0),
+ blockquoteDecoration: BoxDecoration(
+ color: Colors.blue.shade100,
+ borderRadius: BorderRadius.circular(2.0),
+ ),
+ codeblockPadding: const EdgeInsets.all(8.0),
+ codeblockDecoration: BoxDecoration(
+ color: theme.cardTheme.color ?? theme.cardColor,
+ borderRadius: BorderRadius.circular(2.0),
+ ),
+ horizontalRuleDecoration: BoxDecoration(
+ border: Border(
+ top: BorderSide(
+ width: 5.0,
+ color: theme.dividerColor,
+ ),
+ ),
+ ),
+ );
+ }
+
+ /// Creates a [MarkdownStyleSheet] based on the current style, with the
+ /// provided parameters overridden.
+ MarkdownStyleSheet copyWith({
+ TextStyle? a,
+ TextStyle? p,
+ TextStyle? code,
+ TextStyle? h1,
+ TextStyle? h2,
+ TextStyle? h3,
+ TextStyle? h4,
+ TextStyle? h5,
+ TextStyle? h6,
+ TextStyle? em,
+ TextStyle? strong,
+ TextStyle? del,
+ TextStyle? blockquote,
+ TextStyle? img,
+ TextStyle? checkbox,
+ double? blockSpacing,
+ double? listIndent,
+ TextStyle? listBullet,
+ EdgeInsets? listBulletPadding,
+ TextStyle? tableHead,
+ TextStyle? tableBody,
+ TextAlign? tableHeadAlign,
+ TableBorder? tableBorder,
+ TableColumnWidth? tableColumnWidth,
+ EdgeInsets? tableCellsPadding,
+ Decoration? tableCellsDecoration,
+ EdgeInsets? blockquotePadding,
+ Decoration? blockquoteDecoration,
+ EdgeInsets? codeblockPadding,
+ Decoration? codeblockDecoration,
+ Decoration? horizontalRuleDecoration,
+ WrapAlignment? textAlign,
+ WrapAlignment? h1Align,
+ WrapAlignment? h2Align,
+ WrapAlignment? h3Align,
+ WrapAlignment? h4Align,
+ WrapAlignment? h5Align,
+ WrapAlignment? h6Align,
+ WrapAlignment? unorderedListAlign,
+ WrapAlignment? orderedListAlign,
+ WrapAlignment? blockquoteAlign,
+ WrapAlignment? codeblockAlign,
+ double? textScaleFactor,
+ }) {
+ return MarkdownStyleSheet(
+ a: a ?? this.a,
+ p: p ?? this.p,
+ code: code ?? this.code,
+ h1: h1 ?? this.h1,
+ h2: h2 ?? this.h2,
+ h3: h3 ?? this.h3,
+ h4: h4 ?? this.h4,
+ h5: h5 ?? this.h5,
+ h6: h6 ?? this.h6,
+ em: em ?? this.em,
+ strong: strong ?? this.strong,
+ del: del ?? this.del,
+ blockquote: blockquote ?? this.blockquote,
+ img: img ?? this.img,
+ checkbox: checkbox ?? this.checkbox,
+ blockSpacing: blockSpacing ?? this.blockSpacing,
+ listIndent: listIndent ?? this.listIndent,
+ listBullet: listBullet ?? this.listBullet,
+ listBulletPadding: listBulletPadding ?? this.listBulletPadding,
+ tableHead: tableHead ?? this.tableHead,
+ tableBody: tableBody ?? this.tableBody,
+ tableHeadAlign: tableHeadAlign ?? this.tableHeadAlign,
+ tableBorder: tableBorder ?? this.tableBorder,
+ tableColumnWidth: tableColumnWidth ?? this.tableColumnWidth,
+ tableCellsPadding: tableCellsPadding ?? this.tableCellsPadding,
+ tableCellsDecoration: tableCellsDecoration ?? this.tableCellsDecoration,
+ blockquotePadding: blockquotePadding ?? this.blockquotePadding,
+ blockquoteDecoration: blockquoteDecoration ?? this.blockquoteDecoration,
+ codeblockPadding: codeblockPadding ?? this.codeblockPadding,
+ codeblockDecoration: codeblockDecoration ?? this.codeblockDecoration,
+ horizontalRuleDecoration:
+ horizontalRuleDecoration ?? this.horizontalRuleDecoration,
+ textAlign: textAlign ?? this.textAlign,
+ h1Align: h1Align ?? this.h1Align,
+ h2Align: h2Align ?? this.h2Align,
+ h3Align: h3Align ?? this.h3Align,
+ h4Align: h4Align ?? this.h4Align,
+ h5Align: h5Align ?? this.h5Align,
+ h6Align: h6Align ?? this.h6Align,
+ unorderedListAlign: unorderedListAlign ?? this.unorderedListAlign,
+ orderedListAlign: orderedListAlign ?? this.orderedListAlign,
+ blockquoteAlign: blockquoteAlign ?? this.blockquoteAlign,
+ codeblockAlign: codeblockAlign ?? this.codeblockAlign,
+ textScaleFactor: textScaleFactor ?? this.textScaleFactor,
+ );
+ }
+
+ /// Returns a new text style that is a combination of this style and the given
+ /// [other] style.
+ MarkdownStyleSheet merge(MarkdownStyleSheet? other) {
+ if (other == null) {
+ return this;
+ }
+ return copyWith(
+ a: a!.merge(other.a),
+ p: p!.merge(other.p),
+ code: code!.merge(other.code),
+ h1: h1!.merge(other.h1),
+ h2: h2!.merge(other.h2),
+ h3: h3!.merge(other.h3),
+ h4: h4!.merge(other.h4),
+ h5: h5!.merge(other.h5),
+ h6: h6!.merge(other.h6),
+ em: em!.merge(other.em),
+ strong: strong!.merge(other.strong),
+ del: del!.merge(other.del),
+ blockquote: blockquote!.merge(other.blockquote),
+ img: img!.merge(other.img),
+ checkbox: checkbox!.merge(other.checkbox),
+ blockSpacing: other.blockSpacing,
+ listIndent: other.listIndent,
+ listBullet: listBullet!.merge(other.listBullet),
+ listBulletPadding: other.listBulletPadding,
+ tableHead: tableHead!.merge(other.tableHead),
+ tableBody: tableBody!.merge(other.tableBody),
+ tableHeadAlign: other.tableHeadAlign,
+ tableBorder: other.tableBorder,
+ tableColumnWidth: other.tableColumnWidth,
+ tableCellsPadding: other.tableCellsPadding,
+ tableCellsDecoration: other.tableCellsDecoration,
+ blockquotePadding: other.blockquotePadding,
+ blockquoteDecoration: other.blockquoteDecoration,
+ codeblockPadding: other.codeblockPadding,
+ codeblockDecoration: other.codeblockDecoration,
+ horizontalRuleDecoration: other.horizontalRuleDecoration,
+ textAlign: other.textAlign,
+ h1Align: other.h1Align,
+ h2Align: other.h2Align,
+ h3Align: other.h3Align,
+ h4Align: other.h4Align,
+ h5Align: other.h5Align,
+ h6Align: other.h6Align,
+ unorderedListAlign: other.unorderedListAlign,
+ orderedListAlign: other.orderedListAlign,
+ blockquoteAlign: other.blockquoteAlign,
+ codeblockAlign: other.codeblockAlign,
+ textScaleFactor: other.textScaleFactor,
+ );
+ }
+
+ /// The [TextStyle] to use for `a` elements.
+ final TextStyle? a;
+
+ /// The [TextStyle] to use for `p` elements.
+ final TextStyle? p;
+
+ /// The [TextStyle] to use for `code` elements.
+ final TextStyle? code;
+
+ /// The [TextStyle] to use for `h1` elements.
+ final TextStyle? h1;
+
+ /// The [TextStyle] to use for `h2` elements.
+ final TextStyle? h2;
+
+ /// The [TextStyle] to use for `h3` elements.
+ final TextStyle? h3;
+
+ /// The [TextStyle] to use for `h4` elements.
+ final TextStyle? h4;
+
+ /// The [TextStyle] to use for `h5` elements.
+ final TextStyle? h5;
+
+ /// The [TextStyle] to use for `h6` elements.
+ final TextStyle? h6;
+
+ /// The [TextStyle] to use for `em` elements.
+ final TextStyle? em;
+
+ /// The [TextStyle] to use for `strong` elements.
+ final TextStyle? strong;
+
+ /// The [TextStyle] to use for `del` elements.
+ final TextStyle? del;
+
+ /// The [TextStyle] to use for `blockquote` elements.
+ final TextStyle? blockquote;
+
+ /// The [TextStyle] to use for `img` elements.
+ final TextStyle? img;
+
+ /// The [TextStyle] to use for `input` elements.
+ final TextStyle? checkbox;
+
+ /// The amount of vertical space to use between block-level elements.
+ final double? blockSpacing;
+
+ /// The amount of horizontal space to indent list items.
+ final double? listIndent;
+
+ /// The [TextStyle] to use for bullets.
+ final TextStyle? listBullet;
+
+ /// The padding to use for bullets.
+ final EdgeInsets? listBulletPadding;
+
+ /// The [TextStyle] to use for `th` elements.
+ final TextStyle? tableHead;
+
+ /// The [TextStyle] to use for `td` elements.
+ final TextStyle? tableBody;
+
+ /// The [TextAlign] to use for `th` elements.
+ final TextAlign? tableHeadAlign;
+
+ /// The [TableBorder] to use for `table` elements.
+ final TableBorder? tableBorder;
+
+ /// The [TableColumnWidth] to use for `th` and `td` elements.
+ final TableColumnWidth? tableColumnWidth;
+
+ /// The padding to use for `th` and `td` elements.
+ final EdgeInsets? tableCellsPadding;
+
+ /// The decoration to use for `th` and `td` elements.
+ final Decoration? tableCellsDecoration;
+
+ /// The padding to use for `blockquote` elements.
+ final EdgeInsets? blockquotePadding;
+
+ /// The decoration to use behind `blockquote` elements.
+ final Decoration? blockquoteDecoration;
+
+ /// The padding to use for `pre` elements.
+ final EdgeInsets? codeblockPadding;
+
+ /// The decoration to use behind for `pre` elements.
+ final Decoration? codeblockDecoration;
+
+ /// The decoration to use for `hr` elements.
+ final Decoration? horizontalRuleDecoration;
+
+ /// The [WrapAlignment] to use for normal text. Defaults to start.
+ final WrapAlignment textAlign;
+
+ /// The [WrapAlignment] to use for h1 text. Defaults to start.
+ final WrapAlignment h1Align;
+
+ /// The [WrapAlignment] to use for h2 text. Defaults to start.
+ final WrapAlignment h2Align;
+
+ /// The [WrapAlignment] to use for h3 text. Defaults to start.
+ final WrapAlignment h3Align;
+
+ /// The [WrapAlignment] to use for h4 text. Defaults to start.
+ final WrapAlignment h4Align;
+
+ /// The [WrapAlignment] to use for h5 text. Defaults to start.
+ final WrapAlignment h5Align;
+
+ /// The [WrapAlignment] to use for h6 text. Defaults to start.
+ final WrapAlignment h6Align;
+
+ /// The [WrapAlignment] to use for an unordered list. Defaults to start.
+ final WrapAlignment unorderedListAlign;
+
+ /// The [WrapAlignment] to use for an ordered list. Defaults to start.
+ final WrapAlignment orderedListAlign;
+
+ /// The [WrapAlignment] to use for a blockquote. Defaults to start.
+ final WrapAlignment blockquoteAlign;
+
+ /// The [WrapAlignment] to use for a code block. Defaults to start.
+ final WrapAlignment codeblockAlign;
+
+ /// The text scale factor to use in textual elements
+ final double? textScaleFactor;
+
+ /// A [Map] from element name to the corresponding [TextStyle] object.
+ Map get styles => _styles;
+ Map _styles;
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ bool operator ==(dynamic other) {
+ if (identical(this, other)) {
+ return true;
+ }
+ if (other.runtimeType != MarkdownStyleSheet) {
+ return false;
+ }
+ final MarkdownStyleSheet typedOther = other;
+ return typedOther.a == a &&
+ typedOther.p == p &&
+ typedOther.code == code &&
+ typedOther.h1 == h1 &&
+ typedOther.h2 == h2 &&
+ typedOther.h3 == h3 &&
+ typedOther.h4 == h4 &&
+ typedOther.h5 == h5 &&
+ typedOther.h6 == h6 &&
+ typedOther.em == em &&
+ typedOther.strong == strong &&
+ typedOther.del == del &&
+ typedOther.blockquote == blockquote &&
+ typedOther.img == img &&
+ typedOther.checkbox == checkbox &&
+ typedOther.blockSpacing == blockSpacing &&
+ typedOther.listIndent == listIndent &&
+ typedOther.listBullet == listBullet &&
+ typedOther.listBulletPadding == listBulletPadding &&
+ typedOther.tableHead == tableHead &&
+ typedOther.tableBody == tableBody &&
+ typedOther.tableHeadAlign == tableHeadAlign &&
+ typedOther.tableBorder == tableBorder &&
+ typedOther.tableColumnWidth == tableColumnWidth &&
+ typedOther.tableCellsPadding == tableCellsPadding &&
+ typedOther.tableCellsDecoration == tableCellsDecoration &&
+ typedOther.blockquotePadding == blockquotePadding &&
+ typedOther.blockquoteDecoration == blockquoteDecoration &&
+ typedOther.codeblockPadding == codeblockPadding &&
+ typedOther.codeblockDecoration == codeblockDecoration &&
+ typedOther.horizontalRuleDecoration == horizontalRuleDecoration &&
+ typedOther.textAlign == textAlign &&
+ typedOther.h1Align == h1Align &&
+ typedOther.h2Align == h2Align &&
+ typedOther.h3Align == h3Align &&
+ typedOther.h4Align == h4Align &&
+ typedOther.h5Align == h5Align &&
+ typedOther.h6Align == h6Align &&
+ typedOther.unorderedListAlign == unorderedListAlign &&
+ typedOther.orderedListAlign == orderedListAlign &&
+ typedOther.blockquoteAlign == blockquoteAlign &&
+ typedOther.codeblockAlign == codeblockAlign &&
+ typedOther.textScaleFactor == textScaleFactor;
+ }
+
+ @override
+ // ignore: avoid_equals_and_hash_code_on_mutable_classes
+ int get hashCode {
+ return hashList(