├── .flutter-plugins ├── .gitignore ├── .idea ├── encodings.xml ├── flutter_fullhtml_textview.iml ├── misc.xml ├── modules.xml └── vcs.xml ├── .metadata ├── README.md ├── android ├── .gradle │ ├── 4.10.2 │ │ ├── fileChanges │ │ │ └── last-build.bin │ │ ├── fileContent │ │ │ └── fileContent.lock │ │ ├── fileHashes │ │ │ ├── fileHashes.bin │ │ │ ├── fileHashes.lock │ │ │ └── resourceHashesCache.bin │ │ ├── gc.properties │ │ ├── javaCompile │ │ │ ├── classAnalysis.bin │ │ │ ├── jarAnalysis.bin │ │ │ ├── javaCompile.lock │ │ │ └── taskHistory.bin │ │ └── taskHistory │ │ │ ├── taskHistory.bin │ │ │ └── taskHistory.lock │ ├── buildOutputCleanup │ │ ├── buildOutputCleanup.lock │ │ ├── cache.properties │ │ └── outputFiles.bin │ └── vcs-1 │ │ └── gc.properties ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ ├── com │ │ │ │ └── example │ │ │ │ │ └── flutter_app │ │ │ │ │ └── MainActivity.java │ │ │ └── io │ │ │ │ └── flutter │ │ │ │ └── plugins │ │ │ │ └── GeneratedPluginRegistrant.java │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── flutter_app_android.iml ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties └── settings.gradle ├── assets └── images │ ├── 3.0x │ ├── feed_cell_photo_default_big.png │ ├── image_error.png │ └── long_picture_icon.png │ ├── image_error.png │ └── video_placeholder.png ├── flutter_app.iml ├── ios ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ ├── Generated.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.h │ ├── AppDelegate.m │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── GeneratedPluginRegistrant.h │ ├── GeneratedPluginRegistrant.m │ ├── Info.plist │ └── main.m ├── lib ├── ImageClipper.dart ├── circle.dart ├── constants.dart ├── element_parser_to_widget_list.dart ├── flutter_html_text.dart ├── flutter_html_textview.dart ├── html_async_textview.dart ├── html_parser_to_element_list.dart ├── html_parser_to_widget_list.dart ├── html_tags.dart ├── html_text_style.dart ├── image.dart ├── main.dart ├── network_image.dart ├── on_tap_data.dart └── web_view_page.dart ├── preview ├── Screenshot_1.jpg ├── Screenshot_2.jpg ├── Screenshot_3.jpg ├── Screenshot_4.jpg └── Screenshot_5.jpg └── pubspec.yaml /.flutter-plugins: -------------------------------------------------------------------------------- 1 | fluttertoast=/Users/houzhenpu/flutter/.pub-cache/hosted/pub.flutter-io.cn/fluttertoast-3.1.3/ 2 | path_provider=/Users/houzhenpu/flutter/.pub-cache/hosted/pub.flutter-io.cn/path_provider-1.4.0/ 3 | sqflite=/Users/houzhenpu/flutter/.pub-cache/hosted/pub.flutter-io.cn/sqflite-1.1.7+1/ 4 | webview_flutter=/Users/houzhenpu/flutter/.pub-cache/hosted/pub.flutter-io.cn/webview_flutter-0.3.15+1/ 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | 7 | .idea/ 8 | .vagrant/ 9 | .sconsign.dblite 10 | .svn/ 11 | 12 | *.swp 13 | profile 14 | 15 | DerivedData/ 16 | 17 | .generated/ 18 | 19 | *.pbxuser 20 | *.mode1v3 21 | *.mode2v3 22 | *.perspectivev3 23 | 24 | !default.pbxuser 25 | !default.mode1v3 26 | !default.mode2v3 27 | !default.perspectivev3 28 | 29 | xcuserdata 30 | 31 | *.moved-aside 32 | 33 | *.pyc 34 | *sync/ 35 | Icon? 36 | .tags* 37 | 38 | build/ 39 | .android/ 40 | .ios/ 41 | .flutter-plugins 42 | 43 | android/ 44 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/flutter_fullhtml_textview.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 11 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.metadata: -------------------------------------------------------------------------------- 1 | # This file tracks properties of this Flutter project. 2 | # Used by Flutter tool to assess capabilities and perform upgrades etc. 3 | # 4 | # This file should be version controlled and should not be manually edited. 5 | 6 | version: 7 | revision: 88fa7ea4031f5c86225573e58e5558dc4ea1c251 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # flutter_html_text 2 | 3 | 把html解析为自定义Widget,可直接展示,使用方便快捷 4 | 5 | ## 功能 6 | 1. 图片解析展示 7 | 1. 普通图片展示 8 | 2. 长图切割展示 9 | 3. 图片点击 跳转相册(相册没有在解析中实现,只返回了图片地址list) 10 | 4. 可定义图片间距 11 | 2. 链接点击 12 | 3. webview视频播放(可直接播放,也可自定义处理) 13 | 4. 段落样式支持(居左 居中 居右 引用 段落间距) 14 | 5. 文字样式支持 15 | 1. 字体大小 16 | 2. 字体颜色 17 | 3. a 链接是下划线定义 18 | 4. 链接字体颜色 19 | 5. 行高 20 | 6. 数字标签前缀 21 | 7. 数字标签大小 22 | 8. 点标签前缀 23 | 9. 点标签大小 24 | 10. 引用样式 25 | 11. 引用样式颜色 宽度 margin 相对位置 26 | 12. 字符横向间距 27 | 13. 单词间距 28 | 14. 加粗 倾斜等常见样式 29 | 30 | ## 使用方法 31 | 32 | 同步解析 33 | ``` 34 | String html = 35 | '

这样好处,可能是为了了统一的渲染。加入式,会

'; 36 | 37 | HtmlTextView(html) 38 | ``` 39 | 异步解析 40 | ``` 41 | HtmlParserToWidgetList( 42 | imagePadding: 43 | EdgeInsets.only(top: 2.0, left: 12.0, right: 12.0, bottom: 0.0), 44 | videoPadding: 45 | EdgeInsets.only(top: 4.0, left: 12.0, right: 12.0, bottom: 2.0), 46 | htmlTextStyle: HtmlTextStyle( 47 | height: 1.6, 48 | fontSize: valueOfPt(17), 49 | padding: 50 | EdgeInsets.only(top: 6.0, left: 12.0, right: 12.0, bottom: 8.0), 51 | digitalFontWeight: DigitalFontWeight.strong, 52 | digitalPrefix: ' ', 53 | pointPrefix: ' ', 54 | defaultTextColor: isNightStyle ? Color(0xff898989) : Color(0xff333333), 55 | letterSpacing: 1.0, 56 | ), 57 | ) 58 | /// this.data 是需要解析的html 59 | .asyncParseHtmlToWidgetList(this.data, 60 | onTapCallback: this.onTapCallback, imageList: imageList, id: id) 61 | .then((nodes) { 62 | /// nodes 既为解析出的WidgetList 63 | }); 64 | ``` 65 | 66 | ## 预览 67 | 68 | 效果图如下: 69 | 70 | | ![](./preview/Screenshot_1.jpg) | ![](./preview/Screenshot_2.jpg) | ![](./preview/Screenshot_3.jpg) | ![](./preview/Screenshot_4.jpg) | 71 | | :--------------------------------: | :---------------------------------: | :-------------------------------: | :-------------------------------: | 72 | | ![](./preview/Screenshot_5.jpg) | | | | 73 | 74 | **觉得还可以的话,来个Star、Fork支持一波!有问题欢迎提Issue。** 75 | 76 | -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileChanges/last-build.bin: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileContent/fileContent.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/fileContent/fileContent.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/fileHashes.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/fileHashes/fileHashes.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/fileHashes.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/fileHashes/fileHashes.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/fileHashes/resourceHashesCache.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/gc.properties -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/classAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/javaCompile/classAnalysis.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/jarAnalysis.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/javaCompile/jarAnalysis.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/javaCompile.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/javaCompile/javaCompile.lock -------------------------------------------------------------------------------- /android/.gradle/4.10.2/javaCompile/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/javaCompile/taskHistory.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/taskHistory/taskHistory.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/taskHistory/taskHistory.bin -------------------------------------------------------------------------------- /android/.gradle/4.10.2/taskHistory/taskHistory.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/4.10.2/taskHistory/taskHistory.lock -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/buildOutputCleanup.lock: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/buildOutputCleanup/buildOutputCleanup.lock -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/cache.properties: -------------------------------------------------------------------------------- 1 | #Mon Apr 29 09:50:52 CST 2019 2 | gradle.version=4.10.2 3 | -------------------------------------------------------------------------------- /android/.gradle/buildOutputCleanup/outputFiles.bin: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/buildOutputCleanup/outputFiles.bin -------------------------------------------------------------------------------- /android/.gradle/vcs-1/gc.properties: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/.gradle/vcs-1/gc.properties -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 26 | 27 | android { 28 | compileSdkVersion 28 29 | 30 | lintOptions { 31 | disable 'InvalidPackage' 32 | } 33 | 34 | defaultConfig { 35 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 36 | applicationId "com.example.flutter_app" 37 | minSdkVersion 16 38 | targetSdkVersion 28 39 | versionCode flutterVersionCode.toInteger() 40 | versionName flutterVersionName 41 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 42 | } 43 | 44 | buildTypes { 45 | release { 46 | // TODO: Add your own signing config for the release build. 47 | // Signing with the debug keys for now, so `flutter run --release` works. 48 | signingConfig signingConfigs.debug 49 | } 50 | } 51 | } 52 | 53 | flutter { 54 | source '../..' 55 | } 56 | 57 | dependencies { 58 | implementation 'androidx.appcompat:appcompat:1.0.2' 59 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3' 60 | androidTestImplementation 'androidx.test:runner:1.1.1' 61 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' 62 | testImplementation 'junit:junit:4.12' 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 9 | 13 | 20 | 24 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /android/app/src/main/java/com/example/flutter_app/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example.flutter_app; 2 | 3 | import android.os.Bundle; 4 | import io.flutter.app.FlutterActivity; 5 | import io.flutter.plugins.GeneratedPluginRegistrant; 6 | 7 | public class MainActivity extends FlutterActivity { 8 | @Override 9 | protected void onCreate(Bundle savedInstanceState) { 10 | super.onCreate(savedInstanceState); 11 | GeneratedPluginRegistrant.registerWith(this); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java: -------------------------------------------------------------------------------- 1 | package io.flutter.plugins; 2 | 3 | import io.flutter.plugin.common.PluginRegistry; 4 | import io.github.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin; 5 | import io.flutter.plugins.pathprovider.PathProviderPlugin; 6 | import com.tekartik.sqflite.SqflitePlugin; 7 | import io.flutter.plugins.webviewflutter.WebViewFlutterPlugin; 8 | 9 | /** 10 | * Generated file. Do not edit. 11 | */ 12 | public final class GeneratedPluginRegistrant { 13 | public static void registerWith(PluginRegistry registry) { 14 | if (alreadyRegisteredWith(registry)) { 15 | return; 16 | } 17 | FluttertoastPlugin.registerWith(registry.registrarFor("io.github.ponnamkarthik.toast.fluttertoast.FluttertoastPlugin")); 18 | PathProviderPlugin.registerWith(registry.registrarFor("io.flutter.plugins.pathprovider.PathProviderPlugin")); 19 | SqflitePlugin.registerWith(registry.registrarFor("com.tekartik.sqflite.SqflitePlugin")); 20 | WebViewFlutterPlugin.registerWith(registry.registrarFor("io.flutter.plugins.webviewflutter.WebViewFlutterPlugin")); 21 | } 22 | 23 | private static boolean alreadyRegisteredWith(PluginRegistry registry) { 24 | final String key = GeneratedPluginRegistrant.class.getCanonicalName(); 25 | if (registry.hasPlugin(key)) { 26 | return true; 27 | } 28 | registry.registrarFor(key); 29 | return false; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | google() 4 | jcenter() 5 | } 6 | 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.2.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | google() 15 | jcenter() 16 | } 17 | } 18 | 19 | rootProject.buildDir = '../build' 20 | subprojects { 21 | project.buildDir = "${rootProject.buildDir}/${project.name}" 22 | } 23 | subprojects { 24 | project.evaluationDependsOn(':app') 25 | } 26 | 27 | task clean(type: Delete) { 28 | delete rootProject.buildDir 29 | } 30 | -------------------------------------------------------------------------------- /android/flutter_app_android.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/local.properties: -------------------------------------------------------------------------------- 1 | sdk.dir=/Users/houzhenpu/Library/Android/sdk 2 | flutter.sdk=/Users/houzhenpu/flutter 3 | flutter.buildMode=debug 4 | flutter.versionName=0.0.1 -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def flutterProjectRoot = rootProject.projectDir.parentFile.toPath() 4 | 5 | def plugins = new Properties() 6 | def pluginsFile = new File(flutterProjectRoot.toFile(), '.flutter-plugins') 7 | if (pluginsFile.exists()) { 8 | pluginsFile.withReader('UTF-8') { reader -> plugins.load(reader) } 9 | } 10 | 11 | plugins.each { name, path -> 12 | def pluginDirectory = flutterProjectRoot.resolve(path).resolve('android').toFile() 13 | include ":$name" 14 | project(":$name").projectDir = pluginDirectory 15 | } 16 | -------------------------------------------------------------------------------- /assets/images/3.0x/feed_cell_photo_default_big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/assets/images/3.0x/feed_cell_photo_default_big.png -------------------------------------------------------------------------------- /assets/images/3.0x/image_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/assets/images/3.0x/image_error.png -------------------------------------------------------------------------------- /assets/images/3.0x/long_picture_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/assets/images/3.0x/long_picture_icon.png -------------------------------------------------------------------------------- /assets/images/image_error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/assets/images/image_error.png -------------------------------------------------------------------------------- /assets/images/video_placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/assets/images/video_placeholder.png -------------------------------------------------------------------------------- /flutter_app.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Flutter/Generated.xcconfig: -------------------------------------------------------------------------------- 1 | // This is a generated file; do not edit or check into version control. 2 | FLUTTER_ROOT=C:\Users\HouZhenpu\flutter 3 | FLUTTER_APPLICATION_PATH=D:\Development\flutter_fullhtml_textview 4 | FLUTTER_TARGET=lib\main.dart 5 | FLUTTER_BUILD_DIR=build 6 | SYMROOT=${SOURCE_ROOT}/../build\ios 7 | FLUTTER_FRAMEWORK_DIR=C:\Users\HouZhenpu\flutter\bin\cache\artifacts\engine\ios 8 | FLUTTER_BUILD_NAME=0.0.2 9 | FLUTTER_BUILD_NUMBER=0.0.2 10 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 13 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 14 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; }; 15 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; 16 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */ = {isa = PBXBuildFile; fileRef = 9740EEB21CF90195004384FC /* Debug.xcconfig */; }; 17 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; }; 18 | 97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; }; 19 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 20 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 21 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 22 | /* End PBXBuildFile section */ 23 | 24 | /* Begin PBXCopyFilesBuildPhase section */ 25 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 26 | isa = PBXCopyFilesBuildPhase; 27 | buildActionMask = 2147483647; 28 | dstPath = ""; 29 | dstSubfolderSpec = 10; 30 | files = ( 31 | 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */, 32 | 9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */, 33 | ); 34 | name = "Embed Frameworks"; 35 | runOnlyForDeploymentPostprocessing = 0; 36 | }; 37 | /* End PBXCopyFilesBuildPhase section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 41 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 42 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 43 | 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; 44 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 45 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; 46 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; 47 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 48 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 49 | 9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = ""; }; 50 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; 52 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 53 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 54 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 55 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 64 | 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, 65 | ); 66 | runOnlyForDeploymentPostprocessing = 0; 67 | }; 68 | /* End PBXFrameworksBuildPhase section */ 69 | 70 | /* Begin PBXGroup section */ 71 | 9740EEB11CF90186004384FC /* Flutter */ = { 72 | isa = PBXGroup; 73 | children = ( 74 | 3B80C3931E831B6300D905FE /* App.framework */, 75 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 76 | 9740EEBA1CF902C7004384FC /* Flutter.framework */, 77 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 78 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 79 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 80 | ); 81 | name = Flutter; 82 | sourceTree = ""; 83 | }; 84 | 97C146E51CF9000F007C117D = { 85 | isa = PBXGroup; 86 | children = ( 87 | 9740EEB11CF90186004384FC /* Flutter */, 88 | 97C146F01CF9000F007C117D /* Runner */, 89 | 97C146EF1CF9000F007C117D /* Products */, 90 | CF3B75C9A7D2FA2A4C99F110 /* Frameworks */, 91 | ); 92 | sourceTree = ""; 93 | }; 94 | 97C146EF1CF9000F007C117D /* Products */ = { 95 | isa = PBXGroup; 96 | children = ( 97 | 97C146EE1CF9000F007C117D /* Runner.app */, 98 | ); 99 | name = Products; 100 | sourceTree = ""; 101 | }; 102 | 97C146F01CF9000F007C117D /* Runner */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */, 106 | 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */, 107 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 108 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 109 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 110 | 97C147021CF9000F007C117D /* Info.plist */, 111 | 97C146F11CF9000F007C117D /* Supporting Files */, 112 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 113 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 114 | ); 115 | path = Runner; 116 | sourceTree = ""; 117 | }; 118 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 119 | isa = PBXGroup; 120 | children = ( 121 | 97C146F21CF9000F007C117D /* main.m */, 122 | ); 123 | name = "Supporting Files"; 124 | sourceTree = ""; 125 | }; 126 | /* End PBXGroup section */ 127 | 128 | /* Begin PBXNativeTarget section */ 129 | 97C146ED1CF9000F007C117D /* Runner */ = { 130 | isa = PBXNativeTarget; 131 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 132 | buildPhases = ( 133 | 9740EEB61CF901F6004384FC /* Run Script */, 134 | 97C146EA1CF9000F007C117D /* Sources */, 135 | 97C146EB1CF9000F007C117D /* Frameworks */, 136 | 97C146EC1CF9000F007C117D /* Resources */, 137 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 138 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 139 | ); 140 | buildRules = ( 141 | ); 142 | dependencies = ( 143 | ); 144 | name = Runner; 145 | productName = Runner; 146 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 147 | productType = "com.apple.product-type.application"; 148 | }; 149 | /* End PBXNativeTarget section */ 150 | 151 | /* Begin PBXProject section */ 152 | 97C146E61CF9000F007C117D /* Project object */ = { 153 | isa = PBXProject; 154 | attributes = { 155 | LastUpgradeCheck = 0910; 156 | ORGANIZATIONNAME = "The Chromium Authors"; 157 | TargetAttributes = { 158 | 97C146ED1CF9000F007C117D = { 159 | CreatedOnToolsVersion = 7.3.1; 160 | }; 161 | }; 162 | }; 163 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 164 | compatibilityVersion = "Xcode 3.2"; 165 | developmentRegion = English; 166 | hasScannedForEncodings = 0; 167 | knownRegions = ( 168 | en, 169 | Base, 170 | ); 171 | mainGroup = 97C146E51CF9000F007C117D; 172 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 173 | projectDirPath = ""; 174 | projectRoot = ""; 175 | targets = ( 176 | 97C146ED1CF9000F007C117D /* Runner */, 177 | ); 178 | }; 179 | /* End PBXProject section */ 180 | 181 | /* Begin PBXResourcesBuildPhase section */ 182 | 97C146EC1CF9000F007C117D /* Resources */ = { 183 | isa = PBXResourcesBuildPhase; 184 | buildActionMask = 2147483647; 185 | files = ( 186 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 187 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 188 | 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 189 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 190 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 191 | ); 192 | runOnlyForDeploymentPostprocessing = 0; 193 | }; 194 | /* End PBXResourcesBuildPhase section */ 195 | 196 | /* Begin PBXShellScriptBuildPhase section */ 197 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 198 | isa = PBXShellScriptBuildPhase; 199 | buildActionMask = 2147483647; 200 | files = ( 201 | ); 202 | inputPaths = ( 203 | ); 204 | name = "Thin Binary"; 205 | outputPaths = ( 206 | ); 207 | runOnlyForDeploymentPostprocessing = 0; 208 | shellPath = /bin/sh; 209 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; 210 | }; 211 | 9740EEB61CF901F6004384FC /* Run Script */ = { 212 | isa = PBXShellScriptBuildPhase; 213 | buildActionMask = 2147483647; 214 | files = ( 215 | ); 216 | inputPaths = ( 217 | ); 218 | name = "Run Script"; 219 | outputPaths = ( 220 | ); 221 | runOnlyForDeploymentPostprocessing = 0; 222 | shellPath = /bin/sh; 223 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 224 | }; 225 | /* End PBXShellScriptBuildPhase section */ 226 | 227 | /* Begin PBXSourcesBuildPhase section */ 228 | 97C146EA1CF9000F007C117D /* Sources */ = { 229 | isa = PBXSourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */, 233 | 97C146F31CF9000F007C117D /* main.m in Sources */, 234 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXSourcesBuildPhase section */ 239 | 240 | /* Begin PBXVariantGroup section */ 241 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 242 | isa = PBXVariantGroup; 243 | children = ( 244 | 97C146FB1CF9000F007C117D /* Base */, 245 | ); 246 | name = Main.storyboard; 247 | sourceTree = ""; 248 | }; 249 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 250 | isa = PBXVariantGroup; 251 | children = ( 252 | 97C147001CF9000F007C117D /* Base */, 253 | ); 254 | name = LaunchScreen.storyboard; 255 | sourceTree = ""; 256 | }; 257 | /* End PBXVariantGroup section */ 258 | 259 | /* Begin XCBuildConfiguration section */ 260 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 261 | isa = XCBuildConfiguration; 262 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 263 | buildSettings = { 264 | ALWAYS_SEARCH_USER_PATHS = NO; 265 | CLANG_ANALYZER_NONNULL = YES; 266 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 267 | CLANG_CXX_LIBRARY = "libc++"; 268 | CLANG_ENABLE_MODULES = YES; 269 | CLANG_ENABLE_OBJC_ARC = YES; 270 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 271 | CLANG_WARN_BOOL_CONVERSION = YES; 272 | CLANG_WARN_COMMA = YES; 273 | CLANG_WARN_CONSTANT_CONVERSION = YES; 274 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 275 | CLANG_WARN_EMPTY_BODY = YES; 276 | CLANG_WARN_ENUM_CONVERSION = YES; 277 | CLANG_WARN_INFINITE_RECURSION = YES; 278 | CLANG_WARN_INT_CONVERSION = YES; 279 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 280 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 281 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 282 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 283 | CLANG_WARN_STRICT_PROTOTYPES = YES; 284 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 285 | CLANG_WARN_UNREACHABLE_CODE = YES; 286 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 287 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 288 | COPY_PHASE_STRIP = NO; 289 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 290 | ENABLE_NS_ASSERTIONS = NO; 291 | ENABLE_STRICT_OBJC_MSGSEND = YES; 292 | GCC_C_LANGUAGE_STANDARD = gnu99; 293 | GCC_NO_COMMON_BLOCKS = YES; 294 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 295 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 296 | GCC_WARN_UNDECLARED_SELECTOR = YES; 297 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 298 | GCC_WARN_UNUSED_FUNCTION = YES; 299 | GCC_WARN_UNUSED_VARIABLE = YES; 300 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 301 | MTL_ENABLE_DEBUG_INFO = NO; 302 | SDKROOT = iphoneos; 303 | TARGETED_DEVICE_FAMILY = "1,2"; 304 | VALIDATE_PRODUCT = YES; 305 | }; 306 | name = Profile; 307 | }; 308 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 309 | isa = XCBuildConfiguration; 310 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 311 | buildSettings = { 312 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 313 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 314 | DEVELOPMENT_TEAM = S8QB4VV633; 315 | ENABLE_BITCODE = NO; 316 | FRAMEWORK_SEARCH_PATHS = ( 317 | "$(inherited)", 318 | "$(PROJECT_DIR)/Flutter", 319 | ); 320 | INFOPLIST_FILE = Runner/Info.plist; 321 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 322 | LIBRARY_SEARCH_PATHS = ( 323 | "$(inherited)", 324 | "$(PROJECT_DIR)/Flutter", 325 | ); 326 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterApp; 327 | PRODUCT_NAME = "$(TARGET_NAME)"; 328 | VERSIONING_SYSTEM = "apple-generic"; 329 | }; 330 | name = Profile; 331 | }; 332 | 97C147031CF9000F007C117D /* Debug */ = { 333 | isa = XCBuildConfiguration; 334 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 335 | buildSettings = { 336 | ALWAYS_SEARCH_USER_PATHS = NO; 337 | CLANG_ANALYZER_NONNULL = YES; 338 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 339 | CLANG_CXX_LIBRARY = "libc++"; 340 | CLANG_ENABLE_MODULES = YES; 341 | CLANG_ENABLE_OBJC_ARC = YES; 342 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 343 | CLANG_WARN_BOOL_CONVERSION = YES; 344 | CLANG_WARN_COMMA = YES; 345 | CLANG_WARN_CONSTANT_CONVERSION = YES; 346 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 347 | CLANG_WARN_EMPTY_BODY = YES; 348 | CLANG_WARN_ENUM_CONVERSION = YES; 349 | CLANG_WARN_INFINITE_RECURSION = YES; 350 | CLANG_WARN_INT_CONVERSION = YES; 351 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 352 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 353 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 354 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 355 | CLANG_WARN_STRICT_PROTOTYPES = YES; 356 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 357 | CLANG_WARN_UNREACHABLE_CODE = YES; 358 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 359 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 360 | COPY_PHASE_STRIP = NO; 361 | DEBUG_INFORMATION_FORMAT = dwarf; 362 | ENABLE_STRICT_OBJC_MSGSEND = YES; 363 | ENABLE_TESTABILITY = YES; 364 | GCC_C_LANGUAGE_STANDARD = gnu99; 365 | GCC_DYNAMIC_NO_PIC = NO; 366 | GCC_NO_COMMON_BLOCKS = YES; 367 | GCC_OPTIMIZATION_LEVEL = 0; 368 | GCC_PREPROCESSOR_DEFINITIONS = ( 369 | "DEBUG=1", 370 | "$(inherited)", 371 | ); 372 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 373 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 374 | GCC_WARN_UNDECLARED_SELECTOR = YES; 375 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 376 | GCC_WARN_UNUSED_FUNCTION = YES; 377 | GCC_WARN_UNUSED_VARIABLE = YES; 378 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 379 | MTL_ENABLE_DEBUG_INFO = YES; 380 | ONLY_ACTIVE_ARCH = YES; 381 | SDKROOT = iphoneos; 382 | TARGETED_DEVICE_FAMILY = "1,2"; 383 | }; 384 | name = Debug; 385 | }; 386 | 97C147041CF9000F007C117D /* Release */ = { 387 | isa = XCBuildConfiguration; 388 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 389 | buildSettings = { 390 | ALWAYS_SEARCH_USER_PATHS = NO; 391 | CLANG_ANALYZER_NONNULL = YES; 392 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 393 | CLANG_CXX_LIBRARY = "libc++"; 394 | CLANG_ENABLE_MODULES = YES; 395 | CLANG_ENABLE_OBJC_ARC = YES; 396 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 397 | CLANG_WARN_BOOL_CONVERSION = YES; 398 | CLANG_WARN_COMMA = YES; 399 | CLANG_WARN_CONSTANT_CONVERSION = YES; 400 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 401 | CLANG_WARN_EMPTY_BODY = YES; 402 | CLANG_WARN_ENUM_CONVERSION = YES; 403 | CLANG_WARN_INFINITE_RECURSION = YES; 404 | CLANG_WARN_INT_CONVERSION = YES; 405 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 406 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 407 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 408 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 409 | CLANG_WARN_STRICT_PROTOTYPES = YES; 410 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 411 | CLANG_WARN_UNREACHABLE_CODE = YES; 412 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 413 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 414 | COPY_PHASE_STRIP = NO; 415 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 416 | ENABLE_NS_ASSERTIONS = NO; 417 | ENABLE_STRICT_OBJC_MSGSEND = YES; 418 | GCC_C_LANGUAGE_STANDARD = gnu99; 419 | GCC_NO_COMMON_BLOCKS = YES; 420 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 421 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 422 | GCC_WARN_UNDECLARED_SELECTOR = YES; 423 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 424 | GCC_WARN_UNUSED_FUNCTION = YES; 425 | GCC_WARN_UNUSED_VARIABLE = YES; 426 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 427 | MTL_ENABLE_DEBUG_INFO = NO; 428 | SDKROOT = iphoneos; 429 | TARGETED_DEVICE_FAMILY = "1,2"; 430 | VALIDATE_PRODUCT = YES; 431 | }; 432 | name = Release; 433 | }; 434 | 97C147061CF9000F007C117D /* Debug */ = { 435 | isa = XCBuildConfiguration; 436 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 437 | buildSettings = { 438 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 439 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 440 | ENABLE_BITCODE = NO; 441 | FRAMEWORK_SEARCH_PATHS = ( 442 | "$(inherited)", 443 | "$(PROJECT_DIR)/Flutter", 444 | ); 445 | INFOPLIST_FILE = Runner/Info.plist; 446 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 447 | LIBRARY_SEARCH_PATHS = ( 448 | "$(inherited)", 449 | "$(PROJECT_DIR)/Flutter", 450 | ); 451 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterApp; 452 | PRODUCT_NAME = "$(TARGET_NAME)"; 453 | VERSIONING_SYSTEM = "apple-generic"; 454 | }; 455 | name = Debug; 456 | }; 457 | 97C147071CF9000F007C117D /* Release */ = { 458 | isa = XCBuildConfiguration; 459 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 460 | buildSettings = { 461 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 462 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 463 | ENABLE_BITCODE = NO; 464 | FRAMEWORK_SEARCH_PATHS = ( 465 | "$(inherited)", 466 | "$(PROJECT_DIR)/Flutter", 467 | ); 468 | INFOPLIST_FILE = Runner/Info.plist; 469 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks"; 470 | LIBRARY_SEARCH_PATHS = ( 471 | "$(inherited)", 472 | "$(PROJECT_DIR)/Flutter", 473 | ); 474 | PRODUCT_BUNDLE_IDENTIFIER = com.example.flutterApp; 475 | PRODUCT_NAME = "$(TARGET_NAME)"; 476 | VERSIONING_SYSTEM = "apple-generic"; 477 | }; 478 | name = Release; 479 | }; 480 | /* End XCBuildConfiguration section */ 481 | 482 | /* Begin XCConfigurationList section */ 483 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 484 | isa = XCConfigurationList; 485 | buildConfigurations = ( 486 | 97C147031CF9000F007C117D /* Debug */, 487 | 97C147041CF9000F007C117D /* Release */, 488 | 249021D3217E4FDB00AE95B9 /* Profile */, 489 | ); 490 | defaultConfigurationIsVisible = 0; 491 | defaultConfigurationName = Release; 492 | }; 493 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 494 | isa = XCConfigurationList; 495 | buildConfigurations = ( 496 | 97C147061CF9000F007C117D /* Debug */, 497 | 97C147071CF9000F007C117D /* Release */, 498 | 249021D4217E4FDB00AE95B9 /* Profile */, 499 | ); 500 | defaultConfigurationIsVisible = 0; 501 | defaultConfigurationName = Release; 502 | }; 503 | /* End XCConfigurationList section */ 504 | }; 505 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 506 | } 507 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 40 | 41 | 42 | 43 | 44 | 45 | 56 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 75 | 77 | 83 | 84 | 85 | 86 | 88 | 89 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | 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. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/GeneratedPluginRegistrant.h: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #ifndef GeneratedPluginRegistrant_h 6 | #define GeneratedPluginRegistrant_h 7 | 8 | #import 9 | 10 | @interface GeneratedPluginRegistrant : NSObject 11 | + (void)registerWithRegistry:(NSObject*)registry; 12 | @end 13 | 14 | #endif /* GeneratedPluginRegistrant_h */ 15 | -------------------------------------------------------------------------------- /ios/Runner/GeneratedPluginRegistrant.m: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | #import "GeneratedPluginRegistrant.h" 6 | #import 7 | #import 8 | #import 9 | #import 10 | 11 | @implementation GeneratedPluginRegistrant 12 | 13 | + (void)registerWithRegistry:(NSObject*)registry { 14 | [FluttertoastPlugin registerWithRegistrar:[registry registrarForPlugin:@"FluttertoastPlugin"]]; 15 | [FLTPathProviderPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTPathProviderPlugin"]]; 16 | [SqflitePlugin registerWithRegistrar:[registry registrarForPlugin:@"SqflitePlugin"]]; 17 | [FLTWebViewFlutterPlugin registerWithRegistrar:[registry registrarForPlugin:@"FLTWebViewFlutterPlugin"]]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | flutter_app 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/ImageClipper.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | /// 图片裁剪 6 | class ImageClipper extends CustomPainter { 7 | final ui.Image image; 8 | final double left; 9 | final double top; 10 | final double right; 11 | final double bottom; 12 | final BuildContext context; 13 | 14 | ImageClipper(this.image, this.context, 15 | {this.left = 0.3, this.top = 0.3, this.right = 0.6, this.bottom = 0.6}); 16 | 17 | @override 18 | void paint(Canvas canvas, Size size) { 19 | // TODO: implement paint 20 | Paint paint = Paint(); 21 | canvas.drawImageRect( 22 | image, 23 | Rect.fromLTRB( 24 | left, 25 | top, 26 | image.width * 1.0, 27 | MediaQuery.of(context).size.height * 28 | (image.width * 1.0 / MediaQuery.of(context).size.width)), 29 | Rect.fromLTWH(0, 0, size.width, size.height), 30 | paint); 31 | } 32 | 33 | @override 34 | bool shouldRepaint(CustomPainter oldDelegate) { 35 | // TODO: implement shouldRepaint 36 | return false; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/circle.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'dart:math' as math show sin, pi; 3 | 4 | class SpinKitCircle extends StatefulWidget { 5 | SpinKitCircle({ 6 | Key key, 7 | this.color, 8 | this.size = 50.0, 9 | this.itemBuilder, 10 | this.duration = const Duration(milliseconds: 1200), 11 | this.controller, 12 | }) : assert( 13 | !(itemBuilder is IndexedWidgetBuilder && color is Color) && 14 | !(itemBuilder == null && color == null), 15 | 'You should specify either a itemBuilder or a color'), 16 | assert(size != null), 17 | super(key: key); 18 | 19 | final Color color; 20 | final double size; 21 | final IndexedWidgetBuilder itemBuilder; 22 | final Duration duration; 23 | final AnimationController controller; 24 | 25 | @override 26 | _SpinKitCircleState createState() => _SpinKitCircleState(); 27 | } 28 | 29 | class _SpinKitCircleState extends State 30 | with SingleTickerProviderStateMixin { 31 | AnimationController _controller; 32 | 33 | @override 34 | void initState() { 35 | super.initState(); 36 | _controller = (widget.controller ?? 37 | AnimationController(vsync: this, duration: widget.duration)) 38 | ..repeat(); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | _controller.dispose(); 44 | super.dispose(); 45 | } 46 | 47 | @override 48 | Widget build(BuildContext context) { 49 | return Center( 50 | child: SizedBox.fromSize( 51 | size: Size.square(widget.size), 52 | child: Stack( 53 | children: [ 54 | _circle(1, .0), 55 | _circle(2, -1.1), 56 | _circle(3, -1.0), 57 | _circle(4, -0.9), 58 | _circle(5, -0.8), 59 | _circle(6, -0.7), 60 | _circle(7, -0.6), 61 | _circle(8, -0.5), 62 | _circle(9, -0.4), 63 | _circle(10, -0.3), 64 | _circle(11, -0.2), 65 | _circle(12, -0.1), 66 | ], 67 | ), 68 | ), 69 | ); 70 | } 71 | 72 | Widget _circle(int i, [double delay]) { 73 | final _size = widget.size * 0.15, _position = widget.size * .5; 74 | 75 | return Positioned.fill( 76 | left: _position, 77 | top: _position, 78 | child: Transform( 79 | transform: Matrix4.rotationZ(30.0 * (i - 1) * 0.0174533), 80 | child: Align( 81 | alignment: Alignment.center, 82 | child: ScaleTransition( 83 | scale: DelayTween(begin: 0.0, end: 1.0, delay: delay) 84 | .animate(_controller), 85 | child: _itemBuilder(i - 1, _size), 86 | ), 87 | ), 88 | ), 89 | ); 90 | } 91 | 92 | Widget _itemBuilder(int index, double _size) { 93 | return SizedBox.fromSize( 94 | size: Size.square(_size), 95 | child: widget.itemBuilder != null 96 | ? widget.itemBuilder(context, index) 97 | : DecoratedBox( 98 | decoration: BoxDecoration( 99 | color: widget.color, 100 | shape: BoxShape.circle, 101 | ), 102 | ), 103 | ); 104 | } 105 | } 106 | 107 | class DelayTween extends Tween { 108 | DelayTween({ 109 | double begin, 110 | double end, 111 | this.delay, 112 | }) : super(begin: begin, end: end); 113 | 114 | final double delay; 115 | 116 | @override 117 | double lerp(double t) { 118 | return super.lerp((math.sin((t - delay) * 2 * math.pi) + 1) / 2); 119 | } 120 | 121 | @override 122 | double evaluate(Animation animation) => lerp(animation.value); 123 | } 124 | 125 | class AngleDelayTween extends Tween { 126 | AngleDelayTween({ 127 | double begin, 128 | double end, 129 | this.delay, 130 | }) : super(begin: begin, end: end); 131 | 132 | final double delay; 133 | 134 | @override 135 | double lerp(double t) => super.lerp(math.sin((t - delay) * math.pi * 0.5)); 136 | 137 | @override 138 | double evaluate(Animation animation) => lerp(animation.value); 139 | } 140 | -------------------------------------------------------------------------------- /lib/constants.dart: -------------------------------------------------------------------------------- 1 | const String separator = '-=-='; 2 | 3 | const String endStartPTag = '

'; 4 | 5 | const String lineBreakTag = '\n'; 6 | 7 | const String blockQuote = '

'; -------------------------------------------------------------------------------- /lib/element_parser_to_widget_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'constants.dart'; 4 | import 'flutter_html_text.dart'; 5 | import 'html_text_style.dart'; 6 | import 'network_image.dart'; 7 | import 'package:html/dom.dart' as dom; 8 | 9 | import 'on_tap_data.dart'; 10 | 11 | class ElementParserToWidgetList { 12 | static const EdgeInsetsGeometry _defaultImagePadding = 13 | EdgeInsets.only(top: 4.0, left: 0.0, right: 0.0, bottom: 8.0); 14 | 15 | EdgeInsetsGeometry imagePadding; 16 | 17 | static const EdgeInsetsGeometry _defaultVideoPadding = 18 | EdgeInsets.only(top: 4.0, left: 0.0, right: 0.0, bottom: 8.0); 19 | 20 | EdgeInsetsGeometry videoPadding; 21 | 22 | HtmlTextStyle htmlTextStyle; 23 | 24 | ElementParserToWidgetList( 25 | {this.imagePadding = _defaultImagePadding, 26 | this.videoPadding = _defaultVideoPadding, 27 | this.htmlTextStyle}); 28 | 29 | List parseElementToWidgetList(dom.Element element, 30 | {Function onTapCallback, List imageList, String id}) { 31 | List widgetList = new List(); 32 | if (element.outerHtml.contains(" widgetList, Function onTapCallback, 71 | {List imageList, String id}) { 72 | String outerHtml = e.outerHtml; 73 | 74 | var imgElements = e.getElementsByTagName("img"); 75 | if (e.nodes.length > 1) { 76 | imgElements.forEach((f) { 77 | outerHtml = outerHtml.replaceAll( 78 | '${f.outerHtml}', '

$separator

${f.outerHtml}$separator

'); 79 | }); 80 | } 81 | int imageIndex = 0; 82 | outerHtml.split(separator).forEach((html) { 83 | if (html.contains(" widgetList, Function onTapCallback, 95 | {String id}) { 96 | widgetList.add(new GestureDetector( 97 | onTap: () { 98 | if (onTapCallback != null) { 99 | onTapCallback(OnTapData(imageUrl, type: OnTapType.img, id: id)); 100 | } 101 | }, 102 | child: Center( 103 | child: Container( 104 | padding: imagePadding, 105 | child: NetworkImageClipper( 106 | imageUrl, 107 | id: id, 108 | ), 109 | ), 110 | ), 111 | )); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/flutter_html_text.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/gestures.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import 'constants.dart'; 5 | import 'html_tags.dart'; 6 | import 'html_text_style.dart'; 7 | import 'on_tap_data.dart'; 8 | 9 | // ignore: must_be_immutable 10 | class HtmlText extends StatelessWidget { 11 | String data; 12 | 13 | final Function onTapCallback; 14 | 15 | HtmlTextStyle htmlTextStyle; 16 | 17 | Container _dataContainer; 18 | 19 | HtmlText(this.data, {this.onTapCallback, this.htmlTextStyle}); 20 | 21 | @override 22 | Widget build(BuildContext context) { 23 | if (_dataContainer == null) { 24 | HtmlParser parser = new HtmlParser(htmlTextStyle: this.htmlTextStyle); 25 | this.data = this 26 | .data 27 | .replaceAll(' ', ' ') 28 | .replaceAll('>', '>') 29 | .replaceAll('<', '>') 30 | .replaceAll('&', '&'); 31 | _dataContainer = Container( 32 | padding: htmlTextStyle?.padding, 33 | child: this.data.startsWith(blockQuote) 34 | ? createBlockQuote(parser, context) 35 | : _createRichText(parser, context), 36 | ); 37 | } 38 | return _dataContainer; 39 | } 40 | 41 | Stack createBlockQuote(HtmlParser parser, BuildContext context) { 42 | this.data = this.data.replaceAll(endStartPTag, lineBreakTag); 43 | return Stack( 44 | fit: StackFit.loose, 45 | children: [ 46 | Container( 47 | margin: htmlTextStyle.blockQuotTextMargin, 48 | child: _createRichText(parser, context), 49 | ), 50 | Positioned( 51 | top: 1, 52 | bottom: 1, 53 | child: Container( 54 | color: htmlTextStyle.blockQuoteColor, 55 | width: htmlTextStyle.blockQuoteWidth, 56 | alignment: Alignment.topLeft, 57 | margin: htmlTextStyle.blockQuotMargin, 58 | ), 59 | ), 60 | ], 61 | ); 62 | } 63 | 64 | RichText _createRichText(HtmlParser parser, BuildContext context) { 65 | return RichText( 66 | text: this._stackToTextSpan(parser.parse(this.data), context), 67 | softWrap: true, 68 | textAlign: parser.textAlign, 69 | ); 70 | } 71 | 72 | TextSpan _stackToTextSpan(List nodes, BuildContext context) { 73 | List children = []; 74 | for (int i = 0; i < nodes.length; i++) { 75 | children.add(_textSpan(nodes[i])); 76 | } 77 | return new TextSpan( 78 | text: '', 79 | style: DefaultTextStyle.of(context).style, 80 | children: children); 81 | } 82 | 83 | TextSpan _textSpan(Map node) { 84 | TextSpan span = new TextSpan( 85 | text: node['text'], 86 | style: node['style'], 87 | recognizer: TapGestureRecognizer() 88 | ..onTap = () { 89 | if (onTapCallback != null && node['href'] != '') { 90 | onTapCallback(OnTapData(node['href'])); 91 | } 92 | }); 93 | return span; 94 | } 95 | } 96 | 97 | class HtmlParser { 98 | HtmlTextStyle htmlTextStyle; 99 | 100 | HtmlParser({this.htmlTextStyle}); 101 | 102 | List _stack = []; 103 | List _spanStyle = []; 104 | List _result = []; 105 | Map _tag; 106 | TextAlign textAlign = TextAlign.left; 107 | 108 | List parse(String html) { 109 | String last = html; 110 | Match match; 111 | int index; 112 | bool isTextPart; 113 | int tagIndex = 1; 114 | bool isAppendStartTag = false; 115 | htmlTextStyle = htmlTextStyle ?? HtmlTextStyle(); 116 | while (html.length > 0) { 117 | isTextPart = true; 118 | if (this._getStackLastItem() == null || 119 | !specialTags.contains(this._getStackLastItem())) { 120 | if (html.indexOf(''); 122 | if (index >= 0) { 123 | html = html.substring(index + 3); 124 | isTextPart = false; 125 | } 126 | } else if (html.indexOf(']*>'); 161 | html = html.replaceAllMapped(re, (Match match) { 162 | String text = match[0] 163 | ..replaceAll(new RegExp(''), '\$1') 164 | ..replaceAll(new RegExp(''), '\$1'); 165 | this._appendNode(text); 166 | return ''; 167 | }); 168 | this._parseEndTag(this._getStackLastItem()); 169 | } 170 | if (html == last) { 171 | //throw 'Parse Error: ' + html;出现不可解析标签直接显示 172 | _appendNode(html); 173 | html = ''; 174 | } 175 | last = html; 176 | } 177 | this._parseEndTag(); 178 | List result = this._result; 179 | this._stack = []; 180 | this._result = []; 181 | this._spanStyle = []; 182 | return result; 183 | } 184 | 185 | String _createDigitalTags(int tagIndex, String text) { 186 | this._tag = null; 187 | _appendTag('${htmlTextStyle.digitalFontWeight}', 188 | {'style': 'font-size:${htmlTextStyle.digitalFontSize}'}, false); 189 | 190 | this._appendNode( 191 | '${htmlTextStyle.pointPrefix ?? ""}' + tagIndex.toString() + '. '); 192 | text = text + lineBreakTag; 193 | return text; 194 | } 195 | 196 | String _createPointTags(String text) { 197 | this._tag = null; 198 | _appendTag( 199 | 'w900', {'style': 'font-size:${htmlTextStyle.pointFontSize}'}, false); 200 | this._appendNode('${htmlTextStyle.pointPrefix ?? ""}• '); 201 | text = text + lineBreakTag; 202 | return text; 203 | } 204 | 205 | void _parseStartTag(String tag, String tagName, String rest, int unary, 206 | bool isAppendStartTag) { 207 | tagName = tagName.toLowerCase(); 208 | if (blockTags.contains(tagName)) { 209 | while (this._getStackLastItem() != null && 210 | inlineTags.contains(this._getStackLastItem())) { 211 | this._parseEndTag(this._getStackLastItem()); 212 | } 213 | } 214 | if (closeSelfTags.contains(tagName) && 215 | this._getStackLastItem() == tagName) { 216 | this._parseEndTag(tagName); 217 | } 218 | if (emptyTags.contains(tagName)) { 219 | unary = 1; 220 | } 221 | if (unary == 0) { 222 | this._stack.add(tagName); 223 | } 224 | Map attrs = {}; 225 | Iterable matches = attrTag.allMatches(rest); 226 | 227 | if (matches != null) { 228 | for (Match match in matches) { 229 | String attribute = match[1]; 230 | String value; 231 | 232 | if (match[2] != null) { 233 | value = match[2]; 234 | } else if (match[3] != null) { 235 | value = match[3]; 236 | } else if (match[4] != null) { 237 | value = match[4]; 238 | } else if (fillAttrs.contains(attribute) != null) { 239 | value = attribute; 240 | } 241 | attrs[attribute] = value; 242 | if (attribute.endsWith('style') && tagName.endsWith('span')) { 243 | this._spanStyle.add(value); 244 | } 245 | } 246 | } 247 | this._appendTag(tagName, attrs, isAppendStartTag); 248 | } 249 | 250 | void _parseEndTag([String tagName]) { 251 | int pos; 252 | if (tagName == null) { 253 | pos = 0; 254 | } else { 255 | if (tagName.contains('span') && this._spanStyle.length > 0) { 256 | this._spanStyle?.removeLast(); 257 | } 258 | for (pos = this._stack.length - 1; pos >= 0; pos--) { 259 | if (this._stack[pos] == tagName || tagName.contains(this._stack[pos])) { 260 | this._stack.remove(tagName); 261 | break; 262 | } 263 | } 264 | } 265 | if (pos >= 0) { 266 | this._stack.removeRange(pos, this._stack.length); 267 | } 268 | } 269 | 270 | TextStyle _parseStyle(List tags, Map attrs) { 271 | Iterable matches; 272 | String spanStyle = ''; 273 | if (this._spanStyle.isNotEmpty) { 274 | spanStyle = this._spanStyle.last.toString(); 275 | } 276 | String style = '$spanStyle${attrs['style']}'; 277 | String param; 278 | String value; 279 | 280 | FontWeight fontWeight = FontWeight.normal; 281 | FontStyle fontStyle = FontStyle.normal; 282 | TextDecoration textDecoration = TextDecoration.none; 283 | Color backgroundColor; 284 | htmlTextStyle.color = htmlTextStyle.defaultTextColor; 285 | double fontSize = htmlTextStyle.fontSize; 286 | tags.forEach((tag) { 287 | switch (tag) { 288 | case 'h1': 289 | fontSize = 32.0; 290 | break; 291 | case 'h2': 292 | fontSize = 24.0; 293 | break; 294 | case 'h3': 295 | fontSize = 20.8; 296 | break; 297 | case 'h4': 298 | fontSize = 16.0; 299 | break; 300 | case 'h5': 301 | fontSize = 12.8; 302 | break; 303 | case 'h6': 304 | fontSize = 11.2; 305 | break; 306 | case 'a': 307 | textDecoration = htmlTextStyle.hrefTextDecoration; 308 | htmlTextStyle.color = htmlTextStyle.hrefTextColor; 309 | break; 310 | case 'b': 311 | case 'strong': 312 | fontWeight = FontWeight.bold; 313 | break; 314 | case 'w900': 315 | fontWeight = FontWeight.w900; 316 | break; 317 | case 'i': 318 | case 'em': 319 | fontStyle = FontStyle.italic; 320 | break; 321 | case 'u': 322 | textDecoration = TextDecoration.underline; 323 | break; 324 | } 325 | }); 326 | 327 | if (style != null) { 328 | matches = styleTag.allMatches(style); 329 | 330 | for (Match match in matches) { 331 | param = match[1].trim(); 332 | value = match[2].trim(); 333 | switch (param) { 334 | case 'color': 335 | if (colorTag.hasMatch(value)) { 336 | value = value.replaceAll('#', '').trim(); 337 | htmlTextStyle.color = new Color(int.parse('0xFF' + value)); 338 | } 339 | break; 340 | case 'font-weight': 341 | fontWeight = 342 | (value == 'bold') ? FontWeight.bold : FontWeight.normal; 343 | break; 344 | case 'font-style': 345 | fontStyle = 346 | (value == 'italic') ? FontStyle.italic : FontStyle.normal; 347 | break; 348 | case 'text-decoration': 349 | textDecoration = (value == 'underline') 350 | ? TextDecoration.underline 351 | : TextDecoration.none; 352 | break; 353 | case 'text-align': 354 | if (value.endsWith('center')) { 355 | textAlign = TextAlign.center; 356 | } else if (value.endsWith('right')) { 357 | textAlign = TextAlign.right; 358 | } else { 359 | textAlign = TextAlign.left; 360 | } 361 | break; 362 | case 'background-color': 363 | if (colorTag.hasMatch(value)) { 364 | value = value.replaceAll('#', '').trim(); 365 | backgroundColor = new Color(int.parse('0xFF' + value)); 366 | } 367 | break; 368 | case 'font-size': 369 | fontSize = double.parse( 370 | value?.replaceAll(RegExp('([^.0-9])'), '') == '' 371 | ? '${htmlTextStyle.fontSize}' 372 | : value?.replaceAll(RegExp('([^.0-9])'), '')); 373 | break; 374 | } 375 | } 376 | } 377 | return TextStyle( 378 | color: htmlTextStyle.color, 379 | fontWeight: fontWeight, 380 | fontStyle: fontStyle, 381 | decoration: textDecoration, 382 | fontSize: fontSize, 383 | height: htmlTextStyle.height, 384 | backgroundColor: backgroundColor, 385 | letterSpacing: htmlTextStyle.letterSpacing, 386 | wordSpacing: htmlTextStyle.wordSpacing, 387 | ); 388 | } 389 | 390 | void _appendTag(String tag, Map attrs, bool isAppendStartTag) { 391 | if (this._tag == null) { 392 | List tags = [tag]; 393 | this._tag = {'label': tags, 'attrs': attrs}; 394 | } 395 | if (isAppendStartTag) { 396 | (this._tag['label'] as List).add(tag); 397 | } 398 | if (attrs.length > 0) { 399 | this._tag['attrs'] = attrs; 400 | } 401 | } 402 | 403 | void _appendNode(String text) { 404 | if (this._tag == null) { 405 | List tags = ['p']; 406 | this._tag = {'label': tags, 'attrs': {}}; 407 | } 408 | if (this._stack != null) { 409 | (this._tag['label'] as List).addAll(this._stack); 410 | } 411 | this._tag['text'] = text; 412 | this._tag['style'] = 413 | this._parseStyle(this._tag['label'], this._tag['attrs']); 414 | this._tag['href'] = 415 | (this._tag['attrs']['href'] != null) ? this._tag['attrs']['href'] : ''; 416 | 417 | this._result.add(this._tag); 418 | this._tag.remove('attrs'); 419 | this._tag = null; 420 | } 421 | 422 | String _getStackLastItem() { 423 | if (this._stack.length <= 0) { 424 | return null; 425 | } 426 | 427 | return this._stack[this._stack.length - 1]; 428 | } 429 | } 430 | -------------------------------------------------------------------------------- /lib/flutter_html_textview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'html_parser_to_widget_list.dart'; 4 | import 'html_text_style.dart'; 5 | import 'on_tap_data.dart'; 6 | 7 | class HtmlTextView extends StatelessWidget { 8 | final String data; 9 | 10 | HtmlTextView(this.data, {EdgeInsetsGeometry padding}); 11 | 12 | final Function onTapCallback = (data) { 13 | if (data is OnTapData) { 14 | debugPrint('data.type--->${data.type}'); 15 | if (data.type == OnTapType.href) { 16 | } else if (data.type == OnTapType.img) { 17 | } else if (data.type == OnTapType.video) { 18 | } 19 | } 20 | }; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | List nodes = HtmlParserToWidgetList( 25 | imagePadding: 26 | EdgeInsets.only(top: 12.0, left: 0.0, right: 0.0, bottom: 12.0), 27 | videoPadding: 28 | EdgeInsets.only(top: 12.0, left: 0.0, right: 0.0, bottom: 12.0), 29 | htmlTextStyle: HtmlTextStyle( 30 | hrefTextDecoration: TextDecoration.underline, 31 | hrefTextColor: Colors.amber, 32 | height: 1.4, 33 | fontSize: 15, 34 | padding: EdgeInsets.only(top: 0.0, left: 0.0, right: 0.0, bottom: 4.0), 35 | digitalFontWeight: DigitalFontWeight.strong, 36 | digitalPrefix: ' ', 37 | pointPrefix: ' ', 38 | blockQuoteColor: Colors.teal, 39 | blockQuoteWidth: 5, 40 | blockQuotMargin: EdgeInsets.only(left: 10, right: 10), 41 | blockQuotTextMargin: EdgeInsets.only(left: 25), 42 | ), 43 | ).parseHtmlToWidgetList(this.data, onTapCallback: this.onTapCallback); 44 | 45 | return new Container( 46 | padding: const EdgeInsets.all(0.0), 47 | child: new Column( 48 | crossAxisAlignment: CrossAxisAlignment.start, 49 | children: nodes, 50 | )); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /lib/html_async_textview.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:fluttertoast/fluttertoast.dart'; 3 | import 'package:html_text/html_parser_to_widget_list.dart'; 4 | import 'package:html_text/html_text_style.dart'; 5 | import 'package:html_text/on_tap_data.dart'; 6 | 7 | import 'image.dart'; 8 | 9 | class AsyncHtmlTextView { 10 | final String data; 11 | 12 | AsyncHtmlTextView(this.data, {EdgeInsetsGeometry padding}); 13 | 14 | final Function onTapCallback = (data) { 15 | if (data is OnTapData) { 16 | if (data.type == OnTapType.href) { 17 | Fluttertoast.showToast( 18 | msg: data.url, 19 | gravity: ToastGravity.CENTER, 20 | ); 21 | } else if (data.type == OnTapType.img) { 22 | Fluttertoast.showToast( 23 | msg: 24 | 'currentImageUrl: ${data.url} imageList: ${getArticleImageList(data.id).toString()}', 25 | gravity: ToastGravity.CENTER, 26 | ); 27 | } else if (data.type == OnTapType.video) { 28 | Fluttertoast.showToast( 29 | msg: data.url, 30 | gravity: ToastGravity.CENTER, 31 | ); 32 | } 33 | } 34 | }; 35 | 36 | build(Function onParserCallback, {List imageList, String id}) { 37 | HtmlParserToWidgetList( 38 | imagePadding: 39 | EdgeInsets.only(top: 2.0, left: 12.0, right: 12.0, bottom: 0.0), 40 | videoPadding: 41 | EdgeInsets.only(top: 4.0, left: 12.0, right: 12.0, bottom: 2.0), 42 | htmlTextStyle: HtmlTextStyle( 43 | height: 1.6, 44 | fontSize: 17, 45 | padding: 46 | EdgeInsets.only(top: 6.0, left: 12.0, right: 12.0, bottom: 8.0), 47 | digitalFontWeight: DigitalFontWeight.strong, 48 | digitalPrefix: ' ', 49 | pointPrefix: ' ', 50 | defaultTextColor: Color(0xff333333), 51 | letterSpacing: 1.0, 52 | ), 53 | isInPackage: false, 54 | ) 55 | .asyncParseHtmlToWidgetList(this.data, 56 | onTapCallback: this.onTapCallback, imageList: imageList, id: id) 57 | .then((nodes) { 58 | if (onParserCallback != null) { 59 | onParserCallback(nodes); 60 | } 61 | }); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/html_parser_to_element_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:html/dom.dart' as dom; 2 | import 'package:html/parser.dart' show parse; 3 | 4 | class HtmlParserToElementList { 5 | HtmlParserToElementList(); 6 | 7 | Future> asyncParseHtmlToElementList(String html) async { 8 | return await _getElementListFromHtml(html); 9 | } 10 | 11 | Future> _getElementListFromHtml(String html) async { 12 | return _parseHtmlToElementList(html); 13 | } 14 | 15 | List _parseHtmlToElementList(String html) { 16 | return parse(html).body.children; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/html_parser_to_widget_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:html/dom.dart' as dom; 3 | import 'package:html/parser.dart' show parse; 4 | 5 | import 'constants.dart'; 6 | import 'flutter_html_text.dart'; 7 | import 'html_text_style.dart'; 8 | import 'network_image.dart'; 9 | import 'on_tap_data.dart'; 10 | import 'web_view_page.dart'; 11 | 12 | class HtmlParserToWidgetList { 13 | static const EdgeInsetsGeometry _defaultImagePadding = 14 | EdgeInsets.only(top: 4.0, left: 0.0, right: 0.0, bottom: 8.0); 15 | 16 | EdgeInsetsGeometry imagePadding; 17 | 18 | static const EdgeInsetsGeometry _defaultVideoPadding = 19 | EdgeInsets.only(top: 4.0, left: 0.0, right: 0.0, bottom: 8.0); 20 | 21 | EdgeInsetsGeometry videoPadding; 22 | 23 | HtmlTextStyle htmlTextStyle; 24 | 25 | bool isInPackage = true; 26 | bool loadingOffstage; 27 | 28 | HtmlParserToWidgetList( 29 | {this.imagePadding = _defaultImagePadding, 30 | this.videoPadding = _defaultVideoPadding, 31 | this.htmlTextStyle, 32 | this.isInPackage = true, 33 | this.loadingOffstage = true}); 34 | 35 | Future> asyncParseHtmlToWidgetList(String html, 36 | {Function onTapCallback, 37 | List imageList, 38 | String id, 39 | BuildContext context}) async { 40 | return await _getWidgetListFromHtml(html, 41 | onTapCallback: onTapCallback, 42 | imageList: imageList, 43 | id: id, 44 | context: context); 45 | } 46 | 47 | Future> _getWidgetListFromHtml(String html, 48 | {Function onTapCallback, 49 | List imageList, 50 | String id, 51 | BuildContext context}) async { 52 | return parseHtmlToWidgetList( 53 | html, 54 | onTapCallback: onTapCallback, 55 | imageList: imageList, 56 | id: id, 57 | context: context, 58 | ); 59 | } 60 | 61 | List parseHtmlToWidgetList(String html, 62 | {Function onTapCallback, 63 | List imageList, 64 | String id, 65 | BuildContext context}) { 66 | List widgetList = new List(); 67 | if (html == null) { 68 | widgetList.add(Container()); 69 | return widgetList; 70 | } 71 | List docBodyChildren = parse(html).body.children; 72 | if (docBodyChildren.length == 0) { 73 | widgetList.add(Container( 74 | padding: htmlTextStyle.padding, 75 | child: Text(html, 76 | style: TextStyle( 77 | height: htmlTextStyle.height, 78 | letterSpacing: htmlTextStyle.letterSpacing, 79 | fontSize: htmlTextStyle.fontSize)), 80 | )); 81 | } else { 82 | docBodyChildren.forEach((e) { 83 | if (e.outerHtml.contains(" widgetList, Function onTapCallback, 114 | {List imageList, String id, BuildContext context}) { 115 | String outerHtml = e.outerHtml; 116 | 117 | var imgElements = e.getElementsByTagName("img"); 118 | if (imgElements.length == 0) { 119 | imgElements.add(e); 120 | } 121 | if (e.nodes.length > 1) { 122 | imgElements.forEach((f) { 123 | outerHtml = outerHtml.replaceAll( 124 | '${f.outerHtml}', '

$separator

${f.outerHtml}$separator

'); 125 | }); 126 | } 127 | int imageIndex = 0; 128 | outerHtml.split(separator).forEach((html) { 129 | if (html.contains("

'.endsWith(tempHtml)) { 137 | widgetList.add(_createHtmlText(tempHtml, onTapCallback)); 138 | } 139 | } 140 | }); 141 | } 142 | 143 | void _createImage( 144 | String imageUrl, 145 | List widgetList, 146 | Function onTapCallback, { 147 | String id, 148 | BuildContext context, 149 | }) { 150 | widgetList.add(new GestureDetector( 151 | onTap: () { 152 | if (onTapCallback != null) { 153 | onTapCallback(OnTapData(imageUrl, 154 | type: OnTapType.img, id: id, context: context)); 155 | } 156 | }, 157 | onLongPress: () { 158 | if (onTapCallback != null) { 159 | onTapCallback(OnTapData(imageUrl, 160 | type: OnTapType.img, 161 | id: id, 162 | isOnLongPress: true, 163 | context: context)); 164 | } 165 | }, 166 | child: Center( 167 | child: Container( 168 | padding: imagePadding, 169 | child: NetworkImageClipper( 170 | imageUrl, 171 | id: id, 172 | isInPackage: isInPackage, 173 | loadingOffstage: loadingOffstage, 174 | ), 175 | ), 176 | ), 177 | )); 178 | } 179 | } 180 | -------------------------------------------------------------------------------- /lib/html_tags.dart: -------------------------------------------------------------------------------- 1 | final List emptyTags = const [ 2 | 'area', 3 | 'base', 4 | 'basefont', 5 | 'br', 6 | 'col', 7 | 'frame', 8 | 'hr', 9 | 'img', 10 | 'input', 11 | 'isindex', 12 | 'link', 13 | 'meta', 14 | 'param', 15 | 'embed' 16 | ]; 17 | 18 | final List blockTags = const [ 19 | 'address', 20 | 'applet', 21 | 'blockquote', 22 | 'button', 23 | 'center', 24 | 'dd', 25 | 'del', 26 | 'dir', 27 | 'div', 28 | 'dl', 29 | 'dt', 30 | 'fieldset', 31 | 'form', 32 | 'frameset', 33 | 'hr', 34 | 'iframe', 35 | 'ins', 36 | 'isindex', 37 | 'li', 38 | 'map', 39 | 'menu', 40 | 'noframes', 41 | 'noscript', 42 | 'object', 43 | 'ol', 44 | 'p', 45 | 'pre', 46 | 'script', 47 | 'table', 48 | 'tbody', 49 | 'td', 50 | 'tfoot', 51 | 'th', 52 | 'thead', 53 | 'tr', 54 | 'ul' 55 | ]; 56 | final List inlineTags = const [ 57 | 'a', 58 | 'abbr', 59 | 'acronym', 60 | 'applet', 61 | 'b', 62 | 'basefont', 63 | 'bdo', 64 | 'big', 65 | 'br', 66 | 'button', 67 | 'cite', 68 | 'code', 69 | 'del', 70 | 'dfn', 71 | 'em', 72 | 'font', 73 | 'i', 74 | 'iframe', 75 | 'img', 76 | 'input', 77 | 'ins', 78 | 'kbd', 79 | 'label', 80 | 'map', 81 | 'object', 82 | 'q', 83 | 's', 84 | 'samp', 85 | 'script', 86 | 'select', 87 | 'small', 88 | 'span', 89 | 'strike', 90 | 'strong', 91 | 'sub', 92 | 'sup', 93 | 'textarea', 94 | 'tt', 95 | 'u', 96 | 'var' 97 | ]; 98 | final List closeSelfTags = const [ 99 | 'colgroup', 100 | 'dd', 101 | 'dt', 102 | 'li', 103 | 'options', 104 | 'p', 105 | 'td', 106 | 'tfoot', 107 | 'th', 108 | 'thead', 109 | 'tr' 110 | ]; 111 | final List fillAttrs = const [ 112 | 'checked', 113 | 'compact', 114 | 'declare', 115 | 'defer', 116 | 'disabled', 117 | 'ismap', 118 | 'multiple', 119 | 'nohref', 120 | 'noresize', 121 | 'noshade', 122 | 'nowrap', 123 | 'readonly', 124 | 'selected' 125 | ]; 126 | final List specialTags = const ['script', 'style']; 127 | 128 | final String ulTag = '

'; 129 | final String olTag = '

'; 130 | 131 | final RegExp startTag = new RegExp( 132 | r'^<([-A-Za-z0-9_]+)((?:\s+\w+(?:\s*=\s*(?:(?:"[^"]*")' + 133 | "|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>"); 134 | final RegExp endTag = new RegExp("^<\/([-A-Za-z0-9_]+)[^>]*>"); 135 | final RegExp attrTag = new RegExp( 136 | r'([-A-Za-z0-9_]+)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")' + 137 | r"|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?"); 138 | final RegExp styleTag = new RegExp(r'([a-zA-Z\-]+)\s*:\s*([^;]*)'); 139 | final RegExp colorTag = new RegExp(r'^#([a-fA-F0-9]{6})$'); 140 | -------------------------------------------------------------------------------- /lib/html_text_style.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class HtmlTextStyle { 6 | ///字体大小 7 | double fontSize; 8 | 9 | /// a 链接 A linear decoration to draw near the text. 10 | TextDecoration hrefTextDecoration; 11 | 12 | ///a 链接字体颜色 13 | Color hrefTextColor; 14 | 15 | ///a 链接字体默认颜色 16 | static const Color defaultHrefTextColor = Color(0xFF5193ad); 17 | 18 | ///行高 19 | double height; 20 | 21 | ///字体颜色 22 | Color color; 23 | 24 | ///默认字体颜色 25 | Color defaultTextColor = Color(0xFF000000); 26 | 27 | ///默认字体大小 28 | double defaultFontSize = 14.0; 29 | 30 | ///段落padding 31 | EdgeInsetsGeometry padding; 32 | 33 | ///默认段落padding 34 | static const EdgeInsetsGeometry defaultPadding = 35 | EdgeInsets.only(top: 0.0, left: 0.0, right: 0.0, bottom: 4.0); 36 | 37 | ///数字标签大小 38 | double digitalFontSize = 14.0; 39 | 40 | ///点标签大小 41 | double pointFontSize = 14.0; 42 | 43 | ///点标签字体样式,默认普通样式 可以设置为 DigitalFontWeight.strong 44 | String digitalFontWeight; 45 | 46 | ///数字标签前缀 47 | String digitalPrefix; 48 | 49 | ///点标签前缀 50 | String pointPrefix; 51 | 52 | ///引用线条颜色 53 | Color blockQuoteColor; 54 | 55 | ///引用线条默认颜色 56 | static const Color defaultBlockQuoteColor = Color(0xFF2576a5); 57 | 58 | ///引用线条宽度 59 | double blockQuoteWidth; 60 | 61 | ///引用线条Margin 62 | EdgeInsetsGeometry blockQuotMargin; 63 | 64 | ///引用线条默认Margin 65 | static const EdgeInsetsGeometry defaultBlockQuotMargin = 66 | EdgeInsets.only(left: 3, right: 12); 67 | 68 | ///引用线条文字Margin 和 blockQuotMargin,blockQuoteWidth结合绘制线条和文字的相对位置 69 | EdgeInsetsGeometry blockQuotTextMargin; 70 | 71 | ///引用线条文字默认Margin 72 | static const EdgeInsetsGeometry defaultBlockQuotTextMargin = 73 | EdgeInsets.only(left: 15); 74 | 75 | /// 字符间距 就是单个字母或者汉字之间的间隔,可以是负数 76 | double letterSpacing; 77 | 78 | /// 字间距 句字之间的间距 79 | double wordSpacing; 80 | 81 | HtmlTextStyle({ 82 | this.fontSize = 14.0, 83 | this.hrefTextDecoration = TextDecoration.none, 84 | this.hrefTextColor = defaultHrefTextColor, 85 | this.height = 1.4, 86 | this.defaultTextColor, 87 | this.digitalFontSize = 14.0, 88 | this.pointFontSize = 14.0, 89 | this.digitalFontWeight, 90 | this.digitalPrefix, 91 | this.pointPrefix, 92 | this.blockQuoteColor = defaultBlockQuoteColor, 93 | this.blockQuoteWidth = 3, 94 | this.blockQuotMargin = defaultBlockQuotMargin, 95 | this.blockQuotTextMargin = defaultBlockQuotTextMargin, 96 | this.padding = defaultPadding, 97 | this.letterSpacing, 98 | this.wordSpacing, 99 | }); 100 | } 101 | 102 | class DigitalFontWeight { 103 | static const String strong = 'strong'; 104 | } 105 | -------------------------------------------------------------------------------- /lib/image.dart: -------------------------------------------------------------------------------- 1 | var articleImageMap = Map>(); 2 | 3 | var articleImageFilePathMap = Map>(); 4 | 5 | //清空 6 | void removeImageFromArticleImageMap(String articleId) { 7 | if (articleImageMap.containsKey(articleId)) { 8 | articleImageMap.remove(articleId); 9 | } 10 | } 11 | 12 | void addImageToArticleImageMap(String articleId, List imageList) { 13 | if (articleImageMap.containsKey(articleId)) { 14 | articleImageMap.update(articleId, (value) => value..addAll(imageList)); 15 | } else { 16 | articleImageMap.putIfAbsent(articleId, () => imageList); 17 | } 18 | } 19 | 20 | void insertImageToArticleImageMap( 21 | String articleId, List imageList, int index) { 22 | if (articleImageMap.containsKey(articleId)) { 23 | articleImageMap.update( 24 | articleId, (value) => value..insertAll(index, imageList)); 25 | } else { 26 | articleImageMap.putIfAbsent(articleId, () => imageList); 27 | } 28 | } 29 | 30 | List getArticleImageList(String articleId) { 31 | return articleImageMap.putIfAbsent(articleId, () => []); 32 | } 33 | 34 | void addImageToArticleImageFilePathMap( 35 | String articleId, String imageUrl, String filePath) { 36 | if (articleImageFilePathMap.containsKey(articleId)) { 37 | articleImageFilePathMap.update( 38 | articleId, (value) => value..putIfAbsent(imageUrl, () => filePath)); 39 | } else { 40 | articleImageFilePathMap.putIfAbsent(articleId, () => {imageUrl: filePath}); 41 | } 42 | } 43 | 44 | Map getArticleImageUrlFilePathList(String articleId) { 45 | return articleImageFilePathMap[articleId]; 46 | } 47 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'flutter_html_textview.dart'; 4 | import 'html_async_textview.dart'; 5 | import 'image.dart'; 6 | 7 | void main() => runApp(MyApp()); 8 | 9 | class MyApp extends StatelessWidget { 10 | @override 11 | Widget build(BuildContext context) { 12 | return MaterialApp( 13 | home: HtmlTest(), 14 | ); 15 | } 16 | } 17 | 18 | class HtmlTest extends StatefulWidget { 19 | @override 20 | State createState() { 21 | // TODO: implement createState 22 | return SingleChildScrollViewTestRouteState(); 23 | } 24 | } 25 | 26 | // ignore: must_be_immutable 27 | class SingleChildScrollViewTestRouteState extends State { 28 | String html = 29 | '

维生素A报价继续向上 月涨幅超20%

A股上市公司中,新和成002001目前公司维生素A粉产能10000吨。浙江医药600216目前公司维生素A粉产能5000吨。

  • box constraints有人也翻译有人也翻译有人也翻译有人也翻译有人也翻译

  • 为盒约束、箱约束

  • ,我个人还是。

这样做的好处,了统一的渲染。加入样式,会让布局复杂不少,在渲染层面会降低很多性能。因此,Flutter在大的方向上,加入不同类型的布局widget。在小的方向上,只给出很少的定制化的东西,将布局限定在有限的范围内,在完成布局的同时,让整个渲染能够统一,加快了更新和渲染居中结束 居中

但是,缺点也是同样明显,少了很多灵活性,不同的布局方式都被抽离出了widget,大家需要了解的widget非常多,增加了学习成本。 居右

1.2 约束种类

布局空间。Flutter借鉴了很多React相关的东西,包括一些布局思想'; 30 | String htmlOl = 31 | '
  1. widget可以按照

  2. 条件,来决定自身如何

布局空间。Flutter借鉴了很多React

这样好处,可能是为了了统一的渲染。加入式,会

'; 32 | String imageHtml = 33 | '

这样做的好处,我觉得了统一的渲染加粗正常

只给出很少的定制化的东西,将布局限

定在有限的让布局复杂不少范围内中间的文字,在完成布局的同时,

让整个渲染能够统一,

加入视频开始

视频结束

'; 34 | String blockQuote = 35 | '

引用开始

比特币(Bitcoin)的概念最初由中本聪在2008年11月1日提出,并于2009年1月3日正式诞生 [1]  。根据中本聪的思路设计发布的开源软件以及建构其上的P2P网络。比特币是一种P2P形式的虚拟的加密数字货币。点对点的传输意味着一个去中心化的支付系统。

与所有的货币不同,比特币不依靠特定货币机构发行,它依据特定算法,通过大量的计算产生,比特币经济使用整个P2P网络中众多节点构成的分布以确保无法通过大量制造比特币来人为操控币值。基于密码学的设计可以使比特币只能被真实的拥有者转移或支付。这同样确保了货币所有权与流通交易的匿名性。比特币与其他虚拟货币最大的不同,是其总数量非常

2017年12月17日,比特币达到历史最高价19850美元。

引用结束


在渲染层面会降低很多性能。因此,Flutter

'; 36 | 37 | List contentListWidget = []; 38 | 39 | List contentImageList = []; 40 | 41 | @override 42 | void initState() { 43 | super.initState(); 44 | AsyncHtmlTextView(imageHtml + html + htmlOl + blockQuote).build((nodes) { 45 | contentListWidget = nodes; 46 | addImageToArticleImageMap('12', contentImageList); 47 | setState(() { 48 | contentListWidget = nodes; 49 | }); 50 | }, imageList: contentImageList, id: '12'); 51 | } 52 | 53 | @override 54 | Widget build(BuildContext context) { 55 | return Scaffold( 56 | body: Scrollbar( 57 | child: SingleChildScrollView( 58 | padding: EdgeInsets.all(12.0), 59 | child: Column( 60 | crossAxisAlignment: CrossAxisAlignment.start, 61 | children: contentListWidget, 62 | ), 63 | ), 64 | )); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/network_image.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:ui' as ui; 3 | import 'dart:ui'; 4 | 5 | import 'package:cached_network_image/cached_network_image.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter_cache_manager/flutter_cache_manager.dart'; 8 | 9 | import 'ImageClipper.dart'; 10 | import 'circle.dart'; 11 | import 'image.dart'; 12 | 13 | class NetworkImageClipper extends StatefulWidget { 14 | final String id; 15 | final String imageUrl; 16 | bool isInPackage = true; 17 | bool loadingOffstage; 18 | 19 | NetworkImageClipper(this.imageUrl, 20 | {this.id, this.isInPackage, this.loadingOffstage = true}); 21 | 22 | @override 23 | State createState() { 24 | return CachedImage(); 25 | } 26 | } 27 | 28 | class CachedImage extends State { 29 | ImageClipper clipper; 30 | 31 | CachedImage(); 32 | 33 | @override 34 | void initState() { 35 | super.initState(); 36 | clip(widget.imageUrl, context); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | getImageSingleFilePath(widget.id, widget.imageUrl); 42 | 43 | return ImageClipperInstance().containsUrl(widget.imageUrl) 44 | ? clipper == null 45 | ? Container( 46 | width: MediaQuery.of(context).size.width, 47 | height: MediaQuery.of(context).size.height, 48 | ) 49 | : _createCustomPaint(context) 50 | : Hero( 51 | tag: widget.imageUrl, 52 | child: buildCachedNetworkImage(), 53 | placeholderBuilder: (context, heroSize, widget) => 54 | buildCachedNetworkImage(), 55 | ); 56 | } 57 | 58 | CachedNetworkImage buildCachedNetworkImage() { 59 | return CachedNetworkImage( 60 | placeholder: (context, url) => Stack( 61 | alignment: AlignmentDirectional.center, 62 | children: [ 63 | Image( 64 | image: AssetImage("assets/images/feed_cell_photo_default_big.png", 65 | package: getPackageName()), 66 | ), 67 | Offstage( 68 | offstage: widget.loadingOffstage, 69 | child: SpinKitCircle( 70 | color: Colors.blueAccent, 71 | ), 72 | ), 73 | ], 74 | ), 75 | errorWidget: (context, url, error) => Image( 76 | image: AssetImage("assets/images/feed_cell_photo_default_big.png", 77 | package: getPackageName()), 78 | ), 79 | imageUrl: widget.imageUrl, 80 | fit: BoxFit.cover, 81 | ); 82 | } 83 | 84 | String getPackageName() => widget.isInPackage ? 'html_text' : null; 85 | 86 | Future getImageSingleFilePath(String id, String imageUrl) async { 87 | return await DefaultCacheManager().getSingleFile(imageUrl).then((file) { 88 | addImageToArticleImageFilePathMap(id, '"$imageUrl"', '"${file?.path}"'); 89 | }); 90 | } 91 | 92 | Future _loadImage(String url) async { 93 | ImageStream imageStream = NetworkImage(url).resolve(ImageConfiguration()); 94 | Completer completer = Completer(); 95 | void imageListener(ImageInfo info, bool synchronousCall) { 96 | ui.Image image = info.image; 97 | completer.complete(image); 98 | } 99 | 100 | ImageStreamListener imageStreamListener = 101 | ImageStreamListener(imageListener); 102 | imageStream.removeListener(imageStreamListener); 103 | imageStream.addListener(imageStreamListener); 104 | return completer.future; 105 | } 106 | 107 | clip(String url, BuildContext context) async { 108 | ui.Image uiImage; 109 | _loadImage(url).then((image) { 110 | uiImage = image; 111 | }).whenComplete(() { 112 | if (2 * window.physicalSize.height < uiImage.height) { 113 | clipper = ImageClipper(uiImage, context); 114 | setState(() { 115 | ImageClipperInstance() 116 | .addUrl(widget.imageUrl, _createCustomPaint(context)); 117 | }); 118 | } else { 119 | clipper = null; 120 | } 121 | }); 122 | } 123 | 124 | Widget _createCustomPaint(BuildContext context) { 125 | return Stack( 126 | children: [ 127 | CustomPaint( 128 | painter: clipper, 129 | size: Size(MediaQuery.of(context).size.width, 130 | MediaQuery.of(context).size.height), 131 | ), 132 | Positioned( 133 | right: 1, 134 | bottom: 1, 135 | child: Image.asset( 136 | "assets/images/long_picture_icon.png", 137 | package: getPackageName(), 138 | ), 139 | ), 140 | ], 141 | ); 142 | } 143 | } 144 | 145 | class ImageClipperInstance { 146 | factory ImageClipperInstance() => _getInstance(); 147 | 148 | static ImageClipperInstance _instance; 149 | 150 | List clipperImageUrl; 151 | 152 | ImageClipperInstance._() { 153 | clipperImageUrl = List(); 154 | } 155 | 156 | static ImageClipperInstance _getInstance() { 157 | if (_instance == null) { 158 | _instance = ImageClipperInstance._(); 159 | } 160 | return _instance; 161 | } 162 | 163 | void addUrl(String url, Widget imageClipperWidget) { 164 | clipperImageUrl.add(url); 165 | } 166 | 167 | bool containsUrl(String url) { 168 | return clipperImageUrl.contains(url); 169 | } 170 | } 171 | -------------------------------------------------------------------------------- /lib/on_tap_data.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class OnTapData { 4 | OnTapData( 5 | this.url, { 6 | this.type = OnTapType.href, 7 | this.data, 8 | this.id, 9 | this.isOnLongPress = false, 10 | this.context, 11 | }); 12 | 13 | String url; 14 | 15 | OnTapType type; 16 | 17 | dynamic data; 18 | 19 | String id; 20 | 21 | bool isOnLongPress = false; 22 | 23 | BuildContext context; 24 | } 25 | 26 | enum OnTapType { 27 | href, 28 | img, 29 | video, 30 | } 31 | -------------------------------------------------------------------------------- /lib/web_view_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | import 'package:webview_flutter/webview_flutter.dart'; 3 | 4 | import 'on_tap_data.dart'; 5 | 6 | class WebViewVideo extends StatelessWidget { 7 | final String url; 8 | final Function onTapCallback; 9 | 10 | WebViewVideo(this.url, {this.onTapCallback}); 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return Container( 15 | margin: EdgeInsets.only(left: 12, right: 12, top: 12), 16 | height: MediaQuery.of(context).size.width * 9 / 16, 17 | child: Stack( 18 | alignment: Alignment.center, 19 | fit: StackFit.expand, 20 | children: [ 21 | WebView( 22 | initialUrl: url, 23 | javascriptMode: JavascriptMode.unrestricted, 24 | ), 25 | GestureDetector( 26 | onTap: () { 27 | onTapCallback(OnTapData(url, type: OnTapType.video)); 28 | }, 29 | child: Container( 30 | color: Color(0x00FFFFFF), 31 | ), 32 | ), 33 | Positioned( 34 | right: 12, 35 | bottom: 12, 36 | child: GestureDetector( 37 | onTap: () { 38 | if (onTapCallback != null) { 39 | onTapCallback(OnTapData(url, type: OnTapType.video)); 40 | } 41 | }, 42 | child: Image.asset("assets/images/fullscreen_enter.png"), 43 | ), 44 | ), 45 | ], 46 | )); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /preview/Screenshot_1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/preview/Screenshot_1.jpg -------------------------------------------------------------------------------- /preview/Screenshot_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/preview/Screenshot_2.jpg -------------------------------------------------------------------------------- /preview/Screenshot_3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/preview/Screenshot_3.jpg -------------------------------------------------------------------------------- /preview/Screenshot_4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/preview/Screenshot_4.jpg -------------------------------------------------------------------------------- /preview/Screenshot_5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/houzhenpu/flutter_html_text/2d81625ccc3f437483bfa079f0245a9cced9d6ff/preview/Screenshot_5.jpg -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: html_text 2 | description: A new Flutter package. 3 | version: 0.0.2 4 | author: 5 | homepage: https://github.com/houzhenpu/flutter_fullhtml_textview 6 | 7 | environment: 8 | sdk: ">=2.1.0 <3.0.0" 9 | 10 | dependencies: 11 | flutter: 12 | sdk: flutter 13 | webview_flutter: ^0.3.10+4 14 | cached_network_image: ^2.0.0-rc 15 | html: any 16 | fluttertoast: ^3.1.0 17 | 18 | dev_dependencies: 19 | flutter_test: 20 | sdk: flutter 21 | 22 | # For information on the generic Dart part of this file, see the 23 | # following page: https://dart.dev/tools/pub/pubspec 24 | 25 | # The following section is specific to Flutter. 26 | flutter: 27 | assets: 28 | - assets/images/image_error.png 29 | - assets/images/long_picture_icon.png 30 | - assets/images/feed_cell_photo_default_big.png 31 | 32 | # To add assets to your package, add an assets section, like this: 33 | # assets: 34 | # - images/a_dot_burr.jpeg 35 | # - images/a_dot_ham.jpeg 36 | # 37 | # For details regarding assets in packages, see 38 | # https://flutter.dev/assets-and-images/#from-packages 39 | # 40 | # An image asset can refer to one or more resolution-specific "variants", see 41 | # https://flutter.dev/assets-and-images/#resolution-aware. 42 | 43 | # To add custom fonts to your package, add a fonts section here, 44 | # in this "flutter" section. Each entry in this list should have a 45 | # "family" key with the font family name, and a "fonts" key with a 46 | # list giving the asset and other descriptors for the font. For 47 | # example: 48 | # fonts: 49 | # - family: Schyler 50 | # fonts: 51 | # - asset: fonts/Schyler-Regular.ttf 52 | # - asset: fonts/Schyler-Italic.ttf 53 | # style: italic 54 | # - family: Trajan Pro 55 | # fonts: 56 | # - asset: fonts/TrajanPro.ttf 57 | # - asset: fonts/TrajanPro_Bold.ttf 58 | # weight: 700 59 | # 60 | # For details regarding fonts in packages, see 61 | # https://flutter.dev/custom-fonts/#from-packages 62 | --------------------------------------------------------------------------------