Unverified Commit 219ff645 authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Reland "Update ExpansionTile to support Material 3 & add an example" (#121212)

parent cab761d1
......@@ -31,6 +31,7 @@ import 'package:gen_defaults/date_picker_template.dart';
import 'package:gen_defaults/dialog_template.dart';
import 'package:gen_defaults/divider_template.dart';
import 'package:gen_defaults/drawer_template.dart';
import 'package:gen_defaults/expansion_tile_template.dart';
import 'package:gen_defaults/fab_template.dart';
import 'package:gen_defaults/filter_chip_template.dart';
import 'package:gen_defaults/icon_button_template.dart';
......@@ -153,6 +154,7 @@ Future<void> main(List<String> args) async {
DialogTemplate('Dialog', '$materialLib/dialog.dart', tokens).updateFile();
DividerTemplate('Divider', '$materialLib/divider.dart', tokens).updateFile();
DrawerTemplate('Drawer', '$materialLib/drawer.dart', tokens).updateFile();
ExpansionTileTemplate('ExpansionTile', '$materialLib/expansion_tile.dart', tokens).updateFile();
FABTemplate('FAB', '$materialLib/floating_action_button.dart', tokens).updateFile();
FilterChipTemplate('ChoiceChip', '$materialLib/choice_chip.dart', tokens).updateFile();
FilterChipTemplate('FilterChip', '$materialLib/filter_chip.dart', tokens).updateFile();
......
// 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 'template.dart';
class ExpansionTileTemplate extends TokenTemplate {
const ExpansionTileTemplate(super.blockName, super.fileName, super.tokens, {
super.colorSchemePrefix = '_colors.',
});
@override
String generate() => '''
class _${blockName}DefaultsM3 extends ExpansionTileThemeData {
_${blockName}DefaultsM3(this.context);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
@override
Color? get textColor => ${componentColor('md.comp.list.list-item.label-text')};
@override
Color? get iconColor => ${componentColor('md.comp.list.list-item.selected.trailing-icon')};
@override
Color? get collapsedTextColor => ${componentColor('md.comp.list.list-item.label-text')};
@override
Color? get collapsedIconColor => ${componentColor('md.comp.list.list-item.trailing-icon')};
}
''';
}
......@@ -6,33 +6,31 @@
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
void main() => runApp(const ExpansionTileApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
class ExpansionTileApp extends StatelessWidget {
const ExpansionTileApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
theme: ThemeData(useMaterial3: true),
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
appBar: AppBar(title: const Text('ExpansionTile Sample')),
body: const ExpansionTileExample(),
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
class ExpansionTileExample extends StatefulWidget {
const ExpansionTileExample({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
State<ExpansionTileExample> createState() => _ExpansionTileExampleState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
class _ExpansionTileExampleState extends State<ExpansionTileExample> {
bool _customTileExpanded = false;
@override
......@@ -51,14 +49,16 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
subtitle: const Text('Custom expansion arrow icon'),
trailing: Icon(
_customTileExpanded
? Icons.arrow_drop_down_circle
: Icons.arrow_drop_down,
? Icons.arrow_drop_down_circle
: Icons.arrow_drop_down,
),
children: const <Widget>[
ListTile(title: Text('This is tile number 2')),
],
onExpansionChanged: (bool expanded) {
setState(() => _customTileExpanded = expanded);
setState(() {
_customTileExpanded = expanded;
});
},
),
const ExpansionTile(
......
// 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_api_samples/material/expansion_tile/expansion_tile.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('When expansion tiles are expanded tile numbers are revealed', (WidgetTester tester) async {
const int totalTiles = 3;
await tester.pumpWidget(
const example.ExpansionTileApp(),
);
expect(find.byType(ExpansionTile), findsNWidgets(totalTiles));
const String tileOne = 'This is tile number 1';
expect(find.text(tileOne), findsNothing);
await tester.tap(find.text('ExpansionTile 1'));
await tester.pumpAndSettle();
expect(find.text(tileOne), findsOneWidget);
const String tileTwo = 'This is tile number 2';
expect(find.text(tileTwo), findsNothing);
await tester.tap(find.text('ExpansionTile 2'));
await tester.pumpAndSettle();
expect(find.text(tileTwo), findsOneWidget);
const String tileThree = 'This is tile number 3';
expect(find.text(tileThree), findsNothing);
await tester.tap(find.text('ExpansionTile 3'));
await tester.pumpAndSettle();
expect(find.text(tileThree), findsOneWidget);
});
}
......@@ -34,7 +34,8 @@ const Duration _kExpand = Duration(milliseconds: 200);
/// to the [leading] and [trailing] properties of [ExpansionTile].
///
/// {@tool dartpad}
/// This example demonstrates different configurations of ExpansionTile.
/// This example demonstrates how the [ExpansionTile] icon's location and appearance
/// can be customized.
///
/// ** See code in examples/api/lib/material/expansion_tile/expansion_tile.0.dart **
/// {@end-tool}
......@@ -218,7 +219,7 @@ class ExpansionTile extends StatefulWidget {
/// Used to override to the [ListTileThemeData.iconColor].
///
/// If this property is null then [ExpansionTileThemeData.iconColor] is used. If that
/// is also null then the value of [ListTileThemeData.iconColor] is used.
/// is also null then the value of [ColorScheme.primary] is used.
///
/// See also:
///
......@@ -229,6 +230,15 @@ class ExpansionTile extends StatefulWidget {
/// The icon color of tile's expansion arrow icon when the sublist is collapsed.
///
/// Used to override to the [ListTileThemeData.iconColor].
///
/// If this property is null then [ExpansionTileThemeData.collapsedIconColor] is used. If that
/// is also null and [ThemeData.useMaterial3] is true, [ColorScheme.onSurface] is used. Otherwise,
/// defaults to [ThemeData.unselectedWidgetColor] color.
///
/// See also:
///
/// * [ExpansionTileTheme.of], which returns the nearest [ExpansionTileTheme]'s
/// [ExpansionTileThemeData].
final Color? collapsedIconColor;
......@@ -237,7 +247,8 @@ class ExpansionTile extends StatefulWidget {
/// Used to override to the [ListTileThemeData.textColor].
///
/// If this property is null then [ExpansionTileThemeData.textColor] is used. If that
/// is also null then the value of [ListTileThemeData.textColor] is used.
/// is also null then and [ThemeData.useMaterial3] is true, color of the [TextTheme.bodyLarge]
/// will be used for the [title] and [subtitle]. Otherwise, defaults to [ColorScheme.primary] color.
///
/// See also:
///
......@@ -249,8 +260,10 @@ class ExpansionTile extends StatefulWidget {
///
/// Used to override to the [ListTileThemeData.textColor].
///
/// If this property is null then [ExpansionTileThemeData.collapsedTextColor] is used. If that
/// is also null then the value of [ListTileThemeData.textColor] is used.
/// If this property is null then [ExpansionTileThemeData.collapsedTextColor] is used.
/// If that is also null and [ThemeData.useMaterial3] is true, color of the
/// [TextTheme.bodyLarge] will be used for the [title] and [subtitle]. Otherwise,
/// defaults to color of the [TextTheme.titleMedium].
///
/// See also:
///
......@@ -443,7 +456,9 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
void didChangeDependencies() {
final ThemeData theme = Theme.of(context);
final ExpansionTileThemeData expansionTileTheme = ExpansionTileTheme.of(context);
final ColorScheme colorScheme = theme.colorScheme;
final ExpansionTileThemeData defaults = theme.useMaterial3
? _ExpansionTileDefaultsM3(context)
: _ExpansionTileDefaultsM2(context);
_borderTween
..begin = widget.collapsedShape
?? expansionTileTheme.collapsedShape
......@@ -460,13 +475,13 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
_headerColorTween
..begin = widget.collapsedTextColor
?? expansionTileTheme.collapsedTextColor
?? theme.textTheme.titleMedium!.color
..end = widget.textColor ?? expansionTileTheme.textColor ?? colorScheme.primary;
?? defaults.collapsedTextColor
..end = widget.textColor ?? expansionTileTheme.textColor ?? defaults.textColor;
_iconColorTween
..begin = widget.collapsedIconColor
?? expansionTileTheme.collapsedIconColor
?? theme.unselectedWidgetColor
..end = widget.iconColor ?? expansionTileTheme.iconColor ?? colorScheme.primary;
?? defaults.collapsedIconColor
..end = widget.iconColor ?? expansionTileTheme.iconColor ?? defaults.iconColor;
_backgroundColorTween
..begin = widget.collapsedBackgroundColor ?? expansionTileTheme.collapsedBackgroundColor
..end = widget.backgroundColor ?? expansionTileTheme.backgroundColor;
......@@ -500,3 +515,54 @@ class _ExpansionTileState extends State<ExpansionTile> with SingleTickerProvider
);
}
}
class _ExpansionTileDefaultsM2 extends ExpansionTileThemeData {
_ExpansionTileDefaultsM2(this.context);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colorScheme = _theme.colorScheme;
@override
Color? get textColor => _colorScheme.primary;
@override
Color? get iconColor => _colorScheme.primary;
@override
Color? get collapsedTextColor => _theme.textTheme.titleMedium!.color;
@override
Color? get collapsedIconColor => _theme.unselectedWidgetColor;
}
// BEGIN GENERATED TOKEN PROPERTIES - ExpansionTile
// Do not edit by hand. The code between the "BEGIN GENERATED" and
// "END GENERATED" comments are generated from data in the Material
// Design token database by the script:
// dev/tools/gen_defaults/bin/gen_defaults.dart.
// Token database version: v0_158
class _ExpansionTileDefaultsM3 extends ExpansionTileThemeData {
_ExpansionTileDefaultsM3(this.context);
final BuildContext context;
late final ThemeData _theme = Theme.of(context);
late final ColorScheme _colors = _theme.colorScheme;
@override
Color? get textColor => _colors.onSurface;
@override
Color? get iconColor => _colors.primary;
@override
Color? get collapsedTextColor => _colors.onSurface;
@override
Color? get collapsedIconColor => _colors.onSurfaceVariant;
}
// END GENERATED TOKEN PROPERTIES - ExpansionTile
......@@ -522,6 +522,35 @@ void main() {
expect(shapeDecoration.color, backgroundColor);
});
testWidgets('ExpansionTile default iconColor, textColor', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: true);
await tester.pumpWidget(MaterialApp(
theme: theme,
home: const Material(
child: ExpansionTile(
title: TestText('title'),
trailing: TestIcon(),
children: <Widget>[
SizedBox(height: 100, width: 100),
],
),
),
));
Color getIconColor() => tester.state<TestIconState>(find.byType(TestIcon)).iconTheme.color!;
Color getTextColor() => tester.state<TestTextState>(find.byType(TestText)).textStyle.color!;
expect(getIconColor(), theme.colorScheme.onSurfaceVariant);
expect(getTextColor(), theme.colorScheme.onSurface);
await tester.tap(find.text('title'));
await tester.pumpAndSettle();
expect(getIconColor(), theme.colorScheme.primary);
expect(getTextColor(), theme.colorScheme.onSurface);
});
testWidgets('ExpansionTile iconColor, textColor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/78281
......@@ -666,4 +695,38 @@ void main() {
expect(listTile.leading.runtimeType, Icon);
expect(listTile.trailing, isNull);
});
group('Material 2', () {
// Tests that are only relevant for Material 2. Once ThemeData.useMaterial3
// is turned on by default, these tests can be removed.
testWidgets('ExpansionTile default iconColor, textColor', (WidgetTester tester) async {
final ThemeData theme = ThemeData(useMaterial3: false);
await tester.pumpWidget(MaterialApp(
theme: theme,
home: const Material(
child: ExpansionTile(
title: TestText('title'),
trailing: TestIcon(),
children: <Widget>[
SizedBox(height: 100, width: 100),
],
),
),
));
Color getIconColor() => tester.state<TestIconState>(find.byType(TestIcon)).iconTheme.color!;
Color getTextColor() => tester.state<TestTextState>(find.byType(TestText)).textStyle.color!;
expect(getIconColor(), theme.unselectedWidgetColor);
expect(getTextColor(), theme.textTheme.titleMedium!.color);
await tester.tap(find.text('title'));
await tester.pumpAndSettle();
expect(getIconColor(), theme.colorScheme.primary);
expect(getTextColor(), theme.colorScheme.primary);
});
});
}
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