Unverified Commit 9574d585 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Fix `shape` and `collapsedShape` isn't applied to `ExpansionTile`'s splash ink (#141777)

This updates the previous attempt https://github.com/flutter/flutter/pull/135855 and removes the complications when testing M3 ink sparkle effect. 
Thanks to this [PR](https://github.com/flutter/flutter/pull/138757) by @Piinks 

fixes [ExpansionTile InkSplash doesn't respect Shape's borderRadius](https://github.com/flutter/flutter/issues/125779)
fixes [`ExpansionTile.backgroundColor` &  `ExpansionTile.collapsedBackgroundColor` removes splash effect](https://github.com/flutter/flutter/issues/107113)

### Code sample

<details>
<summary>expand to view the code sample</summary> 

```dart
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Example(),
    );
  }
}

class Example extends StatelessWidget {
  const Example({super.key});

  @override
  Widget build(BuildContext context) {
    return const Scaffold(
      body: Center(
          child: Padding(
        padding: EdgeInsets.symmetric(horizontal: 24.0),
        child: ExpansionTile(
          collapsedBackgroundColor: Color(0x25ff0000),
          backgroundColor: Color(0x250000ff),
          collapsedShape: RoundedRectangleBorder(
            borderRadius: BorderRadius.all(Radius.circular(30.0)),
            side: BorderSide(color: Colors.black, width: 2.0),
          ),
          shape: RoundedRectangleBorder(
            borderRadius: BorderRadius.all(Radius.circular(30.0)),
            side: BorderSide(color: Colors.black, width: 2.0),
          ),
          clipBehavior: Clip.hardEdge,
          title: Text('Expansion Tile'),
          children: <Widget>[
            FlutterLogo(size: 50),
            FlutterLogo(size: 50),
            FlutterLogo(size: 50),
            FlutterLogo(size: 50),

          ],
        ),
      )),
    );
  }
}
```

</details>

### Before

<img width="789" alt="Screenshot 2024-01-18 at 18 16 15" src="https://github.com/flutter/flutter/assets/48603081/8c6a6f1e-6986-4acf-8dec-e223a682c0d7">

<img width="789" alt="Screenshot 2024-01-18 at 18 16 44" src="https://github.com/flutter/flutter/assets/48603081/f55f6a26-2128-48a1-b24d-3c14e4f6ecdc">

### After 
<img width="789" alt="Screenshot 2024-01-18 at 18 20 27" src="https://github.com/flutter/flutter/assets/48603081/7ec8b888-7319-460d-8488-9cd44c9246a6">

<img width="789" alt="Screenshot 2024-01-18 at 18 20 53" src="https://github.com/flutter/flutter/assets/48603081/80d66d5b-7eb2-4f47-ab4d-d7f469a731fa">
parent 634b326e
......@@ -475,8 +475,11 @@ class ExpansionTile extends StatefulWidget {
/// {@macro flutter.material.Material.clipBehavior}
///
/// If this is not null and a custom collapsed or expanded shape is provided,
/// the value of [clipBehavior] will be used to clip the expansion tile.
///
/// If this property is null, the [ExpansionTileThemeData.clipBehavior] is used. If that
/// is also null, a [Clip.none] is used
/// is also null, defaults to [Clip.antiAlias].
///
/// See also:
///
......@@ -656,11 +659,12 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
Widget _buildChildren(BuildContext context, Widget? child) {
final ThemeData theme = Theme.of(context);
final ExpansionTileThemeData expansionTileTheme = ExpansionTileTheme.of(context);
final Color backgroundColor = _backgroundColor.value ?? expansionTileTheme.backgroundColor ?? Colors.transparent;
final ShapeBorder expansionTileBorder = _border.value ?? const Border(
top: BorderSide(color: Colors.transparent),
bottom: BorderSide(color: Colors.transparent),
);
final Clip clipBehavior = widget.clipBehavior ?? expansionTileTheme.clipBehavior ?? Clip.none;
top: BorderSide(color: Colors.transparent),
bottom: BorderSide(color: Colors.transparent),
);
final Clip clipBehavior = widget.clipBehavior ?? expansionTileTheme.clipBehavior ?? Clip.antiAlias;
final MaterialLocalizations localizations = MaterialLocalizations.of(context);
final String onTapHint = _isExpanded
? localizations.expansionTileExpandedTapHint
......@@ -679,12 +683,13 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
break;
}
return Container(
clipBehavior: clipBehavior,
decoration: ShapeDecoration(
color: _backgroundColor.value ?? expansionTileTheme.backgroundColor ?? Colors.transparent,
shape: expansionTileBorder,
),
final Decoration decoration = ShapeDecoration(
color: backgroundColor,
shape: expansionTileBorder,
);
final Widget tile = Padding(
padding: decoration.padding,
child: Column(
mainAxisSize: MainAxisSize.min,
children: <Widget>[
......@@ -720,6 +725,23 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
],
),
);
final bool isShapeProvided = widget.shape != null || expansionTileTheme.shape != null
|| widget.collapsedShape != null || expansionTileTheme.collapsedShape != null;
if (isShapeProvided) {
return Material(
clipBehavior: clipBehavior,
color: backgroundColor,
shape: expansionTileBorder,
child: tile,
);
}
return DecoratedBox(
decoration: decoration,
child: tile,
);
}
@override
......
......@@ -2,6 +2,11 @@
// 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'])
library;
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
import 'package:flutter_test/flutter_test.dart';
......@@ -42,6 +47,13 @@ class TestTextState extends State<TestText> {
}
void main() {
Material getMaterial(WidgetTester tester) {
return tester.widget<Material>(find.descendant(
of: find.byType(ExpansionTile),
matching: find.byType(Material),
));
}
test('ExpansionTileThemeData copyWith, ==, hashCode basics', () {
expect(const ExpansionTileThemeData(), const ExpansionTileThemeData().copyWith());
expect(const ExpansionTileThemeData().hashCode, const ExpansionTileThemeData().copyWith().hashCode);
......@@ -173,21 +185,16 @@ void main() {
),
);
final ShapeDecoration shapeDecoration = tester.firstWidget<Container>(find.descendant(
of: find.byKey(tileKey),
matching: find.byType(Container),
)).decoration! as ShapeDecoration;
// When a custom shape is provided, ExpansionTile will use the
// Material widget to draw the shape and background color
// instead of a Container.
final Material material = getMaterial(tester);
final Clip tileClipBehavior = tester.firstWidget<Container>(find.descendant(
of: find.byKey(tileKey),
matching: find.byType(Container),
)).clipBehavior;
// expansionTile should have Clip.antiAlias as clipBehavior
expect(tileClipBehavior, clipBehavior);
// ExpansionTile should have Clip.antiAlias as clipBehavior.
expect(material.clipBehavior, clipBehavior);
// Check the tile's collapsed background color when collapsedBackgroundColor is applied.
expect(shapeDecoration.color, collapsedBackgroundColor);
expect(material.color, collapsedBackgroundColor);
final Rect titleRect = tester.getRect(find.text('Collapsed Tile'));
final Rect trailingRect = tester.getRect(find.byIcon(Icons.expand_more));
......@@ -211,7 +218,7 @@ void main() {
// Check the collapsed text color when textColor is applied.
expect(getTextColor(), collapsedTextColor);
// Check the collapsed ShapeBorder when shape is applied.
expect(shapeDecoration.shape, collapsedShape);
expect(material.shape, collapsedShape);
});
testWidgets('ExpansionTileTheme - expanded', (WidgetTester tester) async {
......@@ -232,6 +239,7 @@ void main() {
top: BorderSide(color: Colors.green),
bottom: BorderSide(color: Colors.green),
);
const Clip clipBehavior = Clip.none;
await tester.pumpWidget(
MaterialApp(
......@@ -248,6 +256,7 @@ void main() {
collapsedTextColor: collapsedTextColor,
shape: shape,
collapsedShape: collapsedShape,
clipBehavior: clipBehavior,
),
),
home: Material(
......@@ -264,12 +273,12 @@ void main() {
),
);
final ShapeDecoration shapeDecoration = tester.firstWidget<Container>(find.descendant(
of: find.byKey(tileKey),
matching: find.byType(Container),
)).decoration! as ShapeDecoration;
// When a custom shape is provided, ExpansionTile will use the
// Material widget to draw the shape and background color
// instead of a Container.
final Material material = getMaterial(tester);
// Check the tile's background color when backgroundColor is applied.
expect(shapeDecoration.color, backgroundColor);
expect(material.color, backgroundColor);
final Rect titleRect = tester.getRect(find.text('Expanded Tile'));
final Rect trailingRect = tester.getRect(find.byIcon(Icons.expand_more));
......@@ -293,7 +302,9 @@ void main() {
// Check the expanded text color when textColor is applied.
expect(getTextColor(), textColor);
// Check the expanded ShapeBorder when shape is applied.
expect(shapeDecoration.shape, shape);
expect(material.shape, shape);
// Check the clipBehavior when shape is applied.
expect(material.clipBehavior, clipBehavior);
// Check the child position when expandedAlignment is applied.
final Rect childRect = tester.getRect(find.text('Tile 1'));
......
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