Unverified Commit 0b7fddae authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add ability to control if bottom child focus can be excluded in `AnimatedCrossFade` (#96593)

parent ec37a4eb
...@@ -130,6 +130,7 @@ class AnimatedCrossFade extends StatefulWidget { ...@@ -130,6 +130,7 @@ class AnimatedCrossFade extends StatefulWidget {
required this.duration, required this.duration,
this.reverseDuration, this.reverseDuration,
this.layoutBuilder = defaultLayoutBuilder, this.layoutBuilder = defaultLayoutBuilder,
this.excludeBottomFocus = true,
}) : assert(firstChild != null), }) : assert(firstChild != null),
assert(secondChild != null), assert(secondChild != null),
assert(firstCurve != null), assert(firstCurve != null),
...@@ -139,6 +140,7 @@ class AnimatedCrossFade extends StatefulWidget { ...@@ -139,6 +140,7 @@ class AnimatedCrossFade extends StatefulWidget {
assert(crossFadeState != null), assert(crossFadeState != null),
assert(duration != null), assert(duration != null),
assert(layoutBuilder != null), assert(layoutBuilder != null),
assert(excludeBottomFocus != null),
super(key: key); super(key: key);
/// The child that is visible when [crossFadeState] is /// The child that is visible when [crossFadeState] is
...@@ -206,6 +208,15 @@ class AnimatedCrossFade extends StatefulWidget { ...@@ -206,6 +208,15 @@ class AnimatedCrossFade extends StatefulWidget {
/// result in the widgets jumping about when the cross-fade state is changed. /// result in the widgets jumping about when the cross-fade state is changed.
final AnimatedCrossFadeBuilder layoutBuilder; final AnimatedCrossFadeBuilder layoutBuilder;
/// When true, this is equivalent to wrapping the bottom widget with an [ExcludeFocus]
/// widget while it is at the bottom of the cross-fade stack.
///
/// Defaults to true. When it is false, the bottom widget in the cross-fade stack
/// can remain in focus until the top widget requests focus. This is useful for
/// animating between different [TextField]s so the keyboard remains open during the
/// cross-fade animation.
final bool excludeBottomFocus;
/// The default layout algorithm used by [AnimatedCrossFade]. /// The default layout algorithm used by [AnimatedCrossFade].
/// ///
/// The top child is placed in a stack that sizes itself to match the top /// The top child is placed in a stack that sizes itself to match the top
...@@ -345,6 +356,7 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid ...@@ -345,6 +356,7 @@ class _AnimatedCrossFadeState extends State<AnimatedCrossFade> with TickerProvid
child: IgnorePointer( child: IgnorePointer(
child: ExcludeSemantics( // Always exclude the semantics of the widget that's fading out. child: ExcludeSemantics( // Always exclude the semantics of the widget that's fading out.
child: ExcludeFocus( child: ExcludeFocus(
excluding: widget.excludeBottomFocus,
child: FadeTransition( child: FadeTransition(
opacity: bottomAnimation, opacity: bottomAnimation,
child: bottomChild, child: bottomChild,
......
...@@ -385,6 +385,31 @@ void main() { ...@@ -385,6 +385,31 @@ void main() {
expect(hiddenNode.hasPrimaryFocus, isFalse); expect(hiddenNode.hasPrimaryFocus, isFalse);
}); });
testWidgets('AnimatedCrossFade bottom child can have focus', (WidgetTester tester) async {
await tester.pumpWidget(
Directionality(
textDirection: TextDirection.ltr,
child: AnimatedCrossFade(
firstChild: TextButton(onPressed: () {}, child: const Text('AAA')),
secondChild: TextButton(onPressed: () {}, child: const Text('BBB')),
crossFadeState: CrossFadeState.showFirst,
duration: const Duration(milliseconds: 50),
excludeBottomFocus: false,
),
),
);
final FocusNode visibleNode = Focus.of(tester.element(find.text('AAA')), scopeOk: true);
visibleNode.requestFocus();
await tester.pump();
expect(visibleNode.hasPrimaryFocus, isTrue);
final FocusNode hiddenNode = Focus.of(tester.element(find.text('BBB')), scopeOk: true);
hiddenNode.requestFocus();
await tester.pump();
expect(hiddenNode.hasPrimaryFocus, isTrue);
});
testWidgets('AnimatedCrossFade second child do not receive touch events', testWidgets('AnimatedCrossFade second child do not receive touch events',
(WidgetTester tester) async { (WidgetTester tester) async {
int numberOfTouchEventNoticed = 0; int numberOfTouchEventNoticed = 0;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment