diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index 81f1b63e77..efa192621c 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart @@ -1484,18 +1484,36 @@ class _RenderListTile extends RenderBox final Size? leadingSize = leading == null ? null : getSize(leading, iconConstraints); final Size? trailingSize = trailing == null ? null : getSize(trailing, iconConstraints); - assert( - tileWidth != leadingSize?.width || tileWidth == 0.0, - 'Leading widget consumes entire tile width. Please use a sized widget, ' - 'or consider replacing ListTile with a custom widget ' - '(see https://api.flutter.dev/flutter/material/ListTile-class.html#material.ListTile.4)', - ); - assert( - tileWidth != trailingSize?.width || tileWidth == 0.0, - 'Trailing widget consumes entire tile width. Please use a sized widget, ' - 'or consider replacing ListTile with a custom widget ' - '(see https://api.flutter.dev/flutter/material/ListTile-class.html#material.ListTile.4)', - ); + assert(() { + if (tileWidth == 0.0) { + return true; + } + + String? overflowedWidget; + if (tileWidth == leadingSize?.width) { + overflowedWidget = 'Leading'; + } else if (tileWidth == trailingSize?.width) { + overflowedWidget = 'Trailing'; + } + + if (overflowedWidget == null) { + return true; + } + + throw FlutterError.fromParts([ + ErrorSummary( + '$overflowedWidget widget consumes the entire tile width (including ListTile.contentPadding).', + ), + ErrorDescription( + 'Either resize the tile width so that the ${overflowedWidget.toLowerCase()} widget plus any content padding ' + 'do not exceed the tile width, or use a sized widget, or consider replacing ' + 'ListTile with a custom widget.', + ), + ErrorHint( + 'See also: https://api.flutter.dev/flutter/material/ListTile-class.html#material.ListTile.4', + ), + ]); + }()); final double titleStart = leadingSize == null diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index 85127ad0ca..ab5fb5313a 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -2618,6 +2618,36 @@ void main() { expect(trailingOffset.dy - tileOffset.dy, topPosition); }); + testWidgets('Leading/Trailing exceeding list tile width throws exception', ( + WidgetTester tester, + ) async { + Widget buildListTile({Widget? leading, Widget? trailing}) { + return MaterialApp( + home: Material( + child: Center( + child: SizedBox(width: 100, child: ListTile(leading: leading, trailing: trailing)), + ), + ), + ); + } + + // Test a trailing widget that exceeds the list tile width. + // 16 (content padding) + 61 (leading width) + 24 (content padding) = 101. + // List tile width is 100 as a result, an exception should be thrown. + await tester.pumpWidget(buildListTile(leading: const SizedBox(width: 61))); + + // Error message cannot be tested as there too many errors thrown. + expect(tester.takeException(), isNotNull); + + // Test a trailing widget that exceeds the list tile width. + // 16 (content padding) + 61 (trailing width) + 24 (content padding) = 101. + // List tile width is 100 as a result, an exception should be thrown. + await tester.pumpWidget(buildListTile(trailing: const SizedBox(width: 61))); + + // Error message cannot tested be as there too many errors thrown. + expect(tester.takeException(), isNotNull); + }); + group('Material 2', () { // These tests are only relevant for Material 2. Once Material 2 // support is deprecated and the APIs are removed, these tests