Feat: Add carousel view theme (#164769)
Feat: Add carousel view theme
fixes: #159679
## Pre-launch Checklist
- [x] I read the [Contributor Guide] and followed the process outlined
there for submitting PRs.
- [x] I read the [Tree Hygiene] wiki page, which explains my
responsibilities.
- [x] I read and followed the [Flutter Style Guide], including [Features
we expect every widget to implement].
- [x] I signed the [CLA].
- [x] I listed at least one issue that this PR fixes in the description
above.
- [x] I updated/added relevant documentation (doc comments with `///`).
- [x] I added new tests to check the change I am making, or this PR is
[test-exempt].
- [x] I followed the [breaking change policy] and added [Data Driven
Fixes] where supported.
- [x] All existing and new tests are passing.
diff --git a/packages/flutter/lib/material.dart b/packages/flutter/lib/material.dart
index fff0a91..caab343 100644
--- a/packages/flutter/lib/material.dart
+++ b/packages/flutter/lib/material.dart
@@ -47,6 +47,7 @@
export 'src/material/card.dart';
export 'src/material/card_theme.dart';
export 'src/material/carousel.dart';
+export 'src/material/carousel_theme.dart';
export 'src/material/checkbox.dart';
export 'src/material/checkbox_list_tile.dart';
export 'src/material/checkbox_theme.dart';
diff --git a/packages/flutter/lib/src/material/carousel.dart b/packages/flutter/lib/src/material/carousel.dart
index 1a36f50..daa1a52 100644
--- a/packages/flutter/lib/src/material/carousel.dart
+++ b/packages/flutter/lib/src/material/carousel.dart
@@ -12,6 +12,8 @@
import 'package:flutter/rendering.dart';
import 'package:flutter/widgets.dart';
+import 'carousel_theme.dart';
+import 'color_scheme.dart';
import 'colors.dart';
import 'ink_well.dart';
import 'material.dart';
@@ -401,25 +403,30 @@
}
}
- Widget _buildCarouselItem(ThemeData theme, int index) {
- final EdgeInsets effectivePadding = widget.padding ?? const EdgeInsets.all(4.0);
+ Widget _buildCarouselItem(int index) {
+ final CarouselViewThemeData carouselTheme = CarouselViewTheme.of(context);
+ final ColorScheme colorScheme = ColorScheme.of(context);
+ final EdgeInsets effectivePadding =
+ widget.padding ?? carouselTheme.padding ?? const EdgeInsets.all(4.0);
final Color effectiveBackgroundColor =
- widget.backgroundColor ?? Theme.of(context).colorScheme.surface;
- final double effectiveElevation = widget.elevation ?? 0.0;
+ widget.backgroundColor ?? carouselTheme.backgroundColor ?? colorScheme.surface;
+ final double effectiveElevation = widget.elevation ?? carouselTheme.elevation ?? 0.0;
final ShapeBorder effectiveShape =
widget.shape ??
+ carouselTheme.shape ??
const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0)));
final WidgetStateProperty<Color?> effectiveOverlayColor =
widget.overlayColor ??
+ carouselTheme.overlayColor ??
WidgetStateProperty.resolveWith((Set<WidgetState> states) {
if (states.contains(WidgetState.pressed)) {
- return theme.colorScheme.onSurface.withOpacity(0.1);
+ return colorScheme.onSurface.withOpacity(0.1);
}
if (states.contains(WidgetState.hovered)) {
- return theme.colorScheme.onSurface.withOpacity(0.08);
+ return colorScheme.onSurface.withOpacity(0.08);
}
if (states.contains(WidgetState.focused)) {
- return theme.colorScheme.onSurface.withOpacity(0.1);
+ return colorScheme.onSurface.withOpacity(0.1);
}
return null;
});
@@ -461,7 +468,7 @@
itemExtent: _itemExtent!,
minExtent: widget.shrinkExtent,
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
- return _buildCarouselItem(theme, index);
+ return _buildCarouselItem(index);
}, childCount: widget.children.length),
);
}
@@ -475,7 +482,7 @@
shrinkExtent: widget.shrinkExtent,
weights: _flexWeights!,
delegate: SliverChildBuilderDelegate((BuildContext context, int index) {
- return _buildCarouselItem(theme, index);
+ return _buildCarouselItem(index);
}, childCount: widget.children.length),
);
}
diff --git a/packages/flutter/lib/src/material/carousel_theme.dart b/packages/flutter/lib/src/material/carousel_theme.dart
new file mode 100644
index 0000000..6bc4d58
--- /dev/null
+++ b/packages/flutter/lib/src/material/carousel_theme.dart
@@ -0,0 +1,183 @@
+// 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.
+
+import 'dart:ui' show lerpDouble;
+
+import 'package:flutter/foundation.dart';
+import 'package:flutter/widgets.dart';
+
+import 'carousel.dart';
+import 'material.dart';
+import 'theme.dart';
+
+/// Defines default property values for descendant [CarouselView] widgets.
+///
+/// Descendant widgets obtain the current [CarouselViewThemeData] object using
+/// `CarouselViewTheme.of(context)`. Instances of [CarouselViewThemeData] can be
+/// customized with [CarouselViewThemeData.copyWith].
+///
+/// Typically a [CarouselViewThemeData] is specified as part of the overall [Theme]
+/// with [ThemeData.carouselViewTheme].
+///
+/// All [CarouselViewThemeData] properties are `null` by default. When null, the [CarouselView]
+/// will provide its own defaults.
+///
+/// See also:
+///
+/// * [CarouselViewTheme], an [InheritedWidget] that propagates the theme to its descendants.
+/// * [ThemeData], which describes the overall theme information for the application.
+@immutable
+class CarouselViewThemeData with Diagnosticable {
+ /// Creates a theme that can be used for [ThemeData.carouselViewTheme].
+ const CarouselViewThemeData({
+ this.elevation,
+ this.backgroundColor,
+ this.overlayColor,
+ this.shape,
+ this.padding,
+ });
+
+ /// The amount of space to surround each carousel item with.
+ ///
+ /// Overrides the default value for [CarouselView.padding].
+ final EdgeInsets? padding;
+
+ /// The background color for each carousel item.
+ ///
+ /// Overrides the default value for [CarouselView.backgroundColor].
+ final Color? backgroundColor;
+
+ /// The z-coordinate of each carousel item.
+ ///
+ /// This controls the size of the shadow below the carousel.
+ ///
+ /// Overrides the default value for [CarouselView.elevation].
+ final double? elevation;
+
+ /// The shape of the carousel item's [Material].
+ ///
+ /// Overrides the default value for [CarouselView.shape].
+ final OutlinedBorder? shape;
+
+ /// The highlight color to indicate the carousel items are in pressed, hovered
+ /// or focused states.
+ ///
+ /// Overrides the default value for [CarouselView.overlayColor].
+ final WidgetStateProperty<Color?>? overlayColor;
+
+ /// Creates a copy of this object with the given fields replaced with the
+ /// new values.
+ CarouselViewThemeData copyWith({
+ Color? backgroundColor,
+ double? elevation,
+ OutlinedBorder? shape,
+ WidgetStateProperty<Color?>? overlayColor,
+ EdgeInsets? padding,
+ }) {
+ return CarouselViewThemeData(
+ backgroundColor: backgroundColor ?? this.backgroundColor,
+ elevation: elevation ?? this.elevation,
+ shape: shape ?? this.shape,
+ overlayColor: overlayColor ?? this.overlayColor,
+ padding: padding ?? this.padding,
+ );
+ }
+
+ /// Linearly interpolate between two carousel themes.
+ ///
+ /// {@macro dart.ui.shadow.lerp}
+ static CarouselViewThemeData lerp(CarouselViewThemeData? a, CarouselViewThemeData? b, double t) {
+ if (identical(a, b) && a != null) {
+ return a;
+ }
+ return CarouselViewThemeData(
+ backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t),
+ elevation: lerpDouble(a?.elevation, b?.elevation, t),
+ shape: ShapeBorder.lerp(a?.shape, b?.shape, t) as OutlinedBorder?,
+ overlayColor: WidgetStateProperty.lerp<Color?>(
+ a?.overlayColor,
+ b?.overlayColor,
+ t,
+ Color.lerp,
+ ),
+ padding: EdgeInsets.lerp(a?.padding, b?.padding, t),
+ );
+ }
+
+ @override
+ int get hashCode => Object.hash(backgroundColor, elevation, shape, overlayColor, padding);
+
+ @override
+ bool operator ==(Object other) {
+ if (identical(this, other)) {
+ return true;
+ }
+ if (other.runtimeType != runtimeType) {
+ return false;
+ }
+ return other is CarouselViewThemeData &&
+ other.backgroundColor == backgroundColor &&
+ other.elevation == elevation &&
+ other.shape == shape &&
+ other.overlayColor == overlayColor &&
+ other.padding == padding;
+ }
+
+ @override
+ void debugFillProperties(DiagnosticPropertiesBuilder properties) {
+ super.debugFillProperties(properties);
+ properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null));
+ properties.add(DoubleProperty('elevation', elevation, defaultValue: null));
+ properties.add(DiagnosticsProperty<OutlinedBorder>('shape', shape, defaultValue: null));
+ properties.add(
+ DiagnosticsProperty<WidgetStateProperty<Color?>>(
+ 'overlayColor',
+ overlayColor,
+ defaultValue: null,
+ ),
+ );
+ properties.add(DiagnosticsProperty<EdgeInsets>('padding', padding, defaultValue: null));
+ }
+}
+
+/// Applies a carousel theme to descendant [CarouselView] widgets.
+///
+/// Descendant widgets obtain the current theme's [CarouselViewThemeData] using
+/// [CarouselViewTheme.of]. When a widget uses [CarouselViewTheme.of], it is automatically
+/// rebuilt if the theme later changes.
+///
+/// A carousel theme can be specified as part of the overall Material theme using
+/// [ThemeData.carouselViewTheme].
+///
+/// See also:
+///
+/// * [CarouselViewThemeData], which describes the actual configuration of a carousel
+/// theme.
+/// * [Theme], which controls the overall theme inheritance.
+class CarouselViewTheme extends InheritedTheme {
+ /// Creates a carousel theme that configures all descendant [CarouselView] widgets.
+ const CarouselViewTheme({super.key, required this.data, required super.child});
+
+ /// The properties for descendant carousel widgets.
+ final CarouselViewThemeData data;
+
+ /// Returns the configuration [data] from the closest [CarouselViewTheme] ancestor.
+ ///
+ /// If there is no ancestor, it returns [ThemeData.carouselViewTheme].
+ static CarouselViewThemeData of(BuildContext context) {
+ final CarouselViewTheme? inheritedTheme =
+ context.dependOnInheritedWidgetOfExactType<CarouselViewTheme>();
+ return inheritedTheme?.data ?? Theme.of(context).carouselViewTheme;
+ }
+
+ /// Wraps the given [child] with a [CarouselViewTheme] containing the [data].
+ @override
+ Widget wrap(BuildContext context, Widget child) {
+ return CarouselViewTheme(data: data, child: child);
+ }
+
+ /// Returns true if the [data] fields of the two themes are different.
+ @override
+ bool updateShouldNotify(CarouselViewTheme oldWidget) => data != oldWidget.data;
+}
diff --git a/packages/flutter/lib/src/material/theme_data.dart b/packages/flutter/lib/src/material/theme_data.dart
index e11e1ee..bedd45b 100644
--- a/packages/flutter/lib/src/material/theme_data.dart
+++ b/packages/flutter/lib/src/material/theme_data.dart
@@ -21,6 +21,7 @@
import 'button_bar_theme.dart';
import 'button_theme.dart';
import 'card_theme.dart';
+import 'carousel_theme.dart';
import 'checkbox_theme.dart';
import 'chip_theme.dart';
import 'color_scheme.dart';
@@ -324,6 +325,7 @@
BottomSheetThemeData? bottomSheetTheme,
ButtonThemeData? buttonTheme,
CardThemeData? cardTheme,
+ CarouselViewThemeData? carouselViewTheme,
CheckboxThemeData? checkboxTheme,
ChipThemeData? chipTheme,
DataTableThemeData? dataTableTheme,
@@ -528,6 +530,7 @@
bottomNavigationBarTheme ??= const BottomNavigationBarThemeData();
bottomSheetTheme ??= const BottomSheetThemeData();
cardTheme ??= const CardThemeData();
+ carouselViewTheme ??= const CarouselViewThemeData();
checkboxTheme ??= const CheckboxThemeData();
chipTheme ??= const ChipThemeData();
dataTableTheme ??= const DataTableThemeData();
@@ -622,6 +625,7 @@
bottomSheetTheme: bottomSheetTheme,
buttonTheme: buttonTheme,
cardTheme: cardTheme,
+ carouselViewTheme: carouselViewTheme,
checkboxTheme: checkboxTheme,
chipTheme: chipTheme,
dataTableTheme: dataTableTheme,
@@ -733,6 +737,7 @@
required this.bottomSheetTheme,
required this.buttonTheme,
required this.cardTheme,
+ required this.carouselViewTheme,
required this.checkboxTheme,
required this.chipTheme,
required this.dataTableTheme,
@@ -1305,6 +1310,9 @@
/// This is the value returned from [CardTheme.of].
final CardThemeData cardTheme;
+ /// A theme for customizing the appearance and layout of [CarouselView] widgets.
+ final CarouselViewThemeData carouselViewTheme;
+
/// A theme for customizing the appearance and layout of [Checkbox] widgets.
final CheckboxThemeData checkboxTheme;
@@ -1516,6 +1524,7 @@
BottomSheetThemeData? bottomSheetTheme,
ButtonThemeData? buttonTheme,
CardThemeData? cardTheme,
+ CarouselViewThemeData? carouselViewTheme,
CheckboxThemeData? checkboxTheme,
ChipThemeData? chipTheme,
DataTableThemeData? dataTableTheme,
@@ -1634,6 +1643,7 @@
bottomSheetTheme: bottomSheetTheme ?? this.bottomSheetTheme,
buttonTheme: buttonTheme ?? this.buttonTheme,
cardTheme: cardTheme ?? this.cardTheme,
+ carouselViewTheme: carouselViewTheme ?? this.carouselViewTheme,
checkboxTheme: checkboxTheme ?? this.checkboxTheme,
chipTheme: chipTheme ?? this.chipTheme,
dataTableTheme: dataTableTheme ?? this.dataTableTheme,
@@ -1954,6 +1964,7 @@
bottomSheetTheme: BottomSheetThemeData.lerp(a.bottomSheetTheme, b.bottomSheetTheme, t)!,
buttonTheme: t < 0.5 ? a.buttonTheme : b.buttonTheme,
cardTheme: CardThemeData.lerp(a.cardTheme, b.cardTheme, t),
+ carouselViewTheme: CarouselViewThemeData.lerp(a.carouselViewTheme, b.carouselViewTheme, t),
checkboxTheme: CheckboxThemeData.lerp(a.checkboxTheme, b.checkboxTheme, t),
chipTheme: ChipThemeData.lerp(a.chipTheme, b.chipTheme, t)!,
dataTableTheme: DataTableThemeData.lerp(a.dataTableTheme, b.dataTableTheme, t),
@@ -2072,6 +2083,7 @@
other.bottomSheetTheme == bottomSheetTheme &&
other.buttonTheme == buttonTheme &&
other.cardTheme == cardTheme &&
+ other.carouselViewTheme == carouselViewTheme &&
other.checkboxTheme == checkboxTheme &&
other.chipTheme == chipTheme &&
other.dataTableTheme == dataTableTheme &&
@@ -2171,6 +2183,7 @@
bottomSheetTheme,
buttonTheme,
cardTheme,
+ carouselViewTheme,
checkboxTheme,
chipTheme,
dataTableTheme,
@@ -2550,6 +2563,14 @@
DiagnosticsProperty<CardThemeData>('cardTheme', cardTheme, level: DiagnosticLevel.debug),
);
properties.add(
+ DiagnosticsProperty<CarouselViewThemeData>(
+ 'carouselViewTheme',
+ carouselViewTheme,
+ defaultValue: defaultData.carouselViewTheme,
+ level: DiagnosticLevel.debug,
+ ),
+ );
+ properties.add(
DiagnosticsProperty<CheckboxThemeData>(
'checkboxTheme',
checkboxTheme,
diff --git a/packages/flutter/test/material/carousel_theme_test.dart b/packages/flutter/test/material/carousel_theme_test.dart
new file mode 100644
index 0000000..5c174e1
--- /dev/null
+++ b/packages/flutter/test/material/carousel_theme_test.dart
@@ -0,0 +1,242 @@
+// 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.
+
+import 'package:flutter/material.dart';
+import 'package:flutter/rendering.dart';
+import 'package:flutter_test/flutter_test.dart';
+
+void main() {
+ test('CarouselViewThemeData copyWith, ==, hashCode basics', () {
+ expect(const CarouselViewThemeData(), const CarouselViewThemeData().copyWith());
+ expect(
+ const CarouselViewThemeData().hashCode,
+ const CarouselViewThemeData().copyWith().hashCode,
+ );
+ });
+
+ test('CarouselViewThemeData null fields by default', () {
+ const CarouselViewThemeData carouselViewTheme = CarouselViewThemeData();
+ expect(carouselViewTheme.backgroundColor, null);
+ expect(carouselViewTheme.elevation, null);
+ expect(carouselViewTheme.overlayColor, null);
+ expect(carouselViewTheme.padding, null);
+ expect(carouselViewTheme.shape, null);
+ });
+
+ testWidgets('Default CarouselViewThemeData debugFillProperties', (WidgetTester tester) async {
+ final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
+ const CarouselViewThemeData().debugFillProperties(builder);
+
+ final List<String> description =
+ builder.properties
+ .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
+ .map((DiagnosticsNode node) => node.toString())
+ .toList();
+
+ expect(description, <String>[]);
+ });
+
+ testWidgets('CarouselViewThemeData implements debugFillProperties', (WidgetTester tester) async {
+ final DiagnosticPropertiesBuilder builder = DiagnosticPropertiesBuilder();
+ const CarouselViewThemeData(
+ backgroundColor: Color(0xFFFFFFFF),
+ elevation: 5.0,
+ padding: EdgeInsets.zero,
+ shape: RoundedRectangleBorder(),
+ overlayColor: MaterialStatePropertyAll<Color>(Colors.red),
+ ).debugFillProperties(builder);
+
+ final List<String> description =
+ builder.properties
+ .where((DiagnosticsNode node) => !node.isFiltered(DiagnosticLevel.info))
+ .map((DiagnosticsNode node) => node.toString())
+ .toList();
+
+ expect(description, <String>[
+ 'backgroundColor: ${const Color(0xffffffff)}',
+ 'elevation: 5.0',
+ 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)',
+ 'overlayColor: WidgetStatePropertyAll(${Colors.red})',
+ 'padding: EdgeInsets.zero',
+ ]);
+ });
+
+ testWidgets('Uses value from CarouselViewThemeData', (WidgetTester tester) async {
+ final CarouselViewThemeData carouselViewTheme = _carouselViewThemeData();
+ await tester.pumpWidget(
+ MaterialApp(
+ theme: ThemeData(carouselViewTheme: carouselViewTheme),
+ home: const Scaffold(
+ body: Center(
+ child: CarouselView(
+ itemExtent: 100,
+ children: <Widget>[SizedBox(width: 100, height: 100)],
+ ),
+ ),
+ ),
+ ),
+ );
+ expect(find.byType(CarouselView), findsOneWidget);
+
+ final Finder padding = find.descendant(
+ of: find.byType(CarouselView),
+ matching: find.byWidgetPredicate(
+ (Widget widget) => widget is Padding && widget.child is Material,
+ ),
+ );
+
+ expect(padding, findsOneWidget);
+ final Padding paddingWidget = tester.widget<Padding>(padding);
+ final Material material = paddingWidget.child! as Material;
+
+ final InkWell inkWell = tester.widget<InkWell>(
+ find.descendant(of: find.byType(CarouselView), matching: find.byType(InkWell)),
+ );
+
+ expect(paddingWidget.padding, carouselViewTheme.padding);
+ expect(material.color, carouselViewTheme.backgroundColor);
+ expect(material.elevation, carouselViewTheme.elevation);
+ expect(material.shape, carouselViewTheme.shape);
+ expect(material.borderRadius, null);
+ expect(material.clipBehavior, Clip.antiAlias);
+ expect(inkWell.overlayColor, carouselViewTheme.overlayColor);
+ });
+
+ testWidgets('Widgets properties override theme', (WidgetTester tester) async {
+ final CarouselViewThemeData carouselViewTheme = _carouselViewThemeData();
+ const Color backgroundColor = Color(0xFFFF0000);
+ const double elevation = 10.0;
+ const EdgeInsets padding = EdgeInsets.all(15.0);
+ const OutlinedBorder shape = RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.circular(10)),
+ );
+ const WidgetStateProperty<Color?> overlayColor = MaterialStatePropertyAll<Color>(Colors.green);
+ await tester.pumpWidget(
+ MaterialApp(
+ theme: ThemeData(carouselViewTheme: carouselViewTheme),
+ home: const Scaffold(
+ body: Center(
+ child: CarouselView(
+ backgroundColor: backgroundColor,
+ elevation: elevation,
+ padding: padding,
+ shape: shape,
+ overlayColor: overlayColor,
+ itemExtent: 100,
+ children: <Widget>[SizedBox(width: 100, height: 100)],
+ ),
+ ),
+ ),
+ ),
+ );
+ expect(find.byType(CarouselView), findsOneWidget);
+
+ final Finder paddingFinder = find.descendant(
+ of: find.byType(CarouselView),
+ matching: find.byWidgetPredicate(
+ (Widget widget) => widget is Padding && widget.child is Material,
+ ),
+ );
+
+ expect(paddingFinder, findsOneWidget);
+ final Padding paddingWidget = tester.widget<Padding>(paddingFinder);
+ final Material material = paddingWidget.child! as Material;
+
+ final InkWell inkWell = tester.widget<InkWell>(
+ find.descendant(of: find.byType(CarouselView), matching: find.byType(InkWell)),
+ );
+
+ expect(paddingWidget.padding, padding);
+ expect(material.color, backgroundColor);
+ expect(material.elevation, elevation);
+ expect(material.shape, shape);
+ expect(inkWell.overlayColor, overlayColor);
+ });
+
+ testWidgets('CarouselViewTheme can override Theme.carouselViewTheme', (
+ WidgetTester tester,
+ ) async {
+ const Color globalBackgroundColor = Color(0xfffffff1);
+ const Color globalOverlayColor = Color(0xff000000);
+ const double globalElevation = 5.0;
+ const EdgeInsets globalPadding = EdgeInsets.all(10.0);
+ const OutlinedBorder globalShape = RoundedRectangleBorder();
+
+ const Color localBackgroundColor = Color(0xffff0000);
+ const Color localOverlayColor = Color(0xffffffff);
+ const double localElevation = 10.0;
+ const EdgeInsets localPadding = EdgeInsets.all(15.0);
+ const OutlinedBorder localShape = RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.circular(10)),
+ );
+
+ await tester.pumpWidget(
+ MaterialApp(
+ theme: ThemeData(
+ carouselViewTheme: const CarouselViewThemeData(
+ backgroundColor: globalBackgroundColor,
+ overlayColor: MaterialStatePropertyAll<Color>(globalOverlayColor),
+ elevation: globalElevation,
+ padding: globalPadding,
+ shape: globalShape,
+ ),
+ ),
+ home: const Scaffold(
+ body: Center(
+ child: CarouselViewTheme(
+ data: CarouselViewThemeData(
+ backgroundColor: localBackgroundColor,
+ overlayColor: MaterialStatePropertyAll<Color>(localOverlayColor),
+ elevation: localElevation,
+ padding: localPadding,
+ shape: localShape,
+ ),
+ child: CarouselView(
+ itemExtent: 100,
+ children: <Widget>[SizedBox(width: 100, height: 100)],
+ ),
+ ),
+ ),
+ ),
+ ),
+ );
+
+ final Finder padding = find.descendant(
+ of: find.byType(CarouselView),
+ matching: find.byWidgetPredicate(
+ (Widget widget) => widget is Padding && widget.child is Material,
+ ),
+ );
+
+ expect(padding, findsOneWidget);
+ final Padding paddingWidget = tester.widget<Padding>(padding);
+ final Material material = paddingWidget.child! as Material;
+
+ final InkWell inkWell = tester.widget<InkWell>(
+ find.descendant(of: find.byType(CarouselView), matching: find.byType(InkWell)),
+ );
+
+ expect(paddingWidget.padding, localPadding);
+ expect(material.color, localBackgroundColor);
+ expect(material.elevation, localElevation);
+ expect(material.shape, localShape);
+ expect(inkWell.overlayColor?.resolve(<MaterialState>{}), localOverlayColor);
+ });
+}
+
+CarouselViewThemeData _carouselViewThemeData() {
+ const Color backgroundColor = Color(0xFF0000FF);
+ const double elevation = 5.0;
+ const EdgeInsets padding = EdgeInsets.all(10.0);
+ const OutlinedBorder shape = RoundedRectangleBorder();
+ const WidgetStateProperty<Color?> overlayColor = MaterialStatePropertyAll<Color>(Colors.red);
+
+ return const CarouselViewThemeData(
+ backgroundColor: backgroundColor,
+ elevation: elevation,
+ padding: padding,
+ shape: shape,
+ overlayColor: overlayColor,
+ );
+}
diff --git a/packages/flutter/test/material/theme_data_test.dart b/packages/flutter/test/material/theme_data_test.dart
index 5de641c..328a7c0 100644
--- a/packages/flutter/test/material/theme_data_test.dart
+++ b/packages/flutter/test/material/theme_data_test.dart
@@ -1339,6 +1339,7 @@
bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Colors.black),
buttonTheme: const ButtonThemeData(colorScheme: ColorScheme.dark()),
cardTheme: const CardThemeData(color: Colors.black),
+ carouselViewTheme: const CarouselViewThemeData(),
checkboxTheme: const CheckboxThemeData(),
chipTheme: chipTheme,
dataTableTheme: const DataTableThemeData(),
@@ -1467,6 +1468,7 @@
bottomSheetTheme: const BottomSheetThemeData(backgroundColor: Colors.white),
buttonTheme: const ButtonThemeData(colorScheme: ColorScheme.light()),
cardTheme: const CardThemeData(color: Colors.white),
+ carouselViewTheme: const CarouselViewThemeData(),
checkboxTheme: const CheckboxThemeData(),
chipTheme: otherChipTheme,
dataTableTheme: const DataTableThemeData(),
@@ -1807,6 +1809,7 @@
'bottomSheetTheme',
'buttonTheme',
'cardTheme',
+ 'carouselViewTheme',
'checkboxTheme',
'chipTheme',
'dataTableTheme',