Unverified Commit 3e71e0ca authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Updated `ListTile` documentation, add Material 3 example and other `ListTile`...

Updated `ListTile` documentation, add Material 3 example and other `ListTile` examples fixes. (#118705)
parent d53cc4a1
...@@ -2,25 +2,19 @@ ...@@ -2,25 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flutter code sample for [ListTile]. // Flutter code sample for custom list items.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
void main() => runApp(const MyApp()); void main() => runApp(const CustomListItemApp());
class MyApp extends StatelessWidget { class CustomListItemApp extends StatelessWidget {
const MyApp({super.key}); const CustomListItemApp({super.key});
static const String _title = 'Flutter Code Sample';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return const MaterialApp(
title: _title, home: CustomListItemExample(),
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatelessWidget(),
),
); );
} }
} }
...@@ -109,32 +103,35 @@ class _VideoDescription extends StatelessWidget { ...@@ -109,32 +103,35 @@ class _VideoDescription extends StatelessWidget {
} }
} }
class MyStatelessWidget extends StatelessWidget { class CustomListItemExample extends StatelessWidget {
const MyStatelessWidget({super.key}); const CustomListItemExample({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return Scaffold(
padding: const EdgeInsets.all(8.0), appBar: AppBar(title: const Text('Custom List Item Sample')),
itemExtent: 106.0, body: ListView(
children: <CustomListItem>[ padding: const EdgeInsets.all(8.0),
CustomListItem( itemExtent: 106.0,
user: 'Flutter', children: <CustomListItem>[
viewCount: 999000, CustomListItem(
thumbnail: Container( user: 'Flutter',
decoration: const BoxDecoration(color: Colors.blue), viewCount: 999000,
thumbnail: Container(
decoration: const BoxDecoration(color: Colors.blue),
),
title: 'The Flutter YouTube Channel',
), ),
title: 'The Flutter YouTube Channel', CustomListItem(
), user: 'Dash',
CustomListItem( viewCount: 884000,
user: 'Dash', thumbnail: Container(
viewCount: 884000, decoration: const BoxDecoration(color: Colors.yellow),
thumbnail: Container( ),
decoration: const BoxDecoration(color: Colors.yellow), title: 'Announcing Flutter 1.0',
), ),
title: 'Announcing Flutter 1.0', ],
), ),
],
); );
} }
} }
...@@ -2,25 +2,19 @@ ...@@ -2,25 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
// Flutter code sample for [ListTile]. // Flutter code sample for custom list items.
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
void main() => runApp(const MyApp()); void main() => runApp(const CustomListItemApp());
class MyApp extends StatelessWidget { class CustomListItemApp extends StatelessWidget {
const MyApp({super.key}); const CustomListItemApp({super.key});
static const String _title = 'Flutter Code Sample';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return const MaterialApp(
title: _title, home: CustomListItemExample(),
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatelessWidget(),
),
); );
} }
} }
...@@ -147,36 +141,39 @@ class CustomListItemTwo extends StatelessWidget { ...@@ -147,36 +141,39 @@ class CustomListItemTwo extends StatelessWidget {
} }
} }
class MyStatelessWidget extends StatelessWidget { class CustomListItemExample extends StatelessWidget {
const MyStatelessWidget({super.key}); const CustomListItemExample({super.key});
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return Scaffold(
padding: const EdgeInsets.all(10.0), appBar: AppBar(title: const Text('Custom List Item Sample')),
children: <Widget>[ body: ListView(
CustomListItemTwo( padding: const EdgeInsets.all(10.0),
thumbnail: Container( children: <Widget>[
decoration: const BoxDecoration(color: Colors.pink), CustomListItemTwo(
thumbnail: Container(
decoration: const BoxDecoration(color: Colors.pink),
),
title: 'Flutter 1.0 Launch',
subtitle: 'Flutter continues to improve and expand its horizons. '
'This text should max out at two lines and clip',
author: 'Dash',
publishDate: 'Dec 28',
readDuration: '5 mins',
), ),
title: 'Flutter 1.0 Launch', CustomListItemTwo(
subtitle: 'Flutter continues to improve and expand its horizons. ' thumbnail: Container(
'This text should max out at two lines and clip', decoration: const BoxDecoration(color: Colors.blue),
author: 'Dash', ),
publishDate: 'Dec 28', title: 'Flutter 1.2 Release - Continual updates to the framework',
readDuration: '5 mins', subtitle: 'Flutter once again improves and makes updates.',
), author: 'Flutter',
CustomListItemTwo( publishDate: 'Feb 26',
thumbnail: Container( readDuration: '12 mins',
decoration: const BoxDecoration(color: Colors.blue),
), ),
title: 'Flutter 1.2 Release - Continual updates to the framework', ],
subtitle: 'Flutter once again improves and makes updates.', ),
author: 'Flutter',
publishDate: 'Feb 26',
readDuration: '12 mins',
),
],
); );
} }
} }
...@@ -19,10 +19,7 @@ class ListTileApp extends StatelessWidget { ...@@ -19,10 +19,7 @@ class ListTileApp extends StatelessWidget {
textColor: Colors.white, textColor: Colors.white,
) )
), ),
home: Scaffold( home: const LisTileExample(),
appBar: AppBar(title: const Text('ListTile Samples')),
body: const LisTileExample(),
),
); );
} }
} }
...@@ -73,77 +70,80 @@ class _LisTileExampleState extends State<LisTileExample> with TickerProviderStat ...@@ -73,77 +70,80 @@ class _LisTileExampleState extends State<LisTileExample> with TickerProviderStat
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Column( return Scaffold(
mainAxisAlignment: MainAxisAlignment.spaceEvenly, appBar: AppBar(title: const Text('ListTile Samples')),
children: <Widget>[ body: Column(
Hero( mainAxisAlignment: MainAxisAlignment.spaceEvenly,
tag: 'ListTile-Hero', children: <Widget>[
// Wrap the ListTile in a Material widget so the ListTile has someplace Hero(
// to draw the animated colors during the hero transition. tag: 'ListTile-Hero',
child: Material( // Wrap the ListTile in a Material widget so the ListTile has someplace
child: ListTile( // to draw the animated colors during the hero transition.
title: const Text('ListTile with Hero'), child: Material(
subtitle: const Text('Tap here for Hero transition'), child: ListTile(
tileColor: Colors.cyan, title: const Text('ListTile with Hero'),
onTap: () { subtitle: const Text('Tap here for Hero transition'),
Navigator.push(context, MaterialPageRoute<Widget>( tileColor: Colors.cyan,
builder: (BuildContext context) { onTap: () {
return Scaffold( Navigator.push(context, MaterialPageRoute<Widget>(
appBar: AppBar(title: const Text('ListTile Hero')), builder: (BuildContext context) {
body: Center( return Scaffold(
child: Hero( appBar: AppBar(title: const Text('ListTile Hero')),
tag: 'ListTile-Hero', body: Center(
child: Material( child: Hero(
child: ListTile( tag: 'ListTile-Hero',
title: const Text('ListTile with Hero'), child: Material(
subtitle: const Text('Tap here to go back'), child: ListTile(
tileColor: Colors.blue[700], title: const Text('ListTile with Hero'),
onTap: () { subtitle: const Text('Tap here to go back'),
Navigator.pop(context); tileColor: Colors.blue[700],
}, onTap: () {
Navigator.pop(context);
},
),
), ),
), ),
), ),
), );
); }),
}), );
); },
}, ),
), ),
), ),
), FadeTransition(
FadeTransition( opacity: _fadeAnimation,
opacity: _fadeAnimation, // Wrap the ListTile in a Material widget so the ListTile has someplace
// Wrap the ListTile in a Material widget so the ListTile has someplace // to draw the animated colors during the fade transition.
// to draw the animated colors during the fade transition. child: const Material(
child: const Material( child: ListTile(
child: ListTile( title: Text('ListTile with FadeTransition'),
title: Text('ListTile with FadeTransition'), selectedTileColor: Colors.green,
selectedTileColor: Colors.green, selectedColor: Colors.white,
selectedColor: Colors.white, selected: true,
selected: true, ),
), ),
), ),
), SizedBox(
SizedBox( height: 100,
height: 100, child: Center(
child: Center( child: SizeTransition(
child: SizeTransition( sizeFactor: _sizeAnimation,
sizeFactor: _sizeAnimation, axisAlignment: -1.0,
axisAlignment: -1.0, // Wrap the ListTile in a Material widget so the ListTile has someplace
// Wrap the ListTile in a Material widget so the ListTile has someplace // to draw the animated colors during the size transition.
// to draw the animated colors during the size transition. child: const Material(
child: const Material( child: ListTile(
child: ListTile( title: Text('ListTile with SizeTransition'),
title: Text('ListTile with SizeTransition'), tileColor: Colors.red,
tileColor: Colors.red, minVerticalPadding: 25.0,
minVerticalPadding: 25.0, ),
), ),
), ),
), ),
), ),
), ],
], ),
); );
} }
} }
...@@ -13,11 +13,8 @@ class ListTileApp extends StatelessWidget { ...@@ -13,11 +13,8 @@ class ListTileApp extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return const MaterialApp(
home: Scaffold( home: LisTileExample(),
appBar: AppBar(title: const Text('ListTile Sample')),
body: const LisTileExample(),
),
); );
} }
} }
...@@ -27,54 +24,57 @@ class LisTileExample extends StatelessWidget { ...@@ -27,54 +24,57 @@ class LisTileExample extends StatelessWidget {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView( return Scaffold(
children: const <Widget>[ appBar: AppBar(title: const Text('ListTile Sample')),
Card(child: ListTile(title: Text('One-line ListTile'))), body: ListView(
Card( children: const <Widget>[
child: ListTile( Card(child: ListTile(title: Text('One-line ListTile'))),
leading: FlutterLogo(), Card(
title: Text('One-line with leading widget'), child: ListTile(
leading: FlutterLogo(),
title: Text('One-line with leading widget'),
),
), ),
), Card(
Card( child: ListTile(
child: ListTile( title: Text('One-line with trailing widget'),
title: Text('One-line with trailing widget'), trailing: Icon(Icons.more_vert),
trailing: Icon(Icons.more_vert), ),
), ),
), Card(
Card( child: ListTile(
child: ListTile( leading: FlutterLogo(),
leading: FlutterLogo(), title: Text('One-line with both widgets'),
title: Text('One-line with both widgets'), trailing: Icon(Icons.more_vert),
trailing: Icon(Icons.more_vert), ),
), ),
), Card(
Card( child: ListTile(
child: ListTile( title: Text('One-line dense ListTile'),
title: Text('One-line dense ListTile'), dense: true,
dense: true, ),
), ),
), Card(
Card( child: ListTile(
child: ListTile( leading: FlutterLogo(size: 56.0),
leading: FlutterLogo(size: 56.0), title: Text('Two-line ListTile'),
title: Text('Two-line ListTile'), subtitle: Text('Here is a second line'),
subtitle: Text('Here is a second line'), trailing: Icon(Icons.more_vert),
trailing: Icon(Icons.more_vert), ),
), ),
), Card(
Card( child: ListTile(
child: ListTile( leading: FlutterLogo(size: 72.0),
leading: FlutterLogo(size: 72.0), title: Text('Three-line ListTile'),
title: Text('Three-line ListTile'), subtitle: Text(
subtitle: Text( 'A sufficiently long subtitle warrants three lines.'
'A sufficiently long subtitle warrants three lines.' ),
trailing: Icon(Icons.more_vert),
isThreeLine: true,
), ),
trailing: Icon(Icons.more_vert),
isThreeLine: true,
), ),
), ],
], ),
); );
} }
} }
// 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.
// Flutter code sample for [ListTile].
import 'package:flutter/material.dart';
void main() => runApp(const ListTileApp());
class ListTileApp extends StatelessWidget {
const ListTileApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
home: const ListTileExample(),
);
}
}
class ListTileExample extends StatelessWidget {
const ListTileExample({super.key});
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ListTile Sample')),
body: Column(
children: const <Widget>[
ListTile(
leading: CircleAvatar(
child: Text('A')
),
title: Text('Headline'),
subtitle: Text('Supporting text'),
trailing: Icon(Icons.favorite_rounded),
),
Divider(),
ListTile(
leading: CircleAvatar(
child: Text('B')
),
title: Text('Headline'),
subtitle: Text('Longer supporting text to demonstrate how the text wraps and how the leading and trailing widgets are centered vertically with the text.'),
trailing: Icon(Icons.favorite_rounded),
),
Divider(),
ListTile(
leading: CircleAvatar(
child: Text('C')
),
title: Text('Headline'),
subtitle: Text("Longer supporting text to demonstrate how the text wraps and how setting 'ListTile.isThreeLine = true' aligns leading and trailing widgets to the top vertically with the text."),
trailing: Icon(Icons.favorite_rounded),
isThreeLine: true,
),
Divider(),
],
),
);
}
}
// 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.
// Flutter code sample for [ListTile].
import 'package:flutter/material.dart';
void main() => runApp(const ListTileApp());
class ListTileApp extends StatelessWidget {
const ListTileApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
home: const ListTileExample(),
);
}
}
class ListTileExample extends StatefulWidget {
const ListTileExample({super.key});
@override
State<ListTileExample> createState() => _ListTileExampleState();
}
class _ListTileExampleState extends State<ListTileExample> {
bool _selected = false;
bool _enabled = true;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ListTile Sample')),
body: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
ListTile(
enabled: _enabled,
selected: _selected,
onTap: () {
setState(() {
// This is called when the user toggles the switch.
_selected = !_selected;
});
},
// This sets text color and icon color to red when list tile is disabled and
// green when list tile is selected, otherwise sets it to black.
iconColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return Colors.red;
}
if (states.contains(MaterialState.selected)) {
return Colors.green;
}
return Colors.black;
}),
// This sets text color and icon color to red when list tile is disabled and
// green when list tile is selected, otherwise sets it to black.
textColor: MaterialStateColor.resolveWith((Set<MaterialState> states) {
if (states.contains(MaterialState.disabled)) {
return Colors.red;
}
if (states.contains(MaterialState.selected)) {
return Colors.green;
}
return Colors.black;
}),
leading: const Icon(Icons.person),
title: const Text('Headline'),
subtitle: Text('Enabled: $_enabled, Selected: $_selected'),
trailing: Switch(
onChanged: (bool? value) {
// This is called when the user toggles the switch.
setState(() {
_enabled = value!;
});
},
value: _enabled,
),
),
],
),
);
}
}
...@@ -6,50 +6,46 @@ ...@@ -6,50 +6,46 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
void main() => runApp(const MyApp()); void main() => runApp(const ListTileApp());
class MyApp extends StatelessWidget { class ListTileApp extends StatelessWidget {
const MyApp({super.key}); const ListTileApp({super.key});
static const String _title = 'Flutter Code Sample';
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return const MaterialApp(
title: _title, home: LisTileExample(),
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const MyStatefulWidget(),
),
); );
} }
} }
class LisTileExample extends StatefulWidget {
class MyStatefulWidget extends StatefulWidget { const LisTileExample({super.key});
const MyStatefulWidget({super.key});
@override @override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState(); State<LisTileExample> createState() => _LisTileExampleState();
} }
class _MyStatefulWidgetState extends State<MyStatefulWidget> { class _LisTileExampleState extends State<LisTileExample> {
int _selectedIndex = 0; int _selectedIndex = 0;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return ListView.builder( return Scaffold(
itemCount: 10, appBar: AppBar(title: const Text('Custom List Item Sample')),
itemBuilder: (BuildContext context, int index) { body: ListView.builder(
return ListTile( itemCount: 10,
title: Text('Item $index'), itemBuilder: (BuildContext context, int index) {
selected: index == _selectedIndex, return ListTile(
onTap: () { title: Text('Item $index'),
setState(() { selected: index == _selectedIndex,
_selectedIndex = index; onTap: () {
}); setState(() {
}, _selectedIndex = index;
); });
}, },
);
},
),
); );
} }
} }
// 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/list_tile/custom_list_item.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Custom list item uses Expanded widgets for the layout', (WidgetTester tester) async {
await tester.pumpWidget(
const example.CustomListItemApp(),
);
// The Expanded widget is used to control the size of the thumbnail.
Expanded thumbnailExpanded = tester.widget(find.ancestor(
of: find.byType(Container).first,
matching: find.byType(Expanded),
));
expect(thumbnailExpanded.flex, 2);
// The Expanded widget is used to control the size of the text.
Expanded textExpanded = tester.widget(find.ancestor(
of: find.text('The Flutter YouTube Channel'),
matching: find.byType(Expanded),
));
expect(textExpanded.flex, 3);
// The Expanded widget is used to control the size of the thumbnail.
thumbnailExpanded = tester.widget(find.ancestor(
of: find.byType(Container).last,
matching: find.byType(Expanded),
));
expect(thumbnailExpanded.flex, 2);
// The Expanded widget is used to control the size of the text.
textExpanded = tester.widget(find.ancestor(
of: find.text('Announcing Flutter 1.0'),
matching: find.byType(Expanded),
));
expect(textExpanded.flex, 3);
});
}
// 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/list_tile/custom_list_item.1.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Custom list item uses AspectRatio and Expanded widgets for the layout', (WidgetTester tester) async {
await tester.pumpWidget(
const example.CustomListItemApp(),
);
// The AspectRatio widget is used to constrain the size of the thumbnail.
AspectRatio thumbnailAspectRatio = tester.widget(find.ancestor(
of: find.byType(Container).first,
matching: find.byType(AspectRatio),
));
expect(thumbnailAspectRatio.aspectRatio, 1.0);
// The Expanded widget is used to control the size of the text.
Expanded textExpanded = tester.widget(find.ancestor(
of: find.text('Flutter 1.0 Launch'),
matching: find.byType(Expanded).at(0),
));
expect(textExpanded.flex, 1);
// The AspectRatio widget is used to constrain the size of the thumbnail.
thumbnailAspectRatio = tester.widget(find.ancestor(
of: find.byType(Container).last,
matching: find.byType(AspectRatio),
));
expect(thumbnailAspectRatio.aspectRatio, 1.0);
// The Expanded widget is used to control the size of the text.
textExpanded = tester.widget(find.ancestor(
of: find.text('Flutter 1.2 Release - Continual updates to the framework'),
matching: find.byType(Expanded).at(3),
));
expect(textExpanded.flex, 1);
});
}
...@@ -21,8 +21,12 @@ void main() { ...@@ -21,8 +21,12 @@ void main() {
expect(find.text(heroTransitionText), findsOneWidget); expect(find.text(heroTransitionText), findsOneWidget);
expect(find.text(goBackText), findsNothing); expect(find.text(goBackText), findsNothing);
// Tap on the ListTile widget to trigger the Hero transition.
await tester.tap(find.text(heroTransitionText)); await tester.tap(find.text(heroTransitionText));
await tester.pumpAndSettle(); await tester.pumpAndSettle();
// The Hero transition is triggered and tap to go back text is displayed.
expect(find.text(heroTransitionText), findsNothing); expect(find.text(heroTransitionText), findsNothing);
expect(find.text(goBackText), findsOneWidget); expect(find.text(goBackText), findsOneWidget);
......
...@@ -16,6 +16,7 @@ void main() { ...@@ -16,6 +16,7 @@ void main() {
expect(find.byType(ListTile), findsNWidgets(totalTiles)); expect(find.byType(ListTile), findsNWidgets(totalTiles));
// The ListTile widget is wrapped in a Card widget.
for (int i = 0; i < totalTiles; i++) { for (int i = 0; i < totalTiles; i++) {
expect( expect(
find.ancestor( find.ancestor(
......
// 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/list_tile/list_tile.2.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('ListTile leading and trailing widgets are aligned appropriately', (WidgetTester tester) async {
await tester.pumpWidget(
const example.ListTileApp(),
);
expect(find.byType(ListTile), findsNWidgets(3));
Offset listTileTopLeft = tester.getTopLeft(find.byType(ListTile).at(0));
Offset leadingTopLeft = tester.getTopLeft(find.byType(CircleAvatar).at(0));
Offset trailingTopLeft = tester.getTopLeft(find.byType(Icon).at(0));
// The leading and trailing widgets are centered vertically with the text.
expect(leadingTopLeft - listTileTopLeft, const Offset(16.0, 16.0));
expect(trailingTopLeft - listTileTopLeft, const Offset(752.0, 24.0));
listTileTopLeft = tester.getTopLeft(find.byType(ListTile).at(1));
leadingTopLeft = tester.getTopLeft(find.byType(CircleAvatar).at(1));
trailingTopLeft = tester.getTopLeft(find.byType(Icon).at(1));
// The leading and trailing widgets are centered vertically with the text.
expect(leadingTopLeft - listTileTopLeft, const Offset(16.0, 30.0));
expect(trailingTopLeft - listTileTopLeft, const Offset(752.0, 38.0));
listTileTopLeft = tester.getTopLeft(find.byType(ListTile).at(2));
leadingTopLeft = tester.getTopLeft(find.byType(CircleAvatar).at(2));
trailingTopLeft = tester.getTopLeft(find.byType(Icon).at(2));
// The leading and trailing widgets are aligned to the top vertically with the text.
expect(leadingTopLeft - listTileTopLeft, const Offset(16.0, 8.0));
expect(trailingTopLeft - listTileTopLeft, const Offset(752.0, 8.0));
});
}
// 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_api_samples/material/list_tile/list_tile.3.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('ListTile color properties respect Material state color', (WidgetTester tester) async {
await tester.pumpWidget(
const example.ListTileApp(),
);
ListTile listTile = tester.widget(find.byType(ListTile));
// Enabled list tile uses black color for icon and headline.
expect(listTile.enabled, true);
expect(listTile.selected, false);
RenderParagraph headline = _getTextRenderObject(tester, 'Headline');
expect(headline.text.style!.color, Colors.black);
RichText icon = tester.widget(find.byType(RichText).at(0));
expect(icon.text.style!.color, Colors.black);
// Tap list tile to select it.
await tester.tap(find.byType(ListTile));
await tester.pumpAndSettle();
// Selected list tile uses green color for icon and headline.
listTile = tester.widget(find.byType(ListTile));
expect(listTile.enabled, true);
expect(listTile.selected, true);
headline = _getTextRenderObject(tester, 'Headline');
expect(headline.text.style!.color, Colors.green);
icon = tester.widget(find.byType(RichText).at(0));
expect(icon.text.style!.color, Colors.green);
// Tap switch to disable list tile.
await tester.tap(find.byType(Switch));
await tester.pumpAndSettle();
// Disabled list tile uses red color for icon and headline.
listTile = tester.widget(find.byType(ListTile));
expect(listTile.enabled, false);
expect(listTile.selected, true);
headline = _getTextRenderObject(tester, 'Headline');
expect(headline.text.style!.color, Colors.red);
icon = tester.widget(find.byType(RichText).at(0));
expect(icon.text.style!.color, Colors.red);
});
}
RenderParagraph _getTextRenderObject(WidgetTester tester, String text) {
return tester.renderObject(find.descendant(
of: find.byType(ListTile),
matching: find.text(text),
));
}
// 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/list_tile/list_tile.selected.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('ListTile item can be selected', (WidgetTester tester) async {
await tester.pumpWidget(
const example.ListTileApp(),
);
expect(find.byType(ListTile), findsNWidgets(10));
// The first item is selected by default.
expect(tester.widget<ListTile>(find.byType(ListTile).at(0)).selected, true);
// Tap a list item to select it.
await tester.tap(find.byType(ListTile).at(5));
await tester.pump();
// The first item is no longer selected.
expect(tester.widget<ListTile>(find.byType(ListTile).at(0)).selected, false);
expect(tester.widget<ListTile>(find.byType(ListTile).at(5)).selected, true);
});
}
...@@ -139,12 +139,18 @@ enum ListTileControlAffinity { ...@@ -139,12 +139,18 @@ enum ListTileControlAffinity {
/// ** See code in examples/api/lib/material/list_tile/list_tile.1.dart ** /// ** See code in examples/api/lib/material/list_tile/list_tile.1.dart **
/// {@end-tool} /// {@end-tool}
/// ///
/// {@tool snippet} /// {@tool dartpad}
/// This sample shows the creation of a [ListTile] using [ThemeData.useMaterial3] flag,
/// as described in: https://m3.material.io/components/lists/overview.
///
/// ** See code in examples/api/lib/material/list_tile/list_tile.2.dart **
/// {@end-tool}
/// ///
/// To use a [ListTile] within a [Row], it needs to be wrapped in an /// To use a [ListTile] within a [Row], it needs to be wrapped in an
/// [Expanded] widget. [ListTile] requires fixed width constraints, /// [Expanded] widget. [ListTile] requires fixed width constraints,
/// whereas a [Row] does not constrain its children. /// whereas a [Row] does not constrain its children.
/// ///
/// {@tool snippet}
/// ```dart /// ```dart
/// Row( /// Row(
/// children: const <Widget>[ /// children: const <Widget>[
...@@ -231,7 +237,7 @@ enum ListTileControlAffinity { ...@@ -231,7 +237,7 @@ enum ListTileControlAffinity {
/// ///
/// ![Custom list item a](https://flutter.github.io/assets-for-api-docs/assets/widgets/custom_list_item_a.png) /// ![Custom list item a](https://flutter.github.io/assets-for-api-docs/assets/widgets/custom_list_item_a.png)
/// ///
/// ** See code in examples/api/lib/material/list_tile/list_tile.4.dart ** /// ** See code in examples/api/lib/material/list_tile/custom_list_item.0.dart **
/// {@end-tool} /// {@end-tool}
/// ///
/// {@tool dartpad} /// {@tool dartpad}
...@@ -241,7 +247,7 @@ enum ListTileControlAffinity { ...@@ -241,7 +247,7 @@ enum ListTileControlAffinity {
/// ///
/// ![Custom list item b](https://flutter.github.io/assets-for-api-docs/assets/widgets/custom_list_item_b.png) /// ![Custom list item b](https://flutter.github.io/assets-for-api-docs/assets/widgets/custom_list_item_b.png)
/// ///
/// ** See code in examples/api/lib/material/list_tile/list_tile.5.dart ** /// ** See code in examples/api/lib/material/list_tile/custom_list_item.1.dart **
/// {@end-tool} /// {@end-tool}
/// ///
/// See also: /// See also:
...@@ -409,7 +415,16 @@ class ListTile extends StatelessWidget { ...@@ -409,7 +415,16 @@ class ListTile extends StatelessWidget {
/// Defines the default color for [leading] and [trailing] icons. /// Defines the default color for [leading] and [trailing] icons.
/// ///
/// If this property is null then [ListTileThemeData.iconColor] is used. /// If this property is null and [selected] is false then [ListTileThemeData.iconColor]
/// is used. If that is also null and [ThemeData.useMaterial3] is true, [ColorScheme.onSurface]
/// is used, otherwise if [ThemeData.brightness] is [Brightness.light], [Colors.black54] is used,
/// and if [ThemeData.brightness] is [Brightness.dark], the value is null.
///
/// If this property is null and [selected] is true then [ListTileThemeData.selectedColor]
/// is used. If that is also null then [ColorScheme.primary] is used.
///
/// If this color is a [MaterialStateColor] it will be resolved against
/// [MaterialState.selected] and [MaterialState.disabled] states.
/// ///
/// See also: /// See also:
/// ///
...@@ -417,10 +432,18 @@ class ListTile extends StatelessWidget { ...@@ -417,10 +432,18 @@ class ListTile extends StatelessWidget {
/// [ListTileThemeData]. /// [ListTileThemeData].
final Color? iconColor; final Color? iconColor;
/// Defines the default color for the [title] and [subtitle]. /// Defines the text color for the [title], [subtitle], [leading], and [trailing].
///
/// If this property is null and [selected] is false then [ListTileThemeData.textColor]
/// is used. If that is also null then default text color is used for the [title], [subtitle]
/// [leading], and [trailing]. Except for [subtitle], if [ThemeData.useMaterial3] is false,
/// [TextTheme.bodySmall] is used.
///
/// If this property is null and [selected] is true then [ListTileThemeData.selectedColor]
/// is used. If that is also null then [ColorScheme.primary] is used.
/// ///
/// If this property is null then [ListTileThemeData.textColor] is used. If that /// If this color is a [MaterialStateColor] it will be resolved against
/// is also null then [ColorScheme.primary] is used. /// [MaterialState.selected] and [MaterialState.disabled] states.
/// ///
/// See also: /// See also:
/// ///
...@@ -541,8 +564,11 @@ class ListTile extends StatelessWidget { ...@@ -541,8 +564,11 @@ class ListTile extends StatelessWidget {
/// {@template flutter.material.ListTile.tileColor} /// {@template flutter.material.ListTile.tileColor}
/// Defines the background color of `ListTile` when [selected] is false. /// Defines the background color of `ListTile` when [selected] is false.
/// ///
/// When the value is null, the [tileColor] is set to [ListTileTheme.tileColor] /// If this property is null and [selected] is false then [ListTileThemeData.tileColor]
/// if it's not null and to [Colors.transparent] if it's null. /// is used. If that is also null and [selected] is true, [selectedTileColor] is used.
/// When that is also null, the [ListTileTheme.selectedTileColor] is used, otherwise
/// [Colors.transparent] is used.
///
/// {@endtemplate} /// {@endtemplate}
final Color? tileColor; final Color? tileColor;
......
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