Skip to content
This repository has been archived by the owner on Feb 22, 2023. It is now read-only.

Commit

Permalink
Update ExpansionTile to support Material 3 & add an example (#119712)
Browse files Browse the repository at this point in the history
  • Loading branch information
TahaTesser committed Feb 7, 2023
1 parent da36bd6 commit e8eac0d
Show file tree
Hide file tree
Showing 6 changed files with 228 additions and 23 deletions.
2 changes: 2 additions & 0 deletions dev/tools/gen_defaults/bin/gen_defaults.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -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();
Expand Down
34 changes: 34 additions & 0 deletions dev/tools/gen_defaults/lib/expansion_tile_template.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// 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 => ${textStyle("md.comp.list.list-item.label-text")}!.color;
@override
Color? get iconColor => ${componentColor('md.comp.list.list-item.selected.trailing-icon')};
@override
Color? get collapsedTextColor => ${textStyle("md.comp.list.list-item.label-text")}!.color;
@override
Color? get collapsedIconColor => ${componentColor('md.comp.list.list-item.unselected.trailing-icon')};
}
''';
}
26 changes: 13 additions & 13 deletions examples/api/lib/material/expansion_tile/expansion_tile.0.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -58,7 +56,9 @@ class _MyStatefulWidgetState extends State<MyStatefulWidget> {
ListTile(title: Text('This is tile number 2')),
],
onExpansionChanged: (bool expanded) {
setState(() => _customTileExpanded = expanded);
setState(() {
_customTileExpanded = expanded;
});
},
),
const ExpansionTile(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// 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);
});
}
86 changes: 76 additions & 10 deletions packages/flutter/lib/src/material/expansion_tile.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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}
Expand Down Expand Up @@ -216,7 +217,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:
///
Expand All @@ -227,6 +228,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;


Expand All @@ -235,7 +245,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:
///
Expand All @@ -247,8 +258,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:
///
Expand Down Expand Up @@ -441,7 +454,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
Expand All @@ -458,13 +473,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;
Expand Down Expand Up @@ -498,3 +513,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_152

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 => Theme.of(context).textTheme.bodyLarge!.color;

@override
Color? get iconColor => _colors.primary;

@override
Color? get collapsedTextColor => Theme.of(context).textTheme.bodyLarge!.color;

@override
Color? get collapsedIconColor => _colors.onSurface;
}

// END GENERATED TOKEN PROPERTIES - ExpansionTile
63 changes: 63 additions & 0 deletions packages/flutter/test/material/expansion_tile_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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.onSurface);
expect(getTextColor(), theme.textTheme.bodyLarge!.color);

await tester.tap(find.text('title'));
await tester.pumpAndSettle();

expect(getIconColor(), theme.colorScheme.primary);
expect(getTextColor(), theme.textTheme.bodyLarge!.color);
});

testWidgets('ExpansionTile iconColor, textColor', (WidgetTester tester) async {
// Regression test for https://github.com/flutter/flutter/pull/78281

Expand Down Expand Up @@ -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);
});
});
}

0 comments on commit e8eac0d

Please sign in to comment.