├── .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 | [](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 | '0x194',
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