├── .github └── FUNDING.yml ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── all_lint_rules.yaml ├── analysis_options.yaml ├── assets ├── brain.obj ├── dash.jpg ├── mattis.jpeg ├── people │ ├── people01.jpg │ ├── people02.jpg │ ├── people03.jpg │ ├── people04.jpg │ ├── people05.jpg │ ├── people06.jpg │ ├── people07.jpg │ ├── people08.jpg │ ├── people09.jpg │ ├── people10.jpg │ ├── people11.jpg │ ├── people12.jpg │ ├── people13.jpg │ ├── people14.jpg │ ├── people15.jpg │ ├── people16.jpg │ ├── people17.jpg │ ├── people18.jpg │ ├── people19.jpg │ ├── people20.jpg │ ├── people21.jpg │ ├── people22.jpg │ ├── people23.jpg │ ├── people24.jpg │ ├── people25.jpg │ ├── people26.jpg │ ├── people27.jpg │ ├── people28.jpg │ ├── people29.jpg │ ├── people30.jpg │ ├── people31.jpg │ ├── people32.jpg │ ├── people33.jpg │ ├── people34.jpg │ ├── people35.jpg │ ├── people36.jpg │ ├── people37.jpg │ ├── people38.jpg │ ├── people39.jpg │ ├── people40.jpg │ ├── people41.jpg │ ├── people42.jpg │ ├── people43.jpg │ ├── people44.jpg │ ├── people45.jpg │ └── people46.jpg ├── tim_sneath.jpg └── volcano.png ├── images ├── blocks.gif ├── circle_wave.gif ├── creatures.gif ├── disks.gif ├── image_bubble.gif ├── mattis.gif ├── particles.gif ├── portrait.gif ├── rotating_bubbles.gif ├── rotating_planets.gif ├── triangles.gif ├── volcano.gif └── wave.gif ├── lib ├── common.dart ├── demo_blocks.dart ├── demo_circle_wave.dart ├── demo_creature.dart ├── demo_disks.dart ├── demo_image_bubble.dart ├── demo_mattis.dart ├── demo_particles.dart ├── demo_portrait.dart ├── demo_rotating_bubbles.dart ├── demo_rotating_planets.dart ├── demo_triangles.dart ├── demo_volcano.dart ├── demo_wave.dart └── main.dart ├── pubspec.lock └── pubspec.yaml /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: letsar 2 | patreon: romainrastel 3 | open_collective: # Replace with a single Open Collective username 4 | ko_fi: # Replace with a single Ko-fi username 5 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel 6 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry 7 | liberapay: # Replace with a single Liberapay username 8 | issuehunt: # Replace with a single IssueHunt username 9 | otechie: # Replace with a single Otechie username 10 | custom: ['https://www.buymeacoffee.com/romainrastel', 'paypal.me/RomainRastel'] 11 | -------------------------------------------------------------------------------- /.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 | coverage/ 33 | 34 | android/ 35 | ios/ 36 | 37 | -------------------------------------------------------------------------------- /.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: 8f89f6505b941329a864fef1527243a72800bf4d 8 | channel: beta 9 | 10 | project_type: app 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Romain Rastel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Sponsor me][sponsor_badge]][sponsor_me] 2 | 3 | # flutter_counter_challenge_2020 4 | 5 | A set of counter apps made for #FlutterCounterChallenge2020. 6 | 7 | Run `flutter create .` inside the repository after cloning it, to initialize the app folders. 8 | 9 | ## Blocks 10 | 11 | ![Blocks](images/blocks.gif) 12 | 13 | ## Circle wave 14 | 15 | ![Blocks](images/circle_wave.gif) 16 | 17 | Original idea from https://dribbble.com/shots/1698964-Circle-wave-II 18 | 19 | ## Creatures 20 | 21 | ![Blocks](images/creatures.gif) 22 | 23 | Original idea from https://gist.github.com/beesandbombs/6f3e6fb723f50b080916816ae8e561e3 24 | 25 | ## Disks 26 | 27 | ![Blocks](images/disks.gif) 28 | 29 | ## Image bubble 30 | 31 | ![Blocks](images/image_bubble.gif) 32 | 33 | ## Mattis 34 | 35 | ![Blocks](images/mattis.gif) 36 | 37 | ## Particles 38 | 39 | ![Blocks](images/particles.gif) 40 | 41 | Original idea from https://www.openprocessing.org/sketch/427313 42 | 43 | ## Portrait 44 | 45 | ![Blocks](images/portrait.gif) 46 | 47 | Original idea from https://www.openprocessing.org/sketch/392202/ 48 | 49 | ## Rotating bubbles 50 | 51 | ![Blocks](images/rotating_bubbles.gif) 52 | 53 | ## Rotating planets 54 | 55 | ![Blocks](images/rotating_planets.gif) 56 | 57 | Original idea from https://twitter.com/beesandbombs/status/1329468633723101187?s=20 58 | 59 | ## Triangles 60 | 61 | ![Blocks](images/triangles.gif) 62 | 63 | ## Volcano 64 | 65 | ![Blocks](images/volcano.gif) 66 | 67 | ## Wave 68 | 69 | ![Blocks](images/wave.gif) 70 | 71 | 72 | [sponsor_badge]: https://img.shields.io/badge/Sponsor-♥-green.svg 73 | [sponsor_me]: https://github.com/letsar#reach-me 74 | 75 | -------------------------------------------------------------------------------- /all_lint_rules.yaml: -------------------------------------------------------------------------------- 1 | linter: 2 | rules: 3 | - always_declare_return_types 4 | - always_put_control_body_on_new_line 5 | - always_put_required_named_parameters_first 6 | - always_require_non_null_named_parameters 7 | - always_specify_types 8 | - always_use_package_imports 9 | - annotate_overrides 10 | - avoid_annotating_with_dynamic 11 | - avoid_as 12 | - avoid_bool_literals_in_conditional_expressions 13 | - avoid_catches_without_on_clauses 14 | - avoid_catching_errors 15 | - avoid_classes_with_only_static_members 16 | - avoid_double_and_int_checks 17 | - avoid_empty_else 18 | - avoid_equals_and_hash_code_on_mutable_classes 19 | - avoid_escaping_inner_quotes 20 | - avoid_field_initializers_in_const_classes 21 | - avoid_function_literals_in_foreach_calls 22 | - avoid_implementing_value_types 23 | - avoid_init_to_null 24 | - avoid_js_rounded_ints 25 | - avoid_null_checks_in_equality_operators 26 | - avoid_positional_boolean_parameters 27 | - avoid_print 28 | - avoid_private_typedef_functions 29 | - avoid_redundant_argument_values 30 | - avoid_relative_lib_imports 31 | - avoid_renaming_method_parameters 32 | - avoid_return_types_on_setters 33 | - avoid_returning_null 34 | - avoid_returning_null_for_future 35 | - avoid_returning_null_for_void 36 | - avoid_returning_this 37 | - avoid_setters_without_getters 38 | - avoid_shadowing_type_parameters 39 | - avoid_single_cascade_in_expression_statements 40 | - avoid_slow_async_io 41 | - avoid_type_to_string 42 | - avoid_types_as_parameter_names 43 | - avoid_types_on_closure_parameters 44 | - avoid_unnecessary_containers 45 | - avoid_unused_constructor_parameters 46 | - avoid_void_async 47 | - avoid_web_libraries_in_flutter 48 | - await_only_futures 49 | - camel_case_extensions 50 | - camel_case_types 51 | - cancel_subscriptions 52 | - cascade_invocations 53 | - cast_nullable_to_non_nullable 54 | - close_sinks 55 | - comment_references 56 | - constant_identifier_names 57 | - control_flow_in_finally 58 | - curly_braces_in_flow_control_structures 59 | - diagnostic_describe_all_properties 60 | - directives_ordering 61 | - do_not_use_environment 62 | - empty_catches 63 | - empty_constructor_bodies 64 | - empty_statements 65 | - exhaustive_cases 66 | - file_names 67 | - flutter_style_todos 68 | - hash_and_equals 69 | - implementation_imports 70 | - invariant_booleans 71 | - iterable_contains_unrelated_type 72 | - join_return_with_assignment 73 | - leading_newlines_in_multiline_strings 74 | - library_names 75 | - library_prefixes 76 | - lines_longer_than_80_chars 77 | - list_remove_unrelated_type 78 | - literal_only_boolean_expressions 79 | - missing_whitespace_between_adjacent_strings 80 | - no_adjacent_strings_in_list 81 | - no_default_cases 82 | - no_duplicate_case_values 83 | - no_logic_in_create_state 84 | - no_runtimeType_toString 85 | - non_constant_identifier_names 86 | - null_check_on_nullable_type_parameter 87 | - null_closures 88 | - omit_local_variable_types 89 | - one_member_abstracts 90 | - only_throw_errors 91 | - overridden_fields 92 | - package_api_docs 93 | - package_names 94 | - package_prefixed_library_names 95 | - parameter_assignments 96 | - prefer_adjacent_string_concatenation 97 | - prefer_asserts_in_initializer_lists 98 | - prefer_asserts_with_message 99 | - prefer_collection_literals 100 | - prefer_conditional_assignment 101 | - prefer_const_constructors 102 | - prefer_const_constructors_in_immutables 103 | - prefer_const_declarations 104 | - prefer_const_literals_to_create_immutables 105 | - prefer_constructors_over_static_methods 106 | - prefer_contains 107 | - prefer_double_quotes 108 | - prefer_equal_for_default_values 109 | - prefer_expression_function_bodies 110 | - prefer_final_fields 111 | - prefer_final_in_for_each 112 | - prefer_final_locals 113 | - prefer_for_elements_to_map_fromIterable 114 | - prefer_foreach 115 | - prefer_function_declarations_over_variables 116 | - prefer_generic_function_type_aliases 117 | - prefer_if_elements_to_conditional_expressions 118 | - prefer_if_null_operators 119 | - prefer_initializing_formals 120 | - prefer_inlined_adds 121 | - prefer_int_literals 122 | - prefer_interpolation_to_compose_strings 123 | - prefer_is_empty 124 | - prefer_is_not_empty 125 | - prefer_is_not_operator 126 | - prefer_iterable_whereType 127 | - prefer_mixin 128 | - prefer_null_aware_operators 129 | - prefer_relative_imports 130 | - prefer_single_quotes 131 | - prefer_spread_collections 132 | - prefer_typing_uninitialized_variables 133 | - prefer_void_to_null 134 | - provide_deprecation_message 135 | - public_member_api_docs 136 | - recursive_getters 137 | - sized_box_for_whitespace 138 | - slash_for_doc_comments 139 | - sort_child_properties_last 140 | - sort_constructors_first 141 | - sort_pub_dependencies 142 | - sort_unnamed_constructors_first 143 | - test_types_in_equals 144 | - throw_in_finally 145 | - tighten_type_of_initializing_formals 146 | - type_annotate_public_apis 147 | - type_init_formals 148 | - unawaited_futures 149 | - unnecessary_await_in_return 150 | - unnecessary_brace_in_string_interps 151 | - unnecessary_const 152 | - unnecessary_final 153 | - unnecessary_getters_setters 154 | - unnecessary_lambdas 155 | - unnecessary_new 156 | - unnecessary_null_aware_assignments 157 | - unnecessary_null_checks 158 | - unnecessary_null_in_if_null_operators 159 | - unnecessary_nullable_for_final_variable_declarations 160 | - unnecessary_overrides 161 | - unnecessary_parenthesis 162 | - unnecessary_raw_strings 163 | - unnecessary_statements 164 | - unnecessary_string_escapes 165 | - unnecessary_string_interpolations 166 | - unnecessary_this 167 | - unrelated_type_equality_checks 168 | - unsafe_html 169 | - use_full_hex_values_for_flutter_colors 170 | - use_function_type_syntax_for_parameters 171 | - use_is_even_rather_than_modulo 172 | - use_key_in_widget_constructors 173 | - use_late_for_private_fields_and_variables 174 | - use_raw_strings 175 | - use_rethrow_when_possible 176 | - use_setters_to_change_properties 177 | - use_string_buffers 178 | - use_to_and_as_if_applicable 179 | - valid_regexps 180 | - void_checks -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # Enable all rules by default 2 | include: all_lint_rules.yaml 3 | analyzer: 4 | strong-mode: 5 | implicit-casts: false 6 | implicit-dynamic: false 7 | errors: 8 | dead_code: warning 9 | # Otherwise cause the import of all_lint_rules to warn because of some rules conflicts. 10 | # The conflicts are fixed in this file instead, so we can safely ignore the warning. 11 | included_file_warning: ignore 12 | missing_required_param: error 13 | missing_return: error 14 | 15 | linter: 16 | rules: 17 | # We prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 18 | always_put_required_named_parameters_first: false 19 | 20 | # Depends on your needs 21 | always_require_non_null_named_parameters: false 22 | 23 | # Conflicts with `omit_local_variable_types` and other rules. 24 | # As per Dart guidelines, we want to avoid unnecessary types to make the code 25 | # more readable. 26 | # See https://dart.dev/guides/language/effective-dart/design#avoid-type-annotating-initialized-local-variables 27 | always_specify_types: false 28 | 29 | # conflicts with `prefer_relative_imports` 30 | always_use_package_imports: false 31 | 32 | # Conflicts with always_specify_types 33 | avoid_annotating_with_dynamic: false 34 | 35 | # There are situations where we voluntarily want to catch everything, 36 | # especially as a library. 37 | avoid_catches_without_on_clauses: false 38 | 39 | # Only useful when targeting JS runtime 40 | avoid_double_and_int_checks: false 41 | 42 | # Improve readbility 43 | avoid_function_literals_in_foreach_calls: false 44 | 45 | # Only useful when targeting JS runtime 46 | avoid_js_rounded_ints: false 47 | 48 | # We prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) 49 | avoid_private_typedef_functions: false 50 | 51 | # Useful in a lot of cases 52 | avoid_returning_null: false 53 | 54 | # Can be useful to have a more readable code 55 | avoid_types_on_closure_parameters: false 56 | 57 | # `as` is not that bad (especially with the upcoming non-nullable types). 58 | # Explicit exceptions is better than implicit exceptions. 59 | avoid_as: false 60 | 61 | # Can be difficult to read in some cases 62 | cascade_invocations: false 63 | 64 | # Not reliable enough 65 | close_sinks: false 66 | 67 | # Blocked on https://github.com/flutter/flutter/issues/20765 68 | comment_references: false 69 | 70 | # Not useful for public properties for a constructor 71 | diagnostic_describe_all_properties: false 72 | 73 | # This project doesn't use Flutter-style todos 74 | flutter_style_todos: false 75 | 76 | # Experimental: Too many false positives: https://github.com/dart-lang/linter/issues/811 77 | invariant_booleans: false 78 | 79 | # Can be useful to have a more readable code 80 | join_return_with_assignment: false 81 | 82 | # Not useful for comments 83 | lines_longer_than_80_chars: false 84 | 85 | # Too many false positives: https://github.com/dart-lang/sdk/issues/34181 86 | literal_only_boolean_expressions: false 87 | 88 | # Disabled for now until we have NNBD as it otherwise conflicts with `missing_return` 89 | no_default_cases: false 90 | 91 | # Can be useful to have a more readable code 92 | omit_local_variable_types: false 93 | 94 | # Too many false positives 95 | one_member_abstracts: false 96 | 97 | # Issue: https://github.com/flutter/flutter/issues/5792 98 | only_throw_errors: false 99 | 100 | # Issue: https://github.com/dart-lang/language/issues/32 101 | prefer_mixin: false 102 | 103 | # Intelissence do the jobs for constructors. 104 | prefer_asserts_with_message: false 105 | 106 | # More readable 107 | prefer_relative_imports: false 108 | 109 | # Conflicts with `prefer_single_quotes` 110 | # Single quotes are easier to type and don't compromise on readability. 111 | prefer_double_quotes: false 112 | 113 | # Conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#consider-using--for-short-functions-and-methods 114 | # Not quite suitable for Flutter, which may have a `build` method with a single 115 | # return, but that return is still complex enough that a "body" is worth it. 116 | prefer_expression_function_bodies: false 117 | 118 | public_member_api_docs: false 119 | 120 | # False positives 121 | top_level_function_literal_block: false 122 | 123 | # We don't want to enforce this rule now. 124 | sort_pub_dependencies: false 125 | 126 | # Too many false positives 127 | unawaited_futures: false 128 | 129 | # Has false positives: https://github.com/dart-lang/linter/issues/498 130 | unnecessary_lambdas: false 131 | 132 | # Has false positives: https://github.com/dart-lang/sdk/issues/34182 133 | use_string_buffers: false 134 | 135 | # Has false positives, so we prefer to catch this by code-review 136 | use_to_and_as_if_applicable: false 137 | 138 | # Incompatible with `prefer_final_locals` 139 | # Having immutable local variables makes larger functions more predictible 140 | # so we will use `prefer_final_locals` instead. 141 | unnecessary_final: false -------------------------------------------------------------------------------- /assets/brain.obj: -------------------------------------------------------------------------------- 1 | mtllib brain.mtl 2 | o brain_Mesh 3 | v -0.109010 1.568602 1.499567 4 | v -0.109010 1.695523 -0.610097 5 | v 2.605067 0.700722 -0.276028 6 | v 2.926058 0.290465 1.108130 7 | v 3.119623 1.244714 -1.081728 8 | v 2.524027 1.557249 0.333647 9 | v -0.109010 -0.039924 1.589780 10 | v -0.109010 -0.080156 -0.328922 11 | v 2.576504 -0.312617 0.294063 12 | v 2.481380 -0.347984 1.797360 13 | v -0.109010 -0.806838 1.776123 14 | v -0.109010 -0.753863 0.075633 15 | v 2.918233 -1.045578 0.120667 16 | v 2.985485 -1.012926 1.153700 17 | v -0.109010 1.656460 -2.194313 18 | v -0.109010 0.856120 -2.033633 19 | v 2.835221 1.584392 -2.170257 20 | v 2.946896 0.550051 -2.181984 21 | v -0.109010 0.046850 -1.832949 22 | v 2.394182 -0.465208 -0.526074 23 | v -0.109010 -0.637533 -0.894609 24 | v 2.928916 -1.005987 -0.704000 25 | v -0.177863 1.625517 -3.730773 26 | v 0.378332 1.391812 -4.530596 27 | v 0.292742 0.462890 -4.523463 28 | v -0.177863 0.680735 -4.092670 29 | v 0.952152 1.603193 -4.302824 30 | v 1.056411 0.534593 -4.234836 31 | v 2.041234 1.732274 -3.666094 32 | v 2.131169 0.692743 -3.664605 33 | v 0.313618 -0.406020 -3.542907 34 | v -0.177863 0.150681 -3.515924 35 | v 1.336422 -0.410033 -3.457938 36 | v 2.399292 -0.382860 -2.578411 37 | v 0.552781 -0.890674 -1.900832 38 | v -0.177863 -0.718046 -1.890411 39 | v 1.425439 -0.726139 -2.049222 40 | v 2.029465 -0.732798 -1.381282 41 | v -0.109010 2.405753 1.132218 42 | v -0.109010 2.232475 -0.064807 43 | v 2.066657 2.551148 0.886353 44 | v 2.609658 2.707507 -0.077548 45 | v -0.109010 2.442489 -1.697970 46 | v 2.491278 2.716882 -1.623200 47 | v -0.177863 2.516430 -2.971306 48 | v 0.368982 2.408572 -3.984802 49 | v 0.983044 2.739457 -3.079274 50 | v 1.776234 2.616948 -2.618322 51 | v -0.171955 3.535935 0.929858 52 | v 0.308627 3.472833 1.286773 53 | v -0.113198 2.854401 0.000686 54 | v 1.171929 3.441301 1.366139 55 | v 1.745877 3.178972 1.232366 56 | v 1.417076 3.130785 0.583078 57 | v -0.109010 3.040888 -0.955737 58 | v 1.608196 3.396252 -0.687264 59 | v -0.177863 3.331360 -1.733953 60 | v 0.327609 3.560844 -1.978279 61 | v 1.011230 3.365204 -1.997607 62 | v 1.521566 3.306870 -1.404284 63 | v -0.133952 -1.373817 1.792044 64 | v -0.133952 -1.304635 0.681870 65 | v 2.482092 -1.833619 1.427522 66 | v 2.660342 -1.758415 0.763902 67 | v -0.109010 -1.318174 -0.247358 68 | v 2.562658 -1.839295 -0.215008 69 | v -0.109010 -1.331600 -1.453813 70 | v 1.036797 -1.488094 -1.846492 71 | v 1.952937 -1.525696 -1.620880 72 | v 2.519212 -1.586742 -0.944044 73 | v -0.217370 -2.199758 1.823502 74 | v -0.217370 -2.199758 1.093828 75 | v 1.876300 -2.304899 1.823502 76 | v 1.769916 -2.304899 1.093828 77 | v -0.217370 -2.199758 0.215393 78 | v 1.769916 -2.304899 0.215393 79 | v -0.217370 -2.245933 -0.315776 80 | v 0.749263 -2.437519 -0.842643 81 | v 1.298847 -2.357762 -0.708124 82 | v 1.601146 -2.304899 -0.422123 83 | v -0.110209 0.765965 3.090869 84 | v -0.119578 1.672374 3.149936 85 | v 2.405605 0.547262 2.670084 86 | v 2.749006 1.615563 1.739605 87 | v -0.109010 -0.103878 3.122056 88 | v 2.490004 -0.477156 2.783638 89 | v -0.109010 -0.997088 3.149765 90 | v 2.623240 -0.889507 2.378924 91 | v -0.143386 2.448461 2.524972 92 | v 2.417181 2.384971 2.054690 93 | v 0.158306 3.651147 2.112179 94 | v -0.172018 3.478726 1.762830 95 | v 0.992849 3.598187 2.094271 96 | v 1.745046 3.284951 2.271611 97 | v -0.133952 -1.529477 2.819777 98 | v 2.098587 -1.749733 2.432554 99 | v -0.217370 -2.562963 2.574506 100 | v 1.536732 -2.368399 2.351668 101 | v -0.147707 0.714235 4.214501 102 | v 1.290673 1.090878 4.158787 103 | v 0.580148 1.548080 4.299147 104 | v -0.160146 1.364690 3.762527 105 | v 1.692086 0.893050 3.769548 106 | v 1.327154 1.513908 4.234165 107 | v 2.132403 0.547628 3.341535 108 | v 2.173979 1.492828 3.487432 109 | v -0.175238 -0.130979 4.520379 110 | v 0.797068 -0.161375 4.842122 111 | v 1.318794 -0.215337 4.315737 112 | v 2.024224 -0.337243 3.829482 113 | v -0.164351 -0.908915 4.502542 114 | v 0.550272 -1.222762 4.952510 115 | v 1.371059 -1.126448 4.339831 116 | v 1.866206 -0.847292 3.807228 117 | v 0.389041 2.408920 4.007754 118 | v -0.174526 2.361984 3.560056 119 | v 1.103669 1.992752 4.171652 120 | v 1.990568 2.298674 2.992501 121 | v 0.416092 3.127461 3.067965 122 | v -0.193497 2.818399 2.781438 123 | v 1.040230 2.818398 3.336583 124 | v 1.542425 2.993644 2.865766 125 | v -0.149157 -2.024255 3.794694 126 | v 0.347821 -1.920331 4.667109 127 | v 1.095699 -1.893358 4.277564 128 | v 1.598286 -2.245576 3.436410 129 | v -0.217370 -2.666736 3.122850 130 | v 0.112026 -2.648774 3.601781 131 | v 0.531768 -2.581573 3.050163 132 | v 1.053119 -2.491881 2.917221 133 | v -0.161150 3.639388 -0.920268 134 | v 0.559743 3.722877 -0.958579 135 | v -0.156592 3.596738 0.611871 136 | v 0.370193 3.900626 -0.018614 137 | v -0.217370 -2.829885 1.481732 138 | v -0.217370 -2.862898 2.062846 139 | v 2.023207 -2.728071 1.481732 140 | v 1.926978 -2.863348 1.922541 141 | v -0.217370 -2.656566 1.032467 142 | v 1.600490 -2.952780 0.766766 143 | v -0.217370 -2.823353 -0.383256 144 | v 0.699985 -2.584772 -0.091860 145 | v 1.083711 -2.814571 0.108950 146 | v 1.417135 -2.952780 0.236577 147 | v -0.217370 -2.716947 2.392934 148 | v 1.660814 -2.842606 2.547087 149 | v -0.217370 -2.848421 2.957785 150 | v -0.030006 -2.894104 3.620877 151 | v 0.637021 -3.072759 3.293113 152 | v 1.146728 -3.085419 2.958234 153 | v -0.217370 -3.077890 2.086186 154 | v 0.043521 -3.628289 1.807360 155 | v -0.081319 -3.440562 2.947565 156 | v 0.438321 -3.346349 3.234768 157 | v 1.153059 -3.627652 2.303813 158 | v 0.536045 -3.674666 2.879880 159 | v 1.186277 -3.737913 1.708276 160 | v 1.796379 -3.240902 1.385171 161 | v 1.570596 -3.255770 1.037538 162 | v 1.425747 -3.255769 0.623033 163 | v 1.093666 -3.196367 0.333699 164 | v -0.217370 -3.418843 0.875262 165 | v -0.217370 -3.187866 1.658570 166 | v -0.217370 -3.420778 0.708387 167 | v -0.217370 -3.419621 -0.140048 168 | v 1.018607 -3.852407 1.139570 169 | v 0.617168 -3.914230 1.379655 170 | v 0.192533 -3.511270 0.878365 171 | v 0.425130 -3.687478 0.465133 172 | v 0.091114 -3.515787 -0.119121 173 | v -0.217370 0.811252 1.672755 174 | v -0.217370 1.568602 1.499567 175 | v -0.187112 0.793265 -0.024583 176 | v -0.217370 1.695523 -0.610097 177 | v -0.217370 -0.039924 1.589780 178 | v -0.217370 -0.080156 -0.328922 179 | v -0.217370 0.856120 -2.033633 180 | v -0.217370 0.765965 3.090869 181 | v 0.519286 -3.918723 2.138791 182 | v 0.516522 -3.952600 1.757080 183 | v -0.121532 -3.998076 1.251003 184 | v -0.217370 -3.992882 0.206164 185 | v 0.218965 -4.125299 1.104264 186 | v 0.358358 -4.103815 0.644018 187 | v 0.102655 -4.103166 0.229039 188 | v -0.217370 -4.704154 1.680936 189 | v -0.217370 -4.686471 0.775108 190 | v 0.188938 -4.822868 1.582736 191 | v 0.310411 -4.817182 1.259581 192 | v 0.110498 -4.830520 0.876572 193 | v -0.189702 -5.806045 2.118915 194 | v -0.217370 -5.475926 1.045202 195 | v 0.251475 -5.909178 1.901316 196 | v 0.331563 -5.839447 1.529176 197 | v 0.092591 -5.820556 1.081514 198 | v -0.214470 -7.000110 2.331185 199 | v -0.214470 -7.000109 0.958082 200 | v 0.340513 -6.999591 2.111630 201 | v 0.452953 -7.000109 1.644367 202 | v 0.146779 -6.997157 1.027625 203 | v -1.283453 0.778813 4.691713 204 | v -1.014888 1.548080 4.127273 205 | v -0.287032 1.131444 4.214501 206 | v -0.274594 1.888448 4.189076 207 | v -0.256877 1.625517 -3.730773 208 | v -0.722034 1.391812 -4.575580 209 | v -0.256877 0.680735 -4.092670 210 | v -0.636444 0.462890 -4.568447 211 | v -3.360797 0.290465 1.108130 212 | v -3.039807 0.700722 -0.276028 213 | v -2.958767 1.557249 0.333647 214 | v -3.554363 1.244714 -1.081728 215 | v -0.217370 0.811252 1.672755 216 | v -0.217370 1.568602 1.499567 217 | v -0.247628 0.793265 -0.024583 218 | v -0.217370 1.695523 -0.610097 219 | v -1.678588 0.534593 -4.234836 220 | v -1.626825 1.603193 -4.302824 221 | v -1.761894 1.297434 4.035217 222 | v -2.126826 0.893050 3.769548 223 | v -2.475974 1.732274 -3.666094 224 | v -2.657059 0.692743 -3.664605 225 | v -2.680982 1.506266 3.487432 226 | v -2.567143 0.547628 3.341535 227 | v -0.915452 -0.161375 4.765997 228 | v -0.259501 0.178780 4.184391 229 | v -0.256877 0.150681 -3.515924 230 | v -0.748358 -0.406020 -3.542907 231 | v -0.217370 -0.039924 1.589780 232 | v -0.217370 -0.080156 -0.328922 233 | v -1.932834 -0.410033 -3.457938 234 | v -1.859306 -0.215337 4.527670 235 | v -2.834031 -0.382860 -2.578411 236 | v -2.458963 -0.337243 3.829482 237 | v -3.011244 -0.312617 0.294063 238 | v -2.916120 -0.347984 1.797360 239 | v -0.270389 -0.918321 4.296327 240 | v -0.985012 -1.222762 4.952510 241 | v -0.256877 -0.718046 -1.890411 242 | v -0.987520 -1.031336 -1.900832 243 | v -0.325730 -0.080156 -0.328922 244 | v -0.325730 -0.753863 0.075633 245 | v -0.325730 -0.039924 1.589780 246 | v -0.325730 -0.806838 1.776123 247 | v -1.860179 -0.726139 -2.049222 248 | v -1.708998 -1.126448 4.482619 249 | v -2.464204 -0.908912 -1.381282 250 | v -2.300946 -0.847292 3.807228 251 | v -3.420224 -1.012926 1.153700 252 | v -3.352973 -1.045578 0.120667 253 | v -0.217370 0.856120 -2.033633 254 | v -0.325730 0.856120 -2.033633 255 | v -0.325730 1.695523 -0.610097 256 | v -0.325730 1.656460 -2.194313 257 | v -3.381636 0.550051 -2.181984 258 | v -3.178409 1.584392 -2.170257 259 | v -0.325730 0.046850 -1.832949 260 | v -2.828922 -0.465208 -0.526074 261 | v -0.325730 -0.637533 -0.894609 262 | v -3.363657 -1.005987 -0.704000 263 | v -0.921991 2.193295 4.205813 264 | v -0.260214 2.361984 3.560056 265 | v -0.325730 1.568602 1.499567 266 | v -0.325730 2.232475 -0.064807 267 | v -0.325730 2.405753 1.132218 268 | v -1.631452 2.150318 4.044981 269 | v -2.489262 2.298674 2.992501 270 | v -3.044398 2.707507 -0.077548 271 | v -2.501397 2.551148 0.886353 272 | v -0.325730 2.442489 -1.697970 273 | v -2.809041 2.716882 -1.623200 274 | v -0.256877 2.516430 -2.971306 275 | v -0.982328 2.408572 -3.984802 276 | v -1.417784 2.594801 -3.079274 277 | v -2.210974 2.616948 -2.618322 278 | v -0.850832 3.127461 3.067965 279 | v -0.241242 2.899757 2.781438 280 | v -0.321541 2.854401 0.000686 281 | v -0.262785 3.535935 0.929858 282 | v -1.461013 3.005864 3.336583 283 | v -2.030031 2.861958 3.054569 284 | v -1.908134 3.500408 0.610731 285 | v -2.039521 3.178972 1.343854 286 | v -0.325730 3.040888 -0.955737 287 | v -2.042936 3.396252 -1.014651 288 | v -0.256877 3.331360 -1.733953 289 | v -0.929335 3.365204 -2.236393 290 | v -1.445969 3.365204 -1.997607 291 | v -1.956306 3.306870 -1.404284 292 | v -0.782561 -1.920331 4.667109 293 | v -0.285583 -1.848808 3.921543 294 | v -0.300788 -1.304635 0.681870 295 | v -0.300788 -1.373817 1.792044 296 | v -1.530438 -1.893358 4.277564 297 | v -2.033026 -2.245576 3.436410 298 | v -3.095081 -1.758415 0.763902 299 | v -2.916832 -1.833619 1.427522 300 | v -0.325730 -1.318174 -0.247358 301 | v -2.997397 -1.839295 -0.215008 302 | v -0.325730 -1.590052 -1.453813 303 | v -1.325730 -1.488094 -1.846492 304 | v -2.387677 -1.525696 -1.620880 305 | v -2.953952 -1.586742 -0.944044 306 | v -0.546765 -2.648774 3.601781 307 | v -0.217370 -2.666736 3.122850 308 | v -0.217370 -2.199758 1.093828 309 | v -0.217370 -2.199758 1.823502 310 | v -0.966508 -2.581573 3.050163 311 | v -1.487859 -2.491881 2.917221 312 | v -2.204656 -2.304899 1.093828 313 | v -2.311040 -2.304899 1.823502 314 | v -0.217370 -2.199758 0.215393 315 | v -2.204656 -2.304899 0.215393 316 | v -1.165502 -2.593542 -0.842643 317 | v -1.733586 -2.357762 -0.708124 318 | v -2.035886 -2.304899 -0.422123 319 | v -0.315161 1.672374 3.149936 320 | v -0.324531 0.765965 3.090869 321 | v -3.183746 1.615563 1.739605 322 | v -3.272924 0.547262 2.670084 323 | v -0.217370 0.765965 3.090869 324 | v -0.325730 -0.103878 3.122056 325 | v -2.924744 -0.477156 2.783638 326 | v -0.325730 -0.997088 3.149765 327 | v -3.057980 -0.889507 2.378924 328 | v -0.291353 2.448461 2.524972 329 | v -2.744046 2.384971 2.054690 330 | v -0.262721 3.478726 2.223085 331 | v -0.743367 3.472833 1.363325 332 | v -0.735593 3.651147 2.112179 333 | v -1.606668 3.308647 1.311126 334 | v -1.427589 3.598187 2.094271 335 | v -2.179785 3.284951 2.271611 336 | v -0.300788 -1.529477 2.819777 337 | v -2.533326 -1.749733 2.432554 338 | v -0.217370 -2.562963 2.574506 339 | v -1.971472 -2.368399 2.351668 340 | v -1.018729 3.844339 -0.958579 341 | v -0.273589 3.639388 -0.920268 342 | v -0.804933 3.900626 -0.018614 343 | v -0.305461 3.596739 -0.021647 344 | v -0.217370 -2.829885 1.481732 345 | v -0.217370 -2.862898 2.062846 346 | v -2.457947 -2.728071 1.481732 347 | v -2.361718 -2.863348 1.922541 348 | v -0.217370 -2.656566 1.032467 349 | v -2.035230 -2.952780 0.766766 350 | v -1.134725 -2.788138 -0.091860 351 | v -1.518451 -2.814571 0.108950 352 | v -1.851874 -2.952780 0.236577 353 | v -0.217370 -2.716947 2.392934 354 | v -2.095553 -2.842606 2.547087 355 | v -0.217370 -2.848421 2.957785 356 | v -0.404734 -2.894104 3.620877 357 | v -1.071761 -3.072759 3.293113 358 | v -1.581467 -3.085419 2.958234 359 | v -0.217370 -3.077890 2.086186 360 | v -0.478260 -3.628289 1.807360 361 | v -0.353421 -3.440562 2.947565 362 | v -0.873060 -3.346349 3.234768 363 | v -1.587798 -3.627652 2.303813 364 | v -0.970784 -3.674666 2.879880 365 | v -2.231119 -3.240902 1.385171 366 | v -1.621016 -3.737913 1.708276 367 | v -2.005336 -3.255770 1.037538 368 | v -1.860486 -3.255769 0.623033 369 | v -1.528406 -3.196367 0.333699 370 | v -0.217370 -3.187866 1.658570 371 | v -0.217370 -3.418843 0.875262 372 | v -0.217370 -3.420778 0.708387 373 | v -1.453347 -3.852407 1.139570 374 | v -1.051908 -3.914230 1.379655 375 | v -0.627272 -3.511270 0.878365 376 | v -0.859870 -3.687478 0.465133 377 | v -0.525854 -3.515787 -0.119121 378 | v -0.951262 -3.952600 1.757080 379 | v -0.954025 -3.918723 2.138791 380 | v -0.653705 -4.125299 1.104264 381 | v -0.793098 -4.103815 0.644018 382 | v -0.537395 -4.103166 0.229039 383 | v -0.585018 -4.822868 1.620322 384 | v -0.745150 -4.817182 1.259581 385 | v -0.545237 -4.830520 0.876572 386 | v -0.686214 -5.909178 1.901316 387 | v -0.766303 -5.839447 1.529176 388 | v -0.527330 -5.820556 1.081514 389 | v -0.769454 -6.999591 2.111629 390 | v -0.881894 -7.000109 1.644367 391 | v -0.575720 -6.997157 1.027625 392 | usemtl occipital 393 | f 100//1 101//2 99//3 394 | f 99//4 101//4 102//4 395 | f 100//5 99//5 108//5 396 | f 108//6 99//6 107//6 397 | f 103//7 100//8 109//9 398 | f 109//9 100//8 108//10 399 | f 105//11 103//11 110//11 400 | f 110//12 103//12 109//12 401 | f 107//13 111//13 108//13 402 | f 108//14 111//14 112//14 403 | f 109//15 108//16 113//17 404 | f 113//18 108//18 112//18 405 | f 110//19 109//19 114//19 406 | f 114//20 109//15 113//17 407 | f 112//21 111//21 124//21 408 | f 124//22 111//22 123//22 409 | f 113//23 112//23 125//23 410 | f 125//24 112//24 124//24 411 | f 114//25 113//25 126//25 412 | f 126//26 113//26 125//26 413 | f 124//27 123//27 128//27 414 | f 128//28 123//28 127//28 415 | f 124//29 128//29 125//29 416 | f 125//30 128//30 129//30 417 | f 125//31 129//31 126//31 418 | f 126//32 129//32 130//32 419 | f 81//33 7//34 85//35 420 | f 7//34 11//36 85//35 421 | f 85//35 11//36 87//36 422 | f 102//37 81//37 99//37 423 | f 81//38 85//38 99//38 424 | f 99//39 85//39 107//39 425 | f 83//40 105//40 86//40 426 | f 86//41 105//41 110//41 427 | f 85//42 87//43 107//44 428 | f 107//44 87//43 111//45 429 | f 88//46 86//46 114//46 430 | f 114//47 86//47 110//47 431 | f 95//48 123//49 87//50 432 | f 87//51 123//51 111//51 433 | f 88//52 114//52 96//52 434 | f 96//53 114//53 126//53 435 | f 96//54 126//54 98//54 436 | f 98//55 126//55 130//55 437 | f 201//56 203//56 202//56 438 | f 202//57 219//57 201//57 439 | f 219//58 220//58 201//58 440 | f 201//59 225//59 203//59 441 | f 225//60 226//60 203//60 442 | f 220//61 232//61 201//61 443 | f 232//62 225//62 201//62 444 | f 224//63 234//63 220//63 445 | f 234//64 232//64 220//64 446 | f 226//65 225//65 237//65 447 | f 225//66 238//66 237//66 448 | f 232//67 246//67 225//67 449 | f 246//68 238//68 225//68 450 | f 234//69 248//69 232//69 451 | f 248//70 246//70 232//70 452 | f 238//71 290//71 237//71 453 | f 290//72 291//72 237//72 454 | f 246//73 294//73 238//73 455 | f 294//74 290//74 238//74 456 | f 248//75 295//75 246//75 457 | f 295//76 294//76 246//76 458 | f 290//77 304//77 291//77 459 | f 304//78 305//78 291//78 460 | f 290//79 294//79 304//79 461 | f 294//80 308//80 304//80 462 | f 294//81 295//81 308//81 463 | f 295//82 309//82 308//82 464 | f 318//83 322//84 243//85 465 | f 243//85 322//84 244//86 466 | f 322//84 324//86 244//86 467 | f 318//87 203//87 322//87 468 | f 203//88 226//88 322//88 469 | f 323//89 234//89 224//89 470 | f 322//90 226//90 324//90 471 | f 226//91 237//91 324//91 472 | f 325//92 248//92 323//92 473 | f 248//93 234//93 323//93 474 | f 334//94 324//95 291//96 475 | f 324//97 237//97 291//97 476 | f 325//98 335//98 248//98 477 | f 335//99 295//99 248//99 478 | f 334//100 291//101 305//102 479 | f 335//103 337//103 295//103 480 | f 337//104 309//104 295//104 481 | usemtl frontal 482 | f 23//105 24//105 26//105 483 | f 26//106 24//106 25//106 484 | f 6//107 3//107 5//107 485 | f 25//108 24//108 28//108 486 | f 28//109 24//109 27//109 487 | f 27//110 29//110 28//110 488 | f 28//111 29//111 30//111 489 | f 26//112 25//112 32//112 490 | f 32//113 25//113 31//113 491 | f 25//114 28//114 31//114 492 | f 31//115 28//115 33//115 493 | f 28//116 30//116 33//116 494 | f 33//117 30//117 34//117 495 | f 32//118 31//118 36//118 496 | f 36//119 31//119 35//119 497 | f 31//120 33//120 35//120 498 | f 35//121 33//121 37//121 499 | f 33//122 34//122 37//122 500 | f 37//123 34//123 38//123 501 | f 16//36 2//36 15//36 502 | f 3//124 18//124 5//124 503 | f 5//125 18//125 17//125 504 | f 8//36 16//36 19//36 505 | f 3//126 20//126 18//126 506 | f 21//36 8//36 19//36 507 | f 15//127 23//127 16//127 508 | f 16//128 23//128 26//128 509 | f 18//129 30//129 17//129 510 | f 17//130 30//130 29//130 511 | f 19//131 16//131 32//131 512 | f 32//132 16//132 26//132 513 | f 20//133 34//133 18//133 514 | f 18//134 34//134 30//134 515 | f 21//135 19//135 36//135 516 | f 36//136 19//136 32//136 517 | f 20//137 38//137 34//137 518 | f 2//36 1//36 40//138 519 | f 5//139 42//139 6//139 520 | f 6//140 42//140 41//140 521 | f 15//36 2//36 43//36 522 | f 43//36 2//36 40//138 523 | f 17//141 44//141 5//141 524 | f 5//142 44//142 42//142 525 | f 23//143 15//144 45//145 526 | f 45//145 15//144 43//146 527 | f 24//147 23//147 46//147 528 | f 46//148 23//148 45//148 529 | f 24//149 46//149 27//149 530 | f 27//150 46//150 47//150 531 | f 29//151 48//151 17//151 532 | f 17//152 48//152 44//152 533 | f 27//153 47//153 29//153 534 | f 29//154 47//154 48//154 535 | f 42//155 54//155 41//155 536 | f 43//36 40//138 55//156 537 | f 55//156 40//138 51//157 538 | f 44//158 56//158 42//158 539 | f 42//159 56//159 54//159 540 | f 45//160 43//161 57//162 541 | f 57//162 43//161 55//163 542 | f 46//164 45//164 58//164 543 | f 58//165 45//165 57//165 544 | f 46//166 58//166 47//166 545 | f 47//167 58//167 59//167 546 | f 48//168 60//168 44//168 547 | f 44//169 60//169 56//169 548 | f 47//170 59//170 48//170 549 | f 48//171 59//171 60//171 550 | f 58//172 57//172 132//172 551 | f 132//173 57//173 131//173 552 | f 132//174 131//174 134//174 553 | f 134//175 131//175 133//175 554 | f 134//176 133//176 50//176 555 | f 134//177 56//177 132//177 556 | f 132//178 56//178 60//178 557 | f 60//179 59//179 132//179 558 | f 132//180 59//180 58//180 559 | f 50//181 54//181 134//181 560 | f 134//182 54//182 56//182 561 | f 133//183 131//183 55//183 562 | f 55//184 131//184 57//184 563 | f 51//185 133//185 55//185 564 | f 205//186 207//186 206//186 565 | f 207//187 208//187 206//187 566 | f 211//188 212//188 210//188 567 | f 208//189 217//190 206//191 568 | f 217//190 218//192 206//191 569 | f 218//193 217//193 221//193 570 | f 217//194 222//194 221//194 571 | f 207//195 227//195 208//195 572 | f 227//196 228//196 208//196 573 | f 208//197 228//197 217//197 574 | f 228//198 231//198 217//198 575 | f 217//199 231//199 222//199 576 | f 231//200 233//200 222//200 577 | f 227//201 239//201 228//201 578 | f 239//202 240//202 228//202 579 | f 228//203 240//203 231//203 580 | f 240//204 245//204 231//204 581 | f 231//205 245//205 233//205 582 | f 245//206 247//206 233//206 583 | f 252//86 254//86 253//86 584 | f 210//207 212//207 255//207 585 | f 212//208 256//208 255//208 586 | f 241//86 257//86 252//86 587 | f 210//209 255//209 258//209 588 | f 259//86 257//86 241//86 589 | f 254//210 252//210 205//210 590 | f 252//211 207//211 205//211 591 | f 255//212 256//212 222//212 592 | f 256//213 221//213 222//213 593 | f 257//214 227//214 252//214 594 | f 227//215 207//215 252//215 595 | f 258//216 255//216 233//216 596 | f 255//217 222//217 233//217 597 | f 259//218 239//218 257//218 598 | f 239//219 227//219 257//219 599 | f 258//220 233//220 247//220 600 | f 253//86 264//221 263//86 601 | f 264//221 265//222 263//86 602 | f 212//223 211//223 268//223 603 | f 211//224 269//224 268//224 604 | f 254//86 270//86 253//86 605 | f 270//86 264//221 253//86 606 | f 256//225 212//225 271//225 607 | f 212//226 268//226 271//226 608 | f 205//227 272//228 254//229 609 | f 272//228 270//230 254//229 610 | f 206//231 273//231 205//231 611 | f 273//232 272//232 205//232 612 | f 206//233 218//233 273//233 613 | f 218//234 274//234 273//234 614 | f 221//235 256//235 275//235 615 | f 256//236 271//236 275//236 616 | f 218//237 221//238 274//239 617 | f 221//238 275//240 274//239 618 | f 264//221 278//241 265//222 619 | f 268//242 269//242 282//242 620 | f 269//243 283//243 282//243 621 | f 270//86 284//244 264//221 622 | f 284//244 278//241 264//221 623 | f 271//245 268//245 285//245 624 | f 268//246 282//246 285//246 625 | f 272//247 286//248 270//249 626 | f 286//248 284//250 270//249 627 | f 273//251 287//251 272//251 628 | f 287//252 286//252 272//252 629 | f 273//253 274//253 287//253 630 | f 274//254 288//254 287//254 631 | f 275//255 271//255 289//255 632 | f 271//256 285//256 289//256 633 | f 274//239 275//240 288//257 634 | f 275//258 289//258 288//258 635 | f 287//259 338//259 286//259 636 | f 338//260 339//260 286//260 637 | f 338//261 340//261 339//261 638 | f 340//262 341//262 339//262 639 | f 340//263 329//263 341//263 640 | f 329//264 279//264 341//264 641 | f 340//265 338//265 285//265 642 | f 338//266 289//266 285//266 643 | f 289//267 338//267 288//267 644 | f 338//268 287//268 288//268 645 | f 329//269 340//269 282//269 646 | f 340//270 285//270 282//270 647 | f 283//271 331//271 282//271 648 | f 331//272 329//272 282//272 649 | f 341//273 284//273 339//273 650 | f 284//274 286//274 339//274 651 | f 279//275 278//275 341//275 652 | f 278//276 284//276 341//276 653 | usemtl parietal 654 | f 4//277 3//278 6//277 655 | f 101//2 100//1 104//279 656 | f 104//280 100//280 103//280 657 | f 104//281 103//281 106//281 658 | f 106//282 103//282 105//282 659 | f 3//283 4//283 9//283 660 | f 9//284 4//284 10//284 661 | f 9//285 20//285 3//285 662 | f 101//286 115//286 102//286 663 | f 102//287 115//287 116//287 664 | f 104//288 117//288 101//288 665 | f 101//289 117//289 115//289 666 | f 104//290 106//290 117//290 667 | f 117//291 106//292 118//293 668 | f 115//294 119//294 116//294 669 | f 116//295 119//295 120//295 670 | f 51//296 39//296 49//296 671 | f 117//297 121//297 115//297 672 | f 115//298 121//298 119//298 673 | f 118//293 122//299 117//291 674 | f 117//300 122//300 121//300 675 | f 41//301 54//301 53//301 676 | f 82//302 1//302 81//302 677 | f 6//277 84//303 4//277 678 | f 4//304 84//305 83//306 679 | f 4//307 83//307 10//307 680 | f 39//308 1//308 89//308 681 | f 89//309 1//309 82//309 682 | f 6//310 41//310 84//310 683 | f 84//311 41//311 90//311 684 | f 49//312 92//312 50//312 685 | f 50//313 92//313 91//313 686 | f 49//314 39//314 92//314 687 | f 92//315 39//315 89//315 688 | f 50//316 91//317 52//318 689 | f 52//318 91//317 93//319 690 | f 41//320 53//320 90//320 691 | f 90//321 53//321 94//321 692 | f 53//322 52//323 94//324 693 | f 94//324 52//323 93//325 694 | f 82//326 81//326 102//326 695 | f 84//305 106//327 83//306 696 | f 83//328 106//328 105//328 697 | f 89//329 82//329 116//329 698 | f 116//330 82//330 102//330 699 | f 84//331 90//331 106//331 700 | f 106//332 90//332 118//332 701 | f 92//333 120//333 91//333 702 | f 91//334 120//334 119//334 703 | f 92//335 89//335 120//335 704 | f 120//336 89//336 116//336 705 | f 91//337 119//337 93//337 706 | f 93//338 119//339 121//338 707 | f 90//340 94//340 118//340 708 | f 118//341 94//341 122//341 709 | f 94//342 93//342 122//342 710 | f 122//343 93//338 121//338 711 | f 50//344 133//344 49//344 712 | f 53//345 54//345 52//345 713 | f 52//346 54//346 50//346 714 | f 49//347 133//347 51//347 715 | f 203//348 204//348 202//348 716 | f 209//349 211//349 210//350 717 | f 219//351 223//351 220//351 718 | f 223//352 224//352 220//352 719 | f 210//353 235//353 209//353 720 | f 235//354 236//354 209//354 721 | f 235//355 210//355 258//355 722 | f 202//356 204//356 261//356 723 | f 204//357 262//357 261//357 724 | f 219//358 202//358 266//358 725 | f 202//359 261//359 266//359 726 | f 219//360 266//360 223//360 727 | f 266//361 267//361 223//361 728 | f 261//362 262//362 276//362 729 | f 262//363 277//363 276//363 730 | f 278//364 279//364 265//364 731 | f 266//365 261//365 280//365 732 | f 261//366 276//366 280//366 733 | f 267//367 266//367 281//367 734 | f 266//368 280//368 281//368 735 | f 317//369 318//369 263//369 736 | f 211//349 209//349 319//370 737 | f 209//371 320//371 319//371 738 | f 209//372 236//372 320//372 739 | f 236//373 323//373 320//373 740 | f 265//374 326//374 263//374 741 | f 326//375 317//375 263//375 742 | f 211//376 319//376 269//376 743 | f 319//377 327//377 269//377 744 | f 279//378 329//378 328//378 745 | f 329//379 330//379 328//379 746 | f 279//380 328//380 265//380 747 | f 328//381 326//381 265//381 748 | f 329//382 331//382 330//382 749 | f 331//383 332//383 330//383 750 | f 269//384 327//384 283//384 751 | f 327//385 333//385 283//385 752 | f 283//386 333//386 331//386 753 | f 333//387 332//387 331//387 754 | f 317//388 204//389 318//390 755 | f 204//389 203//391 318//390 756 | f 319//392 320//392 223//392 757 | f 320//393 224//393 223//393 758 | f 320//394 323//394 224//394 759 | f 326//395 262//396 317//397 760 | f 262//396 204//398 317//397 761 | f 319//399 223//400 327//401 762 | f 223//400 267//402 327//401 763 | f 328//403 330//403 277//403 764 | f 330//404 276//404 277//404 765 | f 328//405 277//405 326//405 766 | f 277//406 262//406 326//406 767 | f 330//407 332//407 276//407 768 | f 332//408 280//408 276//408 769 | f 327//409 267//409 333//409 770 | f 267//410 281//410 333//410 771 | f 333//411 281//411 332//411 772 | f 281//412 280//412 332//412 773 | usemtl stem 774 | f 171//413 172//414 173//415 775 | f 173//416 172//416 174//416 776 | f 171//413 173//415 175//417 777 | f 175//418 173//418 176//418 778 | f 173//419 174//419 177//419 779 | f 173//420 177//420 176//420 780 | f 40//138 1//36 39//421 781 | f 40//138 39//421 51//157 782 | f 62//422 72//423 61//424 783 | f 61//424 72//423 71//425 784 | f 65//426 75//426 62//426 785 | f 62//422 75//427 72//423 786 | f 65//428 77//429 75//430 787 | f 171//36 175//36 178//36 788 | f 61//431 95//48 11//432 789 | f 11//432 95//48 87//50 790 | f 61//433 71//433 95//433 791 | f 95//434 71//434 97//434 792 | f 97//435 127//436 95//437 793 | f 95//437 127//436 123//438 794 | f 72//36 135//36 71//36 795 | f 71//36 135//36 136//36 796 | f 75//36 139//36 72//36 797 | f 72//36 139//36 135//36 798 | f 77//36 141//36 75//36 799 | f 75//36 141//36 139//36 800 | f 78//439 142//439 77//439 801 | f 77//440 142//440 141//440 802 | f 78//441 79//441 142//441 803 | f 142//442 79//442 143//442 804 | f 80//443 76//443 144//443 805 | f 144//444 76//444 140//444 806 | f 79//445 80//446 143//447 807 | f 143//448 80//448 144//448 808 | f 97//36 71//36 145//36 809 | f 71//36 136//36 145//36 810 | f 127//36 97//36 147//36 811 | f 147//36 97//36 145//36 812 | f 145//36 151//36 147//36 813 | f 144//449 140//450 160//451 814 | f 160//451 140//450 159//452 815 | f 143//453 144//453 161//453 816 | f 161//454 144//454 160//454 817 | f 143//455 161//455 142//455 818 | f 142//456 161//456 141//456 819 | f 136//36 135//36 163//36 820 | f 163//36 135//36 162//36 821 | f 135//36 139//36 162//36 822 | f 162//36 139//36 164//36 823 | f 139//36 141//36 164//36 824 | f 164//36 141//36 165//36 825 | f 145//36 136//36 151//36 826 | f 151//36 136//36 163//36 827 | f 159//457 168//457 160//457 828 | f 160//458 168//458 169//458 829 | f 160//459 169//459 161//459 830 | f 161//460 169//460 170//460 831 | f 161//461 170//461 141//461 832 | f 141//462 170//463 165//464 833 | f 162//465 168//466 167//467 834 | f 178//36 172//36 171//36 835 | f 1//468 2//469 172//470 836 | f 172//470 2//469 174//469 837 | f 8//471 7//472 176//473 838 | f 176//473 7//472 175//472 839 | f 2//474 16//474 174//474 840 | f 174//474 16//474 177//474 841 | f 16//475 8//471 177//475 842 | f 177//475 8//471 176//473 843 | f 7//472 81//476 175//472 844 | f 175//472 81//476 178//476 845 | f 81//477 1//468 178//477 846 | f 178//477 1//468 172//470 847 | f 169//478 168//479 184//480 848 | f 184//480 168//479 183//481 849 | f 170//482 169//483 185//484 850 | f 185//484 169//483 184//485 851 | f 165//464 170//463 182//486 852 | f 182//486 170//463 185//487 853 | f 162//488 181//489 168//490 854 | f 168//490 181//489 183//491 855 | f 184//480 183//481 189//492 856 | f 189//492 183//481 188//493 857 | f 185//484 184//485 190//494 858 | f 190//494 184//485 189//495 859 | f 182//486 185//487 187//496 860 | f 187//496 185//487 190//497 861 | f 183//491 181//489 188//498 862 | f 189//492 188//493 194//499 863 | f 194//499 188//493 193//500 864 | f 190//494 189//495 195//501 865 | f 195//501 189//495 194//502 866 | f 187//496 190//497 192//503 867 | f 192//503 190//497 195//504 868 | f 186//505 191//506 188//498 869 | f 188//498 191//506 193//507 870 | f 199//508 194//499 198//509 871 | f 200//510 195//501 194//502 872 | f 192//503 195//504 197//511 873 | f 197//511 195//504 200//512 874 | f 193//507 191//506 198//513 875 | f 213//514 215//515 214//516 876 | f 215//517 216//517 214//517 877 | f 213//514 229//518 215//515 878 | f 229//519 230//519 215//519 879 | f 215//520 251//520 216//520 880 | f 215//521 230//521 251//521 881 | f 292//522 293//523 306//524 882 | f 293//523 307//525 306//524 883 | f 292//522 306//524 312//526 884 | f 213//86 321//86 229//86 885 | f 293//527 244//528 334//94 886 | f 244//528 324//95 334//94 887 | f 293//529 334//529 307//529 888 | f 334//530 336//530 307//530 889 | f 336//531 334//100 305//102 890 | f 306//86 307//86 342//86 891 | f 307//86 343//86 342//86 892 | f 312//86 306//86 346//86 893 | f 306//86 342//86 346//86 894 | f 77//86 312//86 141//86 895 | f 312//86 346//86 141//86 896 | f 314//532 77//532 348//532 897 | f 77//533 141//533 348//533 898 | f 314//534 348//534 315//534 899 | f 348//535 349//535 315//535 900 | f 316//536 350//536 313//536 901 | f 350//537 347//537 313//537 902 | f 315//538 349//539 316//540 903 | f 349//541 350//541 316//541 904 | f 336//86 351//86 307//86 905 | f 307//86 351//86 343//86 906 | f 305//86 353//86 336//86 907 | f 353//86 351//86 336//86 908 | f 351//86 353//86 357//86 909 | f 350//542 366//543 347//544 910 | f 366//543 365//545 347//544 911 | f 349//546 367//546 350//546 912 | f 367//547 366//547 350//547 913 | f 349//548 348//548 367//548 914 | f 348//549 141//549 367//549 915 | f 343//86 368//86 342//86 916 | f 368//86 369//86 342//86 917 | f 342//86 369//86 346//86 918 | f 369//86 370//86 346//86 919 | f 346//86 370//86 141//86 920 | f 370//86 165//86 141//86 921 | f 351//86 357//86 343//86 922 | f 357//86 368//86 343//86 923 | f 365//550 366//550 373//550 924 | f 366//551 374//551 373//551 925 | f 366//552 367//552 374//552 926 | f 367//553 375//553 374//553 927 | f 367//554 141//554 375//554 928 | f 141//555 165//556 375//557 929 | f 321//86 213//86 214//86 930 | f 263//468 214//470 253//469 931 | f 253//469 214//470 216//469 932 | f 241//558 230//473 243//472 933 | f 243//472 230//473 229//472 934 | f 253//474 216//474 252//474 935 | f 252//474 216//474 251//474 936 | f 252//475 251//475 241//558 937 | f 241//558 251//475 230//473 938 | f 243//472 229//472 318//476 939 | f 318//476 229//472 321//476 940 | f 318//477 321//477 263//468 941 | f 263//468 321//477 214//470 942 | f 374//559 379//560 373//561 943 | f 373//561 379//560 378//562 944 | f 375//563 380//564 374//565 945 | f 374//565 380//564 379//566 946 | f 165//556 182//567 375//557 947 | f 375//557 182//567 380//568 948 | f 378//569 181//570 373//571 949 | f 373//571 181//570 369//572 950 | f 379//560 382//573 378//562 951 | f 378//562 382//573 381//574 952 | f 380//564 383//575 379//566 953 | f 379//566 383//575 382//576 954 | f 182//567 187//577 380//568 955 | f 380//568 187//577 383//578 956 | f 181//570 381//579 186//580 957 | f 382//573 385//581 381//574 958 | f 381//574 385//581 384//582 959 | f 383//575 386//583 382//576 960 | f 382//576 386//583 385//584 961 | f 187//577 192//585 383//578 962 | f 383//578 192//585 386//586 963 | f 384//587 191//588 381//579 964 | f 381//579 191//588 186//580 965 | f 384//582 385//581 387//589 966 | f 385//584 386//583 388//590 967 | f 192//585 197//591 386//586 968 | f 386//586 197//591 389//592 969 | f 191//588 387//593 196//594 970 | f 378//569 381//579 181//570 971 | f 188//498 181//489 186//505 972 | f 384//587 387//593 191//588 973 | f 198//513 191//595 196//596 974 | f 388//590 386//583 389//597 975 | f 387//598 385//581 388//598 976 | f 198//509 194//499 193//500 977 | f 200//599 194//600 199//601 978 | usemtl temporal 979 | f 8//36 12//36 7//34 980 | f 7//34 12//36 11//36 981 | f 10//602 14//602 9//602 982 | f 9//603 14//603 13//603 983 | f 12//36 8//36 21//36 984 | f 13//604 22//604 9//604 985 | f 9//605 22//605 20//605 986 | f 22//606 38//606 20//606 987 | f 12//607 62//608 11//432 988 | f 11//432 62//608 61//431 989 | f 13//609 14//609 64//609 990 | f 64//610 14//610 63//610 991 | f 21//36 65//36 12//36 992 | f 12//611 65//611 62//611 993 | f 22//612 13//612 66//612 994 | f 66//613 13//613 64//613 995 | f 36//614 67//614 21//614 996 | f 21//36 67//36 65//36 997 | f 36//615 35//615 67//615 998 | f 67//616 35//616 68//616 999 | f 35//617 37//617 68//617 1000 | f 68//618 37//618 69//618 1001 | f 38//619 22//619 70//619 1002 | f 70//620 22//620 66//620 1003 | f 37//621 38//621 69//621 1004 | f 69//622 38//622 70//622 1005 | f 64//623 63//623 74//623 1006 | f 74//624 63//624 73//624 1007 | f 66//625 64//625 76//625 1008 | f 76//626 64//626 74//626 1009 | f 67//627 77//429 65//428 1010 | f 68//628 78//628 67//628 1011 | f 67//629 78//629 77//629 1012 | f 69//630 79//630 68//630 1013 | f 68//631 79//631 78//631 1014 | f 70//632 66//632 80//632 1015 | f 80//633 66//633 76//633 1016 | f 69//634 70//634 79//634 1017 | f 79//445 70//635 80//446 1018 | f 10//636 83//636 86//636 1019 | f 14//637 10//637 88//637 1020 | f 88//638 10//638 86//638 1021 | f 14//639 88//639 63//639 1022 | f 63//640 88//640 96//640 1023 | f 63//641 96//641 73//641 1024 | f 73//642 96//642 98//642 1025 | f 241//86 243//85 242//86 1026 | f 243//85 244//86 242//86 1027 | f 236//643 235//643 249//643 1028 | f 235//644 250//644 249//644 1029 | f 242//86 259//86 241//86 1030 | f 250//645 235//645 260//645 1031 | f 235//646 258//646 260//646 1032 | f 260//647 258//647 247//647 1033 | f 242//648 244//528 292//649 1034 | f 244//528 293//527 292//649 1035 | f 250//650 296//650 249//650 1036 | f 296//651 297//651 249//651 1037 | f 259//86 242//86 298//86 1038 | f 242//652 292//652 298//652 1039 | f 260//653 299//653 250//653 1040 | f 299//654 296//654 250//654 1041 | f 239//655 259//655 300//655 1042 | f 259//86 298//86 300//86 1043 | f 239//656 300//656 240//656 1044 | f 300//657 301//657 240//657 1045 | f 240//658 301//658 245//658 1046 | f 301//659 302//659 245//659 1047 | f 247//660 303//660 260//660 1048 | f 303//661 299//661 260//661 1049 | f 245//662 302//662 247//662 1050 | f 302//663 303//663 247//663 1051 | f 296//664 310//664 297//664 1052 | f 310//665 311//665 297//665 1053 | f 298//666 292//666 312//666 1054 | f 299//667 313//667 296//667 1055 | f 313//668 310//668 296//668 1056 | f 300//669 298//669 77//669 1057 | f 298//670 312//670 77//670 1058 | f 301//671 300//671 314//671 1059 | f 300//672 77//672 314//672 1060 | f 302//673 301//673 315//673 1061 | f 301//674 314//674 315//674 1062 | f 303//675 316//675 299//675 1063 | f 316//676 313//676 299//676 1064 | f 302//677 315//677 303//677 1065 | f 315//538 316//540 303//678 1066 | f 249//679 325//679 236//679 1067 | f 325//680 323//680 236//680 1068 | f 249//681 297//681 325//681 1069 | f 297//682 335//682 325//682 1070 | f 297//683 311//683 335//683 1071 | f 311//684 337//684 335//684 1072 | usemtl cerebellum 1073 | f 74//685 73//685 137//685 1074 | f 137//686 73//686 138//686 1075 | f 76//687 74//687 140//687 1076 | f 140//688 74//688 137//688 1077 | f 73//689 98//689 138//689 1078 | f 138//690 98//690 146//690 1079 | f 128//691 127//691 148//691 1080 | f 148//692 127//693 147//694 1081 | f 129//695 128//695 149//695 1082 | f 149//696 128//696 148//696 1083 | f 98//697 130//697 146//697 1084 | f 146//698 130//698 150//698 1085 | f 130//699 129//699 150//699 1086 | f 150//700 129//700 149//700 1087 | f 147//701 151//702 152//703 1088 | f 148//692 147//694 153//704 1089 | f 153//705 147//705 152//705 1090 | f 149//706 148//706 154//706 1091 | f 154//707 148//708 153//709 1092 | f 146//710 150//710 155//710 1093 | f 155//711 150//711 156//711 1094 | f 150//712 149//712 156//712 1095 | f 156//713 149//713 154//713 1096 | f 137//714 138//714 158//714 1097 | f 158//715 138//715 157//715 1098 | f 137//716 158//716 140//716 1099 | f 140//717 158//717 159//717 1100 | f 146//718 155//718 138//718 1101 | f 138//719 155//719 157//719 1102 | f 158//720 157//720 166//720 1103 | f 166//721 157//721 167//721 1104 | f 158//722 166//722 159//722 1105 | f 159//723 166//723 168//723 1106 | f 167//467 168//466 166//724 1107 | f 157//725 180//726 167//727 1108 | f 167//728 152//728 162//728 1109 | f 162//729 152//729 163//729 1110 | f 163//730 152//703 151//702 1111 | f 152//731 156//731 153//731 1112 | f 153//709 156//732 154//707 1113 | f 156//733 179//734 155//735 1114 | f 179//736 180//737 155//738 1115 | f 155//738 180//737 157//739 1116 | f 156//733 152//740 179//734 1117 | f 180//737 179//736 152//741 1118 | f 167//727 180//726 152//742 1119 | f 310//743 344//743 311//743 1120 | f 344//744 345//744 311//744 1121 | f 313//745 347//745 310//745 1122 | f 347//746 344//746 310//746 1123 | f 311//747 345//747 337//747 1124 | f 345//748 352//748 337//748 1125 | f 304//749 354//749 305//749 1126 | f 354//750 353//751 305//752 1127 | f 308//753 355//753 304//753 1128 | f 355//754 354//754 304//754 1129 | f 337//755 352//755 309//755 1130 | f 352//756 356//756 309//756 1131 | f 309//757 356//757 308//757 1132 | f 356//758 355//758 308//758 1133 | f 353//759 358//760 357//761 1134 | f 354//750 359//762 353//751 1135 | f 359//763 358//763 353//763 1136 | f 355//764 360//764 354//764 1137 | f 360//765 359//766 354//767 1138 | f 352//768 361//768 356//768 1139 | f 361//769 362//769 356//769 1140 | f 356//770 362//770 355//770 1141 | f 362//771 360//771 355//771 1142 | f 344//772 363//772 345//772 1143 | f 363//773 364//773 345//773 1144 | f 344//774 347//774 363//774 1145 | f 347//775 365//775 363//775 1146 | f 352//776 345//776 361//776 1147 | f 345//777 364//777 361//777 1148 | f 363//778 371//778 364//778 1149 | f 371//779 372//779 364//779 1150 | f 363//780 365//780 371//780 1151 | f 365//781 373//781 371//781 1152 | f 369//782 372//783 373//784 1153 | f 372//783 371//785 373//784 1154 | f 364//786 372//787 376//788 1155 | f 372//789 369//789 358//789 1156 | f 369//790 368//790 358//790 1157 | f 368//791 357//761 358//760 1158 | f 358//792 359//792 362//792 1159 | f 359//766 360//765 362//793 1160 | f 362//794 361//795 377//796 1161 | f 377//797 361//798 376//799 1162 | f 361//798 364//800 376//799 1163 | f 362//794 377//796 358//801 1164 | f 376//799 358//802 377//797 1165 | f 372//787 358//803 376//788 -------------------------------------------------------------------------------- /assets/dash.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/dash.jpg -------------------------------------------------------------------------------- /assets/mattis.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/mattis.jpeg -------------------------------------------------------------------------------- /assets/people/people01.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people01.jpg -------------------------------------------------------------------------------- /assets/people/people02.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people02.jpg -------------------------------------------------------------------------------- /assets/people/people03.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people03.jpg -------------------------------------------------------------------------------- /assets/people/people04.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people04.jpg -------------------------------------------------------------------------------- /assets/people/people05.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people05.jpg -------------------------------------------------------------------------------- /assets/people/people06.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people06.jpg -------------------------------------------------------------------------------- /assets/people/people07.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people07.jpg -------------------------------------------------------------------------------- /assets/people/people08.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people08.jpg -------------------------------------------------------------------------------- /assets/people/people09.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people09.jpg -------------------------------------------------------------------------------- /assets/people/people10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people10.jpg -------------------------------------------------------------------------------- /assets/people/people11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people11.jpg -------------------------------------------------------------------------------- /assets/people/people12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people12.jpg -------------------------------------------------------------------------------- /assets/people/people13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people13.jpg -------------------------------------------------------------------------------- /assets/people/people14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people14.jpg -------------------------------------------------------------------------------- /assets/people/people15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people15.jpg -------------------------------------------------------------------------------- /assets/people/people16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people16.jpg -------------------------------------------------------------------------------- /assets/people/people17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people17.jpg -------------------------------------------------------------------------------- /assets/people/people18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people18.jpg -------------------------------------------------------------------------------- /assets/people/people19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people19.jpg -------------------------------------------------------------------------------- /assets/people/people20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people20.jpg -------------------------------------------------------------------------------- /assets/people/people21.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people21.jpg -------------------------------------------------------------------------------- /assets/people/people22.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people22.jpg -------------------------------------------------------------------------------- /assets/people/people23.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people23.jpg -------------------------------------------------------------------------------- /assets/people/people24.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people24.jpg -------------------------------------------------------------------------------- /assets/people/people25.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people25.jpg -------------------------------------------------------------------------------- /assets/people/people26.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people26.jpg -------------------------------------------------------------------------------- /assets/people/people27.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people27.jpg -------------------------------------------------------------------------------- /assets/people/people28.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people28.jpg -------------------------------------------------------------------------------- /assets/people/people29.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people29.jpg -------------------------------------------------------------------------------- /assets/people/people30.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people30.jpg -------------------------------------------------------------------------------- /assets/people/people31.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people31.jpg -------------------------------------------------------------------------------- /assets/people/people32.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people32.jpg -------------------------------------------------------------------------------- /assets/people/people33.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people33.jpg -------------------------------------------------------------------------------- /assets/people/people34.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people34.jpg -------------------------------------------------------------------------------- /assets/people/people35.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people35.jpg -------------------------------------------------------------------------------- /assets/people/people36.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people36.jpg -------------------------------------------------------------------------------- /assets/people/people37.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people37.jpg -------------------------------------------------------------------------------- /assets/people/people38.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people38.jpg -------------------------------------------------------------------------------- /assets/people/people39.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people39.jpg -------------------------------------------------------------------------------- /assets/people/people40.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people40.jpg -------------------------------------------------------------------------------- /assets/people/people41.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people41.jpg -------------------------------------------------------------------------------- /assets/people/people42.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people42.jpg -------------------------------------------------------------------------------- /assets/people/people43.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people43.jpg -------------------------------------------------------------------------------- /assets/people/people44.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people44.jpg -------------------------------------------------------------------------------- /assets/people/people45.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people45.jpg -------------------------------------------------------------------------------- /assets/people/people46.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/people/people46.jpg -------------------------------------------------------------------------------- /assets/tim_sneath.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/tim_sneath.jpg -------------------------------------------------------------------------------- /assets/volcano.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/assets/volcano.png -------------------------------------------------------------------------------- /images/blocks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/blocks.gif -------------------------------------------------------------------------------- /images/circle_wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/circle_wave.gif -------------------------------------------------------------------------------- /images/creatures.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/creatures.gif -------------------------------------------------------------------------------- /images/disks.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/disks.gif -------------------------------------------------------------------------------- /images/image_bubble.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/image_bubble.gif -------------------------------------------------------------------------------- /images/mattis.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/mattis.gif -------------------------------------------------------------------------------- /images/particles.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/particles.gif -------------------------------------------------------------------------------- /images/portrait.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/portrait.gif -------------------------------------------------------------------------------- /images/rotating_bubbles.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/rotating_bubbles.gif -------------------------------------------------------------------------------- /images/rotating_planets.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/rotating_planets.gif -------------------------------------------------------------------------------- /images/triangles.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/triangles.gif -------------------------------------------------------------------------------- /images/volcano.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/volcano.gif -------------------------------------------------------------------------------- /images/wave.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/letsar/flutter_counter_challenge_2020/481535d20d7eb944e682825e0f38e6bcd24c595b/images/wave.gif -------------------------------------------------------------------------------- /lib/common.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/services.dart'; 5 | 6 | class CounterText extends StatelessWidget { 7 | const CounterText({ 8 | Key key, 9 | @required this.counter, 10 | }) : super(key: key); 11 | 12 | final int counter; 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | return Center( 17 | child: Column( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | Text( 21 | 'You have pushed the button this many times:', 22 | style: Theme.of(context).textTheme.bodyText1.copyWith( 23 | foreground: Paint() 24 | ..blendMode = BlendMode.difference 25 | ..color = Colors.white, 26 | ), 27 | ), 28 | Text( 29 | '$counter', 30 | style: Theme.of(context).textTheme.headline2.copyWith( 31 | // Comment on web. 32 | foreground: Paint() 33 | ..blendMode = BlendMode.difference 34 | ..color = Colors.white, 35 | ), 36 | ), 37 | ], 38 | ), 39 | ); 40 | } 41 | } 42 | 43 | double map(double x, double minIn, double maxIn, double minOut, double maxOut) { 44 | return (x - minIn) * (maxOut - minOut) / (maxIn - minIn) + minOut; 45 | } 46 | 47 | final Random _random = Random(); 48 | 49 | double randNextD(double max) => _random.nextDouble() * max; 50 | int randNextI(int max) => _random.nextInt(max); 51 | double randD(double min, double max) => _random.d(min, max); 52 | int randI(int min, int max) => _random.i(min, max); 53 | 54 | extension RandomExtension on Random { 55 | double d(double min, double max) { 56 | return nextDouble() * (max - min) + min; 57 | } 58 | 59 | int i(int min, int max) { 60 | return nextInt(max - min) + min; 61 | } 62 | } 63 | 64 | class Pixels { 65 | const Pixels({ 66 | @required this.byteData, 67 | @required this.width, 68 | @required this.height, 69 | }); 70 | 71 | final ByteData byteData; 72 | final int width; 73 | final int height; 74 | 75 | Color getColorAt(int x, int y) { 76 | final offset = 4 * (x + y * width); 77 | final rgba = byteData.getUint32(offset); 78 | final a = rgba & 0xFF; 79 | final rgb = rgba >> 8; 80 | final argb = (a << 24) + rgb; 81 | return Color(argb); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /lib/demo_blocks.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'common.dart'; 6 | 7 | class DemoBlocks extends StatefulWidget { 8 | const DemoBlocks({Key key}) : super(key: key); 9 | 10 | @override 11 | _DemoBlocksState createState() => _DemoBlocksState(); 12 | } 13 | 14 | class _DemoBlocksState extends State { 15 | static const blockCount = 5; 16 | final Random random = Random(); 17 | final List indices = []; 18 | final List lastIndices = List.filled(blockCount, blockCount); 19 | 20 | void _incrementCounter() { 21 | final dx = random.nextInt(blockCount); 22 | final dy = (lastIndices[dx] - 1) % blockCount; 23 | lastIndices[dx] = dy; 24 | setState(() { 25 | indices.add(Offset(dx.toDouble(), dy.toDouble())); 26 | }); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: const Text('Blocks'), 34 | ), 35 | body: LayoutBuilder( 36 | builder: (_, constraints) { 37 | final blockSize = Size( 38 | constraints.maxWidth / blockCount, 39 | constraints.maxHeight / blockCount, 40 | ); 41 | 42 | return Stack( 43 | children: [ 44 | for (int i = 0; i < indices.length; i++) 45 | Positioned.fill( 46 | child: Block( 47 | blockSize: blockSize, 48 | endOffset: Offset( 49 | indices[i].dx * blockSize.width, 50 | indices[i].dy * blockSize.height, 51 | ), 52 | ), 53 | ), 54 | CounterText(counter: indices.length), 55 | ], 56 | ); 57 | }, 58 | ), 59 | floatingActionButton: FloatingActionButton( 60 | onPressed: _incrementCounter, 61 | tooltip: 'Increment', 62 | child: const Icon(Icons.add), 63 | ), 64 | ); 65 | } 66 | } 67 | 68 | class Block extends StatefulWidget { 69 | const Block({ 70 | Key key, 71 | this.blockSize, 72 | this.endOffset, 73 | }) : super(key: key); 74 | 75 | final Size blockSize; 76 | final Offset endOffset; 77 | 78 | @override 79 | _BlockState createState() => _BlockState(); 80 | } 81 | 82 | class _BlockState extends State with SingleTickerProviderStateMixin { 83 | AnimationController controller; 84 | Animation offset; 85 | 86 | @override 87 | void initState() { 88 | super.initState(); 89 | 90 | final endOffset = widget.endOffset; 91 | final blockHeight = widget.blockSize.height; 92 | final distance = blockHeight + endOffset.dy; 93 | final duration = (distance * 2).toInt(); 94 | 95 | controller = AnimationController( 96 | vsync: this, 97 | duration: Duration(milliseconds: duration), 98 | )..forward(); 99 | 100 | offset = controller.drive( 101 | Tween( 102 | begin: Offset(endOffset.dx, -blockHeight), 103 | end: endOffset, 104 | ), 105 | ); 106 | } 107 | 108 | @override 109 | void dispose() { 110 | controller.dispose(); 111 | super.dispose(); 112 | } 113 | 114 | @override 115 | Widget build(BuildContext context) { 116 | return CustomPaint( 117 | painter: BlockPainter( 118 | offset, 119 | widget.blockSize, 120 | ), 121 | ); 122 | } 123 | } 124 | 125 | class BlockPainter extends CustomPainter { 126 | BlockPainter( 127 | this.offset, 128 | this.blockSize, 129 | ) : super(repaint: offset); 130 | 131 | final Animation offset; 132 | final Size blockSize; 133 | 134 | @override 135 | void paint(Canvas canvas, Size size) { 136 | canvas.drawRect( 137 | offset.value & blockSize, 138 | Paint() 139 | ..color = Colors.white 140 | ..blendMode = BlendMode.difference, 141 | ); 142 | } 143 | 144 | @override 145 | bool shouldRepaint(BlockPainter oldDelegate) => false; 146 | } 147 | -------------------------------------------------------------------------------- /lib/demo_circle_wave.dart: -------------------------------------------------------------------------------- 1 | // Credits to https://dribbble.com/shots/1698964-Circle-wave-II 2 | 3 | import 'dart:math' as math; 4 | import 'dart:ui'; 5 | 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'common.dart'; 9 | 10 | class DemoCircleWave extends StatefulWidget { 11 | const DemoCircleWave({ 12 | Key key, 13 | }) : super(key: key); 14 | 15 | @override 16 | _DemoCircleWaveState createState() => _DemoCircleWaveState(); 17 | } 18 | 19 | class _DemoCircleWaveState extends State 20 | with TickerProviderStateMixin { 21 | static const colors = [ 22 | Color(0xFFFF2964), 23 | Color(0xFF32FF3A), 24 | Color(0xFF4255FF) 25 | ]; 26 | AnimationController controller; 27 | AnimationController addPointController; 28 | Animation addPointAnimation; 29 | int _counter = 0; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | controller = AnimationController( 35 | vsync: this, 36 | upperBound: 2, 37 | duration: const Duration(seconds: 10), 38 | )..repeat(); 39 | addPointController = AnimationController( 40 | vsync: this, 41 | duration: const Duration(milliseconds: 500), 42 | ); 43 | addPointAnimation = 44 | addPointController.drive(CurveTween(curve: Curves.ease)); 45 | } 46 | 47 | @override 48 | void dispose() { 49 | controller.dispose(); 50 | super.dispose(); 51 | } 52 | 53 | void _incrementCounter() { 54 | setState(() { 55 | _counter++; 56 | addPointController.forward(from: 0); 57 | }); 58 | } 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return Scaffold( 63 | appBar: AppBar(title: const Text('Circle wave')), 64 | backgroundColor: Colors.black, 65 | body: Stack( 66 | children: [ 67 | for (int i = 0; i < 3; i++) 68 | Positioned.fill( 69 | child: TweenAnimationBuilder( 70 | tween: Tween(begin: 0, end: 1), 71 | duration: const Duration(milliseconds: 500), 72 | curve: Curves.easeIn, 73 | builder: (_, double opacity, __) { 74 | return CustomPaint( 75 | painter: CircleWavePainter( 76 | controller, 77 | addPointAnimation, 78 | i, 79 | colors[i].withOpacity(opacity), 80 | _counter, 81 | ), 82 | ); 83 | }, 84 | ), 85 | ), 86 | CounterText(counter: _counter), 87 | ], 88 | ), 89 | floatingActionButton: FloatingActionButton( 90 | onPressed: _incrementCounter, 91 | tooltip: 'Increment', 92 | child: const Icon(Icons.add), 93 | ), 94 | ); 95 | } 96 | } 97 | 98 | class CircleWavePainter extends CustomPainter { 99 | CircleWavePainter( 100 | this.animation, 101 | this.addAnimation, 102 | this.index, 103 | this.color, 104 | this.count, 105 | ) : super(repaint: animation); 106 | final Animation animation; 107 | final Animation addAnimation; 108 | final int index; 109 | final Color color; 110 | final int count; 111 | 112 | static const halfPi = math.pi / 2; 113 | static const twoPi = math.pi * 2; 114 | final n = 7; 115 | 116 | @override 117 | void paint(Canvas canvas, Size size) { 118 | final t = animation.value; 119 | final halfWidth = size.width / 2; 120 | final halfHeight = size.height / 2; 121 | final q = index * halfPi; 122 | 123 | List computeOffsets(int length) { 124 | final offsets = []; 125 | for (var i = 0; i < length; i++) { 126 | final th = i * twoPi / length; 127 | double os = map(math.cos(th - twoPi * t), -1, 1, 0, 1); 128 | os = 0.125 * math.pow(os, 2.75); 129 | final r = 165 * (1 + os * math.cos(n * th + 1.5 * twoPi * t + q)); 130 | offsets.add(Offset( 131 | r * math.sin(th) + halfWidth, -r * math.cos(th) + halfHeight)); 132 | } 133 | return offsets; 134 | } 135 | 136 | final offsets = computeOffsets(count); 137 | 138 | if (count > 1 && addAnimation.value < 1) { 139 | final t = addAnimation.value; 140 | final oldOffsets = computeOffsets(count - 1); 141 | for (var i = 0; i < count - 1; i++) { 142 | offsets[i] = Offset.lerp(oldOffsets[i], offsets[i], t); 143 | } 144 | offsets[count - 1] = Offset.lerp( 145 | oldOffsets[count - 2], 146 | offsets[count - 1], 147 | t, 148 | ); 149 | } 150 | 151 | final path = Path()..addPolygon(offsets, true); 152 | canvas.drawPath( 153 | path, 154 | Paint() 155 | ..blendMode = BlendMode.lighten 156 | ..color = color 157 | ..strokeWidth = 8 158 | ..style = PaintingStyle.stroke, 159 | ); 160 | } 161 | 162 | @override 163 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 164 | return false; 165 | } 166 | } 167 | -------------------------------------------------------------------------------- /lib/demo_creature.dart: -------------------------------------------------------------------------------- 1 | // Credits to https://gist.github.com/beesandbombs/6f3e6fb723f50b080916816ae8e561e3 2 | 3 | import 'dart:math' as math; 4 | import 'dart:ui'; 5 | 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'common.dart'; 9 | 10 | class DemoCreature extends StatefulWidget { 11 | const DemoCreature({ 12 | Key key, 13 | }) : super(key: key); 14 | 15 | @override 16 | _DemoCreatureState createState() => _DemoCreatureState(); 17 | } 18 | 19 | class _DemoCreatureState extends State 20 | with TickerProviderStateMixin { 21 | static const colors = [ 22 | Color(0xFFFF2964), 23 | Color(0xFF32FF3A), 24 | Color(0xFF4255FF) 25 | ]; 26 | AnimationController controller; 27 | AnimationController addPointController; 28 | Animation addPointAnimation; 29 | int _counter = 0; 30 | 31 | @override 32 | void initState() { 33 | super.initState(); 34 | controller = AnimationController( 35 | vsync: this, 36 | upperBound: 2, 37 | duration: const Duration(seconds: 10), 38 | )..repeat(); 39 | addPointController = AnimationController( 40 | vsync: this, 41 | duration: const Duration(milliseconds: 500), 42 | ); 43 | addPointAnimation = 44 | addPointController.drive(CurveTween(curve: Curves.ease)); 45 | } 46 | 47 | @override 48 | void dispose() { 49 | controller.dispose(); 50 | super.dispose(); 51 | } 52 | 53 | void _incrementCounter() { 54 | setState(() { 55 | _counter++; 56 | addPointController.forward(from: 0); 57 | }); 58 | } 59 | 60 | @override 61 | Widget build(BuildContext context) { 62 | return Scaffold( 63 | appBar: AppBar(title: const Text('Creature')), 64 | backgroundColor: Colors.black, 65 | body: Stack( 66 | children: [ 67 | for (int i = 0; i < _counter; i++) 68 | Positioned.fill( 69 | child: TweenAnimationBuilder( 70 | tween: Tween(begin: 0, end: 1), 71 | duration: const Duration(milliseconds: 500), 72 | curve: Curves.easeIn, 73 | builder: (_, double opacity, __) { 74 | return CustomPaint( 75 | painter: CreaturePainter( 76 | controller, 77 | addPointAnimation, 78 | i, 79 | colors[i % colors.length].withOpacity(opacity), 80 | _counter, 81 | ), 82 | ); 83 | }, 84 | ), 85 | ), 86 | CounterText(counter: _counter), 87 | ], 88 | ), 89 | floatingActionButton: FloatingActionButton( 90 | onPressed: _incrementCounter, 91 | tooltip: 'Increment', 92 | child: const Icon(Icons.add), 93 | ), 94 | ); 95 | } 96 | } 97 | 98 | class CreaturePainter extends CustomPainter { 99 | CreaturePainter( 100 | this.animation, 101 | this.addAnimation, 102 | this.index, 103 | this.color, 104 | this.count, 105 | ) : super(repaint: animation); 106 | final Animation animation; 107 | final Animation addAnimation; 108 | final int index; 109 | final Color color; 110 | final int count; 111 | 112 | static const halfPi = math.pi / 2; 113 | static const twoPi = math.pi * 2; 114 | final n = 300; 115 | 116 | @override 117 | void paint(Canvas canvas, Size size) { 118 | final t = animation.value; 119 | final halfWidth = size.width / 2; 120 | final halfHeight = size.height / 2; 121 | final q = twoPi * index / count; 122 | canvas.translate(halfWidth, halfHeight); 123 | if (index > 0 && count > 2) { 124 | canvas.rotate( 125 | twoPi * (index / (count - 1)) * (count - addAnimation.value) / count); 126 | } else { 127 | canvas.rotate(q); 128 | } 129 | 130 | List computeOffsets(int length) { 131 | final offsets = []; 132 | for (var i = 0; i < n; i++) { 133 | final qq = i / (n - 1); 134 | final r = map(math.cos(twoPi * qq), 1, -1, 0, 42) * math.sqrt(qq); 135 | final th = 12 * twoPi * qq - 4 * twoPi * t - q; 136 | final x = r * math.cos(th); 137 | final y = -(halfWidth - 10) * qq + r * math.sin(th); 138 | final tw = math.pi / 10 * math.sin(twoPi * t - math.pi * qq); 139 | final xx = x * math.cos(tw) + y * math.sin(tw); 140 | final yy = y * math.cos(tw) - x * math.sin(tw); 141 | 142 | offsets.add(Offset(xx, yy)); 143 | } 144 | return offsets; 145 | } 146 | 147 | final offsets = computeOffsets(count); 148 | 149 | final path = Path()..addPolygon(offsets, false); 150 | canvas.drawPath( 151 | path, 152 | Paint() 153 | ..blendMode = BlendMode.lighten 154 | ..color = color 155 | ..strokeWidth = 5 156 | ..style = PaintingStyle.stroke 157 | ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 10), 158 | ); 159 | } 160 | 161 | @override 162 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 163 | return false; 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /lib/demo_disks.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'common.dart'; 6 | 7 | class DemoDisks extends StatefulWidget { 8 | const DemoDisks({Key key}) : super(key: key); 9 | 10 | @override 11 | _DemoDisksState createState() => _DemoDisksState(); 12 | } 13 | 14 | class _DemoDisksState extends State { 15 | final Random random = Random(); 16 | int _counter = 0; 17 | 18 | void _incrementCounter() { 19 | setState(() { 20 | _counter++; 21 | }); 22 | } 23 | 24 | @override 25 | Widget build(BuildContext context) { 26 | return Scaffold( 27 | appBar: AppBar( 28 | title: const Text('Disks'), 29 | ), 30 | body: Stack( 31 | children: [ 32 | for (int i = 0; i < _counter; i++) 33 | Positioned.fill( 34 | child: Disk(random: random), 35 | ), 36 | CounterText(counter: _counter), 37 | ], 38 | ), 39 | floatingActionButton: FloatingActionButton( 40 | onPressed: _incrementCounter, 41 | tooltip: 'Increment', 42 | child: const Icon(Icons.add), 43 | ), 44 | ); 45 | } 46 | } 47 | 48 | class Disk extends StatefulWidget { 49 | const Disk({ 50 | Key key, 51 | @required this.random, 52 | }) : super(key: key); 53 | 54 | final Random random; 55 | 56 | @override 57 | _DiskState createState() => _DiskState(); 58 | } 59 | 60 | class _DiskState extends State with SingleTickerProviderStateMixin { 61 | AnimationController controller; 62 | double radius; 63 | CenterTween centerTween; 64 | 65 | @override 66 | void initState() { 67 | super.initState(); 68 | final random = widget.random; 69 | controller = AnimationController( 70 | vsync: this, 71 | duration: const Duration(seconds: 2), 72 | )..repeat(); 73 | radius = random.nextDouble() * 50 + 25; 74 | 75 | final candidates = [ 76 | Offset(random.nextDouble(), -0.1), 77 | Offset(random.nextDouble(), 1.1), 78 | Offset(-0.1, random.nextDouble()), 79 | Offset(1.1, random.nextDouble()), 80 | ]; 81 | 82 | final start = candidates.removeAt(random.nextInt(candidates.length)); 83 | final end = candidates.removeAt(random.nextInt(candidates.length)); 84 | 85 | centerTween = CenterTween(start, end, random.nextDouble()); 86 | } 87 | 88 | @override 89 | void dispose() { 90 | controller.dispose(); 91 | super.dispose(); 92 | } 93 | 94 | @override 95 | Widget build(BuildContext context) { 96 | return TweenAnimationBuilder( 97 | tween: Tween(begin: 0, end: radius), 98 | duration: const Duration(seconds: 1), 99 | curve: Curves.elasticOut, 100 | builder: (_, double effectiveRadius, __) { 101 | return CustomPaint( 102 | painter: DiskPainter(controller, centerTween, effectiveRadius), 103 | ); 104 | }, 105 | ); 106 | } 107 | } 108 | 109 | class CenterTween extends Animatable { 110 | const CenterTween(this.begin, this.end, this.shift) 111 | : translation = begin - end; 112 | 113 | final Offset begin; 114 | final Offset end; 115 | final double shift; 116 | final Offset translation; 117 | 118 | @override 119 | Offset transform(double t) { 120 | return begin + (end - begin) * ((t + shift) % 1); 121 | } 122 | } 123 | 124 | class DiskPainter extends CustomPainter { 125 | const DiskPainter( 126 | this.animation, 127 | this.centerTween, 128 | this.radius, 129 | ) : super(repaint: animation); 130 | 131 | final Animation animation; 132 | final double radius; 133 | final CenterTween centerTween; 134 | 135 | @override 136 | void paint(Canvas canvas, Size size) { 137 | final ratioCenter = centerTween.evaluate(animation); 138 | final center = Offset( 139 | ratioCenter.dx * size.width, 140 | ratioCenter.dy * size.height, 141 | ); 142 | final translation = Offset( 143 | centerTween.translation.dx * size.width, 144 | centerTween.translation.dy * size.height, 145 | ); 146 | canvas.drawCircle( 147 | center, 148 | radius, 149 | Paint() 150 | ..color = Colors.white 151 | ..blendMode = BlendMode.difference, 152 | ); 153 | canvas.drawCircle( 154 | center + translation, 155 | radius, 156 | Paint() 157 | ..color = Colors.white 158 | ..blendMode = BlendMode.difference, 159 | ); 160 | } 161 | 162 | @override 163 | bool shouldRepaint(DiskPainter oldDelegate) => oldDelegate.radius != radius; 164 | } 165 | -------------------------------------------------------------------------------- /lib/demo_image_bubble.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/rendering.dart'; 5 | 6 | import 'common.dart'; 7 | 8 | class DemoImageBubble extends StatefulWidget { 9 | const DemoImageBubble({Key key}) : super(key: key); 10 | 11 | @override 12 | _DemoImageBubbleState createState() => _DemoImageBubbleState(); 13 | } 14 | 15 | class _DemoImageBubbleState extends State 16 | with TickerProviderStateMixin { 17 | final Random random = Random(); 18 | final List bubbles = []; 19 | 20 | void _incrementCounter() { 21 | setState(() { 22 | bubbles.add(ImageBubble( 23 | center: Offset(random.nextDouble(), random.nextDouble()), 24 | radius: (random.nextInt(50) + 20).toDouble(), 25 | )); 26 | }); 27 | } 28 | 29 | @override 30 | Widget build(BuildContext context) { 31 | return Scaffold( 32 | appBar: AppBar( 33 | title: const Text('Image bubble'), 34 | ), 35 | body: Stack( 36 | children: [ 37 | ...bubbles, 38 | CounterText(counter: bubbles.length), 39 | ], 40 | ), 41 | floatingActionButton: FloatingActionButton( 42 | onPressed: _incrementCounter, 43 | tooltip: 'Increment', 44 | child: const Icon(Icons.add), 45 | ), 46 | ); 47 | } 48 | } 49 | 50 | class ImageBubble extends StatefulWidget { 51 | const ImageBubble({ 52 | Key key, 53 | @required this.center, 54 | @required this.radius, 55 | }) : super(key: key); 56 | 57 | final Offset center; 58 | final double radius; 59 | 60 | @override 61 | _ImageBubbleState createState() => _ImageBubbleState(); 62 | } 63 | 64 | class _ImageBubbleState extends State 65 | with TickerProviderStateMixin { 66 | AnimationController centerController; 67 | AnimationController radiusController; 68 | Animation center; 69 | Animation radius; 70 | 71 | @override 72 | void initState() { 73 | super.initState(); 74 | centerController = AnimationController( 75 | vsync: this, 76 | duration: const Duration(seconds: 4), 77 | )..repeat(); 78 | center = centerController.drive(RotationTween(widget.center, 0.01)); 79 | radiusController = AnimationController( 80 | vsync: this, 81 | duration: const Duration(milliseconds: 500), 82 | )..forward(); 83 | radius = radiusController 84 | .drive(CurveTween(curve: Curves.ease)) 85 | .drive(Tween(begin: 0, end: widget.radius)); 86 | } 87 | 88 | @override 89 | void dispose() { 90 | centerController.dispose(); 91 | radiusController.dispose(); 92 | super.dispose(); 93 | } 94 | 95 | @override 96 | Widget build(BuildContext context) { 97 | return SizedBox.expand( 98 | child: AnimatedBuilder( 99 | animation: center, 100 | builder: (_, child) { 101 | return CustomPaint( 102 | painter: ImageBubbleShadowPainter(center.value, radius.value), 103 | child: ClipOval( 104 | clipper: ImageBubbleClipper(center.value, radius.value), 105 | clipBehavior: Clip.hardEdge, 106 | child: CustomPaint( 107 | foregroundPainter: ImageBubblePainter( 108 | center.value, 109 | radius.value, 110 | ), 111 | child: child), 112 | ), 113 | ); 114 | }, 115 | child: Image.asset( 116 | 'assets/dash.jpg', 117 | fit: BoxFit.cover, 118 | ), 119 | ), 120 | ); 121 | } 122 | } 123 | 124 | class RotationTween extends Animatable { 125 | const RotationTween(this.center, this.distance); 126 | 127 | final Offset center; 128 | final double distance; 129 | 130 | @override 131 | Offset transform(double t) { 132 | final direction = t * pi * 2; 133 | return Offset.fromDirection(direction, distance) + center; 134 | } 135 | } 136 | 137 | class ImageBubbleClipper extends CustomClipper { 138 | const ImageBubbleClipper(this.center, this.radius); 139 | 140 | final Offset center; 141 | final double radius; 142 | 143 | @override 144 | Rect getClip(Size size) { 145 | final effectiveCenter = 146 | Offset(center.dx * size.width, center.dy * size.height); 147 | return Rect.fromCircle(center: effectiveCenter, radius: radius); 148 | } 149 | 150 | @override 151 | bool shouldReclip(covariant CustomClipper oldClipper) { 152 | return true; 153 | } 154 | } 155 | 156 | class ImageBubbleShadowPainter extends CustomPainter { 157 | ImageBubbleShadowPainter(this.center, this.radius); 158 | final Offset center; 159 | final double radius; 160 | 161 | @override 162 | void paint(Canvas canvas, Size size) { 163 | final effectiveCenter = 164 | Offset(center.dx * size.width, center.dy * size.height); 165 | final rect = Rect.fromCircle(center: effectiveCenter, radius: radius); 166 | 167 | const boxShadow = BoxShadow( 168 | blurRadius: 4, offset: Offset(2, 2), color: Color(0x80000000)); 169 | final Paint paint = boxShadow.toPaint(); 170 | final Rect bounds = 171 | rect.shift(boxShadow.offset).inflate(boxShadow.spreadRadius); 172 | 173 | canvas.drawCircle( 174 | bounds.center, 175 | bounds.shortestSide / 2.0, 176 | paint, 177 | ); 178 | } 179 | 180 | @override 181 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 182 | return true; 183 | } 184 | } 185 | 186 | class ImageBubblePainter extends CustomPainter { 187 | ImageBubblePainter(this.center, this.radius); 188 | final Offset center; 189 | final double radius; 190 | 191 | @override 192 | void paint(Canvas canvas, Size size) { 193 | final effectiveCenter = 194 | Offset(center.dx * size.width, center.dy * size.height); 195 | final rect = Rect.fromCircle(center: effectiveCenter, radius: radius); 196 | 197 | canvas.drawCircle( 198 | effectiveCenter, 199 | radius, 200 | Paint() 201 | ..blendMode = BlendMode.overlay 202 | ..shader = const LinearGradient(colors: [Colors.black, Colors.white]) 203 | .createShader(rect), 204 | ); 205 | canvas.drawCircle( 206 | rect.topLeft + (rect.center - rect.topLeft) / 2, 207 | rect.longestSide / 6, 208 | Paint() 209 | ..color = const Color(0xCCFFFFFF) 210 | ..maskFilter = MaskFilter.blur( 211 | BlurStyle.normal, 212 | rect.longestSide * 0.1, 213 | ), 214 | ); 215 | } 216 | 217 | @override 218 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 219 | return true; 220 | } 221 | } 222 | -------------------------------------------------------------------------------- /lib/demo_mattis.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'common.dart'; 7 | 8 | class DemoMattis extends StatefulWidget { 9 | const DemoMattis({ 10 | Key key, 11 | }) : super(key: key); 12 | 13 | @override 14 | _DemoMattisState createState() => _DemoMattisState(); 15 | } 16 | 17 | class _DemoMattisState extends State { 18 | static const colors = [ 19 | Color(0xFFFF2964), 20 | Color(0xFF32FF3A), 21 | Color(0xFF4255FF) 22 | ]; 23 | Random random = Random(); 24 | static const cols = 25; 25 | static const rows = 25; 26 | static const xUnit = 1 / cols; 27 | static const yUnit = 1 / rows; 28 | final List indexes = List.generate(cols * rows, (index) => index); 29 | final List blocks = []; 30 | final List balloons = []; 31 | 32 | void _incrementCounter() { 33 | if (indexes.isNotEmpty) { 34 | final index = indexes.removeAt(random.nextInt(indexes.length)); 35 | final col = (index % cols) / cols; 36 | final row = (index ~/ rows) / rows; 37 | final x = col + xUnit / 2; 38 | final y = row + yUnit / 2; 39 | setState(() { 40 | blocks.add(MattisBlock( 41 | key: ValueKey(blocks.length), 42 | center: Offset(x, y), 43 | sizeRatio: const Size(xUnit, yUnit), 44 | )); 45 | balloons.add( 46 | Balloon( 47 | key: ValueKey(balloons.length), 48 | x: x, 49 | color: colors[random.nextInt(colors.length)], 50 | ), 51 | ); 52 | }); 53 | } 54 | } 55 | 56 | @override 57 | Widget build(BuildContext context) { 58 | return Scaffold( 59 | appBar: AppBar( 60 | title: const Text('Happy Birthday'), 61 | ), 62 | backgroundColor: Colors.black, 63 | body: Stack( 64 | fit: StackFit.expand, 65 | children: [ 66 | ...blocks, 67 | ...balloons, 68 | CounterText(counter: blocks.length), 69 | ], 70 | ), 71 | floatingActionButton: FloatingActionButton( 72 | onPressed: _incrementCounter, 73 | tooltip: 'Increment', 74 | child: const Icon(Icons.add), 75 | ), 76 | ); 77 | } 78 | } 79 | 80 | class Balloon extends StatefulWidget { 81 | const Balloon({ 82 | Key key, 83 | this.color, 84 | this.x, 85 | }) : super(key: key); 86 | 87 | final double x; 88 | final Color color; 89 | 90 | @override 91 | _BalloonState createState() => _BalloonState(); 92 | } 93 | 94 | class _BalloonState extends State with SingleTickerProviderStateMixin { 95 | AnimationController yController; 96 | Animation y; 97 | 98 | @override 99 | void initState() { 100 | super.initState(); 101 | yController = AnimationController( 102 | vsync: this, 103 | duration: const Duration(milliseconds: 2000), 104 | )..forward(); 105 | y = yController 106 | .drive(CurveTween(curve: Curves.ease)) 107 | .drive(Tween(begin: 1, end: 0)); 108 | } 109 | 110 | @override 111 | void dispose() { 112 | yController.dispose(); 113 | super.dispose(); 114 | } 115 | 116 | @override 117 | Widget build(BuildContext context) { 118 | return Positioned.fill( 119 | child: CustomPaint( 120 | foregroundPainter: BalloonPainter(widget.x, y, widget.color), 121 | ), 122 | ); 123 | } 124 | } 125 | 126 | class MattisBlock extends StatefulWidget { 127 | const MattisBlock({ 128 | Key key, 129 | this.center, 130 | this.sizeRatio, 131 | }) : super(key: key); 132 | 133 | final Offset center; 134 | final Size sizeRatio; 135 | 136 | @override 137 | _MattisBlockState createState() => _MattisBlockState(); 138 | } 139 | 140 | class _MattisBlockState extends State 141 | with SingleTickerProviderStateMixin { 142 | AnimationController scaleController; 143 | Animation scale; 144 | 145 | @override 146 | void initState() { 147 | super.initState(); 148 | scaleController = AnimationController( 149 | vsync: this, 150 | duration: const Duration(milliseconds: 1000), 151 | )..forward(); 152 | scale = scaleController.drive(CurveTween(curve: Curves.elasticOut)); 153 | } 154 | 155 | @override 156 | void dispose() { 157 | scaleController.dispose(); 158 | super.dispose(); 159 | } 160 | 161 | @override 162 | Widget build(BuildContext context) { 163 | return Positioned.fill( 164 | child: ClipPath( 165 | clipper: MattisClipper(scale, widget.center, widget.sizeRatio), 166 | child: Image.asset( 167 | 'assets/mattis.jpeg', 168 | fit: BoxFit.cover, 169 | ), 170 | ), 171 | ); 172 | } 173 | } 174 | 175 | class MattisClipper extends CustomClipper { 176 | MattisClipper(this.scale, this.center, this.sizeRatio) : super(reclip: scale); 177 | 178 | final Animation scale; 179 | final Offset center; 180 | final Size sizeRatio; 181 | 182 | @override 183 | Path getClip(Size size) { 184 | final width = sizeRatio.width * size.width * scale.value; 185 | final height = sizeRatio.height * size.height * scale.value; 186 | final rect = Rect.fromCenter( 187 | center: Offset( 188 | center.dx * size.width, 189 | center.dy * size.height, 190 | ), 191 | width: width, 192 | height: height, 193 | ); 194 | return Path()..addRect(rect); 195 | } 196 | 197 | @override 198 | bool shouldReclip(covariant CustomClipper oldClipper) { 199 | return false; 200 | } 201 | } 202 | 203 | class BalloonPainter extends CustomPainter { 204 | BalloonPainter(this.x, this.y, this.color) : super(repaint: y); 205 | 206 | final double x; 207 | final Animation y; 208 | final Color color; 209 | 210 | @override 211 | void paint(Canvas canvas, Size size) { 212 | canvas.saveLayer( 213 | Offset.zero & size, 214 | Paint()..blendMode = BlendMode.lighten, 215 | ); 216 | final dx = x * size.width; 217 | final dy = map(y.value, 1, 0, size.height + 10, -85); 218 | final rect = Rect.fromCenter( 219 | center: Offset(dx, dy), 220 | width: 60, 221 | height: 75, 222 | ); 223 | 224 | final bottom = rect.bottomCenter.dy; 225 | final points = [ 226 | Offset(dx, bottom - 10), 227 | Offset(dx - 5, bottom + 10), 228 | Offset(dx + 5, bottom + 10), 229 | ]; 230 | 231 | final path = Path()..addPolygon(points, true); 232 | canvas.drawPath(path, Paint()..color = color); 233 | canvas.drawPath(path, Paint()..color = const Color(0x33000000)); 234 | 235 | canvas.drawOval( 236 | rect, 237 | Paint() 238 | ..color = color 239 | ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 5), 240 | ); 241 | 242 | canvas.drawCircle( 243 | rect.topLeft + const Offset(20, 20), 244 | rect.longestSide / 6, 245 | Paint() 246 | ..color = const Color(0xCCFFFFFF) 247 | ..maskFilter = MaskFilter.blur( 248 | BlurStyle.normal, 249 | rect.longestSide * 0.1, 250 | ), 251 | ); 252 | 253 | canvas.restore(); 254 | } 255 | 256 | @override 257 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 258 | return false; 259 | } 260 | } 261 | -------------------------------------------------------------------------------- /lib/demo_particles.dart: -------------------------------------------------------------------------------- 1 | // Credits to https://www.openprocessing.org/sketch/427313 2 | 3 | import 'dart:async'; 4 | import 'dart:math'; 5 | import 'dart:ui' as ui; 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:vector_math/vector_math.dart'; 9 | 10 | import 'common.dart'; 11 | 12 | const loadPercentage = 0.045; // 0 to 1.0. 13 | const countMultiplier = 1; 14 | const closeEnoughTarget = 50.0; 15 | const particleSize = 8.0; 16 | const speed = 1; 17 | const touchSize = 100; 18 | 19 | class DemoParticles extends StatefulWidget { 20 | const DemoParticles({ 21 | Key key, 22 | }) : super(key: key); 23 | 24 | @override 25 | _DemoParticlesState createState() => _DemoParticlesState(); 26 | } 27 | 28 | class _DemoParticlesState extends State { 29 | int _counter = 0; 30 | 31 | void _incrementCounter() { 32 | setState(() { 33 | _counter++; 34 | }); 35 | } 36 | 37 | String getAssetName(int i) { 38 | final n = i < 10 ? '0$i' : '$i'; 39 | return 'assets/people/people$n.jpg'; 40 | } 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return Scaffold( 45 | appBar: AppBar(title: const Text('Particles')), 46 | body: Stack( 47 | fit: StackFit.expand, 48 | alignment: Alignment.topCenter, 49 | children: [ 50 | Positioned.fill( 51 | child: LayoutBuilder( 52 | builder: (_, constraints) { 53 | return ParticleImageSwitcher( 54 | imagePaths: [for (int i = 1; i < 47; i++) getAssetName(i)], 55 | imageIndex: _counter % 46, 56 | size: constraints.biggest, 57 | ); 58 | }, 59 | ), 60 | ), 61 | Positioned( 62 | left: 0, 63 | right: 0, 64 | top: 0, 65 | height: 100, 66 | child: CounterText(counter: _counter), 67 | ), 68 | ], 69 | ), 70 | floatingActionButton: FloatingActionButton( 71 | onPressed: _incrementCounter, 72 | tooltip: 'Increment', 73 | child: const Icon(Icons.add), 74 | ), 75 | ); 76 | } 77 | } 78 | 79 | class TouchPointer { 80 | Offset offset; 81 | } 82 | 83 | class TouchDetector extends StatelessWidget { 84 | const TouchDetector({ 85 | Key key, 86 | @required this.touchPointer, 87 | @required this.child, 88 | }) : super(key: key); 89 | 90 | final TouchPointer touchPointer; 91 | final Widget child; 92 | 93 | @override 94 | Widget build(BuildContext context) { 95 | return GestureDetector( 96 | behavior: HitTestBehavior.opaque, 97 | onPanStart: (details) => touchPointer.offset = details.localPosition, 98 | onPanUpdate: (details) => touchPointer.offset = details.localPosition, 99 | onPanEnd: (details) => touchPointer.offset = null, 100 | child: child, 101 | ); 102 | } 103 | } 104 | 105 | class ParticleImageSwitcher extends StatefulWidget { 106 | const ParticleImageSwitcher({ 107 | Key key, 108 | @required this.imagePaths, 109 | @required this.imageIndex, 110 | @required this.size, 111 | }) : super(key: key); 112 | 113 | final List imagePaths; 114 | final int imageIndex; 115 | final Size size; 116 | 117 | @override 118 | _ParticleImageSwitcherState createState() => _ParticleImageSwitcherState(); 119 | } 120 | 121 | class _ParticleImageSwitcherState extends State 122 | with SingleTickerProviderStateMixin { 123 | final List particles = []; 124 | final List> allPixels = >[]; 125 | final List onDispose = []; 126 | final TouchPointer touchPointer = TouchPointer(); 127 | AnimationController controller; 128 | 129 | @override 130 | void initState() { 131 | super.initState(); 132 | controller = 133 | AnimationController(vsync: this, duration: const Duration(seconds: 1)) 134 | ..repeat(); 135 | for (var i = 0; i < widget.imagePaths.length; i++) { 136 | allPixels.add(loadPixels(widget.imagePaths[i])); 137 | } 138 | showParticles(0); 139 | } 140 | 141 | @override 142 | void didUpdateWidget(covariant ParticleImageSwitcher oldWidget) { 143 | super.didUpdateWidget(oldWidget); 144 | if (oldWidget.imageIndex != widget.imageIndex) { 145 | showParticles(widget.imageIndex); 146 | } 147 | } 148 | 149 | @override 150 | void dispose() { 151 | controller.dispose(); 152 | onDispose.forEach((x) => x()); 153 | super.dispose(); 154 | } 155 | 156 | Future loadPixels(String imagePath) async { 157 | final provider = ExactAssetImage(imagePath); 158 | final imageStream = provider.resolve(ImageConfiguration.empty); 159 | final completer = Completer(); 160 | ImageStreamListener imageStreamListener; 161 | imageStreamListener = ImageStreamListener((frame, _) { 162 | completer.complete(frame.image); 163 | imageStream.removeListener(imageStreamListener); 164 | }); 165 | imageStream.addListener(imageStreamListener); 166 | final image = await completer.future; 167 | final byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba); 168 | onDispose.add(() => image.dispose()); 169 | return Pixels( 170 | byteData: byteData, 171 | width: image.width, 172 | height: image.height, 173 | ); 174 | } 175 | 176 | Future showParticles(int index) async { 177 | final pixels = await allPixels[index]; 178 | final particleIndices = List.generate(particles.length, (i) => i); 179 | final width = widget.size.width; 180 | final height = widget.size.height; 181 | final halfWidth = width / 2; 182 | final halfHeight = height / 2; 183 | final halfImageWidth = pixels.width / 2; 184 | final halfImageHeight = pixels.height / 2; 185 | final tx = halfWidth - halfImageWidth; 186 | final ty = halfHeight - halfImageHeight; 187 | 188 | for (var y = 0; y < pixels.height; y++) { 189 | for (var x = 0; x < pixels.width; x++) { 190 | // Give it small odds that we'll assign a particle to this pixel. 191 | if (randNextD(1) > loadPercentage * countMultiplier) { 192 | continue; 193 | } 194 | 195 | final pixelColor = pixels.getColorAt(x, y); 196 | Particle newParticle; 197 | if (particleIndices.isNotEmpty) { 198 | // Re-use existing particles. 199 | final index = particleIndices.length == 1 200 | ? particleIndices.removeAt(0) 201 | : particleIndices.removeAt(randI(0, particleIndices.length - 1)); 202 | newParticle = particles[index]; 203 | } else { 204 | // Create a new particle. 205 | newParticle = Particle(halfWidth, halfHeight); 206 | particles.add(newParticle); 207 | } 208 | 209 | newParticle.target.x = x + tx; 210 | newParticle.target.y = y + ty; 211 | newParticle.endColor = pixelColor; 212 | } 213 | } 214 | 215 | // Kill off any left over particles that aren't assigned to anything. 216 | if (particleIndices.isNotEmpty) { 217 | for (var i = 0; i < particleIndices.length; i++) { 218 | particles[particleIndices[i]].kill(width, height); 219 | } 220 | } 221 | setState(() {}); 222 | } 223 | 224 | @override 225 | Widget build(BuildContext context) { 226 | return TouchDetector( 227 | touchPointer: touchPointer, 228 | child: CustomPaint( 229 | painter: ParticulesPainter(controller, particles, touchPointer), 230 | ), 231 | ); 232 | } 233 | } 234 | 235 | class Particle { 236 | Particle(this.x, this.y) 237 | : pos = Vector2(x, y), 238 | maxSpeed = randD(0.25, 2), 239 | maxForce = randD(8, 15), 240 | colorBlendRate = randD(0.01, 0.05); 241 | 242 | final double x; 243 | final double y; 244 | final Vector2 pos; 245 | final double maxSpeed; // How fast it can move per frame. 246 | final double maxForce; // Its speed limit. 247 | final double colorBlendRate; 248 | 249 | Vector2 vel = Vector2.zero(); 250 | Vector2 acc = Vector2.zero(); 251 | Vector2 target = Vector2.zero(); 252 | bool isKilled = false; 253 | Color currentColor = const Color(0x00000000); 254 | Color endColor = const Color(0x00000000); 255 | double currentSize = 0; 256 | double distToTarget = 0; 257 | 258 | void move([Offset touchPosition]) { 259 | distToTarget = pos.distanceTo(target); 260 | 261 | double proximityMult; 262 | 263 | // If it's close enough to its target, the slower it'll get 264 | // so that it can settle. 265 | if (distToTarget < closeEnoughTarget) { 266 | proximityMult = distToTarget / closeEnoughTarget; 267 | vel *= 0.9; 268 | } else { 269 | proximityMult = 1; 270 | vel *= 0.95; 271 | } 272 | 273 | // Steer towards its target. 274 | if (distToTarget > 1) { 275 | final steer = target.clone() 276 | ..sub(pos) 277 | ..normalize() 278 | ..scale(maxSpeed * proximityMult * speed); 279 | acc.add(steer); 280 | } 281 | 282 | if (touchPosition != null) { 283 | final touch = Vector2(touchPosition.dx, touchPosition.dy); 284 | final distToTouch = pos.distanceTo(touch); 285 | if (distToTouch < touchSize) { 286 | final push = pos.clone()..sub(touch); 287 | push.normalize(); 288 | push.scale((touchSize - distToTouch) * 0.05); 289 | acc.add(push); 290 | } 291 | } 292 | 293 | vel.add(acc); 294 | vel.limit(maxForce * speed); 295 | pos.add(vel); 296 | acc.scale(0); 297 | } 298 | 299 | void kill(double width, double height) { 300 | if (!isKilled) { 301 | target = generateRandomPos( 302 | width / 2, height / 2, max(width, height), width, height); 303 | endColor = const Color(0x00000000); 304 | isKilled = true; 305 | } 306 | } 307 | } 308 | 309 | extension on Vector2 { 310 | void limit(double max) { 311 | if (length2 > max * max) { 312 | normalize(); 313 | scale(max); 314 | } 315 | } 316 | } 317 | 318 | Vector2 generateRandomPos( 319 | double x, double y, double mag, double width, double height) { 320 | final pos = Vector2(x, y); 321 | final vel = Vector2(randD(0, width), randD(0, height)); 322 | vel.sub(pos); 323 | vel.normalize(); 324 | vel.scale(mag); 325 | pos.add(vel); 326 | 327 | return pos; 328 | } 329 | 330 | class ParticulesPainter extends CustomPainter { 331 | ParticulesPainter( 332 | this.animation, 333 | this.allParticles, 334 | this.touchPointer, 335 | ) : super(repaint: animation); 336 | 337 | final Animation animation; 338 | final List allParticles; 339 | final TouchPointer touchPointer; 340 | 341 | @override 342 | void paint(Canvas canvas, Size size) { 343 | final width = size.width; 344 | final height = size.height; 345 | 346 | for (var i = allParticles.length - 1; i >= 0; i--) { 347 | final particle = allParticles[i]; 348 | particle.move(touchPointer.offset); 349 | 350 | final color = particle.currentColor; 351 | particle.currentColor = Color.lerp( 352 | particle.currentColor, particle.endColor, particle.colorBlendRate); 353 | double targetSize = 2; 354 | if (!particle.isKilled) { 355 | targetSize = map( 356 | min(particle.distToTarget, closeEnoughTarget), 357 | closeEnoughTarget, 358 | 0, 359 | 0, 360 | particleSize, 361 | ); 362 | } 363 | 364 | particle.currentSize = 365 | ui.lerpDouble(particle.currentSize, targetSize, 0.1); 366 | 367 | final center = Offset(particle.pos.x, particle.pos.y); 368 | canvas.drawCircle(center, particle.currentSize, Paint()..color = color); 369 | 370 | if (particle.isKilled) { 371 | if (particle.pos.x < 0 || 372 | particle.pos.x > width || 373 | particle.pos.y < 0 || 374 | particle.pos.y > height) { 375 | allParticles.removeAt(i); 376 | } 377 | } 378 | } 379 | } 380 | 381 | @override 382 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 383 | return false; 384 | } 385 | } 386 | -------------------------------------------------------------------------------- /lib/demo_portrait.dart: -------------------------------------------------------------------------------- 1 | // Credits to https://www.openprocessing.org/sketch/392202/ 2 | 3 | import 'dart:async'; 4 | import 'dart:math'; 5 | import 'dart:ui' as ui; 6 | 7 | import 'package:flutter/material.dart'; 8 | import 'package:flutter/services.dart'; 9 | 10 | import 'common.dart'; 11 | 12 | class DemoPortrait extends StatefulWidget { 13 | const DemoPortrait({ 14 | Key key, 15 | }) : super(key: key); 16 | 17 | @override 18 | _DemoPortraitState createState() => _DemoPortraitState(); 19 | } 20 | 21 | class _DemoPortraitState extends State { 22 | int _counter = 0; 23 | 24 | void _incrementCounter() { 25 | setState(() { 26 | _counter++; 27 | }); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Scaffold( 33 | appBar: AppBar( 34 | title: const Text('Painter'), 35 | ), 36 | body: Stack( 37 | fit: StackFit.expand, 38 | alignment: Alignment.topCenter, 39 | children: [ 40 | Portrait(assetName: 'assets/tim_sneath.jpg', counter: _counter), 41 | Positioned( 42 | left: 0, 43 | right: 0, 44 | top: 0, 45 | height: 100, 46 | child: CounterText(counter: _counter), 47 | ), 48 | ], 49 | ), 50 | floatingActionButton: FloatingActionButton( 51 | onPressed: _incrementCounter, 52 | tooltip: 'Increment', 53 | child: const Icon(Icons.add), 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class Portrait extends StatefulWidget { 60 | const Portrait({ 61 | Key key, 62 | @required this.assetName, 63 | @required this.counter, 64 | }) : super(key: key); 65 | 66 | final String assetName; 67 | final int counter; 68 | 69 | @override 70 | _PortraitState createState() => _PortraitState(); 71 | } 72 | 73 | class _PortraitState extends State { 74 | final Random random = Random(); 75 | ui.Image image; 76 | ByteData byteData; 77 | 78 | @override 79 | void initState() { 80 | super.initState(); 81 | loadPixels(); 82 | } 83 | 84 | @override 85 | void dispose() { 86 | image?.dispose(); 87 | super.dispose(); 88 | } 89 | 90 | Future loadPixels() async { 91 | image?.dispose(); 92 | final provider = ExactAssetImage(widget.assetName); 93 | final imageStream = provider.resolve(ImageConfiguration.empty); 94 | final completer = Completer(); 95 | ImageStreamListener imageStreamListener; 96 | imageStreamListener = ImageStreamListener((frame, _) { 97 | completer.complete(frame.image); 98 | imageStream.removeListener(imageStreamListener); 99 | }); 100 | imageStream.addListener(imageStreamListener); 101 | image = await completer.future; 102 | byteData = await image.toByteData(format: ui.ImageByteFormat.rawRgba); 103 | setState(() {}); 104 | } 105 | 106 | @override 107 | Widget build(BuildContext context) { 108 | final child = image == null 109 | ? const SizedBox.expand() 110 | : Stack( 111 | fit: StackFit.expand, 112 | children: [ 113 | for (var i = 0; i < widget.counter; i++) 114 | PortaitPaint( 115 | imgWidth: image.width, 116 | imgHeight: image.height, 117 | byteData: byteData, 118 | random: random, 119 | counter: i, 120 | ), 121 | ], 122 | ); 123 | return Positioned.fill(child: child); 124 | } 125 | } 126 | 127 | class PortaitPaint extends StatelessWidget { 128 | const PortaitPaint({ 129 | Key key, 130 | @required this.imgWidth, 131 | @required this.imgHeight, 132 | @required this.byteData, 133 | @required this.random, 134 | @required this.counter, 135 | }) : super(key: key); 136 | 137 | final int imgWidth; 138 | final int imgHeight; 139 | final ByteData byteData; 140 | final Random random; 141 | final int counter; 142 | 143 | @override 144 | Widget build(BuildContext context) { 145 | return RepaintBoundary( 146 | child: CustomPaint( 147 | painter: PortraitPainter( 148 | imgWidth, 149 | imgHeight, 150 | byteData, 151 | random, 152 | counter, 153 | ), 154 | ), 155 | ); 156 | } 157 | } 158 | 159 | class PortraitPainter extends CustomPainter { 160 | PortraitPainter( 161 | this.imgWidth, 162 | this.imgHeight, 163 | ByteData byteData, 164 | this.random, 165 | this.counter, 166 | ) : pixels = Pixels(byteData: byteData, width: imgWidth, height: imgHeight); 167 | 168 | final int imgWidth; 169 | final int imgHeight; 170 | final Random random; 171 | final int counter; 172 | final Pixels pixels; 173 | 174 | @override 175 | void paint(Canvas canvas, Size size) { 176 | final width = size.width; 177 | final height = size.height; 178 | final tdx = (width - imgWidth) / 2; 179 | final tdy = (height - imgHeight) / 2; 180 | 181 | void curve( 182 | double x1, 183 | double y1, 184 | double x2, 185 | double y2, 186 | double x3, 187 | double y3, 188 | double x4, 189 | double y4, 190 | double thickness, 191 | Color color, 192 | ) { 193 | final vertices = [ 194 | Offset(x1, y1), 195 | Offset(x2, y2), 196 | Offset(x3, y3), 197 | Offset(x4, y4), 198 | ]; 199 | final path = Path(); 200 | path.moveTo(x2, y2); 201 | for (int i = 1; i + 2 < vertices.length; i++) { 202 | final v = vertices[i]; 203 | final b = List.filled(4, Offset.zero); 204 | b[0] = v; 205 | b[1] = v + (vertices[i + 1] - vertices[i - 1]) / 6; 206 | b[2] = vertices[i + 1] + (v - vertices[i + 2]) / 6; 207 | b[3] = vertices[i + 1]; 208 | path.cubicTo(b[1].dx, b[1].dy, b[2].dx, b[2].dy, b[3].dx, b[3].dy); 209 | } 210 | canvas.drawPath( 211 | path, 212 | Paint() 213 | ..style = PaintingStyle.stroke 214 | ..strokeWidth = thickness 215 | ..color = color, 216 | ); 217 | } 218 | 219 | void paintStroke(double length, Color color, int thickness) { 220 | final stepLength = length / 4; 221 | 222 | // Determines if the stroke is curved. A straight line is 0. 223 | double tangent1 = 0; 224 | double tangent2 = 0; 225 | 226 | final odds = random.nextDouble(); 227 | 228 | if (odds < 0.7) { 229 | tangent1 = random.d(-length, length); 230 | tangent2 = random.d(-length, length); 231 | } 232 | 233 | curve( 234 | tangent1, 235 | -stepLength * 2, 236 | 0, 237 | -stepLength, 238 | 0, 239 | stepLength, 240 | tangent2, 241 | stepLength * 2, 242 | thickness.toDouble(), 243 | color, 244 | ); 245 | } 246 | 247 | for (var y = 0; y < imgHeight; y++) { 248 | for (var x = 0; x < imgWidth; x++) { 249 | final odds = random.d(0, 2000).toInt(); 250 | 251 | if (odds < 1) { 252 | final color = pixels.getColorAt(x, y).withAlpha(100); 253 | final tx = x + tdx; 254 | final ty = y + tdy; 255 | canvas.translate(tx, ty); 256 | 257 | if (counter < 20) { 258 | paintStroke(random.d(150, 250), color, random.d(20, 40).toInt()); 259 | } else if (counter < 50) { 260 | paintStroke(random.d(75, 125), color, random.d(8, 12).toInt()); 261 | } else if (counter < 300) { 262 | paintStroke(random.d(30, 60), color, random.d(1, 4).toInt()); 263 | } else if (counter < 500) { 264 | paintStroke(random.d(5, 20), color, random.d(5, 15).toInt()); 265 | } else { 266 | paintStroke(random.d(1, 10), color, random.d(1, 7).toInt()); 267 | } 268 | 269 | canvas.translate(-tx, -ty); 270 | } 271 | } 272 | } 273 | } 274 | 275 | @override 276 | bool shouldRepaint(PortraitPainter oldDelegate) { 277 | return false; 278 | } 279 | } 280 | -------------------------------------------------------------------------------- /lib/demo_rotating_bubbles.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'common.dart'; 7 | 8 | class DemoRotatingBubbles extends StatefulWidget { 9 | const DemoRotatingBubbles({ 10 | Key key, 11 | }) : super(key: key); 12 | 13 | @override 14 | _DemoRotatingBubblesState createState() => _DemoRotatingBubblesState(); 15 | } 16 | 17 | class _DemoRotatingBubblesState extends State 18 | with TickerProviderStateMixin { 19 | static const colors = [ 20 | Color(0xFFFF2964), 21 | Color(0xFF32FF3A), 22 | Color(0xFF4255FF) 23 | ]; 24 | final math.Random random = math.Random(); 25 | final List radii = []; 26 | 27 | void _incrementCounter() { 28 | setState(() { 29 | radii.add(random.nextInt(25) + 12.5); 30 | }); 31 | } 32 | 33 | @override 34 | Widget build(BuildContext context) { 35 | return Scaffold( 36 | appBar: AppBar(title: const Text('Rotating bubbles')), 37 | backgroundColor: Colors.black, 38 | body: Stack( 39 | children: [ 40 | for (int i = 0; i < radii.length; i++) 41 | Positioned.fill( 42 | child: TweenAnimationBuilder( 43 | tween: Tween(begin: 0, end: radii[i]), 44 | duration: const Duration(milliseconds: 500), 45 | curve: Curves.easeIn, 46 | builder: (_, double radius, __) { 47 | return RotatingBubble( 48 | random: random, 49 | radius: radius, 50 | color: colors[i % colors.length], 51 | ); 52 | }, 53 | ), 54 | ), 55 | CounterText(counter: radii.length), 56 | ], 57 | ), 58 | floatingActionButton: FloatingActionButton( 59 | onPressed: _incrementCounter, 60 | tooltip: 'Increment', 61 | child: const Icon(Icons.add), 62 | ), 63 | ); 64 | } 65 | } 66 | 67 | class RotatingBubble extends StatefulWidget { 68 | const RotatingBubble({ 69 | Key key, 70 | @required this.random, 71 | @required this.radius, 72 | @required this.color, 73 | }) : super(key: key); 74 | 75 | final math.Random random; 76 | final double radius; 77 | final Color color; 78 | 79 | @override 80 | _RotatingBubbleState createState() => _RotatingBubbleState(); 81 | } 82 | 83 | class _RotatingBubbleState extends State 84 | with SingleTickerProviderStateMixin { 85 | AnimationController controller; 86 | Animation angle; 87 | double shift; 88 | 89 | static const double twoPi = math.pi * 2; 90 | 91 | @override 92 | void initState() { 93 | super.initState(); 94 | final random = widget.random; 95 | controller = AnimationController( 96 | vsync: this, 97 | duration: Duration(milliseconds: random.nextInt(1200) + 800), 98 | )..repeat(); 99 | final startAngle = random.nextDouble() * twoPi; 100 | final endAngle = startAngle + (twoPi * (random.nextBool() ? 1 : -1)); 101 | angle = controller.drive(Tween(begin: startAngle, end: endAngle)); 102 | 103 | shift = random.nextDouble() / 10 + 1; 104 | } 105 | 106 | @override 107 | void dispose() { 108 | controller.dispose(); 109 | super.dispose(); 110 | } 111 | 112 | @override 113 | Widget build(BuildContext context) { 114 | return CustomPaint( 115 | painter: RotatingBubblePainter(angle, shift, widget.radius, widget.color), 116 | ); 117 | } 118 | } 119 | 120 | class RotatingBubblePainter extends CustomPainter { 121 | RotatingBubblePainter( 122 | this.angle, 123 | this.shift, 124 | this.radius, 125 | this.color, 126 | ) : super(repaint: angle); 127 | 128 | final Animation angle; 129 | final double shift; 130 | final double radius; 131 | final Color color; 132 | 133 | @override 134 | void paint(Canvas canvas, Size size) { 135 | final appCenter = size.center(Offset.zero); 136 | final bigRadius = size.width / 2.7; 137 | final center = 138 | (Offset.fromDirection(angle.value, bigRadius * shift)) + appCenter; 139 | canvas.drawCircle( 140 | center, 141 | radius, 142 | Paint() 143 | ..blendMode = BlendMode.lighten 144 | ..color = color, 145 | ); 146 | } 147 | 148 | @override 149 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 150 | return false; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/demo_rotating_planets.dart: -------------------------------------------------------------------------------- 1 | // Credits to https://twitter.com/beesandbombs/status/1329468633723101187?s=20 2 | 3 | import 'dart:math' as math; 4 | import 'dart:ui'; 5 | 6 | import 'package:flutter/material.dart'; 7 | 8 | import 'common.dart'; 9 | 10 | class DemoRotatingPlanets extends StatefulWidget { 11 | const DemoRotatingPlanets({ 12 | Key key, 13 | }) : super(key: key); 14 | 15 | @override 16 | _DemoRotatingPlanetsState createState() => _DemoRotatingPlanetsState(); 17 | } 18 | 19 | class _DemoRotatingPlanetsState extends State 20 | with TickerProviderStateMixin { 21 | static const colors = [ 22 | Color(0xFFFF2964), 23 | Color(0xFF32FF3A), 24 | Color(0xFF4255FF) 25 | ]; 26 | final math.Random random = math.Random(); 27 | final List radii = []; 28 | 29 | void _incrementCounter() { 30 | setState(() { 31 | radii.add(random.nextInt(20) + 10.0); 32 | }); 33 | } 34 | 35 | @override 36 | Widget build(BuildContext context) { 37 | return Scaffold( 38 | appBar: AppBar(title: const Text('Rotating planets')), 39 | backgroundColor: Colors.black, 40 | body: Stack( 41 | children: [ 42 | for (int i = 0; i < radii.length; i++) 43 | Positioned.fill( 44 | child: TweenAnimationBuilder( 45 | tween: Tween(begin: 0, end: radii[i]), 46 | duration: const Duration(milliseconds: 500), 47 | curve: Curves.easeIn, 48 | builder: (_, double radius, __) { 49 | return RotatingBubble( 50 | random: random, 51 | radius: radius, 52 | color: colors[i % colors.length], 53 | ); 54 | }, 55 | ), 56 | ), 57 | CounterText(counter: radii.length), 58 | ], 59 | ), 60 | floatingActionButton: FloatingActionButton( 61 | onPressed: _incrementCounter, 62 | tooltip: 'Increment', 63 | child: const Icon(Icons.add), 64 | ), 65 | ); 66 | } 67 | } 68 | 69 | class RotatingBubble extends StatefulWidget { 70 | const RotatingBubble({ 71 | Key key, 72 | @required this.random, 73 | @required this.radius, 74 | @required this.color, 75 | }) : super(key: key); 76 | 77 | final math.Random random; 78 | final double radius; 79 | final Color color; 80 | 81 | @override 82 | _RotatingBubbleState createState() => _RotatingBubbleState(); 83 | } 84 | 85 | class _RotatingBubbleState extends State 86 | with SingleTickerProviderStateMixin { 87 | AnimationController controller; 88 | double dy; 89 | double margin; 90 | double radiusFactor; 91 | 92 | @override 93 | void initState() { 94 | super.initState(); 95 | final random = widget.random; 96 | controller = AnimationController( 97 | vsync: this, 98 | duration: const Duration(milliseconds: 2000), 99 | )..repeat(reverse: true); 100 | dy = map(random.nextDouble(), 0.3, 0.7); 101 | margin = map(random.nextDouble(), 0.1, 0.3); 102 | radiusFactor = random.nextDouble() + 1.5; 103 | } 104 | 105 | @override 106 | void dispose() { 107 | controller.dispose(); 108 | super.dispose(); 109 | } 110 | 111 | @override 112 | Widget build(BuildContext context) { 113 | return CustomPaint( 114 | painter: RotatingBubblePainter( 115 | controller, 116 | dy, 117 | widget.radius, 118 | radiusFactor, 119 | widget.color, 120 | margin, 121 | ), 122 | ); 123 | } 124 | } 125 | 126 | class RotatingBubblePainter extends CustomPainter { 127 | RotatingBubblePainter( 128 | this.animation, 129 | this.dy, 130 | this.radius, 131 | this.radiusFactor, 132 | this.color, 133 | this.margin, 134 | ) : super(repaint: animation); 135 | 136 | final Animation animation; 137 | final double dy; 138 | final double radius; 139 | final double radiusFactor; 140 | final Color color; 141 | final double margin; 142 | 143 | @override 144 | void paint(Canvas canvas, Size size) { 145 | final curve = Curves.easeInOutSine; 146 | 147 | final t = curve.transform(animation.value); 148 | final y = size.height * dy; 149 | final x = lerpDouble(margin, 1 - margin, t) * size.width; 150 | final center = Offset(x, y); 151 | final factor = animation.status == AnimationStatus.forward 152 | ? radiusFactor 153 | : 1 / radiusFactor; 154 | final effectiveRadus = RadiusCurve(radius, radius * factor).transform(t); 155 | final opacity = animation.status == AnimationStatus.forward 156 | ? 1.0 157 | : const RadiusCurve(1, 0.3).transform(t); 158 | canvas.drawCircle( 159 | center, 160 | effectiveRadus, 161 | Paint() 162 | ..blendMode = BlendMode.lighten 163 | ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 10) 164 | ..color = color.withOpacity(opacity), 165 | ); 166 | } 167 | 168 | @override 169 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 170 | return false; 171 | } 172 | } 173 | 174 | double map(double x, double minOut, double maxOut) { 175 | return x * (maxOut - minOut) + minOut; 176 | } 177 | 178 | class RadiusCurve extends Animatable { 179 | const RadiusCurve(this.small, this.big); 180 | 181 | final double small; 182 | final double big; 183 | 184 | @override 185 | double transform(double t) { 186 | if (t <= 0.5) { 187 | return lerpDouble(small, big, t); 188 | } else { 189 | return lerpDouble(big, small, t); 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /lib/demo_triangles.dart: -------------------------------------------------------------------------------- 1 | // Credits to Richard Adams: https://github.com/RichardCubed/flutter_demo_3d 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart' hide Matrix4; 5 | import 'package:flutter/rendering.dart' hide Matrix4; 6 | import 'package:flutter/services.dart' show rootBundle; 7 | import 'package:vector_math/vector_math.dart' hide Colors; 8 | 9 | class DemoTriangles extends StatefulWidget { 10 | const DemoTriangles({Key key}) : super(key: key); 11 | 12 | @override 13 | _DemoTrianglesState createState() => _DemoTrianglesState(); 14 | } 15 | 16 | class _DemoTrianglesState extends State 17 | with TickerProviderStateMixin { 18 | final Random random = Random(); 19 | final List availableIndices = List.generate(768, (index) => index); 20 | List visibleFaces; 21 | int _counter = 0; 22 | 23 | @override 24 | void initState() { 25 | super.initState(); 26 | visibleFaces = List.generate( 27 | 768, 28 | (index) => AnimationController( 29 | vsync: this, 30 | duration: const Duration(milliseconds: 500), 31 | ), 32 | ); 33 | } 34 | 35 | @override 36 | void dispose() { 37 | visibleFaces.forEach((x) { 38 | x.dispose(); 39 | }); 40 | super.dispose(); 41 | } 42 | 43 | void _incrementCounter() { 44 | if (availableIndices.isNotEmpty) { 45 | visibleFaces[availableIndices 46 | .removeAt(random.nextInt(availableIndices.length))] 47 | .forward(); 48 | } 49 | setState(() { 50 | _counter++; 51 | }); 52 | } 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | final counterWidget = Container( 57 | color: Colors.white, 58 | child: Center( 59 | child: Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: [ 62 | const Text( 63 | 'You have pushed the button this many times:', 64 | ), 65 | Text( 66 | '$_counter', 67 | style: Theme.of(context).textTheme.headline4, 68 | ), 69 | ], 70 | ), 71 | ), 72 | ); 73 | 74 | return Scaffold( 75 | appBar: AppBar( 76 | title: const Text('Triangles'), 77 | ), 78 | body: Stack( 79 | children: [ 80 | counterWidget, 81 | ScratchDetector( 82 | fallbackChild: counterWidget, 83 | child: Brain(visibleFaces: visibleFaces, counter: _counter), 84 | ), 85 | ], 86 | ), 87 | floatingActionButton: FloatingActionButton( 88 | onPressed: _incrementCounter, 89 | tooltip: 'Increment', 90 | child: const Icon(Icons.add), 91 | ), 92 | ); 93 | } 94 | } 95 | 96 | class Brain extends StatefulWidget { 97 | const Brain({ 98 | Key key, 99 | @required this.visibleFaces, 100 | @required this.counter, 101 | }) : super(key: key); 102 | 103 | final List> visibleFaces; 104 | final int counter; 105 | 106 | @override 107 | _BrainState createState() => _BrainState(); 108 | } 109 | 110 | class _BrainState extends State with SingleTickerProviderStateMixin { 111 | AnimationController controller; 112 | Animation rotationY; 113 | 114 | @override 115 | void initState() { 116 | controller = AnimationController( 117 | vsync: this, 118 | duration: const Duration(seconds: 4), 119 | )..repeat(); 120 | rotationY = controller.drive(Tween(begin: 0, end: 360)); 121 | super.initState(); 122 | } 123 | 124 | @override 125 | void dispose() { 126 | controller.dispose(); 127 | super.dispose(); 128 | } 129 | 130 | @override 131 | Widget build(BuildContext context) { 132 | return ColoredBox( 133 | color: Colors.black, 134 | child: Stack( 135 | fit: StackFit.expand, 136 | children: [ 137 | Object3D( 138 | path: 'assets/brain.obj', 139 | zoom: 30, 140 | rotationY: rotationY, 141 | visibleFaces: widget.visibleFaces, 142 | ), 143 | Column( 144 | mainAxisAlignment: MainAxisAlignment.center, 145 | children: [ 146 | Text( 147 | 'You have pushed the button this many times:', 148 | style: Theme.of(context).textTheme.bodyText1.copyWith( 149 | foreground: Paint() 150 | ..blendMode = BlendMode.exclusion 151 | ..color = Colors.white), 152 | ), 153 | Text( 154 | '${widget.counter}', 155 | style: Theme.of(context).textTheme.headline4.copyWith( 156 | foreground: Paint() 157 | ..blendMode = BlendMode.exclusion 158 | ..color = Colors.white), 159 | ), 160 | ], 161 | ), 162 | ], 163 | ), 164 | ); 165 | } 166 | } 167 | 168 | class Object3D extends StatefulWidget { 169 | const Object3D({ 170 | Key key, 171 | @required this.path, 172 | @required this.zoom, 173 | @required this.rotationY, 174 | @required this.visibleFaces, 175 | }) : super(key: key); 176 | 177 | final String path; 178 | final double zoom; 179 | final Animation rotationY; 180 | final List> visibleFaces; 181 | 182 | @override 183 | _Object3DState createState() => _Object3DState(); 184 | } 185 | 186 | class _Object3DState extends State { 187 | double angleX = 0; 188 | double angleZ = 0; 189 | double zoom = 0; 190 | 191 | Model model; 192 | 193 | @override 194 | void initState() { 195 | super.initState(); 196 | rootBundle.loadString(widget.path).then((value) { 197 | setState(() { 198 | model = Model(); 199 | model.loadFromString(value); 200 | }); 201 | }); 202 | } 203 | 204 | @override 205 | Widget build(BuildContext context) { 206 | return CustomPaint( 207 | painter: _ObjectPainter( 208 | model, 209 | angleX, 210 | widget.rotationY, 211 | angleZ, 212 | widget.zoom, 213 | widget.visibleFaces, 214 | ), 215 | ); 216 | } 217 | } 218 | 219 | /* 220 | * To render our 3D model we'll need to implement the CustomPainter interface and 221 | * handle drawing to the canvas ourselves. 222 | * https://api.flutter.dev/flutter/rendering/CustomPainter-class.html 223 | */ 224 | class _ObjectPainter extends CustomPainter { 225 | _ObjectPainter( 226 | this.model, 227 | this.angleX, 228 | this.angleY, 229 | this.angleZ, 230 | this._zoom, 231 | this.visibleFaces, 232 | ) : camera = Vector3.zero(), 233 | light = Vector3(0, 0, 100), 234 | vertices = [], 235 | super(repaint: angleY); 236 | 237 | double _viewPortX; 238 | double _viewPortY; 239 | final double _zoom; 240 | 241 | final List vertices; 242 | final Model model; 243 | final Vector3 camera; 244 | final Vector3 light; 245 | 246 | final double angleX; 247 | final Animation angleY; 248 | final double angleZ; 249 | final List> visibleFaces; 250 | 251 | Vector3 _calcVertex(Vector3 vertex) { 252 | final trans = Matrix4.translationValues(_viewPortX, _viewPortY, 1); 253 | trans.scale(_zoom, -_zoom); 254 | trans.rotateX(degreeToRadian(angleX)); 255 | trans.rotateY(degreeToRadian(angleY.value)); 256 | trans.rotateZ(degreeToRadian(angleZ)); 257 | return trans.transform3(vertex); 258 | } 259 | 260 | void _drawFace(Canvas canvas, List face, Color color) { 261 | // Reference the rotated vertices 262 | final v1 = vertices[face[0] - 1]; 263 | final v2 = vertices[face[1] - 1]; 264 | final v3 = vertices[face[2] - 1]; 265 | 266 | // Calculate the surface normal 267 | final normalVector = normalVector3(v1, v2, v3); 268 | 269 | // Calculate the lighting 270 | final normalizedLight = Vector3.copy(light).normalized(); 271 | final jnv = Vector3.copy(normalVector).normalized(); 272 | final normal = scalarMultiplication(jnv, normalizedLight); 273 | final brightness = normal.clamp(0.0, 1.0); 274 | 275 | // Assign a lighting color 276 | final r = (brightness * color.red).toInt(); 277 | final g = (brightness * color.green).toInt(); 278 | final b = (brightness * color.blue).toInt(); 279 | 280 | final paint = Paint(); 281 | paint.color = Color.fromARGB(color.alpha, r, g, b); 282 | paint.style = PaintingStyle.fill; 283 | 284 | // Paint the face 285 | final path = Path(); 286 | path.moveTo(v1.x, v1.y); 287 | path.lineTo(v2.x, v2.y); 288 | path.lineTo(v3.x, v3.y); 289 | path.lineTo(v1.x, v1.y); 290 | path.close(); 291 | canvas.drawPath(path, paint); 292 | } 293 | 294 | @override 295 | void paint(Canvas canvas, Size size) { 296 | // If we've not loaded the model then there's nothing to render 297 | if (model == null) { 298 | return; 299 | } 300 | 301 | _viewPortX ??= size.width / 2; 302 | _viewPortY ??= size.height / 2; 303 | 304 | // Rotate and translate the vertices 305 | vertices.clear(); 306 | for (int i = 0; i < model.vertices.length; i++) { 307 | vertices.add(_calcVertex(Vector3.copy(model.vertices[i]))); 308 | } 309 | 310 | // Sort 311 | final sorted = []; 312 | for (int i = 0; i < model.faces.length; i++) { 313 | final face = model.faces[i]; 314 | sorted.add(Order( 315 | index: i, 316 | order: zIndex( 317 | vertices[face[0] - 1], 318 | vertices[face[1] - 1], 319 | vertices[face[2] - 1], 320 | ), 321 | )); 322 | } 323 | sorted.sort((Order a, Order b) => a.order.compareTo(b.order)); 324 | 325 | // Render 326 | for (int i = 0; i < sorted.length; i++) { 327 | final index = sorted[i].index; 328 | final face = model.faces[index]; 329 | final opacity = visibleFaces[index].value; 330 | final color = model.colors[index].withOpacity(opacity); 331 | _drawFace(canvas, face, color); 332 | } 333 | } 334 | 335 | @override 336 | bool shouldRepaint(_ObjectPainter old) { 337 | return true; 338 | } 339 | } 340 | 341 | class Order { 342 | const Order({ 343 | @required this.index, 344 | @required this.order, 345 | }); 346 | final int index; 347 | final double order; 348 | } 349 | 350 | class Model { 351 | final List vertices = []; 352 | final List> faces = >[]; 353 | final List colors = []; 354 | final Map materials = { 355 | 'frontal': const Color(0xffffba08), 356 | 'occipital': const Color(0xfffaa307), 357 | 'parietal': const Color(0xfff48c06), 358 | 'temporal': const Color(0xffe85d04), 359 | 'cerebellum': const Color(0xffdc2f02), 360 | 'stem': const Color(0xffd00000), 361 | }; 362 | 363 | void loadFromString(String string) { 364 | String material; 365 | final List lines = string.split('\n'); 366 | lines.forEach((line) { 367 | // Parse a vertex 368 | if (line.startsWith('v ')) { 369 | final values = line.substring(2).split(' '); 370 | vertices.add(Vector3( 371 | double.parse(values[0]), 372 | double.parse(values[1]), 373 | double.parse(values[2]), 374 | )); 375 | } 376 | // Parse a material reference 377 | else if (line.startsWith('usemtl ')) { 378 | material = line.substring(7); 379 | } 380 | // Parse a face 381 | else if (line.startsWith('f ')) { 382 | final values = line.substring(2).split(' '); 383 | faces.add(List.from([ 384 | int.parse(values[0].split('/')[0]), 385 | int.parse(values[1].split('/')[0]), 386 | int.parse(values[2].split('/')[0]), 387 | ])); 388 | colors.add(materials[material]); 389 | } 390 | }); 391 | } 392 | } 393 | 394 | Vector3 normalVector3(Vector3 v1, Vector3 v2, Vector3 v3) { 395 | final s1 = Vector3.copy(v2); 396 | s1.sub(v1); 397 | final s3 = Vector3.copy(v2); 398 | s3.sub(v3); 399 | 400 | return Vector3( 401 | (s1.y * s3.z) - (s1.z * s3.y), 402 | (s1.z * s3.x) - (s1.x * s3.z), 403 | (s1.x * s3.y) - (s1.y * s3.x), 404 | ); 405 | } 406 | 407 | double scalarMultiplication(Vector3 v1, Vector3 v2) { 408 | return (v1.x * v2.x) + (v1.y * v2.y) + (v1.z * v2.z); 409 | } 410 | 411 | double degreeToRadian(double degree) { 412 | return degree * (pi / 180.0); 413 | } 414 | 415 | double zIndex(Vector3 p1, Vector3 p2, Vector3 p3) { 416 | return (p1.z + p2.z + p3.z) / 3; 417 | } 418 | 419 | class ScratchDetector extends StatefulWidget { 420 | const ScratchDetector({ 421 | Key key, 422 | @required this.child, 423 | @required this.fallbackChild, 424 | }) : super(key: key); 425 | 426 | final Widget child; 427 | final Widget fallbackChild; 428 | 429 | @override 430 | _ScratchDetectorState createState() => _ScratchDetectorState(); 431 | } 432 | 433 | class _ScratchDetectorState extends State { 434 | final List points = []; 435 | 436 | void addPoint(Offset point) { 437 | setState(() { 438 | points.add(point); 439 | }); 440 | } 441 | 442 | @override 443 | Widget build(BuildContext context) { 444 | final child = points.isEmpty 445 | ? widget.fallbackChild 446 | : ClipPath( 447 | clipper: ScratchClipper(points), 448 | child: widget.child, 449 | ); 450 | 451 | return Positioned.fill( 452 | child: GestureDetector( 453 | onPanStart: (details) => addPoint(details.localPosition), 454 | onPanUpdate: (details) => addPoint(details.localPosition), 455 | child: child, 456 | ), 457 | ); 458 | } 459 | } 460 | 461 | class ScratchClipper extends CustomClipper { 462 | ScratchClipper(this.points); 463 | final List points; 464 | 465 | @override 466 | Path getClip(Size size) { 467 | final path = Path(); 468 | for (var i = 0; i < points.length; i++) { 469 | path.addOval(Rect.fromCircle(center: points[i], radius: 20)); 470 | } 471 | return path; 472 | } 473 | 474 | @override 475 | bool shouldReclip(covariant CustomClipper oldClipper) { 476 | return true; 477 | } 478 | } 479 | -------------------------------------------------------------------------------- /lib/demo_volcano.dart: -------------------------------------------------------------------------------- 1 | // Icons made by Freepik from www.flaticon.com 2 | // https://www.flaticon.com/free-icon/volcano_1497529import 3 | 4 | import 'dart:math' as math; 5 | import 'dart:ui'; 6 | 7 | import 'package:flutter/material.dart'; 8 | 9 | import 'common.dart'; 10 | 11 | class DemoVolcano extends StatefulWidget { 12 | const DemoVolcano({ 13 | Key key, 14 | }) : super(key: key); 15 | 16 | @override 17 | _DemoVolcanoState createState() => _DemoVolcanoState(); 18 | } 19 | 20 | class _DemoVolcanoState extends State 21 | with TickerProviderStateMixin { 22 | int _counter = 0; 23 | 24 | void _incrementCounter() { 25 | setState(() { 26 | _counter++; 27 | }); 28 | } 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return Scaffold( 33 | appBar: AppBar(title: const Text('Volcano')), 34 | backgroundColor: const Color(0xFF2DB2FF), 35 | body: Stack( 36 | fit: StackFit.expand, 37 | children: [ 38 | const Sky(), 39 | for (int i = 0; i < _counter; i++) Eruption(count: i + 1), 40 | Volcano(onTap: _incrementCounter), 41 | const Grass(), 42 | Counter(count: _counter), 43 | ], 44 | ), 45 | ); 46 | } 47 | } 48 | 49 | class LavaParticle { 50 | LavaParticle({ 51 | this.mass, 52 | this.initialVelocity, 53 | this.initialPosition, 54 | }); 55 | 56 | final double mass; 57 | final Offset initialVelocity; 58 | final Offset initialPosition; 59 | Offset velocity; 60 | Offset position; 61 | } 62 | 63 | class Eruption extends StatefulWidget { 64 | const Eruption({ 65 | Key key, 66 | this.count, 67 | }) : super(key: key); 68 | 69 | final int count; 70 | 71 | @override 72 | _EruptionState createState() => _EruptionState(); 73 | } 74 | 75 | class _EruptionState extends State 76 | with SingleTickerProviderStateMixin { 77 | final math.Random random = math.Random(); 78 | final List particles = []; 79 | AnimationController controller; 80 | 81 | @override 82 | void initState() { 83 | super.initState(); 84 | 85 | for (var i = 0; i < widget.count; i++) { 86 | final mass = map(random.nextDouble(), 0, 1, 3, 6); 87 | final velocityX = map(random.nextDouble(), 0, 1, -10, 10); 88 | final velocityY = map(random.nextDouble(), 0, 1, -200, -50); 89 | final positionX = map(random.nextDouble(), 0, 1, 0.4, 0.6); 90 | particles.add( 91 | LavaParticle( 92 | mass: mass, 93 | initialVelocity: Offset(velocityX, velocityY), 94 | initialPosition: Offset(positionX, 0.6), 95 | ), 96 | ); 97 | } 98 | 99 | controller = AnimationController( 100 | vsync: this, 101 | duration: const Duration(seconds: 8), 102 | )..forward(); 103 | } 104 | 105 | @override 106 | void dispose() { 107 | controller.dispose(); 108 | super.dispose(); 109 | } 110 | 111 | @override 112 | Widget build(BuildContext context) { 113 | return Positioned.fill( 114 | child: CustomPaint( 115 | painter: EruptionPainter( 116 | controller, 117 | particles, 118 | ), 119 | ), 120 | ); 121 | } 122 | } 123 | 124 | class EruptionPainter extends CustomPainter { 125 | EruptionPainter(this.animation, this.particles) 126 | : random = math.Random(), 127 | super(repaint: animation); 128 | 129 | final Animation animation; 130 | final List particles; 131 | final math.Random random; 132 | double dt = 0.1; 133 | static final colorTween = ColorTween( 134 | begin: const Color(0xFFDB4B38), 135 | end: const Color(0xFF732F13), 136 | ); 137 | 138 | @override 139 | void paint(Canvas canvas, Size size) { 140 | final opacity = 1 - animation.value; 141 | final count = particles.length; 142 | final color = colorTween.transform(animation.value); 143 | 144 | for (int i = 0; i < count; i++) { 145 | final particle = particles[i]; 146 | final radius = particle.mass * 3; 147 | 148 | if (particle.position == null || opacity == 1) { 149 | final position = particle.initialPosition; 150 | particle.position = Offset( 151 | position.dx * size.width, 152 | position.dy * size.height, 153 | ); 154 | particle.velocity = Offset( 155 | particle.initialVelocity.dx, 156 | particle.initialVelocity.dy, 157 | ); 158 | } 159 | 160 | final Offset force = Offset(0, particle.mass * 9.81); 161 | final Offset acceleration = force / particle.mass; 162 | particle.velocity += acceleration * dt; 163 | particle.position += particle.velocity * dt; 164 | canvas.drawCircle( 165 | particle.position, 166 | radius, 167 | Paint()..color = color, 168 | ); 169 | } 170 | } 171 | 172 | @override 173 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 174 | return false; 175 | } 176 | } 177 | 178 | class Counter extends StatelessWidget { 179 | const Counter({ 180 | Key key, 181 | this.count, 182 | }) : super(key: key); 183 | 184 | final int count; 185 | 186 | @override 187 | Widget build(BuildContext context) { 188 | return Positioned( 189 | top: 50, 190 | left: 0, 191 | right: 0, 192 | child: Text( 193 | '$count', 194 | textAlign: TextAlign.center, 195 | style: Theme.of(context) 196 | .textTheme 197 | .headline1 198 | .copyWith(foreground: Paint()..color = Colors.white), 199 | ), 200 | ); 201 | } 202 | } 203 | 204 | class Sky extends StatefulWidget { 205 | const Sky({ 206 | Key key, 207 | }) : super(key: key); 208 | 209 | @override 210 | _SkyState createState() => _SkyState(); 211 | } 212 | 213 | class _SkyState extends State with TickerProviderStateMixin { 214 | AnimationController skyController; 215 | AnimationController sunController; 216 | Animation sunAnimation; 217 | Animation skyAnimation; 218 | 219 | @override 220 | void initState() { 221 | super.initState(); 222 | skyController = AnimationController( 223 | vsync: this, 224 | duration: const Duration(seconds: 40), 225 | )..repeat(reverse: true); 226 | sunController = AnimationController( 227 | vsync: this, 228 | duration: const Duration(seconds: 80), 229 | )..repeat(); 230 | skyAnimation = skyController.drive(ColorTween( 231 | begin: Colors.blue, 232 | end: Colors.blueGrey.shade900, 233 | )); 234 | sunAnimation = sunController.drive(Tween( 235 | begin: 3 * math.pi / 2, 236 | end: 7 * math.pi / 2, 237 | )); 238 | } 239 | 240 | @override 241 | void dispose() { 242 | skyController.dispose(); 243 | sunController.dispose(); 244 | super.dispose(); 245 | } 246 | 247 | @override 248 | Widget build(BuildContext context) { 249 | return Positioned.fill( 250 | child: CustomPaint( 251 | painter: SkyPainter(skyAnimation, sunAnimation), 252 | ), 253 | ); 254 | } 255 | } 256 | 257 | class SkyPainter extends CustomPainter { 258 | const SkyPainter(this.skyAnimation, this.sunAnimation) 259 | : super(repaint: skyAnimation); 260 | 261 | final Animation skyAnimation; 262 | final Animation sunAnimation; 263 | 264 | static const sunColor = Color(0xFFFFC471); 265 | static const moonColor = Color(0xFFFFFFFF); 266 | static const sunRadius = 100.0; 267 | static const moonRadius = 75.0; 268 | 269 | @override 270 | void paint(Canvas canvas, Size size) { 271 | // Sky color. 272 | canvas.drawColor(skyAnimation.value, BlendMode.src); 273 | 274 | final halfWidth = size.width / 2; 275 | final distance = 2 / 4 * size.height; 276 | 277 | // Sun. 278 | final sunCenter = Offset.fromDirection( 279 | sunAnimation.value, 280 | distance, 281 | ) + 282 | Offset(halfWidth, size.height); 283 | canvas.drawCircle( 284 | sunCenter, 285 | sunRadius, 286 | Paint() 287 | ..color = sunColor 288 | ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 50), 289 | ); 290 | 291 | // Moon. 292 | final moonCenter = Offset.fromDirection( 293 | sunAnimation.value + math.pi, 294 | distance, 295 | ) + 296 | Offset(halfWidth, size.height); 297 | 298 | canvas.drawCircle( 299 | moonCenter, 300 | moonRadius, 301 | Paint() 302 | ..color = moonColor 303 | ..maskFilter = const MaskFilter.blur(BlurStyle.solid, 50), 304 | ); 305 | } 306 | 307 | @override 308 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 309 | return false; 310 | } 311 | } 312 | 313 | class Volcano extends StatelessWidget { 314 | const Volcano({ 315 | Key key, 316 | @required this.onTap, 317 | }) : super(key: key); 318 | 319 | final VoidCallback onTap; 320 | 321 | @override 322 | Widget build(BuildContext context) { 323 | return Positioned( 324 | bottom: 0, 325 | left: 0, 326 | right: 0, 327 | child: GestureDetector( 328 | onTap: onTap, 329 | child: Image.asset('assets/volcano.png'), 330 | ), 331 | ); 332 | } 333 | } 334 | 335 | class Grass extends StatelessWidget { 336 | const Grass({ 337 | Key key, 338 | }) : super(key: key); 339 | 340 | @override 341 | Widget build(BuildContext context) { 342 | return const Positioned( 343 | bottom: 0, 344 | left: 0, 345 | right: 0, 346 | height: 50, 347 | child: CustomPaint(painter: GrassPainter()), 348 | ); 349 | } 350 | } 351 | 352 | class GrassPainter extends CustomPainter { 353 | const GrassPainter(); 354 | 355 | @override 356 | void paint(Canvas canvas, Size size) { 357 | final paint = Paint()..color = const Color(0xFF00B673); 358 | final width = size.width; 359 | final height = size.height; 360 | final halfHeight = height / 2; 361 | 362 | canvas.drawRect( 363 | Offset(0, halfHeight) & Size(size.width, halfHeight), 364 | paint, 365 | ); 366 | canvas.drawCircle(Offset(0, height), 50, paint); 367 | canvas.drawCircle(Offset(width * 0.3, halfHeight), 40, paint); 368 | canvas.drawCircle(Offset(width * 0.4, halfHeight), 20, paint); 369 | canvas.drawCircle(Offset(width * 0.7, height), 70, paint); 370 | } 371 | 372 | @override 373 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 374 | return false; 375 | } 376 | } 377 | -------------------------------------------------------------------------------- /lib/demo_wave.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | import 'common.dart'; 6 | 7 | class DemoWave extends StatefulWidget { 8 | const DemoWave({Key key}) : super(key: key); 9 | 10 | @override 11 | _DemoWaveState createState() => _DemoWaveState(); 12 | } 13 | 14 | class _DemoWaveState extends State { 15 | int _counter = 0; 16 | 17 | void _incrementCounter() { 18 | setState(() { 19 | _counter++; 20 | }); 21 | } 22 | 23 | @override 24 | Widget build(BuildContext context) { 25 | return Scaffold( 26 | appBar: AppBar( 27 | title: const Text('Wave'), 28 | ), 29 | body: Stack( 30 | children: [ 31 | Positioned.fill( 32 | child: TweenAnimationBuilder( 33 | tween: Tween(begin: 0, end: _counter.toDouble()), 34 | duration: const Duration(milliseconds: 1000), 35 | curve: Curves.bounceOut, 36 | builder: (_, double ratio, __) { 37 | return FractionallySizedBox( 38 | heightFactor: (ratio / 100).clamp(0, 100).toDouble(), 39 | alignment: Alignment.bottomCenter, 40 | child: const Wave( 41 | child: DifferenceMask(), 42 | ), 43 | ); 44 | }, 45 | ), 46 | ), 47 | CounterText(counter: _counter), 48 | ], 49 | ), 50 | floatingActionButton: FloatingActionButton( 51 | onPressed: _incrementCounter, 52 | tooltip: 'Increment', 53 | child: const Icon(Icons.add), 54 | ), 55 | ); 56 | } 57 | } 58 | 59 | class Wave extends StatefulWidget { 60 | const Wave({ 61 | Key key, 62 | @required this.child, 63 | }) : super(key: key); 64 | 65 | final Widget child; 66 | 67 | @override 68 | _WaveState createState() => _WaveState(); 69 | } 70 | 71 | class _WaveState extends State with SingleTickerProviderStateMixin { 72 | AnimationController controller; 73 | Animation> waves; 74 | 75 | @override 76 | void initState() { 77 | super.initState(); 78 | controller = AnimationController( 79 | vsync: this, 80 | duration: const Duration(seconds: 5), 81 | )..repeat(reverse: false); 82 | waves = controller.drive(WaveTween(100, 20)); 83 | } 84 | 85 | @override 86 | void dispose() { 87 | controller.dispose(); 88 | super.dispose(); 89 | } 90 | 91 | @override 92 | Widget build(BuildContext context) { 93 | return ClipPath( 94 | clipper: WaveClipper(waves), 95 | child: widget.child, 96 | ); 97 | } 98 | } 99 | 100 | class WaveTween extends Animatable> { 101 | WaveTween(this.count, this.height); 102 | 103 | final int count; 104 | final double height; 105 | static const twoPi = math.pi * 2; 106 | static const waveCount = 3; 107 | 108 | @override 109 | List transform(double t) { 110 | return List.generate( 111 | count, 112 | (i) { 113 | final ratio = i / (count - 1); 114 | final amplitude = 1 - (0.5 - ratio).abs() * 2; 115 | return Offset( 116 | ratio, 117 | amplitude * height * math.sin(waveCount * (ratio + t) * twoPi) + 118 | height * amplitude, 119 | ); 120 | }, 121 | growable: false, 122 | ); 123 | } 124 | } 125 | 126 | class WaveClipper extends CustomClipper { 127 | WaveClipper(this.waves) : super(reclip: waves); 128 | 129 | Animation> waves; 130 | 131 | @override 132 | Path getClip(Size size) { 133 | final width = size.width; 134 | final points = waves.value.map((o) => Offset(o.dx * width, o.dy)).toList(); 135 | return Path() 136 | ..addPolygon(points, false) 137 | ..lineTo(size.width, size.height) 138 | ..lineTo(0, size.height) 139 | ..close(); 140 | } 141 | 142 | @override 143 | bool shouldReclip(WaveClipper oldClipper) => false; 144 | } 145 | 146 | class DifferenceMask extends StatelessWidget { 147 | const DifferenceMask({ 148 | Key key, 149 | }) : super(key: key); 150 | 151 | @override 152 | Widget build(BuildContext context) { 153 | return const CustomPaint( 154 | painter: DifferencePainter(), 155 | ); 156 | } 157 | } 158 | 159 | class DifferencePainter extends CustomPainter { 160 | const DifferencePainter() : super(); 161 | 162 | @override 163 | void paint(Canvas canvas, Size size) { 164 | canvas.drawRect(Offset.zero & size, Paint()..color = Colors.black); 165 | } 166 | 167 | @override 168 | bool shouldRepaint(DifferencePainter oldDelegate) => false; 169 | } 170 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | import 'demo_blocks.dart'; 4 | import 'demo_circle_wave.dart'; 5 | import 'demo_creature.dart'; 6 | import 'demo_disks.dart'; 7 | import 'demo_image_bubble.dart'; 8 | import 'demo_mattis.dart'; 9 | import 'demo_particles.dart'; 10 | import 'demo_portrait.dart'; 11 | import 'demo_rotating_bubbles.dart'; 12 | import 'demo_rotating_planets.dart'; 13 | import 'demo_triangles.dart'; 14 | import 'demo_volcano.dart'; 15 | import 'demo_wave.dart'; 16 | 17 | void main() { 18 | runApp(const MyApp()); 19 | } 20 | 21 | class MyApp extends StatelessWidget { 22 | const MyApp({ 23 | Key key, 24 | }) : super(key: key); 25 | 26 | @override 27 | Widget build(BuildContext context) { 28 | return MaterialApp( 29 | title: 'Flutter Demo', 30 | theme: ThemeData(primarySwatch: Colors.blue), 31 | home: const Demos(), 32 | ); 33 | } 34 | } 35 | 36 | class Demos extends StatelessWidget { 37 | const Demos({ 38 | Key key, 39 | }) : super(key: key); 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return Scaffold( 44 | appBar: AppBar( 45 | title: const Text('Counter Apps'), 46 | ), 47 | body: ListView( 48 | children: [ 49 | Tile(label: 'Blocks', builder: () => const DemoBlocks()), 50 | Tile(label: 'Disks', builder: () => const DemoDisks()), 51 | Tile(label: 'Wave', builder: () => const DemoWave()), 52 | Tile(label: 'Circle wave', builder: () => const DemoCircleWave()), 53 | Tile(label: 'Bubbles', builder: () => const DemoRotatingBubbles()), 54 | Tile(label: 'Planets', builder: () => const DemoRotatingPlanets()), 55 | Tile(label: 'Creature', builder: () => const DemoCreature()), 56 | Tile(label: 'Volcano', builder: () => const DemoVolcano()), 57 | Tile(label: 'ImageBubble', builder: () => const DemoImageBubble()), 58 | Tile(label: 'Triangles', builder: () => const DemoTriangles()), 59 | Tile(label: 'Mattis', builder: () => const DemoMattis()), 60 | Tile(label: 'Portrait', builder: () => const DemoPortrait()), 61 | Tile(label: 'Particles', builder: () => const DemoParticles()), 62 | ], 63 | ), 64 | ); 65 | } 66 | } 67 | 68 | class Tile extends StatelessWidget { 69 | const Tile({ 70 | Key key, 71 | this.label, 72 | this.builder, 73 | }) : super(key: key); 74 | 75 | final String label; 76 | final Widget Function() builder; 77 | 78 | @override 79 | Widget build(BuildContext context) { 80 | return Padding( 81 | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), 82 | child: OutlinedButton( 83 | onPressed: () { 84 | Navigator.push( 85 | context, 86 | MaterialPageRoute(builder: (_) => builder()), 87 | ); 88 | }, 89 | child: Text(label), 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | _fe_analyzer_shared: 5 | dependency: transitive 6 | description: 7 | name: _fe_analyzer_shared 8 | url: "https://pub.dartlang.org" 9 | source: hosted 10 | version: "12.0.0" 11 | analyzer: 12 | dependency: transitive 13 | description: 14 | name: analyzer 15 | url: "https://pub.dartlang.org" 16 | source: hosted 17 | version: "0.40.6" 18 | archive: 19 | dependency: transitive 20 | description: 21 | name: archive 22 | url: "https://pub.dartlang.org" 23 | source: hosted 24 | version: "2.0.13" 25 | args: 26 | dependency: transitive 27 | description: 28 | name: args 29 | url: "https://pub.dartlang.org" 30 | source: hosted 31 | version: "1.6.0" 32 | async: 33 | dependency: transitive 34 | description: 35 | name: async 36 | url: "https://pub.dartlang.org" 37 | source: hosted 38 | version: "2.5.0-nullsafety.3" 39 | boolean_selector: 40 | dependency: transitive 41 | description: 42 | name: boolean_selector 43 | url: "https://pub.dartlang.org" 44 | source: hosted 45 | version: "2.1.0-nullsafety.3" 46 | characters: 47 | dependency: transitive 48 | description: 49 | name: characters 50 | url: "https://pub.dartlang.org" 51 | source: hosted 52 | version: "1.1.0-nullsafety.5" 53 | charcode: 54 | dependency: transitive 55 | description: 56 | name: charcode 57 | url: "https://pub.dartlang.org" 58 | source: hosted 59 | version: "1.2.0-nullsafety.3" 60 | cli_util: 61 | dependency: transitive 62 | description: 63 | name: cli_util 64 | url: "https://pub.dartlang.org" 65 | source: hosted 66 | version: "0.2.0" 67 | clock: 68 | dependency: transitive 69 | description: 70 | name: clock 71 | url: "https://pub.dartlang.org" 72 | source: hosted 73 | version: "1.1.0-nullsafety.3" 74 | collection: 75 | dependency: transitive 76 | description: 77 | name: collection 78 | url: "https://pub.dartlang.org" 79 | source: hosted 80 | version: "1.15.0-nullsafety.5" 81 | convert: 82 | dependency: transitive 83 | description: 84 | name: convert 85 | url: "https://pub.dartlang.org" 86 | source: hosted 87 | version: "2.1.1" 88 | coverage: 89 | dependency: transitive 90 | description: 91 | name: coverage 92 | url: "https://pub.dartlang.org" 93 | source: hosted 94 | version: "0.14.2" 95 | crypto: 96 | dependency: transitive 97 | description: 98 | name: crypto 99 | url: "https://pub.dartlang.org" 100 | source: hosted 101 | version: "2.1.5" 102 | cupertino_icons: 103 | dependency: "direct main" 104 | description: 105 | name: cupertino_icons 106 | url: "https://pub.dartlang.org" 107 | source: hosted 108 | version: "1.0.2" 109 | fake_async: 110 | dependency: transitive 111 | description: 112 | name: fake_async 113 | url: "https://pub.dartlang.org" 114 | source: hosted 115 | version: "1.2.0-nullsafety.3" 116 | file: 117 | dependency: transitive 118 | description: 119 | name: file 120 | url: "https://pub.dartlang.org" 121 | source: hosted 122 | version: "6.0.0-nullsafety.4" 123 | flutter: 124 | dependency: "direct main" 125 | description: flutter 126 | source: sdk 127 | version: "0.0.0" 128 | flutter_driver: 129 | dependency: transitive 130 | description: flutter 131 | source: sdk 132 | version: "0.0.0" 133 | flutter_test: 134 | dependency: "direct dev" 135 | description: flutter 136 | source: sdk 137 | version: "0.0.0" 138 | fuchsia_remote_debug_protocol: 139 | dependency: transitive 140 | description: flutter 141 | source: sdk 142 | version: "0.0.0" 143 | glob: 144 | dependency: transitive 145 | description: 146 | name: glob 147 | url: "https://pub.dartlang.org" 148 | source: hosted 149 | version: "1.2.0" 150 | integration_test: 151 | dependency: "direct dev" 152 | description: flutter 153 | source: sdk 154 | version: "0.9.2+2" 155 | io: 156 | dependency: transitive 157 | description: 158 | name: io 159 | url: "https://pub.dartlang.org" 160 | source: hosted 161 | version: "0.3.4" 162 | js: 163 | dependency: transitive 164 | description: 165 | name: js 166 | url: "https://pub.dartlang.org" 167 | source: hosted 168 | version: "0.6.3-nullsafety.3" 169 | json_rpc_2: 170 | dependency: transitive 171 | description: 172 | name: json_rpc_2 173 | url: "https://pub.dartlang.org" 174 | source: hosted 175 | version: "2.2.2" 176 | logging: 177 | dependency: transitive 178 | description: 179 | name: logging 180 | url: "https://pub.dartlang.org" 181 | source: hosted 182 | version: "0.11.4" 183 | matcher: 184 | dependency: transitive 185 | description: 186 | name: matcher 187 | url: "https://pub.dartlang.org" 188 | source: hosted 189 | version: "0.12.10-nullsafety.3" 190 | meta: 191 | dependency: transitive 192 | description: 193 | name: meta 194 | url: "https://pub.dartlang.org" 195 | source: hosted 196 | version: "1.3.0-nullsafety.6" 197 | node_interop: 198 | dependency: transitive 199 | description: 200 | name: node_interop 201 | url: "https://pub.dartlang.org" 202 | source: hosted 203 | version: "1.2.1" 204 | node_io: 205 | dependency: transitive 206 | description: 207 | name: node_io 208 | url: "https://pub.dartlang.org" 209 | source: hosted 210 | version: "1.1.1" 211 | package_config: 212 | dependency: transitive 213 | description: 214 | name: package_config 215 | url: "https://pub.dartlang.org" 216 | source: hosted 217 | version: "1.9.3" 218 | path: 219 | dependency: transitive 220 | description: 221 | name: path 222 | url: "https://pub.dartlang.org" 223 | source: hosted 224 | version: "1.8.0-nullsafety.3" 225 | pedantic: 226 | dependency: transitive 227 | description: 228 | name: pedantic 229 | url: "https://pub.dartlang.org" 230 | source: hosted 231 | version: "1.10.0-nullsafety.3" 232 | platform: 233 | dependency: transitive 234 | description: 235 | name: platform 236 | url: "https://pub.dartlang.org" 237 | source: hosted 238 | version: "3.0.0-nullsafety.4" 239 | pool: 240 | dependency: transitive 241 | description: 242 | name: pool 243 | url: "https://pub.dartlang.org" 244 | source: hosted 245 | version: "1.5.0-nullsafety.3" 246 | process: 247 | dependency: transitive 248 | description: 249 | name: process 250 | url: "https://pub.dartlang.org" 251 | source: hosted 252 | version: "4.0.0-nullsafety.4" 253 | pub_semver: 254 | dependency: transitive 255 | description: 256 | name: pub_semver 257 | url: "https://pub.dartlang.org" 258 | source: hosted 259 | version: "1.4.4" 260 | sky_engine: 261 | dependency: transitive 262 | description: flutter 263 | source: sdk 264 | version: "0.0.99" 265 | source_map_stack_trace: 266 | dependency: transitive 267 | description: 268 | name: source_map_stack_trace 269 | url: "https://pub.dartlang.org" 270 | source: hosted 271 | version: "2.1.0-nullsafety.4" 272 | source_maps: 273 | dependency: transitive 274 | description: 275 | name: source_maps 276 | url: "https://pub.dartlang.org" 277 | source: hosted 278 | version: "0.10.10-nullsafety.3" 279 | source_span: 280 | dependency: transitive 281 | description: 282 | name: source_span 283 | url: "https://pub.dartlang.org" 284 | source: hosted 285 | version: "1.8.0-nullsafety.4" 286 | stack_trace: 287 | dependency: transitive 288 | description: 289 | name: stack_trace 290 | url: "https://pub.dartlang.org" 291 | source: hosted 292 | version: "1.10.0-nullsafety.6" 293 | stream_channel: 294 | dependency: transitive 295 | description: 296 | name: stream_channel 297 | url: "https://pub.dartlang.org" 298 | source: hosted 299 | version: "2.1.0-nullsafety.3" 300 | string_scanner: 301 | dependency: transitive 302 | description: 303 | name: string_scanner 304 | url: "https://pub.dartlang.org" 305 | source: hosted 306 | version: "1.1.0-nullsafety.3" 307 | sync_http: 308 | dependency: transitive 309 | description: 310 | name: sync_http 311 | url: "https://pub.dartlang.org" 312 | source: hosted 313 | version: "0.2.0" 314 | term_glyph: 315 | dependency: transitive 316 | description: 317 | name: term_glyph 318 | url: "https://pub.dartlang.org" 319 | source: hosted 320 | version: "1.2.0-nullsafety.3" 321 | test_api: 322 | dependency: transitive 323 | description: 324 | name: test_api 325 | url: "https://pub.dartlang.org" 326 | source: hosted 327 | version: "0.2.19-nullsafety.6" 328 | test_core: 329 | dependency: transitive 330 | description: 331 | name: test_core 332 | url: "https://pub.dartlang.org" 333 | source: hosted 334 | version: "0.3.12-nullsafety.9" 335 | typed_data: 336 | dependency: transitive 337 | description: 338 | name: typed_data 339 | url: "https://pub.dartlang.org" 340 | source: hosted 341 | version: "1.3.0-nullsafety.5" 342 | vector_math: 343 | dependency: transitive 344 | description: 345 | name: vector_math 346 | url: "https://pub.dartlang.org" 347 | source: hosted 348 | version: "2.1.0-nullsafety.5" 349 | vm_service: 350 | dependency: transitive 351 | description: 352 | name: vm_service 353 | url: "https://pub.dartlang.org" 354 | source: hosted 355 | version: "5.5.0" 356 | watcher: 357 | dependency: transitive 358 | description: 359 | name: watcher 360 | url: "https://pub.dartlang.org" 361 | source: hosted 362 | version: "0.9.7+15" 363 | web_socket_channel: 364 | dependency: transitive 365 | description: 366 | name: web_socket_channel 367 | url: "https://pub.dartlang.org" 368 | source: hosted 369 | version: "1.1.0" 370 | webdriver: 371 | dependency: transitive 372 | description: 373 | name: webdriver 374 | url: "https://pub.dartlang.org" 375 | source: hosted 376 | version: "2.1.2" 377 | yaml: 378 | dependency: transitive 379 | description: 380 | name: yaml 381 | url: "https://pub.dartlang.org" 382 | source: hosted 383 | version: "2.2.1" 384 | sdks: 385 | dart: ">=2.12.0-0.0 <3.0.0" 386 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: flutter_counter_challenge_2020 2 | description: A set of counter apps made for the Flutter Counter Challenge 2020. 3 | publish_to: "none" 4 | version: 1.0.0+1 5 | 6 | environment: 7 | sdk: ">=2.7.0 <3.0.0" 8 | 9 | dependencies: 10 | flutter: 11 | sdk: flutter 12 | 13 | cupertino_icons: ^1.0.1 14 | 15 | dev_dependencies: 16 | flutter_test: 17 | sdk: flutter 18 | integration_test: 19 | sdk: flutter 20 | 21 | flutter: 22 | uses-material-design: true 23 | 24 | assets: 25 | - assets/ 26 | - assets/people/ 27 | --------------------------------------------------------------------------------