├── .gitignore ├── .metadata ├── .vscode ├── launch.json └── settings.json ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── custom_paint │ │ │ │ └── MainActivity.kt │ │ └── 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 ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── .gitignore ├── Flutter │ ├── .last_build_id │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Podfile ├── Podfile.lock ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── 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 │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── charts │ ├── area_chart │ │ └── area_chart.dart │ ├── bar_chart.dart │ ├── base_chart.dart │ ├── calender_heatmap │ │ ├── calendar_heatmap.dart │ │ └── utils.dart │ ├── column_chart.dart │ ├── donut_chart.dart │ ├── line_chart.dart │ ├── pie_chart.dart │ ├── radar_chart.dart │ ├── time_sheet │ │ └── time_sheet.dart │ └── tree_map │ │ ├── draw_tree_rects.dart │ │ ├── parse_array_to_bst.dart │ │ ├── tree_map.dart │ │ └── tree_node.dart ├── colors.dart ├── home_page.dart ├── main.dart ├── pages │ ├── area_chart_page.dart │ ├── bar_chart_page.dart │ ├── calender_heatmap_page.dart │ ├── column_chart_page.dart │ ├── donut_chart_page.dart │ ├── line_chart_page.dart │ ├── pie_chart_page.dart │ ├── radar_chart_page.dart │ └── tree_map_page.dart └── utils │ ├── create_animated_path.dart │ ├── draw_grid.dart │ └── get_next_num.dart ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .buildlog/ 9 | .history 10 | .svn/ 11 | 12 | # IntelliJ related 13 | *.iml 14 | *.ipr 15 | *.iws 16 | .idea/ 17 | 18 | # The .vscode folder contains launch configuration and tasks you configure in 19 | # VS Code which you may wish to be included in version control, so this line 20 | # is commented out by default. 21 | #.vscode/ 22 | 23 | # Flutter/Dart/Pub related 24 | **/doc/api/ 25 | .dart_tool/ 26 | .flutter-plugins 27 | .flutter-plugins-dependencies 28 | .packages 29 | .pub-cache/ 30 | .pub/ 31 | /build/ 32 | 33 | # Web related 34 | lib/generated_plugin_registrant.dart 35 | 36 | # Symbolication related 37 | app.*.symbols 38 | 39 | # Obfuscation related 40 | app.*.map.json 41 | 42 | # Exceptions to above rules. 43 | !/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages 44 | -------------------------------------------------------------------------------- /.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: e6b34c2b5c96bb95325269a29a84e83ed8909b5f 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // 使用 IntelliSense 了解相关属性。 3 | // 悬停以查看现有属性的描述。 4 | // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "Dev", 9 | "request": "launch", 10 | "type": "dart" 11 | }, 12 | { 13 | "name": "WEB", 14 | "request": "launch", 15 | "type": "dart" 16 | }, 17 | ] 18 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "vue3snippets.enable-compile-vue-file-on-did-save-code": true 3 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter charts 2 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # # Specify analysis options. 2 | # # 3 | # # Until there are meta linter rules, each desired lint must be explicitly enabled. 4 | # # See: https://github.com/dart-lang/linter/issues/288 5 | # # 6 | # # For a list of lints, see: http://dart-lang.github.io/linter/lints/ 7 | # # See the configuration guide for more 8 | # # https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer 9 | # # 10 | # # There are other similar analysis options files in the flutter repos, 11 | # # which should be kept in sync with this file: 12 | # # 13 | # # - analysis_options.yaml (this file) 14 | # # - packages/flutter/lib/analysis_options_user.yaml 15 | # # - https://github.com/flutter/plugins/blob/master/analysis_options.yaml 16 | # # - https://github.com/flutter/engine/blob/master/analysis_options.yaml 17 | # # 18 | # # This file contains the analysis options used by Flutter tools, such as IntelliJ, 19 | # # Android Studio, and the `flutter analyze` command. 20 | 21 | # analyzer: 22 | # strong-mode: 23 | # implicit-casts: false 24 | # implicit-dynamic: false 25 | # errors: 26 | # # treat missing required parameters as a warning (not a hint) 27 | # missing_required_param: warning 28 | # # treat missing returns as a warning (not a hint) 29 | # missing_return: warning 30 | # # allow having TODOs in the code 31 | # todo: ignore 32 | # # Ignore analyzer hints for updating pubspecs when using Future or 33 | # # Stream and not importing dart:async 34 | # # Please see https://github.com/flutter/flutter/pull/24528 for details. 35 | # sdk_version_async_exported_from_core: ignore 36 | # exclude: 37 | # - "bin/cache/**" 38 | # # the following two are relative to the stocks example and the flutter package respectively 39 | # # see https://github.com/dart-lang/sdk/issues/28463 40 | # - "lib/i18n/messages_*.dart" 41 | # - "lib/src/http/**" 42 | 43 | # linter: 44 | # rules: 45 | # # these rules are documented on and in the same order as 46 | # # the Dart Lint rules page to make maintenance easier 47 | # # https://github.com/dart-lang/linter/blob/master/example/all.yaml 48 | # - always_declare_return_types 49 | # - always_put_control_body_on_new_line 50 | # # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 51 | # - always_require_non_null_named_parameters 52 | # - always_specify_types 53 | # - annotate_overrides 54 | # # - avoid_annotating_with_dynamic # conflicts with always_specify_types 55 | # # - avoid_as # required for implicit-casts: true 56 | # - avoid_bool_literals_in_conditional_expressions 57 | # # - avoid_catches_without_on_clauses # we do this commonly 58 | # # - avoid_catching_errors # we do this commonly 59 | # - avoid_classes_with_only_static_members 60 | # # - avoid_double_and_int_checks # only useful when targeting JS runtime 61 | # - avoid_empty_else 62 | # # - avoid_equals_and_hash_code_on_mutable_classes # not yet tested 63 | # - avoid_field_initializers_in_const_classes 64 | # - avoid_function_literals_in_foreach_calls 65 | # # - avoid_implementing_value_types # not yet tested 66 | # - avoid_init_to_null 67 | # # - avoid_js_rounded_ints # only useful when targeting JS runtime 68 | # - avoid_null_checks_in_equality_operators 69 | # # - avoid_positional_boolean_parameters # not yet tested 70 | # # - avoid_print # not yet tested 71 | # # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) 72 | # # - avoid_redundant_argument_values # not yet tested 73 | # - avoid_relative_lib_imports 74 | # - avoid_renaming_method_parameters 75 | # - avoid_return_types_on_setters 76 | # # - avoid_returning_null # there are plenty of valid reasons to return null 77 | # # - avoid_returning_null_for_future # not yet tested 78 | # - avoid_returning_null_for_void 79 | # # - avoid_returning_this # there are plenty of valid reasons to return this 80 | # # - avoid_setters_without_getters # not yet tested 81 | # # - avoid_shadowing_type_parameters # not yet tested 82 | # - avoid_single_cascade_in_expression_statements 83 | # - avoid_slow_async_io 84 | # - avoid_types_as_parameter_names 85 | # # - avoid_types_on_closure_parameters # conflicts with always_specify_types 86 | # # - avoid_unnecessary_containers # not yet tested 87 | # - avoid_unused_constructor_parameters 88 | # - avoid_void_async 89 | # # - avoid_web_libraries_in_flutter # not yet tested 90 | # - await_only_futures 91 | # - camel_case_extensions 92 | # - camel_case_types 93 | # - cancel_subscriptions 94 | # # - cascade_invocations # not yet tested 95 | # # - close_sinks # not reliable enough 96 | # # - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 97 | # # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 98 | # - control_flow_in_finally 99 | # # - curly_braces_in_flow_control_structures # not yet tested 100 | # # - diagnostic_describe_all_properties # not yet tested 101 | # - directives_ordering 102 | # - empty_catches 103 | # - empty_constructor_bodies 104 | # - empty_statements 105 | # # - file_names # not yet tested 106 | # - flutter_style_todos 107 | # - hash_and_equals 108 | # - implementation_imports 109 | # # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811 110 | # - iterable_contains_unrelated_type 111 | # # - join_return_with_assignment # not yet tested 112 | # - library_names 113 | # - library_prefixes 114 | # # - lines_longer_than_80_chars # not yet tested 115 | # - list_remove_unrelated_type 116 | # # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 117 | # # - missing_whitespace_between_adjacent_strings # not yet tested 118 | # - no_adjacent_strings_in_list 119 | # - no_duplicate_case_values 120 | # # - no_logic_in_create_state # not yet tested 121 | # # - no_runtimeType_toString # not yet tested 122 | # - non_constant_identifier_names 123 | # # - null_closures # not yet tested 124 | # # - omit_local_variable_types # opposite of always_specify_types 125 | # # - one_member_abstracts # too many false positives 126 | # # - only_throw_errors # https://github.com/flutter/flutter/issues/5792 127 | # - overridden_fields 128 | # - package_api_docs 129 | # - package_names 130 | # - package_prefixed_library_names 131 | # # - parameter_assignments # we do this commonly 132 | # - prefer_adjacent_string_concatenation 133 | # - prefer_asserts_in_initializer_lists 134 | # # - prefer_asserts_with_message # not yet tested 135 | # - prefer_collection_literals 136 | # - prefer_conditional_assignment 137 | # - prefer_const_constructors 138 | # - prefer_const_constructors_in_immutables 139 | # - prefer_const_declarations 140 | # - prefer_const_literals_to_create_immutables 141 | # # - prefer_constructors_over_static_methods # not yet tested 142 | # - prefer_contains 143 | # # - prefer_double_quotes # opposite of prefer_single_quotes 144 | # - prefer_equal_for_default_values 145 | # # - prefer_expression_function_bodies # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods 146 | # - prefer_final_fields 147 | # - prefer_final_in_for_each 148 | # - prefer_final_locals 149 | # - prefer_for_elements_to_map_fromIterable 150 | # - prefer_foreach 151 | # # - prefer_function_declarations_over_variables # not yet tested 152 | # - prefer_generic_function_type_aliases 153 | # - prefer_if_elements_to_conditional_expressions 154 | # - prefer_if_null_operators 155 | # - prefer_initializing_formals 156 | # - prefer_inlined_adds 157 | # # - prefer_int_literals # not yet tested 158 | # # - prefer_interpolation_to_compose_strings # not yet tested 159 | # - prefer_is_empty 160 | # - prefer_is_not_empty 161 | # - prefer_is_not_operator 162 | # - prefer_iterable_whereType 163 | # # - prefer_mixin # https://github.com/dart-lang/language/issues/32 164 | # # - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932 165 | # # - prefer_relative_imports # not yet tested 166 | # - prefer_single_quotes 167 | # - prefer_spread_collections 168 | # - prefer_typing_uninitialized_variables 169 | # - prefer_void_to_null 170 | # # - provide_deprecation_message # not yet tested 171 | # # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml 172 | # - recursive_getters 173 | # - slash_for_doc_comments 174 | # # - sort_child_properties_last # not yet tested 175 | # - sort_constructors_first 176 | # - sort_pub_dependencies 177 | # - sort_unnamed_constructors_first 178 | # - test_types_in_equals 179 | # - throw_in_finally 180 | # # - type_annotate_public_apis # subset of always_specify_types 181 | # - type_init_formals 182 | # # - unawaited_futures # too many false positives 183 | # # - unnecessary_await_in_return # not yet tested 184 | # - unnecessary_brace_in_string_interps 185 | # - unnecessary_const 186 | # # - unnecessary_final # conflicts with prefer_final_locals 187 | # - unnecessary_getters_setters 188 | # # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 189 | # - unnecessary_new 190 | # - unnecessary_null_aware_assignments 191 | # - unnecessary_null_in_if_null_operators 192 | # - unnecessary_overrides 193 | # - unnecessary_parenthesis 194 | # - unnecessary_statements 195 | # - unnecessary_string_interpolations 196 | # - unnecessary_this 197 | # - unrelated_type_equality_checks 198 | # # - unsafe_html # not yet tested 199 | # - use_full_hex_values_for_flutter_colors 200 | # # - use_function_type_syntax_for_parameters # not yet tested 201 | # # - use_key_in_widget_constructors # not yet tested 202 | # - use_rethrow_when_possible 203 | # # - use_setters_to_change_properties # not yet tested 204 | # # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 205 | # # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review 206 | # - valid_regexps 207 | # - void_checks 208 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | -------------------------------------------------------------------------------- /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 plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion 28 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.custom_paint" 42 | minSdkVersion 16 43 | targetSdkVersion 28 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 8 | 12 | 19 | 23 | 27 | 32 | 36 | 37 | 38 | 39 | 40 | 41 | 43 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/custom_paint/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.custom_paint 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.enableR8=true 3 | android.useAndroidX=true 4 | android.enableJetifier=true 5 | -------------------------------------------------------------------------------- /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-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/.last_build_id: -------------------------------------------------------------------------------- 1 | 9fdedd93144233526c9f0f63902a1889 -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig" 2 | #include "Generated.xcconfig" 3 | -------------------------------------------------------------------------------- /ios/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment this line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' 6 | 7 | project 'Runner', { 8 | 'Debug' => :debug, 9 | 'Profile' => :release, 10 | 'Release' => :release, 11 | } 12 | 13 | def parse_KV_file(file, separator='=') 14 | file_abs_path = File.expand_path(file) 15 | if !File.exists? file_abs_path 16 | return []; 17 | end 18 | generated_key_values = {} 19 | skip_line_start_symbols = ["#", "/"] 20 | File.foreach(file_abs_path) do |line| 21 | next if skip_line_start_symbols.any? { |symbol| line =~ /^\s*#{symbol}/ } 22 | plugin = line.split(pattern=separator) 23 | if plugin.length == 2 24 | podname = plugin[0].strip() 25 | path = plugin[1].strip() 26 | podpath = File.expand_path("#{path}", file_abs_path) 27 | generated_key_values[podname] = podpath 28 | else 29 | puts "Invalid plugin specification: #{line}" 30 | end 31 | end 32 | generated_key_values 33 | end 34 | 35 | target 'Runner' do 36 | use_frameworks! 37 | use_modular_headers! 38 | 39 | # Flutter Pod 40 | 41 | copied_flutter_dir = File.join(__dir__, 'Flutter') 42 | copied_framework_path = File.join(copied_flutter_dir, 'Flutter.framework') 43 | copied_podspec_path = File.join(copied_flutter_dir, 'Flutter.podspec') 44 | unless File.exist?(copied_framework_path) && File.exist?(copied_podspec_path) 45 | # Copy Flutter.framework and Flutter.podspec to Flutter/ to have something to link against if the xcode backend script has not run yet. 46 | # That script will copy the correct debug/profile/release version of the framework based on the currently selected Xcode configuration. 47 | # CocoaPods will not embed the framework on pod install (before any build phases can generate) if the dylib does not exist. 48 | 49 | generated_xcode_build_settings_path = File.join(copied_flutter_dir, 'Generated.xcconfig') 50 | unless File.exist?(generated_xcode_build_settings_path) 51 | raise "Generated.xcconfig must exist. If you're running pod install manually, make sure flutter pub get is executed first" 52 | end 53 | generated_xcode_build_settings = parse_KV_file(generated_xcode_build_settings_path) 54 | cached_framework_dir = generated_xcode_build_settings['FLUTTER_FRAMEWORK_DIR']; 55 | 56 | unless File.exist?(copied_framework_path) 57 | FileUtils.cp_r(File.join(cached_framework_dir, 'Flutter.framework'), copied_flutter_dir) 58 | end 59 | unless File.exist?(copied_podspec_path) 60 | FileUtils.cp(File.join(cached_framework_dir, 'Flutter.podspec'), copied_flutter_dir) 61 | end 62 | end 63 | 64 | # Keep pod path relative so it can be checked into Podfile.lock. 65 | pod 'Flutter', :path => 'Flutter' 66 | 67 | # Plugin Pods 68 | 69 | # Prepare symlinks folder. We use symlinks to avoid having Podfile.lock 70 | # referring to absolute paths on developers' machines. 71 | system('rm -rf .symlinks') 72 | system('mkdir -p .symlinks/plugins') 73 | plugin_pods = parse_KV_file('../.flutter-plugins') 74 | plugin_pods.each do |name, path| 75 | symlink = File.join('.symlinks', 'plugins', name) 76 | File.symlink(path, symlink) 77 | pod name, :path => File.join(symlink, 'ios') 78 | end 79 | end 80 | 81 | post_install do |installer| 82 | installer.pods_project.targets.each do |target| 83 | target.build_configurations.each do |config| 84 | config.build_settings['ENABLE_BITCODE'] = 'NO' 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /ios/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Flutter (1.0.0) 3 | 4 | DEPENDENCIES: 5 | - Flutter (from `Flutter`) 6 | 7 | EXTERNAL SOURCES: 8 | Flutter: 9 | :path: Flutter 10 | 11 | SPEC CHECKSUMS: 12 | Flutter: 0e3d915762c693b495b44d77113d4970485de6ec 13 | 14 | PODFILE CHECKSUM: c34e2287a9ccaa606aeceab922830efb9a6ff69a 15 | 16 | COCOAPODS: 1.10.0 17 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 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 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | B2E2F1B473540E1E9FB960DC /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F2CE4CE2154501945F169FEC /* Pods_Runner.framework */; }; 17 | /* End PBXBuildFile section */ 18 | 19 | /* Begin PBXCopyFilesBuildPhase section */ 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 21 | isa = PBXCopyFilesBuildPhase; 22 | buildActionMask = 2147483647; 23 | dstPath = ""; 24 | dstSubfolderSpec = 10; 25 | files = ( 26 | ); 27 | name = "Embed Frameworks"; 28 | runOnlyForDeploymentPostprocessing = 0; 29 | }; 30 | /* End PBXCopyFilesBuildPhase section */ 31 | 32 | /* Begin PBXFileReference section */ 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 35 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 36 | 727218B7D4A6C7AD75613D5F /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 37 | 7471488D202D8205900385DF /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; 38 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 39 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 44 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 45 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 46 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 47 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | E6E3255E32FD2C034263E2D7 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; 49 | F2CE4CE2154501945F169FEC /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | /* End PBXFileReference section */ 51 | 52 | /* Begin PBXFrameworksBuildPhase section */ 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 54 | isa = PBXFrameworksBuildPhase; 55 | buildActionMask = 2147483647; 56 | files = ( 57 | B2E2F1B473540E1E9FB960DC /* Pods_Runner.framework in Frameworks */, 58 | ); 59 | runOnlyForDeploymentPostprocessing = 0; 60 | }; 61 | /* End PBXFrameworksBuildPhase section */ 62 | 63 | /* Begin PBXGroup section */ 64 | 3E8F1DD15E07E3A4FC3A91A8 /* Pods */ = { 65 | isa = PBXGroup; 66 | children = ( 67 | 727218B7D4A6C7AD75613D5F /* Pods-Runner.debug.xcconfig */, 68 | 7471488D202D8205900385DF /* Pods-Runner.release.xcconfig */, 69 | E6E3255E32FD2C034263E2D7 /* Pods-Runner.profile.xcconfig */, 70 | ); 71 | path = Pods; 72 | sourceTree = ""; 73 | }; 74 | 57AAF5FB1F552D07280C5F33 /* Frameworks */ = { 75 | isa = PBXGroup; 76 | children = ( 77 | F2CE4CE2154501945F169FEC /* Pods_Runner.framework */, 78 | ); 79 | name = Frameworks; 80 | sourceTree = ""; 81 | }; 82 | 9740EEB11CF90186004384FC /* Flutter */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 86 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 87 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 88 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 89 | ); 90 | name = Flutter; 91 | sourceTree = ""; 92 | }; 93 | 97C146E51CF9000F007C117D = { 94 | isa = PBXGroup; 95 | children = ( 96 | 9740EEB11CF90186004384FC /* Flutter */, 97 | 97C146F01CF9000F007C117D /* Runner */, 98 | 97C146EF1CF9000F007C117D /* Products */, 99 | 3E8F1DD15E07E3A4FC3A91A8 /* Pods */, 100 | 57AAF5FB1F552D07280C5F33 /* Frameworks */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 97C146EF1CF9000F007C117D /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 97C146EE1CF9000F007C117D /* Runner.app */, 108 | ); 109 | name = Products; 110 | sourceTree = ""; 111 | }; 112 | 97C146F01CF9000F007C117D /* Runner */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 116 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 117 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 118 | 97C147021CF9000F007C117D /* Info.plist */, 119 | 97C146F11CF9000F007C117D /* Supporting Files */, 120 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 121 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 122 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 123 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 124 | ); 125 | path = Runner; 126 | sourceTree = ""; 127 | }; 128 | 97C146F11CF9000F007C117D /* Supporting Files */ = { 129 | isa = PBXGroup; 130 | children = ( 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 97C146ED1CF9000F007C117D /* Runner */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 141 | buildPhases = ( 142 | 8CF0617A7E21FA0C5D4D9DA7 /* [CP] Check Pods Manifest.lock */, 143 | 9740EEB61CF901F6004384FC /* Run Script */, 144 | 97C146EA1CF9000F007C117D /* Sources */, 145 | 97C146EB1CF9000F007C117D /* Frameworks */, 146 | 97C146EC1CF9000F007C117D /* Resources */, 147 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 148 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 149 | E05D957293DF30DD64C52328 /* [CP] Embed Pods Frameworks */, 150 | ); 151 | buildRules = ( 152 | ); 153 | dependencies = ( 154 | ); 155 | name = Runner; 156 | productName = Runner; 157 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 158 | productType = "com.apple.product-type.application"; 159 | }; 160 | /* End PBXNativeTarget section */ 161 | 162 | /* Begin PBXProject section */ 163 | 97C146E61CF9000F007C117D /* Project object */ = { 164 | isa = PBXProject; 165 | attributes = { 166 | LastUpgradeCheck = 1020; 167 | ORGANIZATIONNAME = ""; 168 | TargetAttributes = { 169 | 97C146ED1CF9000F007C117D = { 170 | CreatedOnToolsVersion = 7.3.1; 171 | LastSwiftMigration = 1100; 172 | }; 173 | }; 174 | }; 175 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 176 | compatibilityVersion = "Xcode 9.3"; 177 | developmentRegion = en; 178 | hasScannedForEncodings = 0; 179 | knownRegions = ( 180 | en, 181 | Base, 182 | ); 183 | mainGroup = 97C146E51CF9000F007C117D; 184 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 185 | projectDirPath = ""; 186 | projectRoot = ""; 187 | targets = ( 188 | 97C146ED1CF9000F007C117D /* Runner */, 189 | ); 190 | }; 191 | /* End PBXProject section */ 192 | 193 | /* Begin PBXResourcesBuildPhase section */ 194 | 97C146EC1CF9000F007C117D /* Resources */ = { 195 | isa = PBXResourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 199 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 200 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 201 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 202 | ); 203 | runOnlyForDeploymentPostprocessing = 0; 204 | }; 205 | /* End PBXResourcesBuildPhase section */ 206 | 207 | /* Begin PBXShellScriptBuildPhase section */ 208 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 209 | isa = PBXShellScriptBuildPhase; 210 | buildActionMask = 2147483647; 211 | files = ( 212 | ); 213 | inputPaths = ( 214 | ); 215 | name = "Thin Binary"; 216 | outputPaths = ( 217 | ); 218 | runOnlyForDeploymentPostprocessing = 0; 219 | shellPath = /bin/sh; 220 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 221 | }; 222 | 8CF0617A7E21FA0C5D4D9DA7 /* [CP] Check Pods Manifest.lock */ = { 223 | isa = PBXShellScriptBuildPhase; 224 | buildActionMask = 2147483647; 225 | files = ( 226 | ); 227 | inputFileListPaths = ( 228 | ); 229 | inputPaths = ( 230 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 231 | "${PODS_ROOT}/Manifest.lock", 232 | ); 233 | name = "[CP] Check Pods Manifest.lock"; 234 | outputFileListPaths = ( 235 | ); 236 | outputPaths = ( 237 | "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", 238 | ); 239 | runOnlyForDeploymentPostprocessing = 0; 240 | shellPath = /bin/sh; 241 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 242 | showEnvVarsInLog = 0; 243 | }; 244 | 9740EEB61CF901F6004384FC /* Run Script */ = { 245 | isa = PBXShellScriptBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | ); 249 | inputPaths = ( 250 | ); 251 | name = "Run Script"; 252 | outputPaths = ( 253 | ); 254 | runOnlyForDeploymentPostprocessing = 0; 255 | shellPath = /bin/sh; 256 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 257 | }; 258 | E05D957293DF30DD64C52328 /* [CP] Embed Pods Frameworks */ = { 259 | isa = PBXShellScriptBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | ); 263 | inputFileListPaths = ( 264 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", 265 | ); 266 | name = "[CP] Embed Pods Frameworks"; 267 | outputFileListPaths = ( 268 | "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", 269 | ); 270 | runOnlyForDeploymentPostprocessing = 0; 271 | shellPath = /bin/sh; 272 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; 273 | showEnvVarsInLog = 0; 274 | }; 275 | /* End PBXShellScriptBuildPhase section */ 276 | 277 | /* Begin PBXSourcesBuildPhase section */ 278 | 97C146EA1CF9000F007C117D /* Sources */ = { 279 | isa = PBXSourcesBuildPhase; 280 | buildActionMask = 2147483647; 281 | files = ( 282 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 283 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 284 | ); 285 | runOnlyForDeploymentPostprocessing = 0; 286 | }; 287 | /* End PBXSourcesBuildPhase section */ 288 | 289 | /* Begin PBXVariantGroup section */ 290 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 291 | isa = PBXVariantGroup; 292 | children = ( 293 | 97C146FB1CF9000F007C117D /* Base */, 294 | ); 295 | name = Main.storyboard; 296 | sourceTree = ""; 297 | }; 298 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 299 | isa = PBXVariantGroup; 300 | children = ( 301 | 97C147001CF9000F007C117D /* Base */, 302 | ); 303 | name = LaunchScreen.storyboard; 304 | sourceTree = ""; 305 | }; 306 | /* End PBXVariantGroup section */ 307 | 308 | /* Begin XCBuildConfiguration section */ 309 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 319 | CLANG_WARN_BOOL_CONVERSION = YES; 320 | CLANG_WARN_COMMA = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 340 | ENABLE_NS_ASSERTIONS = NO; 341 | ENABLE_STRICT_OBJC_MSGSEND = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_NO_COMMON_BLOCKS = YES; 344 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 345 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 346 | GCC_WARN_UNDECLARED_SELECTOR = YES; 347 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 348 | GCC_WARN_UNUSED_FUNCTION = YES; 349 | GCC_WARN_UNUSED_VARIABLE = YES; 350 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 351 | MTL_ENABLE_DEBUG_INFO = NO; 352 | SDKROOT = iphoneos; 353 | SUPPORTED_PLATFORMS = iphoneos; 354 | TARGETED_DEVICE_FAMILY = "1,2"; 355 | VALIDATE_PRODUCT = YES; 356 | }; 357 | name = Profile; 358 | }; 359 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 360 | isa = XCBuildConfiguration; 361 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 362 | buildSettings = { 363 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 364 | CLANG_ENABLE_MODULES = YES; 365 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 366 | ENABLE_BITCODE = NO; 367 | FRAMEWORK_SEARCH_PATHS = ( 368 | "$(inherited)", 369 | "$(PROJECT_DIR)/Flutter", 370 | ); 371 | INFOPLIST_FILE = Runner/Info.plist; 372 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 373 | LD_RUNPATH_SEARCH_PATHS = ( 374 | "$(inherited)", 375 | "@executable_path/Frameworks", 376 | ); 377 | LIBRARY_SEARCH_PATHS = ( 378 | "$(inherited)", 379 | "$(PROJECT_DIR)/Flutter", 380 | ); 381 | PRODUCT_BUNDLE_IDENTIFIER = com.example.customPaint1; 382 | PRODUCT_NAME = "$(TARGET_NAME)"; 383 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 384 | SWIFT_VERSION = 5.0; 385 | VERSIONING_SYSTEM = "apple-generic"; 386 | }; 387 | name = Profile; 388 | }; 389 | 97C147031CF9000F007C117D /* Debug */ = { 390 | isa = XCBuildConfiguration; 391 | buildSettings = { 392 | ALWAYS_SEARCH_USER_PATHS = NO; 393 | CLANG_ANALYZER_NONNULL = YES; 394 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 395 | CLANG_CXX_LIBRARY = "libc++"; 396 | CLANG_ENABLE_MODULES = YES; 397 | CLANG_ENABLE_OBJC_ARC = YES; 398 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 399 | CLANG_WARN_BOOL_CONVERSION = YES; 400 | CLANG_WARN_COMMA = YES; 401 | CLANG_WARN_CONSTANT_CONVERSION = YES; 402 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 403 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 404 | CLANG_WARN_EMPTY_BODY = YES; 405 | CLANG_WARN_ENUM_CONVERSION = YES; 406 | CLANG_WARN_INFINITE_RECURSION = YES; 407 | CLANG_WARN_INT_CONVERSION = YES; 408 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 409 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 410 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 412 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 413 | CLANG_WARN_STRICT_PROTOTYPES = YES; 414 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 415 | CLANG_WARN_UNREACHABLE_CODE = YES; 416 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 417 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 418 | COPY_PHASE_STRIP = NO; 419 | DEBUG_INFORMATION_FORMAT = dwarf; 420 | ENABLE_STRICT_OBJC_MSGSEND = YES; 421 | ENABLE_TESTABILITY = YES; 422 | GCC_C_LANGUAGE_STANDARD = gnu99; 423 | GCC_DYNAMIC_NO_PIC = NO; 424 | GCC_NO_COMMON_BLOCKS = YES; 425 | GCC_OPTIMIZATION_LEVEL = 0; 426 | GCC_PREPROCESSOR_DEFINITIONS = ( 427 | "DEBUG=1", 428 | "$(inherited)", 429 | ); 430 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 431 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 432 | GCC_WARN_UNDECLARED_SELECTOR = YES; 433 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 434 | GCC_WARN_UNUSED_FUNCTION = YES; 435 | GCC_WARN_UNUSED_VARIABLE = YES; 436 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 437 | MTL_ENABLE_DEBUG_INFO = YES; 438 | ONLY_ACTIVE_ARCH = YES; 439 | SDKROOT = iphoneos; 440 | TARGETED_DEVICE_FAMILY = "1,2"; 441 | }; 442 | name = Debug; 443 | }; 444 | 97C147041CF9000F007C117D /* Release */ = { 445 | isa = XCBuildConfiguration; 446 | buildSettings = { 447 | ALWAYS_SEARCH_USER_PATHS = NO; 448 | CLANG_ANALYZER_NONNULL = YES; 449 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 450 | CLANG_CXX_LIBRARY = "libc++"; 451 | CLANG_ENABLE_MODULES = YES; 452 | CLANG_ENABLE_OBJC_ARC = YES; 453 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 454 | CLANG_WARN_BOOL_CONVERSION = YES; 455 | CLANG_WARN_COMMA = YES; 456 | CLANG_WARN_CONSTANT_CONVERSION = YES; 457 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 458 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 459 | CLANG_WARN_EMPTY_BODY = YES; 460 | CLANG_WARN_ENUM_CONVERSION = YES; 461 | CLANG_WARN_INFINITE_RECURSION = YES; 462 | CLANG_WARN_INT_CONVERSION = YES; 463 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 464 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 465 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 466 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 467 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 468 | CLANG_WARN_STRICT_PROTOTYPES = YES; 469 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 470 | CLANG_WARN_UNREACHABLE_CODE = YES; 471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 472 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 473 | COPY_PHASE_STRIP = NO; 474 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 475 | ENABLE_NS_ASSERTIONS = NO; 476 | ENABLE_STRICT_OBJC_MSGSEND = YES; 477 | GCC_C_LANGUAGE_STANDARD = gnu99; 478 | GCC_NO_COMMON_BLOCKS = YES; 479 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 480 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 481 | GCC_WARN_UNDECLARED_SELECTOR = YES; 482 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 483 | GCC_WARN_UNUSED_FUNCTION = YES; 484 | GCC_WARN_UNUSED_VARIABLE = YES; 485 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 486 | MTL_ENABLE_DEBUG_INFO = NO; 487 | SDKROOT = iphoneos; 488 | SUPPORTED_PLATFORMS = iphoneos; 489 | SWIFT_COMPILATION_MODE = wholemodule; 490 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 491 | TARGETED_DEVICE_FAMILY = "1,2"; 492 | VALIDATE_PRODUCT = YES; 493 | }; 494 | name = Release; 495 | }; 496 | 97C147061CF9000F007C117D /* Debug */ = { 497 | isa = XCBuildConfiguration; 498 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 499 | buildSettings = { 500 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 501 | CLANG_ENABLE_MODULES = YES; 502 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 503 | ENABLE_BITCODE = NO; 504 | FRAMEWORK_SEARCH_PATHS = ( 505 | "$(inherited)", 506 | "$(PROJECT_DIR)/Flutter", 507 | ); 508 | INFOPLIST_FILE = Runner/Info.plist; 509 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 510 | LD_RUNPATH_SEARCH_PATHS = ( 511 | "$(inherited)", 512 | "@executable_path/Frameworks", 513 | ); 514 | LIBRARY_SEARCH_PATHS = ( 515 | "$(inherited)", 516 | "$(PROJECT_DIR)/Flutter", 517 | ); 518 | PRODUCT_BUNDLE_IDENTIFIER = com.example.customPaint1; 519 | PRODUCT_NAME = "$(TARGET_NAME)"; 520 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 521 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 522 | SWIFT_VERSION = 5.0; 523 | VERSIONING_SYSTEM = "apple-generic"; 524 | }; 525 | name = Debug; 526 | }; 527 | 97C147071CF9000F007C117D /* Release */ = { 528 | isa = XCBuildConfiguration; 529 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 530 | buildSettings = { 531 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 532 | CLANG_ENABLE_MODULES = YES; 533 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 534 | ENABLE_BITCODE = NO; 535 | FRAMEWORK_SEARCH_PATHS = ( 536 | "$(inherited)", 537 | "$(PROJECT_DIR)/Flutter", 538 | ); 539 | INFOPLIST_FILE = Runner/Info.plist; 540 | IPHONEOS_DEPLOYMENT_TARGET = 9.0; 541 | LD_RUNPATH_SEARCH_PATHS = ( 542 | "$(inherited)", 543 | "@executable_path/Frameworks", 544 | ); 545 | LIBRARY_SEARCH_PATHS = ( 546 | "$(inherited)", 547 | "$(PROJECT_DIR)/Flutter", 548 | ); 549 | PRODUCT_BUNDLE_IDENTIFIER = com.example.customPaint1; 550 | PRODUCT_NAME = "$(TARGET_NAME)"; 551 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 552 | SWIFT_VERSION = 5.0; 553 | VERSIONING_SYSTEM = "apple-generic"; 554 | }; 555 | name = Release; 556 | }; 557 | /* End XCBuildConfiguration section */ 558 | 559 | /* Begin XCConfigurationList section */ 560 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 561 | isa = XCConfigurationList; 562 | buildConfigurations = ( 563 | 97C147031CF9000F007C117D /* Debug */, 564 | 97C147041CF9000F007C117D /* Release */, 565 | 249021D3217E4FDB00AE95B9 /* Profile */, 566 | ); 567 | defaultConfigurationIsVisible = 0; 568 | defaultConfigurationName = Release; 569 | }; 570 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 571 | isa = XCConfigurationList; 572 | buildConfigurations = ( 573 | 97C147061CF9000F007C117D /* Debug */, 574 | 97C147071CF9000F007C117D /* Release */, 575 | 249021D4217E4FDB00AE95B9 /* Profile */, 576 | ); 577 | defaultConfigurationIsVisible = 0; 578 | defaultConfigurationName = Release; 579 | }; 580 | /* End XCConfigurationList section */ 581 | }; 582 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 583 | } 584 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xrr2016/flutter-charts/55ccfdaeaa0b4b11242419132660d239b905d026/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/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | custom_paint 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/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/charts/area_chart/area_chart.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter_charts/colors.dart'; 3 | import '../../utils/draw_grid.dart'; 4 | 5 | class AreaChart extends StatefulWidget { 6 | final List datas; 7 | 8 | const AreaChart({@required this.datas}); 9 | 10 | @override 11 | _AreaChartState createState() => _AreaChartState(); 12 | } 13 | 14 | class _AreaChartState extends State { 15 | @override 16 | Widget build(BuildContext context) { 17 | return LayoutBuilder( 18 | builder: (BuildContext context, BoxConstraints constraints) { 19 | return InteractiveViewer( 20 | child: CustomPaint( 21 | size: constraints.biggest, 22 | painter: AreaChartPainter(widget.datas), 23 | ), 24 | ); 25 | }, 26 | ); 27 | } 28 | } 29 | 30 | class AreaChartPainter extends CustomPainter { 31 | final List datas; 32 | 33 | AreaChartPainter(this.datas); 34 | 35 | void _drawLines(Canvas canvas, Size size) { 36 | final sw = size.width; 37 | final sh = size.height; 38 | final gap = sw / datas.length; 39 | final paint = Paint() 40 | ..style = PaintingStyle.stroke 41 | ..color = Colors.blue 42 | ..isAntiAlias = true 43 | ..strokeCap = StrokeCap.round 44 | ..strokeJoin = StrokeJoin.round 45 | ..strokeWidth = 2.5; 46 | 47 | canvas.save(); 48 | canvas.translate(0, sh); 49 | canvas.scale(1, -1); 50 | 51 | Offset p1 = Offset(100, 100); 52 | Offset p2 = Offset(200, 200); 53 | canvas.drawLine(p1, p2, paint); 54 | 55 | final path = Path()..moveTo(gap / 2, datas[0]); 56 | 57 | for (int i = 1; i < datas.length; i++) { 58 | final data = datas[i]; 59 | final x = gap * i + gap / 2; 60 | final y = sh - data; 61 | 62 | path.lineTo(x, y); 63 | } 64 | 65 | canvas.drawPath(path, paint); 66 | canvas.restore(); 67 | } 68 | 69 | @override 70 | void paint(Canvas canvas, Size size) { 71 | Rect rect = Rect.fromLTWH(0, 0, size.width, size.height); 72 | Paint paint = Paint()..color = Colors.black12; 73 | canvas.clipRect(Offset.zero & size); 74 | drawGrid(canvas, size); 75 | _drawLines(canvas, size); 76 | 77 | // canvas.drawRect(rect, paint); 78 | } 79 | 80 | @override 81 | bool shouldRepaint(AreaChartPainter oldDelegate) => false; 82 | 83 | @override 84 | bool shouldRebuildSemantics(AreaChartPainter oldDelegate) => false; 85 | } 86 | -------------------------------------------------------------------------------- /lib/charts/bar_chart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | import '../colors.dart'; 7 | 8 | class BarChart extends StatefulWidget { 9 | final double max; 10 | final List> data; 11 | 12 | const BarChart({ 13 | @required this.data, 14 | @required this.max, 15 | }); 16 | 17 | @override 18 | _BarChartState createState() => _BarChartState(); 19 | } 20 | 21 | class _BarChartState extends State { 22 | @override 23 | Widget build(BuildContext context) { 24 | return Center( 25 | child: Container( 26 | width: 300, 27 | height: 300, 28 | child: CustomPaint( 29 | painter: BarChartPainter( 30 | data: widget.data, 31 | max: widget.max, 32 | ), 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | 39 | class BarChartPainter extends CustomPainter { 40 | final List> data; 41 | final List xAxis; 42 | final double max; 43 | 44 | BarChartPainter({this.data, this.xAxis, this.max}); 45 | 46 | void _drawGrid(Canvas canvas, Size size) { 47 | final double sw = size.width; 48 | final double sh = size.height; 49 | final double vgap = sw / 10; 50 | final double hgap = sh / 10; 51 | 52 | final Paint paint = Paint() 53 | ..color = Colors.black12 54 | ..style = PaintingStyle.stroke 55 | ..strokeWidth = .5; 56 | 57 | for (double i = 0; i <= sw; i += vgap) { 58 | Offset p1 = Offset(i, 0); 59 | Offset p2 = Offset(i, sh); 60 | canvas.drawLine(p1, p2, paint); 61 | } 62 | 63 | for (double i = 0; i < sh; i += hgap) { 64 | Offset p1 = Offset(0.0, i); 65 | Offset p2 = Offset(sw, i); 66 | canvas.drawLine(p1, p2, paint); 67 | } 68 | } 69 | 70 | void _drawAxis(Canvas canvas, Size size) { 71 | final double sw = size.width; 72 | final double sh = size.height; 73 | 74 | final paint = Paint() 75 | ..color = Colors.black87 76 | ..style = PaintingStyle.stroke 77 | ..strokeWidth = 1.0 78 | ..isAntiAlias = true; 79 | 80 | final Path path = Path() 81 | ..moveTo(0.0, 0.0) 82 | ..lineTo(0.0, sh) 83 | ..lineTo(sw, sh); 84 | canvas.drawPath(path, paint); 85 | } 86 | 87 | void _drawXAxis(Canvas canvas, Size size) { 88 | final double sw = size.width; 89 | final double sh = size.height; 90 | final double fontSize = 16.0; 91 | final double vgap = sw / data.length; 92 | double val = 0.0; 93 | 94 | for (double i = 0; i <= sw; i += vgap) { 95 | final Offset offset = Offset(i, sh + fontSize); 96 | 97 | TextPainter( 98 | text: TextSpan( 99 | text: val.toInt().toString(), 100 | style: TextStyle( 101 | fontSize: fontSize, 102 | color: Colors.black87, 103 | ), 104 | ), 105 | textDirection: TextDirection.ltr, 106 | ) 107 | ..layout(minWidth: 0, maxWidth: sw) 108 | ..paint(canvas, offset); 109 | val += max / data.length; 110 | } 111 | } 112 | 113 | void _drawBars(Canvas canvas, Size size) { 114 | final datas = data; 115 | final double sh = size.height; 116 | final double sw = size.width; 117 | final double topOffset = 30.0; 118 | final double barGap = 20.0; 119 | final double barHeright = sh / datas.length - barGap - 10; 120 | final Paint paint = Paint()..isAntiAlias = true; 121 | 122 | for (var i = 0; i < datas.length; i++) { 123 | paint.color = colors[i]; 124 | final data = datas[i]; 125 | final double barTop = i * (barHeright + barGap) + topOffset; 126 | final double barWidth = data["value"] * sw / max; 127 | 128 | Rect bar = Rect.fromLTWH( 129 | 0.0, 130 | barTop, 131 | barWidth, 132 | barHeright, 133 | ); 134 | canvas.drawRect(bar, paint); 135 | 136 | final double valueFontSize = 16.0; 137 | final Offset valueOffset = 138 | Offset(barWidth + valueFontSize, barTop + valueFontSize / 2); 139 | 140 | TextPainter( 141 | text: TextSpan( 142 | text: data["value"].toString(), 143 | style: TextStyle(fontSize: 12.0, color: Colors.black87), 144 | ), 145 | textDirection: TextDirection.ltr, 146 | ) 147 | ..layout(minWidth: 0, maxWidth: sw) 148 | ..paint(canvas, valueOffset); 149 | 150 | final tipWidth = 4.0; 151 | final double tipTop = barTop + (barHeright / 2); 152 | final Rect tip = Rect.fromLTWH(-tipWidth, tipTop, tipWidth, 1); 153 | final Paint tipPaint = Paint() 154 | ..isAntiAlias = true 155 | ..color = Colors.black; 156 | 157 | canvas.drawRect(tip, tipPaint); 158 | 159 | final double labelFontSize = 16.0; 160 | final Offset labelOffset = Offset( 161 | -40.0, 162 | tipTop - labelFontSize / 2, 163 | ); 164 | 165 | TextPainter( 166 | text: TextSpan( 167 | text: data["label"], 168 | style: TextStyle(fontSize: 12.0, color: Colors.black87), 169 | ), 170 | textDirection: TextDirection.ltr, 171 | ) 172 | ..layout(minWidth: 0, maxWidth: 24) 173 | ..paint(canvas, labelOffset); 174 | } 175 | } 176 | 177 | @override 178 | void paint(Canvas canvas, Size size) { 179 | _drawXAxis(canvas, size); 180 | _drawGrid(canvas, size); 181 | _drawAxis(canvas, size); 182 | _drawBars(canvas, size); 183 | } 184 | 185 | @override 186 | bool shouldRepaint(BarChartPainter oldDelegate) => false; 187 | 188 | @override 189 | bool shouldRebuildSemantics(BarChartPainter oldDelegate) => false; 190 | } 191 | -------------------------------------------------------------------------------- /lib/charts/base_chart.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class BaseChart extends StatefulWidget { 4 | @override 5 | _BaseChartState createState() => _BaseChartState(); 6 | } 7 | 8 | class _BaseChartState extends State { 9 | @override 10 | Widget build(BuildContext context) { 11 | return Container(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/charts/calender_heatmap/calendar_heatmap.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import '../../utils/draw_grid.dart'; 6 | import './utils.dart'; 7 | 8 | class CalenderHeatMap extends StatelessWidget { 9 | final List datas; 10 | 11 | const CalenderHeatMap({ 12 | @required this.datas, 13 | }); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return LayoutBuilder( 18 | builder: (BuildContext context, BoxConstraints constraints) { 19 | return CustomPaint( 20 | painter: CalenderHeatMapPainter(datas: datas), 21 | size: constraints.biggest, 22 | ); 23 | }, 24 | ); 25 | } 26 | } 27 | 28 | class CalenderHeatMapPainter extends CustomPainter { 29 | final List datas; 30 | 31 | CalenderHeatMapPainter({ 32 | @required this.datas, 33 | }); 34 | 35 | final double gap = 5.0; 36 | final double blockWidth = 25.0; 37 | final double blockHeight = 25.0; 38 | final double blockRadius = 5.0; 39 | 40 | Color _generateBlockColor({double opacity = 1}) { 41 | if (opacity == 0) { 42 | return Colors.black12; 43 | } 44 | 45 | return Color.fromRGBO(45, 181, 93, opacity); 46 | } 47 | 48 | void _drawBlock(Canvas canvas, Offset offset, Color color) { 49 | final Paint paint = Paint() 50 | ..color = color 51 | ..isAntiAlias = true 52 | ..style = PaintingStyle.fill; 53 | final Rect rect = 54 | Rect.fromLTWH(offset.dx, offset.dy, blockWidth, blockHeight); 55 | final Radius radius = Radius.circular(blockRadius); 56 | final rrect = RRect.fromRectAndRadius(rect, radius); 57 | 58 | canvas.drawRRect(rrect, paint); 59 | } 60 | 61 | void _drawWeekDayTexts(Canvas canvas, Size size) { 62 | canvas.save(); 63 | canvas.translate(0, size.height / 4); 64 | Offset start = Offset(30.0, 0.0); 65 | 66 | weekDayTextEn.asMap().forEach((index, text) { 67 | TextPainter( 68 | textAlign: TextAlign.center, 69 | textDirection: TextDirection.ltr, 70 | ) 71 | ..text = TextSpan( 72 | text: text, 73 | style: TextStyle( 74 | fontSize: 24.0, 75 | fontWeight: FontWeight.bold, 76 | color: Colors.black87, 77 | ), 78 | ) 79 | ..layout( 80 | minWidth: 0.0, 81 | maxWidth: 100.0, 82 | ) 83 | ..paint(canvas, start); 84 | 85 | start += Offset(0, gap + blockHeight); 86 | }); 87 | canvas.restore(); 88 | } 89 | 90 | @override 91 | void paint(Canvas canvas, Size size) { 92 | drawGrid(canvas, size); 93 | _drawWeekDayTexts(canvas, size); 94 | 95 | canvas.save(); 96 | canvas.translate(0, size.height / 4); 97 | Offset start = Offset(100.0, 0.0); 98 | 99 | for (int i = 0; i < datas.length; i++) { 100 | Offset move; 101 | 102 | if (i == 0) { 103 | move = Offset.zero; 104 | } else { 105 | if (i % 7 == 0) { 106 | move = Offset(gap + blockWidth, -start.dy); 107 | } else { 108 | move = Offset(0, gap + blockHeight); 109 | } 110 | } 111 | start += move; 112 | 113 | final double val = datas[i]; 114 | final double maxVal = datas.reduce(max); 115 | final percent = val / maxVal; 116 | double opacity; 117 | 118 | if (percent > .8) { 119 | opacity = 1; 120 | } else if (percent > .6) { 121 | opacity = 0.8; 122 | } else if (percent > .4) { 123 | opacity = 0.6; 124 | } else if (percent < .2) { 125 | opacity = 0.4; 126 | } else { 127 | opacity = 0.0; 128 | } 129 | 130 | _drawBlock(canvas, start, _generateBlockColor(opacity: opacity)); 131 | } 132 | canvas.restore(); 133 | } 134 | 135 | @override 136 | bool shouldRepaint(CalenderHeatMapPainter oldDelegate) => false; 137 | 138 | @override 139 | bool shouldRebuildSemantics(CalenderHeatMapPainter oldDelegate) => false; 140 | } 141 | -------------------------------------------------------------------------------- /lib/charts/calender_heatmap/utils.dart: -------------------------------------------------------------------------------- 1 | leapYear(int year) { 2 | bool leapYear = false; 3 | bool leap = ((year % 100 == 0) && (year % 400 != 0)); 4 | 5 | if (leap == true) { 6 | leapYear = false; 7 | } else if (year % 4 == 0) { 8 | leapYear = true; 9 | } 10 | 11 | return leapYear; 12 | } 13 | 14 | daysInMonth(int year, int month) { 15 | List monthLength = List(12); 16 | 17 | monthLength[0] = 31; 18 | monthLength[2] = 31; 19 | monthLength[4] = 31; 20 | monthLength[6] = 31; 21 | monthLength[7] = 31; 22 | monthLength[9] = 31; 23 | monthLength[11] = 31; 24 | monthLength[3] = 30; 25 | monthLength[8] = 30; 26 | monthLength[5] = 30; 27 | monthLength[10] = 30; 28 | 29 | if (leapYear(year) == true) { 30 | monthLength[1] = 29; 31 | } else { 32 | monthLength[1] = 28; 33 | } 34 | 35 | return monthLength[month - 1]; 36 | } 37 | 38 | yearLength(int year) { 39 | int yearLength = 0; 40 | 41 | for (int counter = 1; counter < year; counter++) { 42 | if (counter >= 4) { 43 | if (leapYear(counter) == true) 44 | yearLength += 366; 45 | else 46 | yearLength += 365; 47 | } else 48 | yearLength += 365; 49 | } 50 | return yearLength; 51 | } 52 | 53 | lastDayOfMonth(int year, int month) { 54 | return DateTime(year, month + 1, 0); 55 | } 56 | 57 | firstDayOfMonth(int year, int month) { 58 | return DateTime(year, month, 1).day; 59 | } 60 | 61 | List weekDayTextEn = [ 62 | 'Mon', 63 | 'Tue', 64 | 'Wed', 65 | 'Thur', 66 | 'Fri', 67 | 'Sat', 68 | 'Sun', 69 | ]; 70 | 71 | List weekDayTextCh = [ 72 | '星期一', 73 | '星期二', 74 | '星期三', 75 | '星期四', 76 | '星期五', 77 | '星期六', 78 | '星期日', 79 | ]; 80 | 81 | List monthTextEn = [ 82 | 'Jan', 83 | 'Feb', 84 | 'Mar', 85 | 'Apr', 86 | 'May', 87 | 'Jun', 88 | 'Jul', 89 | 'Aug', 90 | 'Sep', 91 | 'Oct', 92 | 'Nov', 93 | 'Dec', 94 | ]; 95 | 96 | List monthTextCn = [ 97 | '一月', 98 | '二月', 99 | '三月', 100 | '四月', 101 | '五月', 102 | '六月', 103 | '七月', 104 | '八月', 105 | '九月', 106 | '十月', 107 | '十一月', 108 | '十二月', 109 | ]; 110 | -------------------------------------------------------------------------------- /lib/charts/column_chart.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../colors.dart'; 4 | 5 | class ColumnChart extends StatefulWidget { 6 | final List data; 7 | final List xAxis; 8 | 9 | const ColumnChart({ 10 | @required this.data, 11 | @required this.xAxis, 12 | }); 13 | 14 | @override 15 | _ColumnChartState createState() => _ColumnChartState(); 16 | } 17 | 18 | class _ColumnChartState extends State 19 | with TickerProviderStateMixin { 20 | AnimationController _controller; 21 | final _animations = []; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | double begin = 0.0; 27 | List datas = widget.data; 28 | // 初始化动画控制器,并调用 forward 方法启动动画 29 | _controller = AnimationController( 30 | vsync: this, 31 | duration: Duration(milliseconds: 3000), 32 | )..forward(); 33 | 34 | for (int i = 0; i < datas.length; i++) { 35 | final double end = datas[i]; 36 | // 使用一个补间值 Tween 创建每个矩形的动画值 37 | final Tween tween = Tween(begin: begin, end: end); 38 | // 初始化数组里面的值 39 | _animations.add(begin); 40 | 41 | // 创建补间动画 42 | Animation animation = tween.animate( 43 | CurvedAnimation( 44 | parent: _controller, 45 | curve: Curves.ease, 46 | ), 47 | ); 48 | _controller.addListener(() { 49 | // 使用 setState 更新 _animations 数组里面的动画值 50 | setState(() { 51 | _animations[i] = animation.value; 52 | }); 53 | }); 54 | } 55 | } 56 | 57 | @override 58 | Widget build(BuildContext context) { 59 | return Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: [ 62 | CustomPaint( 63 | painter: ColumnChartPainter( 64 | // 最后向 ColumnChartPainter 传入 _animations 数组 65 | datas: _animations, 66 | xAxis: widget.xAxis, 67 | animation: _controller, 68 | ), 69 | child: Container(width: 300, height: 300), 70 | ), 71 | SizedBox(height: 48), 72 | Container( 73 | decoration: BoxDecoration( 74 | color: Colors.blue, 75 | shape: BoxShape.circle, 76 | ), 77 | child: IconButton( 78 | color: Colors.white, 79 | icon: Icon(Icons.refresh), 80 | onPressed: () { 81 | _controller.reset(); 82 | _controller.forward(); 83 | }, 84 | ), 85 | ), 86 | ], 87 | ); 88 | } 89 | } 90 | 91 | class ColumnChartPainter extends CustomPainter { 92 | final List datas; 93 | final List xAxis; 94 | final Animation animation; 95 | 96 | static double _barGap = 18; 97 | static double _barWidth = _barGap * 2; 98 | static double labelFontSize = 12.0; 99 | 100 | ColumnChartPainter({ 101 | @required this.xAxis, 102 | @required this.datas, 103 | this.animation, 104 | }) : super(repaint: animation); 105 | 106 | void _drawAxis(Canvas canvas, Size size) { 107 | final double sw = size.width; 108 | final double sh = size.height; 109 | 110 | // 使用 Paint 定义路径的样式 111 | final Paint paint = Paint() 112 | ..color = Colors.black87 113 | ..style = PaintingStyle.stroke 114 | ..strokeWidth = 1.0; 115 | 116 | // 使用 Path 定义绘制的路径,从画布的左上角到左下角在到右下角 117 | final Path path = Path() 118 | ..moveTo(0, 0) 119 | ..lineTo(0, sh) 120 | ..lineTo(sw, sh); 121 | 122 | // 使用 drawPath 方法绘制路径 123 | canvas.drawPath(path, paint); 124 | } 125 | 126 | void _drawLabels(Canvas canvas, Size size) { 127 | final double gap = 50.0; 128 | final double sh = size.height; 129 | final List yAxisLabels = []; 130 | 131 | Paint paint = Paint() 132 | ..color = Colors.black87 133 | ..strokeWidth = 2.0; 134 | 135 | // 使用 50.0 为间隔绘制比传入数据多一个的标识 136 | for (int i = 1; i <= datas.length; i++) { 137 | yAxisLabels.add(gap * i); 138 | } 139 | 140 | yAxisLabels.asMap().forEach( 141 | (index, label) { 142 | // 标识的高度为画布高度减去标识的值 143 | final double top = sh - label; 144 | final rect = Rect.fromLTWH(-4, top, 4, 1); 145 | final Offset textOffset = Offset( 146 | 0 - labelFontSize * 3, 147 | top - labelFontSize / 2, 148 | ); 149 | 150 | // 绘制 Y 轴右边的线条 151 | canvas.drawRect(rect, paint); 152 | 153 | // 绘制文字需要用 `TextPainter`,最后调用 paint 方法绘制文字 154 | TextPainter( 155 | text: TextSpan( 156 | text: label.toStringAsFixed(0), 157 | style: TextStyle(fontSize: labelFontSize, color: Colors.black87), 158 | ), 159 | textAlign: TextAlign.right, 160 | textDirection: TextDirection.ltr, 161 | textWidthBasis: TextWidthBasis.longestLine, 162 | ) 163 | ..layout(minWidth: 0, maxWidth: 24) 164 | ..paint(canvas, textOffset); 165 | }, 166 | ); 167 | } 168 | 169 | void _darwBars(Canvas canvas, Size size) { 170 | final sh = size.height; 171 | final paint = Paint()..style = PaintingStyle.fill; 172 | 173 | for (int i = 0; i < datas.length; i++) { 174 | // 每个矩形使用预设的 colors 数组里面的颜色 175 | paint.color = colors[i]; 176 | final double textFontSize = 14.0; 177 | final double data = datas[i]; 178 | // 矩形的上边缘为画布高度减去数据值 179 | final double top = sh - data; 180 | // 矩形的左边缘为当前索引值乘以矩形宽度加上矩形之间的间距 181 | final double left = i * _barWidth + (i * _barGap) + _barGap; 182 | 183 | // 使用 Rect.fromLTWH 方法创建要绘制的矩形 184 | final rect = Rect.fromLTWH(left, top, _barWidth, data); 185 | // 使用 drawRect 方法绘制矩形 186 | canvas.drawRect(rect, paint); 187 | 188 | final offset = Offset( 189 | left + _barWidth / 2 - textFontSize * 1.2, 190 | top - textFontSize * 2, 191 | ); 192 | // 使用 TextPainter 绘制矩形上放的数值 193 | TextPainter( 194 | text: TextSpan( 195 | text: data.toStringAsFixed(1), 196 | style: TextStyle(fontSize: textFontSize, color: paint.color), 197 | ), 198 | textAlign: TextAlign.center, 199 | textDirection: TextDirection.ltr, 200 | ) 201 | ..layout( 202 | minWidth: 0, 203 | maxWidth: textFontSize * data.toString().length, 204 | ) 205 | ..paint(canvas, offset); 206 | 207 | final xData = xAxis[i]; 208 | final xOffset = Offset(left + _barWidth / 2 - textFontSize, sh + 12); 209 | // 绘制横轴标识 210 | TextPainter( 211 | textAlign: TextAlign.center, 212 | text: TextSpan( 213 | text: '$xData', 214 | style: TextStyle(fontSize: 12, color: Colors.black87), 215 | ), 216 | textDirection: TextDirection.ltr, 217 | ) 218 | ..layout( 219 | minWidth: 0, 220 | maxWidth: size.width, 221 | ) 222 | ..paint(canvas, xOffset); 223 | } 224 | } 225 | 226 | @override 227 | void paint(Canvas canvas, Size size) { 228 | _drawAxis(canvas, size); 229 | _drawLabels(canvas, size); 230 | _darwBars(canvas, size); 231 | } 232 | 233 | @override 234 | bool shouldRepaint(ColumnChartPainter oldDelegate) => true; 235 | 236 | @override 237 | bool shouldRebuildSemantics(ColumnChartPainter oldDelegate) => false; 238 | } 239 | -------------------------------------------------------------------------------- /lib/charts/donut_chart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_charts/colors.dart'; 5 | 6 | class DonutCahrt extends StatefulWidget { 7 | final List> datas; 8 | 9 | const DonutCahrt({this.datas}); 10 | 11 | @override 12 | _DonutCahrtState createState() => _DonutCahrtState(); 13 | } 14 | 15 | class _DonutCahrtState extends State { 16 | @override 17 | Widget build(BuildContext context) { 18 | return LayoutBuilder( 19 | builder: (BuildContext context, BoxConstraints constraints) { 20 | return CustomPaint( 21 | size: constraints.biggest, 22 | painter: DonutChartPainter(widget.datas), 23 | ); 24 | }, 25 | ); 26 | } 27 | } 28 | 29 | class DonutChartPainter extends CustomPainter { 30 | final List> datas; 31 | 32 | DonutChartPainter(this.datas); 33 | 34 | double initStartAngle = 0.0; 35 | 36 | void _drawBackgroudArc(Canvas canvas, Size size) { 37 | Paint paint = Paint() 38 | ..color = Colors.black12 39 | ..strokeWidth = 40.0 40 | ..style = PaintingStyle.stroke 41 | ..isAntiAlias = true; 42 | 43 | Rect rect = Rect.fromCircle( 44 | radius: 100.0, 45 | center: Offset(size.width / 2, size.height / 2), 46 | ); 47 | 48 | double startAngle = 0 / pi; 49 | double sweepAngle = 180 / pi; 50 | 51 | canvas.drawArc(rect, startAngle, sweepAngle, false, paint); 52 | } 53 | 54 | void _drawItemArc( 55 | double value, 56 | total, 57 | double startAngle, 58 | int index, 59 | Canvas canvas, 60 | Size size, 61 | ) { 62 | Paint paint = Paint() 63 | ..color = colors[index % colors.length] 64 | ..strokeWidth = 40.0 65 | ..style = PaintingStyle.stroke 66 | ..isAntiAlias = true; 67 | Rect rect = Rect.fromCircle( 68 | radius: 100.0, 69 | center: Offset(size.width / 2, size.height / 2), 70 | ); 71 | 72 | double sweepAngle = -value / total * pi * 2; 73 | initStartAngle = startAngle + sweepAngle; 74 | 75 | canvas.drawArc(rect, startAngle, sweepAngle, false, paint); 76 | } 77 | 78 | void _drawTotalText(double total, Canvas canvas, Offset center) { 79 | TextPainter( 80 | textAlign: TextAlign.center, 81 | textDirection: TextDirection.ltr, 82 | ) 83 | ..text = TextSpan( 84 | text: total.toString(), 85 | style: TextStyle( 86 | fontSize: 32.0, 87 | color: Colors.black, 88 | ), 89 | ) 90 | ..layout( 91 | minWidth: 0.0, 92 | maxWidth: 100.0, 93 | ) 94 | ..paint(canvas, Offset(center.dx - 32.0, center.dy - 16.0)); 95 | } 96 | 97 | @override 98 | void paint(Canvas canvas, Size size) { 99 | var total = 0.0; 100 | Offset center = Offset(size.width / 2, size.height / 2); 101 | datas.forEach((item) { 102 | total += item['value']; 103 | }); 104 | 105 | _drawBackgroudArc(canvas, size); 106 | _drawTotalText(total, canvas, center); 107 | datas.asMap().forEach((index, item) { 108 | _drawItemArc(item['value'], total, initStartAngle, index, canvas, size); 109 | }); 110 | } 111 | 112 | @override 113 | bool shouldRepaint(DonutChartPainter oldDelegate) => false; 114 | 115 | @override 116 | bool shouldRebuildSemantics(DonutChartPainter oldDelegate) => false; 117 | } 118 | -------------------------------------------------------------------------------- /lib/charts/line_chart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | import 'package:flutter/material.dart'; 3 | import '../colors.dart'; 4 | import '../utils/create_animated_path.dart'; 5 | 6 | class LineChart extends StatefulWidget { 7 | final List datas; 8 | final List xAxis; 9 | 10 | const LineChart({ 11 | @required this.datas, 12 | @required this.xAxis, 13 | }); 14 | 15 | @override 16 | _LineChartState createState() => _LineChartState(); 17 | } 18 | 19 | class _LineChartState extends State 20 | with SingleTickerProviderStateMixin { 21 | AnimationController _controller; 22 | final _animationPoints = []; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | double begin = 0.0; 28 | double end = 4.0; 29 | List datas = widget.datas; 30 | _controller = AnimationController( 31 | vsync: this, 32 | duration: Duration(milliseconds: 3000), 33 | ); 34 | 35 | final double interval = 1 / datas.length; 36 | 37 | for (int i = 0; i < datas.length; i++) { 38 | _animationPoints.add(begin); 39 | final tween = Tween(begin: begin, end: end); 40 | 41 | Animation animation = tween.animate( 42 | CurvedAnimation( 43 | parent: _controller, 44 | curve: Interval( 45 | interval * i, 46 | interval * i + interval, 47 | curve: Curves.ease, 48 | ), 49 | ), 50 | ); 51 | _controller.addListener(() { 52 | _animationPoints[i] = animation.value; 53 | }); 54 | } 55 | 56 | _controller.forward(); 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | return Column( 62 | mainAxisAlignment: MainAxisAlignment.center, 63 | children: [ 64 | CustomPaint( 65 | painter: LineChartPainter( 66 | xAxis: widget.xAxis, 67 | datas: widget.datas, 68 | animation: _controller, 69 | points: _animationPoints, 70 | ), 71 | child: Container(width: 300, height: 300), 72 | ), 73 | SizedBox(height: 48), 74 | Container( 75 | decoration: BoxDecoration( 76 | color: Colors.blue, 77 | shape: BoxShape.circle, 78 | ), 79 | child: IconButton( 80 | color: Colors.white, 81 | icon: Icon(Icons.refresh), 82 | onPressed: () { 83 | _controller.reset(); 84 | _controller.forward(); 85 | }, 86 | ), 87 | ), 88 | ], 89 | ); 90 | } 91 | } 92 | 93 | class LineChartPainter extends CustomPainter { 94 | final List datas; 95 | final List xAxis; 96 | final List points; 97 | final Animation animation; 98 | 99 | LineChartPainter({ 100 | @required this.datas, 101 | @required this.xAxis, 102 | @required this.points, 103 | @required this.animation, 104 | }) : super(repaint: animation); 105 | 106 | void _drawLines(Canvas canvas, Size size) { 107 | final sw = size.width; 108 | final sh = size.height; 109 | final gap = sw / datas.length; 110 | final paint = Paint() 111 | ..style = PaintingStyle.stroke 112 | ..color = Colors.blue 113 | ..isAntiAlias = true 114 | ..strokeCap = StrokeCap.round 115 | ..strokeJoin = StrokeJoin.round 116 | ..strokeWidth = 1.5; 117 | 118 | final path = Path()..moveTo(gap / 2, sh - datas[0]); 119 | 120 | for (int i = 1; i < datas.length; i++) { 121 | final data = datas[i]; 122 | final x = gap * i + gap / 2; 123 | final y = sh - data; 124 | 125 | path.lineTo(x, y); 126 | } 127 | 128 | canvas.drawPath(createAnimatedPath(path, animation.value), paint); 129 | } 130 | 131 | void _drawPoints(Canvas canvas, Size size) { 132 | final sw = size.width; 133 | final sh = size.height; 134 | final gap = sw / datas.length; 135 | 136 | final paint = Paint() 137 | ..isAntiAlias = true 138 | ..style = PaintingStyle.fill; 139 | 140 | for (int i = 0; i < datas.length; i++) { 141 | paint.color = colors[i]; 142 | final data = datas[i]; 143 | final dx = 0.0; 144 | final dy = sh - data; 145 | final offset = Offset(dx + gap * i + gap / 2, dy); 146 | canvas.drawCircle(offset, points[i], paint); 147 | 148 | final textOffset = Offset(dx + gap * i + 14, dy - 30); 149 | TextPainter( 150 | text: TextSpan( 151 | text: '$data', 152 | style: TextStyle( 153 | fontSize: points[i] * 3, 154 | color: Colors.black87, 155 | ), 156 | ), 157 | textDirection: TextDirection.ltr, 158 | ) 159 | ..layout( 160 | minWidth: 0, 161 | maxWidth: size.width, 162 | ) 163 | ..paint(canvas, textOffset); 164 | 165 | final xData = xAxis[i]; 166 | final double labelFontSize = 12.0; 167 | final xOffset = Offset(dx + gap * i + labelFontSize, sh + labelFontSize); 168 | 169 | TextPainter( 170 | textAlign: TextAlign.center, 171 | text: TextSpan( 172 | text: '$xData', 173 | style: TextStyle( 174 | fontSize: labelFontSize, 175 | color: Colors.black87, 176 | ), 177 | ), 178 | textDirection: TextDirection.ltr, 179 | ) 180 | ..layout( 181 | minWidth: 0, 182 | maxWidth: size.width, 183 | ) 184 | ..paint(canvas, xOffset); 185 | } 186 | } 187 | 188 | void _drawAxis(Canvas canvas, Size size) { 189 | final double sw = size.width; 190 | final double sh = size.height; 191 | 192 | // 设置绘制属性 193 | final paint = Paint() 194 | ..color = Colors.black87 195 | ..style = PaintingStyle.stroke 196 | ..strokeWidth = 1.0 197 | ..isAntiAlias = true; 198 | 199 | // 创建一个 Path 对象,并规定它的路线 200 | final Path path = Path() 201 | ..moveTo(0.0, 0.0) 202 | ..lineTo(0.0, sh) 203 | ..lineTo(sw, sh); 204 | // 绘制路径 205 | canvas.drawPath(path, paint); 206 | } 207 | 208 | void _drawLabels(Canvas canvas, Size size) { 209 | final double labelFontSize = 12.0; 210 | final double gap = 50.0; 211 | final double sh = size.height; 212 | final List yAxisLabels = []; 213 | 214 | Paint paint = Paint() 215 | ..color = Colors.black87 216 | ..strokeWidth = 2.0; 217 | 218 | // 使用 50.0 为间隔绘制比传入数据多一个的标识 219 | for (int i = 0; i <= datas.length; i++) { 220 | yAxisLabels.add(gap * i); 221 | } 222 | 223 | yAxisLabels.asMap().forEach( 224 | (index, label) { 225 | // 标识的高度为画布高度减去标识的值 226 | final double top = sh - label; 227 | final rect = Rect.fromLTWH(0.0, top, 4, 1); 228 | final Offset textOffset = Offset( 229 | 0 - labelFontSize * 3, 230 | top - labelFontSize / 2, 231 | ); 232 | 233 | // 绘制 Y 轴右边的线条 234 | canvas.drawRect(rect, paint); 235 | 236 | // 绘制文字需要用 `TextPainter`,最后调用 paint 方法绘制文字 237 | TextPainter( 238 | text: TextSpan( 239 | text: label.toStringAsFixed(0), 240 | style: TextStyle(fontSize: labelFontSize, color: Colors.black87), 241 | ), 242 | textAlign: TextAlign.right, 243 | textDirection: TextDirection.ltr, 244 | textWidthBasis: TextWidthBasis.longestLine, 245 | ) 246 | ..layout(minWidth: 0, maxWidth: 24) 247 | ..paint(canvas, textOffset); 248 | }, 249 | ); 250 | } 251 | 252 | @override 253 | void paint(Canvas canvas, Size size) { 254 | _drawAxis(canvas, size); 255 | _drawLabels(canvas, size); 256 | _drawLines(canvas, size); 257 | _drawPoints(canvas, size); 258 | } 259 | 260 | @override 261 | bool shouldRepaint(LineChartPainter oldDelegate) => true; 262 | 263 | @override 264 | bool shouldRebuildSemantics(LineChartPainter oldDelegate) => false; 265 | } 266 | -------------------------------------------------------------------------------- /lib/charts/pie_chart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import '../colors.dart'; 6 | 7 | class PiePart { 8 | double sweepAngle; 9 | final Color color; 10 | final double startAngle; 11 | 12 | PiePart( 13 | this.startAngle, 14 | this.sweepAngle, 15 | this.color, 16 | ); 17 | } 18 | 19 | class PieChart extends StatefulWidget { 20 | final List datas; 21 | final List legends; 22 | 23 | const PieChart({ 24 | @required this.datas, 25 | @required this.legends, 26 | }); 27 | 28 | @override 29 | _PieChartState createState() => _PieChartState(); 30 | } 31 | 32 | class _PieChartState extends State with TickerProviderStateMixin { 33 | double _total = 0.0; 34 | AnimationController _controller; 35 | List _animateDatas = []; 36 | final List _parts = []; 37 | 38 | @override 39 | void initState() { 40 | super.initState(); 41 | // 初始化动画控制器 42 | _controller = AnimationController( 43 | duration: Duration(milliseconds: 3000), 44 | vsync: this, 45 | ); 46 | 47 | List datas = widget.datas; 48 | // 计算出数据总和 49 | _total = datas.reduce((a, b) => a + b); 50 | // 设置一个起始变量 51 | double startAngle = 0.0; 52 | 53 | for (int i = 0; i < datas.length; i++) { 54 | // 填充动画数组 55 | _animateDatas.add(0.0); 56 | final data = datas[i]; 57 | // 计算出每个数据所占的弧度值 58 | final angle = (data / _total) * -math.pi * 2; 59 | PiePart peiPart; 60 | 61 | if (i > 0) { 62 | // 下一个数据的起始弧度值等于之前的弧度值相加 63 | double lastSweepAngle = _parts[i - 1].sweepAngle; 64 | startAngle += lastSweepAngle; 65 | peiPart = PiePart(startAngle, angle, colors[i]); 66 | } else { 67 | // 第一个数据的起始弧度为 0.0 68 | peiPart = PiePart(0.0, angle, colors[i]); 69 | } 70 | // 添加到数组中 71 | _parts.add(peiPart); 72 | 73 | CurvedAnimation curvedAnimation = CurvedAnimation( 74 | parent: _controller, 75 | curve: Curves.ease, 76 | ); 77 | 78 | // 创建弧形的补间动画 79 | final partTween = Tween(begin: 0.0, end: peiPart.sweepAngle); 80 | Animation animation = partTween.animate(curvedAnimation); 81 | 82 | // 创建文字的补间动画 83 | final percentTween = Tween(begin: 0.0, end: data); 84 | Animation percentAnimation = 85 | percentTween.animate(curvedAnimation); 86 | 87 | // 在动画启动后不断改变数据值 88 | _controller.addListener(() { 89 | _parts[i].sweepAngle = animation.value; 90 | _animateDatas[i] = 91 | double.parse(percentAnimation.value.toStringAsFixed(1)); 92 | setState(() {}); 93 | }); 94 | // 开始动画 95 | _controller.forward(); 96 | } 97 | } 98 | 99 | @override 100 | Widget build(BuildContext context) { 101 | return Column( 102 | mainAxisAlignment: MainAxisAlignment.center, 103 | children: [ 104 | Container( 105 | width: 300, 106 | height: 300, 107 | child: CustomPaint( 108 | // 将数据传给 PeiChartPainter 109 | painter: PeiChartPainter( 110 | total: _total, 111 | parts: _parts, 112 | datas: _animateDatas, 113 | legends: widget.legends, 114 | ), 115 | ), 116 | ), 117 | SizedBox(height: 80), 118 | Container( 119 | decoration: BoxDecoration( 120 | color: Colors.blue, 121 | shape: BoxShape.circle, 122 | ), 123 | child: IconButton( 124 | color: Colors.white, 125 | icon: Icon(Icons.refresh), 126 | onPressed: () { 127 | _controller.reset(); 128 | _controller.forward(); 129 | }, 130 | ), 131 | ), 132 | ], 133 | ); 134 | } 135 | } 136 | 137 | class PeiChartPainter extends CustomPainter { 138 | final double total; 139 | final List datas; 140 | final List parts; 141 | final List legends; 142 | 143 | PeiChartPainter({ 144 | @required this.total, 145 | @required this.datas, 146 | @required this.legends, 147 | @required this.parts, 148 | }); 149 | 150 | void drawParts(Canvas canvas, Size size) { 151 | final sw = size.width; 152 | final sh = size.height; 153 | final double fontSize = 10.0; 154 | final double radius = math.min(sw, sh) / 2; 155 | final Offset center = Offset(sw / 2, sh / 2); 156 | 157 | // 创建弧形依照的矩形 158 | final rect = Rect.fromCenter( 159 | center: center, 160 | width: radius * 2, 161 | height: radius * 2, 162 | ); 163 | // 设置绘制属性 164 | final paint = Paint() 165 | ..strokeWidth = 0.0 166 | ..isAntiAlias = true 167 | ..style = PaintingStyle.fill; 168 | 169 | for (int i = 0; i < parts.length; i++) { 170 | final PiePart part = parts[i]; 171 | // 设置每部分的颜色 172 | paint.color = part.color; 173 | // 使用 drawArc 方法画出弧形,参数依次是依照的矩形,起始弧度值,占据的弧度值,是否从中心点绘制,绘制属性 174 | canvas.drawArc(rect, part.startAngle, part.sweepAngle, true, paint); 175 | 176 | final double data = datas[i]; 177 | // 计算每部分占比 178 | final String percent = (data / total * 100).toStringAsFixed(1); 179 | final double radians = part.startAngle + part.sweepAngle / 2; 180 | // 使用三角函数计算文字位置 181 | double x = math.cos(radians) * radius / 2 + sw / 2 - fontSize * 3; 182 | double y = math.sin(radians) * radius / 2 + sh / 2; 183 | final Offset offset = Offset(x, y); 184 | 185 | // 使用 TextPainter 绘制文字标识 186 | TextPainter( 187 | textAlign: TextAlign.start, 188 | text: TextSpan( 189 | text: '$data $percent%', 190 | style: TextStyle( 191 | fontSize: fontSize, 192 | color: Colors.white, 193 | fontWeight: FontWeight.bold, 194 | ), 195 | ), 196 | textDirection: TextDirection.ltr, 197 | ) 198 | ..layout( 199 | minWidth: 0, 200 | maxWidth: size.width, 201 | ) 202 | ..paint(canvas, offset); 203 | } 204 | } 205 | 206 | void drawCircle(Canvas canvas, Size size) { 207 | final sw = size.width; 208 | final sh = size.height; 209 | // 确定圆的半径 210 | final double radius = math.min(sw, sh) / 2; 211 | // 定义中心点 212 | final Offset center = Offset(sw / 2, sh / 2); 213 | 214 | // 定义圆形的绘制属性 215 | final paint = Paint() 216 | ..style = PaintingStyle.stroke 217 | ..color = Colors.grey 218 | ..strokeWidth = 1.0; 219 | 220 | // 使用 Canvas 的 drawCircle 绘制 221 | canvas.drawCircle(center, radius, paint); 222 | } 223 | 224 | void drawLegends(Canvas canvas, Size size) { 225 | final sw = size.width; 226 | final sh = size.height; 227 | final double radius = math.min(sw, sh) / 2; 228 | final double fontSize = 12.0; 229 | 230 | for (int i = 0; i < datas.length; i++) { 231 | final PiePart part = parts[i]; 232 | final String legend = legends[i]; 233 | // 根据每部分的起始弧度加上自身弧度值的一半得到每部分的中间弧度值 234 | final radians = part.startAngle + part.sweepAngle / 2; 235 | // 根据三角函数计算中出标识文字的 x 和 y 位置,需要加上宽和高的一半适配 Canvas 的坐标 236 | double x = math.cos(radians) * (radius + 32) + sw / 2 - fontSize; 237 | double y = math.sin(radians) * (radius + 32) + sh / 2; 238 | final offset = Offset(x, y); 239 | 240 | // 使用 TextPainter 绘制文字标识 241 | TextPainter( 242 | textAlign: TextAlign.center, 243 | text: TextSpan( 244 | text: legend, 245 | style: TextStyle( 246 | fontSize: fontSize, 247 | color: Colors.black, 248 | ), 249 | ), 250 | textDirection: TextDirection.ltr, 251 | ) 252 | ..layout( 253 | minWidth: 0, 254 | maxWidth: size.width, 255 | ) 256 | ..paint(canvas, offset); 257 | } 258 | } 259 | 260 | @override 261 | void paint(Canvas canvas, Size size) { 262 | drawCircle(canvas, size); 263 | drawLegends(canvas, size); 264 | drawParts(canvas, size); 265 | } 266 | 267 | @override 268 | bool shouldRepaint(PeiChartPainter oldDelegate) => true; 269 | 270 | @override 271 | bool shouldRebuildSemantics(PeiChartPainter oldDelegate) => false; 272 | } 273 | -------------------------------------------------------------------------------- /lib/charts/radar_chart.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../colors.dart'; 5 | import '../utils/create_animated_path.dart'; 6 | 7 | class RadarChart extends StatefulWidget { 8 | final List scores; 9 | final List features; 10 | final List> datas; 11 | 12 | const RadarChart({ 13 | @required this.datas, 14 | @required this.scores, 15 | @required this.features, 16 | }); 17 | 18 | @override 19 | _RadarChartState createState() => _RadarChartState(); 20 | } 21 | 22 | class _RadarChartState extends State with TickerProviderStateMixin { 23 | AnimationController _controller; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | _controller = AnimationController( 29 | vsync: this, 30 | duration: Duration(milliseconds: 3000), 31 | )..forward(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | return Column( 37 | mainAxisAlignment: MainAxisAlignment.center, 38 | children: [ 39 | CustomPaint( 40 | size: Size(300, 300), 41 | painter: RadarChartPainter( 42 | datas: widget.datas, 43 | scores: widget.scores, 44 | features: widget.features, 45 | animation: _controller, 46 | ), 47 | ), 48 | SizedBox(height: 48), 49 | Container( 50 | decoration: BoxDecoration( 51 | color: Colors.blue, 52 | shape: BoxShape.circle, 53 | ), 54 | child: IconButton( 55 | color: Colors.white, 56 | icon: Icon(Icons.refresh), 57 | onPressed: () { 58 | _controller.reset(); 59 | _controller.forward(); 60 | }, 61 | ), 62 | ), 63 | ], 64 | ); 65 | } 66 | } 67 | 68 | class RadarChartPainter extends CustomPainter { 69 | final List features; 70 | final List scores; 71 | final List> datas; 72 | final Animation animation; 73 | 74 | RadarChartPainter({ 75 | @required this.scores, 76 | @required this.datas, 77 | @required this.features, 78 | @required this.animation, 79 | }) : super(repaint: animation); 80 | 81 | void _drawOutline(Canvas canvas, Size size) { 82 | Paint outlinePaint = Paint() 83 | ..color = Colors.black 84 | ..style = PaintingStyle.stroke 85 | ..strokeWidth = 1.0 86 | ..isAntiAlias = true; 87 | 88 | double centerX = size.width / 2.0; 89 | double centerY = size.height / 2.0; 90 | Offset centerOffset = Offset(centerX, centerY); 91 | double radius = centerX * 0.8; 92 | 93 | canvas.drawCircle(centerOffset, radius, outlinePaint); 94 | } 95 | 96 | void _drawScores(Canvas canvas, Size size) { 97 | double centerX = size.width / 2.0; 98 | double centerY = size.height / 2.0; 99 | Offset centerOffset = Offset(centerX, centerY); 100 | double radius = centerX * 0.8; 101 | 102 | double scoreDistance = radius / (scores.length + 1); 103 | const double scoreLabelFontSize = 10; 104 | 105 | Paint scoresPaint = Paint() 106 | ..color = Colors.grey[500] 107 | ..style = PaintingStyle.stroke 108 | ..strokeWidth = 1.0 109 | ..isAntiAlias = true; 110 | 111 | scores.asMap().forEach((index, score) { 112 | double scoreRadius = scoreDistance * (index + 1); 113 | canvas.drawCircle(centerOffset, scoreRadius, scoresPaint); 114 | TextPainter( 115 | text: TextSpan( 116 | text: score.toString(), 117 | style: TextStyle(color: Colors.grey, fontSize: scoreLabelFontSize), 118 | ), 119 | textDirection: TextDirection.ltr, 120 | ) 121 | ..layout(minWidth: 0, maxWidth: size.width) 122 | ..paint( 123 | canvas, 124 | Offset( 125 | centerX - scoreLabelFontSize, 126 | centerY - scoreRadius - scoreLabelFontSize, 127 | ), 128 | ); 129 | }); 130 | } 131 | 132 | void _drawRotateText({ 133 | @required Canvas canvas, 134 | @required Size size, 135 | @required String text, 136 | @required Offset offset, 137 | @required double radius, 138 | Color color = Colors.black, 139 | double fontSize = 12.0, 140 | }) { 141 | canvas.save(); 142 | canvas.rotate(radius); 143 | 144 | TextPainter( 145 | text: TextSpan( 146 | style: TextStyle( 147 | color: color, 148 | fontSize: fontSize, 149 | ), 150 | text: text, 151 | ), 152 | textAlign: TextAlign.center, 153 | textDirection: TextDirection.ltr, 154 | ) 155 | ..layout(minWidth: 0, maxWidth: size.width) 156 | ..paint(canvas, offset); 157 | canvas.restore(); 158 | } 159 | 160 | void _drawFetures(Canvas canvas, Size size) { 161 | double centerX = size.width / 2.0; 162 | double centerY = size.height / 2.0; 163 | Offset centerOffset = Offset(centerX, centerY); 164 | double radius = centerX * 0.8; 165 | 166 | double angle = (2 * pi) / features.length; 167 | const double featureLabelFontSize = 14; 168 | const double featureLabelFontWidth = 12; 169 | 170 | features.asMap().forEach( 171 | (index, feature) { 172 | double xAngle = cos(angle * index - pi / 2); 173 | double yAngle = sin(angle * index - pi / 2); 174 | 175 | Offset featureOffset = Offset( 176 | centerX + radius * xAngle, 177 | centerY + radius * yAngle, 178 | ); 179 | 180 | Paint linePaint = Paint() 181 | ..color = Colors.grey[500] 182 | ..style = PaintingStyle.stroke 183 | ..strokeWidth = 1.0 184 | ..isAntiAlias = true; 185 | 186 | canvas.drawLine(centerOffset, featureOffset, linePaint); 187 | 188 | double labelYOffset = 189 | yAngle < 0 ? -featureLabelFontSize * 2.5 : featureLabelFontSize * 2; 190 | double labelXOffset = xAngle < 0 191 | ? -featureLabelFontWidth * (feature.length + 1) 192 | : -featureLabelFontWidth / 1.5; 193 | 194 | _drawRotateText( 195 | canvas: canvas, 196 | size: size, 197 | text: feature, 198 | radius: 0.0, 199 | fontSize: featureLabelFontSize, 200 | offset: Offset( 201 | featureOffset.dx + labelXOffset, 202 | featureOffset.dy + labelYOffset, 203 | ), 204 | ); 205 | }, 206 | ); 207 | } 208 | 209 | void _drawDatas(Canvas canvas, Size size) { 210 | double centerX = size.width / 2.0; 211 | double centerY = size.height / 2.0; 212 | double radius = centerX * 0.8; 213 | 214 | var angle = (2 * pi) / features.length; 215 | var scale = radius / scores.last; 216 | 217 | datas.asMap().forEach( 218 | (index, graph) { 219 | Paint graphPaint = Paint() 220 | ..color = colors[index % colors.length].withOpacity(0.2) 221 | ..style = PaintingStyle.fill; 222 | 223 | Color outLineColor = colors[index % colors.length]; 224 | 225 | Paint graphOutlinePaint = Paint() 226 | ..color = outLineColor 227 | ..style = PaintingStyle.stroke 228 | ..strokeWidth = 2.0 229 | ..isAntiAlias = true; 230 | 231 | Path path = Path(); 232 | double scoreSize = 12.0; 233 | double scaledPoint = scale * graph[0]; 234 | path.moveTo(centerX, centerY - scaledPoint); 235 | 236 | _drawRotateText( 237 | canvas: canvas, 238 | size: size, 239 | text: graph[0].toStringAsFixed(1), 240 | radius: 0.0, 241 | fontSize: scoreSize, 242 | color: outLineColor, 243 | offset: Offset(centerX, centerY - scaledPoint), 244 | ); 245 | 246 | graph.sublist(1).asMap().forEach( 247 | (index, point) { 248 | double scaledPoint = scale * point; 249 | double xAngle = cos(angle * (index + 1) - pi / 2); 250 | double yAngle = sin(angle * (index + 1) - pi / 2); 251 | double x = centerX + scaledPoint * xAngle; 252 | double y = centerY + scaledPoint * yAngle; 253 | 254 | path.lineTo(x, y); 255 | 256 | _drawRotateText( 257 | canvas: canvas, 258 | size: size, 259 | text: point.toStringAsFixed(1), 260 | radius: 0.0, 261 | fontSize: scoreSize, 262 | color: outLineColor, 263 | offset: Offset(x - scoreSize, y), 264 | ); 265 | }, 266 | ); 267 | 268 | path.close(); 269 | canvas.drawPath( 270 | createAnimatedPath(path, animation.value), 271 | graphPaint, 272 | ); 273 | canvas.drawPath( 274 | createAnimatedPath(path, animation.value), 275 | graphOutlinePaint, 276 | ); 277 | }, 278 | ); 279 | } 280 | 281 | void paint(Canvas canvas, Size size) { 282 | _drawOutline(canvas, size); 283 | _drawScores(canvas, size); 284 | _drawFetures(canvas, size); 285 | _drawDatas(canvas, size); 286 | } 287 | 288 | @override 289 | bool shouldRepaint(RadarChartPainter oldDelegate) { 290 | return false; 291 | } 292 | } 293 | -------------------------------------------------------------------------------- /lib/charts/time_sheet/time_sheet.dart: -------------------------------------------------------------------------------- 1 | // https://github.com/sbstjn/timesheet.js/ 2 | -------------------------------------------------------------------------------- /lib/charts/tree_map/draw_tree_rects.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'package:flutter/material.dart'; 3 | 4 | import '../../colors.dart'; 5 | import './tree_node.dart'; 6 | 7 | TextPainter textPainter = TextPainter( 8 | text: TextSpan(), 9 | textAlign: TextAlign.center, 10 | textDirection: TextDirection.ltr, 11 | )..layout( 12 | minWidth: 0.0, 13 | maxWidth: 100.0, 14 | ); 15 | 16 | void _drawText(double value, Canvas canvas, Offset offset) { 17 | TextSpan textSpan = TextSpan( 18 | text: value.toString(), 19 | style: TextStyle( 20 | fontSize: 24.0, 21 | color: Colors.white, 22 | ), 23 | ); 24 | 25 | textPainter 26 | ..text = textSpan 27 | ..layout( 28 | minWidth: 0.0, 29 | maxWidth: 100.0, 30 | ) 31 | ..paint(canvas, offset); 32 | } 33 | 34 | Paint linePaint = Paint() 35 | ..style = PaintingStyle.stroke 36 | ..color = Colors.white 37 | ..strokeWidth = 2.0 38 | ..isAntiAlias = true; 39 | 40 | Paint paint = Paint() 41 | ..style = PaintingStyle.fill 42 | ..color = colors[0] 43 | ..isAntiAlias = true; 44 | 45 | void drawTreeRects(TreeNode node, Rect rootRectLeft, TreeNode rootNodeLeft, 46 | int level, Canvas canvas, 47 | {double scale = 1.0, Color color = Colors.blue}) { 48 | if (node == null) { 49 | return; 50 | } 51 | if (node.left == null && node.right == null) { 52 | return; 53 | } 54 | 55 | // print({ 56 | // 'level': level, 57 | // 'node: ': node.value, 58 | // 'node.left: ': node.left.value, 59 | // 'node.right: ': node.right.value, 60 | // }); 61 | 62 | // paint.color = color; 63 | paint.color = colors[Random().nextInt(colors.length)]; 64 | 65 | Rect rectLeft; 66 | Rect rectRight; 67 | 68 | if (level.isEven) { 69 | double left; 70 | double top; 71 | double width; 72 | double height; 73 | 74 | top = rootRectLeft.top; 75 | left = rootRectLeft.left; 76 | width = rootRectLeft.width; 77 | height = (node.left.value / rootNodeLeft.value) * rootRectLeft.height; 78 | 79 | double scaleWidth = width * scale; 80 | double scaleHeight = height * scale; 81 | 82 | Offset rectLeftCenter = Offset(width / 2 + left, height / 2 + top); 83 | rectLeft = Rect.fromCenter( 84 | center: rectLeftCenter, 85 | width: scaleWidth, 86 | height: scaleHeight, 87 | ); 88 | canvas.drawRect(rectLeft, paint); 89 | 90 | Rect rectLineLeft = Rect.fromLTWH(left, top, width, height); 91 | canvas.drawRect(rectLineLeft, linePaint); 92 | 93 | _drawText( 94 | node.left.value, 95 | canvas, 96 | Offset(left + width / 2 - 12.0, top + height / 2 - 12.0), 97 | ); 98 | 99 | left = rootRectLeft.left; 100 | top = rootRectLeft.top + height; 101 | width = rootRectLeft.width; 102 | height = (node.right.value / rootNodeLeft.value) * rootRectLeft.height; 103 | 104 | scaleWidth = width * scale; 105 | scaleHeight = height * scale; 106 | 107 | Offset rectRightCenter = Offset(width / 2 + left, height / 2 + top); 108 | rectRight = Rect.fromCenter( 109 | center: rectRightCenter, 110 | width: scaleWidth, 111 | height: scaleHeight, 112 | ); 113 | canvas.drawRect(rectRight, paint); 114 | 115 | Rect rectLineRight = Rect.fromLTWH(left, top, width, height); 116 | canvas.drawRect(rectLineRight, linePaint); 117 | 118 | _drawText( 119 | node.right.value, 120 | canvas, 121 | Offset(left + width / 2 - 12.0, top + height / 2 - 12.0), 122 | ); 123 | } else { 124 | double left; 125 | double width; 126 | double top = rootRectLeft.top; 127 | double height = rootRectLeft.height; 128 | 129 | // left node 130 | left = rootRectLeft.left; 131 | width = (node.left.value / rootNodeLeft.value) * rootRectLeft.width; 132 | 133 | double scaleWidth = width * scale; 134 | double scaleHeight = height * scale; 135 | 136 | Offset rectLeftCenter = Offset(width / 2 + left, height / 2 + top); 137 | rectLeft = Rect.fromCenter( 138 | center: rectLeftCenter, 139 | width: scaleWidth, 140 | height: scaleHeight, 141 | ); 142 | canvas.drawRect(rectLeft, paint); 143 | 144 | Rect rectLineLeft = Rect.fromLTWH(left, top, width, height); 145 | canvas.drawRect(rectLineLeft, linePaint); 146 | 147 | _drawText( 148 | node.left.value, 149 | canvas, 150 | Offset(left + width / 2 - 12.0, top + height / 2 - 12.0), 151 | ); 152 | 153 | top = rootRectLeft.top; 154 | height = rootRectLeft.height; 155 | left = rootRectLeft.left + width; 156 | width = (node.right.value / rootNodeLeft.value) * rootRectLeft.width; 157 | 158 | scaleWidth = width * scale; 159 | scaleHeight = height * scale; 160 | 161 | Offset rectRightCenter = Offset(width / 2 + left, height / 2 + top); 162 | rectRight = Rect.fromCenter( 163 | center: rectRightCenter, 164 | width: scaleWidth, 165 | height: scaleHeight, 166 | ); 167 | canvas.drawRect(rectRight, paint); 168 | 169 | Rect rectLineRight = Rect.fromLTWH(left, top, width, height); 170 | canvas.drawRect(rectLineRight, linePaint); 171 | 172 | _drawText( 173 | node.right.value, 174 | canvas, 175 | Offset(left + width / 2 - 12.0, top + height / 2 - 12.0), 176 | ); 177 | } 178 | 179 | level++; 180 | 181 | // 递归绘制左节点 182 | drawTreeRects(node.left, rectLeft, node.left, level, canvas, 183 | scale: scale, color: color); 184 | // 递归绘制右节点 185 | drawTreeRects(node.right, rectRight, node.right, level, canvas, 186 | scale: scale, color: color); 187 | } 188 | -------------------------------------------------------------------------------- /lib/charts/tree_map/parse_array_to_bst.dart: -------------------------------------------------------------------------------- 1 | // eg: 2 | // List input = [2, 10, 4, 3, 7, 5, 9, 8, 1, 6]; 3 | // List output = [0, 2, 12, 16, 19, 26, 31, 40, 48, 49,55]; 4 | 5 | import './tree_node.dart'; 6 | 7 | TreeNode parseArrayToBST(List array) { 8 | int n = array.length; 9 | double sum = 0.0; 10 | List sums = List(n + 1); 11 | sums[0] = 0.0; 12 | 13 | for (int i = 0; i < n; i++) { 14 | sum += array[i]; 15 | sums[i + 1] = sum; 16 | } 17 | 18 | final TreeNode root = TreeNode(sums.last); 19 | 20 | void partition(int start, int end, TreeNode node) { 21 | if (start >= end - 1) { 22 | return; 23 | } 24 | 25 | double valueOffset = sums[start]; 26 | double valueTarget = (node.value / 2) + valueOffset; 27 | int left = start + 1; 28 | int right = end - 1; 29 | 30 | while (left < right) { 31 | int mid = left + right >> 1; 32 | 33 | if (sums[mid] < valueTarget) { 34 | left = mid + 1; 35 | } else { 36 | right = mid; 37 | } 38 | } 39 | 40 | if ((valueTarget - sums[left - 1] < (sums[left] - valueTarget) && 41 | start + 1 < left)) { 42 | left--; 43 | } 44 | 45 | double valueLeft = sums[left] - valueOffset; 46 | double valueRight = node.value - valueLeft; 47 | 48 | TreeNode nodeLeft = TreeNode(valueLeft); 49 | TreeNode nodeRight = TreeNode(valueRight); 50 | 51 | node.left = nodeLeft; 52 | node.right = nodeRight; 53 | 54 | partition(start, left, nodeLeft); 55 | partition(left, end, nodeRight); 56 | } 57 | 58 | partition(0, n, root); 59 | 60 | return root; 61 | } 62 | -------------------------------------------------------------------------------- /lib/charts/tree_map/tree_map.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import './parse_array_to_bst.dart'; 4 | import './tree_node.dart'; 5 | import './draw_tree_rects.dart'; 6 | 7 | // 1. 将数据集转成近似平衡的二叉树 √ 8 | // 2. 前序遍历二叉树 9 | // 3. 遍历过程中绘制每个字树 10 | 11 | class TreeMap extends StatefulWidget { 12 | final List datas; 13 | 14 | const TreeMap({@required this.datas}); 15 | 16 | @override 17 | _TreeMapState createState() => _TreeMapState(); 18 | } 19 | 20 | class _TreeMapState extends State with SingleTickerProviderStateMixin { 21 | AnimationController _controller; 22 | Animation _scale; 23 | Animation _color; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | _controller = AnimationController( 29 | vsync: this, 30 | duration: Duration(seconds: 5), 31 | )..addListener(() { 32 | print( 33 | {'scale': _scale.value}, 34 | ); 35 | }); 36 | 37 | _scale = Tween(begin: 0.0, end: 1.0).animate( 38 | CurvedAnimation(parent: _controller, curve: Curves.easeInOut), 39 | ); 40 | 41 | _color = ColorTween(begin: Colors.orange, end: Colors.red).animate( 42 | CurvedAnimation(parent: _controller, curve: Curves.easeInOut), 43 | ); 44 | 45 | _controller.forward(); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return LayoutBuilder( 51 | builder: (BuildContext context, BoxConstraints constraints) { 52 | return CustomPaint( 53 | painter: TreeMapPainter( 54 | datas: widget.datas, 55 | scale: _scale, 56 | color: _color, 57 | ), 58 | size: constraints.biggest, 59 | ); 60 | }, 61 | ); 62 | } 63 | } 64 | 65 | class TreeMapPainter extends CustomPainter { 66 | final List datas; 67 | final Animation scale; 68 | final Animation color; 69 | 70 | TreeMapPainter({ 71 | @required this.datas, 72 | this.scale, 73 | this.color, 74 | }) : super(repaint: scale); 75 | 76 | @override 77 | void paint(Canvas canvas, Size size) { 78 | Paint paint = Paint() 79 | ..color = Colors.black26 80 | ..style = PaintingStyle.fill; 81 | 82 | Rect rootRect = Rect.fromLTWH(0, 0, size.width, size.height); 83 | TreeNode rootNode = parseArrayToBST(datas); 84 | canvas.drawRect(rootRect, paint); 85 | 86 | drawTreeRects(rootNode, rootRect, rootNode, 0, canvas, 87 | scale: scale.value, color: color.value); 88 | } 89 | 90 | @override 91 | bool shouldRepaint(TreeMapPainter oldDelegate) => false; 92 | 93 | @override 94 | bool shouldRebuildSemantics(TreeMapPainter oldDelegate) => false; 95 | } 96 | -------------------------------------------------------------------------------- /lib/charts/tree_map/tree_node.dart: -------------------------------------------------------------------------------- 1 | class TreeNode { 2 | final double value; 3 | TreeNode left; 4 | TreeNode right; 5 | 6 | TreeNode(this.value); 7 | } 8 | 9 | void dfs(TreeNode root) { 10 | if (root == null) { 11 | return; 12 | } 13 | 14 | print(root.value); 15 | dfs(root.left); 16 | dfs(root.right); 17 | } 18 | -------------------------------------------------------------------------------- /lib/colors.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | List colors = [ 4 | Color(0xff8e43e7), 5 | Color(0xffff4f81), 6 | Color(0xff1cc7d0), 7 | Color(0xff00aeff), 8 | Color(0xff3369e7), 9 | Color(0xffb84592), 10 | Color(0xff2dde98), 11 | Color(0xffff6c5f), 12 | Color(0xff003666), 13 | Color(0xffffc168), 14 | Color(0xff050f2c), 15 | ]; 16 | -------------------------------------------------------------------------------- /lib/home_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import './pages/bar_chart_page.dart'; 4 | import './pages/column_chart_page.dart'; 5 | import './pages/line_chart_page.dart'; 6 | import './pages/pie_chart_page.dart'; 7 | import './pages/radar_chart_page.dart'; 8 | import './pages/tree_map_page.dart'; 9 | import './pages/donut_chart_page.dart'; 10 | import './pages/area_chart_page.dart'; 11 | import './pages/calender_heatmap_page.dart'; 12 | 13 | class HomePage extends StatefulWidget { 14 | @override 15 | _HomePageState createState() => _HomePageState(); 16 | } 17 | 18 | class _HomePageState extends State { 19 | List _tabs = [ 20 | Tab(child: Text('矩形树图')), 21 | Tab(child: Text('日历热力图')), 22 | Tab(child: Text('面积图')), 23 | Tab(child: Text('环形图')), 24 | Tab(child: Text('条形图')), 25 | Tab(child: Text('柱状图')), 26 | Tab(child: Text('饼图')), 27 | Tab(child: Text('折线图')), 28 | Tab(child: Text('雷达图')), 29 | ]; 30 | 31 | List _tabViews = [ 32 | TreeMapPage(), 33 | CalenderHeatMapPage(), 34 | AreaChartPage(), 35 | DonutChartPage(), 36 | BarChartPage(), 37 | ColumnChartPage(), 38 | PieChartPage(), 39 | LineChartPage(), 40 | RadarChartPage(), 41 | ]; 42 | 43 | @override 44 | Widget build(BuildContext context) { 45 | return DefaultTabController( 46 | length: _tabViews.length, 47 | child: Scaffold( 48 | appBar: AppBar( 49 | title: Text('Charts'), 50 | bottom: TabBar( 51 | tabs: _tabs, 52 | isScrollable: true, 53 | ), 54 | ), 55 | body: TabBarView(children: _tabViews), 56 | ), 57 | ); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'home_page.dart'; 4 | 5 | void main() { 6 | WidgetsFlutterBinding.ensureInitialized(); 7 | // SystemChrome.setEnabledSystemUIOverlays([]); // 全屏显示 8 | runApp(MyApp()); 9 | } 10 | 11 | class MyApp extends StatelessWidget { 12 | @override 13 | Widget build(BuildContext context) { 14 | return MaterialApp( 15 | debugShowCheckedModeBanner: false, 16 | home: HomePage(), 17 | ); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/pages/area_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../charts/area_chart/area_chart.dart'; 3 | 4 | class AreaChartPage extends StatefulWidget { 5 | @override 6 | _AreaChartPageState createState() => _AreaChartPageState(); 7 | } 8 | 9 | class _AreaChartPageState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return AreaChart( 13 | datas: [120.0, 90.0, 80.0, 60.0, 108.0], 14 | // xAxis: ['一月', '二月', '三月', '四月', '五月'], 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/pages/bar_chart_page.dart: -------------------------------------------------------------------------------- 1 | import '../charts/bar_chart.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class BarChartPage extends StatelessWidget { 5 | @override 6 | Widget build(BuildContext context) { 7 | return BarChart( 8 | max: 6000.0, 9 | data: [ 10 | { 11 | "label": "中国", 12 | "value": 2800.0, 13 | }, 14 | { 15 | "label": "印度", 16 | "value": 3000.0, 17 | }, 18 | { 19 | "label": "美国", 20 | "value": 2200.0, 21 | }, 22 | { 23 | "label": "巴西", 24 | "value": 3800.0, 25 | }, 26 | { 27 | "label": "法国", 28 | "value": 5200.0, 29 | }, 30 | ], 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/pages/calender_heatmap_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_charts/charts/calender_heatmap/calendar_heatmap.dart'; 5 | 6 | class CalenderHeatMapPage extends StatelessWidget { 7 | @override 8 | Widget build(BuildContext context) { 9 | // List datas = List.generate(31, (int index) { 10 | // return Random().nextDouble() * 10; 11 | // }, growable: false); 12 | List datas = [ 13 | 1, 14 | 0, 15 | 2, 16 | 10, 17 | 8, 18 | 4, 19 | 0, 20 | 12, 21 | 5, 22 | 8, 23 | 12, 24 | 12, 25 | 2, 26 | 4, 27 | 6, 28 | 0, 29 | 2, 30 | 2, 31 | ]; 32 | 33 | return CalenderHeatMap(datas: datas); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/pages/column_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../charts/column_chart.dart'; 4 | 5 | class ColumnChartPage extends StatelessWidget { 6 | @override 7 | Widget build(BuildContext context) { 8 | return ColumnChart( 9 | data: [180.0, 98.0, 126.0, 64.0, 118.0], 10 | xAxis: ['一月', '二月', '三月', '四月', '五月'], 11 | ); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /lib/pages/donut_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../charts/donut_chart.dart'; 3 | 4 | class DonutChartPage extends StatefulWidget { 5 | @override 6 | _DonutChartState createState() => _DonutChartState(); 7 | } 8 | 9 | class _DonutChartState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return Center( 13 | child: DonutCahrt( 14 | datas: [ 15 | { 16 | "name": '青年', 17 | "value": 90.0, 18 | }, 19 | { 20 | "name": '少年', 21 | "value": 40.0, 22 | }, 23 | { 24 | "name": '老年', 25 | "value": 120.0, 26 | }, 27 | { 28 | "name": '幼年', 29 | "value": 200.0, 30 | }, 31 | ], 32 | ), 33 | ); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/pages/line_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import '../charts/line_chart.dart'; 3 | 4 | class LineChartPage extends StatefulWidget { 5 | @override 6 | _LineChartPageState createState() => _LineChartPageState(); 7 | } 8 | 9 | class _LineChartPageState extends State { 10 | @override 11 | Widget build(BuildContext context) { 12 | return LineChart( 13 | datas: [120.0, 90.0, 80.0, 60.0, 108.0], 14 | xAxis: ['一月', '二月', '三月', '四月', '五月'], 15 | ); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/pages/pie_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../charts/pie_chart.dart'; 4 | 5 | class PieChartPage extends StatefulWidget { 6 | @override 7 | _PieChartPageState createState() => _PieChartPageState(); 8 | } 9 | 10 | class _PieChartPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return PieChart( 14 | datas: [60.0, 50.0, 40.0, 80.0, 90.0], 15 | legends: ['一月', '二月', '三月', '四月', '五月'], 16 | ); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /lib/pages/radar_chart_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import '../charts/radar_chart.dart'; 4 | 5 | class RadarChartPage extends StatefulWidget { 6 | @override 7 | _RadarChartPageState createState() => _RadarChartPageState(); 8 | } 9 | 10 | class _RadarChartPageState extends State { 11 | @override 12 | Widget build(BuildContext context) { 13 | return RadarChart( 14 | datas: [ 15 | [60.0, 70.0, 80.0, 30.0, 70.0, 80.0], 16 | [80.0, 50.0, 60.0, 80.0, 40.0, 90.0], 17 | [40.0, 70.0, 90.0, 50.0, 60.0, 80.0], 18 | ], 19 | scores: [60.0, 70.0, 80.0, 90.0], 20 | features: ["学习能力", "英语水平", "编码能力", "解决问题能力", "工作态度", '沟通能力'], 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/pages/tree_map_page.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import '../charts/tree_map/tree_map.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class TreeMapPage extends StatefulWidget { 7 | @override 8 | _TreeMapPageState createState() => _TreeMapPageState(); 9 | } 10 | 11 | class _TreeMapPageState extends State { 12 | @override 13 | Widget build(BuildContext context) { 14 | return TreeMap(datas: [2, 10, 4, 3, 7, 5, 9, 8, 1, 6, 9]); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /lib/utils/create_animated_path.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui'; 2 | 3 | Path createAnimatedPath( 4 | Path path, 5 | double percent, 6 | ) { 7 | final totalLength = path 8 | .computeMetrics() 9 | .fold(0.0, (double prev, PathMetric metric) => prev + metric.length); 10 | final currentLength = totalLength * percent; 11 | 12 | return extractPathUntilLength(path, currentLength); 13 | } 14 | 15 | Path extractPathUntilLength( 16 | Path originalPath, 17 | double length, 18 | ) { 19 | final path = Path(); 20 | var currentLength = 0.0; 21 | var metricsIterator = originalPath.computeMetrics().iterator; 22 | 23 | while (metricsIterator.moveNext()) { 24 | var metric = metricsIterator.current; 25 | var nextLength = currentLength + metric.length; 26 | final isLastSegment = nextLength > length; 27 | 28 | if (isLastSegment) { 29 | final remainingLength = length - currentLength; 30 | final pathSegment = metric.extractPath(0.0, remainingLength); 31 | path.addPath(pathSegment, Offset.zero); 32 | break; 33 | } else { 34 | final pathSegment = metric.extractPath(0.0, metric.length); 35 | path.addPath(pathSegment, Offset.zero); 36 | } 37 | 38 | currentLength = nextLength; 39 | } 40 | 41 | return path; 42 | } 43 | -------------------------------------------------------------------------------- /lib/utils/draw_grid.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | Paint paint = Paint() 4 | ..color = Colors.grey 5 | ..strokeWidth = .5; 6 | final double step = 30; // 小格边长 7 | 8 | void drawGrid(Canvas canvas, Size size) { 9 | canvas.save(); 10 | Offset p1 = Offset(0, 0); 11 | Offset p2 = Offset(size.width, 0); 12 | 13 | for (double i = 0; i < size.height; i += step) { 14 | if (i % 90 == 0) { 15 | paint.color = Colors.black26; 16 | paint.strokeWidth = 1.0; 17 | } else { 18 | paint.color = Colors.black12; 19 | } 20 | canvas.drawLine(p1, p2, paint); 21 | canvas.translate(0, step); 22 | } 23 | 24 | canvas.restore(); 25 | 26 | canvas.save(); 27 | p1 = Offset(0, 0); 28 | p2 = Offset(0, size.height); 29 | 30 | for (double i = 0; i < size.width; i += step) { 31 | if (i % 90 == 0) { 32 | paint.color = Colors.black26; 33 | paint.strokeWidth = 1.0; 34 | } else { 35 | paint.color = Colors.black12; 36 | } 37 | canvas.drawLine(p1, p2, paint); 38 | canvas.translate(step, 0); 39 | } 40 | 41 | canvas.restore(); 42 | } 43 | -------------------------------------------------------------------------------- /lib/utils/get_next_num.dart: -------------------------------------------------------------------------------- 1 | double getNextNum(double val) { 2 | double res; 3 | String a = ''; 4 | 5 | while (val > 1) { 6 | val /= 10; 7 | a += '9'; 8 | } 9 | 10 | double b = double.parse(a); 11 | double half = b / 2; 12 | 13 | if (val > half) { 14 | res = b; 15 | } else { 16 | res = half; 17 | } 18 | 19 | print(res); 20 | return res; 21 | } 22 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | async: 5 | dependency: transitive 6 | description: 7 | name: async 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "2.6.1" 11 | boolean_selector: 12 | dependency: transitive 13 | description: 14 | name: boolean_selector 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "2.1.0" 18 | characters: 19 | dependency: transitive 20 | description: 21 | name: characters 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "1.1.0" 25 | charcode: 26 | dependency: transitive 27 | description: 28 | name: charcode 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.2.0" 32 | clock: 33 | dependency: transitive 34 | description: 35 | name: clock 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "1.1.0" 39 | collection: 40 | dependency: transitive 41 | description: 42 | name: collection 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "1.15.0" 46 | fake_async: 47 | dependency: transitive 48 | description: 49 | name: fake_async 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.2.0" 53 | flutter: 54 | dependency: "direct main" 55 | description: flutter 56 | source: sdk 57 | version: "0.0.0" 58 | flutter_test: 59 | dependency: "direct dev" 60 | description: flutter 61 | source: sdk 62 | version: "0.0.0" 63 | matcher: 64 | dependency: transitive 65 | description: 66 | name: matcher 67 | url: "https://pub.dartlang.org" 68 | source: hosted 69 | version: "0.12.10" 70 | meta: 71 | dependency: transitive 72 | description: 73 | name: meta 74 | url: "https://pub.dartlang.org" 75 | source: hosted 76 | version: "1.3.0" 77 | path: 78 | dependency: transitive 79 | description: 80 | name: path 81 | url: "https://pub.dartlang.org" 82 | source: hosted 83 | version: "1.8.0" 84 | sky_engine: 85 | dependency: transitive 86 | description: flutter 87 | source: sdk 88 | version: "0.0.99" 89 | source_span: 90 | dependency: transitive 91 | description: 92 | name: source_span 93 | url: "https://pub.dartlang.org" 94 | source: hosted 95 | version: "1.8.1" 96 | stack_trace: 97 | dependency: transitive 98 | description: 99 | name: stack_trace 100 | url: "https://pub.dartlang.org" 101 | source: hosted 102 | version: "1.10.0" 103 | stream_channel: 104 | dependency: transitive 105 | description: 106 | name: stream_channel 107 | url: "https://pub.dartlang.org" 108 | source: hosted 109 | version: "2.1.0" 110 | string_scanner: 111 | dependency: transitive 112 | description: 113 | name: string_scanner 114 | url: "https://pub.dartlang.org" 115 | source: hosted 116 | version: "1.1.0" 117 | term_glyph: 118 | dependency: transitive 119 | description: 120 | name: term_glyph 121 | url: "https://pub.dartlang.org" 122 | source: hosted 123 | version: "1.2.0" 124 | test_api: 125 | dependency: transitive 126 | description: 127 | name: test_api 128 | url: "https://pub.dartlang.org" 129 | source: hosted 130 | version: "0.3.0" 131 | typed_data: 132 | dependency: transitive 133 | description: 134 | name: typed_data 135 | url: "https://pub.dartlang.org" 136 | source: hosted 137 | version: "1.3.0" 138 | vector_math: 139 | dependency: transitive 140 | description: 141 | name: vector_math 142 | url: "https://pub.dartlang.org" 143 | source: hosted 144 | version: "2.1.0" 145 | sdks: 146 | dart: ">=2.12.0 <3.0.0" 147 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_charts 2 | description: A new Flutter project. 3 | 4 | # The following line prevents the package from being accidentally published to 5 | # pub.dev using `pub publish`. This is preferred for private packages. 6 | publish_to: "none" # Remove this line if you wish to publish to pub.dev 7 | 8 | # The following defines the version and build number for your application. 9 | # A version number is three numbers separated by dots, like 1.2.43 10 | # followed by an optional build number separated by a +. 11 | # Both the version and the builder number may be overridden in flutter 12 | # build by specifying --build-name and --build-number, respectively. 13 | # In Android, build-name is used as versionName while build-number used as versionCode. 14 | # Read more about Android versioning at https://developer.android.com/studio/publish/versioning 15 | # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. 16 | # Read more about iOS versioning at 17 | # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html 18 | version: 1.0.0+1 19 | 20 | environment: 21 | sdk: ">=2.7.0 <3.0.0" 22 | 23 | dependencies: 24 | flutter: 25 | sdk: flutter 26 | # The following adds the Cupertino Icons font to your application. 27 | # Use with the CupertinoIcons class for iOS style icons. 28 | 29 | dev_dependencies: 30 | flutter_test: 31 | sdk: flutter 32 | 33 | # For information on the generic Dart part of this file, see the 34 | # following page: https://dart.dev/tools/pub/pubspec 35 | 36 | # The following section is specific to Flutter. 37 | flutter: 38 | # The following line ensures that the Material Icons font is 39 | # included with your application, so that you can use the icons in 40 | # the material Icons class. 41 | uses-material-design: true 42 | # To add assets to your application, add an assets section, like this: 43 | # assets: 44 | # An image asset can refer to one or more resolution-specific "variants", see 45 | # https://flutter.dev/assets-and-images/#resolution-aware. 46 | # For details regarding adding assets from package dependencies, see 47 | # https://flutter.dev/assets-and-images/#from-packages 48 | # To add custom fonts to your application, add a fonts section here, 49 | # in this "flutter" section. Each entry in this list should have a 50 | # "family" key with the font family name, and a "fonts" key with a 51 | # list giving the asset and other descriptors for the font. For 52 | # example: 53 | # fonts: 54 | # - family: Schyler 55 | # fonts: 56 | # - asset: fonts/Schyler-Regular.ttf 57 | # - asset: fonts/Schyler-Italic.ttf 58 | # style: italic 59 | # - family: Trajan Pro 60 | # fonts: 61 | # - asset: fonts/TrajanPro.ttf 62 | # - asset: fonts/TrajanPro_Bold.ttf 63 | # weight: 700 64 | # 65 | # For details regarding fonts from package dependencies, 66 | # see https://flutter.dev/custom-fonts/#from-packages 67 | -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility that Flutter provides. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_charts/main.dart'; 10 | import 'package:flutter_test/flutter_test.dart'; 11 | 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | --------------------------------------------------------------------------------