├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── jideguru │ │ │ │ └── animation_playground │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable-v21 │ │ │ └── launch_background.xml │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── values-night │ │ │ └── styles.xml │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── assets └── images │ ├── cat.jpeg │ └── dvd-logo.png ├── gifs ├── 3d-card.gif ├── bouncing-ball.gif ├── bouncing-dvd.gif ├── clock_loader.gif ├── fading-grid.gif ├── grid-magn.gif ├── metaball-fab.gif ├── particle-emitter.gif ├── pixel-painting.gif ├── pyramid-shader.gif ├── rain.gif └── simple-particle-system.gif ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── WorkspaceSettings.xcsettings │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib ├── animations │ ├── card │ │ ├── barrel.dart │ │ └── three_dimensional_card.dart │ ├── coding_train_challenges │ │ ├── barrel.dart │ │ ├── bouncing_ball.dart │ │ ├── bouncing_dvd.dart │ │ ├── painting_with_pixels.dart │ │ ├── particle_system_with_emitters.dart │ │ ├── rain.dart │ │ └── simple_particle_system.dart │ ├── grid_magnification │ │ ├── barrel.dart │ │ ├── fading_grid.dart │ │ └── grid_magnification.dart │ ├── loaders │ │ ├── barrel.dart │ │ └── clock_loader.dart │ ├── morphing_segmented_bar.dart │ └── segmented_bar_flip.dart ├── animations_list.dart ├── buildcontext_extension.dart ├── list_page.dart ├── main.dart ├── random_extension.dart ├── routes.dart ├── shaders │ ├── barrel.dart │ ├── fractal_pyramid.dart │ └── metaball_fab.dart └── utils.dart ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ ├── Flutter-Release.xcconfig │ └── GeneratedPluginRegistrant.swift ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ └── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── app_icon_1024.png │ │ ├── app_icon_128.png │ │ ├── app_icon_16.png │ │ ├── app_icon_256.png │ │ ├── app_icon_32.png │ │ ├── app_icon_512.png │ │ └── app_icon_64.png │ ├── Base.lproj │ └── MainMenu.xib │ ├── Configs │ ├── AppInfo.xcconfig │ ├── Debug.xcconfig │ ├── Release.xcconfig │ └── Warnings.xcconfig │ ├── DebugProfile.entitlements │ ├── Info.plist │ ├── MainFlutterWindow.swift │ └── Release.entitlements ├── pubspec.lock ├── pubspec.yaml ├── shaders ├── metaball_shader.frag └── pyramid.glsl ├── test └── widget_test.dart └── web ├── favicon.png ├── icons ├── Icon-192.png ├── Icon-512.png ├── Icon-maskable-192.png └── Icon-maskable-512.png ├── index.html └── manifest.json /.gitignore: -------------------------------------------------------------------------------- 1 | # Miscellaneous 2 | *.class 3 | *.log 4 | *.pyc 5 | *.swp 6 | .DS_Store 7 | .atom/ 8 | .build/ 9 | .buildlog/ 10 | .history 11 | .svn/ 12 | .swiftpm/ 13 | migrate_working_dir/ 14 | 15 | # IntelliJ related 16 | *.iml 17 | *.ipr 18 | *.iws 19 | .idea/ 20 | 21 | # The .vscode folder contains launch configuration and tasks you configure in 22 | # VS Code which you may wish to be included in version control, so this line 23 | # is commented out by default. 24 | #.vscode/ 25 | 26 | # Flutter/Dart/Pub related 27 | **/doc/api/ 28 | **/ios/Flutter/.last_build_id 29 | .dart_tool/ 30 | .flutter-plugins 31 | .flutter-plugins-dependencies 32 | .packages 33 | .pub-cache/ 34 | .pub/ 35 | /build/ 36 | 37 | # Symbolication related 38 | app.*.symbols 39 | 40 | # Obfuscation related 41 | app.*.map.json 42 | 43 | # Android Studio will place build artifacts here 44 | /android/app/debug 45 | /android/app/profile 46 | /android/app/release 47 | 48 | 49 | .firebaserc 50 | firebase.json -------------------------------------------------------------------------------- /.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: "5dcb86f68f239346676ceb1ed1ea385bd215fba1" 8 | channel: "stable" 9 | 10 | project_type: app 11 | 12 | # Tracks metadata for the flutter migrate command 13 | migration: 14 | platforms: 15 | - platform: root 16 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 17 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 18 | - platform: web 19 | create_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 20 | base_revision: 5dcb86f68f239346676ceb1ed1ea385bd215fba1 21 | 22 | # User provided section 23 | 24 | # List of Local paths (relative to this file) that should be 25 | # ignored by the migrate tool. 26 | # 27 | # Files that are not part of the templates will be ignored by default. 28 | unmanaged_files: 29 | - 'lib/main.dart' 30 | - 'ios/Runner.xcodeproj/project.pbxproj' 31 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [2022] [JideGuru] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Animations Playground ![GitHub stars](https://img.shields.io/github/stars/JideGuru/animation_playground?style=social) 2 | 3 | ![platform](https://img.shields.io/badge/platform-Flutter-blue) 4 | [![GitHub license](https://img.shields.io/badge/License-Apache2.0-blue.svg)](LICENSE) 5 | ![Repository size](https://img.shields.io/github/repo-size/JideGuru/animation_playground) 6 | ![GitHub follow](https://img.shields.io/github/followers/JideGuru?style=social) 7 | 8 | This repo contains some animations that i implemented using Flutter in my free time. 9 | 10 | You can see the animations on my [Twitter Profile](https://twitter.com/iamjideguru) 11 | 12 | Try out the demos here [animation-playground.jideguru.dev](https://animation-playground.jideguru.dev/) 13 | 14 | Feel free to star, fork and contribute. 15 | 16 | ## Animation Demos 17 | 18 | #### Fading Grid || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/grid_magnification/fading_grid.dart) 19 | ![Fading Grid](gifs/fading-grid.gif) 20 | 21 | --- 22 | 23 | #### Grid Magnification || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/grid_magnification/grid_magnification.dart) 24 | ![Grid Magnification](gifs/grid-magn.gif) 25 | 26 | --- 27 | 28 | #### 3D Card || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/card/three_dimensional_card.dart) 29 | ![3D Card](gifs/3d-card.gif) 30 | 31 | --- 32 | 33 | #### Bouncing DVD || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/coding_train_challenges/bouncing_dvd.dart) 34 | ![Bouncing DVD](gifs/bouncing-dvd.gif) 35 | 36 | --- 37 | 38 | #### Rain Animation || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/coding_train_challenges/rain.dart) 39 | ![Rain Animation](gifs/rain.gif) 40 | 41 | --- 42 | 43 | #### Clock Loader || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/loaders/clock_loader.dart) 44 | ![Clock Loader](gifs/clock_loader.gif) 45 | 46 | 47 | --- 48 | 49 | #### Fractal Pyramid Shader || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/shaders/fractal_pyramid.dart) 50 | ![Fractal Pyramid Shader](gifs/pyramid-shader.gif) 51 | 52 | 53 | --- 54 | 55 | #### Metaball FAB || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/shaders/metaball_fab.dart) 56 | ![Metaball FAB](gifs/metaball-fab.gif) 57 | 58 | 59 | --- 60 | 61 | #### Simple Particle System || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/coding_train_challenges/simple_particle_system.dart) 62 | ![Simple Particle System](gifs/simple-particle-system.gif) 63 | 64 | 65 | --- 66 | 67 | #### The Bouncing Ball || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/coding_train_challenges/bouncing_ball.dart) 68 | ![The Bouncing Ball](gifs/bouncing-ball.gif) 69 | 70 | 71 | --- 72 | 73 | #### Particle System with Emitters || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/coding_train_challenges/particle_system_with_emitters.dart) 74 | ![Particle System with Emitters](gifs/particle-emitter.gif) 75 | 76 | 77 | --- 78 | 79 | #### Painting with Pixels || [Link to code](https://github.com/JideGuru/animation_playground/blob/master/lib/animations/coding_train_challenges/painting_with_pixels.dart) 80 | ![Painting with Pixels](gifs/pixel-painting.gif) 81 | 82 | 83 | ## Getting Started 84 | 85 | This project is a starting point for a Flutter application. 86 | 87 | A few resources to get you started if this is your first Flutter project: 88 | 89 | - [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) 90 | - [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) 91 | 92 | For help getting started with Flutter development, view the 93 | [online documentation](https://docs.flutter.dev/), which offers tutorials, 94 | samples, guidance on mobile development, and a full API reference. 95 | -------------------------------------------------------------------------------- /analysis_options.yaml: -------------------------------------------------------------------------------- 1 | # This file configures the analyzer, which statically analyzes Dart code to 2 | # check for errors, warnings, and lints. 3 | # 4 | # The issues identified by the analyzer are surfaced in the UI of Dart-enabled 5 | # IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be 6 | # invoked from the command line by running `flutter analyze`. 7 | 8 | # The following line activates a set of recommended lints for Flutter apps, 9 | # packages, and plugins designed to encourage good coding practices. 10 | include: package:flutter_lints/flutter.yaml 11 | 12 | linter: 13 | # The lint rules applied to this project can be customized in the 14 | # section below to disable rules from the `package:flutter_lints/flutter.yaml` 15 | # included above or to enable additional rules. A list of all available lints 16 | # and their documentation is published at 17 | # https://dart-lang.github.io/linter/lints/index.html. 18 | # 19 | # Instead of disabling a lint rule for the entire project in the 20 | # section below, it can also be suppressed for a single line of code 21 | # or a specific dart file by using the `// ignore: name_of_lint` and 22 | # `// ignore_for_file: name_of_lint` syntax on the line or in the file 23 | # producing the lint. 24 | rules: 25 | # avoid_print: false # Uncomment to disable the `avoid_print` rule 26 | # prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule 27 | 28 | # Additional information about this file can be found at 29 | # https://dart.dev/guides/language/analysis-options 30 | -------------------------------------------------------------------------------- /android/.gitignore: -------------------------------------------------------------------------------- 1 | gradle-wrapper.jar 2 | /.gradle 3 | /captures/ 4 | /gradlew 5 | /gradlew.bat 6 | /local.properties 7 | GeneratedPluginRegistrant.java 8 | 9 | # Remember to never publicly share your keystore. 10 | # See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app 11 | key.properties 12 | **/*.keystore 13 | **/*.jks 14 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | def localProperties = new Properties() 2 | def localPropertiesFile = rootProject.file('local.properties') 3 | if (localPropertiesFile.exists()) { 4 | localPropertiesFile.withReader('UTF-8') { reader -> 5 | localProperties.load(reader) 6 | } 7 | } 8 | 9 | def flutterRoot = localProperties.getProperty('flutter.sdk') 10 | if (flutterRoot == null) { 11 | throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") 12 | } 13 | 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') 15 | if (flutterVersionCode == null) { 16 | flutterVersionCode = '1' 17 | } 18 | 19 | def flutterVersionName = localProperties.getProperty('flutter.versionName') 20 | if (flutterVersionName == null) { 21 | flutterVersionName = '1.0' 22 | } 23 | 24 | apply plugin: 'com.android.application' 25 | apply plugin: 'kotlin-android' 26 | apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" 27 | 28 | android { 29 | compileSdkVersion flutter.compileSdkVersion 30 | ndkVersion flutter.ndkVersion 31 | 32 | compileOptions { 33 | sourceCompatibility JavaVersion.VERSION_1_8 34 | targetCompatibility JavaVersion.VERSION_1_8 35 | } 36 | 37 | kotlinOptions { 38 | jvmTarget = '1.8' 39 | } 40 | 41 | sourceSets { 42 | main.java.srcDirs += 'src/main/kotlin' 43 | } 44 | 45 | defaultConfig { 46 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 47 | applicationId "com.jideguru.animation_playground" 48 | // You can update the following values to match your application needs. 49 | // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. 50 | minSdkVersion flutter.minSdkVersion 51 | targetSdkVersion flutter.targetSdkVersion 52 | versionCode flutterVersionCode.toInteger() 53 | versionName flutterVersionName 54 | } 55 | 56 | buildTypes { 57 | release { 58 | // TODO: Add your own signing config for the release build. 59 | // Signing with the debug keys for now, so `flutter run --release` works. 60 | signingConfig signingConfigs.debug 61 | } 62 | } 63 | } 64 | 65 | flutter { 66 | source '../..' 67 | } 68 | 69 | dependencies { 70 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 71 | } 72 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 15 | 19 | 23 | 24 | 25 | 26 | 27 | 28 | 30 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/jideguru/animation_playground/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.jideguru.animation_playground 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable-v21/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/drawable/launch_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /android/app/src/main/res/values-night/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 15 | 18 | 19 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.6.10' 3 | repositories { 4 | google() 5 | mavenCentral() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:7.1.2' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | mavenCentral() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | zipStoreBase=GRADLE_USER_HOME 4 | zipStorePath=wrapper/dists 5 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4-all.zip 6 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | 3 | def localPropertiesFile = new File(rootProject.projectDir, "local.properties") 4 | def properties = new Properties() 5 | 6 | assert localPropertiesFile.exists() 7 | localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } 8 | 9 | def flutterSdkPath = properties.getProperty("flutter.sdk") 10 | assert flutterSdkPath != null, "flutter.sdk not set in local.properties" 11 | apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" 12 | -------------------------------------------------------------------------------- /assets/images/cat.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/assets/images/cat.jpeg -------------------------------------------------------------------------------- /assets/images/dvd-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/assets/images/dvd-logo.png -------------------------------------------------------------------------------- /gifs/3d-card.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/3d-card.gif -------------------------------------------------------------------------------- /gifs/bouncing-ball.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/bouncing-ball.gif -------------------------------------------------------------------------------- /gifs/bouncing-dvd.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/bouncing-dvd.gif -------------------------------------------------------------------------------- /gifs/clock_loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/clock_loader.gif -------------------------------------------------------------------------------- /gifs/fading-grid.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/fading-grid.gif -------------------------------------------------------------------------------- /gifs/grid-magn.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/grid-magn.gif -------------------------------------------------------------------------------- /gifs/metaball-fab.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/metaball-fab.gif -------------------------------------------------------------------------------- /gifs/particle-emitter.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/particle-emitter.gif -------------------------------------------------------------------------------- /gifs/pixel-painting.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/pixel-painting.gif -------------------------------------------------------------------------------- /gifs/pyramid-shader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/pyramid-shader.gif -------------------------------------------------------------------------------- /gifs/rain.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/rain.gif -------------------------------------------------------------------------------- /gifs/simple-particle-system.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/gifs/simple-particle-system.gif -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | **/dgph 2 | *.mode1v3 3 | *.mode2v3 4 | *.moved-aside 5 | *.pbxuser 6 | *.perspectivev3 7 | **/*sync/ 8 | .sconsign.dblite 9 | .tags* 10 | **/.vagrant/ 11 | **/DerivedData/ 12 | Icon? 13 | **/Pods/ 14 | **/.symlinks/ 15 | profile 16 | xcuserdata 17 | **/.generated/ 18 | Flutter/App.framework 19 | Flutter/Flutter.framework 20 | Flutter/Flutter.podspec 21 | Flutter/Generated.xcconfig 22 | Flutter/ephemeral/ 23 | Flutter/app.flx 24 | Flutter/app.zip 25 | Flutter/flutter_assets/ 26 | Flutter/flutter_export_environment.sh 27 | ServiceDefinitions.json 28 | Runner/GeneratedPluginRegistrant.* 29 | 30 | # Exceptions to above rules. 31 | !default.mode1v3 32 | !default.mode2v3 33 | !default.pbxuser 34 | !default.perspectivev3 35 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 12.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; 16 | /* End PBXBuildFile section */ 17 | 18 | /* Begin PBXCopyFilesBuildPhase section */ 19 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { 20 | isa = PBXCopyFilesBuildPhase; 21 | buildActionMask = 2147483647; 22 | dstPath = ""; 23 | dstSubfolderSpec = 10; 24 | files = ( 25 | ); 26 | name = "Embed Frameworks"; 27 | runOnlyForDeploymentPostprocessing = 0; 28 | }; 29 | /* End PBXCopyFilesBuildPhase section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 33 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; 34 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 35 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 36 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 37 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 38 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 39 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; 40 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 42 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 43 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 44 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 45 | /* End PBXFileReference section */ 46 | 47 | /* Begin PBXFrameworksBuildPhase section */ 48 | 97C146EB1CF9000F007C117D /* Frameworks */ = { 49 | isa = PBXFrameworksBuildPhase; 50 | buildActionMask = 2147483647; 51 | files = ( 52 | ); 53 | runOnlyForDeploymentPostprocessing = 0; 54 | }; 55 | /* End PBXFrameworksBuildPhase section */ 56 | 57 | /* Begin PBXGroup section */ 58 | 9740EEB11CF90186004384FC /* Flutter */ = { 59 | isa = PBXGroup; 60 | children = ( 61 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 62 | 9740EEB21CF90195004384FC /* Debug.xcconfig */, 63 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */, 64 | 9740EEB31CF90195004384FC /* Generated.xcconfig */, 65 | ); 66 | name = Flutter; 67 | sourceTree = ""; 68 | }; 69 | 97C146E51CF9000F007C117D = { 70 | isa = PBXGroup; 71 | children = ( 72 | 9740EEB11CF90186004384FC /* Flutter */, 73 | 97C146F01CF9000F007C117D /* Runner */, 74 | 97C146EF1CF9000F007C117D /* Products */, 75 | ); 76 | sourceTree = ""; 77 | }; 78 | 97C146EF1CF9000F007C117D /* Products */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 97C146EE1CF9000F007C117D /* Runner.app */, 82 | ); 83 | name = Products; 84 | sourceTree = ""; 85 | }; 86 | 97C146F01CF9000F007C117D /* Runner */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 97C146FA1CF9000F007C117D /* Main.storyboard */, 90 | 97C146FD1CF9000F007C117D /* Assets.xcassets */, 91 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */, 92 | 97C147021CF9000F007C117D /* Info.plist */, 93 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */, 94 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */, 95 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */, 96 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */, 97 | ); 98 | path = Runner; 99 | sourceTree = ""; 100 | }; 101 | /* End PBXGroup section */ 102 | 103 | /* Begin PBXNativeTarget section */ 104 | 97C146ED1CF9000F007C117D /* Runner */ = { 105 | isa = PBXNativeTarget; 106 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; 107 | buildPhases = ( 108 | 9740EEB61CF901F6004384FC /* Run Script */, 109 | 97C146EA1CF9000F007C117D /* Sources */, 110 | 97C146EB1CF9000F007C117D /* Frameworks */, 111 | 97C146EC1CF9000F007C117D /* Resources */, 112 | 9705A1C41CF9048500538489 /* Embed Frameworks */, 113 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, 114 | ); 115 | buildRules = ( 116 | ); 117 | dependencies = ( 118 | ); 119 | name = Runner; 120 | productName = Runner; 121 | productReference = 97C146EE1CF9000F007C117D /* Runner.app */; 122 | productType = "com.apple.product-type.application"; 123 | }; 124 | /* End PBXNativeTarget section */ 125 | 126 | /* Begin PBXProject section */ 127 | 97C146E61CF9000F007C117D /* Project object */ = { 128 | isa = PBXProject; 129 | attributes = { 130 | LastUpgradeCheck = 1510; 131 | ORGANIZATIONNAME = ""; 132 | TargetAttributes = { 133 | 97C146ED1CF9000F007C117D = { 134 | CreatedOnToolsVersion = 7.3.1; 135 | LastSwiftMigration = 1100; 136 | }; 137 | }; 138 | }; 139 | buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */; 140 | compatibilityVersion = "Xcode 9.3"; 141 | developmentRegion = en; 142 | hasScannedForEncodings = 0; 143 | knownRegions = ( 144 | en, 145 | Base, 146 | ); 147 | mainGroup = 97C146E51CF9000F007C117D; 148 | productRefGroup = 97C146EF1CF9000F007C117D /* Products */; 149 | projectDirPath = ""; 150 | projectRoot = ""; 151 | targets = ( 152 | 97C146ED1CF9000F007C117D /* Runner */, 153 | ); 154 | }; 155 | /* End PBXProject section */ 156 | 157 | /* Begin PBXResourcesBuildPhase section */ 158 | 97C146EC1CF9000F007C117D /* Resources */ = { 159 | isa = PBXResourcesBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 163 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 164 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 165 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, 166 | ); 167 | runOnlyForDeploymentPostprocessing = 0; 168 | }; 169 | /* End PBXResourcesBuildPhase section */ 170 | 171 | /* Begin PBXShellScriptBuildPhase section */ 172 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { 173 | isa = PBXShellScriptBuildPhase; 174 | alwaysOutOfDate = 1; 175 | buildActionMask = 2147483647; 176 | files = ( 177 | ); 178 | inputPaths = ( 179 | "${TARGET_BUILD_DIR}/${INFOPLIST_PATH}", 180 | ); 181 | name = "Thin Binary"; 182 | outputPaths = ( 183 | ); 184 | runOnlyForDeploymentPostprocessing = 0; 185 | shellPath = /bin/sh; 186 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; 187 | }; 188 | 9740EEB61CF901F6004384FC /* Run Script */ = { 189 | isa = PBXShellScriptBuildPhase; 190 | alwaysOutOfDate = 1; 191 | buildActionMask = 2147483647; 192 | files = ( 193 | ); 194 | inputPaths = ( 195 | ); 196 | name = "Run Script"; 197 | outputPaths = ( 198 | ); 199 | runOnlyForDeploymentPostprocessing = 0; 200 | shellPath = /bin/sh; 201 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; 202 | }; 203 | /* End PBXShellScriptBuildPhase section */ 204 | 205 | /* Begin PBXSourcesBuildPhase section */ 206 | 97C146EA1CF9000F007C117D /* Sources */ = { 207 | isa = PBXSourcesBuildPhase; 208 | buildActionMask = 2147483647; 209 | files = ( 210 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */, 211 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXSourcesBuildPhase section */ 216 | 217 | /* Begin PBXVariantGroup section */ 218 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { 219 | isa = PBXVariantGroup; 220 | children = ( 221 | 97C146FB1CF9000F007C117D /* Base */, 222 | ); 223 | name = Main.storyboard; 224 | sourceTree = ""; 225 | }; 226 | 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = { 227 | isa = PBXVariantGroup; 228 | children = ( 229 | 97C147001CF9000F007C117D /* Base */, 230 | ); 231 | name = LaunchScreen.storyboard; 232 | sourceTree = ""; 233 | }; 234 | /* End PBXVariantGroup section */ 235 | 236 | /* Begin XCBuildConfiguration section */ 237 | 249021D3217E4FDB00AE95B9 /* Profile */ = { 238 | isa = XCBuildConfiguration; 239 | buildSettings = { 240 | ALWAYS_SEARCH_USER_PATHS = NO; 241 | CLANG_ANALYZER_NONNULL = YES; 242 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 243 | CLANG_CXX_LIBRARY = "libc++"; 244 | CLANG_ENABLE_MODULES = YES; 245 | CLANG_ENABLE_OBJC_ARC = YES; 246 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 247 | CLANG_WARN_BOOL_CONVERSION = YES; 248 | CLANG_WARN_COMMA = YES; 249 | CLANG_WARN_CONSTANT_CONVERSION = YES; 250 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 251 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 252 | CLANG_WARN_EMPTY_BODY = YES; 253 | CLANG_WARN_ENUM_CONVERSION = YES; 254 | CLANG_WARN_INFINITE_RECURSION = YES; 255 | CLANG_WARN_INT_CONVERSION = YES; 256 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 257 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 258 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 259 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 260 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 261 | CLANG_WARN_STRICT_PROTOTYPES = YES; 262 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 263 | CLANG_WARN_UNREACHABLE_CODE = YES; 264 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 265 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 266 | COPY_PHASE_STRIP = NO; 267 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 268 | ENABLE_NS_ASSERTIONS = NO; 269 | ENABLE_STRICT_OBJC_MSGSEND = YES; 270 | GCC_C_LANGUAGE_STANDARD = gnu99; 271 | GCC_NO_COMMON_BLOCKS = YES; 272 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 273 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 274 | GCC_WARN_UNDECLARED_SELECTOR = YES; 275 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 276 | GCC_WARN_UNUSED_FUNCTION = YES; 277 | GCC_WARN_UNUSED_VARIABLE = YES; 278 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 279 | MTL_ENABLE_DEBUG_INFO = NO; 280 | SDKROOT = iphoneos; 281 | SUPPORTED_PLATFORMS = iphoneos; 282 | TARGETED_DEVICE_FAMILY = "1,2"; 283 | VALIDATE_PRODUCT = YES; 284 | }; 285 | name = Profile; 286 | }; 287 | 249021D4217E4FDB00AE95B9 /* Profile */ = { 288 | isa = XCBuildConfiguration; 289 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 290 | buildSettings = { 291 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 292 | CLANG_ENABLE_MODULES = YES; 293 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 294 | DEVELOPMENT_TEAM = 6U5CJ2ZVM3; 295 | ENABLE_BITCODE = NO; 296 | INFOPLIST_FILE = Runner/Info.plist; 297 | LD_RUNPATH_SEARCH_PATHS = ( 298 | "$(inherited)", 299 | "@executable_path/Frameworks", 300 | ); 301 | PRODUCT_BUNDLE_IDENTIFIER = com.jideguru.animationPlayground; 302 | PRODUCT_NAME = "$(TARGET_NAME)"; 303 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 304 | SWIFT_VERSION = 5.0; 305 | VERSIONING_SYSTEM = "apple-generic"; 306 | }; 307 | name = Profile; 308 | }; 309 | 97C147031CF9000F007C117D /* Debug */ = { 310 | isa = XCBuildConfiguration; 311 | buildSettings = { 312 | ALWAYS_SEARCH_USER_PATHS = NO; 313 | CLANG_ANALYZER_NONNULL = YES; 314 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 315 | CLANG_CXX_LIBRARY = "libc++"; 316 | CLANG_ENABLE_MODULES = YES; 317 | CLANG_ENABLE_OBJC_ARC = YES; 318 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 319 | CLANG_WARN_BOOL_CONVERSION = YES; 320 | CLANG_WARN_COMMA = YES; 321 | CLANG_WARN_CONSTANT_CONVERSION = YES; 322 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 323 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 324 | CLANG_WARN_EMPTY_BODY = YES; 325 | CLANG_WARN_ENUM_CONVERSION = YES; 326 | CLANG_WARN_INFINITE_RECURSION = YES; 327 | CLANG_WARN_INT_CONVERSION = YES; 328 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 329 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 330 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 331 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 332 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 333 | CLANG_WARN_STRICT_PROTOTYPES = YES; 334 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 335 | CLANG_WARN_UNREACHABLE_CODE = YES; 336 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 337 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 338 | COPY_PHASE_STRIP = NO; 339 | DEBUG_INFORMATION_FORMAT = dwarf; 340 | ENABLE_STRICT_OBJC_MSGSEND = YES; 341 | ENABLE_TESTABILITY = YES; 342 | GCC_C_LANGUAGE_STANDARD = gnu99; 343 | GCC_DYNAMIC_NO_PIC = NO; 344 | GCC_NO_COMMON_BLOCKS = YES; 345 | GCC_OPTIMIZATION_LEVEL = 0; 346 | GCC_PREPROCESSOR_DEFINITIONS = ( 347 | "DEBUG=1", 348 | "$(inherited)", 349 | ); 350 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 351 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 352 | GCC_WARN_UNDECLARED_SELECTOR = YES; 353 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 354 | GCC_WARN_UNUSED_FUNCTION = YES; 355 | GCC_WARN_UNUSED_VARIABLE = YES; 356 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 357 | MTL_ENABLE_DEBUG_INFO = YES; 358 | ONLY_ACTIVE_ARCH = YES; 359 | SDKROOT = iphoneos; 360 | TARGETED_DEVICE_FAMILY = "1,2"; 361 | }; 362 | name = Debug; 363 | }; 364 | 97C147041CF9000F007C117D /* Release */ = { 365 | isa = XCBuildConfiguration; 366 | buildSettings = { 367 | ALWAYS_SEARCH_USER_PATHS = NO; 368 | CLANG_ANALYZER_NONNULL = YES; 369 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 370 | CLANG_CXX_LIBRARY = "libc++"; 371 | CLANG_ENABLE_MODULES = YES; 372 | CLANG_ENABLE_OBJC_ARC = YES; 373 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 374 | CLANG_WARN_BOOL_CONVERSION = YES; 375 | CLANG_WARN_COMMA = YES; 376 | CLANG_WARN_CONSTANT_CONVERSION = YES; 377 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 378 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 379 | CLANG_WARN_EMPTY_BODY = YES; 380 | CLANG_WARN_ENUM_CONVERSION = YES; 381 | CLANG_WARN_INFINITE_RECURSION = YES; 382 | CLANG_WARN_INT_CONVERSION = YES; 383 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 384 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 385 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 386 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 387 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 388 | CLANG_WARN_STRICT_PROTOTYPES = YES; 389 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 390 | CLANG_WARN_UNREACHABLE_CODE = YES; 391 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 392 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 393 | COPY_PHASE_STRIP = NO; 394 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 395 | ENABLE_NS_ASSERTIONS = NO; 396 | ENABLE_STRICT_OBJC_MSGSEND = YES; 397 | GCC_C_LANGUAGE_STANDARD = gnu99; 398 | GCC_NO_COMMON_BLOCKS = YES; 399 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 400 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 401 | GCC_WARN_UNDECLARED_SELECTOR = YES; 402 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 403 | GCC_WARN_UNUSED_FUNCTION = YES; 404 | GCC_WARN_UNUSED_VARIABLE = YES; 405 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 406 | MTL_ENABLE_DEBUG_INFO = NO; 407 | SDKROOT = iphoneos; 408 | SUPPORTED_PLATFORMS = iphoneos; 409 | SWIFT_COMPILATION_MODE = wholemodule; 410 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 411 | TARGETED_DEVICE_FAMILY = "1,2"; 412 | VALIDATE_PRODUCT = YES; 413 | }; 414 | name = Release; 415 | }; 416 | 97C147061CF9000F007C117D /* Debug */ = { 417 | isa = XCBuildConfiguration; 418 | baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; 419 | buildSettings = { 420 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 421 | CLANG_ENABLE_MODULES = YES; 422 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 423 | DEVELOPMENT_TEAM = 6U5CJ2ZVM3; 424 | ENABLE_BITCODE = NO; 425 | INFOPLIST_FILE = Runner/Info.plist; 426 | LD_RUNPATH_SEARCH_PATHS = ( 427 | "$(inherited)", 428 | "@executable_path/Frameworks", 429 | ); 430 | PRODUCT_BUNDLE_IDENTIFIER = com.jideguru.animationPlayground; 431 | PRODUCT_NAME = "$(TARGET_NAME)"; 432 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 433 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 434 | SWIFT_VERSION = 5.0; 435 | VERSIONING_SYSTEM = "apple-generic"; 436 | }; 437 | name = Debug; 438 | }; 439 | 97C147071CF9000F007C117D /* Release */ = { 440 | isa = XCBuildConfiguration; 441 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; 442 | buildSettings = { 443 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 444 | CLANG_ENABLE_MODULES = YES; 445 | CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)"; 446 | DEVELOPMENT_TEAM = 6U5CJ2ZVM3; 447 | ENABLE_BITCODE = NO; 448 | INFOPLIST_FILE = Runner/Info.plist; 449 | LD_RUNPATH_SEARCH_PATHS = ( 450 | "$(inherited)", 451 | "@executable_path/Frameworks", 452 | ); 453 | PRODUCT_BUNDLE_IDENTIFIER = com.jideguru.animationPlayground; 454 | PRODUCT_NAME = "$(TARGET_NAME)"; 455 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; 456 | SWIFT_VERSION = 5.0; 457 | VERSIONING_SYSTEM = "apple-generic"; 458 | }; 459 | name = Release; 460 | }; 461 | /* End XCBuildConfiguration section */ 462 | 463 | /* Begin XCConfigurationList section */ 464 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { 465 | isa = XCConfigurationList; 466 | buildConfigurations = ( 467 | 97C147031CF9000F007C117D /* Debug */, 468 | 97C147041CF9000F007C117D /* Release */, 469 | 249021D3217E4FDB00AE95B9 /* Profile */, 470 | ); 471 | defaultConfigurationIsVisible = 0; 472 | defaultConfigurationName = Release; 473 | }; 474 | 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = { 475 | isa = XCConfigurationList; 476 | buildConfigurations = ( 477 | 97C147061CF9000F007C117D /* Debug */, 478 | 97C147071CF9000F007C117D /* Release */, 479 | 249021D4217E4FDB00AE95B9 /* Profile */, 480 | ); 481 | defaultConfigurationIsVisible = 0; 482 | defaultConfigurationName = Release; 483 | }; 484 | /* End XCConfigurationList section */ 485 | }; 486 | rootObject = 97C146E61CF9000F007C117D /* Project object */; 487 | } 488 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @main 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "LaunchImage.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "LaunchImage@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "LaunchImage@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | Animation Playground 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | animation_playground 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | $(FLUTTER_BUILD_NUMBER) 25 | LSRequiresIPhoneOS 26 | 27 | UILaunchStoryboardName 28 | LaunchScreen 29 | UIMainStoryboardFile 30 | Main 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | UIViewControllerBasedStatusBarAppearance 45 | 46 | CADisableMinimumFrameDurationOnPhone 47 | 48 | UIApplicationSupportsIndirectInputEvents 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /lib/animations/card/barrel.dart: -------------------------------------------------------------------------------- 1 | export 'three_dimensional_card.dart'; 2 | -------------------------------------------------------------------------------- /lib/animations/card/three_dimensional_card.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class ThreeDimensionalCard extends StatefulWidget { 4 | const ThreeDimensionalCard({Key? key}) : super(key: key); 5 | 6 | @override 7 | State createState() => _ThreeDimensionalCardState(); 8 | } 9 | 10 | class _ThreeDimensionalCardState extends State { 11 | Offset location = Offset.zero; 12 | @override 13 | Widget build(BuildContext context) { 14 | Size screenSize = MediaQuery.of(context).size; 15 | const height = 256.0; 16 | final width = screenSize.width > 400 ? 400.0 : screenSize.width * 0.9; 17 | 18 | const cardHeight = height - 7; 19 | final cardWidth = width - 7; 20 | return Scaffold( 21 | appBar: AppBar( 22 | title: const Text('3D Card'), 23 | ), 24 | backgroundColor: Colors.black, 25 | body: Center( 26 | child: Stack( 27 | children: [ 28 | Align( 29 | alignment: Alignment.center, 30 | child: Container( 31 | height: 256, 32 | width: width, 33 | decoration: BoxDecoration( 34 | gradient: const SweepGradient(colors: [ 35 | Colors.cyanAccent, 36 | Colors.pinkAccent, 37 | Colors.yellowAccent, 38 | Colors.cyanAccent 39 | ]), 40 | borderRadius: BorderRadius.circular(20), 41 | ), 42 | ), 43 | ), 44 | Align( 45 | alignment: Alignment.center, 46 | child: Transform( 47 | transform: Matrix4.identity() 48 | ..setEntry(3, 2, 0.002) // perspective 49 | ..rotateX(0.001 * location.dy) 50 | ..rotateY(-0.001 * location.dx), 51 | alignment: FractionalOffset.center, 52 | child: GestureDetector( 53 | onPanUpdate: (details) { 54 | location += details.delta; 55 | setState(() {}); 56 | }, 57 | onPanEnd: (details) { 58 | location = Offset.zero; 59 | setState(() {}); 60 | }, 61 | child: Container( 62 | height: cardHeight, 63 | width: cardWidth, 64 | decoration: BoxDecoration( 65 | borderRadius: BorderRadius.circular(20), 66 | color: Colors.black, 67 | ), 68 | padding: const EdgeInsets.only(left: 20, bottom: 20), 69 | child: Align( 70 | alignment: Alignment.bottomLeft, 71 | child: Row( 72 | children: [ 73 | Container( 74 | height: 50, 75 | width: 50, 76 | decoration: BoxDecoration( 77 | color: Colors.grey.shade800, 78 | shape: BoxShape.circle, 79 | ), 80 | ), 81 | const SizedBox(width: 10), 82 | Column( 83 | crossAxisAlignment: CrossAxisAlignment.start, 84 | mainAxisSize: MainAxisSize.min, 85 | children: [ 86 | Container( 87 | height: 20, 88 | width: 80 * 2, 89 | decoration: BoxDecoration( 90 | color: Colors.grey.shade800, 91 | borderRadius: BorderRadius.circular(20), 92 | ), 93 | ), 94 | const SizedBox(height: 8), 95 | Container( 96 | height: 20, 97 | width: 80, 98 | decoration: BoxDecoration( 99 | color: Colors.grey.shade800, 100 | borderRadius: BorderRadius.circular(20), 101 | ), 102 | ), 103 | ], 104 | ), 105 | ], 106 | ), 107 | ), 108 | ), 109 | ), 110 | ), 111 | ) 112 | ], 113 | ), 114 | ), 115 | ); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/barrel.dart: -------------------------------------------------------------------------------- 1 | export 'bouncing_dvd.dart'; 2 | export 'rain.dart'; 3 | export 'simple_particle_system.dart' hide Particle; 4 | export 'bouncing_ball.dart'; 5 | export 'particle_system_with_emitters.dart' hide Particle; 6 | export 'painting_with_pixels.dart'; 7 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/bouncing_ball.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/scheduler.dart'; 6 | 7 | class BouncingBall extends StatefulWidget { 8 | const BouncingBall({Key? key}) : super(key: key); 9 | 10 | @override 11 | State createState() => _BouncingBallState(); 12 | } 13 | 14 | class _BouncingBallState extends State with SingleTickerProviderStateMixin { 15 | late final Ticker _ticker; 16 | double _vx = 5, _vy = 5, _x = 170, _y = 400; 17 | final _ballWidth = 30, _ballHeight = 30; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | Timer(const Duration(microseconds: 300), () { 23 | _x = Random().nextDouble() * (MediaQuery.of(context).size.width + _ballWidth); 24 | _y = Random().nextDouble() * (MediaQuery.of(context).size.height + _ballHeight); 25 | setState(() {}); 26 | _ticker = createTicker((elapsed) { 27 | _x += _vx; 28 | _y += _vy; 29 | 30 | final size = MediaQuery.of(context).size; 31 | if (_x <= 0 || _x >= size.width - _ballWidth) { 32 | _vx = -_vx; 33 | } 34 | if (_y <= 0 || _y >= size.height - _ballHeight) { 35 | _vy = -_vy; 36 | } 37 | setState(() {}); 38 | }); 39 | _ticker.start(); 40 | }); 41 | 42 | } 43 | 44 | @override 45 | void dispose() { 46 | super.dispose(); 47 | _ticker.dispose(); 48 | } 49 | 50 | @override 51 | Widget build(BuildContext context) { 52 | return Scaffold( 53 | backgroundColor: Colors.black, 54 | body: Stack( 55 | children: [ 56 | Transform.translate( 57 | offset: Offset(_x, _y), 58 | child: Container( 59 | height: 30, 60 | width: 30, 61 | decoration: const BoxDecoration( 62 | color: Colors.white, 63 | shape: BoxShape.circle, 64 | ), 65 | ), 66 | ), 67 | ], 68 | ), 69 | ); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/bouncing_dvd.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | class BouncingDVD extends StatefulWidget { 7 | const BouncingDVD({Key? key}) : super(key: key); 8 | 9 | @override 10 | State createState() => _BouncingDVDState(); 11 | } 12 | 13 | class _BouncingDVDState extends State { 14 | Random random = Random(); 15 | Color dvdColor = Colors.pink; 16 | double dvdWidth = 150, dvdHeight = 80; 17 | double x = 90, y = 30, xSpeed = 50, ySpeed = 50, speed = 150; 18 | 19 | pickColor() { 20 | // removing the timer will make the dvd color change before bouncing 21 | // off the edge 22 | Timer(const Duration(milliseconds: 100), () { 23 | int r = random.nextInt(255); 24 | int g = random.nextInt(255); 25 | int b = random.nextInt(255); 26 | dvdColor = Color.fromRGBO(r, g, b, 1); 27 | }); 28 | } 29 | 30 | @override 31 | initState() { 32 | super.initState(); 33 | update(); 34 | } 35 | 36 | update() { 37 | Timer.periodic(Duration(milliseconds: speed.toInt()), (timer) { 38 | double screenWidth = MediaQuery.of(context).size.width; 39 | double screenHeight = MediaQuery.of(context).size.height; 40 | x += xSpeed; 41 | y += ySpeed; 42 | 43 | if (x + dvdWidth >= screenWidth) { 44 | xSpeed = -xSpeed; 45 | x = screenWidth - dvdWidth; 46 | pickColor(); 47 | } else if (x <= 0) { 48 | xSpeed = -xSpeed; 49 | x = 0; 50 | pickColor(); 51 | } 52 | 53 | if (y + dvdHeight >= screenHeight) { 54 | ySpeed = -ySpeed; 55 | y = screenHeight - dvdHeight; 56 | pickColor(); 57 | } else if (y <= 0) { 58 | ySpeed = -ySpeed; 59 | y = 0; 60 | pickColor(); 61 | } 62 | 63 | setState(() {}); 64 | }); 65 | } 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return Scaffold( 70 | backgroundColor: Colors.black, 71 | appBar: AppBar( 72 | title: const Text('Bouncing DVD'), 73 | ), 74 | body: Stack( 75 | children: [ 76 | AnimatedPositioned( 77 | duration: Duration(milliseconds: speed.toInt()), 78 | left: x, 79 | top: y, 80 | child: Image.asset( 81 | 'assets/images/dvd-logo.png', 82 | color: dvdColor, 83 | height: dvdHeight, 84 | width: dvdWidth, 85 | ), 86 | ), 87 | ], 88 | ), 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/painting_with_pixels.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:ui' as ui; 3 | 4 | import 'package:flutter/material.dart' hide Image; 5 | import 'package:flutter/scheduler.dart'; 6 | import 'package:flutter/services.dart' show rootBundle; 7 | import 'package:image/image.dart' as img; 8 | 9 | class PaintingWithPixels extends StatefulWidget { 10 | const PaintingWithPixels({Key? key}) : super(key: key); 11 | 12 | @override 13 | State createState() => _PaintingWithPixelsState(); 14 | } 15 | 16 | class _PaintingWithPixelsState extends State 17 | with SingleTickerProviderStateMixin { 18 | late ui.Image image; 19 | late img.Image imagePixels; 20 | bool _loading = true; 21 | List offsets = List.empty(growable: true); 22 | final math.Random _random = math.Random(); 23 | late final Ticker _ticker; 24 | 25 | @override 26 | void initState() { 27 | super.initState(); 28 | loadImage(); 29 | _ticker = createTicker((elapsed) { 30 | if (!_loading) { 31 | for (int i = 1; i < 100; i++) { 32 | randomizePoints(); 33 | } 34 | } 35 | setState(() {}); 36 | }); 37 | _ticker.start(); 38 | } 39 | 40 | void loadImage() async { 41 | var data = await rootBundle.load('assets/images/cat.jpeg'); 42 | final buffer = data.buffer; 43 | var bytes = buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); 44 | image = await decodeImageFromList(bytes); 45 | imagePixels = img.decodeJpg(bytes)!; 46 | _loading = false; 47 | randomizePoints(); 48 | setState(() {}); 49 | } 50 | 51 | randomizePoints() { 52 | int x = _random.nextInt(imagePixels.width); 53 | int y = _random.nextInt(imagePixels.height); 54 | 55 | Offset offset = Offset(x.toDouble(), y.toDouble()); 56 | if (!offsets.contains(offset)) { 57 | offsets.add(offset); 58 | } else { 59 | randomizePoints(); 60 | } 61 | } 62 | 63 | @override 64 | void dispose() { 65 | _ticker.dispose(); 66 | super.dispose(); 67 | } 68 | 69 | @override 70 | Widget build(BuildContext context) { 71 | final size = MediaQuery.of(context).size; 72 | return Scaffold( 73 | appBar: AppBar(title: const Text('Painting With Pixels')), 74 | backgroundColor: Colors.white, 75 | body: _loading 76 | ? const SizedBox.shrink() 77 | : Center( 78 | child: CustomPaint( 79 | painter: _PixelPainter( 80 | image: image, 81 | imagePixels: imagePixels, 82 | offsets: offsets, 83 | ), 84 | child: SizedBox(height: size.height, width: size.width), 85 | ), 86 | ), 87 | ); 88 | } 89 | } 90 | 91 | class _PixelPainter extends CustomPainter { 92 | ui.Image image; 93 | img.Image imagePixels; 94 | final List offsets; 95 | 96 | _PixelPainter({ 97 | required this.image, 98 | required this.imagePixels, 99 | required this.offsets, 100 | }); 101 | 102 | @override 103 | void paint(Canvas canvas, Size size) { 104 | for (Offset offset in offsets) { 105 | img.Pixel pixel = 106 | imagePixels.getPixelSafe(offset.dx.toInt(), offset.dy.toInt()); 107 | List colorList = pixel.toList(); 108 | canvas.drawCircle( 109 | offset, 110 | 3, 111 | Paint() 112 | ..color = 113 | Color.fromARGB(255, colorList[0], colorList[1], colorList[2]), 114 | ); 115 | } 116 | } 117 | 118 | @override 119 | bool shouldRepaint(covariant CustomPainter oldDelegate) => true; 120 | } 121 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/particle_system_with_emitters.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_playground/utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/scheduler.dart'; 4 | 5 | class ParticleSystemWithEmitters extends StatefulWidget { 6 | const ParticleSystemWithEmitters({Key? key}) : super(key: key); 7 | 8 | @override 9 | State createState() => 10 | _ParticleSystemWithEmittersState(); 11 | } 12 | 13 | class _ParticleSystemWithEmittersState extends State 14 | with SingleTickerProviderStateMixin { 15 | late List emitters; 16 | late Ticker _ticker; 17 | 18 | @override 19 | void initState() { 20 | super.initState(); 21 | emitters = List.empty(growable: true); 22 | _ticker = createTicker((elapsed) { 23 | setState(() {}); 24 | }); 25 | _ticker.start(); 26 | } 27 | 28 | @override 29 | void dispose() { 30 | _ticker.dispose(); 31 | super.dispose(); 32 | } 33 | 34 | @override 35 | Widget build(BuildContext context) { 36 | final size = MediaQuery.of(context).size; 37 | return Scaffold( 38 | backgroundColor: Colors.black, 39 | appBar: AppBar( 40 | title: const Text('Tap on the screen to display Particles'), 41 | ), 42 | body: GestureDetector( 43 | onTapDown: (details) { 44 | if (emitters.length < 38) { 45 | emitters.add(Emitter(position: details.localPosition)); 46 | } 47 | }, 48 | child: SizedBox( 49 | height: size.height, 50 | width: size.width, 51 | child: CustomPaint( 52 | painter: _ParticlePainter( 53 | emitters: emitters, 54 | ), 55 | ), 56 | ), 57 | ), 58 | ); 59 | } 60 | } 61 | 62 | class _ParticlePainter extends CustomPainter { 63 | final List emitters; 64 | 65 | _ParticlePainter({ 66 | required this.emitters, 67 | }); 68 | 69 | @override 70 | void paint(Canvas canvas, Size size) { 71 | for (var emitter in emitters) { 72 | emitter.update(); 73 | for (var particle in emitter.particles) { 74 | particle.update(); 75 | canvas.drawCircle( 76 | Offset(particle.x, particle.y), 77 | 10, 78 | Paint() 79 | ..color = Color.fromARGB( 80 | particle.alpha, 81 | 255, 82 | 255, 83 | 255, 84 | ), 85 | ); 86 | } 87 | } 88 | } 89 | 90 | @override 91 | bool shouldRepaint(covariant CustomPainter oldDelegate) => true; 92 | } 93 | 94 | class Particle { 95 | double x, y; 96 | double vx = doubleInRange(-1, 1); 97 | double vy = doubleInRange(5, 1); 98 | int alpha = 255; 99 | 100 | Particle({ 101 | required this.x, 102 | required this.y, 103 | }); 104 | 105 | update() { 106 | x += vx; 107 | y += vy; 108 | alpha -= 5; 109 | } 110 | 111 | bool finished() { 112 | return alpha <= 0; 113 | } 114 | } 115 | 116 | class Emitter { 117 | final Offset position; 118 | List particles; 119 | List particlesToRemove = List.empty(growable: true); 120 | 121 | Emitter({required this.position}) 122 | : particles = List.generate( 123 | 1, 124 | (index) => Particle( 125 | x: position.dx, 126 | y: position.dy, 127 | ), 128 | ); 129 | 130 | update() { 131 | particles.add(Particle(x: position.dx, y: position.dy)); 132 | for (var particle in particles) { 133 | if (particle.finished()) particlesToRemove.add(particle); 134 | } 135 | 136 | for (var particle in particlesToRemove) { 137 | particles.remove(particle); 138 | } 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/rain.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:animation_playground/utils.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:flutter/scheduler.dart'; 6 | 7 | class RainAnimation extends StatelessWidget { 8 | const RainAnimation({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final screenSize = MediaQuery.of(context).size; 13 | return Scaffold( 14 | backgroundColor: Colors.black, 15 | appBar: AppBar( 16 | title: const Text('Rain Simulation'), 17 | ), 18 | body: Stack( 19 | children: [ 20 | for (int index = 0; index < 300; index++) 21 | _RainDrop( 22 | screenHeight: screenSize.height, 23 | screenWidth: screenSize.width, 24 | ), 25 | ], 26 | ), 27 | ); 28 | } 29 | } 30 | 31 | class _RainDrop extends StatefulWidget { 32 | final double screenHeight, screenWidth; 33 | 34 | const _RainDrop({ 35 | Key? key, 36 | required this.screenHeight, 37 | required this.screenWidth, 38 | }) : super(key: key); 39 | 40 | @override 41 | State<_RainDrop> createState() => _RainDropState(); 42 | } 43 | 44 | class _RainDropState extends State<_RainDrop> with SingleTickerProviderStateMixin { 45 | // vy(velocity Y) for the speed on the yAxis 46 | late double dx, dy, length, z, vy; 47 | 48 | Random random = Random(); 49 | double get screenHeight => widget.screenHeight; 50 | 51 | double get screenWidth => widget.screenWidth; 52 | late final Ticker _ticker; 53 | 54 | @override 55 | void initState() { 56 | super.initState(); 57 | randomizeValues(); 58 | _ticker = createTicker((elapsed) { 59 | dy += vy; 60 | if (dy >= screenHeight + 100) { 61 | randomizeValues(); 62 | } 63 | setState(() {}); 64 | }); 65 | _ticker.start(); 66 | } 67 | 68 | randomizeValues() { 69 | dx = random.nextDouble() * screenWidth; 70 | dy = -500 - (random.nextDouble() * -500); 71 | z = random.nextDouble() * 20; 72 | length = rangeMap(z, 0, 20, 10, 20); 73 | vy = rangeMap(z, 0, 20, 15, 5); 74 | } 75 | 76 | @override 77 | void dispose() { 78 | _ticker.dispose(); 79 | super.dispose(); 80 | } 81 | 82 | @override 83 | Widget build(BuildContext context) { 84 | return Transform.translate( 85 | offset: Offset(dx, dy), 86 | child: Container( 87 | height: length, 88 | width: 2, 89 | decoration: BoxDecoration( 90 | borderRadius: BorderRadius.circular(5), 91 | color: Colors.white, 92 | border: Border.all(width: rangeMap(z, 0, 20, 1, 3)), 93 | ), 94 | ), 95 | ); 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /lib/animations/coding_train_challenges/simple_particle_system.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_playground/utils.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter/scheduler.dart'; 4 | 5 | class SimpleParticleSystem extends StatefulWidget { 6 | const SimpleParticleSystem({Key? key}) : super(key: key); 7 | 8 | @override 9 | State createState() => _SimpleParticleSystemState(); 10 | } 11 | 12 | class _SimpleParticleSystemState extends State 13 | with SingleTickerProviderStateMixin { 14 | late List particles; 15 | late List particlesToRemove; 16 | late final Ticker _ticker; 17 | 18 | @override 19 | void initState() { 20 | super.initState(); 21 | particles = List.generate(1, (index) => Particle()); 22 | particlesToRemove = List.empty(growable: true); 23 | 24 | _ticker = createTicker((elapsed) { 25 | particles.add(Particle()); 26 | for (var particle in particlesToRemove) { 27 | particles.remove(particle); 28 | } 29 | setState(() {}); 30 | }); 31 | _ticker.start(); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | _ticker.dispose(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | final size = MediaQuery.of(context).size; 43 | return Scaffold( 44 | appBar: AppBar( 45 | title: const Text('Simple Particle System'), 46 | ), 47 | backgroundColor: Colors.black, 48 | body: SizedBox( 49 | width: size.width, 50 | height: size.height, 51 | child: CustomPaint( 52 | painter: ParticlePainter( 53 | particles: particles, 54 | particlesToRemove: particlesToRemove, 55 | ), 56 | ), 57 | ), 58 | ); 59 | } 60 | } 61 | 62 | class ParticlePainter extends CustomPainter { 63 | List particles; 64 | List particlesToRemove; 65 | 66 | ParticlePainter({required this.particles, required this.particlesToRemove}); 67 | 68 | @override 69 | void paint(Canvas canvas, Size size) { 70 | for (var particle in particles) { 71 | particle.setPosition(particle.x * size.width, particle.y * size.height); 72 | particle.update(); 73 | var paint = Paint() 74 | ..color = Color.fromARGB(particle.opacity, 255, 255, 255) 75 | ..strokeWidth = 3.0; 76 | canvas.drawCircle(Offset(particle.x, particle.y), 13.0, paint); 77 | if (particle.finished()) particlesToRemove.add(particle); 78 | } 79 | } 80 | 81 | @override 82 | bool shouldRepaint(CustomPainter oldDelegate) => true; 83 | } 84 | 85 | class Particle { 86 | double x = 0.5; 87 | double y = 0.9; 88 | double vx = doubleInRange(-1, 1); 89 | double vy = doubleInRange(-5, -1); 90 | int opacity = 255; 91 | 92 | Particle(); 93 | 94 | setPosition(double xPos, double yPos) { 95 | if (x < 1 && y < 1) { 96 | x = xPos; 97 | y = yPos; 98 | } 99 | } 100 | 101 | update() { 102 | x += vx; 103 | y += vy; 104 | if (opacity > 0) { 105 | opacity -= 3; 106 | } 107 | } 108 | 109 | bool finished() { 110 | return opacity <= 0; 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /lib/animations/grid_magnification/barrel.dart: -------------------------------------------------------------------------------- 1 | export 'fading_grid.dart'; 2 | export 'grid_magnification.dart'; 3 | -------------------------------------------------------------------------------- /lib/animations/grid_magnification/fading_grid.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class FadingGrid extends StatefulWidget { 6 | const FadingGrid({Key? key}) : super(key: key); 7 | 8 | @override 9 | State createState() => _FadingGridState(); 10 | } 11 | 12 | class _FadingGridState extends State { 13 | Offset location = Offset.zero; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | Size screenSize = MediaQuery.of(context).size; 18 | int squareAmountHorizontal = screenSize.width ~/ 32.7; 19 | double squareContainerSize = screenSize.width / squareAmountHorizontal; 20 | double squarePadding = 10; 21 | double squareSize = squareContainerSize - squarePadding; 22 | int squareAmountVertical = 23 | (screenSize.height / squareContainerSize).floor() - 3; 24 | return Scaffold( 25 | backgroundColor: Colors.black, 26 | appBar: AppBar( 27 | title: const Text('Fading Grid'), 28 | ), 29 | body: Center( 30 | child: GestureDetector( 31 | onPanDown: (details) { 32 | location = details.globalPosition; 33 | setState(() {}); 34 | }, 35 | onPanUpdate: (details) { 36 | location = details.globalPosition; 37 | setState(() {}); 38 | }, 39 | onPanEnd: (details) { 40 | location = Offset.zero; 41 | setState(() {}); 42 | }, 43 | child: SizedBox( 44 | width: screenSize.width, 45 | height: squareAmountVertical * squareContainerSize, 46 | child: CustomPaint( 47 | size: Size( 48 | screenSize.width, squareAmountVertical * squareContainerSize), 49 | painter: _GridPainter( 50 | squareAmountHorizontal: squareAmountHorizontal, 51 | squareAmountVertical: squareAmountVertical, 52 | squareContainerSize: squareContainerSize, 53 | padding: squarePadding, 54 | squareSize: squareSize, 55 | location: location, 56 | ), 57 | ), 58 | ), 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | 65 | class _GridPainter extends CustomPainter { 66 | final int squareAmountHorizontal; 67 | final int squareAmountVertical; 68 | final double squareContainerSize; 69 | final double padding; 70 | final double squareSize; 71 | final Offset location; 72 | 73 | _GridPainter({ 74 | required this.squareAmountHorizontal, 75 | required this.squareAmountVertical, 76 | required this.squareContainerSize, 77 | required this.padding, 78 | required this.squareSize, 79 | required this.location, 80 | }); 81 | 82 | @override 83 | void paint(Canvas canvas, Size size) { 84 | var gradient = const SweepGradient( 85 | colors: [Colors.cyan, Colors.pink, Colors.yellow, Colors.cyan]); 86 | Rect canvasRect = Rect.fromCenter( 87 | center: size.center(Offset.zero), 88 | width: size.width / 2, 89 | height: size.height / 2, 90 | ); 91 | Paint paint = Paint()..shader = gradient.createShader(canvasRect); 92 | for (int i = 0; i < squareAmountHorizontal; i++) { 93 | for (int j = 0; j < squareAmountVertical; j++) { 94 | final rectPos = Offset( 95 | i * squareContainerSize + padding * 2, 96 | j * squareContainerSize + padding * 2, 97 | ); 98 | 99 | final a = location.dx - rectPos.dx; 100 | final b = location.dy - rectPos.dy; 101 | final root = sqrt((a * a) + (b * b)); 102 | final diagonalValue = 103 | sqrt((size.width * size.width) + (size.height * size.height)); 104 | final scale = root / (diagonalValue / 2); 105 | final modifiedScale = location == Offset.zero ? 1 : (1 - scale); 106 | final fadingScale = modifiedScale > 0 ? modifiedScale : 0; 107 | Rect rect = Rect.fromCenter( 108 | center: rectPos, 109 | height: fadingScale * squareSize, 110 | width: fadingScale * squareSize, 111 | ); 112 | canvas.drawRRect( 113 | RRect.fromRectAndRadius(rect, const Radius.circular(4)), paint); 114 | } 115 | } 116 | } 117 | 118 | @override 119 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 120 | return true; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /lib/animations/grid_magnification/grid_magnification.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class GridMagnification extends StatefulWidget { 6 | const GridMagnification({Key? key}) : super(key: key); 7 | 8 | @override 9 | State createState() => _GridMagnificationState(); 10 | } 11 | 12 | class _GridMagnificationState extends State { 13 | Offset location = Offset.zero; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | Size screenSize = MediaQuery.of(context).size; 18 | int squareAmountHorizontal = screenSize.width ~/ 32.7; 19 | double squareContainerSize = screenSize.width / squareAmountHorizontal; 20 | double squarePadding = 10; 21 | double squareSize = squareContainerSize - squarePadding; 22 | int squareAmountVertical = 23 | (screenSize.height / squareContainerSize).floor() - 3; 24 | return Scaffold( 25 | backgroundColor: Colors.black, 26 | appBar: AppBar( 27 | title: const Text('Grid Magnification'), 28 | ), 29 | body: Center( 30 | child: GestureDetector( 31 | onPanDown: (details) { 32 | location = details.globalPosition; 33 | setState(() {}); 34 | }, 35 | onPanUpdate: (details) { 36 | location = details.globalPosition; 37 | setState(() {}); 38 | }, 39 | onPanEnd: (details) { 40 | location = Offset.zero; 41 | setState(() {}); 42 | }, 43 | child: SizedBox( 44 | width: screenSize.width, 45 | height: squareAmountVertical * squareContainerSize, 46 | child: CustomPaint( 47 | size: Size( 48 | screenSize.width, squareAmountVertical * squareContainerSize), 49 | painter: _GridPainter( 50 | squareAmountHorizontal: squareAmountHorizontal, 51 | squareAmountVertical: squareAmountVertical, 52 | squareContainerSize: squareContainerSize, 53 | padding: squarePadding, 54 | squareSize: squareSize, 55 | location: location, 56 | ), 57 | ), 58 | ), 59 | ), 60 | ), 61 | ); 62 | } 63 | } 64 | 65 | class _GridPainter extends CustomPainter { 66 | final int squareAmountHorizontal; 67 | final int squareAmountVertical; 68 | final double squareContainerSize; 69 | final double padding; 70 | final double squareSize; 71 | final Offset location; 72 | 73 | _GridPainter({ 74 | required this.squareAmountHorizontal, 75 | required this.squareAmountVertical, 76 | required this.squareContainerSize, 77 | required this.padding, 78 | required this.squareSize, 79 | required this.location, 80 | }); 81 | 82 | @override 83 | void paint(Canvas canvas, Size size) { 84 | var gradient = const SweepGradient( 85 | colors: [Colors.cyan, Colors.pink, Colors.yellow, Colors.cyan]); 86 | Rect canvasRect = Rect.fromCenter( 87 | center: size.center(Offset.zero), 88 | width: size.width / 2, 89 | height: size.height / 2, 90 | ); 91 | Paint paint = Paint()..color = Colors.white; 92 | paint.shader = gradient.createShader(canvasRect); 93 | const radius = 110; 94 | for (int i = 0; i < squareAmountHorizontal; i++) { 95 | for (int j = 0; j < squareAmountVertical; j++) { 96 | final rectPos = Offset( 97 | i * squareContainerSize + padding * 2, 98 | j * squareContainerSize + padding * 2, 99 | ); 100 | 101 | final a = location.dx - rectPos.dx; 102 | final b = location.dy - rectPos.dy; 103 | final distance = sqrt( 104 | pow(location.dx - rectPos.dx, 2) + pow(location.dy - rectPos.dy, 2), 105 | ); 106 | final median = (distance - radius) / radius; 107 | final root = sqrt((a * a) + (b * b)); 108 | final scale = (root - radius) / radius; 109 | final modifiedScale = location == Offset.zero ? 1 : (1 - scale); 110 | final fadingScale = modifiedScale > 0 ? modifiedScale : 0; 111 | final translateX = a * median; 112 | final translateY = b * median; 113 | Rect rect = Rect.fromCenter( 114 | center: rectPos, 115 | height: fadingScale * squareSize, 116 | width: fadingScale * squareSize, 117 | ); 118 | Rect transformedRect = rect.translate(translateX, translateY); 119 | canvas.drawRRect( 120 | RRect.fromRectAndRadius( 121 | location == Offset.zero ? rect : transformedRect, 122 | const Radius.circular(4), 123 | ), 124 | paint, 125 | ); 126 | } 127 | } 128 | } 129 | 130 | @override 131 | bool shouldRepaint(covariant CustomPainter oldDelegate) { 132 | return true; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /lib/animations/loaders/barrel.dart: -------------------------------------------------------------------------------- 1 | export 'clock_loader.dart'; 2 | -------------------------------------------------------------------------------- /lib/animations/loaders/clock_loader.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | class ClockLoader extends StatefulWidget { 6 | const ClockLoader({Key? key}) : super(key: key); 7 | 8 | @override 9 | State createState() => _ClockLoaderState(); 10 | } 11 | 12 | class _ClockLoaderState extends State 13 | with TickerProviderStateMixin { 14 | static const tickLength = 12; 15 | static const squareSize = 12.0; 16 | List colors = List.generate(tickLength, (index) => index); 17 | late Animation tickAnimation; 18 | late AnimationController animationController; 19 | 20 | @override 21 | void initState() { 22 | super.initState(); 23 | animationController = AnimationController( 24 | vsync: this, 25 | duration: const Duration(milliseconds: 8000), 26 | ) 27 | ..forward()..repeat(); 28 | tickAnimation = Tween( 29 | begin: 0, 30 | end: 4 * math.pi, 31 | ).animate( 32 | CurvedAnimation(parent: animationController, curve: Curves.easeIn)); 33 | animationController.addListener(() { 34 | if (animationController.isAnimating) { 35 | setState(() {}); 36 | } 37 | }); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | animationController.dispose(); 43 | super.dispose(); 44 | } 45 | 46 | @override 47 | Widget build(BuildContext context) { 48 | const offsetAngle = (2 * math.pi) / tickLength; 49 | return Scaffold( 50 | appBar: AppBar( 51 | title: const Text('Clock Loader'), 52 | ), 53 | body: Center( 54 | child: Stack( 55 | children: [ 56 | ...colors.map((e) { 57 | int index = colors.indexOf(e); 58 | final finalAngle = offsetAngle * (tickLength - 1 - index); 59 | double rotate = () { 60 | if (tickAnimation.value <= 2 * math.pi) { 61 | return math.min(finalAngle, tickAnimation.value); 62 | } 63 | if (tickAnimation.value - 2 * math.pi < finalAngle) { 64 | return finalAngle; 65 | } 66 | 67 | return tickAnimation.value; 68 | }(); 69 | double yDefaultPosition = -index * squareSize; 70 | double translateY = () { 71 | if (rotate == finalAngle) { 72 | return -tickLength * squareSize; 73 | } 74 | 75 | if (tickAnimation.value > 2 * math.pi) { 76 | return (index - tickLength) * squareSize; 77 | } 78 | 79 | return yDefaultPosition; 80 | }(); 81 | return TweenAnimationBuilder( 82 | tween: Tween( 83 | begin: yDefaultPosition, 84 | end: translateY, 85 | ), 86 | duration: const Duration(milliseconds: 500), 87 | builder: (context, translateYAnimValue, _) { 88 | return Transform.rotate( 89 | angle: rotate, 90 | child: Transform.translate( 91 | offset: Offset(0.0, translateYAnimValue), 92 | child: Container( 93 | height: squareSize, 94 | width: squareSize, 95 | color: Colors.white, 96 | ), 97 | ), 98 | ); 99 | }, 100 | ); 101 | }).toList(), 102 | ], 103 | ), 104 | ), 105 | ); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /lib/animations/morphing_segmented_bar.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:animation_playground/buildcontext_extension.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | enum PlanType { free, premiumMonthly, premiumYearly } 7 | 8 | class MorphingSegmentedControlPage extends StatefulWidget { 9 | const MorphingSegmentedControlPage({super.key}); 10 | 11 | @override 12 | State createState() => 13 | _MorphingSegmentedControlPageState(); 14 | } 15 | 16 | class _MorphingSegmentedControlPageState 17 | extends State { 18 | PlanType _selectedPlan = PlanType.free; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return Scaffold( 23 | backgroundColor: Colors.white, 24 | appBar: AppBar(title: const Text('Morphing Segmented Control')), 25 | body: Column( 26 | mainAxisAlignment: MainAxisAlignment.center, 27 | crossAxisAlignment: CrossAxisAlignment.center, 28 | children: [ 29 | Center( 30 | child: IntrinsicWidth( 31 | child: Container( 32 | decoration: BoxDecoration( 33 | color: Colors.white, 34 | borderRadius: BorderRadius.circular(30), 35 | border: Border.all(color: Colors.grey), 36 | boxShadow: [ 37 | BoxShadow( 38 | color: Colors.grey.shade200, 39 | spreadRadius: 5, 40 | blurRadius: 7, 41 | offset: const Offset(0, 3), 42 | ), 43 | ], 44 | ), 45 | child: MorphingSegmentedControl( 46 | width: context.isMobile ? context.screenWidth - 20 : 420, 47 | height: 50, 48 | value: _selectedPlan, 49 | onChanged: (newValue) { 50 | setState(() { 51 | _selectedPlan = newValue; 52 | }); 53 | }, 54 | simpleTabValue: PlanType.free, 55 | simpleTabLabel: "Free", 56 | groupTabCollapsedLabel: "Premium", 57 | groupTabOptions: const [ 58 | MorphingTabItem( 59 | value: PlanType.premiumMonthly, 60 | label: "Monthly", 61 | ), 62 | MorphingTabItem( 63 | value: PlanType.premiumYearly, 64 | label: "Annual", 65 | ), 66 | ], 67 | backgroundColor: Colors.white, 68 | selectedColor: Colors.black, 69 | nestedSelectedColor: Colors.white, 70 | selectedTextColor: Colors.white, 71 | unselectedTextColor: Colors.grey, 72 | nestedSelectedTextColor: Colors.black, 73 | nestedUnselectedTextColor: Colors.white, 74 | ), 75 | ), 76 | ), 77 | ), 78 | ], 79 | ), 80 | ); 81 | } 82 | } 83 | 84 | class MorphingTabItem { 85 | final T value; 86 | final String label; 87 | 88 | const MorphingTabItem({required this.value, required this.label}); 89 | 90 | @override 91 | bool operator ==(Object other) => 92 | identical(this, other) || 93 | other is MorphingTabItem && 94 | runtimeType == other.runtimeType && 95 | value == other.value; 96 | 97 | @override 98 | int get hashCode => value.hashCode; 99 | } 100 | 101 | class MorphingSegmentedControl extends StatefulWidget { 102 | final T value; 103 | final ValueChanged onChanged; 104 | final T simpleTabValue; 105 | final String simpleTabLabel; 106 | final String groupTabCollapsedLabel; 107 | final List> groupTabOptions; 108 | final double height; 109 | final double? width; 110 | final EdgeInsets padding; 111 | final EdgeInsets mainThumbMargin; 112 | final EdgeInsets nestedThumbMargin; 113 | final Color backgroundColor; 114 | final Color selectedColor; 115 | final Color nestedSelectedColor; 116 | final Color selectedTextColor; 117 | final Color unselectedTextColor; 118 | final Color nestedSelectedTextColor; 119 | final Color nestedUnselectedTextColor; 120 | final TextStyle? textStyle; 121 | final Duration mainAnimationDuration; 122 | final Duration nestedAnimationDuration; 123 | final Duration switchAnimationDuration; 124 | final Curve animationCurve; 125 | final Curve switchScaleInCurve; 126 | final Curve switchScaleOutCurve; 127 | final Curve switchFadeInCurve; 128 | final Curve switchFadeOutCurve; 129 | final Curve switchSlideOutCurve; 130 | 131 | const MorphingSegmentedControl({ 132 | super.key, 133 | required this.value, 134 | required this.onChanged, 135 | required this.simpleTabValue, 136 | required this.simpleTabLabel, 137 | required this.groupTabCollapsedLabel, 138 | required this.groupTabOptions, 139 | this.height = 48, 140 | this.width = 320, 141 | this.padding = const EdgeInsets.all(0), 142 | this.mainThumbMargin = const EdgeInsets.all(2), 143 | this.nestedThumbMargin = const EdgeInsets.all(4), 144 | this.backgroundColor = Colors.black, 145 | this.selectedColor = Colors.white, 146 | this.nestedSelectedColor = Colors.black, 147 | this.selectedTextColor = Colors.black, 148 | this.unselectedTextColor = Colors.white, 149 | this.nestedSelectedTextColor = Colors.white, 150 | this.nestedUnselectedTextColor = Colors.black, 151 | this.textStyle, 152 | this.mainAnimationDuration = const Duration(milliseconds: 250), 153 | this.nestedAnimationDuration = const Duration(milliseconds: 250), 154 | this.switchAnimationDuration = const Duration(milliseconds: 250), 155 | this.animationCurve = Curves.easeInOut, 156 | this.switchScaleInCurve = Curves.easeOutCubic, 157 | this.switchScaleOutCurve = Curves.easeInCubic, 158 | this.switchFadeInCurve = Curves.easeIn, 159 | this.switchFadeOutCurve = Curves.easeOut, 160 | this.switchSlideOutCurve = Curves.easeOut, 161 | }) : assert(groupTabOptions.length >= 2, 162 | 'groupTabOptions must have at least two items for the default subtitle display'); 163 | 164 | @override 165 | State> createState() => 166 | _MorphingSegmentedControlState(); 167 | } 168 | 169 | class _MorphingSegmentedControlState 170 | extends State> with TickerProviderStateMixin { 171 | late AnimationController _switchController; 172 | bool _wasGroupTabSelected = false; 173 | 174 | bool get _isCurrentlyGroupTabSelected => 175 | widget.groupTabOptions.any((option) => option.value == widget.value); 176 | 177 | @override 178 | void initState() { 179 | super.initState(); 180 | _wasGroupTabSelected = _isCurrentlyGroupTabSelected; 181 | _switchController = AnimationController( 182 | vsync: this, 183 | duration: widget.switchAnimationDuration, 184 | value: _wasGroupTabSelected ? 1.0 : 0.0, 185 | ); 186 | } 187 | 188 | @override 189 | void didUpdateWidget(MorphingSegmentedControl oldWidget) { 190 | super.didUpdateWidget(oldWidget); 191 | 192 | final bool isNowGroupSelected = _isCurrentlyGroupTabSelected; 193 | 194 | if (_wasGroupTabSelected != isNowGroupSelected) { 195 | if (isNowGroupSelected) { 196 | _switchController.forward(); 197 | } else { 198 | _switchController.reverse(); 199 | } 200 | _wasGroupTabSelected = isNowGroupSelected; 201 | } 202 | 203 | if (widget.switchAnimationDuration != oldWidget.switchAnimationDuration) { 204 | _switchController.duration = widget.switchAnimationDuration; 205 | } 206 | } 207 | 208 | @override 209 | void dispose() { 210 | _switchController.dispose(); 211 | super.dispose(); 212 | } 213 | 214 | Map _calculateMainThumbMetrics(double maxWidth) { 215 | if (maxWidth <= 0) return {'left': 0.0, 'width': 0.0}; 216 | double availableWidth = math.max(0, maxWidth - widget.padding.horizontal); 217 | double thumbWidth = availableWidth / 2; 218 | double thumbLeft = widget.padding.left; 219 | if (_isCurrentlyGroupTabSelected) { 220 | thumbLeft = widget.padding.left + (availableWidth / 2); 221 | } 222 | return {'left': thumbLeft, 'width': thumbWidth}; 223 | } 224 | 225 | Widget _buildSimpleTabView({ 226 | required TextStyle effectiveTextStyle, 227 | required Color simpleTextColor, 228 | }) { 229 | return Expanded( 230 | child: GestureDetector( 231 | behavior: HitTestBehavior.opaque, 232 | onTap: () => widget.onChanged(widget.simpleTabValue), 233 | child: Container( 234 | alignment: Alignment.center, 235 | child: Text( 236 | widget.simpleTabLabel, 237 | style: effectiveTextStyle.copyWith(color: simpleTextColor), 238 | maxLines: 1, 239 | overflow: TextOverflow.ellipsis, 240 | ), 241 | ), 242 | ), 243 | ); 244 | } 245 | 246 | Widget _buildGroupTabAnimatedSwitcher({ 247 | required TextStyle effectiveTextStyle, 248 | }) { 249 | return Expanded( 250 | child: AnimatedBuilder( 251 | animation: _switchController, 252 | builder: (context, _) { 253 | final double controllerValue = _switchController.value; 254 | 255 | final double collapsedOpacity = (1.0 - 256 | CurvedAnimation( 257 | parent: _switchController, 258 | curve: widget.switchFadeOutCurve, 259 | ).value) 260 | .clamp(0.0, 1.0); 261 | final double collapsedScale = 1.0 - 262 | (CurvedAnimation( 263 | parent: _switchController, 264 | curve: widget.switchScaleOutCurve) 265 | .value * 266 | 0.4); 267 | final Offset collapsedSlide = 268 | Tween(begin: Offset.zero, end: const Offset(0.0, -0.5)) 269 | .transform(CurvedAnimation( 270 | parent: _switchController, 271 | curve: widget.switchSlideOutCurve) 272 | .value); 273 | final double expandedOpacity = CurvedAnimation( 274 | parent: _switchController, curve: widget.switchFadeInCurve) 275 | .value 276 | .clamp(0.0, 1.0); 277 | final double expandedScale = 0.6 + 278 | (CurvedAnimation( 279 | parent: _switchController, 280 | curve: widget.switchScaleInCurve) 281 | .value * 282 | 0.4); 283 | 284 | return Stack( 285 | alignment: Alignment.center, 286 | children: [ 287 | if (controllerValue < 1.0) 288 | _buildCollapsedGroupView( 289 | effectiveTextStyle: effectiveTextStyle, 290 | opacity: collapsedOpacity, 291 | scale: collapsedScale, 292 | slideOffset: collapsedSlide, 293 | ), 294 | if (controllerValue > 0.0) 295 | _buildExpandedGroupView( 296 | effectiveTextStyle: effectiveTextStyle, 297 | opacity: expandedOpacity, 298 | scale: expandedScale, 299 | ), 300 | ], 301 | ); 302 | }, 303 | ), 304 | ); 305 | } 306 | 307 | Widget _buildCollapsedGroupView({ 308 | required TextStyle effectiveTextStyle, 309 | required double opacity, 310 | required double scale, 311 | required Offset slideOffset, 312 | }) { 313 | final Color groupCollapsedTextColor = widget.unselectedTextColor; 314 | final String subtitleText1 = widget.groupTabOptions.isNotEmpty 315 | ? widget.groupTabOptions[0].label 316 | : ''; 317 | final String subtitleText2 = widget.groupTabOptions.length > 1 318 | ? widget.groupTabOptions[1].label 319 | : ''; 320 | final double subtitleFontSize = 321 | math.max(10.0, (effectiveTextStyle.fontSize ?? 14.0) * 0.7); 322 | final Color subtitleColor = groupCollapsedTextColor.withValues(alpha: 0.6); 323 | final TextStyle subtitleStyle = effectiveTextStyle.copyWith( 324 | color: subtitleColor, 325 | height: 1.0, 326 | fontSize: subtitleFontSize, 327 | fontWeight: FontWeight.w400); 328 | 329 | return IgnorePointer( 330 | ignoring: opacity < 0.15, 331 | child: Opacity( 332 | opacity: opacity, 333 | child: Transform.translate( 334 | offset: slideOffset, 335 | child: Transform.scale( 336 | scale: scale, 337 | child: GestureDetector( 338 | behavior: HitTestBehavior.opaque, 339 | onTap: () { 340 | if (!_isCurrentlyGroupTabSelected && 341 | widget.groupTabOptions.isNotEmpty) { 342 | widget.onChanged(widget.groupTabOptions.first.value); 343 | } 344 | }, 345 | child: Container( 346 | alignment: Alignment.center, 347 | padding: const EdgeInsets.symmetric(horizontal: 4.0), 348 | child: Column( 349 | mainAxisAlignment: MainAxisAlignment.center, 350 | mainAxisSize: MainAxisSize.min, 351 | children: [ 352 | Text( 353 | widget.groupTabCollapsedLabel, 354 | style: effectiveTextStyle.copyWith( 355 | color: groupCollapsedTextColor, height: 1.1), 356 | maxLines: 1, 357 | overflow: TextOverflow.ellipsis, 358 | textAlign: TextAlign.center, 359 | ), 360 | const SizedBox(height: 1), 361 | Row( 362 | mainAxisAlignment: MainAxisAlignment.center, 363 | mainAxisSize: MainAxisSize.min, 364 | children: [ 365 | Flexible( 366 | child: Text(subtitleText1, 367 | style: subtitleStyle, 368 | maxLines: 1, 369 | overflow: TextOverflow.ellipsis)), 370 | Padding( 371 | padding: const EdgeInsets.symmetric(horizontal: 2.0), 372 | child: Text(' - ', style: subtitleStyle), 373 | ), 374 | Flexible( 375 | child: Text(subtitleText2, 376 | style: subtitleStyle, 377 | maxLines: 1, 378 | overflow: TextOverflow.ellipsis)), 379 | ], 380 | ), 381 | ], 382 | ), 383 | ), 384 | ), 385 | ), 386 | ), 387 | ), 388 | ); 389 | } 390 | 391 | Widget _buildExpandedGroupView({ 392 | required TextStyle effectiveTextStyle, 393 | required double opacity, 394 | required double scale, 395 | }) { 396 | return IgnorePointer( 397 | ignoring: opacity < 0.15, 398 | child: Opacity( 399 | opacity: opacity, 400 | child: Transform.scale( 401 | scale: scale, 402 | alignment: const Alignment(0.0, 0.3), // Align lower during scale 403 | child: _NestedTabBar( 404 | selectedTabValue: widget.value, 405 | options: widget.groupTabOptions, 406 | onChanged: widget.onChanged, 407 | parentHeight: widget.height - widget.padding.vertical, 408 | nestedThumbMargin: widget.nestedThumbMargin, 409 | animationDuration: widget.nestedAnimationDuration, 410 | animationCurve: widget.animationCurve, 411 | selectedColor: widget.nestedSelectedColor, 412 | selectedTextColor: widget.nestedSelectedTextColor, 413 | unselectedTextColor: widget.nestedUnselectedTextColor, 414 | textStyle: effectiveTextStyle, 415 | ), 416 | ), 417 | ), 418 | ); 419 | } 420 | 421 | @override 422 | Widget build(BuildContext context) { 423 | final double mainThumbEffectiveHeight = math.max( 424 | 0, 425 | widget.height - 426 | widget.padding.vertical - 427 | widget.mainThumbMargin.vertical); 428 | final double mainThumbBorderRadius = mainThumbEffectiveHeight / 2; 429 | final TextStyle defaultTextStyle = widget.textStyle ?? 430 | Theme.of(context).textTheme.bodyMedium ?? 431 | const TextStyle(); 432 | final TextStyle effectiveTextStyle = 433 | defaultTextStyle.copyWith(fontWeight: FontWeight.bold); 434 | 435 | final Color simpleTextColor = widget.value == widget.simpleTabValue 436 | ? widget.selectedTextColor 437 | : widget.unselectedTextColor; 438 | 439 | return Container( 440 | height: widget.height, 441 | width: widget.width, 442 | clipBehavior: Clip.antiAlias, 443 | decoration: BoxDecoration( 444 | color: widget.backgroundColor, 445 | borderRadius: BorderRadius.circular(widget.height / 2), 446 | ), 447 | child: LayoutBuilder( 448 | builder: (context, constraints) { 449 | final double maxWidth = constraints.maxWidth; 450 | if (maxWidth <= 0 || maxWidth.isInfinite) { 451 | return const SizedBox.shrink(); 452 | } 453 | 454 | final mainThumbMetrics = _calculateMainThumbMetrics(maxWidth); 455 | final double mainThumbLeft = mainThumbMetrics['left']!; 456 | final double mainThumbWidth = mainThumbMetrics['width']!; 457 | 458 | return Stack( 459 | alignment: Alignment.centerLeft, 460 | children: [ 461 | AnimatedPositioned( 462 | duration: widget.mainAnimationDuration, 463 | curve: widget.animationCurve, 464 | left: mainThumbLeft, 465 | top: widget.padding.top, 466 | bottom: widget.padding.bottom, 467 | width: mainThumbWidth, 468 | child: Container( 469 | margin: widget.mainThumbMargin, 470 | decoration: BoxDecoration( 471 | color: widget.selectedColor, 472 | borderRadius: BorderRadius.circular( 473 | mainThumbBorderRadius > 0 ? mainThumbBorderRadius : 0), 474 | ), 475 | ), 476 | ), 477 | Padding( 478 | padding: widget.padding, 479 | child: Row( 480 | children: [ 481 | _buildSimpleTabView( 482 | effectiveTextStyle: effectiveTextStyle, 483 | simpleTextColor: simpleTextColor, 484 | ), 485 | _buildGroupTabAnimatedSwitcher( 486 | effectiveTextStyle: effectiveTextStyle, 487 | ), 488 | ], 489 | ), 490 | ), 491 | ], 492 | ); 493 | }, 494 | ), 495 | ); 496 | } 497 | } 498 | 499 | class _NestedTabBar extends StatelessWidget { 500 | final T selectedTabValue; 501 | final List> options; 502 | final ValueChanged onChanged; 503 | 504 | final double parentHeight; 505 | final EdgeInsets nestedThumbMargin; 506 | final Duration animationDuration; 507 | final Curve animationCurve; 508 | final Color selectedColor; 509 | final Color selectedTextColor; 510 | final Color unselectedTextColor; 511 | final TextStyle? textStyle; 512 | 513 | const _NestedTabBar({ 514 | super.key, 515 | required this.selectedTabValue, 516 | required this.options, 517 | required this.onChanged, 518 | required this.parentHeight, 519 | required this.nestedThumbMargin, 520 | required this.animationDuration, 521 | required this.animationCurve, 522 | required this.selectedColor, 523 | required this.selectedTextColor, 524 | required this.unselectedTextColor, 525 | this.textStyle, 526 | }); 527 | 528 | Map _calculateInternalThumbMetrics(double maxWidth) { 529 | if (options.isEmpty || maxWidth <= 0) { 530 | return {'left': 0.0, 'width': 0.0}; 531 | } 532 | int selectedIndex = options.indexWhere((o) => o.value == selectedTabValue); 533 | if (selectedIndex < 0) selectedIndex = 0; 534 | 535 | double totalOptions = options.length.toDouble(); 536 | double thumbWidth = maxWidth / totalOptions; 537 | double thumbLeft = thumbWidth * selectedIndex; 538 | 539 | thumbLeft = math.max(0, thumbLeft); 540 | thumbWidth = math.min(thumbWidth, maxWidth - thumbLeft); 541 | 542 | return {'left': thumbLeft, 'width': thumbWidth}; 543 | } 544 | 545 | @override 546 | Widget build(BuildContext context) { 547 | final double thumbEffectiveHeight = 548 | math.max(0, parentHeight - nestedThumbMargin.vertical); 549 | final double thumbBorderRadius = thumbEffectiveHeight / 2; 550 | 551 | return LayoutBuilder(builder: (context, constraints) { 552 | final double maxWidth = constraints.maxWidth; 553 | if (maxWidth <= 0 || maxWidth.isInfinite || options.isEmpty) { 554 | return const SizedBox.shrink(); 555 | } 556 | 557 | final internalThumbMetrics = _calculateInternalThumbMetrics(maxWidth); 558 | final double thumbLeft = internalThumbMetrics['left']!; 559 | final double thumbWidth = internalThumbMetrics['width']!; 560 | 561 | return Stack( 562 | alignment: Alignment.centerLeft, 563 | children: [ 564 | if (thumbWidth > 0) 565 | AnimatedPositioned( 566 | duration: animationDuration, 567 | curve: animationCurve, 568 | left: thumbLeft, 569 | top: 0, 570 | bottom: 0, 571 | width: thumbWidth, 572 | child: Container( 573 | margin: nestedThumbMargin, 574 | decoration: BoxDecoration( 575 | color: selectedColor, 576 | borderRadius: BorderRadius.circular( 577 | thumbBorderRadius > 0 ? thumbBorderRadius : 0, 578 | ), 579 | ), 580 | ), 581 | ), 582 | Row( 583 | children: List.generate(options.length, (index) { 584 | final item = options[index]; 585 | final bool isSelected = item.value == selectedTabValue; 586 | final Color textColor = 587 | isSelected ? selectedTextColor : unselectedTextColor; 588 | 589 | return Expanded( 590 | child: GestureDetector( 591 | behavior: HitTestBehavior.opaque, 592 | onTap: () => onChanged(item.value), 593 | child: Container( 594 | alignment: Alignment.center, 595 | child: Text( 596 | item.label, 597 | style: textStyle?.copyWith(color: textColor) ?? 598 | TextStyle( 599 | color: textColor, 600 | fontWeight: FontWeight.bold, 601 | ), 602 | maxLines: 1, 603 | overflow: TextOverflow.ellipsis, 604 | ), 605 | ), 606 | ), 607 | ); 608 | }), 609 | ), 610 | ], 611 | ); 612 | }); 613 | } 614 | } 615 | -------------------------------------------------------------------------------- /lib/animations/segmented_bar_flip.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | 3 | import 'package:flutter/material.dart'; 4 | 5 | const kSegmentBarHeight = 45.0; 6 | const color = Colors.black; 7 | const images = [ 8 | 'https://images.unsplash.com/photo-1516280030429-27679b3dc9cf?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Mnx8Y2F0c3xlbnwwfHwwfHx8MA%3D%3D', 9 | 'https://images.unsplash.com/photo-1536590158209-e9d615d525e4?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8NHx8Y2F0c3xlbnwwfHwwfHx8MA%3D%3D', 10 | 'https://images.unsplash.com/photo-1536589961747-e239b2abbec2?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8Nnx8Y2F0c3xlbnwwfHwwfHx8MA%3D%3D', 11 | 'https://images.unsplash.com/photo-1518791841217-8f162f1e1131?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTB8fGNhdHN8ZW58MHx8MHx8fDA%3D', 12 | 'https://images.unsplash.com/photo-1548366086-7f1b76106622?w=500&auto=format&fit=crop&q=60&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxzZWFyY2h8MTV8fGNhdHN8ZW58MHx8MHx8fDA%3D', 13 | ]; 14 | 15 | class SegmentedBarFlip extends StatefulWidget { 16 | const SegmentedBarFlip({super.key}); 17 | 18 | @override 19 | State createState() => _SegmentedBarFlipState(); 20 | } 21 | 22 | class _SegmentedBarFlipState extends State { 23 | int _selectedIndex = 0; 24 | 25 | void _onTabChanged(int index) { 26 | setState(() { 27 | _selectedIndex = index; 28 | }); 29 | } 30 | 31 | @override 32 | Widget build(BuildContext context) { 33 | return Theme( 34 | data: ThemeData( 35 | primarySwatch: Colors.blue, 36 | brightness: Brightness.light, 37 | ), 38 | child: Scaffold( 39 | body: Padding( 40 | padding: const EdgeInsets.symmetric(horizontal: 25), 41 | child: Column( 42 | crossAxisAlignment: CrossAxisAlignment.start, 43 | children: [ 44 | const SizedBox(height: kToolbarHeight + 10), 45 | Center( 46 | child: CustomSegmentBar( 47 | onTabChanged: _onTabChanged, 48 | ), 49 | ), 50 | const SizedBox(height: 50), 51 | const Text('Recent', style: TextStyle(fontSize: 20)), 52 | const SizedBox(height: 15), 53 | Flexible( 54 | child: AnimatedSwitcher( 55 | duration: const Duration(milliseconds: 500), 56 | switchInCurve: Curves.easeInOut, 57 | switchOutCurve: Curves.easeInOut, 58 | child: 59 | _selectedIndex == 0 ? _buildGridView() : _buildListView(), 60 | ), 61 | ), 62 | ], 63 | ), 64 | ), 65 | ), 66 | ); 67 | } 68 | 69 | Widget _buildGridView() { 70 | return GridView.builder( 71 | padding: EdgeInsets.zero, 72 | physics: const ClampingScrollPhysics(), 73 | gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( 74 | crossAxisCount: 3, 75 | crossAxisSpacing: 10, 76 | mainAxisSpacing: 10, 77 | ), 78 | itemCount: 15, 79 | itemBuilder: (context, index) { 80 | return ClipRRect( 81 | borderRadius: BorderRadius.circular(10), 82 | child: Image.network( 83 | images[index % images.length], 84 | height: 100, 85 | width: 100, 86 | fit: BoxFit.cover, 87 | ), 88 | ); 89 | }, 90 | ); 91 | } 92 | 93 | Widget _buildListView() { 94 | return ListView.separated( 95 | padding: EdgeInsets.zero, 96 | physics: const ClampingScrollPhysics(), 97 | itemCount: 5, 98 | itemBuilder: (context, index) { 99 | return Row( 100 | children: [ 101 | ClipRRect( 102 | borderRadius: BorderRadius.circular(10), 103 | child: Image.network( 104 | images[index % images.length], 105 | height: 100, 106 | width: 100, 107 | fit: BoxFit.cover, 108 | ), 109 | ), 110 | const SizedBox(width: 10), 111 | const Column( 112 | crossAxisAlignment: CrossAxisAlignment.start, 113 | children: [ 114 | SizedBox(height: 10), 115 | Text( 116 | 'AUD_000012374Y74WA000', 117 | style: TextStyle(fontSize: 15), 118 | ), 119 | SizedBox(height: 5), 120 | Text( 121 | '34KB', 122 | style: TextStyle(fontSize: 15, color: Colors.deepPurple), 123 | ), 124 | SizedBox(height: 5), 125 | Text( 126 | '31/99/33', 127 | style: TextStyle(fontSize: 15, color: Colors.grey), 128 | ), 129 | ], 130 | ), 131 | ], 132 | ); 133 | }, 134 | separatorBuilder: (context, index) => const SizedBox(height: 10), 135 | ); 136 | } 137 | } 138 | 139 | class CustomSegmentBar extends StatefulWidget { 140 | final Function(int) onTabChanged; 141 | const CustomSegmentBar({super.key, required this.onTabChanged}); 142 | 143 | @override 144 | State createState() => _CustomSegmentBarState(); 145 | } 146 | 147 | class _CustomSegmentBarState extends State { 148 | int _selectedIndex = 0; 149 | bool _showInitialFill = true; 150 | 151 | void _onTap(int index) { 152 | setState(() { 153 | _selectedIndex = index; 154 | _showInitialFill = false; 155 | }); 156 | widget.onTabChanged(index); 157 | } 158 | 159 | @override 160 | Widget build(BuildContext context) { 161 | return Row( 162 | mainAxisAlignment: MainAxisAlignment.center, 163 | children: [ 164 | Segment( 165 | text: "Photos", 166 | isSelected: _selectedIndex == 0, 167 | onTap: () => _onTap(0), 168 | isLeftSegment: true, 169 | isInitiallyFilled: _showInitialFill && _selectedIndex == 0, 170 | ), 171 | Segment( 172 | text: "Videos", 173 | isSelected: _selectedIndex == 1, 174 | onTap: () => _onTap(1), 175 | isLeftSegment: false, 176 | isInitiallyFilled: false, 177 | ), 178 | ], 179 | ); 180 | } 181 | } 182 | 183 | class Segment extends StatefulWidget { 184 | final String text; 185 | final bool isSelected; 186 | final VoidCallback onTap; 187 | final bool isLeftSegment; 188 | final bool isInitiallyFilled; 189 | 190 | const Segment({ 191 | required this.text, 192 | required this.isSelected, 193 | required this.onTap, 194 | required this.isLeftSegment, 195 | required this.isInitiallyFilled, 196 | super.key, 197 | }); 198 | 199 | @override 200 | State createState() => _SegmentState(); 201 | } 202 | 203 | class _SegmentState extends State with SingleTickerProviderStateMixin { 204 | late AnimationController _controller; 205 | late Animation _animation; 206 | 207 | @override 208 | void initState() { 209 | super.initState(); 210 | _controller = AnimationController( 211 | duration: const Duration(milliseconds: 500), 212 | vsync: this, 213 | ); 214 | _animation = Tween(begin: 1, end: 0).animate( 215 | CurvedAnimation(parent: _controller, curve: Curves.easeInOut), 216 | ); 217 | 218 | if (widget.isSelected) { 219 | _controller.value = 0; 220 | } 221 | } 222 | 223 | @override 224 | void didUpdateWidget(covariant Segment oldWidget) { 225 | super.didUpdateWidget(oldWidget); 226 | if (widget.isSelected != oldWidget.isSelected && widget.isSelected) { 227 | _controller.forward(from: 0); 228 | } else if (!widget.isSelected && oldWidget.isSelected) { 229 | _controller.reverse(from: 1); 230 | } 231 | } 232 | 233 | @override 234 | void dispose() { 235 | _controller.dispose(); 236 | super.dispose(); 237 | } 238 | 239 | @override 240 | Widget build(BuildContext context) { 241 | return GestureDetector( 242 | onTap: widget.onTap, 243 | child: Stack( 244 | children: [ 245 | // Background for all segments with initial fill logic 246 | Container( 247 | width: 150, 248 | height: kSegmentBarHeight, 249 | alignment: Alignment.center, 250 | decoration: BoxDecoration( 251 | color: widget.isInitiallyFilled ? color : Colors.transparent, 252 | borderRadius: widget.isLeftSegment 253 | ? const BorderRadius.only( 254 | topLeft: Radius.circular(100), 255 | bottomLeft: Radius.circular(100), 256 | ) 257 | : const BorderRadius.only( 258 | topRight: Radius.circular(100), 259 | bottomRight: Radius.circular(100), 260 | ), 261 | border: Border.all( 262 | color: color, 263 | width: 3, 264 | ), 265 | ), 266 | child: Text( 267 | widget.text, 268 | style: TextStyle( 269 | fontSize: 20, 270 | color: widget.isInitiallyFilled ? Colors.white : color, 271 | fontWeight: FontWeight.w600, 272 | ), 273 | ), 274 | ), 275 | AnimatedBuilder( 276 | animation: _animation, 277 | builder: (context, child) { 278 | final double rotationAngle = widget.isSelected 279 | ? _animation.value * math.pi / 2 280 | : (1 - _animation.value) * math.pi / 2; 281 | 282 | return Transform( 283 | alignment: widget.isLeftSegment 284 | ? Alignment.centerRight 285 | : Alignment.centerLeft, 286 | transform: Matrix4.identity() 287 | ..setEntry(3, 2, 0.001) 288 | ..rotateY( 289 | widget.isLeftSegment ? -rotationAngle : rotationAngle), 290 | child: Container( 291 | width: 150, 292 | height: kSegmentBarHeight, 293 | alignment: Alignment.center, 294 | decoration: BoxDecoration( 295 | color: widget.isSelected ? color : Colors.transparent, 296 | borderRadius: widget.isLeftSegment 297 | ? const BorderRadius.only( 298 | topLeft: Radius.circular(100), 299 | bottomLeft: Radius.circular(100), 300 | ) 301 | : const BorderRadius.only( 302 | topRight: Radius.circular(100), 303 | bottomRight: Radius.circular(100), 304 | ), 305 | ), 306 | child: Text( 307 | widget.text, 308 | style: TextStyle( 309 | fontSize: 20, 310 | color: 311 | widget.isSelected ? Colors.white : Colors.transparent, 312 | fontWeight: FontWeight.w600, 313 | ), 314 | ), 315 | ), 316 | ); 317 | }, 318 | ), 319 | ], 320 | ), 321 | ); 322 | } 323 | } 324 | -------------------------------------------------------------------------------- /lib/animations_list.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | List animationList = [ 4 | AnimationPage( 5 | title: 'Fading Grid', 6 | route: '/fading-grid', 7 | icon: Icons.grid_view_rounded, 8 | ), 9 | AnimationPage( 10 | title: 'Grid Magnification', 11 | route: '/grid-magnification', 12 | icon: Icons.grid_view_rounded, 13 | ), 14 | AnimationPage( 15 | title: '3D Card', 16 | route: '/3d-card', 17 | icon: Icons.threed_rotation, 18 | ), 19 | AnimationPage( 20 | title: 'Bouncing DVD', 21 | route: '/bouncing-dvd', 22 | icon: Icons.animation, 23 | ), 24 | AnimationPage( 25 | title: 'Clock Loader', 26 | route: '/clock-loader', 27 | icon: Icons.access_time_rounded, 28 | ), 29 | AnimationPage( 30 | title: 'Rain', 31 | route: '/rain', 32 | icon: Icons.thunderstorm_outlined, 33 | ), 34 | AnimationPage( 35 | title: 'Fractal Pyramid Shader', 36 | route: '/pyramid-shader', 37 | icon: Icons.animation, 38 | ), 39 | AnimationPage( 40 | title: 'Metaball FAB', 41 | route: '/metaball-fab', 42 | icon: Icons.animation, 43 | ), 44 | AnimationPage( 45 | title: 'Simple Particle System', 46 | route: '/simple-particle-system', 47 | icon: Icons.snowing, 48 | ), 49 | AnimationPage( 50 | title: 'The Bouncing Ball', 51 | route: '/bouncing-ball', 52 | icon: Icons.sports_baseball, 53 | ), 54 | AnimationPage( 55 | title: 'Particle System with emitters', 56 | route: '/particle-system-with-emitters', 57 | icon: Icons.snowing, 58 | ), 59 | AnimationPage( 60 | title: 'Painting with Pixels', 61 | route: '/painting-with-pixels', 62 | icon: Icons.format_paint, 63 | ), 64 | AnimationPage( 65 | title: 'Segmented bar Flip animation', 66 | route: '/segmented-bar-flip-animation', 67 | icon: Icons.tab, 68 | ), 69 | AnimationPage( 70 | title: 'MorphingSegmentedControl', 71 | route: '/morphing-segmented-control', 72 | icon: Icons.animation, 73 | ), 74 | ]; 75 | 76 | class AnimationPage { 77 | final String title; 78 | final String route; 79 | final IconData icon; 80 | 81 | AnimationPage({required this.title, required this.route, required this.icon}); 82 | } 83 | -------------------------------------------------------------------------------- /lib/buildcontext_extension.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | extension BuildContextExtension on BuildContext { 4 | double get screenWidth => MediaQuery.sizeOf(this).width; 5 | double get screenHeight => MediaQuery.sizeOf(this).height; 6 | 7 | double get screenWidthFraction => screenWidth / 100; 8 | 9 | bool get isMobile => screenWidth < 600; 10 | } 11 | -------------------------------------------------------------------------------- /lib/list_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_playground/animations_list.dart'; 2 | import 'package:flutter/material.dart'; 3 | 4 | class ListPage extends StatelessWidget { 5 | const ListPage({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Scaffold( 10 | appBar: AppBar( 11 | title: const Text('Animation Playground'), 12 | elevation: 0, 13 | actions: [ 14 | IconButton( 15 | onPressed: () { 16 | showAboutDialog( 17 | context: context, 18 | children: [ 19 | const Text( 20 | 'This is a project where i basically dump some animations ' 21 | 'i work on from time to time. I hope it inspires and ' 22 | 'help people with Flutter animations.', 23 | ), 24 | const SizedBox(height: 15), 25 | const Row( 26 | mainAxisAlignment: MainAxisAlignment.end, 27 | children: [ 28 | Text( 29 | '- JideGuru 💙', 30 | style: TextStyle(fontWeight: FontWeight.bold), 31 | ), 32 | ], 33 | ), 34 | ], 35 | ); 36 | }, 37 | icon: const Icon(Icons.info_outline), 38 | ), 39 | ], 40 | ), 41 | body: ListView.builder( 42 | itemCount: animationList.length, 43 | itemBuilder: (BuildContext context, int index) { 44 | AnimationPage page = animationList[index]; 45 | return ListTile( 46 | leading: Icon(page.icon), 47 | title: Text(page.title), 48 | onTap: () { 49 | Navigator.pushNamed(context, page.route); 50 | }, 51 | ); 52 | }, 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_playground/routes.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:flutter_web_plugins/url_strategy.dart'; 4 | 5 | void main() { 6 | usePathUrlStrategy(); 7 | runApp(const MyApp()); 8 | } 9 | 10 | class MyApp extends StatelessWidget { 11 | const MyApp({super.key}); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return MaterialApp( 16 | title: 'Animation Playground', 17 | theme: ThemeData( 18 | primarySwatch: Colors.blue, 19 | brightness: Brightness.dark, 20 | ), 21 | initialRoute: '/', 22 | routes: routes, 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/random_extension.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math'; 2 | 3 | Random random = Random(); 4 | 5 | extension RandomExtension on Random { 6 | double fromRange(double min, double max) => 7 | (nextDouble() * (max - min + 1)).floor() + min; 8 | } -------------------------------------------------------------------------------- /lib/routes.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_playground/animations/segmented_bar_flip.dart'; 2 | import 'package:animation_playground/list_page.dart'; 3 | import 'package:animation_playground/shaders/barrel.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'animations/card/barrel.dart'; 7 | import 'animations/coding_train_challenges/barrel.dart'; 8 | import 'animations/grid_magnification/barrel.dart'; 9 | import 'animations/loaders/barrel.dart'; 10 | import 'animations/morphing_segmented_bar.dart'; 11 | 12 | Map routes = { 13 | '/': (context) => const ListPage(), 14 | '/fading-grid': (context) => const FadingGrid(), 15 | '/grid-magnification': (context) => const GridMagnification(), 16 | '/3d-card': (context) => const ThreeDimensionalCard(), 17 | '/bouncing-dvd': (context) => const BouncingDVD(), 18 | '/clock-loader': (context) => const ClockLoader(), 19 | '/rain': (context) => const RainAnimation(), 20 | '/pyramid-shader': (context) => const PyramidShader(), 21 | '/metaball-fab': (context) => const MetaballFAB(), 22 | '/simple-particle-system': (context) => const SimpleParticleSystem(), 23 | '/bouncing-ball': (context) => const BouncingBall(), 24 | '/particle-system-with-emitters': (context) => 25 | const ParticleSystemWithEmitters(), 26 | '/painting-with-pixels': (context) => const PaintingWithPixels(), 27 | '/segmented-bar-flip-animation': (context) => const SegmentedBarFlip(), 28 | '/morphing-segmented-control': (context) => 29 | const MorphingSegmentedControlPage(), 30 | }; 31 | -------------------------------------------------------------------------------- /lib/shaders/barrel.dart: -------------------------------------------------------------------------------- 1 | export 'fractal_pyramid.dart'; 2 | export 'metaball_fab.dart'; 3 | -------------------------------------------------------------------------------- /lib/shaders/fractal_pyramid.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/scheduler.dart'; 5 | import 'package:flutter_shaders/flutter_shaders.dart'; 6 | 7 | // This shader was taken from ShaderToy 8 | // Origin of Shader: https://www.shadertoy.com/view/tsXBzS 9 | class PyramidShader extends StatefulWidget { 10 | const PyramidShader({Key? key}) : super(key: key); 11 | 12 | @override 13 | State createState() => _PyramidShaderState(); 14 | } 15 | 16 | class _PyramidShaderState extends State 17 | with SingleTickerProviderStateMixin { 18 | double time = 0; 19 | 20 | late final Ticker _ticker; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | _ticker = createTicker((elapsed) { 26 | time += 0.015; 27 | setState(() {}); 28 | }); 29 | _ticker.start(); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | _ticker.dispose(); 35 | super.dispose(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | final size = MediaQuery.of(context).size; 41 | return Scaffold( 42 | appBar: AppBar( 43 | title: const Text('Fractal Pyramid Shader'), 44 | ), 45 | backgroundColor: Colors.black, 46 | body: ShaderBuilder( 47 | assetKey: 'shaders/pyramid.glsl', 48 | child: SizedBox(width: size.width, height: size.height), 49 | (context, shader, child) { 50 | return AnimatedSampler( 51 | child: child!, 52 | (ui.Image image, Size size, Canvas canvas) { 53 | shader 54 | ..setFloat(0, time) 55 | ..setFloat(1, size.width) 56 | ..setFloat(2, size.height); 57 | canvas.drawPaint(Paint()..shader = shader); 58 | }, 59 | ); 60 | }, 61 | ), 62 | ); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /lib/shaders/metaball_fab.dart: -------------------------------------------------------------------------------- 1 | import 'dart:ui' as ui; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter_shaders/flutter_shaders.dart'; 5 | 6 | class MetaballFAB extends StatefulWidget { 7 | const MetaballFAB({Key? key}) : super(key: key); 8 | 9 | @override 10 | State createState() => _MetaballFABState(); 11 | } 12 | 13 | class _MetaballFABState extends State 14 | with TickerProviderStateMixin { 15 | late Offset mainButtonPos; 16 | late Offset firstButtonPos; 17 | late Offset secondButtonPos; 18 | late final AnimationController _firstController; 19 | late final Animation _firstAnimation; 20 | late final AnimationController _secondController; 21 | late final Animation _secondAnimation; 22 | bool expanded = false; 23 | 24 | @override 25 | void initState() { 26 | super.initState(); 27 | var screenWidth = 28 | (ui.window.physicalSize.shortestSide / ui.window.devicePixelRatio); 29 | var screenHeight = 30 | (ui.window.physicalSize.longestSide / ui.window.devicePixelRatio); 31 | mainButtonPos = Offset(screenWidth / 2, screenHeight - 100); 32 | firstButtonPos = Offset(screenWidth / 2, screenHeight - 100); 33 | secondButtonPos = Offset(screenWidth / 2, screenHeight - 100); 34 | _firstController = AnimationController( 35 | // value: _open ? 1.0 : 0.0, 36 | duration: const Duration(milliseconds: 500), 37 | vsync: this, 38 | ); 39 | _firstAnimation = Tween( 40 | begin: firstButtonPos, 41 | end: Offset(firstButtonPos.dx, firstButtonPos.dy - 250), 42 | ).animate(_firstController); 43 | 44 | _secondController = AnimationController( 45 | // value: _open ? 1.0 : 0.0, 46 | duration: const Duration(milliseconds: 500), 47 | vsync: this, 48 | ); 49 | _secondAnimation = Tween( 50 | begin: secondButtonPos, 51 | end: Offset(secondButtonPos.dx, secondButtonPos.dy - 140), 52 | ).animate(_secondController); 53 | 54 | _firstController.addListener(() { 55 | setState(() {}); 56 | }); 57 | _secondController.addListener(() { 58 | setState(() {}); 59 | }); 60 | } 61 | 62 | @override 63 | Widget build(BuildContext context) { 64 | return Scaffold( 65 | appBar: AppBar( 66 | title: const Text('Metaball FAB'), 67 | ), 68 | backgroundColor: Colors.black, 69 | body: ShaderBuilder( 70 | assetKey: 'shaders/metaball_shader.frag', 71 | child: Stack( 72 | children: [ 73 | Positioned( 74 | top: firstButtonPos.dy, 75 | left: firstButtonPos.dx, 76 | child: Container( 77 | height: 40, 78 | width: 40, 79 | color: Colors.transparent, 80 | ), 81 | ), 82 | Positioned( 83 | top: mainButtonPos.dy -100, 84 | left: mainButtonPos.dx - 90, 85 | child: GestureDetector( 86 | onTap: () { 87 | if (expanded) { 88 | _firstController.reverse(); 89 | _secondController.reverse(); 90 | } else { 91 | _firstController.forward(); 92 | _secondController.forward(); 93 | } 94 | expanded = !expanded; 95 | }, 96 | child: Container( 97 | height: 150, 98 | width: 150, 99 | color: Colors.transparent, 100 | ), 101 | ), 102 | ), 103 | ], 104 | ), 105 | (context, shader, child) { 106 | return AnimatedSampler( 107 | child: child!, 108 | (ui.Image image, Size size, Canvas canvas) { 109 | shader 110 | ..setFloat(0, mainButtonPos.dx) 111 | ..setFloat(1, mainButtonPos.dy) 112 | ..setFloat(2, 90) 113 | ..setFloat(3, _firstAnimation.value.dx) 114 | ..setFloat(4, _firstAnimation.value.dy) 115 | ..setFloat(5, 40) 116 | ..setFloat(6, 3) 117 | ..setFloat(7, 1.9) 118 | ..setFloat(8, 10.0) 119 | ..setFloat(9, _secondAnimation.value.dx) 120 | ..setFloat(10, _secondAnimation.value.dy) 121 | ..setFloat(11, 40); 122 | canvas.drawRect( 123 | Offset.zero & size, 124 | Paint() 125 | ..color = Colors.white 126 | ..shader = shader, 127 | ); 128 | 129 | // canvas.drawPaint( 130 | // Paint() 131 | // ..shader = shader 132 | // ..color = Colors.black, 133 | // ); 134 | // canvas.drawCircle(dragOffset - Offset(0, 50), 20, Paint()..color = Colors.white); 135 | // canvas.drawCircle( 136 | // size.center(Offset.zero), 20, Paint()..color = Colors.white); 137 | }, 138 | ); 139 | }, 140 | ), 141 | ); 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /lib/utils.dart: -------------------------------------------------------------------------------- 1 | import 'dart:math' as math; 2 | import 'dart:math'; 3 | 4 | import 'package:flutter/material.dart'; 5 | 6 | List colors = [ 7 | Colors.green, 8 | Colors.blue, 9 | Colors.indigo, 10 | Colors.purple, 11 | Colors.pink, 12 | Colors.orange, 13 | Colors.yellow, 14 | Colors.greenAccent, 15 | Colors.green, 16 | ]; 17 | 18 | const fullAngleInRadians = math.pi * 2; 19 | 20 | double normalizeAngle(double angle) => normalize(angle, fullAngleInRadians); 21 | 22 | Offset toPolar(Offset center, double radians, double radius) => 23 | center + Offset.fromDirection(radians, radius); 24 | 25 | double normalize(double value, double max) => (value % max + max) % max; 26 | 27 | double toAngle(Offset position, Offset center) => (position - center).direction; 28 | 29 | double toRadian(double value) => (value * math.pi) / 180; 30 | 31 | /// Re-maps a number from one range to another 32 | /// See map Function in processing https://processing.org/reference/map_.html 33 | /// https://stackoverflow.com/a/5735770/10835183 34 | double rangeMap( 35 | double x, double inMin, double inMax, double outMin, double outMax) { 36 | return (x - inMin) * (outMax - outMin) / (inMax - inMin) + outMin; 37 | } 38 | 39 | Random _random = Random(); 40 | 41 | double doubleInRange(num start, num end) => 42 | _random.nextDouble() * (end - start) + start; 43 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/dgph 7 | **/xcuserdata/ 8 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/Flutter-Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "ephemeral/Flutter-Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /macos/Flutter/GeneratedPluginRegistrant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Generated file. Do not edit. 3 | // 4 | 5 | import FlutterMacOS 6 | import Foundation 7 | 8 | 9 | func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { 10 | } 11 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 52 | 54 | 60 | 61 | 62 | 63 | 69 | 71 | 77 | 78 | 79 | 80 | 82 | 83 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /macos/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | @NSApplicationMain 5 | class AppDelegate: FlutterAppDelegate { 6 | override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { 7 | return true 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "16x16", 5 | "idiom" : "mac", 6 | "filename" : "app_icon_16.png", 7 | "scale" : "1x" 8 | }, 9 | { 10 | "size" : "16x16", 11 | "idiom" : "mac", 12 | "filename" : "app_icon_32.png", 13 | "scale" : "2x" 14 | }, 15 | { 16 | "size" : "32x32", 17 | "idiom" : "mac", 18 | "filename" : "app_icon_32.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "32x32", 23 | "idiom" : "mac", 24 | "filename" : "app_icon_64.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "128x128", 29 | "idiom" : "mac", 30 | "filename" : "app_icon_128.png", 31 | "scale" : "1x" 32 | }, 33 | { 34 | "size" : "128x128", 35 | "idiom" : "mac", 36 | "filename" : "app_icon_256.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "256x256", 41 | "idiom" : "mac", 42 | "filename" : "app_icon_256.png", 43 | "scale" : "1x" 44 | }, 45 | { 46 | "size" : "256x256", 47 | "idiom" : "mac", 48 | "filename" : "app_icon_512.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "512x512", 53 | "idiom" : "mac", 54 | "filename" : "app_icon_512.png", 55 | "scale" : "1x" 56 | }, 57 | { 58 | "size" : "512x512", 59 | "idiom" : "mac", 60 | "filename" : "app_icon_1024.png", 61 | "scale" : "2x" 62 | } 63 | ], 64 | "info" : { 65 | "version" : 1, 66 | "author" : "xcode" 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png -------------------------------------------------------------------------------- /macos/Runner/Configs/AppInfo.xcconfig: -------------------------------------------------------------------------------- 1 | // Application-level settings for the Runner target. 2 | // 3 | // This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the 4 | // future. If not, the values below would default to using the project name when this becomes a 5 | // 'flutter create' template. 6 | 7 | // The application's name. By default this is also the title of the Flutter window. 8 | PRODUCT_NAME = animation_playground 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.jideguru.animationPlayground 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2023 com.jideguru. All rights reserved. 15 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Debug.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "../../Flutter/Flutter-Release.xcconfig" 2 | #include "Warnings.xcconfig" 3 | -------------------------------------------------------------------------------- /macos/Runner/Configs/Warnings.xcconfig: -------------------------------------------------------------------------------- 1 | WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings 2 | GCC_WARN_UNDECLARED_SELECTOR = YES 3 | CLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES 4 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE 5 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES 6 | CLANG_WARN_PRAGMA_PACK = YES 7 | CLANG_WARN_STRICT_PROTOTYPES = YES 8 | CLANG_WARN_COMMA = YES 9 | GCC_WARN_STRICT_SELECTOR_MATCH = YES 10 | CLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES 11 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES 12 | GCC_WARN_SHADOW = YES 13 | CLANG_WARN_UNREACHABLE_CODE = YES 14 | -------------------------------------------------------------------------------- /macos/Runner/DebugProfile.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.cs.allow-jit 8 | 9 | com.apple.security.network.server 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /macos/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | $(FLUTTER_BUILD_NAME) 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | NSHumanReadableCopyright 26 | $(PRODUCT_COPYRIGHT) 27 | NSMainNibFile 28 | MainMenu 29 | NSPrincipalClass 30 | NSApplication 31 | 32 | 33 | -------------------------------------------------------------------------------- /macos/Runner/MainFlutterWindow.swift: -------------------------------------------------------------------------------- 1 | import Cocoa 2 | import FlutterMacOS 3 | 4 | class MainFlutterWindow: NSWindow { 5 | override func awakeFromNib() { 6 | let flutterViewController = FlutterViewController.init() 7 | let windowFrame = self.frame 8 | self.contentViewController = flutterViewController 9 | self.setFrame(windowFrame, display: true) 10 | 11 | RegisterGeneratedPlugins(registry: flutterViewController) 12 | 13 | super.awakeFromNib() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /macos/Runner/Release.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /pubspec.lock: -------------------------------------------------------------------------------- 1 | # Generated by pub 2 | # See https://dart.dev/tools/pub/glossary#lockfile 3 | packages: 4 | archive: 5 | dependency: transitive 6 | description: 7 | name: archive 8 | sha256: "7dcbd0f87fe5f61cb28da39a1a8b70dbc106e2fe0516f7836eb7bb2948481a12" 9 | url: "https://pub.dev" 10 | source: hosted 11 | version: "4.0.5" 12 | async: 13 | dependency: transitive 14 | description: 15 | name: async 16 | sha256: d2872f9c19731c2e5f10444b14686eb7cc85c76274bd6c16e1816bff9a3bab63 17 | url: "https://pub.dev" 18 | source: hosted 19 | version: "2.12.0" 20 | boolean_selector: 21 | dependency: transitive 22 | description: 23 | name: boolean_selector 24 | sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea" 25 | url: "https://pub.dev" 26 | source: hosted 27 | version: "2.1.2" 28 | characters: 29 | dependency: transitive 30 | description: 31 | name: characters 32 | sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803 33 | url: "https://pub.dev" 34 | source: hosted 35 | version: "1.4.0" 36 | clock: 37 | dependency: transitive 38 | description: 39 | name: clock 40 | sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b 41 | url: "https://pub.dev" 42 | source: hosted 43 | version: "1.1.2" 44 | collection: 45 | dependency: transitive 46 | description: 47 | name: collection 48 | sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76" 49 | url: "https://pub.dev" 50 | source: hosted 51 | version: "1.19.1" 52 | crypto: 53 | dependency: transitive 54 | description: 55 | name: crypto 56 | sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855" 57 | url: "https://pub.dev" 58 | source: hosted 59 | version: "3.0.6" 60 | cupertino_icons: 61 | dependency: "direct main" 62 | description: 63 | name: cupertino_icons 64 | sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 65 | url: "https://pub.dev" 66 | source: hosted 67 | version: "1.0.8" 68 | fake_async: 69 | dependency: transitive 70 | description: 71 | name: fake_async 72 | sha256: "6a95e56b2449df2273fd8c45a662d6947ce1ebb7aafe80e550a3f68297f3cacc" 73 | url: "https://pub.dev" 74 | source: hosted 75 | version: "1.3.2" 76 | ffi: 77 | dependency: transitive 78 | description: 79 | name: ffi 80 | sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418" 81 | url: "https://pub.dev" 82 | source: hosted 83 | version: "2.1.4" 84 | flutter: 85 | dependency: "direct main" 86 | description: flutter 87 | source: sdk 88 | version: "0.0.0" 89 | flutter_lints: 90 | dependency: "direct dev" 91 | description: 92 | name: flutter_lints 93 | sha256: a25a15ebbdfc33ab1cd26c63a6ee519df92338a9c10f122adda92938253bef04 94 | url: "https://pub.dev" 95 | source: hosted 96 | version: "2.0.3" 97 | flutter_shaders: 98 | dependency: "direct main" 99 | description: 100 | name: flutter_shaders 101 | sha256: "1cf98f699701b520ed029c144f22c478c1f37f5784a4789b61d4fdbe95350a56" 102 | url: "https://pub.dev" 103 | source: hosted 104 | version: "0.0.6" 105 | flutter_test: 106 | dependency: "direct dev" 107 | description: flutter 108 | source: sdk 109 | version: "0.0.0" 110 | flutter_web_plugins: 111 | dependency: "direct main" 112 | description: flutter 113 | source: sdk 114 | version: "0.0.0" 115 | image: 116 | dependency: "direct main" 117 | description: 118 | name: image 119 | sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928" 120 | url: "https://pub.dev" 121 | source: hosted 122 | version: "4.5.4" 123 | leak_tracker: 124 | dependency: transitive 125 | description: 126 | name: leak_tracker 127 | sha256: c35baad643ba394b40aac41080300150a4f08fd0fd6a10378f8f7c6bc161acec 128 | url: "https://pub.dev" 129 | source: hosted 130 | version: "10.0.8" 131 | leak_tracker_flutter_testing: 132 | dependency: transitive 133 | description: 134 | name: leak_tracker_flutter_testing 135 | sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573 136 | url: "https://pub.dev" 137 | source: hosted 138 | version: "3.0.9" 139 | leak_tracker_testing: 140 | dependency: transitive 141 | description: 142 | name: leak_tracker_testing 143 | sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" 144 | url: "https://pub.dev" 145 | source: hosted 146 | version: "3.0.1" 147 | lints: 148 | dependency: transitive 149 | description: 150 | name: lints 151 | sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452" 152 | url: "https://pub.dev" 153 | source: hosted 154 | version: "2.1.1" 155 | matcher: 156 | dependency: transitive 157 | description: 158 | name: matcher 159 | sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2 160 | url: "https://pub.dev" 161 | source: hosted 162 | version: "0.12.17" 163 | material_color_utilities: 164 | dependency: transitive 165 | description: 166 | name: material_color_utilities 167 | sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec 168 | url: "https://pub.dev" 169 | source: hosted 170 | version: "0.11.1" 171 | meta: 172 | dependency: transitive 173 | description: 174 | name: meta 175 | sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c 176 | url: "https://pub.dev" 177 | source: hosted 178 | version: "1.16.0" 179 | path: 180 | dependency: transitive 181 | description: 182 | name: path 183 | sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5" 184 | url: "https://pub.dev" 185 | source: hosted 186 | version: "1.9.1" 187 | petitparser: 188 | dependency: transitive 189 | description: 190 | name: petitparser 191 | sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646" 192 | url: "https://pub.dev" 193 | source: hosted 194 | version: "6.1.0" 195 | posix: 196 | dependency: transitive 197 | description: 198 | name: posix 199 | sha256: a0117dc2167805aa9125b82eee515cc891819bac2f538c83646d355b16f58b9a 200 | url: "https://pub.dev" 201 | source: hosted 202 | version: "6.0.1" 203 | sky_engine: 204 | dependency: transitive 205 | description: flutter 206 | source: sdk 207 | version: "0.0.0" 208 | source_span: 209 | dependency: transitive 210 | description: 211 | name: source_span 212 | sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c" 213 | url: "https://pub.dev" 214 | source: hosted 215 | version: "1.10.1" 216 | stack_trace: 217 | dependency: transitive 218 | description: 219 | name: stack_trace 220 | sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1" 221 | url: "https://pub.dev" 222 | source: hosted 223 | version: "1.12.1" 224 | stream_channel: 225 | dependency: transitive 226 | description: 227 | name: stream_channel 228 | sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d" 229 | url: "https://pub.dev" 230 | source: hosted 231 | version: "2.1.4" 232 | string_scanner: 233 | dependency: transitive 234 | description: 235 | name: string_scanner 236 | sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43" 237 | url: "https://pub.dev" 238 | source: hosted 239 | version: "1.4.1" 240 | term_glyph: 241 | dependency: transitive 242 | description: 243 | name: term_glyph 244 | sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e" 245 | url: "https://pub.dev" 246 | source: hosted 247 | version: "1.2.2" 248 | test_api: 249 | dependency: transitive 250 | description: 251 | name: test_api 252 | sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd 253 | url: "https://pub.dev" 254 | source: hosted 255 | version: "0.7.4" 256 | typed_data: 257 | dependency: transitive 258 | description: 259 | name: typed_data 260 | sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 261 | url: "https://pub.dev" 262 | source: hosted 263 | version: "1.4.0" 264 | vector_math: 265 | dependency: transitive 266 | description: 267 | name: vector_math 268 | sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803" 269 | url: "https://pub.dev" 270 | source: hosted 271 | version: "2.1.4" 272 | vm_service: 273 | dependency: transitive 274 | description: 275 | name: vm_service 276 | sha256: "0968250880a6c5fe7edc067ed0a13d4bae1577fe2771dcf3010d52c4a9d3ca14" 277 | url: "https://pub.dev" 278 | source: hosted 279 | version: "14.3.1" 280 | xml: 281 | dependency: transitive 282 | description: 283 | name: xml 284 | sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226 285 | url: "https://pub.dev" 286 | source: hosted 287 | version: "6.5.0" 288 | sdks: 289 | dart: ">=3.7.0 <4.0.0" 290 | flutter: ">=3.18.0-18.0.pre.54" 291 | -------------------------------------------------------------------------------- /pubspec.yaml: -------------------------------------------------------------------------------- 1 | name: animation_playground 2 | description: A new Flutter project. 3 | 4 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev 5 | 6 | version: 1.0.0+1 7 | 8 | environment: 9 | sdk: '>=2.18.2 <3.0.0' 10 | 11 | dependencies: 12 | flutter: 13 | sdk: flutter 14 | flutter_web_plugins: 15 | sdk: flutter 16 | cupertino_icons: ^1.0.2 17 | flutter_shaders: ^0.0.6 18 | image: ^4.0.15 19 | 20 | dev_dependencies: 21 | flutter_test: 22 | sdk: flutter 23 | flutter_lints: ^2.0.0 24 | 25 | flutter: 26 | uses-material-design: true 27 | 28 | # To add assets to your application, add an assets section, like this: 29 | assets: 30 | - assets/images/ 31 | # - images/a_dot_ham.jpeg 32 | 33 | shaders: 34 | - shaders/metaball_shader.frag 35 | - shaders/pyramid.glsl 36 | 37 | # An image asset can refer to one or more resolution-specific "variants", see 38 | # https://flutter.dev/assets-and-images/#resolution-aware 39 | 40 | # For details regarding adding assets from package dependencies, see 41 | # https://flutter.dev/assets-and-images/#from-packages 42 | 43 | # To add custom fonts to your application, add a fonts section here, 44 | # in this "flutter" section. Each entry in this list should have a 45 | # "family" key with the font family name, and a "fonts" key with a 46 | # list giving the asset and other descriptors for the font. For 47 | # example: 48 | # fonts: 49 | # - family: Schyler 50 | # fonts: 51 | # - asset: fonts/Schyler-Regular.ttf 52 | # - asset: fonts/Schyler-Italic.ttf 53 | # style: italic 54 | # - family: Trajan Pro 55 | # fonts: 56 | # - asset: fonts/TrajanPro.ttf 57 | # - asset: fonts/TrajanPro_Bold.ttf 58 | # weight: 700 59 | # 60 | # For details regarding fonts from package dependencies, 61 | # see https://flutter.dev/custom-fonts/#from-packages 62 | -------------------------------------------------------------------------------- /shaders/metaball_shader.frag: -------------------------------------------------------------------------------- 1 | /// ref: https://github.com/T99Rots/flutter_metaballs/blob/main/lib/metaballs_shader.glsl 2 | 3 | #version 320 es 4 | 5 | precision highp float; 6 | 7 | out vec4 fragColor; 8 | 9 | uniform vec3 metaball1; 10 | uniform vec3 metaball2; 11 | uniform float metaballs; 12 | uniform float minimumGlowSum; 13 | uniform float glowIntensity; 14 | uniform vec3 metaball3; 15 | 16 | float addSum(vec3 metaball, vec2 coords) { 17 | float dx = metaball.x - coords.x; 18 | float dy = metaball.y - coords.y; 19 | float radius = metaball.z; 20 | return ((radius * radius) / (dx * dx + dy * dy)); 21 | } 22 | 23 | vec4 noise(vec4 v){ 24 | // ensure reasonable range 25 | v = fract(v) + fract(v*1e4) + fract(v*1e-4); 26 | // seed 27 | v += vec4(0.12345, 0.6789, 0.314159, 0.271828); 28 | // more iterations => more random 29 | v = fract(v*dot(v, v)*123.456); 30 | v = fract(v*dot(v, v)*123.456); 31 | return v; 32 | } 33 | 34 | float getSum(vec2 coords) { 35 | float sum = 0.0; 36 | if (metaballs < 1.0) return sum; 37 | sum += addSum(metaball1, coords); 38 | if (metaballs < 2.0) return sum; 39 | sum += addSum(metaball2, coords); 40 | if (metaballs < 3.0) return sum; 41 | sum += addSum(metaball3, coords); 42 | return sum; 43 | } 44 | 45 | void main() { 46 | vec2 coords = gl_FragCoord.xy; 47 | float time = 0.5; 48 | // float minimumGlowSum = 1.5; 49 | // float glowIntensity = 5.0; 50 | float sum = getSum(coords); 51 | 52 | // if (sum >= 1.0) { 53 | // fragColor = vec4(1,1,1,1); 54 | // } else if (sum > minimumGlowSum) { 55 | float n = ((sum - minimumGlowSum) / (1.0 - minimumGlowSum)) * glowIntensity; 56 | 57 | fragColor = vec4(n) + ((noise(vec4(coords, time, 0.0)) - 0.5) / 255.0); 58 | // } else { 59 | // fragColor = vec4(0, 0, 0, 0); 60 | // } 61 | } -------------------------------------------------------------------------------- /shaders/pyramid.glsl: -------------------------------------------------------------------------------- 1 | // This shader was taken from ShaderToy 2 | // Origin of Shader: https://www.shadertoy.com/view/tsXBzS 3 | #version 320 es 4 | 5 | precision highp float; 6 | uniform float iTime; 7 | uniform vec2 iResolution; 8 | 9 | out vec4 fragColor; 10 | 11 | vec3 palette(float d){ 12 | return mix(vec3(0.2,0.7,0.9),vec3(1.,0.,1.),d); 13 | } 14 | 15 | vec2 rotate(vec2 p,float a){ 16 | float c = cos(a); 17 | float s = sin(a); 18 | return p*mat2(c,s,-s,c); 19 | } 20 | 21 | float map(vec3 p){ 22 | for( int i = 0; i<8; ++i){ 23 | float t = iTime*0.2; 24 | p.xz =rotate(p.xz,t); 25 | p.xy =rotate(p.xy,t*1.89); 26 | p.xz = abs(p.xz); 27 | p.xz-=.5; 28 | } 29 | return dot(sign(p),p)/5.; 30 | } 31 | 32 | vec4 rm (vec3 ro, vec3 rd){ 33 | float t = 0.; 34 | vec3 col = vec3(0.); 35 | float d; 36 | for(float i =0.; i<64.; i++){ 37 | vec3 p = ro + rd*t; 38 | d = map(p)*.5; 39 | if(d<0.02){ 40 | break; 41 | } 42 | if(d>100.){ 43 | break; 44 | } 45 | //col+=vec3(0.6,0.8,0.8)/(400.*(d)); 46 | col+=palette(length(p)*.1)/(400.*(d)); 47 | t+=d; 48 | } 49 | return vec4(col,1./(d*100.)); 50 | } 51 | vec4 mainImage( in vec2 fragCoord ) 52 | { 53 | vec2 uv = (fragCoord-(iResolution.xy/2.))/iResolution.x; 54 | vec3 ro = vec3(0.,0.,-50.); 55 | ro.xz = rotate(ro.xz,iTime); 56 | vec3 cf = normalize(-ro); 57 | vec3 cs = normalize(cross(cf,vec3(0.,1.,0.))); 58 | vec3 cu = normalize(cross(cf,cs)); 59 | 60 | vec3 uuv = ro+cf*3. + uv.x*cs + uv.y*cu; 61 | 62 | vec3 rd = normalize(uuv-ro); 63 | 64 | vec4 col = rm(ro,rd); 65 | 66 | 67 | return col; 68 | } 69 | 70 | void main() { 71 | vec2 pos = gl_FragCoord.xy; 72 | // vec2 uv = pos / vec2(width, height); 73 | fragColor = mainImage(pos); 74 | } -------------------------------------------------------------------------------- /test/widget_test.dart: -------------------------------------------------------------------------------- 1 | // This is a basic Flutter widget test. 2 | // 3 | // To perform an interaction with a widget in your test, use the WidgetTester 4 | // utility in the flutter_test package. For example, you can send tap and scroll 5 | // gestures. You can also use WidgetTester to find child widgets in the widget 6 | // tree, read text, and verify that the values of widget properties are correct. 7 | 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter_test/flutter_test.dart'; 10 | 11 | import 'package:animation_playground/main.dart'; 12 | 13 | void main() { 14 | testWidgets('Counter increments smoke test', (WidgetTester tester) async { 15 | // Build our app and trigger a frame. 16 | await tester.pumpWidget(const MyApp()); 17 | 18 | // Verify that our counter starts at 0. 19 | expect(find.text('0'), findsOneWidget); 20 | expect(find.text('1'), findsNothing); 21 | 22 | // Tap the '+' icon and trigger a frame. 23 | await tester.tap(find.byIcon(Icons.add)); 24 | await tester.pump(); 25 | 26 | // Verify that our counter has incremented. 27 | expect(find.text('0'), findsNothing); 28 | expect(find.text('1'), findsOneWidget); 29 | }); 30 | } 31 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/web/icons/Icon-maskable-192.png -------------------------------------------------------------------------------- /web/icons/Icon-maskable-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JideGuru/animation_playground/b53a541c25039b11aadbabb5c173964242e980da/web/icons/Icon-maskable-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | animation_playground 33 | 34 | 35 | 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "animation_playground", 3 | "short_name": "animation_playground", 4 | "start_url": ".", 5 | "display": "standalone", 6 | "background_color": "#0175C2", 7 | "theme_color": "#0175C2", 8 | "description": "A new Flutter project.", 9 | "orientation": "portrait-primary", 10 | "prefer_related_applications": false, 11 | "icons": [ 12 | { 13 | "src": "icons/Icon-192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "icons/Icon-512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | }, 22 | { 23 | "src": "icons/Icon-maskable-192.png", 24 | "sizes": "192x192", 25 | "type": "image/png", 26 | "purpose": "maskable" 27 | }, 28 | { 29 | "src": "icons/Icon-maskable-512.png", 30 | "sizes": "512x512", 31 | "type": "image/png", 32 | "purpose": "maskable" 33 | } 34 | ] 35 | } 36 | --------------------------------------------------------------------------------