Unverified Commit 29ab437e authored by Taha Tesser's avatar Taha Tesser Committed by GitHub

Add Material 3 `CheckboxListTile` example and update existing examples (#118792)

* Add Material 3 `CheckboxListTile` example and update existing examples

* fix `list_tile.dart` doc issues

* Remove unnecessary comma
parent eced23ea
......@@ -7,46 +7,43 @@
import 'package:flutter/material.dart';
import 'package:flutter/scheduler.dart' show timeDilation;
void main() => runApp(const MyApp());
void main() => runApp(const CheckboxListTileApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
class CheckboxListTileApp extends StatelessWidget {
const CheckboxListTileApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
return const MaterialApp(
home: CheckboxListTileExample(),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
class CheckboxListTileExample extends StatefulWidget {
const CheckboxListTileExample({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
State<CheckboxListTileExample> createState() => _CheckboxListTileExampleState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
class _CheckboxListTileExampleState extends State<CheckboxListTileExample> {
@override
Widget build(BuildContext context) {
return CheckboxListTile(
title: const Text('Animate Slowly'),
value: timeDilation != 1.0,
onChanged: (bool? value) {
setState(() {
timeDilation = value! ? 10.0 : 1.0;
});
},
secondary: const Icon(Icons.hourglass_empty),
return Scaffold(
appBar: AppBar(title: const Text('CheckboxListTile Sample')),
body: Center(
child: CheckboxListTile(
title: const Text('Animate Slowly'),
value: timeDilation != 1.0,
onChanged: (bool? value) {
setState(() {
timeDilation = value! ? 10.0 : 1.0;
});
},
secondary: const Icon(Icons.hourglass_empty),
),
),
);
}
}
......@@ -4,99 +4,76 @@
// Flutter code sample for [CheckboxListTile].
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
void main() => runApp(const CheckboxListTileApp());
static const String _title = 'Flutter Code Sample';
class CheckboxListTileApp extends StatelessWidget {
const CheckboxListTileApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
theme: ThemeData(colorSchemeSeed: const Color(0xff6750a4), useMaterial3: true),
home: const CheckboxListTileExample(),
);
}
}
class LinkedLabelCheckbox extends StatelessWidget {
const LinkedLabelCheckbox({
super.key,
required this.label,
required this.padding,
required this.value,
required this.onChanged,
});
class CheckboxListTileExample extends StatefulWidget {
const CheckboxListTileExample({super.key});
final String label;
final EdgeInsets padding;
final bool value;
final ValueChanged<bool> onChanged;
@override
State<CheckboxListTileExample> createState() => _CheckboxListTileExampleState();
}
class _CheckboxListTileExampleState extends State<CheckboxListTileExample> {
bool checkboxValue1 = true;
bool checkboxValue2 = true;
bool checkboxValue3 = true;
@override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: Row(
return Scaffold(
appBar: AppBar(title: const Text('CheckboxListTile Sample')),
body: Column(
children: <Widget>[
Expanded(
child: RichText(
text: TextSpan(
text: label,
style: const TextStyle(
color: Colors.blueAccent,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () {
debugPrint('Label has been tapped.');
},
),
),
CheckboxListTile(
value: checkboxValue1,
onChanged: (bool? value) {
setState(() {
checkboxValue1 = value!;
});
},
title: const Text('Headline'),
subtitle: const Text('Supporting text'),
),
const Divider(height: 0),
CheckboxListTile(
value: checkboxValue2,
onChanged: (bool? value) {
setState(() {
checkboxValue2 = value!;
});
},
title: const Text('Headline'),
subtitle: const Text('Longer supporting text to demonstrate how the text wraps and the checkbox is centered vertically with the text.'),
),
Checkbox(
value: value,
onChanged: (bool? newValue) {
onChanged(newValue!);
},
const Divider(height: 0),
CheckboxListTile(
value: checkboxValue3,
onChanged: (bool? value) {
setState(() {
checkboxValue3 = value!;
});
},
title: const Text('Headline'),
subtitle: const Text("Longer supporting text to demonstrate how the text wraps and how setting 'CheckboxListTile.isThreeLine = true' aligns the checkbox to the top vertically with the text."),
isThreeLine: true,
),
const Divider(height: 0),
],
),
);
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
bool _isSelected = false;
@override
Widget build(BuildContext context) {
return LinkedLabelCheckbox(
label: 'Linked, tappable label text',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
value: _isSelected,
onChanged: (bool newValue) {
setState(() {
_isSelected = newValue;
});
},
);
}
}
// 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 custom labeled checkbox.
import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
void main() => runApp(const LabeledCheckBoxApp());
class LabeledCheckBoxApp extends StatelessWidget {
const LabeledCheckBoxApp({super.key});
@override
Widget build(BuildContext context) {
return const MaterialApp(
home: LabeledCheckBoxExample(),
);
}
}
class LinkedLabelCheckbox extends StatelessWidget {
const LinkedLabelCheckbox({
super.key,
required this.label,
required this.padding,
required this.value,
required this.onChanged,
});
final String label;
final EdgeInsets padding;
final bool value;
final ValueChanged<bool> onChanged;
@override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: Row(
children: <Widget>[
Expanded(
child: RichText(
text: TextSpan(
text: label,
style: const TextStyle(
color: Colors.blueAccent,
decoration: TextDecoration.underline,
),
recognizer: TapGestureRecognizer()
..onTap = () {
debugPrint('Label has been tapped.');
},
),
),
),
Checkbox(
value: value,
onChanged: (bool? newValue) {
onChanged(newValue!);
},
),
],
),
);
}
}
class LabeledCheckBoxExample extends StatefulWidget {
const LabeledCheckBoxExample({super.key});
@override
State<LabeledCheckBoxExample> createState() => _LabeledCheckBoxExampleState();
}
class _LabeledCheckBoxExampleState extends State<LabeledCheckBoxExample> {
bool _isSelected = false;
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('Custom Labeled Checkbox Sample')),
body: Center(
child: LinkedLabelCheckbox(
label: 'Linked, tappable label text',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
value: _isSelected,
onChanged: (bool newValue) {
setState(() {
_isSelected = newValue;
});
},
),
),
);
}
}
......@@ -2,27 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flutter code sample for [CheckboxListTile].
// Flutter code sample for custom labeled checkbox.
import 'package:flutter/material.dart';
void main() => runApp(const MyApp());
void main() => runApp(const LabeledCheckBoxApp());
class MyApp extends StatelessWidget {
const MyApp({super.key});
static const String _title = 'Flutter Code Sample';
class LabeledCheckBoxApp extends StatelessWidget {
const LabeledCheckBoxApp({super.key});
@override
Widget build(BuildContext context) {
return MaterialApp(
title: _title,
home: Scaffold(
appBar: AppBar(title: const Text(_title)),
body: const Center(
child: MyStatefulWidget(),
),
),
return const MaterialApp(
home: LabeledCheckBoxExample(),
);
}
}
......@@ -65,27 +57,32 @@ class LabeledCheckbox extends StatelessWidget {
}
}
class MyStatefulWidget extends StatefulWidget {
const MyStatefulWidget({super.key});
class LabeledCheckBoxExample extends StatefulWidget {
const LabeledCheckBoxExample({super.key});
@override
State<MyStatefulWidget> createState() => _MyStatefulWidgetState();
State<LabeledCheckBoxExample> createState() => _LabeledCheckBoxExampleState();
}
class _MyStatefulWidgetState extends State<MyStatefulWidget> {
class _LabeledCheckBoxExampleState extends State<LabeledCheckBoxExample> {
bool _isSelected = false;
@override
Widget build(BuildContext context) {
return LabeledCheckbox(
label: 'This is the label text',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
value: _isSelected,
onChanged: (bool newValue) {
setState(() {
_isSelected = newValue;
});
},
return Scaffold(
appBar: AppBar(title: const Text('Custom Labeled Checkbox Sample')),
body: Center(
child: LabeledCheckbox(
label: 'This is the label text',
padding: const EdgeInsets.symmetric(horizontal: 20.0),
value: _isSelected,
onChanged: (bool newValue) {
setState(() {
_isSelected = newValue;
});
},
),
),
);
}
}
......@@ -27,7 +27,7 @@ class ListTileExample extends StatelessWidget {
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: const Text('ListTile Sample')),
body: Column(
body: ListView(
children: const <Widget>[
ListTile(
leading: CircleAvatar(
......@@ -37,7 +37,7 @@ class ListTileExample extends StatelessWidget {
subtitle: Text('Supporting text'),
trailing: Icon(Icons.favorite_rounded),
),
Divider(),
Divider(height: 0),
ListTile(
leading: CircleAvatar(
child: Text('B')
......@@ -46,7 +46,7 @@ class ListTileExample extends StatelessWidget {
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(),
Divider(height: 0),
ListTile(
leading: CircleAvatar(
child: Text('C')
......@@ -56,7 +56,7 @@ class ListTileExample extends StatelessWidget {
trailing: Icon(Icons.favorite_rounded),
isThreeLine: true,
),
Divider(),
Divider(height: 0),
],
),
);
......
......@@ -35,54 +35,51 @@ class _ListTileExampleState extends State<ListTileExample> {
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: () {
body: Center(
child: 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(() {
// This is called when the user toggles the switch.
_selected = !_selected;
_enabled = value!;
});
},
// 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,
),
value: _enabled,
),
],
),
),
);
}
......
// 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/scheduler.dart';
import 'package:flutter_api_samples/material/checkbox_list_tile/checkbox_list_tile.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('CheckboxListTile can be checked', (WidgetTester tester) async {
await tester.pumpWidget(
const example.CheckboxListTileApp(),
);
CheckboxListTile checkboxListTile = tester.widget(find.byType(CheckboxListTile));
expect(checkboxListTile.value, isFalse);
await tester.tap(find.byType(CheckboxListTile));
await tester.pump();
timeDilation = 1.0;
checkboxListTile = tester.widget(find.byType(CheckboxListTile));
expect(checkboxListTile.value, isTrue);
await tester.tap(find.byType(CheckboxListTile));
await tester.pump();
checkboxListTile = tester.widget(find.byType(CheckboxListTile));
expect(checkboxListTile.value, isFalse);
});
}
// 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/checkbox_list_tile/checkbox_list_tile.1.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Checkbox aligns appropriately', (WidgetTester tester) async {
await tester.pumpWidget(
const example.CheckboxListTileApp(),
);
expect(find.byType(CheckboxListTile), findsNWidgets(3));
Offset tileTopLeft = tester.getTopLeft(find.byType(CheckboxListTile).at(0));
Offset checkboxTopLeft = tester.getTopLeft(find.byType(Checkbox).at(0));
// The checkbox is centered vertically with the text.
expect(checkboxTopLeft - tileTopLeft, const Offset(736.0, 16.0));
tileTopLeft = tester.getTopLeft(find.byType(CheckboxListTile).at(1));
checkboxTopLeft = tester.getTopLeft(find.byType(Checkbox).at(1));
// The checkbox is centered vertically with the text.
expect(checkboxTopLeft - tileTopLeft, const Offset(736.0, 30.0));
tileTopLeft = tester.getTopLeft(find.byType(CheckboxListTile).at(2));
checkboxTopLeft = tester.getTopLeft(find.byType(Checkbox).at(2));
// The checkbox is aligned to the top vertically with the text.
expect(checkboxTopLeft - tileTopLeft, const Offset(736.0, 8.0));
});
testWidgets('Checkboxes can be checked', (WidgetTester tester) async {
await tester.pumpWidget(
const example.CheckboxListTileApp(),
);
expect(find.byType(CheckboxListTile), findsNWidgets(3));
// All checkboxes are checked.
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(0)).value, isTrue);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(1)).value, isTrue);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(2)).value, isTrue);
// Tap the first checkbox.
await tester.tap(find.byType(Checkbox).at(0));
await tester.pumpAndSettle();
// The first checkbox is unchecked.
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(0)).value, isFalse);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(1)).value, isTrue);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(2)).value, isTrue);
// Tap the second checkbox.
await tester.tap(find.byType(Checkbox).at(1));
await tester.pumpAndSettle();
// The first and second checkboxes are unchecked.
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(0)).value, isFalse);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(1)).value, isFalse);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(2)).value, isTrue);
// Tap the third checkbox.
await tester.tap(find.byType(Checkbox).at(2));
await tester.pumpAndSettle();
// All checkboxes are unchecked.
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(0)).value, isFalse);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(1)).value, isFalse);
expect(tester.widget<Checkbox>(find.byType(Checkbox).at(2)).value, isFalse);
});
}
// 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/checkbox_list_tile/custom_labeled_checkbox.0.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('LinkedLabelCheckbox contains RichText and Checkbox', (WidgetTester tester) async {
await tester.pumpWidget(
const example.LabeledCheckBoxApp(),
);
// Label text is in a RichText widget with the correct text.
final RichText richText = tester.widget(find.byType(RichText).first);
expect(richText.text.toPlainText(), 'Linked, tappable label text');
// Checkbox is initially unchecked.
Checkbox checkbox = tester.widget(find.byType(Checkbox));
expect(checkbox.value, isFalse);
// Tap the checkbox to check it.
await tester.tap(find.byType(Checkbox));
await tester.pumpAndSettle();
// Checkbox is now checked.
checkbox = tester.widget(find.byType(Checkbox));
expect(checkbox.value, isTrue);
});
}
// 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/checkbox_list_tile/custom_labeled_checkbox.1.dart' as example;
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('Tapping LabeledCheckbox toggles the checkbox', (WidgetTester tester) async {
await tester.pumpWidget(
const example.LabeledCheckBoxApp(),
);
// Checkbox is initially unchecked.
Checkbox checkbox = tester.widget(find.byType(Checkbox));
expect(checkbox.value, isFalse);
// Tap the LabeledCheckBoxApp to toggle the checkbox.
await tester.tap(find.byType(example.LabeledCheckbox));
await tester.pumpAndSettle();
// Checkbox is now checked.
checkbox = tester.widget(find.byType(Checkbox));
expect(checkbox.value, isTrue);
});
}
......@@ -85,6 +85,13 @@ import 'theme_data.dart';
/// ** See code in examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.0.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This sample shows the creation of a [CheckboxListTile] using [ThemeData.useMaterial3] flag,
/// as described in: https://m3.material.io/components/lists/overview.
///
/// ** See code in examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.1.dart **
/// {@end-tool}
///
/// ## Semantics in CheckboxListTile
///
/// Since the entirety of the CheckboxListTile is interactive, it should represent
......@@ -109,7 +116,7 @@ import 'theme_data.dart';
/// LinkedLabelCheckbox, that includes an interactive [RichText] widget that
/// handles tap gestures.
///
/// ** See code in examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.1.dart **
/// ** See code in examples/api/lib/material/checkbox_list_tile/custom_labeled_checkbox.0.dart **
/// {@end-tool}
///
/// ## CheckboxListTile isn't exactly what I want
......@@ -125,7 +132,7 @@ import 'theme_data.dart';
/// Here is an example of a custom LabeledCheckbox widget, but you can easily
/// make your own configurable widget.
///
/// ** See code in examples/api/lib/material/checkbox_list_tile/checkbox_list_tile.2.dart **
/// ** See code in examples/api/lib/material/checkbox_list_tile/custom_labeled_checkbox.1.dart **
/// {@end-tool}
///
/// See also:
......
......@@ -146,11 +146,19 @@ enum ListTileControlAffinity {
/// ** See code in examples/api/lib/material/list_tile/list_tile.2.dart **
/// {@end-tool}
///
/// {@tool dartpad}
/// This sample shows [ListTile]'s [textColor] and [iconColor] can use
/// [MaterialStateColor] color to change the color of the text and icon
/// when the [ListTile] is enabled, selected, or disabled.
///
/// ** See code in examples/api/lib/material/list_tile/list_tile.3.dart **
/// {@end-tool}
///
/// {@tool snippet}
/// To use a [ListTile] within a [Row], it needs to be wrapped in an
/// [Expanded] widget. [ListTile] requires fixed width constraints,
/// whereas a [Row] does not constrain its children.
///
/// {@tool snippet}
/// ```dart
/// Row(
/// children: const <Widget>[
......
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