mirror of
https://github.com/flutter/packages.git
synced 2025-06-29 22:33:11 +08:00
Introduce and migrate to DualTransitionBuilder (#160)
This commit is contained in:

committed by
GitHub

parent
5ca63a2879
commit
3d34e64da4
49
packages/animations/.gitignore
vendored
49
packages/animations/.gitignore
vendored
@ -22,52 +22,23 @@
|
|||||||
|
|
||||||
# Flutter/Dart/Pub related
|
# Flutter/Dart/Pub related
|
||||||
**/doc/api/
|
**/doc/api/
|
||||||
|
**/ios/Flutter/.last_build_id
|
||||||
.dart_tool/
|
.dart_tool/
|
||||||
.flutter-plugins
|
.flutter-plugins
|
||||||
|
.flutter-plugins-dependencies
|
||||||
.packages
|
.packages
|
||||||
.pub-cache/
|
.pub-cache/
|
||||||
.pub/
|
.pub/
|
||||||
build/
|
/build/
|
||||||
|
|
||||||
# Android related
|
# Web related
|
||||||
**/android/**/gradle-wrapper.jar
|
lib/generated_plugin_registrant.dart
|
||||||
**/android/.gradle
|
|
||||||
**/android/captures/
|
|
||||||
**/android/gradlew
|
|
||||||
**/android/gradlew.bat
|
|
||||||
**/android/local.properties
|
|
||||||
**/android/**/GeneratedPluginRegistrant.java
|
|
||||||
|
|
||||||
# iOS/XCode related
|
# Symbolication related
|
||||||
**/ios/**/*.mode1v3
|
app.*.symbols
|
||||||
**/ios/**/*.mode2v3
|
|
||||||
**/ios/**/*.moved-aside
|
# Obfuscation related
|
||||||
**/ios/**/*.pbxuser
|
app.*.map.json
|
||||||
**/ios/**/*.perspectivev3
|
|
||||||
**/ios/**/*sync/
|
|
||||||
**/ios/**/.sconsign.dblite
|
|
||||||
**/ios/**/.tags*
|
|
||||||
**/ios/**/.vagrant/
|
|
||||||
**/ios/**/DerivedData/
|
|
||||||
**/ios/**/Icon?
|
|
||||||
**/ios/**/Pods/
|
|
||||||
**/ios/**/.symlinks/
|
|
||||||
**/ios/**/profile
|
|
||||||
**/ios/**/xcuserdata
|
|
||||||
**/ios/.generated/
|
|
||||||
**/ios/Flutter/App.framework
|
|
||||||
**/ios/Flutter/Flutter.framework
|
|
||||||
**/ios/Flutter/Generated.xcconfig
|
|
||||||
**/ios/Flutter/app.flx
|
|
||||||
**/ios/Flutter/app.zip
|
|
||||||
**/ios/Flutter/flutter_assets/
|
|
||||||
**/ios/Flutter/flutter_export_environment.sh
|
|
||||||
**/ios/ServiceDefinitions.json
|
|
||||||
**/ios/Runner/GeneratedPluginRegistrant.*
|
|
||||||
|
|
||||||
# Exceptions to above rules.
|
# Exceptions to above rules.
|
||||||
!**/ios/**/default.mode1v3
|
|
||||||
!**/ios/**/default.mode2v3
|
|
||||||
!**/ios/**/default.pbxuser
|
|
||||||
!**/ios/**/default.perspectivev3
|
|
||||||
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages
|
||||||
|
@ -9,11 +9,7 @@
|
|||||||
/* Begin PBXBuildFile section */
|
/* Begin PBXBuildFile section */
|
||||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; };
|
|
||||||
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; };
|
|
||||||
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9740EEBA1CF902C7004384FC /* Flutter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
|
||||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||||
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
|
||||||
@ -26,8 +22,6 @@
|
|||||||
dstPath = "";
|
dstPath = "";
|
||||||
dstSubfolderSpec = 10;
|
dstSubfolderSpec = 10;
|
||||||
files = (
|
files = (
|
||||||
3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */,
|
|
||||||
9705A1C71CF904A300538489 /* Flutter.framework in Embed Frameworks */,
|
|
||||||
);
|
);
|
||||||
name = "Embed Frameworks";
|
name = "Embed Frameworks";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@ -38,13 +32,11 @@
|
|||||||
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
|
||||||
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||||
3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = "<group>"; };
|
|
||||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
|
||||||
9740EEBA1CF902C7004384FC /* Flutter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Flutter.framework; path = Flutter/Flutter.framework; sourceTree = "<group>"; };
|
|
||||||
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||||
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||||
@ -57,8 +49,6 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */,
|
|
||||||
3B80C3941E831B6300D905FE /* App.framework in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@ -68,9 +58,7 @@
|
|||||||
9740EEB11CF90186004384FC /* Flutter */ = {
|
9740EEB11CF90186004384FC /* Flutter */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
3B80C3931E831B6300D905FE /* App.framework */,
|
|
||||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
|
||||||
9740EEBA1CF902C7004384FC /* Flutter.framework */,
|
|
||||||
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
9740EEB21CF90195004384FC /* Debug.xcconfig */,
|
||||||
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
|
||||||
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
9740EEB31CF90195004384FC /* Generated.xcconfig */,
|
||||||
@ -201,7 +189,7 @@
|
|||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
shellPath = /bin/sh;
|
shellPath = /bin/sh;
|
||||||
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin";
|
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
|
||||||
};
|
};
|
||||||
9740EEB61CF901F6004384FC /* Run Script */ = {
|
9740EEB61CF901F6004384FC /* Run Script */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
@ -253,7 +241,6 @@
|
|||||||
/* Begin XCBuildConfiguration section */
|
/* Begin XCBuildConfiguration section */
|
||||||
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
249021D3217E4FDB00AE95B9 /* Profile */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
@ -309,6 +296,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -330,7 +318,6 @@
|
|||||||
};
|
};
|
||||||
97C147031CF9000F007C117D /* Debug */ = {
|
97C147031CF9000F007C117D /* Debug */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
@ -386,7 +373,6 @@
|
|||||||
};
|
};
|
||||||
97C147041CF9000F007C117D /* Release */ = {
|
97C147041CF9000F007C117D /* Release */ = {
|
||||||
isa = XCBuildConfiguration;
|
isa = XCBuildConfiguration;
|
||||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
CLANG_ANALYZER_NONNULL = YES;
|
||||||
@ -443,6 +429,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
@ -470,6 +457,7 @@
|
|||||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||||
|
DEVELOPMENT_TEAM = "";
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
FRAMEWORK_SEARCH_PATHS = (
|
FRAMEWORK_SEARCH_PATHS = (
|
||||||
"$(inherited)",
|
"$(inherited)",
|
||||||
|
@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>IDEDidComputeMac32BitWarning</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
200
packages/animations/lib/src/dual_transition_builder.dart
Normal file
200
packages/animations/lib/src/dual_transition_builder.dart
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
// Copyright 2020 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
/// Builder callback used by [DualTransitionBuilder].
|
||||||
|
///
|
||||||
|
/// The builder is expected to return a transition powered by the provided
|
||||||
|
/// `animation` and wrapping the provided `child`.
|
||||||
|
///
|
||||||
|
/// The `animation` provided to the builder always runs forward from 0.0 to 1.0.
|
||||||
|
typedef TransitionBuilder = Widget Function(
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
);
|
||||||
|
|
||||||
|
/// A transition builder that animates its [child] based on the
|
||||||
|
/// [AnimationStatus] of the provided [animation].
|
||||||
|
///
|
||||||
|
/// This widget can be used to specify different enter and exit transitions for
|
||||||
|
/// a [child].
|
||||||
|
///
|
||||||
|
/// While the [animation] runs forward, the [child] is animated according to
|
||||||
|
/// [forwardBuilder] and while the [animation] is running in reverse, it is
|
||||||
|
/// animated according to [reverseBuilder].
|
||||||
|
///
|
||||||
|
/// Using this builder allows the widget tree to maintain its shape by nesting
|
||||||
|
/// the enter and exit transitions. This ensures that no state information of
|
||||||
|
/// any descendant widget is lost when the transition starts or completes.
|
||||||
|
class DualTransitionBuilder extends StatefulWidget {
|
||||||
|
/// Creates a [DualTransitionBuilder].
|
||||||
|
///
|
||||||
|
/// The [animation], [forwardBuilder], and [reverseBuilder] arguments are
|
||||||
|
/// required and must not be null.
|
||||||
|
const DualTransitionBuilder({
|
||||||
|
Key key,
|
||||||
|
@required this.animation,
|
||||||
|
@required this.forwardBuilder,
|
||||||
|
@required this.reverseBuilder,
|
||||||
|
this.child,
|
||||||
|
}) : assert(animation != null),
|
||||||
|
assert(forwardBuilder != null),
|
||||||
|
assert(reverseBuilder != null),
|
||||||
|
super(key: key);
|
||||||
|
|
||||||
|
/// The animation that drives the [child]'s transition.
|
||||||
|
///
|
||||||
|
/// When this animation runs forward, the [child] transitions as specified by
|
||||||
|
/// [forwardBuilder]. When it runs in reverse, the child transitions according
|
||||||
|
/// to [reverseBuilder].
|
||||||
|
final Animation<double> animation;
|
||||||
|
|
||||||
|
/// A builder for the transition that makes [child] appear on screen.
|
||||||
|
///
|
||||||
|
/// The [child] should be fully visible when the provided `animation` reaches
|
||||||
|
/// 1.0.
|
||||||
|
///
|
||||||
|
/// The `animation` provided to this builder is running forward from 0.0 to
|
||||||
|
/// 1.0 when [animation] runs _forward_. When [animation] runs in reverse,
|
||||||
|
/// the given `animation` is set to [kAlwaysCompleteAnimation].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [reverseBuilder], which builds the transition for making the [child]
|
||||||
|
/// disappear from the screen.
|
||||||
|
final TransitionBuilder forwardBuilder;
|
||||||
|
|
||||||
|
/// A builder for a transition that makes [child] disappear from the screen.
|
||||||
|
///
|
||||||
|
/// The [child] should be fully invisible when the provided `animation`
|
||||||
|
/// reaches 1.0.
|
||||||
|
///
|
||||||
|
/// The `animation` provided to this builder is running forward from 0.0 to
|
||||||
|
/// 1.0 when [animation] runs in _reverse_. When [animation] runs forward,
|
||||||
|
/// the given `animation` is set to [kAlwaysDismissedAnimation].
|
||||||
|
///
|
||||||
|
/// See also:
|
||||||
|
///
|
||||||
|
/// * [forwardBuilder], which builds the transition for making the [child]
|
||||||
|
/// appear on screen.
|
||||||
|
final TransitionBuilder reverseBuilder;
|
||||||
|
|
||||||
|
/// The widget below this [DualTransitionBuilder] in the tree.
|
||||||
|
///
|
||||||
|
/// This child widget will be wrapped by the transitions built by
|
||||||
|
/// [forwardBuilder] and [reverseBuilder].
|
||||||
|
final Widget child;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<DualTransitionBuilder> createState() => _DualTransitionBuilderState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _DualTransitionBuilderState extends State<DualTransitionBuilder> {
|
||||||
|
AnimationStatus _effectiveAnimationStatus;
|
||||||
|
final ProxyAnimation _forwardAnimation = ProxyAnimation();
|
||||||
|
final ProxyAnimation _reverseAnimation = ProxyAnimation();
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
super.initState();
|
||||||
|
_effectiveAnimationStatus = widget.animation.status;
|
||||||
|
widget.animation.addStatusListener(_animationListener);
|
||||||
|
_updateAnimations();
|
||||||
|
}
|
||||||
|
|
||||||
|
void _animationListener(AnimationStatus animationStatus) {
|
||||||
|
final AnimationStatus oldEffective = _effectiveAnimationStatus;
|
||||||
|
_effectiveAnimationStatus = _calculateEffectiveAnimationStatus(
|
||||||
|
lastEffective: _effectiveAnimationStatus,
|
||||||
|
current: animationStatus,
|
||||||
|
);
|
||||||
|
if (oldEffective != _effectiveAnimationStatus) {
|
||||||
|
_updateAnimations();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void didUpdateWidget(DualTransitionBuilder oldWidget) {
|
||||||
|
super.didUpdateWidget(oldWidget);
|
||||||
|
if (oldWidget.animation != widget.animation) {
|
||||||
|
oldWidget.animation.removeStatusListener(_animationListener);
|
||||||
|
widget.animation.addStatusListener(_animationListener);
|
||||||
|
_animationListener(widget.animation.status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// When a transition is interrupted midway we just want to play the ongoing
|
||||||
|
// animation in reverse. Switching to the actual reverse transition would
|
||||||
|
// yield a disjoint experience since the forward and reverse transitions are
|
||||||
|
// very different.
|
||||||
|
AnimationStatus _calculateEffectiveAnimationStatus({
|
||||||
|
@required AnimationStatus lastEffective,
|
||||||
|
@required AnimationStatus current,
|
||||||
|
}) {
|
||||||
|
assert(current != null);
|
||||||
|
assert(lastEffective != null);
|
||||||
|
switch (current) {
|
||||||
|
case AnimationStatus.dismissed:
|
||||||
|
case AnimationStatus.completed:
|
||||||
|
return current;
|
||||||
|
case AnimationStatus.forward:
|
||||||
|
switch (lastEffective) {
|
||||||
|
case AnimationStatus.dismissed:
|
||||||
|
case AnimationStatus.completed:
|
||||||
|
case AnimationStatus.forward:
|
||||||
|
return current;
|
||||||
|
case AnimationStatus.reverse:
|
||||||
|
return lastEffective;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case AnimationStatus.reverse:
|
||||||
|
switch (lastEffective) {
|
||||||
|
case AnimationStatus.dismissed:
|
||||||
|
case AnimationStatus.completed:
|
||||||
|
case AnimationStatus.reverse:
|
||||||
|
return current;
|
||||||
|
case AnimationStatus.forward:
|
||||||
|
return lastEffective;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return null; // unreachable
|
||||||
|
}
|
||||||
|
|
||||||
|
void _updateAnimations() {
|
||||||
|
switch (_effectiveAnimationStatus) {
|
||||||
|
case AnimationStatus.dismissed:
|
||||||
|
case AnimationStatus.forward:
|
||||||
|
_forwardAnimation.parent = widget.animation;
|
||||||
|
_reverseAnimation.parent = kAlwaysDismissedAnimation;
|
||||||
|
break;
|
||||||
|
case AnimationStatus.reverse:
|
||||||
|
case AnimationStatus.completed:
|
||||||
|
_forwardAnimation.parent = kAlwaysCompleteAnimation;
|
||||||
|
_reverseAnimation.parent = ReverseAnimation(widget.animation);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
widget.animation.removeStatusListener(_animationListener);
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return widget.forwardBuilder(
|
||||||
|
context,
|
||||||
|
_forwardAnimation,
|
||||||
|
widget.reverseBuilder(
|
||||||
|
context,
|
||||||
|
_reverseAnimation,
|
||||||
|
widget.child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'dual_transition_builder.dart';
|
||||||
import 'modal.dart';
|
import 'modal.dart';
|
||||||
import 'utils/curves.dart';
|
import 'utils/curves.dart';
|
||||||
|
|
||||||
@ -102,7 +103,7 @@ class FadeScaleTransitionConfiguration extends ModalConfiguration {
|
|||||||
///
|
///
|
||||||
/// This widget is not to be confused with Flutter's [FadeTransition] widget,
|
/// This widget is not to be confused with Flutter's [FadeTransition] widget,
|
||||||
/// which animates only the opacity of its child widget.
|
/// which animates only the opacity of its child widget.
|
||||||
class FadeScaleTransition extends StatefulWidget {
|
class FadeScaleTransition extends StatelessWidget {
|
||||||
/// Creates a widget that implements the Material fade transition.
|
/// Creates a widget that implements the Material fade transition.
|
||||||
///
|
///
|
||||||
/// The fade pattern is used for UI elements that enter or exit from within
|
/// The fade pattern is used for UI elements that enter or exit from within
|
||||||
@ -136,143 +137,46 @@ class FadeScaleTransition extends StatefulWidget {
|
|||||||
/// [secondaryAnimation].
|
/// [secondaryAnimation].
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
static final Animatable<double> _fadeInTransition = CurveTween(
|
||||||
_FadeScaleTransitionState createState() => _FadeScaleTransitionState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _FadeScaleTransitionState extends State<FadeScaleTransition> {
|
|
||||||
AnimationStatus _effectiveAnimationStatus;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_effectiveAnimationStatus = widget.animation.status;
|
|
||||||
widget.animation.addStatusListener(_animationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _animationListener(AnimationStatus animationStatus) {
|
|
||||||
_effectiveAnimationStatus = _calculateEffectiveAnimationStatus(
|
|
||||||
lastEffective: _effectiveAnimationStatus,
|
|
||||||
current: animationStatus,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When a transition is interrupted midway we just want to play the ongoing
|
|
||||||
// animation in reverse. Switching to the actual reverse transition would
|
|
||||||
// yield a disjoint experience since the forward and reverse transitions are
|
|
||||||
// very different.
|
|
||||||
AnimationStatus _calculateEffectiveAnimationStatus({
|
|
||||||
@required AnimationStatus lastEffective,
|
|
||||||
@required AnimationStatus current,
|
|
||||||
}) {
|
|
||||||
assert(current != null);
|
|
||||||
assert(lastEffective != null);
|
|
||||||
switch (current) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
switch (lastEffective) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
return lastEffective;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
switch (lastEffective) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return lastEffective;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
void _updateAnimationListener(
|
|
||||||
Animation<double> oldAnimation,
|
|
||||||
Animation<double> animation,
|
|
||||||
) {
|
|
||||||
if (oldAnimation != animation) {
|
|
||||||
oldAnimation.removeStatusListener(_animationListener);
|
|
||||||
animation.addStatusListener(_animationListener);
|
|
||||||
_animationListener(animation.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(FadeScaleTransition oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
_updateAnimationListener(
|
|
||||||
oldWidget.animation,
|
|
||||||
widget.animation,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
widget.animation.removeStatusListener(_animationListener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return AnimatedBuilder(
|
|
||||||
animation: widget.animation,
|
|
||||||
builder: (BuildContext context, Widget child) {
|
|
||||||
assert(_effectiveAnimationStatus != null);
|
|
||||||
switch (_effectiveAnimationStatus) {
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return _EnterTransition(
|
|
||||||
animation: widget.animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
return FadeTransition(
|
|
||||||
opacity: widget.animation,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
},
|
|
||||||
child: widget.child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class _EnterTransition extends StatelessWidget {
|
|
||||||
const _EnterTransition({
|
|
||||||
this.animation,
|
|
||||||
this.child,
|
|
||||||
});
|
|
||||||
|
|
||||||
final Animation<double> animation;
|
|
||||||
final Widget child;
|
|
||||||
|
|
||||||
static Animatable<double> fadeInTransition = CurveTween(
|
|
||||||
curve: const Interval(0.0, 0.3),
|
curve: const Interval(0.0, 0.3),
|
||||||
);
|
);
|
||||||
static Animatable<double> scaleInTransition = Tween<double>(
|
static final Animatable<double> _scaleInTransition = Tween<double>(
|
||||||
begin: 0.80,
|
begin: 0.80,
|
||||||
end: 1.00,
|
end: 1.00,
|
||||||
).chain(CurveTween(curve: decelerateEasing));
|
).chain(CurveTween(curve: decelerateEasing));
|
||||||
|
static final Animatable<double> _fadeOutTransition = Tween<double>(
|
||||||
|
begin: 1.0,
|
||||||
|
end: 0.0,
|
||||||
|
);
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return FadeTransition(
|
return DualTransitionBuilder(
|
||||||
opacity: fadeInTransition.animate(animation),
|
animation: animation,
|
||||||
child: ScaleTransition(
|
forwardBuilder: (
|
||||||
scale: scaleInTransition.animate(animation),
|
BuildContext context,
|
||||||
child: child,
|
Animation<double> animation,
|
||||||
),
|
Widget child,
|
||||||
|
) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: _fadeInTransition.animate(animation),
|
||||||
|
child: ScaleTransition(
|
||||||
|
scale: _scaleInTransition.animate(animation),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reverseBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: _fadeOutTransition.animate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
|
||||||
|
import 'dual_transition_builder.dart';
|
||||||
|
|
||||||
/// Used by [PageTransitionsTheme] to define a page route transition animation
|
/// Used by [PageTransitionsTheme] to define a page route transition animation
|
||||||
/// in which the outgoing page fades out, then the incoming page fades in and
|
/// in which the outgoing page fades out, then the incoming page fades in and
|
||||||
/// scale up.
|
/// scale up.
|
||||||
@ -60,7 +62,12 @@ import 'package:flutter/material.dart';
|
|||||||
/// ```
|
/// ```
|
||||||
class FadeThroughPageTransitionsBuilder extends PageTransitionsBuilder {
|
class FadeThroughPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||||
/// Creates a [FadeThroughPageTransitionsBuilder].
|
/// Creates a [FadeThroughPageTransitionsBuilder].
|
||||||
const FadeThroughPageTransitionsBuilder();
|
const FadeThroughPageTransitionsBuilder({this.fillColor});
|
||||||
|
|
||||||
|
/// The color to use for the background color during the transition.
|
||||||
|
///
|
||||||
|
/// This defaults to the [Theme]'s [ThemeData.canvasColor].
|
||||||
|
final Color fillColor;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget buildTransitions<T>(
|
Widget buildTransitions<T>(
|
||||||
@ -73,6 +80,7 @@ class FadeThroughPageTransitionsBuilder extends PageTransitionsBuilder {
|
|||||||
return FadeThroughTransition(
|
return FadeThroughTransition(
|
||||||
animation: animation,
|
animation: animation,
|
||||||
secondaryAnimation: secondaryAnimation,
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
fillColor: fillColor,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -150,7 +158,7 @@ class FadeThroughPageTransitionsBuilder extends PageTransitionsBuilder {
|
|||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
class FadeThroughTransition extends StatefulWidget {
|
class FadeThroughTransition extends StatelessWidget {
|
||||||
/// Creates a [FadeThroughTransition].
|
/// Creates a [FadeThroughTransition].
|
||||||
///
|
///
|
||||||
/// The [animation] and [secondaryAnimation] argument are required and must
|
/// The [animation] and [secondaryAnimation] argument are required and must
|
||||||
@ -158,6 +166,7 @@ class FadeThroughTransition extends StatefulWidget {
|
|||||||
const FadeThroughTransition({
|
const FadeThroughTransition({
|
||||||
@required this.animation,
|
@required this.animation,
|
||||||
@required this.secondaryAnimation,
|
@required this.secondaryAnimation,
|
||||||
|
this.fillColor,
|
||||||
this.child,
|
this.child,
|
||||||
}) : assert(animation != null),
|
}) : assert(animation != null),
|
||||||
assert(secondaryAnimation != null);
|
assert(secondaryAnimation != null);
|
||||||
@ -179,6 +188,11 @@ class FadeThroughTransition extends StatefulWidget {
|
|||||||
// property when the [FadeThroughTransition] is used as a page transition.
|
// property when the [FadeThroughTransition] is used as a page transition.
|
||||||
final Animation<double> secondaryAnimation;
|
final Animation<double> secondaryAnimation;
|
||||||
|
|
||||||
|
/// The color to use for the background color during the transition.
|
||||||
|
///
|
||||||
|
/// This defaults to the [Theme]'s [ThemeData.canvasColor].
|
||||||
|
final Color fillColor;
|
||||||
|
|
||||||
/// The widget below this widget in the tree.
|
/// The widget below this widget in the tree.
|
||||||
///
|
///
|
||||||
/// This widget will transition in and out as driven by [animation] and
|
/// This widget will transition in and out as driven by [animation] and
|
||||||
@ -186,152 +200,52 @@ class FadeThroughTransition extends StatefulWidget {
|
|||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<FadeThroughTransition> createState() => _FadeThroughTransitionState();
|
Widget build(BuildContext context) {
|
||||||
|
return _ZoomedFadeInFadeOut(
|
||||||
|
animation: animation,
|
||||||
|
child: Container(
|
||||||
|
color: fillColor ?? Theme.of(context).canvasColor,
|
||||||
|
child: _ZoomedFadeInFadeOut(
|
||||||
|
animation: ReverseAnimation(secondaryAnimation),
|
||||||
|
child: child,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _FadeThroughTransitionState extends State<FadeThroughTransition> {
|
class _ZoomedFadeInFadeOut extends StatelessWidget {
|
||||||
AnimationStatus _effectiveAnimationStatus;
|
const _ZoomedFadeInFadeOut({Key key, this.animation, this.child})
|
||||||
AnimationStatus _effectiveSecondaryAnimationStatus;
|
: super(key: key);
|
||||||
|
|
||||||
@override
|
final Animation<double> animation;
|
||||||
void initState() {
|
final Widget child;
|
||||||
super.initState();
|
|
||||||
_effectiveAnimationStatus = widget.animation.status;
|
|
||||||
_effectiveSecondaryAnimationStatus = widget.secondaryAnimation.status;
|
|
||||||
widget.animation.addStatusListener(_animationListener);
|
|
||||||
widget.secondaryAnimation.addStatusListener(_secondaryAnimationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _animationListener(AnimationStatus animationStatus) {
|
|
||||||
_effectiveAnimationStatus = _calculateEffectiveAnimationStatus(
|
|
||||||
lastEffective: _effectiveAnimationStatus,
|
|
||||||
current: animationStatus,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _secondaryAnimationListener(AnimationStatus animationStatus) {
|
|
||||||
_effectiveSecondaryAnimationStatus = _calculateEffectiveAnimationStatus(
|
|
||||||
lastEffective: _effectiveSecondaryAnimationStatus,
|
|
||||||
current: animationStatus,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When a transition is interrupted midway we just want to play the ongoing
|
|
||||||
// animation in reverse. Switching to the actual reverse transition would
|
|
||||||
// yield a disjoint experience since the forward and reverse transitions are
|
|
||||||
// very different.
|
|
||||||
AnimationStatus _calculateEffectiveAnimationStatus({
|
|
||||||
@required AnimationStatus lastEffective,
|
|
||||||
@required AnimationStatus current,
|
|
||||||
}) {
|
|
||||||
assert(current != null);
|
|
||||||
assert(lastEffective != null);
|
|
||||||
switch (current) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
switch (lastEffective) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
return lastEffective;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
switch (lastEffective) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return lastEffective;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(FadeThroughTransition oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (oldWidget.animation != widget.animation) {
|
|
||||||
oldWidget.animation.removeStatusListener(_animationListener);
|
|
||||||
widget.animation.addStatusListener(_animationListener);
|
|
||||||
_animationListener(widget.animation.status);
|
|
||||||
}
|
|
||||||
if (oldWidget.secondaryAnimation != widget.secondaryAnimation) {
|
|
||||||
oldWidget.secondaryAnimation
|
|
||||||
.removeStatusListener(_secondaryAnimationListener);
|
|
||||||
widget.secondaryAnimation.addStatusListener(_secondaryAnimationListener);
|
|
||||||
_secondaryAnimationListener(widget.secondaryAnimation.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
widget.animation.removeStatusListener(_animationListener);
|
|
||||||
widget.secondaryAnimation.removeStatusListener(_secondaryAnimationListener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Tween<double> _flippedTween = Tween<double>(
|
|
||||||
begin: 1.0,
|
|
||||||
end: 0.0,
|
|
||||||
);
|
|
||||||
static Animation<double> _flip(Animation<double> animation) {
|
|
||||||
return _flippedTween.animate(animation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AnimatedBuilder(
|
return DualTransitionBuilder(
|
||||||
animation: widget.animation,
|
animation: animation,
|
||||||
builder: (BuildContext context, Widget child) {
|
forwardBuilder: (
|
||||||
assert(_effectiveAnimationStatus != null);
|
BuildContext context,
|
||||||
switch (_effectiveAnimationStatus) {
|
Animation<double> animation,
|
||||||
case AnimationStatus.forward:
|
Widget child,
|
||||||
return _ZoomedFadeIn(
|
) {
|
||||||
animation: widget.animation,
|
return _ZoomedFadeIn(
|
||||||
child: child,
|
animation: animation,
|
||||||
);
|
child: child,
|
||||||
case AnimationStatus.dismissed:
|
);
|
||||||
case AnimationStatus.reverse:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
return _FadeOut(
|
|
||||||
animation: _flip(widget.animation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
},
|
},
|
||||||
child: Container(
|
reverseBuilder: (
|
||||||
color: Theme.of(context).canvasColor,
|
BuildContext context,
|
||||||
child: AnimatedBuilder(
|
Animation<double> animation,
|
||||||
animation: widget.secondaryAnimation,
|
Widget child,
|
||||||
builder: (BuildContext context, Widget child) {
|
) {
|
||||||
assert(_effectiveSecondaryAnimationStatus != null);
|
return _FadeOut(
|
||||||
switch (_effectiveSecondaryAnimationStatus) {
|
child: child,
|
||||||
case AnimationStatus.forward:
|
animation: animation,
|
||||||
return _FadeOut(
|
);
|
||||||
child: child,
|
},
|
||||||
animation: widget.secondaryAnimation,
|
child: child,
|
||||||
);
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
return _ZoomedFadeIn(
|
|
||||||
animation: _flip(widget.secondaryAnimation),
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
},
|
|
||||||
child: widget.child,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import 'package:flutter/foundation.dart';
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/widgets.dart';
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
import 'dual_transition_builder.dart';
|
||||||
import 'utils/curves.dart';
|
import 'utils/curves.dart';
|
||||||
|
|
||||||
/// Determines which type of shared axis transition is used.
|
/// Determines which type of shared axis transition is used.
|
||||||
@ -79,9 +80,9 @@ enum SharedAxisTransitionType {
|
|||||||
class SharedAxisPageTransitionsBuilder extends PageTransitionsBuilder {
|
class SharedAxisPageTransitionsBuilder extends PageTransitionsBuilder {
|
||||||
/// Construct a [SharedAxisPageTransitionsBuilder].
|
/// Construct a [SharedAxisPageTransitionsBuilder].
|
||||||
const SharedAxisPageTransitionsBuilder({
|
const SharedAxisPageTransitionsBuilder({
|
||||||
this.transitionType,
|
@required this.transitionType,
|
||||||
this.fillColor,
|
this.fillColor,
|
||||||
});
|
}) : assert(transitionType != null);
|
||||||
|
|
||||||
/// Determines which [SharedAxisTransitionType] to build.
|
/// Determines which [SharedAxisTransitionType] to build.
|
||||||
final SharedAxisTransitionType transitionType;
|
final SharedAxisTransitionType transitionType;
|
||||||
@ -183,7 +184,7 @@ class SharedAxisPageTransitionsBuilder extends PageTransitionsBuilder {
|
|||||||
/// );
|
/// );
|
||||||
/// }
|
/// }
|
||||||
/// ```
|
/// ```
|
||||||
class SharedAxisTransition extends StatefulWidget {
|
class SharedAxisTransition extends StatelessWidget {
|
||||||
/// Creates a [SharedAxisTransition].
|
/// Creates a [SharedAxisTransition].
|
||||||
///
|
///
|
||||||
/// The [animation] and [secondaryAnimation] argument are required and must
|
/// The [animation] and [secondaryAnimation] argument are required and must
|
||||||
@ -234,157 +235,62 @@ class SharedAxisTransition extends StatefulWidget {
|
|||||||
/// [secondaryAnimation].
|
/// [secondaryAnimation].
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
@override
|
|
||||||
_SharedAxisTransitionState createState() => _SharedAxisTransitionState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _SharedAxisTransitionState extends State<SharedAxisTransition> {
|
|
||||||
AnimationStatus _effectiveAnimationStatus;
|
|
||||||
AnimationStatus _effectiveSecondaryAnimationStatus;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
_effectiveAnimationStatus = widget.animation.status;
|
|
||||||
_effectiveSecondaryAnimationStatus = widget.secondaryAnimation.status;
|
|
||||||
widget.animation.addStatusListener(_animationListener);
|
|
||||||
widget.secondaryAnimation.addStatusListener(_secondaryAnimationListener);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _animationListener(AnimationStatus animationStatus) {
|
|
||||||
_effectiveAnimationStatus = _calculateEffectiveAnimationStatus(
|
|
||||||
lastEffective: _effectiveAnimationStatus,
|
|
||||||
current: animationStatus,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
void _secondaryAnimationListener(AnimationStatus animationStatus) {
|
|
||||||
_effectiveSecondaryAnimationStatus = _calculateEffectiveAnimationStatus(
|
|
||||||
lastEffective: _effectiveSecondaryAnimationStatus,
|
|
||||||
current: animationStatus,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// When a transition is interrupted midway we just want to play the ongoing
|
|
||||||
// animation in reverse. Switching to the actual reverse transition would
|
|
||||||
// yield a disjoint experience since the forward and reverse transitions are
|
|
||||||
// very different.
|
|
||||||
AnimationStatus _calculateEffectiveAnimationStatus({
|
|
||||||
@required AnimationStatus lastEffective,
|
|
||||||
@required AnimationStatus current,
|
|
||||||
}) {
|
|
||||||
assert(current != null);
|
|
||||||
assert(lastEffective != null);
|
|
||||||
switch (current) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
switch (lastEffective) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
return lastEffective;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
switch (lastEffective) {
|
|
||||||
case AnimationStatus.dismissed:
|
|
||||||
case AnimationStatus.completed:
|
|
||||||
case AnimationStatus.reverse:
|
|
||||||
return current;
|
|
||||||
case AnimationStatus.forward:
|
|
||||||
return lastEffective;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void didUpdateWidget(SharedAxisTransition oldWidget) {
|
|
||||||
super.didUpdateWidget(oldWidget);
|
|
||||||
if (oldWidget.animation != widget.animation) {
|
|
||||||
oldWidget.animation.removeStatusListener(_animationListener);
|
|
||||||
widget.animation.addStatusListener(_animationListener);
|
|
||||||
_animationListener(widget.animation.status);
|
|
||||||
}
|
|
||||||
if (oldWidget.secondaryAnimation != widget.secondaryAnimation) {
|
|
||||||
oldWidget.secondaryAnimation
|
|
||||||
.removeStatusListener(_secondaryAnimationListener);
|
|
||||||
widget.secondaryAnimation.addStatusListener(_secondaryAnimationListener);
|
|
||||||
_secondaryAnimationListener(widget.secondaryAnimation.status);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void dispose() {
|
|
||||||
widget.animation.removeStatusListener(_animationListener);
|
|
||||||
widget.secondaryAnimation.removeStatusListener(_secondaryAnimationListener);
|
|
||||||
super.dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
static final Tween<double> _flippedTween = Tween<double>(
|
|
||||||
begin: 1.0,
|
|
||||||
end: 0.0,
|
|
||||||
);
|
|
||||||
static Animation<double> _flip(Animation<double> animation) {
|
|
||||||
return _flippedTween.animate(animation);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return AnimatedBuilder(
|
final Color color = fillColor ?? Theme.of(context).canvasColor;
|
||||||
animation: widget.animation,
|
return DualTransitionBuilder(
|
||||||
builder: (BuildContext context, Widget child) {
|
animation: animation,
|
||||||
assert(_effectiveAnimationStatus != null);
|
forwardBuilder: (
|
||||||
switch (_effectiveAnimationStatus) {
|
BuildContext context,
|
||||||
case AnimationStatus.forward:
|
Animation<double> animation,
|
||||||
return _EnterTransition(
|
Widget child,
|
||||||
animation: widget.animation,
|
) {
|
||||||
transitionType: widget.transitionType,
|
return _EnterTransition(
|
||||||
child: child,
|
animation: animation,
|
||||||
);
|
transitionType: transitionType,
|
||||||
case AnimationStatus.dismissed:
|
child: child,
|
||||||
case AnimationStatus.reverse:
|
);
|
||||||
case AnimationStatus.completed:
|
|
||||||
return _ExitTransition(
|
|
||||||
animation: _flip(widget.animation),
|
|
||||||
transitionType: widget.transitionType,
|
|
||||||
reverse: true,
|
|
||||||
fillColor: widget.fillColor,
|
|
||||||
child: child,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return null; // unreachable
|
|
||||||
},
|
},
|
||||||
child: AnimatedBuilder(
|
reverseBuilder: (
|
||||||
animation: widget.secondaryAnimation,
|
BuildContext context,
|
||||||
builder: (BuildContext context, Widget child) {
|
Animation<double> animation,
|
||||||
assert(_effectiveSecondaryAnimationStatus != null);
|
Widget child,
|
||||||
switch (_effectiveSecondaryAnimationStatus) {
|
) {
|
||||||
case AnimationStatus.forward:
|
return _ExitTransition(
|
||||||
return _ExitTransition(
|
animation: animation,
|
||||||
animation: widget.secondaryAnimation,
|
transitionType: transitionType,
|
||||||
transitionType: widget.transitionType,
|
reverse: true,
|
||||||
fillColor: widget.fillColor,
|
fillColor: color,
|
||||||
child: child,
|
child: child,
|
||||||
);
|
);
|
||||||
case AnimationStatus.dismissed:
|
},
|
||||||
case AnimationStatus.reverse:
|
child: DualTransitionBuilder(
|
||||||
case AnimationStatus.completed:
|
animation: ReverseAnimation(secondaryAnimation),
|
||||||
return _EnterTransition(
|
forwardBuilder: (
|
||||||
animation: _flip(widget.secondaryAnimation),
|
BuildContext context,
|
||||||
transitionType: widget.transitionType,
|
Animation<double> animation,
|
||||||
reverse: true,
|
Widget child,
|
||||||
child: child,
|
) {
|
||||||
);
|
return _EnterTransition(
|
||||||
}
|
animation: animation,
|
||||||
return null; // unreachable
|
transitionType: transitionType,
|
||||||
|
reverse: true,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
},
|
},
|
||||||
child: widget.child,
|
reverseBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return _ExitTransition(
|
||||||
|
animation: animation,
|
||||||
|
transitionType: transitionType,
|
||||||
|
fillColor: color,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -428,8 +334,14 @@ class _EnterTransition extends StatelessWidget {
|
|||||||
|
|
||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: _fadeInTransition.animate(animation),
|
opacity: _fadeInTransition.animate(animation),
|
||||||
child: Transform.translate(
|
child: AnimatedBuilder(
|
||||||
offset: slideInTransition.evaluate(animation),
|
animation: animation,
|
||||||
|
builder: (BuildContext context, Widget child) {
|
||||||
|
return Transform.translate(
|
||||||
|
offset: slideInTransition.evaluate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -442,8 +354,14 @@ class _EnterTransition extends StatelessWidget {
|
|||||||
|
|
||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: _fadeInTransition.animate(animation),
|
opacity: _fadeInTransition.animate(animation),
|
||||||
child: Transform.translate(
|
child: AnimatedBuilder(
|
||||||
offset: slideInTransition.evaluate(animation),
|
animation: animation,
|
||||||
|
builder: (BuildContext context, Widget child) {
|
||||||
|
return Transform.translate(
|
||||||
|
offset: slideInTransition.evaluate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@ -468,7 +386,7 @@ class _ExitTransition extends StatelessWidget {
|
|||||||
this.animation,
|
this.animation,
|
||||||
this.transitionType,
|
this.transitionType,
|
||||||
this.reverse = false,
|
this.reverse = false,
|
||||||
this.fillColor,
|
@required this.fillColor,
|
||||||
this.child,
|
this.child,
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -504,9 +422,15 @@ class _ExitTransition extends StatelessWidget {
|
|||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: _fadeOutTransition.animate(animation),
|
opacity: _fadeOutTransition.animate(animation),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: fillColor ?? Theme.of(context).canvasColor,
|
color: fillColor,
|
||||||
child: Transform.translate(
|
child: AnimatedBuilder(
|
||||||
offset: slideOutTransition.evaluate(animation),
|
animation: animation,
|
||||||
|
builder: (BuildContext context, Widget child) {
|
||||||
|
return Transform.translate(
|
||||||
|
offset: slideOutTransition.evaluate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -521,9 +445,15 @@ class _ExitTransition extends StatelessWidget {
|
|||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: _fadeOutTransition.animate(animation),
|
opacity: _fadeOutTransition.animate(animation),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: fillColor ?? Theme.of(context).canvasColor,
|
color: fillColor,
|
||||||
child: Transform.translate(
|
child: AnimatedBuilder(
|
||||||
offset: slideOutTransition.evaluate(animation),
|
animation: animation,
|
||||||
|
builder: (BuildContext context, Widget child) {
|
||||||
|
return Transform.translate(
|
||||||
|
offset: slideOutTransition.evaluate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
child: child,
|
child: child,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
@ -533,7 +463,7 @@ class _ExitTransition extends StatelessWidget {
|
|||||||
return FadeTransition(
|
return FadeTransition(
|
||||||
opacity: _fadeOutTransition.animate(animation),
|
opacity: _fadeOutTransition.animate(animation),
|
||||||
child: Container(
|
child: Container(
|
||||||
color: fillColor ?? Theme.of(context).canvasColor,
|
color: fillColor,
|
||||||
child: ScaleTransition(
|
child: ScaleTransition(
|
||||||
scale: (!reverse ? _scaleUpTransition : _scaleDownTransition)
|
scale: (!reverse ? _scaleUpTransition : _scaleDownTransition)
|
||||||
.animate(animation),
|
.animate(animation),
|
||||||
|
299
packages/animations/test/dual_transition_builder_test.dart
Normal file
299
packages/animations/test/dual_transition_builder_test.dart
Normal file
@ -0,0 +1,299 @@
|
|||||||
|
// Copyright 2020 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:animations/src/dual_transition_builder.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:flutter/widgets.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
testWidgets('runs animations', (WidgetTester tester) async {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(Center(
|
||||||
|
child: DualTransitionBuilder(
|
||||||
|
animation: controller,
|
||||||
|
forwardBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reverseBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: Tween<double>(begin: 1.0, end: 0.0).animate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.green,
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
expect(_getScale(tester), 0.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(_getScale(tester), 0.5);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 0.5);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 0.0);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(_getScale(tester), 0.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('keeps state', (WidgetTester tester) async {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: DualTransitionBuilder(
|
||||||
|
animation: controller,
|
||||||
|
forwardBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reverseBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: Tween<double>(begin: 1.0, end: 0.0).animate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: const _StatefulTestWidget(name: 'Foo'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
final State<StatefulWidget> state =
|
||||||
|
tester.state(find.byType(_StatefulTestWidget));
|
||||||
|
expect(state, isNotNull);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('does not jump when interrupted - forward',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(Center(
|
||||||
|
child: DualTransitionBuilder(
|
||||||
|
animation: controller,
|
||||||
|
forwardBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reverseBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: Tween<double>(begin: 1.0, end: 0.0).animate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.green,
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
expect(_getScale(tester), 0.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(_getScale(tester), 0.5);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
expect(_getScale(tester), 0.5);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
await tester.pump();
|
||||||
|
expect(_getScale(tester), 0.5);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 75));
|
||||||
|
expect(_getScale(tester), 0.25);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 75));
|
||||||
|
expect(_getScale(tester), 0.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(_getScale(tester), 0.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
});
|
||||||
|
|
||||||
|
testWidgets('does not jump when interrupted - reverse',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
value: 1.0,
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(Center(
|
||||||
|
child: DualTransitionBuilder(
|
||||||
|
animation: controller,
|
||||||
|
forwardBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return ScaleTransition(
|
||||||
|
scale: animation,
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
reverseBuilder: (
|
||||||
|
BuildContext context,
|
||||||
|
Animation<double> animation,
|
||||||
|
Widget child,
|
||||||
|
) {
|
||||||
|
return FadeTransition(
|
||||||
|
opacity: Tween<double>(begin: 1.0, end: 0.0).animate(animation),
|
||||||
|
child: child,
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
color: Colors.green,
|
||||||
|
height: 100,
|
||||||
|
width: 100,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 0.5);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 0.5);
|
||||||
|
await tester.pump();
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 0.5);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 75));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 0.75);
|
||||||
|
|
||||||
|
await tester.pump(const Duration(milliseconds: 75));
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(_getScale(tester), 1.0);
|
||||||
|
expect(_getOpacity(tester), 1.0);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getScale(WidgetTester tester) {
|
||||||
|
final ScaleTransition scale = tester.widget(find.byType(ScaleTransition));
|
||||||
|
return scale.scale.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
double _getOpacity(WidgetTester tester) {
|
||||||
|
final FadeTransition scale = tester.widget(find.byType(FadeTransition));
|
||||||
|
return scale.opacity.value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StatefulTestWidget extends StatefulWidget {
|
||||||
|
const _StatefulTestWidget({Key key, this.name}) : super(key: key);
|
||||||
|
|
||||||
|
final String name;
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_StatefulTestWidget> createState() => _StatefulTestWidgetState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _StatefulTestWidgetState extends State<_StatefulTestWidget> {
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Text(widget.name);
|
||||||
|
}
|
||||||
|
}
|
@ -395,6 +395,58 @@ void main() {
|
|||||||
expect(find.byKey(topKey), findsNothing);
|
expect(find.byKey(topKey), findsNothing);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
testWidgets(
|
||||||
|
'should preserve state',
|
||||||
|
(WidgetTester tester) async {
|
||||||
|
final AnimationController controller = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
|
||||||
|
await tester.pumpWidget(
|
||||||
|
MaterialApp(
|
||||||
|
home: Scaffold(
|
||||||
|
body: Center(
|
||||||
|
child: FadeScaleTransition(
|
||||||
|
animation: controller,
|
||||||
|
child: const _FlutterLogoModal(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final State<StatefulWidget> state = tester.state(
|
||||||
|
find.byType(_FlutterLogoModal),
|
||||||
|
);
|
||||||
|
expect(state, isNotNull);
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
|
||||||
|
controller.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
|
||||||
|
controller.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_FlutterLogoModal))));
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
double _getOpacity(GlobalKey key, WidgetTester tester) {
|
double _getOpacity(GlobalKey key, WidgetTester tester) {
|
||||||
|
@ -364,6 +364,71 @@ void main() {
|
|||||||
);
|
);
|
||||||
expect(find.byKey(const ValueKey<String>(topRoute)), findsNothing);
|
expect(find.byKey(const ValueKey<String>(topRoute)), findsNothing);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('should keep state', (WidgetTester tester) async {
|
||||||
|
final AnimationController animation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
final AnimationController secondaryAnimation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: FadeThroughTransition(
|
||||||
|
child: const _StatefulTestWidget(name: 'Foo'),
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
final State<StatefulWidget> state = tester.state(
|
||||||
|
find.byType(_StatefulTestWidget),
|
||||||
|
);
|
||||||
|
expect(state, isNotNull);
|
||||||
|
|
||||||
|
animation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
animation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
double _getOpacity(String key, WidgetTester tester) {
|
double _getOpacity(String key, WidgetTester tester) {
|
||||||
|
@ -583,6 +583,72 @@ void main() {
|
|||||||
expect(fillContainerFinder, findsOneWidget);
|
expect(fillContainerFinder, findsOneWidget);
|
||||||
expect(tester.widget<Container>(fillContainerFinder).color, Colors.green);
|
expect(tester.widget<Container>(fillContainerFinder).color, Colors.green);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('should keep state', (WidgetTester tester) async {
|
||||||
|
final AnimationController animation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
final AnimationController secondaryAnimation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: SharedAxisTransition(
|
||||||
|
transitionType: SharedAxisTransitionType.horizontal,
|
||||||
|
child: const _StatefulTestWidget(name: 'Foo'),
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
final State<StatefulWidget> state = tester.state(
|
||||||
|
find.byType(_StatefulTestWidget),
|
||||||
|
);
|
||||||
|
expect(state, isNotNull);
|
||||||
|
|
||||||
|
animation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
animation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('SharedAxisTransitionType.vertical', () {
|
group('SharedAxisTransitionType.vertical', () {
|
||||||
@ -1160,6 +1226,72 @@ void main() {
|
|||||||
expect(fillContainerFinder, findsOneWidget);
|
expect(fillContainerFinder, findsOneWidget);
|
||||||
expect(tester.widget<Container>(fillContainerFinder).color, Colors.green);
|
expect(tester.widget<Container>(fillContainerFinder).color, Colors.green);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('should keep state', (WidgetTester tester) async {
|
||||||
|
final AnimationController animation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
final AnimationController secondaryAnimation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: SharedAxisTransition(
|
||||||
|
transitionType: SharedAxisTransitionType.vertical,
|
||||||
|
child: const _StatefulTestWidget(name: 'Foo'),
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
final State<StatefulWidget> state = tester.state(
|
||||||
|
find.byType(_StatefulTestWidget),
|
||||||
|
);
|
||||||
|
expect(state, isNotNull);
|
||||||
|
|
||||||
|
animation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
animation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
group('SharedAxisTransitionType.scaled', () {
|
group('SharedAxisTransitionType.scaled', () {
|
||||||
@ -1630,6 +1762,72 @@ void main() {
|
|||||||
expect(fillContainerFinder, findsOneWidget);
|
expect(fillContainerFinder, findsOneWidget);
|
||||||
expect(tester.widget<Container>(fillContainerFinder).color, Colors.green);
|
expect(tester.widget<Container>(fillContainerFinder).color, Colors.green);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
testWidgets('should keep state', (WidgetTester tester) async {
|
||||||
|
final AnimationController animation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
final AnimationController secondaryAnimation = AnimationController(
|
||||||
|
vsync: const TestVSync(),
|
||||||
|
duration: const Duration(milliseconds: 300),
|
||||||
|
);
|
||||||
|
await tester.pumpWidget(Directionality(
|
||||||
|
textDirection: TextDirection.ltr,
|
||||||
|
child: Center(
|
||||||
|
child: SharedAxisTransition(
|
||||||
|
transitionType: SharedAxisTransitionType.scaled,
|
||||||
|
child: const _StatefulTestWidget(name: 'Foo'),
|
||||||
|
animation: animation,
|
||||||
|
secondaryAnimation: secondaryAnimation,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
final State<StatefulWidget> state = tester.state(
|
||||||
|
find.byType(_StatefulTestWidget),
|
||||||
|
);
|
||||||
|
expect(state, isNotNull);
|
||||||
|
|
||||||
|
animation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.forward();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
secondaryAnimation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
|
||||||
|
animation.reverse();
|
||||||
|
await tester.pump();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pump(const Duration(milliseconds: 150));
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
await tester.pumpAndSettle();
|
||||||
|
expect(state, same(tester.state(find.byType(_StatefulTestWidget))));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user