[Material] Create an InkSparkle splash effect that matches the Material 3 ripple effect (#99731)
Create an InkSparkle effect that matches the Material 3 ripple on Android 12. Similar to InkRipple, but uses the new custom shader API.
This commit is contained in:
parent
cb9e9a3786
commit
bb2cca5f59
@ -87,6 +87,7 @@ export 'src/material/icons.dart';
|
||||
export 'src/material/ink_decoration.dart';
|
||||
export 'src/material/ink_highlight.dart';
|
||||
export 'src/material/ink_ripple.dart';
|
||||
export 'src/material/ink_sparkle.dart';
|
||||
export 'src/material/ink_splash.dart';
|
||||
export 'src/material/ink_well.dart';
|
||||
export 'src/material/input_border.dart';
|
||||
|
3666
packages/flutter/lib/src/material/ink_sparkle.dart
Normal file
3666
packages/flutter/lib/src/material/ink_sparkle.dart
Normal file
File diff suppressed because it is too large
Load Diff
152
packages/flutter/test/material/ink_sparkle_test.dart
Normal file
152
packages/flutter/test/material/ink_sparkle_test.dart
Normal file
@ -0,0 +1,152 @@
|
||||
// Copyright 2014 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.
|
||||
|
||||
// This file is run as part of a reduced test set in CI on Mac and Windows
|
||||
// machines.
|
||||
@Tags(<String>['reduced-test-set'])
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/src/foundation/constants.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
import '../rendering/mock_canvas.dart';
|
||||
|
||||
void main() {
|
||||
testWidgets('InkSparkle in a Button compiles and does not crash', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(splashFactory: InkSparkle.splashFactory),
|
||||
child: const Text('Sparkle!'),
|
||||
onPressed: () { },
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
final Finder buttonFinder = find.text('Sparkle!');
|
||||
await tester.tap(buttonFinder);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
},
|
||||
skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.
|
||||
);
|
||||
|
||||
testWidgets('InkSparkle default splashFactory paints with drawRect when bounded', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: InkWell(
|
||||
splashFactory: InkSparkle.splashFactory,
|
||||
child: const Text('Sparkle!'),
|
||||
onTap: () { },
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
final Finder buttonFinder = find.text('Sparkle!');
|
||||
await tester.tap(buttonFinder);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final MaterialInkController material = Material.of(tester.element(buttonFinder))!;
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawRect, 1));
|
||||
},
|
||||
skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.
|
||||
);
|
||||
|
||||
testWidgets('InkSparkle default splashFactory paints with drawPaint when unbounded', (WidgetTester tester) async {
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: InkResponse(
|
||||
splashFactory: InkSparkle.splashFactory,
|
||||
child: const Text('Sparkle!'),
|
||||
onTap: () { },
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
final Finder buttonFinder = find.text('Sparkle!');
|
||||
await tester.tap(buttonFinder);
|
||||
await tester.pump();
|
||||
await tester.pumpAndSettle();
|
||||
|
||||
final MaterialInkController material = Material.of(tester.element(buttonFinder))!;
|
||||
await tester.pump(const Duration(milliseconds: 200));
|
||||
expect(material, paintsExactlyCountTimes(#drawPaint, 1));
|
||||
},
|
||||
skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.
|
||||
);
|
||||
|
||||
/////////////
|
||||
// Goldens //
|
||||
/////////////
|
||||
|
||||
testWidgets('InkSparkle renders with sparkles when top left of button is tapped', (WidgetTester tester) async {
|
||||
await _runTest(tester, 'top_left', 0.2);
|
||||
},
|
||||
skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.
|
||||
);
|
||||
|
||||
testWidgets('InkSparkle renders with sparkles when center of button is tapped', (WidgetTester tester) async {
|
||||
await _runTest(tester, 'center', 0.5);
|
||||
},
|
||||
skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.
|
||||
);
|
||||
|
||||
testWidgets('InkSparkle renders with sparkles when bottom right of button is tapped', (WidgetTester tester) async {
|
||||
await _runTest(tester, 'bottom_right', 0.8);
|
||||
},
|
||||
skip: kIsWeb, // [intended] SPIR-V shaders are not yet supported for web.
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _runTest(WidgetTester tester, String positionName, double distanceFromTopLeft) async {
|
||||
final Key repaintKey = UniqueKey();
|
||||
final Key buttonKey = UniqueKey();
|
||||
|
||||
await tester.pumpWidget(MaterialApp(
|
||||
home: Scaffold(
|
||||
body: Center(
|
||||
child: RepaintBoundary(
|
||||
key: repaintKey,
|
||||
child: ElevatedButton(
|
||||
key: buttonKey,
|
||||
style: ElevatedButton.styleFrom(splashFactory: InkSparkle.constantTurbulenceSeedSplashFactory),
|
||||
child: const Text('Sparkle!'),
|
||||
onPressed: () { },
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
final Finder buttonFinder = find.byKey(buttonKey);
|
||||
final Finder repaintFinder = find.byKey(repaintKey);
|
||||
|
||||
await _warmUpShader(tester, buttonFinder);
|
||||
|
||||
final Offset topLeft = tester.getTopLeft(buttonFinder);
|
||||
final Offset bottomRight = tester.getBottomRight(buttonFinder);
|
||||
|
||||
final Offset target = topLeft + (bottomRight - topLeft) * distanceFromTopLeft;
|
||||
await tester.tapAt(target);
|
||||
for (int i = 0; i <= 5; i++) {
|
||||
await tester.pump(const Duration(milliseconds: 50));
|
||||
await expectLater(
|
||||
repaintFinder,
|
||||
matchesGoldenFile('ink_sparkle.$positionName.$i.png'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Warm up shader. Compilation is of the order of 10 milliseconds and
|
||||
// Animation is < 1000 milliseconds. Use 2000 milliseconds as a safety
|
||||
// net to prevent flakiness.
|
||||
Future<void> _warmUpShader(WidgetTester tester, Finder buttonFinder) async {
|
||||
await tester.tap(buttonFinder);
|
||||
await tester.pumpAndSettle(const Duration(milliseconds: 2000));
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user