Updated ListTile
documentation, add Material 3 example and other ListTile
examples fixes. (#118705)
This commit is contained in:
parent
d53cc4a10f
commit
3e71e0caf4
@ -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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 @@
|
|||||||
// 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 {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
|||||||
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
|
|||||||
|
|
||||||
@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 {
|
|||||||
|
|
||||||
@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 {
|
|||||||
|
|
||||||
@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(
|
|
||||||
child: ListTile(
|
|
||||||
title: Text('One-line with trailing widget'),
|
|
||||||
trailing: Icon(Icons.more_vert),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
leading: FlutterLogo(),
|
|
||||||
title: Text('One-line with both widgets'),
|
|
||||||
trailing: Icon(Icons.more_vert),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
title: Text('One-line dense ListTile'),
|
|
||||||
dense: true,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
leading: FlutterLogo(size: 56.0),
|
|
||||||
title: Text('Two-line ListTile'),
|
|
||||||
subtitle: Text('Here is a second line'),
|
|
||||||
trailing: Icon(Icons.more_vert),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Card(
|
|
||||||
child: ListTile(
|
|
||||||
leading: FlutterLogo(size: 72.0),
|
|
||||||
title: Text('Three-line ListTile'),
|
|
||||||
subtitle: Text(
|
|
||||||
'A sufficiently long subtitle warrants three lines.'
|
|
||||||
),
|
),
|
||||||
trailing: Icon(Icons.more_vert),
|
|
||||||
isThreeLine: true,
|
|
||||||
),
|
),
|
||||||
),
|
Card(
|
||||||
],
|
child: ListTile(
|
||||||
|
title: Text('One-line with trailing widget'),
|
||||||
|
trailing: Icon(Icons.more_vert),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: FlutterLogo(),
|
||||||
|
title: Text('One-line with both widgets'),
|
||||||
|
trailing: Icon(Icons.more_vert),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
title: Text('One-line dense ListTile'),
|
||||||
|
dense: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: FlutterLogo(size: 56.0),
|
||||||
|
title: Text('Two-line ListTile'),
|
||||||
|
subtitle: Text('Here is a second line'),
|
||||||
|
trailing: Icon(Icons.more_vert),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
child: ListTile(
|
||||||
|
leading: FlutterLogo(size: 72.0),
|
||||||
|
title: Text('Three-line ListTile'),
|
||||||
|
subtitle: Text(
|
||||||
|
'A sufficiently long subtitle warrants three lines.'
|
||||||
|
),
|
||||||
|
trailing: Icon(Icons.more_vert),
|
||||||
|
isThreeLine: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
64
examples/api/lib/material/list_tile/list_tile.2.dart
Normal file
64
examples/api/lib/material/list_tile/list_tile.2.dart
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
// 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(),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
89
examples/api/lib/material/list_tile/list_tile.3.dart
Normal file
89
examples/api/lib/material/list_tile/list_tile.3.dart
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
// 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 @@
|
|||||||
|
|
||||||
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;
|
||||||
);
|
});
|
||||||
},
|
},
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,43 @@
|
|||||||
|
// 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);
|
||||||
|
});
|
||||||
|
}
|
@ -0,0 +1,43 @@
|
|||||||
|
// 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() {
|
|||||||
|
|
||||||
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() {
|
|||||||
|
|
||||||
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(
|
||||||
|
41
examples/api/test/material/list_tile/list_tile.2_test.dart
Normal file
41
examples/api/test/material/list_tile/list_tile.2_test.dart
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// 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));
|
||||||
|
});
|
||||||
|
}
|
58
examples/api/test/material/list_tile/list_tile.3_test.dart
Normal file
58
examples/api/test/material/list_tile/list_tile.3_test.dart
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
// 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),
|
||||||
|
));
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
// 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 {
|
|||||||
/// ** 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 {
|
|||||||
///
|
///
|
||||||
/// 
|
/// 
|
||||||
///
|
///
|
||||||
/// ** 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 {
|
|||||||
///
|
///
|
||||||
/// 
|
/// 
|
||||||
///
|
///
|
||||||
/// ** 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 {
|
|||||||
|
|
||||||
/// 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 {
|
|||||||
/// [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 then [ListTileThemeData.textColor] is used. If that
|
/// If this property is null and [selected] is false then [ListTileThemeData.textColor]
|
||||||
/// is also null then [ColorScheme.primary] is used.
|
/// 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 color is a [MaterialStateColor] it will be resolved against
|
||||||
|
/// [MaterialState.selected] and [MaterialState.disabled] states.
|
||||||
///
|
///
|
||||||
/// See also:
|
/// See also:
|
||||||
///
|
///
|
||||||
@ -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;
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user