Unverified Commit 81241910 authored by LongCatIsLooong's avatar LongCatIsLooong Committed by GitHub

Revert "Disable cursor opacity animation on macOS, make iOS cursor animation...

Revert "Disable cursor opacity animation on macOS, make iOS cursor animation discrete (#104335)" (#106762)
parent 6fbd6ea0
...@@ -1168,7 +1168,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements ...@@ -1168,7 +1168,7 @@ class _TextFieldState extends State<TextField> with RestorationMixin implements
forcePressEnabled = false; forcePressEnabled = false;
textSelectionControls ??= cupertinoDesktopTextSelectionControls; textSelectionControls ??= cupertinoDesktopTextSelectionControls;
paintCursorAboveText = true; paintCursorAboveText = true;
cursorOpacityAnimates = false; cursorOpacityAnimates = true;
cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor; cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor;
selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40); selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40);
cursorRadius ??= const Radius.circular(2.0); cursorRadius ??= const Radius.circular(2.0);
......
...@@ -702,6 +702,40 @@ void main() { ...@@ -702,6 +702,40 @@ void main() {
expect(editableText.cursorOffset, const Offset(-2.0 / 3.0, 0)); expect(editableText.cursorOffset, const Offset(-2.0 / 3.0, 0));
}); });
testWidgets('Cursor animates', (WidgetTester tester) async {
await tester.pumpWidget(
const CupertinoApp(
home: CupertinoTextField(),
),
);
final Finder textFinder = find.byType(CupertinoTextField);
await tester.tap(textFinder);
await tester.pump();
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorColor!.alpha, 255);
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 400));
expect(renderEditable.cursorColor!.alpha, 255);
await tester.pump(const Duration(milliseconds: 200));
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor!.alpha, 110);
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor!.alpha, 16);
await tester.pump(const Duration(milliseconds: 50));
expect(renderEditable.cursorColor!.alpha, 0);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Cursor radius is 2.0', (WidgetTester tester) async { testWidgets('Cursor radius is 2.0', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
const CupertinoApp( const CupertinoApp(
......
...@@ -609,6 +609,42 @@ void main() { ...@@ -609,6 +609,42 @@ void main() {
await checkCursorToggle(); await checkCursorToggle();
}); });
testWidgets('Cursor animates', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(
child: TextField(),
),
),
);
final Finder textFinder = find.byType(TextField);
await tester.tap(textFinder);
await tester.pump();
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorColor!.alpha, 255);
await tester.pump(const Duration(milliseconds: 100));
await tester.pump(const Duration(milliseconds: 400));
expect(renderEditable.cursorColor!.alpha, 255);
await tester.pump(const Duration(milliseconds: 200));
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor!.alpha, 110);
await tester.pump(const Duration(milliseconds: 100));
expect(renderEditable.cursorColor!.alpha, 16);
await tester.pump(const Duration(milliseconds: 50));
expect(renderEditable.cursorColor!.alpha, 0);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
// Regression test for https://github.com/flutter/flutter/issues/78918. // Regression test for https://github.com/flutter/flutter/issues/78918.
testWidgets('RenderEditable sets correct text editing value', (WidgetTester tester) async { testWidgets('RenderEditable sets correct text editing value', (WidgetTester tester) async {
final TextEditingController controller = TextEditingController(text: 'how are you'); final TextEditingController controller = TextEditingController(text: 'how are you');
...@@ -1292,7 +1328,7 @@ void main() { ...@@ -1292,7 +1328,7 @@ void main() {
editText = (findRenderEditable(tester).text! as TextSpan).text!; editText = (findRenderEditable(tester).text! as TextSpan).text!;
expect(editText.substring(editText.length - 1), '\u2022'); expect(editText.substring(editText.length - 1), '\u2022');
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.android })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.android }));
testWidgets('desktop obscureText control test', (WidgetTester tester) async { testWidgets('desktop obscureText control test', (WidgetTester tester) async {
await tester.pumpWidget( await tester.pumpWidget(
......
...@@ -166,75 +166,61 @@ void main() { ...@@ -166,75 +166,61 @@ void main() {
); );
}); });
testWidgets('Cursor animates on iOS', (WidgetTester tester) async { testWidgets('Cursor animates', (WidgetTester tester) async {
await tester.pumpWidget( const Widget widget = MaterialApp(
const MaterialApp(
home: Material( home: Material(
child: TextField(), child: TextField(
maxLines: 3,
), ),
), ),
); );
await tester.pumpWidget(widget);
final Finder textFinder = find.byType(TextField); await tester.tap(find.byType(TextField));
await tester.tap(textFinder);
await tester.pump(); await tester.pump();
final EditableTextState editableTextState = tester.firstState(find.byType(EditableText)); final EditableTextState editableTextState = tester.firstState(find.byType(EditableText));
final RenderEditable renderEditable = editableTextState.renderEditable; final RenderEditable renderEditable = editableTextState.renderEditable;
expect(renderEditable.cursorColor!.opacity, 1.0); expect(renderEditable.cursorColor!.alpha, 255);
int walltimeMicrosecond = 0; // Trigger initial timer. When focusing the first time, the cursor shows
double lastVerifiedOpacity = 1.0; // for slightly longer than the average on time.
await tester.pump();
await tester.pump(const Duration(milliseconds: 200));
// Start timing standard cursor show period.
expect(renderEditable.cursorColor!.alpha, 255);
expect(renderEditable, paints..rrect(color: const Color(0xff2196f3)));
Future<void> verifyKeyFrame({ required double opacity, required int at }) async { await tester.pump(const Duration(milliseconds: 500));
const int delta = 1; // Start to animate the cursor away.
assert(at - delta > walltimeMicrosecond); expect(renderEditable.cursorColor!.alpha, 255);
await tester.pump(Duration(microseconds: at - delta - walltimeMicrosecond)); expect(renderEditable, paints..rrect(color: const Color(0xff2196f3)));
// Instead of verifying the opacity at each key frame, this function await tester.pump(const Duration(milliseconds: 100));
// verifies the opacity immediately *before* each key frame to avoid expect(renderEditable.cursorColor!.alpha, 110);
// fp precision issues. expect(renderEditable, paints..rrect(color: const Color(0x6e2196f3)));
expect(
renderEditable.cursorColor!.opacity,
closeTo(lastVerifiedOpacity, 0.01),
reason: 'opacity at ${at-delta} microseconds',
);
walltimeMicrosecond = at - delta; await tester.pump(const Duration(milliseconds: 100));
lastVerifiedOpacity = opacity; expect(renderEditable.cursorColor!.alpha, 16);
} expect(renderEditable, paints..rrect(color: const Color(0x102196f3)));
await verifyKeyFrame(opacity: 1.0, at: 500000); await tester.pump(const Duration(milliseconds: 100));
await verifyKeyFrame(opacity: 0.75, at: 537500); expect(renderEditable.cursorColor!.alpha, 0);
await verifyKeyFrame(opacity: 0.5, at: 575000); // Don't try to draw the cursor.
await verifyKeyFrame(opacity: 0.25, at: 612500); expect(renderEditable, paintsExactlyCountTimes(#drawRRect, 0));
await verifyKeyFrame(opacity: 0.0, at: 650000);
await verifyKeyFrame(opacity: 0.0, at: 850000);
await verifyKeyFrame(opacity: 0.25, at: 887500);
await verifyKeyFrame(opacity: 0.5, at: 925000);
await verifyKeyFrame(opacity: 0.75, at: 962500);
await verifyKeyFrame(opacity: 1.0, at: 1000000);
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS }));
testWidgets('Cursor does not animate on non-iOS platforms', (WidgetTester tester) async {
await tester.pumpWidget(
const MaterialApp(
home: Material(child: TextField(maxLines: 3)),
),
);
await tester.tap(find.byType(TextField)); // Wait some more while the cursor is gone. It'll trigger the cursor to
await tester.pump(); // start animating in again.
// Wait for the current animation to finish. If the cursor never stops its await tester.pump(const Duration(milliseconds: 300));
// blinking animation the test will timeout. expect(renderEditable.cursorColor!.alpha, 0);
await tester.pumpAndSettle(); expect(renderEditable, paintsExactlyCountTimes(#drawRRect, 0));
for (int i = 0; i < 40; i += 1) { await tester.pump(const Duration(milliseconds: 50));
await tester.pump(const Duration(milliseconds: 100)); // Cursor starts coming back.
expect(tester.hasRunningAnimations, false); expect(renderEditable.cursorColor!.alpha, 79);
} expect(renderEditable, paints..rrect(color: const Color(0x4f2196f3)));
}, variant: TargetPlatformVariant.all(excluding: <TargetPlatform>{ TargetPlatform.iOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('Cursor does not animate on Android', (WidgetTester tester) async { testWidgets('Cursor does not animate on Android', (WidgetTester tester) async {
final Color defaultCursorColor = Color(ThemeData.fallback().colorScheme.primary.value); final Color defaultCursorColor = Color(ThemeData.fallback().colorScheme.primary.value);
...@@ -970,40 +956,4 @@ void main() { ...@@ -970,40 +956,4 @@ void main() {
); );
EditableText.debugDeterministicCursor = false; EditableText.debugDeterministicCursor = false;
}, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS })); }, variant: const TargetPlatformVariant(<TargetPlatform>{ TargetPlatform.iOS, TargetPlatform.macOS }));
testWidgets('password briefly does not show last character when disabled by system', (WidgetTester tester) async {
final bool debugDeterministicCursor = EditableText.debugDeterministicCursor;
EditableText.debugDeterministicCursor = false;
addTearDown(() {
EditableText.debugDeterministicCursor = debugDeterministicCursor;
});
await tester.pumpWidget(MaterialApp(
home: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
obscureText: true,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
));
await tester.enterText(find.byType(EditableText), 'AA');
await tester.pump();
await tester.enterText(find.byType(EditableText), 'AAA');
await tester.pump();
tester.binding.platformDispatcher.brieflyShowPasswordTestValue = false;
addTearDown(() {
tester.binding.platformDispatcher.brieflyShowPasswordTestValue = true;
});
expect((findRenderEditable(tester).text! as TextSpan).text, '••A');
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
});
} }
...@@ -3943,6 +3943,42 @@ void main() { ...@@ -3943,6 +3943,42 @@ void main() {
expect((findRenderEditable(tester).text! as TextSpan).text, '•••'); expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
}); });
testWidgets('password briefly does not show last character on Android if turned off', (WidgetTester tester) async {
final bool debugDeterministicCursor = EditableText.debugDeterministicCursor;
EditableText.debugDeterministicCursor = false;
addTearDown(() {
EditableText.debugDeterministicCursor = debugDeterministicCursor;
});
await tester.pumpWidget(MaterialApp(
home: EditableText(
backgroundCursorColor: Colors.grey,
controller: controller,
obscureText: true,
focusNode: focusNode,
style: textStyle,
cursorColor: cursorColor,
),
));
await tester.enterText(find.byType(EditableText), 'AA');
await tester.pump();
await tester.enterText(find.byType(EditableText), 'AAA');
await tester.pump();
tester.binding.platformDispatcher.brieflyShowPasswordTestValue = false;
addTearDown(() {
tester.binding.platformDispatcher.brieflyShowPasswordTestValue = true;
});
expect((findRenderEditable(tester).text! as TextSpan).text, '••A');
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
await tester.pump(const Duration(milliseconds: 500));
expect((findRenderEditable(tester).text! as TextSpan).text, '•••');
});
group('a11y copy/cut/paste', () { group('a11y copy/cut/paste', () {
Future<void> buildApp(MockTextSelectionControls controls, WidgetTester tester) { Future<void> buildApp(MockTextSelectionControls controls, WidgetTester tester) {
return tester.pumpWidget(MaterialApp( return tester.pumpWidget(MaterialApp(
......
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