├── .gitattributes ├── .gitignore ├── .metadata ├── LICENSE ├── README.md ├── analysis_options.yaml ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── debug │ │ └── AndroidManifest.xml │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── kotlin │ │ │ └── com │ │ │ └── example │ │ │ └── animation_cheat_page │ │ │ └── MainActivity.kt │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── assets ├── favicon.png ├── logo-192.png └── manifest.json ├── build.yaml ├── codemagic.yaml ├── 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.h │ ├── AppDelegate.m │ ├── 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 │ └── main.m ├── lib ├── animated_widgets │ ├── align.dart │ ├── all_animated_widgets.dart │ ├── container.dart │ ├── cross_fade.dart │ ├── default_text_style.dart │ ├── opacity.dart │ ├── padding.dart │ ├── physical_model.dart │ ├── positioned.dart │ └── positioned_directional.dart ├── animations_cheat_sheet.dart ├── config.dart ├── curves │ ├── curves.dart │ └── curves_page.dart ├── generic │ └── not_found_page.dart ├── main.dart ├── pages │ ├── content_page.dart │ ├── examples.dart │ ├── frame_page.dart │ ├── licenses_page.dart │ └── root_page.dart ├── shared │ ├── code │ │ ├── code_block.dart │ │ └── code_button.dart │ ├── deferred.dart │ ├── enum.dart │ ├── frame.dart │ ├── header_page.dart │ ├── interop.dart │ ├── lists.dart │ ├── network │ │ └── urls.dart │ ├── string.dart │ ├── system.dart │ └── ui │ │ ├── description.dart │ │ ├── footer.dart │ │ ├── header.dart │ │ ├── new_label.dart │ │ ├── new_section.dart │ │ ├── search_bar.dart │ │ ├── section.dart │ │ └── separator.dart ├── slivers │ ├── constraints │ │ ├── slivers_constraints.dart │ │ └── slivers_constraints_page.dart │ ├── fill_remaining │ │ ├── fill_remaining_sliver.dart │ │ ├── pages.dart │ │ └── pages │ │ │ ├── fill_overscroll.dart │ │ │ ├── fill_remaining_page.dart │ │ │ ├── has_scroll_body.dart │ │ │ └── widgets.dart │ ├── geometry │ │ ├── slivers_geomerty_page.dart │ │ └── slivers_geometry.dart │ ├── shared │ │ ├── overlapping.dart │ │ ├── scroll_constraints.dart │ │ └── sliver_section.dart │ └── single_page.dart └── transitions │ ├── align.dart │ ├── all_transitions.dart │ ├── decorated_box.dart │ ├── default_text_style.dart │ ├── fade.dart │ ├── positioned.dart │ ├── relative_positioned.dart │ ├── rotation.dart │ ├── scale.dart │ ├── size.dart │ └── slide.dart ├── linux ├── .gitignore ├── CMakeLists.txt ├── flutter │ └── CMakeLists.txt ├── main.cc ├── my_application.cc └── my_application.h ├── macos ├── .gitignore ├── Flutter │ ├── Flutter-Debug.xcconfig │ └── Flutter-Release.xcconfig ├── 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.yaml ├── test ├── app_test.dart └── main_page_test.dart ├── test_driver ├── curves_test.dart ├── generic │ ├── generic.dart │ └── test_configuration.dart ├── root_test.dart └── sliver_fill_remaining_test.dart ├── web ├── favicon.png ├── icons │ ├── Icon-192.png │ └── Icon-512.png ├── index.html └── manifest.json └── windows ├── .gitignore ├── CMakeLists.txt ├── flutter └── CMakeLists.txt └── runner ├── CMakeLists.txt ├── Runner.rc ├── flutter_window.cpp ├── flutter_window.h ├── main.cpp ├── resource.h ├── resources └── app_icon.ico ├── run_loop.cpp ├── run_loop.h ├── runner.exe.manifest ├── utils.cpp ├── utils.h ├── win32_window.cpp └── win32_window.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .dart_tool/ 3 | 4 | .packages 5 | .pub/ 6 | .idea/ 7 | 8 | build/ 9 | 10 | .flutter-plugins 11 | *.iml 12 | *.lock 13 | .vscode/launch.json 14 | test_driver/screenshots/ 15 | /screenshots 16 | 17 | linux/.generated_flutter_root 18 | windows/Generated.props 19 | lib/generated/i18n.dart 20 | res/ 21 | 22 | android/local.properties 23 | /android/.gradle 24 | ios/Flutter/Generated.xcconfig 25 | android/app/src/main/java/io/flutter/plugins/GeneratedPluginRegistrant.java 26 | ios/Runner/GeneratedPluginRegistrant.m 27 | ios/Runner/GeneratedPluginRegistrant.h 28 | linux/flutter/generated_plugin_registrant.cc 29 | linux/flutter/generated_plugin_registrant.h 30 | macos/Flutter/GeneratedPluginRegistrant.swift 31 | windows/flutter/generated_plugin_registrant.cc 32 | windows/flutter/generated_plugin_registrant.h 33 | ios/Flutter/flutter_export_environment.sh 34 | .flutter-plugins-dependencies 35 | windows/window_configuration.cpp_copy 36 | lib/generated_plugin_registrant.dart 37 | windows/flutter/generated_plugins.cmake 38 | test_driver/generic/generic_test.dart 39 | linux/flutter/generated_plugins.cmake 40 | -------------------------------------------------------------------------------- /.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: 44b7e7d3f42f050a79712daab253af06e9daf530 8 | channel: beta 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Flutter Animations Cheat Sheet 2 | [![Codemagic build status](https://api.codemagic.io/apps/5d360a1b3b905a001cfaa874/5d360a1b3b905a001cfaa873/status_badge.svg)](https://codemagic.io/apps/5d360a1b3b905a001cfaa874/5d360a1b3b905a001cfaa873/latest_build) 3 | 4 | This is a list of animations and transitions that you can used in Flutter. 5 | 6 | Available as a [webpage](https://flutter-animations-cheat-sheet.codemagic.app/#/). 7 | The content of the page is [searchable](https://flutter-animations-cheat-sheet.codemagic.app/#/content). 8 | 9 | # Building 10 | Last tested version of Flutter that works with this repo is `2.1.0-12.2.pre`: 11 | * **Important**: You cannot be on beta/stable channel to run flutter desktop - check out how [to switch to a specific version](https://tomek-polanski.medium.com/running-flutter-desktop-on-stable-channel-c2ee38157e7b). 12 | 13 | ## Building for Web 14 | Upgrade flutter to minimum `2.1.0-12.2.pre`, enable web compilation and run: 15 | ``` 16 | flutter version 2.1.0-12.2.pre 17 | flutter run -d chrome -v 18 | ``` 19 | 20 | ## Building for Desktop 21 | ### Linux 22 | - Add the following to `.bash_profile` replacing `$HOME/flutter/` with path to your Flutter folder. 23 | ``` 24 | # Add Flutter 25 | export PATH="$PATH:$HOME/flutter/bin" 26 | # Add Dart 27 | export PATH="$PATH:$HOME/flutter/bin/cache/dart-sdk/bin" 28 | ``` 29 | ### Mac 30 | - Add the following to `.bash_profile` replacing `$HOME/flutter/` with path to your Flutter folder. 31 | ``` 32 | # Add Flutter 33 | export PATH="$PATH:$HOME/flutter/bin" 34 | # Add Dart 35 | export PATH="$PATH:$HOME/flutter/bin/cache/dart-sdk/bin" 36 | ``` 37 | - Update CocoaPods: 38 | ``` 39 | brew upgrade cocoapods 40 | pod setup 41 | gem install cocoapods 42 | ``` 43 | ### Windows 44 | - Install Visual Studio Community 2019 with [Desktop development with C++](https://devblogs.microsoft.com/cppblog/windows-desktop-development-with-c-in-visual-studio/#installation). 45 | Make sure that those components are installed: 46 | * `MSVC v142 - VS 2019 C++ x64/x86 build tools (v14.23)` 47 | * `Windows 10 SDK (10.0.17763.0)` 48 | 49 | ## Running on Desktop 50 | Enter `flutter-animations` in your terminal and run `flutter run`. 51 | You might need to use `-d` to pick desktop if you have any other device connected. 52 | 53 | OR 54 | 55 | Open in your ide `flutter-animations` 56 | In IntelliJ/VS Code select appropriate device via device picker and run the app. 57 | 58 | # Running Flutter Driver tests on Desktop 59 | Installing [fast_flutter_driver](https://github.com/tomaszpolanski/fast_flutter_driver) script: 60 | ```shell script 61 | pub global activate fast_flutter_driver_tool 62 | flutter packages get 63 | ``` 64 | Running: 65 | ``` 66 | fastdriver -r 800x800 67 | ``` 68 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 29 30 | 31 | sourceSets { 32 | main.java.srcDirs += 'src/main/kotlin' 33 | } 34 | 35 | lintOptions { 36 | disable 'InvalidPackage' 37 | } 38 | 39 | defaultConfig { 40 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). 41 | applicationId "com.example.animation_cheat_page" 42 | minSdkVersion 16 43 | targetSdkVersion 29 44 | versionCode flutterVersionCode.toInteger() 45 | versionName flutterVersionName 46 | } 47 | 48 | buildTypes { 49 | release { 50 | // TODO: Add your own signing config for the release build. 51 | // Signing with the debug keys for now, so `flutter run --release` works. 52 | signingConfig signingConfigs.debug 53 | } 54 | } 55 | } 56 | 57 | flutter { 58 | source '../..' 59 | } 60 | 61 | dependencies { 62 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 63 | } 64 | -------------------------------------------------------------------------------- /android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 13 | 17 | 21 | 26 | 30 | 31 | 32 | 33 | 34 | 35 | 37 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /android/app/src/main/kotlin/com/example/animation_cheat_page/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.example.animation_cheat_page 2 | 3 | import io.flutter.embedding.android.FlutterActivity 4 | 5 | class MainActivity: FlutterActivity() { 6 | } 7 | -------------------------------------------------------------------------------- /android/app/src/profile/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | ext.kotlin_version = '1.3.50' 3 | repositories { 4 | google() 5 | jcenter() 6 | } 7 | 8 | dependencies { 9 | classpath 'com.android.tools.build:gradle:3.5.0' 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 11 | } 12 | } 13 | 14 | allprojects { 15 | repositories { 16 | google() 17 | jcenter() 18 | } 19 | } 20 | 21 | rootProject.buildDir = '../build' 22 | subprojects { 23 | project.buildDir = "${rootProject.buildDir}/${project.name}" 24 | } 25 | subprojects { 26 | project.evaluationDependsOn(':app') 27 | } 28 | 29 | task clean(type: Delete) { 30 | delete rootProject.buildDir 31 | } 32 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | org.gradle.jvmargs=-Xmx1536M 2 | android.useAndroidX=true 3 | android.enableJetifier=true 4 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jun 23 08:50:38 CEST 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.2-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/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/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/assets/favicon.png -------------------------------------------------------------------------------- /assets/logo-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/assets/logo-192.png -------------------------------------------------------------------------------- /assets/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Flutter Animations Cheat Sheet", 3 | "short_name": "Animations", 4 | "icons": [ 5 | { 6 | "src": "/assets/assets/logo-192.png", 7 | "type": "image/png", 8 | "sizes": "192x192" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "background_color": "#4089D4", 14 | "theme_color": "#4089D4", 15 | "description": "Cheat sheet for working with animations and Slivers on Flutter" 16 | } -------------------------------------------------------------------------------- /build.yaml: -------------------------------------------------------------------------------- 1 | targets: 2 | $default: 3 | sources: 4 | exclude: 5 | - lib/tests/**.dart 6 | -------------------------------------------------------------------------------- /codemagic.yaml: -------------------------------------------------------------------------------- 1 | # Automatically generated on 2020-02-11 UTC from https://codemagic.io/app/5d360a1b3b905a001cfaa874/settings 2 | # Note that this configuration is not an exact match to UI settings. Review and adjust as necessary. 3 | 4 | workflows: 5 | default-workflow: 6 | name: Default Workflow 7 | environment: 8 | flutter: flutte-1.19.0-4.1.pre 9 | xcode: latest 10 | cache: 11 | cache_paths: 12 | - $FCI_BUILD_DIR/build 13 | triggering: 14 | events: 15 | - pull_request 16 | branch_patterns: 17 | - pattern: master 18 | include: true 19 | source: true 20 | scripts: 21 | - flutter packages pub get 22 | - flutter config --enable-web 23 | - flutter analyze 24 | - flutter test 25 | - | 26 | flutter config --enable-linux-desktop --enable-macos-desktop 27 | flutter devices 28 | - | 29 | # build web 30 | flutter build web --release 31 | cd build/web 32 | 7z a -r ../web.zip ./* 33 | artifacts: 34 | - build/web.zip 35 | - flutter_drive.log 36 | publishing: 37 | email: 38 | recipients: 39 | - polanski.tomek@gmail.com 40 | -------------------------------------------------------------------------------- /ios/.gitignore: -------------------------------------------------------------------------------- 1 | *.mode1v3 2 | *.mode2v3 3 | *.moved-aside 4 | *.pbxuser 5 | *.perspectivev3 6 | **/*sync/ 7 | .sconsign.dblite 8 | .tags* 9 | **/.vagrant/ 10 | **/DerivedData/ 11 | Icon? 12 | **/Pods/ 13 | **/.symlinks/ 14 | profile 15 | xcuserdata 16 | **/.generated/ 17 | Flutter/App.framework 18 | Flutter/Flutter.framework 19 | Flutter/Flutter.podspec 20 | Flutter/Generated.xcconfig 21 | Flutter/app.flx 22 | Flutter/app.zip 23 | Flutter/flutter_assets/ 24 | Flutter/flutter_export_environment.sh 25 | ServiceDefinitions.json 26 | Runner/GeneratedPluginRegistrant.* 27 | 28 | # Exceptions to above rules. 29 | !default.mode1v3 30 | !default.mode2v3 31 | !default.pbxuser 32 | !default.perspectivev3 33 | -------------------------------------------------------------------------------- /ios/Flutter/AppFrameworkInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | App 9 | CFBundleIdentifier 10 | io.flutter.flutter.app 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | App 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1.0 23 | MinimumOSVersion 24 | 8.0 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Flutter/Debug.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Flutter/Release.xcconfig: -------------------------------------------------------------------------------- 1 | #include "Generated.xcconfig" 2 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PreviewsEnabled 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 39 | 40 | 41 | 42 | 43 | 44 | 54 | 56 | 62 | 63 | 64 | 65 | 66 | 67 | 73 | 75 | 81 | 82 | 83 | 84 | 86 | 87 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /ios/Runner.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 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.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : FlutterAppDelegate 5 | 6 | @end 7 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #include "AppDelegate.h" 2 | #include "GeneratedPluginRegistrant.h" 3 | 4 | @implementation AppDelegate 5 | 6 | - (BOOL)application:(UIApplication *)application 7 | didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 8 | [GeneratedPluginRegistrant registerWithRegistry:self]; 9 | // Override point for customization after application launch. 10 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 11 | } 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/Runner/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | import UIKit 2 | import Flutter 3 | 4 | @UIApplicationMain 5 | @objc class AppDelegate: FlutterAppDelegate { 6 | override func application( 7 | _ application: UIApplication, 8 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? 9 | ) -> Bool { 10 | GeneratedPluginRegistrant.register(with: self) 11 | return super.application(application, didFinishLaunchingWithOptions: launchOptions) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "20x20", 5 | "idiom" : "iphone", 6 | "filename" : "Icon-App-20x20@2x.png", 7 | "scale" : "2x" 8 | }, 9 | { 10 | "size" : "20x20", 11 | "idiom" : "iphone", 12 | "filename" : "Icon-App-20x20@3x.png", 13 | "scale" : "3x" 14 | }, 15 | { 16 | "size" : "29x29", 17 | "idiom" : "iphone", 18 | "filename" : "Icon-App-29x29@1x.png", 19 | "scale" : "1x" 20 | }, 21 | { 22 | "size" : "29x29", 23 | "idiom" : "iphone", 24 | "filename" : "Icon-App-29x29@2x.png", 25 | "scale" : "2x" 26 | }, 27 | { 28 | "size" : "29x29", 29 | "idiom" : "iphone", 30 | "filename" : "Icon-App-29x29@3x.png", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "size" : "40x40", 35 | "idiom" : "iphone", 36 | "filename" : "Icon-App-40x40@2x.png", 37 | "scale" : "2x" 38 | }, 39 | { 40 | "size" : "40x40", 41 | "idiom" : "iphone", 42 | "filename" : "Icon-App-40x40@3x.png", 43 | "scale" : "3x" 44 | }, 45 | { 46 | "size" : "60x60", 47 | "idiom" : "iphone", 48 | "filename" : "Icon-App-60x60@2x.png", 49 | "scale" : "2x" 50 | }, 51 | { 52 | "size" : "60x60", 53 | "idiom" : "iphone", 54 | "filename" : "Icon-App-60x60@3x.png", 55 | "scale" : "3x" 56 | }, 57 | { 58 | "size" : "20x20", 59 | "idiom" : "ipad", 60 | "filename" : "Icon-App-20x20@1x.png", 61 | "scale" : "1x" 62 | }, 63 | { 64 | "size" : "20x20", 65 | "idiom" : "ipad", 66 | "filename" : "Icon-App-20x20@2x.png", 67 | "scale" : "2x" 68 | }, 69 | { 70 | "size" : "29x29", 71 | "idiom" : "ipad", 72 | "filename" : "Icon-App-29x29@1x.png", 73 | "scale" : "1x" 74 | }, 75 | { 76 | "size" : "29x29", 77 | "idiom" : "ipad", 78 | "filename" : "Icon-App-29x29@2x.png", 79 | "scale" : "2x" 80 | }, 81 | { 82 | "size" : "40x40", 83 | "idiom" : "ipad", 84 | "filename" : "Icon-App-40x40@1x.png", 85 | "scale" : "1x" 86 | }, 87 | { 88 | "size" : "40x40", 89 | "idiom" : "ipad", 90 | "filename" : "Icon-App-40x40@2x.png", 91 | "scale" : "2x" 92 | }, 93 | { 94 | "size" : "76x76", 95 | "idiom" : "ipad", 96 | "filename" : "Icon-App-76x76@1x.png", 97 | "scale" : "1x" 98 | }, 99 | { 100 | "size" : "76x76", 101 | "idiom" : "ipad", 102 | "filename" : "Icon-App-76x76@2x.png", 103 | "scale" : "2x" 104 | }, 105 | { 106 | "size" : "83.5x83.5", 107 | "idiom" : "ipad", 108 | "filename" : "Icon-App-83.5x83.5@2x.png", 109 | "scale" : "2x" 110 | }, 111 | { 112 | "size" : "1024x1024", 113 | "idiom" : "ios-marketing", 114 | "filename" : "Icon-App-1024x1024@1x.png", 115 | "scale" : "1x" 116 | } 117 | ], 118 | "info" : { 119 | "version" : 1, 120 | "author" : "xcode" 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png -------------------------------------------------------------------------------- /ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md: -------------------------------------------------------------------------------- 1 | # Launch Screen Assets 2 | 3 | You can customize the launch screen with your own desired assets by replacing the image files in this directory. 4 | 5 | You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images. -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /ios/Runner/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /ios/Runner/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | animation_cheat_page 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | $(FLUTTER_BUILD_NAME) 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(FLUTTER_BUILD_NUMBER) 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UISupportedInterfaceOrientations 30 | 31 | UIInterfaceOrientationPortrait 32 | UIInterfaceOrientationLandscapeLeft 33 | UIInterfaceOrientationLandscapeRight 34 | 35 | UISupportedInterfaceOrientations~ipad 36 | 37 | UIInterfaceOrientationPortrait 38 | UIInterfaceOrientationPortraitUpsideDown 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UIViewControllerBasedStatusBarAppearance 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /ios/Runner/Runner-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import "GeneratedPluginRegistrant.h" 2 | -------------------------------------------------------------------------------- /ios/Runner/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char* argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /lib/animated_widgets/align.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class AlignExample extends StatefulWidget { 6 | const AlignExample({ 7 | required this.child, 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | final Widget child; 12 | 13 | @override 14 | _AlignExampleState createState() => _AlignExampleState(); 15 | } 16 | 17 | class _AlignExampleState extends State { 18 | bool _showFirst = false; 19 | late Timer _timer; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | // starts animating just after the first frame 25 | WidgetsBinding.instance!.addPostFrameCallback( 26 | (_) => setState(() => _showFirst = !_showFirst), 27 | ); 28 | _timer = Timer.periodic( 29 | const Duration(seconds: 4), 30 | (_) => setState(() => _showFirst = !_showFirst), 31 | ); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | _timer.cancel(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return AnimatedAlign( 43 | duration: const Duration(seconds: 4), 44 | alignment: _showFirst ? Alignment.topLeft : Alignment.bottomRight, 45 | child: widget.child, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/animated_widgets/container.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class ContainerExample extends StatefulWidget { 7 | const ContainerExample({ 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | @override 12 | _ContainerExampleState createState() => _ContainerExampleState(); 13 | } 14 | 15 | class _ContainerExampleState extends State { 16 | bool _showFirst = false; 17 | late Timer _timer; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | // starts animating just after the first frame 23 | WidgetsBinding.instance!.addPostFrameCallback( 24 | (_) => setState(() => _showFirst = !_showFirst), 25 | ); 26 | _timer = Timer.periodic( 27 | const Duration(seconds: 4), 28 | (_) => setState(() => _showFirst = !_showFirst), 29 | ); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | _timer.cancel(); 35 | super.dispose(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return AnimatedContainer( 41 | duration: const Duration(seconds: 4), 42 | padding: _showFirst ? const EdgeInsets.all(40) : const EdgeInsets.all(80), 43 | decoration: _showFirst 44 | ? const BoxDecoration(color: Colors.yellowAccent) 45 | : BoxDecoration( 46 | color: Colors.blue, 47 | border: Border.all(width: 5), 48 | borderRadius: const BorderRadius.all(Radius.circular(15)), 49 | ), 50 | child: const Icon( 51 | Icons.star, 52 | size: 50, 53 | ), 54 | ); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /lib/animated_widgets/cross_fade.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class CrossFadeExample extends StatefulWidget { 7 | const CrossFadeExample({ 8 | required this.child, 9 | Key? key, 10 | }) : super(key: key); 11 | 12 | final Widget child; 13 | 14 | @override 15 | _CrossFadeExampleState createState() => _CrossFadeExampleState(); 16 | } 17 | 18 | class _CrossFadeExampleState extends State { 19 | bool _showFirst = false; 20 | late Timer _timer; 21 | 22 | @override 23 | void initState() { 24 | super.initState(); 25 | // starts animating just after the first frame 26 | WidgetsBinding.instance!.addPostFrameCallback( 27 | (_) => setState(() => _showFirst = !_showFirst), 28 | ); 29 | _timer = Timer.periodic( 30 | const Duration(seconds: 4), 31 | (_) => setState(() => _showFirst = !_showFirst), 32 | ); 33 | } 34 | 35 | @override 36 | void dispose() { 37 | _timer.cancel(); 38 | super.dispose(); 39 | } 40 | 41 | @override 42 | Widget build(BuildContext context) { 43 | return AnimatedCrossFade( 44 | duration: const Duration(seconds: 4), 45 | firstChild: widget.child, 46 | secondChild: const Card( 47 | color: Colors.greenAccent, 48 | child: Padding( 49 | padding: EdgeInsets.all(40), 50 | child: Icon( 51 | Icons.public, 52 | size: 50, 53 | ), 54 | ), 55 | ), 56 | crossFadeState: 57 | _showFirst ? CrossFadeState.showFirst : CrossFadeState.showSecond, 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/animated_widgets/default_text_style.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class AnimatedDefaultTextStyleExample extends StatefulWidget { 7 | const AnimatedDefaultTextStyleExample({ 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | @override 12 | _AnimatedDefaultTextStyleExampleState createState() => 13 | _AnimatedDefaultTextStyleExampleState(); 14 | } 15 | 16 | class _AnimatedDefaultTextStyleExampleState 17 | extends State { 18 | bool _showFirst = false; 19 | late Timer _timer; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | // starts animating just after the first frame 25 | WidgetsBinding.instance!.addPostFrameCallback( 26 | (_) => setState(() => _showFirst = !_showFirst), 27 | ); 28 | _timer = Timer.periodic( 29 | const Duration(seconds: 4), 30 | (_) => setState(() => _showFirst = !_showFirst), 31 | ); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | _timer.cancel(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return AnimatedDefaultTextStyle( 43 | duration: const Duration(seconds: 4), 44 | style: _showFirst 45 | ? Theme.of(context).textTheme.headline4! 46 | : Theme.of(context).textTheme.headline2!, 47 | child: const Text('FlutterFlutterFlutterFlutterFlutterFlutter'), 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/animated_widgets/opacity.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/widgets.dart'; 4 | 5 | class AnimatedOpacityExample extends StatefulWidget { 6 | const AnimatedOpacityExample({ 7 | required this.child, 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | final Widget child; 12 | 13 | @override 14 | _AnimatedOpacityExampleState createState() => _AnimatedOpacityExampleState(); 15 | } 16 | 17 | class _AnimatedOpacityExampleState extends State { 18 | bool _showFirst = false; 19 | late Timer _timer; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | // starts animating just after the first frame 25 | WidgetsBinding.instance!.addPostFrameCallback( 26 | (_) => setState(() => _showFirst = !_showFirst), 27 | ); 28 | _timer = Timer.periodic( 29 | const Duration(seconds: 4), 30 | (_) => setState(() => _showFirst = !_showFirst), 31 | ); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | _timer.cancel(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return AnimatedOpacity( 43 | duration: const Duration(seconds: 4), 44 | opacity: _showFirst ? 1 : 0, 45 | child: widget.child, 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /lib/animated_widgets/padding.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class PaddingExample extends StatefulWidget { 7 | const PaddingExample({ 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | @override 12 | _PaddingExampleState createState() => _PaddingExampleState(); 13 | } 14 | 15 | class _PaddingExampleState extends State { 16 | bool _showFirst = false; 17 | late Timer _timer; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | // starts animating just after the first frame 23 | WidgetsBinding.instance!.addPostFrameCallback( 24 | (_) => setState(() => _showFirst = !_showFirst), 25 | ); 26 | _timer = Timer.periodic( 27 | const Duration(seconds: 4), 28 | (_) => setState(() => _showFirst = !_showFirst), 29 | ); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | _timer.cancel(); 35 | super.dispose(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Card( 41 | color: Colors.yellowAccent, 42 | child: AnimatedPadding( 43 | duration: const Duration(seconds: 4), 44 | padding: 45 | _showFirst ? const EdgeInsets.all(40) : const EdgeInsets.all(80), 46 | child: const Icon( 47 | Icons.star, 48 | size: 50, 49 | ), 50 | ), 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /lib/animated_widgets/physical_model.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class AnimatedPhysicalModelExample extends StatefulWidget { 7 | const AnimatedPhysicalModelExample({ 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | @override 12 | _AnimatedPhysicalModelExampleState createState() => 13 | _AnimatedPhysicalModelExampleState(); 14 | } 15 | 16 | class _AnimatedPhysicalModelExampleState 17 | extends State { 18 | bool _showFirst = false; 19 | late Timer _timer; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | // starts animating just after the first frame 25 | WidgetsBinding.instance!.addPostFrameCallback( 26 | (_) => setState(() => _showFirst = !_showFirst), 27 | ); 28 | _timer = Timer.periodic( 29 | const Duration(seconds: 4), 30 | (_) => setState(() => _showFirst = !_showFirst), 31 | ); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | _timer.cancel(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return AnimatedPhysicalModel( 43 | duration: const Duration(seconds: 4), 44 | color: _showFirst ? Colors.yellowAccent : Colors.greenAccent, 45 | elevation: _showFirst ? 0 : 7, 46 | shadowColor: Colors.black, 47 | shape: BoxShape.rectangle, 48 | borderRadius: _showFirst 49 | ? const BorderRadius.all(Radius.circular(0)) 50 | : const BorderRadius.all(Radius.circular(10)), 51 | child: const Padding( 52 | padding: EdgeInsets.all(40), 53 | child: Icon( 54 | Icons.star, 55 | size: 50, 56 | ), 57 | ), 58 | ); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /lib/animated_widgets/positioned.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class PositionedExample extends StatefulWidget { 7 | const PositionedExample({ 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | @override 12 | _PositionedExampleState createState() => _PositionedExampleState(); 13 | } 14 | 15 | class _PositionedExampleState extends State { 16 | bool _showFirst = false; 17 | late Timer _timer; 18 | 19 | @override 20 | void initState() { 21 | super.initState(); 22 | // starts animating just after the first frame 23 | WidgetsBinding.instance!.addPostFrameCallback( 24 | (_) => setState(() => _showFirst = !_showFirst), 25 | ); 26 | _timer = Timer.periodic( 27 | const Duration(seconds: 4), 28 | (_) => setState(() => _showFirst = !_showFirst), 29 | ); 30 | } 31 | 32 | @override 33 | void dispose() { 34 | _timer.cancel(); 35 | super.dispose(); 36 | } 37 | 38 | @override 39 | Widget build(BuildContext context) { 40 | return Stack( 41 | children: [ 42 | AnimatedPositioned( 43 | duration: const Duration(seconds: 4), 44 | top: _showFirst ? 0 : 100, 45 | left: _showFirst ? 0 : 100, 46 | right: 0, 47 | child: const Card( 48 | color: Colors.yellowAccent, 49 | child: Padding( 50 | padding: EdgeInsets.all(40), 51 | child: Icon( 52 | Icons.star, 53 | size: 50, 54 | ), 55 | ), 56 | ), 57 | ), 58 | ], 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/animated_widgets/positioned_directional.dart: -------------------------------------------------------------------------------- 1 | import 'dart:async'; 2 | 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | class PositionedDirectionalExample extends StatefulWidget { 7 | const PositionedDirectionalExample({ 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | @override 12 | _PositionedDirectionalExampleState createState() => 13 | _PositionedDirectionalExampleState(); 14 | } 15 | 16 | class _PositionedDirectionalExampleState 17 | extends State { 18 | bool _showFirst = false; 19 | late Timer _timer; 20 | 21 | @override 22 | void initState() { 23 | super.initState(); 24 | // starts animating just after the first frame 25 | WidgetsBinding.instance!.addPostFrameCallback( 26 | (_) => setState(() => _showFirst = !_showFirst), 27 | ); 28 | _timer = Timer.periodic( 29 | const Duration(seconds: 4), 30 | (_) => setState(() => _showFirst = !_showFirst), 31 | ); 32 | } 33 | 34 | @override 35 | void dispose() { 36 | _timer.cancel(); 37 | super.dispose(); 38 | } 39 | 40 | @override 41 | Widget build(BuildContext context) { 42 | return Directionality( 43 | textDirection: TextDirection.rtl, 44 | child: Stack( 45 | children: [ 46 | AnimatedPositionedDirectional( 47 | duration: const Duration(seconds: 4), 48 | top: _showFirst ? 0 : 100, 49 | start: _showFirst ? 0 : 100, 50 | end: 0, 51 | child: const Card( 52 | color: Colors.yellowAccent, 53 | child: Padding( 54 | padding: EdgeInsets.all(40), 55 | child: Icon( 56 | Icons.star, 57 | size: 50, 58 | ), 59 | ), 60 | ), 61 | ), 62 | ], 63 | ), 64 | ); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /lib/animations_cheat_sheet.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/config.dart'; 2 | import 'package:animation_cheat_page/curves/curves_page.dart'; 3 | import 'package:animation_cheat_page/generic/not_found_page.dart'; 4 | import 'package:animation_cheat_page/pages/content_page.dart'; 5 | import 'package:animation_cheat_page/pages/licenses_page.dart' 6 | deferred as licenses; 7 | import 'package:animation_cheat_page/pages/root_page.dart'; 8 | import 'package:animation_cheat_page/shared/deferred.dart'; 9 | import 'package:animation_cheat_page/slivers/constraints/slivers_constraints.dart' 10 | as slivers_constraints; 11 | import 'package:animation_cheat_page/slivers/fill_remaining/pages.dart' 12 | as fill_remaining; 13 | import 'package:animation_cheat_page/slivers/geometry/slivers_geometry.dart' 14 | as slivers_geometry; 15 | import 'package:flutter/foundation.dart'; 16 | import 'package:flutter/material.dart'; 17 | import 'package:google_fonts/google_fonts.dart'; 18 | 19 | class AnimationCheatSheet extends StatelessWidget { 20 | const AnimationCheatSheet({ 21 | Key? key, 22 | required this.config, 23 | }) : super(key: key); 24 | 25 | final Configuration config; 26 | 27 | static const title = 'Flutter Animations'; 28 | static final gKey = GlobalKey(); 29 | 30 | @override 31 | Widget build(BuildContext context) { 32 | return MaterialApp( 33 | debugShowCheckedModeBanner: false, 34 | navigatorKey: gKey, 35 | builder: (_, child) { 36 | return ScrollConfiguration(behavior: NoOverflow(), child: child!); 37 | }, 38 | theme: ThemeData( 39 | textTheme: GoogleFonts.crimsonProTextTheme(), 40 | ), 41 | initialRoute: config.route, 42 | title: title, 43 | onUnknownRoute: (settings) => MaterialPageRoute( 44 | settings: settings, 45 | builder: (_) => const NotFoundPage(), 46 | ), 47 | routes: routes(repeatAnimations: config.repeatAnimations), 48 | ); 49 | } 50 | 51 | static Map routes({bool? repeatAnimations = false}) { 52 | return { 53 | Routes.root: (_) => RootPage(repeatAnimations: repeatAnimations), 54 | Routes.curves: (_) => CurvesPage(repeatAnimations: repeatAnimations), 55 | Routes.not_found: (_) => const NotFoundPage(), 56 | Routes.content: (_) => const ContentPage(), 57 | Routes.licenses: (_) => Deferred( 58 | future: licenses.loadLibrary(), 59 | // ignore: prefer_const_constructors 60 | builder: (_) => licenses.LicensesPage(), 61 | ), 62 | ...slivers_constraints.pages, 63 | ...slivers_geometry.pages, 64 | ...fill_remaining.pages, 65 | }; 66 | } 67 | } 68 | 69 | class NoOverflow extends ScrollBehavior { 70 | @override 71 | Widget buildViewportChrome( 72 | BuildContext context, 73 | Widget child, 74 | AxisDirection axisDirection, 75 | ) => 76 | child; 77 | } 78 | -------------------------------------------------------------------------------- /lib/config.dart: -------------------------------------------------------------------------------- 1 | class Configuration { 2 | const Configuration({ 3 | this.route, 4 | this.repeatAnimations = true, 5 | }); 6 | 7 | factory Configuration.fromJson(Map json) { 8 | return Configuration( 9 | route: json['route'], 10 | repeatAnimations: json['enableAnimations'], 11 | ); 12 | } 13 | 14 | final String? route; 15 | final bool? repeatAnimations; 16 | 17 | Map toJson() => { 18 | 'route': route, 19 | 'enableAnimations': repeatAnimations, 20 | }; 21 | } 22 | 23 | class Routes { 24 | static const root = '/'; 25 | static const curves = '/curves'; 26 | static const not_found = '/not-found'; 27 | static const content = '/content'; 28 | static const licenses = '/licenses'; 29 | static const slivers = '/slivers'; 30 | static const slivers_constraints = '$slivers/constraints'; 31 | static const slivers_geometry = '$slivers/geometry'; 32 | static const slivers_fill_remaining_example = '$slivers/fill-remaining'; 33 | static const slivers_fill_remaining_fill_overscroll = 34 | '$slivers_fill_remaining_example/fillOverscroll'; 35 | static const slivers_fill_remaining_has_scroll_body = 36 | '$slivers_fill_remaining_example/hasScrollBody'; 37 | } 38 | -------------------------------------------------------------------------------- /lib/curves/curves_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/curves/curves.dart'; 2 | import 'package:animation_cheat_page/curves/curves.dart' as curves; 3 | import 'package:animation_cheat_page/shared/header_page.dart'; 4 | import 'package:animation_cheat_page/shared/ui/section.dart'; 5 | import 'package:flutter/widgets.dart'; 6 | 7 | class CurvesPage extends StatelessWidget { 8 | const CurvesPage({Key? key, required this.repeatAnimations}) 9 | : super(key: key); 10 | final bool? repeatAnimations; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return HeaderPage( 15 | repeatAnimations: repeatAnimations, 16 | builder: (animation, child) { 17 | return Column( 18 | children: [ 19 | const SectionHeader( 20 | title: Text('Curves'), 21 | child: Text(curves.description), 22 | ), 23 | CurvesSection( 24 | animation: animation, 25 | onPressed: (url) {}, 26 | child: child, 27 | ), 28 | ], 29 | ); 30 | }, 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/generic/not_found_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class NotFoundPage extends StatelessWidget { 4 | const NotFoundPage({Key? key}) : super(key: key); 5 | 6 | @override 7 | Widget build(BuildContext context) { 8 | return Material( 9 | color: Theme.of(context).scaffoldBackgroundColor, 10 | child: Center( 11 | child: Row( 12 | crossAxisAlignment: CrossAxisAlignment.baseline, 13 | mainAxisAlignment: MainAxisAlignment.center, 14 | children: [ 15 | Text( 16 | '0x‭194‬', 17 | style: Theme.of(context).textTheme.headline1!.copyWith( 18 | color: Colors.black, 19 | fontSize: 200, 20 | ), 21 | ), 22 | Text( 23 | '(404)', 24 | style: Theme.of(context).textTheme.headline4, 25 | ), 26 | ], 27 | ), 28 | ), 29 | ); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /lib/main.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/animations_cheat_sheet.dart'; 2 | import 'package:animation_cheat_page/config.dart'; 3 | import 'package:flutter/widgets.dart'; 4 | 5 | void main() { 6 | runApp( 7 | const AnimationCheatSheet( 8 | config: Configuration(), 9 | ), 10 | ); 11 | } 12 | -------------------------------------------------------------------------------- /lib/pages/content_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/animations_cheat_sheet.dart'; 2 | import 'package:animation_cheat_page/shared/string.dart'; 3 | import 'package:animation_cheat_page/shared/ui/search_bar.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | class ContentPage extends StatefulWidget { 7 | const ContentPage({Key? key}) : super(key: key); 8 | 9 | @override 10 | _ContentPageState createState() => _ContentPageState(); 11 | } 12 | 13 | class _ContentPageState extends State { 14 | String? _searchQuery; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | appBar: SearchBar( 20 | onChanged: (String value) => setState(() => _searchQuery = value), 21 | ), 22 | body: Scrollbar( 23 | child: ListView( 24 | children: [ 25 | for (final route in AnimationCheatSheet.routes() 26 | .keys 27 | .where((route) => route.containsIgnoreCase(_searchQuery))) 28 | ListTile( 29 | title: Text(route, maxLines: 1), 30 | onTap: () => Navigator.of(context).pushNamed(route), 31 | ) 32 | ], 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/pages/examples.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/material.dart'; 3 | import 'package:animation_cheat_page/animated_widgets/all_animated_widgets.dart' 4 | as animated; 5 | import 'package:animation_cheat_page/curves/curves.dart' as curves; 6 | import 'package:animation_cheat_page/shared/ui/description.dart'; 7 | import 'package:animation_cheat_page/shared/ui/new_section.dart' as new_section; 8 | import 'package:animation_cheat_page/shared/ui/section.dart' as section; 9 | import 'package:animation_cheat_page/shared/ui/separator.dart'; 10 | import 'package:animation_cheat_page/transitions/all_transitions.dart' 11 | as transitions; 12 | import 'package:flutter/services.dart'; 13 | 14 | import 'package:universal_html/html.dart' deferred as html; 15 | 16 | class Examples extends StatefulWidget { 17 | const Examples({Key? key, required this.repeatAnimations}) : super(key: key); 18 | final bool repeatAnimations; 19 | 20 | @override 21 | _ExamplesState createState() => _ExamplesState(); 22 | } 23 | 24 | class _ExamplesState extends State 25 | with SingleTickerProviderStateMixin { 26 | late AnimationController _controller; 27 | 28 | @override 29 | void initState() { 30 | _controller = AnimationController( 31 | vsync: this, 32 | duration: const Duration(seconds: 3), 33 | ); 34 | if (widget.repeatAnimations) { 35 | _controller.repeat(reverse: true); 36 | } 37 | super.initState(); 38 | } 39 | 40 | @override 41 | void dispose() { 42 | _controller.dispose(); 43 | super.dispose(); 44 | } 45 | 46 | Future _handleUrl(BuildContext context, String url) async { 47 | if (kIsWeb) { 48 | await html.loadLibrary(); 49 | html.window.open(url, 'Source Code'); 50 | } else { 51 | await Clipboard.setData(ClipboardData(text: url)); 52 | final snackBar = SnackBar( 53 | content: Text('Copied link:\n$url'), 54 | ); 55 | ScaffoldMessenger.of(context).showSnackBar(snackBar); 56 | } 57 | } 58 | 59 | @override 60 | Widget build(BuildContext context) { 61 | const child = Card( 62 | color: Colors.yellowAccent, 63 | child: Padding( 64 | padding: EdgeInsets.all(40), 65 | child: Icon( 66 | Icons.star, 67 | size: 50, 68 | ), 69 | ), 70 | ); 71 | return SliverList( 72 | delegate: SliverChildListDelegate( 73 | [ 74 | const Align(child: Description()), 75 | const Align(child: Separator()), 76 | Align( 77 | child: new_section.NewSection( 78 | transitions: transitions.allTransitions, 79 | animated: animated.allAnimatedWidgets, 80 | curves: [curves.singleCurveExample], 81 | ), 82 | ), 83 | const section.SectionHeader( 84 | title: Text('Curves'), 85 | child: Text(curves.description), 86 | ), 87 | curves.CurvesSection( 88 | animation: _controller, 89 | onPressed: (url) => _handleUrl(context, url), 90 | child: child, 91 | ), 92 | const section.SectionHeader( 93 | title: Text('Transitions'), 94 | child: Text(transitions.description), 95 | ), 96 | for (final example in transitions.allTransitions) 97 | section.Section( 98 | title: example.title, 99 | url: example.fileUrl, 100 | released: example.released, 101 | body: example.body, 102 | onPressed: () { 103 | _handleUrl(context, example.pageUrl); 104 | }, 105 | child: example.builder(_controller, child), 106 | ), 107 | const section.SectionHeader( 108 | title: Text('Animated Widgets'), 109 | child: Text(animated.description), 110 | ), 111 | for (final example in animated.allAnimatedWidgets) 112 | section.Section( 113 | title: example.title, 114 | url: example.fileUrl, 115 | released: example.released, 116 | body: example.body, 117 | onPressed: () { 118 | _handleUrl(context, example.pageUrl); 119 | }, 120 | child: example.builder(_controller, child), 121 | ), 122 | ], 123 | ), 124 | ); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /lib/pages/frame_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | class FramePage extends StatelessWidget { 6 | const FramePage({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Theme( 11 | data: ThemeData.light().copyWith( 12 | primaryColor: Colors.green, 13 | ), 14 | child: Scaffold( 15 | appBar: AppBar(title: const Text('Align: without Align')), 16 | body: Column( 17 | crossAxisAlignment: CrossAxisAlignment.stretch, 18 | children: [ 19 | Align( 20 | child: ElevatedButton( 21 | onPressed: () {}, 22 | child: Text( 23 | 'Button', 24 | style: GoogleFonts.roboto(), 25 | ), 26 | ), 27 | ), 28 | ], 29 | ), 30 | ), 31 | ); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /lib/pages/licenses_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/animations_cheat_sheet.dart'; 2 | import 'package:flutter/foundation.dart'; 3 | import 'package:flutter/material.dart'; 4 | import 'package:flutter/scheduler.dart'; 5 | 6 | class LicensesPage extends StatefulWidget { 7 | const LicensesPage({ 8 | Key? key, 9 | this.applicationName = AnimationCheatSheet.title, 10 | this.applicationVersion, 11 | this.applicationLegalese = '© Tomek Polański', 12 | }) : super(key: key); 13 | 14 | final String applicationName; 15 | final String? applicationVersion; 16 | final String applicationLegalese; 17 | 18 | @override 19 | _LicensesPageState createState() => _LicensesPageState(); 20 | } 21 | 22 | class _LicensesPageState extends State { 23 | @override 24 | void initState() { 25 | super.initState(); 26 | _initLicenses(); 27 | } 28 | 29 | final List _licenses = []; 30 | bool _loaded = false; 31 | 32 | Future _initLicenses() async { 33 | await for (final license in LicenseRegistry.licenses) { 34 | if (!mounted) { 35 | return; 36 | } 37 | final List paragraphs = 38 | await SchedulerBinding.instance!.scheduleTask>( 39 | license.paragraphs.toList, 40 | Priority.animation, 41 | debugLabel: 'License', 42 | ); 43 | if (!mounted) { 44 | return; 45 | } 46 | setState(() { 47 | _licenses.addAll(_loadLicenses(license, paragraphs)); 48 | }); 49 | } 50 | setState(() { 51 | _loaded = true; 52 | }); 53 | } 54 | 55 | Iterable _loadLicenses( 56 | LicenseEntry license, List paragraphs) sync* { 57 | yield const Padding( 58 | padding: EdgeInsets.symmetric(vertical: 18), 59 | child: Text('🍀‬', textAlign: TextAlign.center), 60 | ); 61 | 62 | yield Container( 63 | decoration: const BoxDecoration( 64 | border: Border( 65 | bottom: BorderSide(width: 0), 66 | ), 67 | ), 68 | child: Text( 69 | license.packages.join(', '), 70 | style: const TextStyle(fontWeight: FontWeight.bold), 71 | textAlign: TextAlign.center, 72 | ), 73 | ); 74 | for (final paragraph in paragraphs) { 75 | if (paragraph.indent == LicenseParagraph.centeredIndent) { 76 | yield Padding( 77 | padding: const EdgeInsets.only(top: 16), 78 | child: Text( 79 | paragraph.text, 80 | style: const TextStyle(fontWeight: FontWeight.bold), 81 | textAlign: TextAlign.center, 82 | ), 83 | ); 84 | } else { 85 | assert(paragraph.indent >= 0); 86 | yield Padding( 87 | padding: EdgeInsetsDirectional.only( 88 | top: 8, 89 | start: 16.0 * paragraph.indent, 90 | ), 91 | child: Text(paragraph.text), 92 | ); 93 | } 94 | } 95 | } 96 | 97 | @override 98 | Widget build(BuildContext context) { 99 | assert(debugCheckHasMaterialLocalizations(context)); 100 | final String name = widget.applicationName; 101 | final String? version = widget.applicationVersion; 102 | return Scaffold( 103 | body: Localizations.override( 104 | locale: const Locale('en', 'US'), 105 | context: context, 106 | child: DefaultTextStyle( 107 | style: Theme.of(context).textTheme.caption!, 108 | child: SafeArea( 109 | bottom: false, 110 | child: DefaultTextStyle.merge( 111 | textAlign: TextAlign.center, 112 | child: ListView( 113 | padding: const EdgeInsets.symmetric( 114 | horizontal: 8, 115 | vertical: 12, 116 | ), 117 | children: [ 118 | Text( 119 | name, 120 | style: Theme.of(context).textTheme.headline5, 121 | ), 122 | if (version != null) 123 | Text( 124 | version, 125 | style: Theme.of(context).textTheme.bodyText2, 126 | ), 127 | const SizedBox(height: 18), 128 | Text( 129 | widget.applicationLegalese, 130 | style: Theme.of(context).textTheme.caption, 131 | ), 132 | Container(height: 18), 133 | Text( 134 | 'Powered by Flutter', 135 | style: Theme.of(context).textTheme.bodyText2, 136 | ), 137 | Container(height: 24), 138 | ..._licenses, 139 | if (!_loaded) 140 | const Padding( 141 | padding: EdgeInsets.symmetric(vertical: 24), 142 | child: Center(child: CircularProgressIndicator()), 143 | ), 144 | ], 145 | ), 146 | ), 147 | ), 148 | ), 149 | ), 150 | ); 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /lib/pages/root_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/pages/examples.dart' deferred as examples; 2 | import 'package:animation_cheat_page/shared/deferred.dart'; 3 | import 'package:animation_cheat_page/shared/ui/footer.dart'; 4 | import 'package:animation_cheat_page/shared/ui/header.dart'; 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:flutter/material.dart'; 7 | 8 | class RootPage extends StatelessWidget { 9 | const RootPage({ 10 | Key? key, 11 | this.repeatAnimations = true, 12 | }) : super(key: key); 13 | 14 | final bool? repeatAnimations; 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | return Scaffold( 19 | body: _AnimationProvider(repeatAnimations: repeatAnimations), 20 | ); 21 | } 22 | } 23 | 24 | class _AnimationProvider extends StatefulWidget { 25 | const _AnimationProvider({ 26 | Key? key, 27 | required this.repeatAnimations, 28 | }) : super(key: key); 29 | 30 | final bool? repeatAnimations; 31 | 32 | @override 33 | _AnimationProviderState createState() => _AnimationProviderState(); 34 | } 35 | 36 | class _AnimationProviderState extends State<_AnimationProvider> 37 | with TickerProviderStateMixin { 38 | late AnimationController _headerController; 39 | 40 | @override 41 | void initState() { 42 | _headerController = AnimationController( 43 | vsync: this, 44 | duration: const Duration(seconds: 3), 45 | )..forward(); 46 | super.initState(); 47 | } 48 | 49 | @override 50 | void dispose() { 51 | _headerController.dispose(); 52 | super.dispose(); 53 | } 54 | 55 | @override 56 | Widget build(BuildContext context) { 57 | return Column( 58 | children: [ 59 | Expanded( 60 | child: Scrollbar( 61 | child: CustomScrollView( 62 | slivers: [ 63 | SliverToBoxAdapter( 64 | child: Align( 65 | child: Header( 66 | 'Animations', 67 | animation: _headerController, 68 | ), 69 | ), 70 | ), 71 | Deferred( 72 | future: Future.delayed(_headerController.isCompleted 73 | ? Duration.zero 74 | : _headerController.duration ?? Duration.zero) 75 | .then( 76 | (_) => Future.wait([examples.loadLibrary()]), 77 | ), 78 | fallback: const SliverPadding(padding: EdgeInsets.zero), 79 | builder: (context) => examples.Examples( 80 | repeatAnimations: widget.repeatAnimations ?? false, 81 | ), 82 | ), 83 | ], 84 | ), 85 | ), 86 | ), 87 | const Footer(), 88 | ], 89 | ); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/shared/code/code_block.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:presentation/presentation.dart'; 4 | 5 | class CodeBlock extends StatefulWidget { 6 | const CodeBlock( 7 | this.code, { 8 | Key? key, 9 | }) : super(key: key); 10 | 11 | final String? code; 12 | 13 | @override 14 | _CodeBlockState createState() => _CodeBlockState(); 15 | } 16 | 17 | class _CodeBlockState extends State { 18 | Brightness _brightness = Brightness.light; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | return GestureDetector( 23 | onTap: () { 24 | setState(() { 25 | _brightness = _brightness == Brightness.dark 26 | ? Brightness.light 27 | : Brightness.dark; 28 | }); 29 | }, 30 | child: Container( 31 | width: double.infinity, 32 | decoration: BoxDecoration( 33 | border: Border.all(color: Colors.grey, width: 2), 34 | borderRadius: const BorderRadius.all(Radius.circular(5)), 35 | ), 36 | child: Editor( 37 | widget.code!, 38 | brightness: _brightness, 39 | nested: true, 40 | padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), 41 | ), 42 | ), 43 | ); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/shared/code/code_button.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:http/http.dart' as http; 4 | 5 | class CodeButton extends StatefulWidget { 6 | const CodeButton({ 7 | Key? key, 8 | required this.url, 9 | required this.onFetched, 10 | }) : super(key: key); 11 | 12 | final String url; 13 | final ValueChanged onFetched; 14 | 15 | @override 16 | _CodeButtonState createState() => _CodeButtonState(); 17 | } 18 | 19 | class _CodeButtonState extends State { 20 | bool _isLoading = false; 21 | 22 | @override 23 | Widget build(BuildContext context) { 24 | return DefaultTextStyle.merge( 25 | child: IconButton( 26 | icon: _isLoading 27 | ? const SizedBox( 28 | height: 20, 29 | width: 20, 30 | child: CircularProgressIndicator(), 31 | ) 32 | : const Icon(Icons.code), 33 | tooltip: 'Show code', 34 | onPressed: () async { 35 | setState(() => _isLoading = true); 36 | final code = await http.get(Uri.parse(widget.url)); 37 | setState(() => _isLoading = false); 38 | widget.onFetched( 39 | code.body 40 | .replaceAll("import 'package:flutter/widgets.dart';", '') 41 | .trim(), 42 | ); 43 | }, 44 | ), 45 | ); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /lib/shared/deferred.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class Deferred extends StatelessWidget { 4 | const Deferred({ 5 | Key? key, 6 | required this.future, 7 | required this.builder, 8 | this.fallback = const SizedBox(), 9 | }) : super(key: key); 10 | 11 | final Future future; 12 | final WidgetBuilder builder; 13 | final Widget fallback; 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return FutureBuilder( 18 | future: future, 19 | builder: (_, snapshot) => snapshot.connectionState != ConnectionState.done 20 | ? fallback 21 | : builder(context), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/shared/enum.dart: -------------------------------------------------------------------------------- 1 | String fromEnum(Object enumeration) { 2 | return enumeration.toString().split('.').last; 3 | } 4 | -------------------------------------------------------------------------------- /lib/shared/frame.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:intl/intl.dart'; 5 | import 'package:presentation/presentation.dart'; 6 | 7 | class AppFrameCard extends StatelessWidget { 8 | const AppFrameCard({ 9 | Key? key, 10 | required this.title, 11 | required this.child, 12 | }) : super(key: key); 13 | 14 | final String title; 15 | final Widget child; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return DefaultTextStyle.merge( 20 | style: GoogleFonts.roboto(), 21 | child: SizedBox( 22 | width: 300, 23 | height: 400, 24 | child: Card( 25 | clipBehavior: Clip.antiAlias, 26 | child: AppFrame( 27 | title: Text( 28 | title, 29 | style: GoogleFonts.roboto(), 30 | overflow: TextOverflow.clip, 31 | ), 32 | time: Text(DateFormat.Hm().format(DateTime.now())), 33 | child: child, 34 | ), 35 | ), 36 | ), 37 | ); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/shared/header_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/ui/header.dart'; 2 | import 'package:animation_cheat_page/transitions/all_transitions.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class HeaderPage extends StatefulWidget { 6 | const HeaderPage({ 7 | Key? key, 8 | required this.builder, 9 | this.header, 10 | required this.repeatAnimations, 11 | }) : super(key: key); 12 | 13 | final AnimatedWidgetBuilder builder; 14 | final Widget? header; 15 | final bool? repeatAnimations; 16 | 17 | @override 18 | _HeaderPageState createState() => _HeaderPageState(); 19 | } 20 | 21 | class _HeaderPageState extends State with TickerProviderStateMixin { 22 | late AnimationController _controller; 23 | late AnimationController _headerController; 24 | 25 | @override 26 | void initState() { 27 | _controller = AnimationController( 28 | vsync: this, 29 | duration: const Duration(seconds: 3), 30 | ); 31 | if (widget.repeatAnimations!) { 32 | _controller.repeat(reverse: true); 33 | } 34 | _headerController = AnimationController( 35 | vsync: this, 36 | duration: const Duration(seconds: 3), 37 | )..forward(); 38 | super.initState(); 39 | } 40 | 41 | @override 42 | void dispose() { 43 | _controller.dispose(); 44 | _headerController.dispose(); 45 | super.dispose(); 46 | } 47 | 48 | @override 49 | Widget build(BuildContext context) { 50 | return Scaffold( 51 | body: Scrollbar( 52 | child: ListView( 53 | children: [ 54 | Align( 55 | child: widget.header ?? 56 | Header( 57 | 'Animations', 58 | onPressed: () { 59 | Navigator.pushNamed(context, '/'); 60 | }, 61 | animation: _headerController, 62 | ), 63 | ), 64 | widget.builder( 65 | _controller, 66 | const Card( 67 | color: Colors.yellowAccent, 68 | child: Padding( 69 | padding: EdgeInsets.all(40), 70 | child: Icon( 71 | Icons.star, 72 | size: 50, 73 | ), 74 | ), 75 | ), 76 | ), 77 | ], 78 | ), 79 | ), 80 | ); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /lib/shared/interop.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/foundation.dart'; 2 | import 'package:flutter/services.dart'; 3 | import 'package:universal_html/html.dart' as html; 4 | 5 | Future copyText(String? text) async { 6 | if (kIsWeb) { 7 | await html.window.navigator.clipboard!.writeText(text!); 8 | } else { 9 | await Clipboard.setData(ClipboardData(text: text)); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /lib/shared/lists.dart: -------------------------------------------------------------------------------- 1 | extension IterableEx on Iterable { 2 | Iterable joinEx(T separator) sync* { 3 | final iterator = this.iterator; 4 | if (iterator.moveNext()) { 5 | yield iterator.current; 6 | while (iterator.moveNext()) { 7 | yield separator; 8 | yield iterator.current; 9 | } 10 | } 11 | } 12 | 13 | Iterable mapIndexed(R Function(int index, T) mapper) sync* { 14 | int i = 0; 15 | for (final value in this) { 16 | yield mapper(i++, value); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /lib/shared/network/urls.dart: -------------------------------------------------------------------------------- 1 | const rootUrl = 'https://github.com/tomaszpolanski/flutter-animations/blob'; 2 | const rawUrl = 3 | 'https://raw.githubusercontent.com/tomaszpolanski/flutter-animations'; 4 | -------------------------------------------------------------------------------- /lib/shared/string.dart: -------------------------------------------------------------------------------- 1 | extension StringEx1 on String? { 2 | bool get isNotNullOrEmpty => this?.isNotEmpty == false; 3 | } 4 | 5 | extension StringEx2 on String { 6 | bool containsIgnoreCase(String? other, [int startIndex = 0]) { 7 | if (other == null) { 8 | return true; 9 | } 10 | return toLowerCase().contains(other.toLowerCase(), startIndex); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /lib/shared/system.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_const_declarations 2 | import 'dart:io' as io; 3 | 4 | // ignore: avoid_classes_with_only_static_members 5 | abstract class System { 6 | static const bool isWeb = identical(0, 0.0); 7 | 8 | static final bool isLinux = !isWeb && io.Platform.isLinux; 9 | static final bool isWindows = !isWeb && io.Platform.isWindows; 10 | static final bool isMacOS = !isWeb && io.Platform.isMacOS; 11 | 12 | static final bool isIOS = !isWeb && io.Platform.isIOS; 13 | static final bool isAndroid = !isWeb && io.Platform.isAndroid; 14 | 15 | static final bool isDesktop = isLinux || isWindows || isMacOS; 16 | static final bool isMobile = isIOS || isAndroid; 17 | } 18 | -------------------------------------------------------------------------------- /lib/shared/ui/footer.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/config.dart'; 2 | import 'package:animation_cheat_page/shared/lists.dart'; 3 | import 'package:animation_cheat_page/shared/system.dart'; 4 | import 'package:flutter/material.dart'; 5 | import 'package:url_launcher/url_launcher.dart'; 6 | 7 | class Footer extends StatelessWidget { 8 | const Footer({Key? key}) : super(key: key); 9 | 10 | @override 11 | Widget build(BuildContext context) { 12 | final theme = Theme.of(context); 13 | return DefaultTextStyle.merge( 14 | child: Container( 15 | color: theme.primaryColor, 16 | width: double.infinity, 17 | child: Row( 18 | mainAxisAlignment: MainAxisAlignment.center, 19 | children: [ 20 | _Button( 21 | onPressed: () => Navigator.of(context).pushNamed(Routes.content), 22 | icon: Icons.all_inclusive, 23 | child: Text( 24 | 'Content', 25 | style: theme.textTheme.headline6!.copyWith( 26 | color: Colors.white, 27 | ), 28 | ), 29 | ), 30 | _Button( 31 | onPressed: () => Navigator.of(context).pushNamed(Routes.licenses), 32 | icon: Icons.copyright, 33 | child: const Text('Licenses'), 34 | ), 35 | if (System.isMobile || System.isWeb || System.isMacOS) 36 | _Button( 37 | onPressed: () async { 38 | await launch( 39 | 'https://github.com/tomaszpolanski/flutter-animations/issues/new', 40 | ); 41 | }, 42 | icon: Icons.feedback, 43 | child: const Text('Suggestions'), 44 | ) 45 | ] 46 | .joinEx( 47 | Container( 48 | height: 20, 49 | width: 2, 50 | color: Colors.white, 51 | ), 52 | ) 53 | .toList(), 54 | ), 55 | ), 56 | ); 57 | } 58 | } 59 | 60 | class _Button extends StatelessWidget { 61 | const _Button({ 62 | Key? key, 63 | required this.onPressed, 64 | required this.icon, 65 | required this.child, 66 | }) : super(key: key); 67 | 68 | final VoidCallback onPressed; 69 | final IconData icon; 70 | final Widget child; 71 | 72 | @override 73 | Widget build(BuildContext context) { 74 | return TextButton( 75 | style: TextButton.styleFrom( 76 | padding: const EdgeInsets.symmetric(horizontal: 15), 77 | ), 78 | onPressed: onPressed, 79 | child: Row( 80 | children: [ 81 | Icon(icon, color: Colors.white, size: 16), 82 | const SizedBox(width: 5), 83 | DefaultTextStyle.merge( 84 | style: Theme.of(context).textTheme.headline6!.copyWith( 85 | color: Colors.white, 86 | ), 87 | child: child, 88 | ), 89 | ], 90 | ), 91 | ); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /lib/shared/ui/header.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | import 'package:presentation/presentation.dart'; 5 | 6 | class AnimatedHeader extends StatefulWidget { 7 | const AnimatedHeader( 8 | this.text, { 9 | this.onPressed, 10 | Key? key, 11 | }) : super(key: key); 12 | 13 | final String text; 14 | final VoidCallback? onPressed; 15 | 16 | @override 17 | _AnimatedHeaderState createState() => _AnimatedHeaderState(); 18 | } 19 | 20 | class _AnimatedHeaderState extends State 21 | with SingleTickerProviderStateMixin { 22 | AnimationController? _controller; 23 | 24 | @override 25 | void initState() { 26 | _controller = AnimationController( 27 | vsync: this, 28 | duration: const Duration(seconds: 3), 29 | )..forward(); 30 | super.initState(); 31 | } 32 | 33 | @override 34 | void dispose() { 35 | _controller!.dispose(); 36 | super.dispose(); 37 | } 38 | 39 | @override 40 | Widget build(BuildContext context) { 41 | return Header( 42 | widget.text, 43 | onPressed: widget.onPressed, 44 | animation: _controller, 45 | ); 46 | } 47 | } 48 | 49 | class Header extends StatelessWidget { 50 | Header( 51 | String text, { 52 | required this.animation, 53 | this.onPressed, 54 | Key? key, 55 | }) : letters = text.split(''), 56 | super(key: key); 57 | 58 | final Animation? animation; 59 | final List letters; 60 | final VoidCallback? onPressed; 61 | 62 | int calculateRowCount(double screenWidth) { 63 | if (screenWidth > 1080) { 64 | return 1; 65 | } else if (screenWidth > 700) { 66 | return letters.length > 6 ? 2 : 1; 67 | } 68 | return (letters.length / 2).ceil(); 69 | } 70 | 71 | @override 72 | Widget build(BuildContext context) { 73 | final children = [ 74 | for (var i = 0; i < letters.length; i++) 75 | Container( 76 | height: 140, 77 | width: 100, 78 | color: Color.lerp( 79 | const Color(0xFF64B5F6), 80 | const Color(0xFF0C47A1), 81 | i / letters.length, 82 | ), 83 | child: Center( 84 | child: WrappedAnimatedBuilder( 85 | animation: Tween( 86 | begin: 0, 87 | end: 1, 88 | ).animate( 89 | CurvedAnimation( 90 | parent: animation!, 91 | curve: Interval( 92 | (i / 2) / letters.length, 93 | (1 + i) / letters.length, 94 | curve: Curves.ease, 95 | ), 96 | ), 97 | ), 98 | builder: (context, animation, child) { 99 | return FadeTransition( 100 | opacity: animation, 101 | child: SlideTransition( 102 | position: animation.drive(Tween( 103 | begin: const Offset(-1, 0), 104 | end: const Offset(0, 0), 105 | )), 106 | child: child, 107 | ), 108 | ); 109 | }, 110 | child: Text( 111 | letters[i], 112 | style: GoogleFonts.roboto( 113 | fontSize: 44, 114 | ).copyWith(color: Colors.white), 115 | ), 116 | ), 117 | ), 118 | ), 119 | ]; 120 | final screenWidth = MediaQuery.of(context).size.width; 121 | final rowCount = calculateRowCount(screenWidth); 122 | return GestureDetector( 123 | onTap: onPressed, 124 | child: Padding( 125 | padding: const EdgeInsets.all(20), 126 | child: Card( 127 | clipBehavior: Clip.antiAlias, 128 | child: Column( 129 | children: [ 130 | const Padding( 131 | padding: EdgeInsets.symmetric(vertical: 20), 132 | child: FlutterLogo(size: 140), 133 | ), 134 | for (int i = 0; i < rowCount; i++) 135 | Row( 136 | mainAxisSize: MainAxisSize.min, 137 | children: children.sublist( 138 | i * (children.length / rowCount).round(), 139 | (i + 1) * (children.length / rowCount).round(), 140 | ), 141 | ), 142 | ], 143 | ), 144 | ), 145 | ), 146 | ); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /lib/shared/ui/new_label.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | import 'package:google_fonts/google_fonts.dart'; 4 | 5 | class NewLabel extends StatelessWidget { 6 | const NewLabel({Key? key}) : super(key: key); 7 | 8 | @override 9 | Widget build(BuildContext context) { 10 | return Container( 11 | padding: const EdgeInsets.symmetric(horizontal: 6), 12 | decoration: BoxDecoration( 13 | color: Theme.of(context).accentColor, 14 | borderRadius: const BorderRadius.all(Radius.circular(15)), 15 | ), 16 | child: Padding( 17 | padding: const EdgeInsets.only(left: 8, right: 11), 18 | child: Text( 19 | 'new', 20 | style: GoogleFonts.roboto( 21 | fontSize: 30, 22 | fontWeight: FontWeight.bold, 23 | fontStyle: FontStyle.italic, 24 | ).copyWith(color: Colors.white), 25 | ), 26 | ), 27 | ); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /lib/shared/ui/new_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/ui/section.dart'; 2 | import 'package:animation_cheat_page/shared/ui/separator.dart'; 3 | import 'package:animation_cheat_page/transitions/all_transitions.dart'; 4 | import 'package:flutter/material.dart'; 5 | 6 | import 'new_label.dart'; 7 | 8 | class NewSection extends StatelessWidget { 9 | const NewSection({ 10 | Key? key, 11 | required this.transitions, 12 | required this.animated, 13 | required this.curves, 14 | }) : super(key: key); 15 | 16 | final List transitions; 17 | final List animated; 18 | final List curves; 19 | 20 | @override 21 | Widget build(BuildContext context) { 22 | final children = [ 23 | ...transitions.where((example) => showNew(example.released)), 24 | ...animated.where((example) => showNew(example.released)), 25 | ...curves.where((example) => showNew(example.released)), 26 | ] 27 | .map((example) => Text( 28 | ' • ${example.title}', 29 | style: 30 | Theme.of(context).textTheme.headline6!.copyWith(fontSize: 25), 31 | )) 32 | .toList(growable: false); 33 | return children.isNotEmpty 34 | ? Container( 35 | width: 640, 36 | padding: const EdgeInsets.symmetric(horizontal: 20), 37 | child: Column( 38 | crossAxisAlignment: CrossAxisAlignment.start, 39 | children: [ 40 | Row( 41 | crossAxisAlignment: CrossAxisAlignment.baseline, 42 | children: [ 43 | const NewLabel(), 44 | Text( 45 | ' wigets:', 46 | style: Theme.of(context).textTheme.headline4!.copyWith( 47 | fontWeight: FontWeight.bold, color: Colors.black), 48 | ), 49 | ], 50 | ), 51 | ...children, 52 | const Align(child: Separator()), 53 | ], 54 | ), 55 | ) 56 | : const SizedBox(); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /lib/shared/ui/search_bar.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | 3 | class SearchBar extends StatefulWidget implements PreferredSizeWidget { 4 | const SearchBar({ 5 | Key? key, 6 | required this.onChanged, 7 | }) : super(key: key); 8 | 9 | final ValueChanged onChanged; 10 | 11 | @override 12 | _SearchBarState createState() => _SearchBarState(); 13 | 14 | @override 15 | Size get preferredSize => const Size.fromHeight(kToolbarHeight); 16 | } 17 | 18 | class _SearchBarState extends State { 19 | TextEditingController? _controller; 20 | 21 | @override 22 | void initState() { 23 | _controller = TextEditingController() 24 | ..addListener(() { 25 | widget.onChanged(_controller!.text); 26 | }); 27 | super.initState(); 28 | } 29 | 30 | @override 31 | void dispose() { 32 | _controller!.dispose(); 33 | super.dispose(); 34 | } 35 | 36 | @override 37 | Widget build(BuildContext context) { 38 | return AppBar( 39 | automaticallyImplyLeading: false, 40 | title: TextField( 41 | controller: _controller, 42 | style: Theme.of(context).primaryTextTheme.headline6, 43 | cursorColor: Theme.of(context).primaryTextTheme.headline6!.color, 44 | keyboardType: TextInputType.text, 45 | autofocus: true, 46 | ), 47 | actions: [ 48 | IconButton( 49 | icon: const Icon(Icons.clear), 50 | onPressed: _controller!.clear, 51 | ) 52 | ], 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /lib/shared/ui/separator.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class Separator extends StatelessWidget { 5 | const Separator({Key? key}) : super(key: key); 6 | 7 | @override 8 | Widget build(BuildContext context) { 9 | return Row( 10 | mainAxisSize: MainAxisSize.min, 11 | children: [ 12 | for (var i = 0; i < 3; i++) 13 | Container( 14 | height: 4, 15 | width: 4, 16 | margin: const EdgeInsets.all(20), 17 | decoration: BoxDecoration( 18 | shape: BoxShape.circle, 19 | color: Colors.black.withOpacity(0.7), 20 | ), 21 | ) 22 | ], 23 | ); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/slivers/constraints/slivers_constraints.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_single_quotes 2 | import 'package:animation_cheat_page/config.dart'; 3 | import 'package:animation_cheat_page/shared/deferred.dart'; 4 | import 'package:animation_cheat_page/shared/enum.dart'; 5 | import 'package:animation_cheat_page/slivers/constraints/slivers_constraints_page.dart' 6 | deferred as constraints; 7 | import 'package:animation_cheat_page/slivers/shared/overlapping.dart'; 8 | import 'package:animation_cheat_page/slivers/shared/sliver_section.dart'; 9 | import 'package:animation_cheat_page/slivers/single_page.dart' 10 | deferred as single; 11 | import 'package:flutter/rendering.dart'; 12 | import 'package:flutter/widgets.dart'; 13 | 14 | const description = ''' 15 | This page explains in an interactive way how 16 | different **SliverConstraints** describe Slivers.'''; 17 | 18 | final sliverExamples = >[ 19 | SliverSectionData( 20 | title: 'userScrollDirection', 21 | description: 22 | "The direction in which the user is attempting to scroll, relative to the " 23 | "**axisDirection** and **growthDirection**.", 24 | mapper: (constraints) => fromEnum(constraints.userScrollDirection), 25 | ), 26 | SliverSectionData( 27 | title: 'scrollOffset', 28 | description: "The scroll offset, in this sliver's coordinate " 29 | "system, that corresponds to the earliest visible part of this sliver in " 30 | "the **AxisDirection** if **growthDirection** is **GrowthDirection.forward** " 31 | "or in the opposite **AxisDirection** direction if **growthDirection** " 32 | "is **GrowthDirection.reverse**.", 33 | mapper: (constraints) => constraints.scrollOffset.round(), 34 | ), 35 | SliverSectionData( 36 | title: 'precedingScrollExtent', 37 | description: 38 | "The scroll distance that has been consumed by all **Sliver**s that came " 39 | "before this **Sliver**.", 40 | mapper: (constraints) => constraints.precedingScrollExtent.round(), 41 | ), 42 | SliverSectionData( 43 | title: 'overlap', 44 | description: 45 | "The number of pixels from where the pixels corresponding to the " 46 | "**scrollOffset** will be painted up to the first pixel that has not yet been " 47 | "painted on by an earlier sliver, in the **axisDirection**.", 48 | leading: const SliverOverlappingExample( 49 | layoutPercentage: 0.8, 50 | child: Placeholder(fallbackHeight: 100), 51 | ), 52 | mapper: (constraints) => constraints.overlap.round(), 53 | ), 54 | SliverSectionData( 55 | title: 'remainingPaintExtent', 56 | description: 57 | "The number of pixels of content that the sliver should consider providing." 58 | "\n(Providing more pixels than this is inefficient.)\n" 59 | "The actual number of pixels provided should be specified in the " 60 | "**RenderSliver.geometry** as **SliverGeometry.paintExtent**.", 61 | mapper: (constraints) => constraints.remainingPaintExtent.round(), 62 | ), 63 | SliverSectionData( 64 | title: 'crossAxisExtent', 65 | description: 'The number of pixels in the cross-axis.\n' 66 | 'For a vertical list, this is the width of the sliver.', 67 | mapper: (constraints) => constraints.crossAxisExtent.round(), 68 | ), 69 | SliverSectionData( 70 | title: 'viewportMainAxisExtent', 71 | description: 72 | "The number of pixels the viewport can display in the main axis.", 73 | mapper: (constraints) => constraints.viewportMainAxisExtent.round(), 74 | ), 75 | SliverSectionData( 76 | title: 'cacheOrigin', 77 | description: "Where the cache area starts relative to the **scrollOffset**", 78 | mapper: (constraints) => constraints.cacheOrigin.round(), 79 | ), 80 | SliverSectionData( 81 | title: 'remainingCacheExtent', 82 | description: 83 | "Describes how much content the sliver should provide starting from the " 84 | "**cacheOrigin**.", 85 | mapper: (constraints) => constraints.remainingCacheExtent.round(), 86 | ), 87 | ]; 88 | 89 | Map get pages { 90 | return { 91 | Routes.slivers_constraints: (_) => Deferred( 92 | future: constraints.loadLibrary(), 93 | // ignore: prefer_const_constructors 94 | builder: (_) => constraints.SliversConstraintsPage(), 95 | ), 96 | ...singlePages( 97 | sliverExamples, 98 | builder: (example) => Deferred( 99 | future: single.loadLibrary(), 100 | // ignore: prefer_const_constructors 101 | builder: (_) => single.SingleSliverConstraintsPage(example), 102 | ), 103 | ) 104 | }; 105 | } 106 | 107 | Map singlePages( 108 | List> examples, { 109 | Widget Function(SliverSectionData data)? builder, 110 | }) { 111 | return Map.fromEntries( 112 | examples.map((example) { 113 | return MapEntry( 114 | '${Routes.slivers}/${example.title}', 115 | (_) => builder!(example), 116 | ); 117 | }), 118 | ); 119 | } 120 | -------------------------------------------------------------------------------- /lib/slivers/constraints/slivers_constraints_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/header_page.dart'; 2 | import 'package:animation_cheat_page/shared/ui/header.dart'; 3 | import 'package:animation_cheat_page/shared/ui/section.dart'; 4 | import 'package:animation_cheat_page/slivers/constraints/slivers_constraints.dart' 5 | as slivers; 6 | import 'package:animation_cheat_page/slivers/shared/scroll_constraints.dart'; 7 | import 'package:animation_cheat_page/slivers/shared/sliver_section.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter/widgets.dart'; 10 | import 'package:google_fonts/google_fonts.dart'; 11 | import 'package:presentation/presentation.dart'; 12 | 13 | class SliversConstraintsPage extends StatelessWidget { 14 | const SliversConstraintsPage({Key? key}) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final body = Container( 19 | height: 100, 20 | color: Colors.red, 21 | ); 22 | return HeaderPage( 23 | repeatAnimations: false, 24 | header: AnimatedHeader( 25 | 'Sliver', 26 | onPressed: () => Navigator.pushNamed(context, '/'), 27 | ), 28 | builder: (animation, child) { 29 | return Column( 30 | children: [ 31 | SectionHeader( 32 | title: const Text('Constraints'), 33 | child: Markdown( 34 | slivers.description, 35 | style: GoogleFonts.crimsonPro(), 36 | ), 37 | ), 38 | for (final sliverData in slivers.sliverExamples) 39 | SliverSection( 40 | title: sliverData.title, 41 | body: Markdown( 42 | sliverData.description, 43 | style: GoogleFonts.crimsonPro(), 44 | ), 45 | leading: sliverData.leading, 46 | trailing: sliverData.trailing, 47 | builder: (context, onChanged) { 48 | return SliverValueChanged( 49 | onConstraintsChanged: (constraints) { 50 | onChanged(sliverData.mapper(constraints)); 51 | }, 52 | child: body, 53 | ); 54 | }, 55 | ), 56 | ], 57 | ); 58 | }, 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/slivers/fill_remaining/pages.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/config.dart'; 2 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/fill_overscroll.dart'; 3 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/fill_remaining_page.dart'; 4 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/has_scroll_body.dart'; 5 | import 'package:flutter/material.dart'; 6 | 7 | Map get pages { 8 | return { 9 | Routes.slivers_fill_remaining_example: (_) => const FillRemainingPage(), 10 | Routes.slivers_fill_remaining_fill_overscroll: (_) => 11 | const FillOverscrollPage(), 12 | Routes.slivers_fill_remaining_has_scroll_body: (_) => 13 | const HasScrollBodyPage(), 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /lib/slivers/fill_remaining/pages/fill_overscroll.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/frame.dart'; 2 | import 'package:animation_cheat_page/shared/ui/section.dart'; 3 | import 'package:animation_cheat_page/slivers/fill_remaining/fill_remaining_sliver.dart'; 4 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/widgets.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:google_fonts/google_fonts.dart'; 8 | import 'package:presentation/presentation.dart'; 9 | 10 | class FillOverscrollPage extends StatelessWidget { 11 | const FillOverscrollPage({Key? key}) : super(key: key); 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | final SliverExample example = widgetExamples[FillOverscrollPage]!; 16 | return MaterialApp( 17 | color: Colors.white, 18 | home: Theme( 19 | data: Theme.of(context).copyWith(platform: TargetPlatform.iOS), 20 | child: Scaffold( 21 | body: DoubleSection( 22 | title: example.title, 23 | url: '', 24 | released: DateTime(2000), 25 | body: Markdown( 26 | example.description, 27 | style: GoogleFonts.crimsonPro(), 28 | ), 29 | children: const [ 30 | AppFrameCard( 31 | title: 'fillOverscroll: false', 32 | child: _OverscrollExample(fillOverscroll: false), 33 | ), 34 | AppFrameCard( 35 | title: 'fillOverscroll: true', 36 | child: _OverscrollExample(fillOverscroll: true), 37 | ), 38 | ], 39 | ), 40 | ), 41 | ), 42 | ); 43 | } 44 | } 45 | 46 | class _OverscrollExample extends StatelessWidget { 47 | const _OverscrollExample({ 48 | Key? key, 49 | required this.fillOverscroll, 50 | }) : super(key: key); 51 | 52 | final bool fillOverscroll; 53 | 54 | @override 55 | Widget build(BuildContext context) { 56 | return CustomScrollView( 57 | slivers: [ 58 | SliverList( 59 | delegate: SliverChildListDelegate(const [ 60 | ListTile(title: Text('First item')), 61 | ListTile(title: Text('Second item')), 62 | ListTile(title: Text('Third item')), 63 | ListTile(title: Text('Fourth item')), 64 | ]), 65 | ), 66 | CustomSliverFillRemaining( 67 | hasScrollBody: false, 68 | fillOverscroll: fillOverscroll, 69 | child: Container( 70 | color: Colors.yellowAccent, 71 | child: Column( 72 | mainAxisAlignment: MainAxisAlignment.center, 73 | children: const [ 74 | FlutterLogo(size: 50), 75 | Text( 76 | 'This is some longest text that should be centered ' 77 | 'together with the logo', 78 | textAlign: TextAlign.center, 79 | ), 80 | ], 81 | ), 82 | ), 83 | ) 84 | ], 85 | ); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/slivers/fill_remaining/pages/fill_remaining_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/header_page.dart'; 2 | import 'package:animation_cheat_page/shared/ui/section.dart'; 3 | import 'package:animation_cheat_page/slivers/fill_remaining/fill_remaining_sliver.dart'; 4 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/widgets.dart'; 5 | import 'package:flutter/material.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:google_fonts/google_fonts.dart'; 8 | import 'package:presentation/presentation.dart'; 9 | 10 | class FillRemainingPage extends StatelessWidget { 11 | const FillRemainingPage({ 12 | Key? key, 13 | }) : super(key: key); 14 | 15 | @override 16 | Widget build(BuildContext context) { 17 | return HeaderPage( 18 | repeatAnimations: false, 19 | builder: (_, __) => const _SliverFillRemainingContent(), 20 | ); 21 | } 22 | } 23 | 24 | class _SliverFillRemainingContent extends StatelessWidget { 25 | const _SliverFillRemainingContent({Key? key}) : super(key: key); 26 | 27 | @override 28 | Widget build(BuildContext context) { 29 | final SliverExample example = widgetExamples[SliverFillRemaining]!; 30 | return Section( 31 | title: example.title, 32 | url: example.fileUrl, 33 | released: DateTime(2000), 34 | body: Markdown( 35 | example.description, 36 | style: GoogleFonts.crimsonPro(), 37 | ), 38 | child: CustomScrollView( 39 | slivers: [ 40 | SliverList( 41 | delegate: SliverChildListDelegate(const [ 42 | ListTile(title: Text('First item')), 43 | ListTile(title: Text('Second item')), 44 | ListTile(title: Text('Third item')), 45 | ListTile(title: Text('Fourth item')), 46 | ListTile(title: Text('Second item')), 47 | ListTile(title: Text('Third item')), 48 | ListTile(title: Text('Fourth item')), 49 | ListTile(title: Text('Second item')), 50 | ListTile(title: Text('Third item')), 51 | ListTile(title: Text('Fourth item')), 52 | ]), 53 | ), 54 | CustomSliverFillRemaining( 55 | hasScrollBody: false, 56 | fillOverscroll: true, 57 | child: Container( 58 | color: Colors.yellowAccent, 59 | child: Column( 60 | mainAxisAlignment: MainAxisAlignment.center, 61 | children: const [ 62 | FlutterLogo(size: 200), 63 | Text( 64 | 'This is some longest text that should be centered ' 65 | 'together with the logo', 66 | textAlign: TextAlign.center, 67 | ), 68 | ], 69 | ), 70 | ), 71 | ) 72 | ], 73 | ), 74 | ); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /lib/slivers/fill_remaining/pages/has_scroll_body.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/frame.dart'; 2 | import 'package:animation_cheat_page/shared/ui/footer.dart'; 3 | import 'package:animation_cheat_page/shared/ui/section.dart'; 4 | import 'package:animation_cheat_page/slivers/fill_remaining/fill_remaining_sliver.dart'; 5 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/widgets.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | import 'package:google_fonts/google_fonts.dart'; 9 | import 'package:presentation/presentation.dart'; 10 | 11 | class HasScrollBodyPage extends StatelessWidget { 12 | const HasScrollBodyPage({Key? key}) : super(key: key); 13 | 14 | @override 15 | Widget build(BuildContext context) { 16 | final SliverExample example = widgetExamples[HasScrollBodyPage]!; 17 | return Scaffold( 18 | body: Column( 19 | children: [ 20 | Expanded( 21 | child: DoubleSection( 22 | title: example.title, 23 | url: example.fileUrl, 24 | released: DateTime(2000), 25 | body: Markdown( 26 | example.description, 27 | style: GoogleFonts.crimsonPro(), 28 | ), 29 | children: [ 30 | AppFrameCard( 31 | title: 'hasScrollBody: false', 32 | child: _HasScrollBodyExample( 33 | hasScrollBody: false, 34 | builder: (children) => Column( 35 | crossAxisAlignment: CrossAxisAlignment.stretch, 36 | children: children, 37 | ), 38 | ), 39 | ), 40 | AppFrameCard( 41 | title: 'hasScrollBody: true', 42 | child: _HasScrollBodyExample( 43 | hasScrollBody: true, 44 | builder: (children) => ListView(children: children), 45 | ), 46 | ), 47 | ], 48 | ), 49 | ), 50 | const Footer(), 51 | ], 52 | ), 53 | ); 54 | } 55 | } 56 | 57 | class _HasScrollBodyExample extends StatelessWidget { 58 | const _HasScrollBodyExample({ 59 | Key? key, 60 | required this.hasScrollBody, 61 | required this.builder, 62 | }) : super(key: key); 63 | 64 | final bool hasScrollBody; 65 | final Widget Function(List children) builder; 66 | 67 | @override 68 | Widget build(BuildContext context) { 69 | return CustomScrollView( 70 | slivers: [ 71 | SliverList( 72 | delegate: SliverChildListDelegate(const [ 73 | ListTile(title: Text('First item')), 74 | ListTile(title: Text('Second item')), 75 | ]), 76 | ), 77 | CustomSliverFillRemaining( 78 | child: builder( 79 | [ 80 | Container( 81 | color: Colors.yellowAccent, 82 | child: const FlutterLogo(size: 50), 83 | ), 84 | Container( 85 | color: Colors.yellowAccent, 86 | child: const ListTile(title: Text('First item')), 87 | ), 88 | Container( 89 | color: Colors.yellowAccent, 90 | child: const ListTile(title: Text('Second item')), 91 | ), 92 | Container( 93 | color: Colors.yellowAccent, 94 | child: const ListTile(title: Text('Third item')), 95 | ), 96 | Container( 97 | color: Colors.yellowAccent, 98 | child: const ListTile(title: Text('Fourth item')), 99 | ), 100 | Container( 101 | color: Colors.yellowAccent, 102 | child: const ListTile(title: Text('Fifth item')), 103 | ), 104 | Container( 105 | color: Colors.yellowAccent, 106 | child: const ListTile(title: Text('Sixth item')), 107 | ), 108 | ], 109 | ), 110 | ) 111 | ], 112 | ); 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /lib/slivers/fill_remaining/pages/widgets.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/network/urls.dart'; 2 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/fill_overscroll.dart'; 3 | import 'package:animation_cheat_page/slivers/fill_remaining/pages/has_scroll_body.dart'; 4 | import 'package:flutter/widgets.dart'; 5 | 6 | final widgetExamples = { 7 | SliverFillRemaining: const SliverExample( 8 | title: 'SliverFillRemaining', 9 | description: 'This Widget is irreplaceable when you want to ' 10 | 'center your content even if there is not enough space for it.', 11 | url: 'master/lib/slivers/fill_remaining/pages/fill_remaining_page.dart', 12 | ), 13 | FillOverscrollPage: const SliverExample( 14 | title: 'fillOverscroll', 15 | description: 'Demonstration how **fillOverscroll** works.', 16 | url: 'master/lib/slivers/fill_remaining/pages/fill_overscroll.dart', 17 | ), 18 | HasScrollBodyPage: const SliverExample( 19 | title: 'hasScrollBody', 20 | description: 'Demonstration how **hasScrollBody** works.\n' 21 | '**SliverFillRemaining** should not be larger than the available viewport. ' 22 | 'If the content is bigger you could either consider making it scrollable ' 23 | 'with **hasScrollBody** or just put that content in another scrollable sliver.', 24 | url: 'master/lib/slivers/fill_remaining/pages/has_scroll_body.dart', 25 | ), 26 | }; 27 | 28 | class SliverExample { 29 | const SliverExample({ 30 | required this.title, 31 | required this.description, 32 | required String url, 33 | }) : _url = url; 34 | 35 | final String title; 36 | final String description; 37 | 38 | final String _url; 39 | 40 | String get fileUrl => '$rawUrl/$_url'; 41 | } 42 | -------------------------------------------------------------------------------- /lib/slivers/geometry/slivers_geomerty_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/header_page.dart'; 2 | import 'package:animation_cheat_page/shared/ui/header.dart'; 3 | import 'package:animation_cheat_page/shared/ui/section.dart'; 4 | import 'package:animation_cheat_page/slivers/geometry/slivers_geometry.dart' 5 | as slivers; 6 | import 'package:animation_cheat_page/slivers/shared/scroll_constraints.dart'; 7 | import 'package:animation_cheat_page/slivers/shared/sliver_section.dart'; 8 | import 'package:flutter/material.dart'; 9 | import 'package:flutter/widgets.dart'; 10 | import 'package:google_fonts/google_fonts.dart'; 11 | import 'package:presentation/presentation.dart'; 12 | 13 | class SliversGeometryPage extends StatelessWidget { 14 | const SliversGeometryPage({Key? key}) : super(key: key); 15 | 16 | @override 17 | Widget build(BuildContext context) { 18 | final body = Container( 19 | height: 100, 20 | color: Colors.red, 21 | ); 22 | return HeaderPage( 23 | repeatAnimations: false, 24 | header: AnimatedHeader( 25 | 'Sliver', 26 | onPressed: () => Navigator.pushNamed(context, '/'), 27 | ), 28 | builder: (animation, child) { 29 | return Column( 30 | children: [ 31 | SectionHeader( 32 | title: const Text('Geometry'), 33 | child: Markdown( 34 | slivers.description, 35 | style: GoogleFonts.crimsonPro(), 36 | ), 37 | ), 38 | for (final sliverData in slivers.sliverExamples) 39 | SliverSection( 40 | title: sliverData.title, 41 | body: Markdown( 42 | sliverData.description, 43 | style: GoogleFonts.crimsonPro(), 44 | ), 45 | leading: sliverData.leading, 46 | trailing: sliverData.trailing, 47 | builder: (context, onChanged) { 48 | return SliverValueChanged( 49 | onGeometryChanged: (geometry) { 50 | onChanged(sliverData.mapper(geometry)); 51 | }, 52 | child: body, 53 | ); 54 | }, 55 | ), 56 | ], 57 | ); 58 | }, 59 | ); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /lib/slivers/geometry/slivers_geometry.dart: -------------------------------------------------------------------------------- 1 | // ignore_for_file: prefer_single_quotes 2 | import 'package:animation_cheat_page/config.dart'; 3 | import 'package:animation_cheat_page/shared/deferred.dart'; 4 | import 'package:animation_cheat_page/slivers/geometry/slivers_geomerty_page.dart' 5 | deferred as geometry; 6 | import 'package:animation_cheat_page/slivers/shared/sliver_section.dart'; 7 | import 'package:animation_cheat_page/slivers/single_page.dart' 8 | deferred as single; 9 | import 'package:flutter/rendering.dart'; 10 | import 'package:flutter/widgets.dart'; 11 | 12 | const description = ''' 13 | This page explains in an interactive way how 14 | different **SliverGeometry** describe Slivers.'''; 15 | 16 | final sliverExamples = >[ 17 | SliverSectionData( 18 | title: 'scrollExtent', 19 | description: "Total size of the sliver", 20 | mapper: (geometry) => geometry.scrollExtent.round(), 21 | ), 22 | SliverSectionData( 23 | title: 'paintOrigin', 24 | description: 25 | "The visual location of the first visible part of this sliver relative to " 26 | "its layout position.", 27 | mapper: (geometry) => geometry.paintOrigin.round(), 28 | ), 29 | SliverSectionData( 30 | title: 'paintExtent', 31 | description: 32 | "The visible size of the sliver. If only 50% of the sliver is visible " 33 | "then **paintExtent** is 50% of **Geometry.scrollExtent**", 34 | mapper: (geometry) => geometry.paintExtent.round(), 35 | ), 36 | SliverSectionData( 37 | title: 'layoutExtent', 38 | description: 39 | "The distance from the first visible part of this sliver to the first " 40 | "visible part of the next sliver, assuming the next sliver's " 41 | "**SliverConstraints.scrollOffset** is zero.", 42 | mapper: (geometry) => geometry.layoutExtent.round(), 43 | ), 44 | SliverSectionData( 45 | title: 'maxPaintExtent', 46 | description: 47 | "The (estimated) total paint extent that this sliver would be able to " 48 | "provide if the **SliverConstraints.remainingPaintExtent** was infinite.", 49 | mapper: (geometry) => geometry.maxPaintExtent.round(), 50 | ), 51 | SliverSectionData( 52 | title: 'maxScrollObstructionExtent', 53 | description: 54 | "The maximum extent by which this sliver can reduce the area in which " 55 | "content can scroll if the sliver were pinned at the edge.", 56 | mapper: (geometry) => geometry.maxScrollObstructionExtent.round(), 57 | ), 58 | SliverSectionData( 59 | title: 'hitTestExtent', 60 | description: 61 | "The distance from where this sliver started painting to the bottom of " 62 | "where it should accept touch events.", 63 | mapper: (geometry) => geometry.hitTestExtent.round(), 64 | ), 65 | SliverSectionData( 66 | title: 'visible', 67 | description: "Whether this sliver should be painted.", 68 | mapper: (geometry) => geometry.visible, 69 | ), 70 | SliverSectionData( 71 | title: 'hasVisualOverflow', 72 | description: "Whether this sliver has visual overflow.", 73 | mapper: (geometry) => geometry.hasVisualOverflow, 74 | ), 75 | SliverSectionData( 76 | title: 'scrollOffsetCorrection', 77 | description: 78 | "If this is non-zero after **RenderSliver.performLayout** returns, the scroll " 79 | "offset will be adjusted by the parent and then the entire layout of the " 80 | "parent will be rerun.", 81 | mapper: (geometry) => geometry.scrollOffsetCorrection?.round(), 82 | ), 83 | SliverSectionData( 84 | title: 'cacheExtent', 85 | description: "How many pixels the sliver has consumed in the " 86 | "**SliverConstraints.remainingCacheExtent**.", 87 | leading: const SliverToBoxAdapter( 88 | child: Placeholder(fallbackHeight: 1000), 89 | ), 90 | trailing: const SliverToBoxAdapter( 91 | child: Placeholder(fallbackHeight: 1000), 92 | ), 93 | mapper: (geometry) => geometry.cacheExtent.round(), 94 | ), 95 | ]; 96 | 97 | Map get pages { 98 | return { 99 | Routes.slivers_geometry: (_) => Deferred( 100 | future: geometry.loadLibrary(), 101 | // ignore: prefer_const_constructors 102 | builder: (_) => geometry.SliversGeometryPage(), 103 | ), 104 | ...singlePages( 105 | sliverExamples, 106 | builder: (example) => Deferred( 107 | future: single.loadLibrary(), 108 | // ignore: prefer_const_constructors 109 | builder: (_) => single.SingleSliverGeometryPage(example), 110 | ), 111 | ), 112 | }; 113 | } 114 | 115 | Map singlePages( 116 | List> examples, { 117 | Widget Function(SliverSectionData data)? builder, 118 | }) { 119 | return Map.fromEntries( 120 | examples.map((example) { 121 | return MapEntry( 122 | '${Routes.slivers}/${example.title}', 123 | (_) => builder!(example), 124 | ); 125 | }), 126 | ); 127 | } 128 | -------------------------------------------------------------------------------- /lib/slivers/shared/overlapping.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | class SliverOverlappingExample extends SingleChildRenderObjectWidget { 5 | const SliverOverlappingExample({ 6 | Key? key, 7 | required this.layoutPercentage, 8 | Widget? child, 9 | }) : super(key: key, child: child); 10 | 11 | final double layoutPercentage; 12 | 13 | @override 14 | RenderSliverOverlapping createRenderObject(BuildContext context) => 15 | RenderSliverOverlapping(layoutPercentage); 16 | } 17 | 18 | class RenderSliverOverlapping extends RenderSliverSingleBoxAdapter { 19 | RenderSliverOverlapping( 20 | this.layoutPercentage, { 21 | RenderBox? child, 22 | }) : super(child: child); 23 | 24 | final double layoutPercentage; 25 | 26 | @override 27 | void performLayout() { 28 | if (child == null) { 29 | geometry = SliverGeometry.zero; 30 | return; 31 | } 32 | child!.layout(constraints.asBoxConstraints(), parentUsesSize: true); 33 | double? childExtent; 34 | switch (constraints.axis) { 35 | case Axis.horizontal: 36 | childExtent = child!.size.width; 37 | break; 38 | case Axis.vertical: 39 | childExtent = child!.size.height; 40 | break; 41 | } 42 | 43 | final double paintedChildSize = 44 | calculatePaintOffset(constraints, from: 0, to: childExtent); 45 | final double cacheExtent = 46 | calculateCacheOffset(constraints, from: 0, to: childExtent); 47 | 48 | assert(paintedChildSize.isFinite); 49 | assert(paintedChildSize >= 0.0); 50 | geometry = SliverGeometry( 51 | scrollExtent: childExtent, 52 | paintExtent: paintedChildSize, 53 | layoutExtent: paintedChildSize * layoutPercentage, 54 | cacheExtent: cacheExtent, 55 | maxPaintExtent: childExtent, 56 | hitTestExtent: paintedChildSize, 57 | hasVisualOverflow: childExtent > constraints.remainingPaintExtent || 58 | constraints.scrollOffset > 0.0, 59 | ); 60 | setChildParentData(child!, constraints, geometry!); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /lib/slivers/shared/scroll_constraints.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/rendering.dart'; 3 | 4 | class SliverValueChanged extends SingleChildRenderObjectWidget { 5 | const SliverValueChanged({ 6 | Key? key, 7 | Widget? child, 8 | this.onConstraintsChanged, 9 | this.onGeometryChanged, 10 | }) : assert(onConstraintsChanged != null || onGeometryChanged != null), 11 | super(key: key, child: child); 12 | 13 | final ValueChanged? onConstraintsChanged; 14 | final ValueChanged? onGeometryChanged; 15 | 16 | @override 17 | RenderSliverExample createRenderObject(BuildContext context) => 18 | RenderSliverExample( 19 | onConstraintsChanged: onConstraintsChanged, 20 | onGeometryChanged: onGeometryChanged, 21 | ); 22 | } 23 | 24 | class RenderSliverExample extends RenderSliverSingleBoxAdapter { 25 | RenderSliverExample({ 26 | required this.onConstraintsChanged, 27 | required this.onGeometryChanged, 28 | RenderBox? child, 29 | }) : super(child: child); 30 | 31 | final ValueChanged? onConstraintsChanged; 32 | final ValueChanged? onGeometryChanged; 33 | 34 | @override 35 | void performLayout() { 36 | if (child == null) { 37 | geometry = SliverGeometry.zero; 38 | return; 39 | } 40 | child!.layout(constraints.asBoxConstraints(), parentUsesSize: true); 41 | double? childExtent; 42 | switch (constraints.axis) { 43 | case Axis.horizontal: 44 | childExtent = child!.size.width; 45 | break; 46 | case Axis.vertical: 47 | childExtent = child!.size.height; 48 | break; 49 | } 50 | final double paintedChildSize = 51 | calculatePaintOffset(constraints, from: 0, to: childExtent); 52 | final double cacheExtent = 53 | calculateCacheOffset(constraints, from: 0, to: childExtent); 54 | 55 | assert(paintedChildSize.isFinite); 56 | assert(paintedChildSize >= 0.0); 57 | geometry = SliverGeometry( 58 | scrollExtent: childExtent, 59 | paintExtent: paintedChildSize, 60 | cacheExtent: cacheExtent, 61 | maxPaintExtent: childExtent, 62 | hasVisualOverflow: childExtent > constraints.remainingPaintExtent || 63 | constraints.scrollOffset > 0.0, 64 | ); 65 | if (onConstraintsChanged != null) { 66 | onConstraintsChanged!(constraints); 67 | } 68 | final onGeo = onGeometryChanged; 69 | final geo = geometry; 70 | if (onGeo != null && geo != null) { 71 | onGeo(geo); 72 | } 73 | setChildParentData(child!, constraints, geometry!); 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /lib/slivers/shared/sliver_section.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/network/urls.dart'; 2 | import 'package:animation_cheat_page/shared/ui/section.dart'; 3 | import 'package:flutter/material.dart'; 4 | 5 | class SliverSection extends StatefulWidget { 6 | const SliverSection({ 7 | Key? key, 8 | required this.title, 9 | required this.body, 10 | this.leading, 11 | this.trailing, 12 | required this.builder, 13 | }) : super(key: key); 14 | 15 | final String title; 16 | final Widget body; 17 | final Widget? leading; 18 | final Widget? trailing; 19 | final Widget Function(BuildContext, ValueChanged onChanged) builder; 20 | 21 | @override 22 | _SliverSectionState createState() => _SliverSectionState(); 23 | } 24 | 25 | class _SliverSectionState extends State { 26 | String _value = ''; 27 | 28 | @override 29 | Widget build(BuildContext context) { 30 | return Section( 31 | title: '$_value ${widget.title}', 32 | url: '$rawUrl/master/lib/slivers/shared/scroll_constraints.dart', 33 | released: DateTime(2000), 34 | body: widget.body, 35 | onPressed: () {}, 36 | child: CustomScrollView( 37 | slivers: [ 38 | widget.leading ?? 39 | const SliverToBoxAdapter( 40 | child: Placeholder(fallbackHeight: 500), 41 | ), 42 | widget.builder(context, (value) { 43 | WidgetsBinding.instance!.addPostFrameCallback((_) { 44 | setState(() => _value = value.toString()); 45 | }); 46 | }), 47 | widget.trailing ?? 48 | const SliverToBoxAdapter( 49 | child: Placeholder(fallbackHeight: 500), 50 | ), 51 | ], 52 | ), 53 | ); 54 | } 55 | } 56 | 57 | class SliverSectionData { 58 | const SliverSectionData({ 59 | required this.title, 60 | required this.description, 61 | required this.mapper, 62 | this.leading, 63 | this.trailing, 64 | }); 65 | 66 | final String title; 67 | final String description; 68 | final Widget? leading; 69 | final Widget? trailing; 70 | final Object? Function(T) mapper; 71 | } 72 | -------------------------------------------------------------------------------- /lib/slivers/single_page.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/shared/header_page.dart'; 2 | import 'package:animation_cheat_page/shared/ui/header.dart'; 3 | import 'package:animation_cheat_page/shared/ui/section.dart'; 4 | import 'package:animation_cheat_page/slivers/shared/scroll_constraints.dart'; 5 | import 'package:animation_cheat_page/slivers/shared/sliver_section.dart'; 6 | import 'package:flutter/material.dart'; 7 | import 'package:flutter/rendering.dart'; 8 | import 'package:flutter/widgets.dart'; 9 | import 'package:google_fonts/google_fonts.dart'; 10 | import 'package:presentation/presentation.dart'; 11 | 12 | class SingleSliverConstraintsPage extends StatelessWidget { 13 | const SingleSliverConstraintsPage(this.data, {Key? key}) : super(key: key); 14 | 15 | final SliverSectionData data; 16 | 17 | @override 18 | Widget build(BuildContext context) { 19 | return _SingleSliverPage( 20 | data, 21 | title: const Text('Constraints'), 22 | builder: (context, ValueChanged onChanged) { 23 | return SliverValueChanged( 24 | onConstraintsChanged: (constraints) { 25 | onChanged(data.mapper(constraints)); 26 | }, 27 | child: Container( 28 | height: 100, 29 | color: Colors.red, 30 | ), 31 | ); 32 | }, 33 | ); 34 | } 35 | } 36 | 37 | class SingleSliverGeometryPage extends StatelessWidget { 38 | const SingleSliverGeometryPage(this.data, {Key? key}) : super(key: key); 39 | 40 | final SliverSectionData data; 41 | 42 | @override 43 | Widget build(BuildContext context) { 44 | return _SingleSliverPage( 45 | data, 46 | title: const Text('Geometry'), 47 | builder: (context, ValueChanged onChanged) { 48 | return SliverValueChanged( 49 | onGeometryChanged: (geometry) { 50 | onChanged(data.mapper(geometry)); 51 | }, 52 | child: Container( 53 | height: 100, 54 | color: Colors.red, 55 | ), 56 | ); 57 | }, 58 | ); 59 | } 60 | } 61 | 62 | class _SingleSliverPage extends StatelessWidget { 63 | const _SingleSliverPage( 64 | this.data, { 65 | Key? key, 66 | required this.title, 67 | required this.builder, 68 | }) : super(key: key); 69 | 70 | final SliverSectionData data; 71 | final Widget title; 72 | final Widget Function(BuildContext, ValueChanged onChanged) builder; 73 | 74 | @override 75 | Widget build(BuildContext context) { 76 | return HeaderPage( 77 | repeatAnimations: false, 78 | header: AnimatedHeader( 79 | 'Sliver', 80 | onPressed: () => Navigator.pushNamed(context, '/'), 81 | ), 82 | builder: (animation, child) { 83 | return Column( 84 | children: [ 85 | SectionHeader( 86 | title: title, 87 | child: Markdown( 88 | 'This example is **interactable**.\n' 89 | 'You can scroll it to see how the value changes with ' 90 | 'different scroll position.', 91 | style: GoogleFonts.crimsonPro(), 92 | ), 93 | ), 94 | SliverSection( 95 | title: data.title, 96 | body: Markdown( 97 | data.description, 98 | style: GoogleFonts.crimsonPro(), 99 | ), 100 | leading: data.leading, 101 | trailing: data.trailing, 102 | builder: builder, 103 | ), 104 | ], 105 | ); 106 | }, 107 | ); 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /lib/transitions/align.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class AlignExample extends StatelessWidget { 4 | const AlignExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return AlignTransition( 16 | alignment: animation.drive(Tween( 17 | begin: Alignment.topLeft, 18 | end: Alignment.bottomRight, 19 | )), 20 | child: child, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /lib/transitions/decorated_box.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class DecoratedBoxExample extends StatelessWidget { 5 | const DecoratedBoxExample({ 6 | required this.animation, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Animation animation; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return DecoratedBoxTransition( 15 | decoration: animation.drive(DecorationTween( 16 | begin: BoxDecoration( 17 | color: Colors.white, 18 | border: Border.all(width: 0), 19 | borderRadius: const BorderRadius.all(Radius.circular(0)), 20 | ), 21 | end: BoxDecoration( 22 | color: Colors.yellowAccent, 23 | border: Border.all(width: 35), 24 | borderRadius: const BorderRadius.all(Radius.circular(30)), 25 | ), 26 | )), 27 | child: const SizedBox( 28 | width: 140, 29 | height: 140, 30 | child: Icon( 31 | Icons.star, 32 | size: 50, 33 | ), 34 | ), 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/transitions/default_text_style.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/material.dart'; 2 | import 'package:flutter/widgets.dart'; 3 | 4 | class DefaultTextStyleExample extends StatelessWidget { 5 | const DefaultTextStyleExample({ 6 | required this.animation, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Animation animation; 11 | 12 | @override 13 | Widget build(BuildContext context) { 14 | return DefaultTextStyleTransition( 15 | style: animation 16 | .drive(CurveTween(curve: Curves.fastOutSlowIn)) 17 | .drive(TextStyleTween( 18 | begin: Theme.of(context).textTheme.headline4, 19 | end: Theme.of(context).textTheme.headline2, 20 | )), 21 | child: const Text('Flutter'), 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lib/transitions/fade.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class FadeExample extends StatelessWidget { 4 | const FadeExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return FadeTransition( 16 | opacity: animation, 17 | child: child, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/transitions/positioned.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class PositionedExample extends StatelessWidget { 4 | const PositionedExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Stack( 16 | fit: StackFit.expand, 17 | children: [ 18 | PositionedTransition( 19 | rect: animation.drive(RelativeRectTween( 20 | begin: const RelativeRect.fromLTRB(0, 0, 50, 50), 21 | end: const RelativeRect.fromLTRB(50, 50, 100, 100), 22 | )), 23 | child: child, 24 | ), 25 | ], 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/transitions/relative_positioned.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class RelativePositionedExample extends StatelessWidget { 4 | const RelativePositionedExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return Stack( 16 | fit: StackFit.expand, 17 | children: [ 18 | RelativePositionedTransition( 19 | rect: animation.drive(_RectTween( 20 | begin: const Rect.fromLTRB(0, 0, 0, 0), 21 | end: const Rect.fromLTRB(100, 100, 100, 100), 22 | )), 23 | size: const Size(100, 100), 24 | child: Align(child: child), 25 | ), 26 | ], 27 | ); 28 | } 29 | } 30 | 31 | class _RectTween extends Tween { 32 | /// Creates a [Rect] tween. 33 | /// 34 | /// The [begin] and [end] properties may be null; the null value 35 | /// is treated as an empty rect at the top left corner. 36 | _RectTween({required Rect begin, required Rect end}) 37 | : super(begin: begin, end: end); 38 | 39 | /// Returns the value this variable has at the given animation clock value. 40 | @override 41 | Rect lerp(double t) => Rect.lerp(begin, end, t)!; 42 | } 43 | -------------------------------------------------------------------------------- /lib/transitions/rotation.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class RotationExample extends StatelessWidget { 4 | const RotationExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return RotationTransition( 16 | turns: animation, 17 | child: child, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/transitions/scale.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class ScaleExample extends StatelessWidget { 4 | const ScaleExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return ScaleTransition( 16 | scale: animation, 17 | child: child, 18 | ); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /lib/transitions/size.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class SizeExample extends StatelessWidget { 4 | const SizeExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SizeTransition( 16 | axis: Axis.horizontal, 17 | sizeFactor: animation, 18 | child: child, 19 | ); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /lib/transitions/slide.dart: -------------------------------------------------------------------------------- 1 | import 'package:flutter/widgets.dart'; 2 | 3 | class SlideExample extends StatelessWidget { 4 | const SlideExample({ 5 | required this.animation, 6 | required this.child, 7 | Key? key, 8 | }) : super(key: key); 9 | 10 | final Widget child; 11 | final Animation animation; 12 | 13 | @override 14 | Widget build(BuildContext context) { 15 | return SlideTransition( 16 | position: animation.drive(Tween( 17 | begin: const Offset(-0.5, 0), 18 | end: const Offset(0.5, 0), 19 | )), 20 | child: child, 21 | ); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /linux/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral 2 | -------------------------------------------------------------------------------- /linux/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | project(runner LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "animation_cheat_page") 5 | set(APPLICATION_ID "com.example.animation_cheat_page") 6 | 7 | cmake_policy(SET CMP0063 NEW) 8 | 9 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 10 | 11 | # Root filesystem for cross-building. 12 | if(FLUTTER_TARGET_PLATFORM_SYSROOT) 13 | set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT}) 14 | set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT}) 15 | set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) 16 | set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) 17 | set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) 18 | set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) 19 | endif() 20 | 21 | # Configure build options. 22 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 23 | set(CMAKE_BUILD_TYPE "Debug" CACHE 24 | STRING "Flutter build mode" FORCE) 25 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 26 | "Debug" "Profile" "Release") 27 | endif() 28 | 29 | # Compilation settings that should be applied to most targets. 30 | function(APPLY_STANDARD_SETTINGS TARGET) 31 | target_compile_features(${TARGET} PUBLIC cxx_std_14) 32 | target_compile_options(${TARGET} PRIVATE -Wall -Werror) 33 | target_compile_options(${TARGET} PRIVATE "$<$>:-O3>") 34 | target_compile_definitions(${TARGET} PRIVATE "$<$>:NDEBUG>") 35 | endfunction() 36 | 37 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 38 | 39 | # Flutter library and tool build rules. 40 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 41 | 42 | # System-level dependencies. 43 | find_package(PkgConfig REQUIRED) 44 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 45 | 46 | add_definitions(-DAPPLICATION_ID="${APPLICATION_ID}") 47 | 48 | # Application build 49 | add_executable(${BINARY_NAME} 50 | "main.cc" 51 | "my_application.cc" 52 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 53 | ) 54 | apply_standard_settings(${BINARY_NAME}) 55 | target_link_libraries(${BINARY_NAME} PRIVATE flutter) 56 | target_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK) 57 | add_dependencies(${BINARY_NAME} flutter_assemble) 58 | # Only the install-generated bundle's copy of the executable will launch 59 | # correctly, since the resources must in the right relative locations. To avoid 60 | # people trying to run the unbundled copy, put it in a subdirectory instead of 61 | # the default top-level location. 62 | set_target_properties(${BINARY_NAME} 63 | PROPERTIES 64 | RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/intermediates_do_not_run" 65 | ) 66 | 67 | # Generated plugin build rules, which manage building the plugins and adding 68 | # them to the application. 69 | include(flutter/generated_plugins.cmake) 70 | 71 | 72 | # === Installation === 73 | # By default, "installing" just makes a relocatable bundle in the build 74 | # directory. 75 | set(BUILD_BUNDLE_DIR "${PROJECT_BINARY_DIR}/bundle") 76 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 77 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 78 | endif() 79 | 80 | # Start with a clean build bundle directory every time. 81 | install(CODE " 82 | file(REMOVE_RECURSE \"${BUILD_BUNDLE_DIR}/\") 83 | " COMPONENT Runtime) 84 | 85 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 86 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}/lib") 87 | 88 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 89 | COMPONENT Runtime) 90 | 91 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 92 | COMPONENT Runtime) 93 | 94 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 95 | COMPONENT Runtime) 96 | 97 | if(PLUGIN_BUNDLED_LIBRARIES) 98 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 99 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 100 | COMPONENT Runtime) 101 | endif() 102 | 103 | # Fully re-copy the assets directory on each build to avoid having stale files 104 | # from a previous install. 105 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 106 | install(CODE " 107 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 108 | " COMPONENT Runtime) 109 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 110 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 111 | 112 | # Install the AOT library on non-Debug builds only. 113 | if(NOT CMAKE_BUILD_TYPE MATCHES "Debug") 114 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 115 | COMPONENT Runtime) 116 | endif() 117 | -------------------------------------------------------------------------------- /linux/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.10) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | 11 | # Serves the same purpose as list(TRANSFORM ... PREPEND ...), 12 | # which isn't available in 3.10. 13 | function(list_prepend LIST_NAME PREFIX) 14 | set(NEW_LIST "") 15 | foreach(element ${${LIST_NAME}}) 16 | list(APPEND NEW_LIST "${PREFIX}${element}") 17 | endforeach(element) 18 | set(${LIST_NAME} "${NEW_LIST}" PARENT_SCOPE) 19 | endfunction() 20 | 21 | # === Flutter Library === 22 | # System-level dependencies. 23 | find_package(PkgConfig REQUIRED) 24 | pkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0) 25 | pkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0) 26 | pkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0) 27 | pkg_check_modules(BLKID REQUIRED IMPORTED_TARGET blkid) 28 | pkg_check_modules(LZMA REQUIRED IMPORTED_TARGET liblzma) 29 | 30 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/libflutter_linux_gtk.so") 31 | 32 | # Published to parent scope for install step. 33 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 34 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 35 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 36 | set(AOT_LIBRARY "${PROJECT_DIR}/build/lib/libapp.so" PARENT_SCOPE) 37 | 38 | list(APPEND FLUTTER_LIBRARY_HEADERS 39 | "fl_basic_message_channel.h" 40 | "fl_binary_codec.h" 41 | "fl_binary_messenger.h" 42 | "fl_dart_project.h" 43 | "fl_engine.h" 44 | "fl_json_message_codec.h" 45 | "fl_json_method_codec.h" 46 | "fl_message_codec.h" 47 | "fl_method_call.h" 48 | "fl_method_channel.h" 49 | "fl_method_codec.h" 50 | "fl_method_response.h" 51 | "fl_plugin_registrar.h" 52 | "fl_plugin_registry.h" 53 | "fl_standard_message_codec.h" 54 | "fl_standard_method_codec.h" 55 | "fl_string_codec.h" 56 | "fl_value.h" 57 | "fl_view.h" 58 | "flutter_linux.h" 59 | ) 60 | list_prepend(FLUTTER_LIBRARY_HEADERS "${EPHEMERAL_DIR}/flutter_linux/") 61 | add_library(flutter INTERFACE) 62 | target_include_directories(flutter INTERFACE 63 | "${EPHEMERAL_DIR}" 64 | ) 65 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}") 66 | target_link_libraries(flutter INTERFACE 67 | PkgConfig::GTK 68 | PkgConfig::GLIB 69 | PkgConfig::GIO 70 | PkgConfig::BLKID 71 | PkgConfig::LZMA 72 | ) 73 | add_dependencies(flutter flutter_assemble) 74 | 75 | # === Flutter tool backend === 76 | # _phony_ is a non-existent file to force this command to run every time, 77 | # since currently there's no way to get a full input/output list from the 78 | # flutter tool. 79 | add_custom_command( 80 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 81 | ${CMAKE_CURRENT_BINARY_DIR}/_phony_ 82 | COMMAND ${CMAKE_COMMAND} -E env 83 | ${FLUTTER_TOOL_ENVIRONMENT} 84 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh" 85 | ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE} 86 | VERBATIM 87 | ) 88 | add_custom_target(flutter_assemble DEPENDS 89 | "${FLUTTER_LIBRARY}" 90 | ${FLUTTER_LIBRARY_HEADERS} 91 | ) 92 | -------------------------------------------------------------------------------- /linux/main.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | int main(int argc, char** argv) { 4 | g_autoptr(MyApplication) app = my_application_new(); 5 | return g_application_run(G_APPLICATION(app), argc, argv); 6 | } 7 | -------------------------------------------------------------------------------- /linux/my_application.cc: -------------------------------------------------------------------------------- 1 | #include "my_application.h" 2 | 3 | #include 4 | #ifdef GDK_WINDOWING_X11 5 | #include 6 | #endif 7 | 8 | #include "flutter/generated_plugin_registrant.h" 9 | 10 | struct _MyApplication { 11 | GtkApplication parent_instance; 12 | char** dart_entrypoint_arguments; 13 | }; 14 | 15 | G_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION) 16 | 17 | // Implements GApplication::activate. 18 | static void my_application_activate(GApplication* application) { 19 | MyApplication* self = MY_APPLICATION(application); 20 | GtkWindow* window = 21 | GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application))); 22 | 23 | // Use a header bar when running in GNOME as this is the common style used 24 | // by applications and is the setup most users will be using (e.g. Ubuntu 25 | // desktop). 26 | // If running on X and not using GNOME then just use a traditional title bar 27 | // in case the window manager does more exotic layout, e.g. tiling. 28 | // If running on Wayland assume the header bar will work (may need changing 29 | // if future cases occur). 30 | gboolean use_header_bar = TRUE; 31 | #ifdef GDK_WINDOWING_X11 32 | GdkScreen *screen = gtk_window_get_screen(window); 33 | if (GDK_IS_X11_SCREEN(screen)) { 34 | const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen); 35 | if (g_strcmp0(wm_name, "GNOME Shell") != 0) { 36 | use_header_bar = FALSE; 37 | } 38 | } 39 | #endif 40 | if (use_header_bar) { 41 | GtkHeaderBar *header_bar = GTK_HEADER_BAR(gtk_header_bar_new()); 42 | gtk_widget_show(GTK_WIDGET(header_bar)); 43 | gtk_header_bar_set_title(header_bar, "animation_cheat_page"); 44 | gtk_header_bar_set_show_close_button(header_bar, TRUE); 45 | gtk_window_set_titlebar(window, GTK_WIDGET(header_bar)); 46 | } 47 | else { 48 | gtk_window_set_title(window, "animation_cheat_page"); 49 | } 50 | 51 | gtk_window_set_default_size(window, 1280, 720); 52 | gtk_widget_show(GTK_WIDGET(window)); 53 | 54 | g_autoptr(FlDartProject) project = fl_dart_project_new(); 55 | fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments); 56 | 57 | FlView* view = fl_view_new(project); 58 | gtk_widget_show(GTK_WIDGET(view)); 59 | gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view)); 60 | 61 | fl_register_plugins(FL_PLUGIN_REGISTRY(view)); 62 | 63 | gtk_widget_grab_focus(GTK_WIDGET(view)); 64 | } 65 | 66 | // Implements GApplication::local_command_line. 67 | static gboolean my_application_local_command_line(GApplication* application, gchar ***arguments, int *exit_status) { 68 | MyApplication* self = MY_APPLICATION(application); 69 | // Strip out the first argument as it is the binary name. 70 | self->dart_entrypoint_arguments = g_strdupv(*arguments + 1); 71 | 72 | g_autoptr(GError) error = nullptr; 73 | if (!g_application_register(application, nullptr, &error)) { 74 | g_warning("Failed to register: %s", error->message); 75 | *exit_status = 1; 76 | return TRUE; 77 | } 78 | 79 | g_application_activate(application); 80 | *exit_status = 0; 81 | 82 | return TRUE; 83 | } 84 | 85 | // Implements GObject::dispose. 86 | static void my_application_dispose(GObject *object) { 87 | MyApplication* self = MY_APPLICATION(object); 88 | g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev); 89 | G_OBJECT_CLASS(my_application_parent_class)->dispose(object); 90 | } 91 | 92 | static void my_application_class_init(MyApplicationClass* klass) { 93 | G_APPLICATION_CLASS(klass)->activate = my_application_activate; 94 | G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line; 95 | G_OBJECT_CLASS(klass)->dispose = my_application_dispose; 96 | } 97 | 98 | static void my_application_init(MyApplication* self) {} 99 | 100 | MyApplication* my_application_new() { 101 | return MY_APPLICATION(g_object_new(my_application_get_type(), 102 | "application-id", APPLICATION_ID, 103 | "flags", G_APPLICATION_NON_UNIQUE, 104 | nullptr)); 105 | } 106 | -------------------------------------------------------------------------------- /linux/my_application.h: -------------------------------------------------------------------------------- 1 | #ifndef FLUTTER_MY_APPLICATION_H_ 2 | #define FLUTTER_MY_APPLICATION_H_ 3 | 4 | #include 5 | 6 | G_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION, 7 | GtkApplication) 8 | 9 | /** 10 | * my_application_new: 11 | * 12 | * Creates a new Flutter-based application. 13 | * 14 | * Returns: a new #MyApplication. 15 | */ 16 | MyApplication* my_application_new(); 17 | 18 | #endif // FLUTTER_MY_APPLICATION_H_ 19 | -------------------------------------------------------------------------------- /macos/.gitignore: -------------------------------------------------------------------------------- 1 | # Flutter-related 2 | **/Flutter/ephemeral/ 3 | **/Pods/ 4 | 5 | # Xcode-related 6 | **/xcuserdata/ 7 | -------------------------------------------------------------------------------- /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/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 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 85 | 91 | 92 | 93 | 94 | 96 | 97 | 100 | 101 | 102 | -------------------------------------------------------------------------------- /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/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png -------------------------------------------------------------------------------- /macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/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_cheat_page 9 | 10 | // The application's bundle identifier 11 | PRODUCT_BUNDLE_IDENTIFIER = com.example.animationCheatPage 12 | 13 | // The copyright displayed in application information 14 | PRODUCT_COPYRIGHT = Copyright © 2020 com.example. 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.yaml: -------------------------------------------------------------------------------- 1 | name: animation_cheat_page 2 | description: Presentations made with Flutter 3 | 4 | environment: 5 | sdk: '>=2.12.0 <3.0.0' 6 | 7 | dependencies: 8 | flutter: 9 | sdk: flutter 10 | flutter_localizations: 11 | sdk: flutter 12 | 13 | google_fonts: ^2.0.0 14 | http: ^0.13.0 15 | presentation: ^1.0.0 16 | universal_html: ^2.0.8 17 | args: ^2.0.0 18 | process_run: ^0.12.0 19 | rxdart: ^0.24.1 20 | url_launcher: ^6.0.3 21 | 22 | dev_dependencies: 23 | flutter_driver: 24 | sdk: flutter 25 | flutter_test: 26 | sdk: flutter 27 | 28 | 29 | test: ^1.6.3 30 | fast_flutter_driver: ^3.0.0 31 | 32 | flutter: 33 | 34 | uses-material-design: true 35 | assets: 36 | - assets/favicon.png 37 | - assets/logo-192.png 38 | - assets/manifest.json 39 | -------------------------------------------------------------------------------- /test/app_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/animations_cheat_sheet.dart'; 2 | import 'package:animation_cheat_page/config.dart'; 3 | import 'package:animation_cheat_page/pages/root_page.dart'; 4 | import 'package:flutter_test/flutter_test.dart'; 5 | 6 | void main() { 7 | testWidgets('$RootPage is the initial page', (tester) async { 8 | await tester.pumpWidget( 9 | const AnimationCheatSheet( 10 | config: Configuration(), 11 | ), 12 | ); 13 | 14 | await tester.pump(const Duration(seconds: 4)); 15 | 16 | expect(find.byType(RootPage), findsOneWidget); 17 | }); 18 | } 19 | -------------------------------------------------------------------------------- /test/main_page_test.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/animations_cheat_sheet.dart'; 2 | import 'package:animation_cheat_page/config.dart'; 3 | import 'package:animation_cheat_page/shared/ui/description.dart' as ui; 4 | import 'package:animation_cheat_page/shared/ui/header.dart'; 5 | import 'package:animation_cheat_page/shared/ui/section.dart'; 6 | import 'package:flutter/widgets.dart'; 7 | import 'package:flutter_test/flutter_test.dart'; 8 | 9 | void main() { 10 | testWidgets('Header is rendered', (tester) async { 11 | await tester.pumpWidget( 12 | const AnimationCheatSheet( 13 | config: Configuration(), 14 | ), 15 | ); 16 | 17 | await tester.pump(const Duration(seconds: 4)); 18 | expect(find.byType(Header), findsOneWidget); 19 | }); 20 | 21 | testWidgets('display description', (tester) async { 22 | await tester.pumpWidget( 23 | const TickerMode( 24 | enabled: false, 25 | child: AnimationCheatSheet( 26 | config: Configuration(), 27 | ), 28 | ), 29 | ); 30 | 31 | await tester.pump(const Duration(seconds: 4)); 32 | await tester.drag(find.byType(ListView), const Offset(0, -600)); 33 | await tester.pumpAndSettle(); 34 | 35 | expect(find.byType(ui.Description), findsOneWidget); 36 | }, skip: true); // TODO(tomek): why deferred loading breaks it? 37 | 38 | testWidgets('display content', (tester) async { 39 | await tester.pumpWidget( 40 | const TickerMode( 41 | enabled: false, 42 | child: AnimationCheatSheet( 43 | config: Configuration(), 44 | ), 45 | ), 46 | ); 47 | 48 | await tester.pump(const Duration(seconds: 4)); 49 | await tester.drag(find.byType(ListView), const Offset(0, -1600)); 50 | await tester.pumpAndSettle(); 51 | 52 | expect(find.byType(Section), findsWidgets); 53 | }, skip: true); // TODO(tomek): why deferred loading breaks it? 54 | } 55 | -------------------------------------------------------------------------------- /test_driver/curves_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:animation_cheat_page/config.dart'; 4 | import 'package:fast_flutter_driver/tool.dart'; 5 | import 'package:flutter_driver/flutter_driver.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'generic/test_configuration.dart'; 9 | 10 | void main(List args) { 11 | group('Curves', () { 12 | late FlutterDriver driver; 13 | final properties = TestProperties(args); 14 | 15 | setUpAll(() async { 16 | driver = await FlutterDriver.connect(dartVmServiceUrl: properties.vmUrl); 17 | }); 18 | 19 | tearDownAll(() async { 20 | await driver.close(); 21 | }); 22 | 23 | setUp(() async { 24 | await driver.requestData( 25 | json.encode( 26 | TestConfiguration( 27 | resolution: properties.resolution, 28 | configuration: const Configuration( 29 | route: Routes.curves, 30 | repeatAnimations: false, 31 | ), 32 | ), 33 | ), 34 | ); 35 | }); 36 | 37 | test('shows curves', () async { 38 | await driver.waitFor(find.byType('CurvesPage')); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /test_driver/generic/generic.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:animation_cheat_page/animations_cheat_sheet.dart'; 4 | import 'package:fast_flutter_driver/driver.dart'; 5 | import 'package:flutter/foundation.dart'; 6 | import 'package:flutter/scheduler.dart'; 7 | import 'package:flutter/widgets.dart'; 8 | import 'package:flutter_driver/driver_extension.dart'; 9 | 10 | import 'test_configuration.dart'; 11 | 12 | void main() { 13 | debugDefaultTargetPlatformOverride = TargetPlatform.fuchsia; 14 | timeDilation = 0.1; 15 | enableFlutterDriverExtension( 16 | handler: (playload) async { 17 | await configureTest( 18 | TestConfiguration.fromJson(json.decode(playload ?? '{}')), 19 | ); 20 | return ''; 21 | }, 22 | ); 23 | 24 | runApp( 25 | RestartWidget( 26 | builder: (_, config) => AnimationCheatSheet(config: config.configuration), 27 | ), 28 | ); 29 | } 30 | -------------------------------------------------------------------------------- /test_driver/generic/test_configuration.dart: -------------------------------------------------------------------------------- 1 | import 'package:animation_cheat_page/config.dart'; 2 | import 'package:fast_flutter_driver/tool.dart'; 3 | 4 | class TestConfiguration implements BaseConfiguration { 5 | const TestConfiguration({ 6 | required this.resolution, 7 | this.platform, 8 | required this.configuration, 9 | }); 10 | 11 | factory TestConfiguration.fromJson(Map json) { 12 | return TestConfiguration( 13 | resolution: Resolution.fromJson(json['resolution']), 14 | platform: TestPlatformEx.fromString(json['platform']), 15 | configuration: Configuration.fromJson(json['configuration']), 16 | ); 17 | } 18 | 19 | @override 20 | final TestPlatform? platform; 21 | @override 22 | final Resolution resolution; 23 | final Configuration configuration; 24 | 25 | @override 26 | Map toJson() { 27 | final p = platform; 28 | return { 29 | 'resolution': resolution, 30 | if (p != null) 'platform': p.asString(), 31 | 'configuration': configuration, 32 | }; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /test_driver/root_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:animation_cheat_page/config.dart'; 4 | import 'package:fast_flutter_driver/tool.dart'; 5 | import 'package:flutter_driver/flutter_driver.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'generic/test_configuration.dart'; 9 | 10 | void main(List args) { 11 | group('Root', () { 12 | late FlutterDriver driver; 13 | final properties = TestProperties(args); 14 | 15 | setUpAll(() async { 16 | driver = await FlutterDriver.connect(dartVmServiceUrl: properties.vmUrl); 17 | }); 18 | 19 | tearDownAll(() async { 20 | await driver.close(); 21 | }); 22 | 23 | setUp(() async { 24 | await driver.requestData( 25 | json.encode( 26 | TestConfiguration( 27 | resolution: properties.resolution, 28 | configuration: const Configuration( 29 | route: Routes.root, 30 | repeatAnimations: false, 31 | ), 32 | ), 33 | ), 34 | ); 35 | }); 36 | 37 | test('shows root', () async { 38 | await driver.waitFor(find.byType('RootPage')); 39 | }); 40 | 41 | test('scrolls to bottom', () async { 42 | await driver.scroll( 43 | find.byType('ListView'), 44 | 0, 45 | -40000, 46 | const Duration(milliseconds: 100), 47 | ); 48 | await driver.scrollUntilVisible( 49 | find.byType('ListView'), 50 | find.byType('AnimatedPhysicalModelExample'), 51 | ); 52 | }); 53 | }); 54 | } 55 | -------------------------------------------------------------------------------- /test_driver/sliver_fill_remaining_test.dart: -------------------------------------------------------------------------------- 1 | import 'dart:convert'; 2 | 3 | import 'package:animation_cheat_page/config.dart'; 4 | import 'package:fast_flutter_driver/tool.dart'; 5 | import 'package:flutter_driver/flutter_driver.dart'; 6 | import 'package:test/test.dart'; 7 | 8 | import 'generic/test_configuration.dart'; 9 | 10 | void main(List args) { 11 | group('Sliver Fill Remaining', () { 12 | late FlutterDriver driver; 13 | final properties = TestProperties(args); 14 | 15 | setUpAll(() async { 16 | driver = await FlutterDriver.connect(dartVmServiceUrl: properties.vmUrl); 17 | }); 18 | 19 | tearDownAll(() async { 20 | await driver.close(); 21 | }); 22 | 23 | setUp(() async { 24 | await driver.requestData( 25 | json.encode( 26 | TestConfiguration( 27 | resolution: properties.resolution, 28 | configuration: const Configuration( 29 | route: Routes.slivers_fill_remaining_example, 30 | repeatAnimations: false, 31 | ), 32 | ), 33 | ), 34 | ); 35 | }); 36 | 37 | test('shows sliver', () async { 38 | await driver.waitFor(find.byType('FillRemainingPage')); 39 | }); 40 | }); 41 | } 42 | -------------------------------------------------------------------------------- /web/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/web/favicon.png -------------------------------------------------------------------------------- /web/icons/Icon-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/web/icons/Icon-192.png -------------------------------------------------------------------------------- /web/icons/Icon-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/web/icons/Icon-512.png -------------------------------------------------------------------------------- /web/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | Flutter Animations 18 | 19 | 20 | 21 | 28 | 29 | 30 | 33 | 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /web/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "animation_cheat_page", 3 | "short_name": "animation_cheat_page", 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 | } 24 | -------------------------------------------------------------------------------- /windows/.gitignore: -------------------------------------------------------------------------------- 1 | flutter/ephemeral/ 2 | 3 | # Visual Studio user-specific files. 4 | *.suo 5 | *.user 6 | *.userosscache 7 | *.sln.docstates 8 | 9 | # Visual Studio build-related files. 10 | x64/ 11 | x86/ 12 | 13 | # Visual Studio cache files 14 | # files ending in .cache can be ignored 15 | *.[Cc]ache 16 | # but keep track of directories ending in .cache 17 | !*.[Cc]ache/ 18 | -------------------------------------------------------------------------------- /windows/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(animation_cheat_page LANGUAGES CXX) 3 | 4 | set(BINARY_NAME "animation_cheat_page") 5 | 6 | cmake_policy(SET CMP0063 NEW) 7 | 8 | set(CMAKE_INSTALL_RPATH "$ORIGIN/lib") 9 | 10 | # Configure build options. 11 | get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) 12 | if(IS_MULTICONFIG) 13 | set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" 14 | CACHE STRING "" FORCE) 15 | else() 16 | if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) 17 | set(CMAKE_BUILD_TYPE "Debug" CACHE 18 | STRING "Flutter build mode" FORCE) 19 | set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS 20 | "Debug" "Profile" "Release") 21 | endif() 22 | endif() 23 | 24 | set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") 25 | set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") 26 | set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") 27 | set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") 28 | 29 | # Use Unicode for all projects. 30 | add_definitions(-DUNICODE -D_UNICODE) 31 | 32 | # Compilation settings that should be applied to most targets. 33 | function(APPLY_STANDARD_SETTINGS TARGET) 34 | target_compile_features(${TARGET} PUBLIC cxx_std_17) 35 | target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") 36 | target_compile_options(${TARGET} PRIVATE /EHsc) 37 | target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") 38 | target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") 39 | endfunction() 40 | 41 | set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") 42 | 43 | # Flutter library and tool build rules. 44 | add_subdirectory(${FLUTTER_MANAGED_DIR}) 45 | 46 | # Application build 47 | add_subdirectory("runner") 48 | 49 | # Generated plugin build rules, which manage building the plugins and adding 50 | # them to the application. 51 | include(flutter/generated_plugins.cmake) 52 | 53 | 54 | # === Installation === 55 | # Support files are copied into place next to the executable, so that it can 56 | # run in place. This is done instead of making a separate bundle (as on Linux) 57 | # so that building and running from within Visual Studio will work. 58 | set(BUILD_BUNDLE_DIR "$") 59 | # Make the "install" step default, as it's required to run. 60 | set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) 61 | if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) 62 | set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) 63 | endif() 64 | 65 | set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") 66 | set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") 67 | 68 | install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" 69 | COMPONENT Runtime) 70 | 71 | install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 72 | COMPONENT Runtime) 73 | 74 | install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 75 | COMPONENT Runtime) 76 | 77 | if(PLUGIN_BUNDLED_LIBRARIES) 78 | install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" 79 | DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" 80 | COMPONENT Runtime) 81 | endif() 82 | 83 | # Fully re-copy the assets directory on each build to avoid having stale files 84 | # from a previous install. 85 | set(FLUTTER_ASSET_DIR_NAME "flutter_assets") 86 | install(CODE " 87 | file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") 88 | " COMPONENT Runtime) 89 | install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" 90 | DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) 91 | 92 | # Install the AOT library on non-Debug builds only. 93 | install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" 94 | CONFIGURATIONS Profile;Release 95 | COMPONENT Runtime) 96 | -------------------------------------------------------------------------------- /windows/flutter/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | 3 | set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") 4 | 5 | # Configuration provided via flutter tool. 6 | include(${EPHEMERAL_DIR}/generated_config.cmake) 7 | 8 | # TODO: Move the rest of this into files in ephemeral. See 9 | # https://github.com/flutter/flutter/issues/57146. 10 | set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") 11 | 12 | # === Flutter Library === 13 | set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") 14 | 15 | # Published to parent scope for install step. 16 | set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) 17 | set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) 18 | set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) 19 | set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) 20 | 21 | list(APPEND FLUTTER_LIBRARY_HEADERS 22 | "flutter_export.h" 23 | "flutter_windows.h" 24 | "flutter_messenger.h" 25 | "flutter_plugin_registrar.h" 26 | ) 27 | list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") 28 | add_library(flutter INTERFACE) 29 | target_include_directories(flutter INTERFACE 30 | "${EPHEMERAL_DIR}" 31 | ) 32 | target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") 33 | add_dependencies(flutter flutter_assemble) 34 | 35 | # === Wrapper === 36 | list(APPEND CPP_WRAPPER_SOURCES_CORE 37 | "core_implementations.cc" 38 | "standard_codec.cc" 39 | ) 40 | list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") 41 | list(APPEND CPP_WRAPPER_SOURCES_PLUGIN 42 | "plugin_registrar.cc" 43 | ) 44 | list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") 45 | list(APPEND CPP_WRAPPER_SOURCES_APP 46 | "flutter_engine.cc" 47 | "flutter_view_controller.cc" 48 | ) 49 | list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") 50 | 51 | # Wrapper sources needed for a plugin. 52 | add_library(flutter_wrapper_plugin STATIC 53 | ${CPP_WRAPPER_SOURCES_CORE} 54 | ${CPP_WRAPPER_SOURCES_PLUGIN} 55 | ) 56 | apply_standard_settings(flutter_wrapper_plugin) 57 | set_target_properties(flutter_wrapper_plugin PROPERTIES 58 | POSITION_INDEPENDENT_CODE ON) 59 | set_target_properties(flutter_wrapper_plugin PROPERTIES 60 | CXX_VISIBILITY_PRESET hidden) 61 | target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) 62 | target_include_directories(flutter_wrapper_plugin PUBLIC 63 | "${WRAPPER_ROOT}/include" 64 | ) 65 | add_dependencies(flutter_wrapper_plugin flutter_assemble) 66 | 67 | # Wrapper sources needed for the runner. 68 | add_library(flutter_wrapper_app STATIC 69 | ${CPP_WRAPPER_SOURCES_CORE} 70 | ${CPP_WRAPPER_SOURCES_APP} 71 | ) 72 | apply_standard_settings(flutter_wrapper_app) 73 | target_link_libraries(flutter_wrapper_app PUBLIC flutter) 74 | target_include_directories(flutter_wrapper_app PUBLIC 75 | "${WRAPPER_ROOT}/include" 76 | ) 77 | add_dependencies(flutter_wrapper_app flutter_assemble) 78 | 79 | # === Flutter tool backend === 80 | # _phony_ is a non-existent file to force this command to run every time, 81 | # since currently there's no way to get a full input/output list from the 82 | # flutter tool. 83 | set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") 84 | set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) 85 | add_custom_command( 86 | OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} 87 | ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} 88 | ${CPP_WRAPPER_SOURCES_APP} 89 | ${PHONY_OUTPUT} 90 | COMMAND ${CMAKE_COMMAND} -E env 91 | ${FLUTTER_TOOL_ENVIRONMENT} 92 | "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" 93 | windows-x64 $ 94 | ) 95 | add_custom_target(flutter_assemble DEPENDS 96 | "${FLUTTER_LIBRARY}" 97 | ${FLUTTER_LIBRARY_HEADERS} 98 | ${CPP_WRAPPER_SOURCES_CORE} 99 | ${CPP_WRAPPER_SOURCES_PLUGIN} 100 | ${CPP_WRAPPER_SOURCES_APP} 101 | ) 102 | -------------------------------------------------------------------------------- /windows/runner/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.15) 2 | project(runner LANGUAGES CXX) 3 | 4 | add_executable(${BINARY_NAME} WIN32 5 | "flutter_window.cpp" 6 | "main.cpp" 7 | "run_loop.cpp" 8 | "utils.cpp" 9 | "win32_window.cpp" 10 | "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" 11 | "Runner.rc" 12 | "runner.exe.manifest" 13 | ) 14 | apply_standard_settings(${BINARY_NAME}) 15 | target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") 16 | target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) 17 | target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") 18 | add_dependencies(${BINARY_NAME} flutter_assemble) 19 | -------------------------------------------------------------------------------- /windows/runner/Runner.rc: -------------------------------------------------------------------------------- 1 | // Microsoft Visual C++ generated resource script. 2 | // 3 | #pragma code_page(65001) 4 | #include "resource.h" 5 | 6 | #define APSTUDIO_READONLY_SYMBOLS 7 | ///////////////////////////////////////////////////////////////////////////// 8 | // 9 | // Generated from the TEXTINCLUDE 2 resource. 10 | // 11 | #include "winres.h" 12 | 13 | ///////////////////////////////////////////////////////////////////////////// 14 | #undef APSTUDIO_READONLY_SYMBOLS 15 | 16 | ///////////////////////////////////////////////////////////////////////////// 17 | // English (United States) resources 18 | 19 | #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) 20 | LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US 21 | 22 | #ifdef APSTUDIO_INVOKED 23 | ///////////////////////////////////////////////////////////////////////////// 24 | // 25 | // TEXTINCLUDE 26 | // 27 | 28 | 1 TEXTINCLUDE 29 | BEGIN 30 | "resource.h\0" 31 | END 32 | 33 | 2 TEXTINCLUDE 34 | BEGIN 35 | "#include ""winres.h""\r\n" 36 | "\0" 37 | END 38 | 39 | 3 TEXTINCLUDE 40 | BEGIN 41 | "\r\n" 42 | "\0" 43 | END 44 | 45 | #endif // APSTUDIO_INVOKED 46 | 47 | 48 | ///////////////////////////////////////////////////////////////////////////// 49 | // 50 | // Icon 51 | // 52 | 53 | // Icon with lowest ID value placed first to ensure application icon 54 | // remains consistent on all systems. 55 | IDI_APP_ICON ICON "resources\\app_icon.ico" 56 | 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // 60 | // Version 61 | // 62 | 63 | #ifdef FLUTTER_BUILD_NUMBER 64 | #define VERSION_AS_NUMBER FLUTTER_BUILD_NUMBER 65 | #else 66 | #define VERSION_AS_NUMBER 1,0,0 67 | #endif 68 | 69 | #ifdef FLUTTER_BUILD_NAME 70 | #define VERSION_AS_STRING #FLUTTER_BUILD_NAME 71 | #else 72 | #define VERSION_AS_STRING "1.0.0" 73 | #endif 74 | 75 | VS_VERSION_INFO VERSIONINFO 76 | FILEVERSION VERSION_AS_NUMBER 77 | PRODUCTVERSION VERSION_AS_NUMBER 78 | FILEFLAGSMASK VS_FFI_FILEFLAGSMASK 79 | #ifdef _DEBUG 80 | FILEFLAGS VS_FF_DEBUG 81 | #else 82 | FILEFLAGS 0x0L 83 | #endif 84 | FILEOS VOS__WINDOWS32 85 | FILETYPE VFT_APP 86 | FILESUBTYPE 0x0L 87 | BEGIN 88 | BLOCK "StringFileInfo" 89 | BEGIN 90 | BLOCK "040904e4" 91 | BEGIN 92 | VALUE "CompanyName", "com.example" "\0" 93 | VALUE "FileDescription", "A new Flutter project." "\0" 94 | VALUE "FileVersion", VERSION_AS_STRING "\0" 95 | VALUE "InternalName", "animation_cheat_page" "\0" 96 | VALUE "LegalCopyright", "Copyright (C) 2020 com.example. All rights reserved." "\0" 97 | VALUE "OriginalFilename", "animation_cheat_page.exe" "\0" 98 | VALUE "ProductName", "animation_cheat_page" "\0" 99 | VALUE "ProductVersion", VERSION_AS_STRING "\0" 100 | END 101 | END 102 | BLOCK "VarFileInfo" 103 | BEGIN 104 | VALUE "Translation", 0x409, 1252 105 | END 106 | END 107 | 108 | #endif // English (United States) resources 109 | ///////////////////////////////////////////////////////////////////////////// 110 | 111 | 112 | 113 | #ifndef APSTUDIO_INVOKED 114 | ///////////////////////////////////////////////////////////////////////////// 115 | // 116 | // Generated from the TEXTINCLUDE 3 resource. 117 | // 118 | 119 | 120 | ///////////////////////////////////////////////////////////////////////////// 121 | #endif // not APSTUDIO_INVOKED 122 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.cpp: -------------------------------------------------------------------------------- 1 | #include "flutter_window.h" 2 | 3 | #include 4 | 5 | #include "flutter/generated_plugin_registrant.h" 6 | 7 | FlutterWindow::FlutterWindow(RunLoop* run_loop, 8 | const flutter::DartProject& project) 9 | : run_loop_(run_loop), project_(project) {} 10 | 11 | FlutterWindow::~FlutterWindow() {} 12 | 13 | bool FlutterWindow::OnCreate() { 14 | if (!Win32Window::OnCreate()) { 15 | return false; 16 | } 17 | 18 | RECT frame = GetClientArea(); 19 | 20 | // The size here must match the window dimensions to avoid unnecessary surface 21 | // creation / destruction in the startup path. 22 | flutter_controller_ = std::make_unique( 23 | frame.right - frame.left, frame.bottom - frame.top, project_); 24 | // Ensure that basic setup of the controller was successful. 25 | if (!flutter_controller_->engine() || !flutter_controller_->view()) { 26 | return false; 27 | } 28 | RegisterPlugins(flutter_controller_->engine()); 29 | run_loop_->RegisterFlutterInstance(flutter_controller_->engine()); 30 | SetChildContent(flutter_controller_->view()->GetNativeWindow()); 31 | return true; 32 | } 33 | 34 | void FlutterWindow::OnDestroy() { 35 | if (flutter_controller_) { 36 | run_loop_->UnregisterFlutterInstance(flutter_controller_->engine()); 37 | flutter_controller_ = nullptr; 38 | } 39 | 40 | Win32Window::OnDestroy(); 41 | } 42 | 43 | LRESULT 44 | FlutterWindow::MessageHandler(HWND hwnd, UINT const message, 45 | WPARAM const wparam, 46 | LPARAM const lparam) noexcept { 47 | // Give Flutter, including plugins, an opporutunity to handle window messages. 48 | if (flutter_controller_) { 49 | std::optional result = 50 | flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, 51 | lparam); 52 | if (result) { 53 | return *result; 54 | } 55 | } 56 | 57 | switch (message) { 58 | case WM_FONTCHANGE: 59 | flutter_controller_->engine()->ReloadSystemFonts(); 60 | break; 61 | } 62 | 63 | return Win32Window::MessageHandler(hwnd, message, wparam, lparam); 64 | } 65 | -------------------------------------------------------------------------------- /windows/runner/flutter_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_FLUTTER_WINDOW_H_ 2 | #define RUNNER_FLUTTER_WINDOW_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include "run_loop.h" 10 | #include "win32_window.h" 11 | 12 | // A window that does nothing but host a Flutter view. 13 | class FlutterWindow : public Win32Window { 14 | public: 15 | // Creates a new FlutterWindow driven by the |run_loop|, hosting a 16 | // Flutter view running |project|. 17 | explicit FlutterWindow(RunLoop* run_loop, 18 | const flutter::DartProject& project); 19 | virtual ~FlutterWindow(); 20 | 21 | protected: 22 | // Win32Window: 23 | bool OnCreate() override; 24 | void OnDestroy() override; 25 | LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, 26 | LPARAM const lparam) noexcept override; 27 | 28 | private: 29 | // The run loop driving events for this window. 30 | RunLoop* run_loop_; 31 | 32 | // The project to run. 33 | flutter::DartProject project_; 34 | 35 | // The Flutter instance hosted by this window. 36 | std::unique_ptr flutter_controller_; 37 | }; 38 | 39 | #endif // RUNNER_FLUTTER_WINDOW_H_ 40 | -------------------------------------------------------------------------------- /windows/runner/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "flutter_window.h" 6 | #include "run_loop.h" 7 | #include "utils.h" 8 | 9 | int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, 10 | _In_ wchar_t *command_line, _In_ int show_command) { 11 | // Attach to console when present (e.g., 'flutter run') or create a 12 | // new console when running with a debugger. 13 | if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { 14 | CreateAndAttachConsole(); 15 | } 16 | 17 | // Initialize COM, so that it is available for use in the library and/or 18 | // plugins. 19 | ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); 20 | 21 | RunLoop run_loop; 22 | 23 | flutter::DartProject project(L"data"); 24 | FlutterWindow window(&run_loop, project); 25 | Win32Window::Point origin(10, 10); 26 | Win32Window::Size size(1280, 720); 27 | if (!window.CreateAndShow(L"animation_cheat_page", origin, size)) { 28 | return EXIT_FAILURE; 29 | } 30 | window.SetQuitOnClose(true); 31 | 32 | run_loop.Run(); 33 | 34 | ::CoUninitialize(); 35 | return EXIT_SUCCESS; 36 | } 37 | -------------------------------------------------------------------------------- /windows/runner/resource.h: -------------------------------------------------------------------------------- 1 | //{{NO_DEPENDENCIES}} 2 | // Microsoft Visual C++ generated include file. 3 | // Used by Runner.rc 4 | // 5 | #define IDI_APP_ICON 101 6 | 7 | // Next default values for new objects 8 | // 9 | #ifdef APSTUDIO_INVOKED 10 | #ifndef APSTUDIO_READONLY_SYMBOLS 11 | #define _APS_NEXT_RESOURCE_VALUE 102 12 | #define _APS_NEXT_COMMAND_VALUE 40001 13 | #define _APS_NEXT_CONTROL_VALUE 1001 14 | #define _APS_NEXT_SYMED_VALUE 101 15 | #endif 16 | #endif 17 | -------------------------------------------------------------------------------- /windows/runner/resources/app_icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tomaszpolanski/flutter-animations/d32b10e96edc785b88a21549a6b24294cd91ed60/windows/runner/resources/app_icon.ico -------------------------------------------------------------------------------- /windows/runner/run_loop.cpp: -------------------------------------------------------------------------------- 1 | #include "run_loop.h" 2 | 3 | #include 4 | 5 | #include 6 | 7 | RunLoop::RunLoop() {} 8 | 9 | RunLoop::~RunLoop() {} 10 | 11 | void RunLoop::Run() { 12 | bool keep_running = true; 13 | TimePoint next_flutter_event_time = TimePoint::clock::now(); 14 | while (keep_running) { 15 | std::chrono::nanoseconds wait_duration = 16 | std::max(std::chrono::nanoseconds(0), 17 | next_flutter_event_time - TimePoint::clock::now()); 18 | ::MsgWaitForMultipleObjects( 19 | 0, nullptr, FALSE, static_cast(wait_duration.count() / 1000), 20 | QS_ALLINPUT); 21 | bool processed_events = false; 22 | MSG message; 23 | // All pending Windows messages must be processed; MsgWaitForMultipleObjects 24 | // won't return again for items left in the queue after PeekMessage. 25 | while (::PeekMessage(&message, nullptr, 0, 0, PM_REMOVE)) { 26 | processed_events = true; 27 | if (message.message == WM_QUIT) { 28 | keep_running = false; 29 | break; 30 | } 31 | ::TranslateMessage(&message); 32 | ::DispatchMessage(&message); 33 | // Allow Flutter to process messages each time a Windows message is 34 | // processed, to prevent starvation. 35 | next_flutter_event_time = 36 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 37 | } 38 | // If the PeekMessage loop didn't run, process Flutter messages. 39 | if (!processed_events) { 40 | next_flutter_event_time = 41 | std::min(next_flutter_event_time, ProcessFlutterMessages()); 42 | } 43 | } 44 | } 45 | 46 | void RunLoop::RegisterFlutterInstance( 47 | flutter::FlutterEngine* flutter_instance) { 48 | flutter_instances_.insert(flutter_instance); 49 | } 50 | 51 | void RunLoop::UnregisterFlutterInstance( 52 | flutter::FlutterEngine* flutter_instance) { 53 | flutter_instances_.erase(flutter_instance); 54 | } 55 | 56 | RunLoop::TimePoint RunLoop::ProcessFlutterMessages() { 57 | TimePoint next_event_time = TimePoint::max(); 58 | for (auto instance : flutter_instances_) { 59 | std::chrono::nanoseconds wait_duration = instance->ProcessMessages(); 60 | if (wait_duration != std::chrono::nanoseconds::max()) { 61 | next_event_time = 62 | std::min(next_event_time, TimePoint::clock::now() + wait_duration); 63 | } 64 | } 65 | return next_event_time; 66 | } 67 | -------------------------------------------------------------------------------- /windows/runner/run_loop.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_RUN_LOOP_H_ 2 | #define RUNNER_RUN_LOOP_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | 9 | // A runloop that will service events for Flutter instances as well 10 | // as native messages. 11 | class RunLoop { 12 | public: 13 | RunLoop(); 14 | ~RunLoop(); 15 | 16 | // Prevent copying 17 | RunLoop(RunLoop const&) = delete; 18 | RunLoop& operator=(RunLoop const&) = delete; 19 | 20 | // Runs the run loop until the application quits. 21 | void Run(); 22 | 23 | // Registers the given Flutter instance for event servicing. 24 | void RegisterFlutterInstance( 25 | flutter::FlutterEngine* flutter_instance); 26 | 27 | // Unregisters the given Flutter instance from event servicing. 28 | void UnregisterFlutterInstance( 29 | flutter::FlutterEngine* flutter_instance); 30 | 31 | private: 32 | using TimePoint = std::chrono::steady_clock::time_point; 33 | 34 | // Processes all currently pending messages for registered Flutter instances. 35 | TimePoint ProcessFlutterMessages(); 36 | 37 | std::set flutter_instances_; 38 | }; 39 | 40 | #endif // RUNNER_RUN_LOOP_H_ 41 | -------------------------------------------------------------------------------- /windows/runner/runner.exe.manifest: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | PerMonitorV2 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /windows/runner/utils.cpp: -------------------------------------------------------------------------------- 1 | #include "utils.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | void CreateAndAttachConsole() { 11 | if (::AllocConsole()) { 12 | FILE *unused; 13 | if (freopen_s(&unused, "CONOUT$", "w", stdout)) { 14 | _dup2(_fileno(stdout), 1); 15 | } 16 | if (freopen_s(&unused, "CONOUT$", "w", stderr)) { 17 | _dup2(_fileno(stdout), 2); 18 | } 19 | std::ios::sync_with_stdio(); 20 | FlutterDesktopResyncOutputStreams(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /windows/runner/utils.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_UTILS_H_ 2 | #define RUNNER_UTILS_H_ 3 | 4 | // Creates a console for the process, and redirects stdout and stderr to 5 | // it for both the runner and the Flutter library. 6 | void CreateAndAttachConsole(); 7 | 8 | #endif // RUNNER_UTILS_H_ 9 | -------------------------------------------------------------------------------- /windows/runner/win32_window.h: -------------------------------------------------------------------------------- 1 | #ifndef RUNNER_WIN32_WINDOW_H_ 2 | #define RUNNER_WIN32_WINDOW_H_ 3 | 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | // A class abstraction for a high DPI-aware Win32 Window. Intended to be 11 | // inherited from by classes that wish to specialize with custom 12 | // rendering and input handling 13 | class Win32Window { 14 | public: 15 | struct Point { 16 | unsigned int x; 17 | unsigned int y; 18 | Point(unsigned int x, unsigned int y) : x(x), y(y) {} 19 | }; 20 | 21 | struct Size { 22 | unsigned int width; 23 | unsigned int height; 24 | Size(unsigned int width, unsigned int height) 25 | : width(width), height(height) {} 26 | }; 27 | 28 | Win32Window(); 29 | virtual ~Win32Window(); 30 | 31 | // Creates and shows a win32 window with |title| and position and size using 32 | // |origin| and |size|. New windows are created on the default monitor. Window 33 | // sizes are specified to the OS in physical pixels, hence to ensure a 34 | // consistent size to will treat the width height passed in to this function 35 | // as logical pixels and scale to appropriate for the default monitor. Returns 36 | // true if the window was created successfully. 37 | bool CreateAndShow(const std::wstring& title, 38 | const Point& origin, 39 | const Size& size); 40 | 41 | // Release OS resources associated with window. 42 | void Destroy(); 43 | 44 | // Inserts |content| into the window tree. 45 | void SetChildContent(HWND content); 46 | 47 | // Returns the backing Window handle to enable clients to set icon and other 48 | // window properties. Returns nullptr if the window has been destroyed. 49 | HWND GetHandle(); 50 | 51 | // If true, closing this window will quit the application. 52 | void SetQuitOnClose(bool quit_on_close); 53 | 54 | // Return a RECT representing the bounds of the current client area. 55 | RECT GetClientArea(); 56 | 57 | protected: 58 | // Processes and route salient window messages for mouse handling, 59 | // size change and DPI. Delegates handling of these to member overloads that 60 | // inheriting classes can handle. 61 | virtual LRESULT MessageHandler(HWND window, 62 | UINT const message, 63 | WPARAM const wparam, 64 | LPARAM const lparam) noexcept; 65 | 66 | // Called when CreateAndShow is called, allowing subclass window-related 67 | // setup. Subclasses should return false if setup fails. 68 | virtual bool OnCreate(); 69 | 70 | // Called when Destroy is called. 71 | virtual void OnDestroy(); 72 | 73 | private: 74 | friend class WindowClassRegistrar; 75 | 76 | // OS callback called by message pump. Handles the WM_NCCREATE message which 77 | // is passed when the non-client area is being created and enables automatic 78 | // non-client DPI scaling so that the non-client area automatically 79 | // responsponds to changes in DPI. All other messages are handled by 80 | // MessageHandler. 81 | static LRESULT CALLBACK WndProc(HWND const window, 82 | UINT const message, 83 | WPARAM const wparam, 84 | LPARAM const lparam) noexcept; 85 | 86 | // Retrieves a class instance pointer for |window| 87 | static Win32Window* GetThisFromHandle(HWND const window) noexcept; 88 | 89 | bool quit_on_close_ = false; 90 | 91 | // window handle for top level window. 92 | HWND window_handle_ = nullptr; 93 | 94 | // window handle for hosted content. 95 | HWND child_content_ = nullptr; 96 | }; 97 | 98 | #endif // RUNNER_WIN32_WINDOW_H_ 99 | --------------------------------------------------------------------------------