From cef35e77909fc7f3ca3f187a59ca6b31b21f526b Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Mon, 13 Jan 2025 15:57:38 +0200 Subject: [PATCH] Update error message for when leading/trailing width exceeds `ListTile` width and add missing test (#161091) Fixes [ListTile Crashes When Width Is Reduced with Leading or Trailing Widgets](https://github.com/flutter/flutter/issues/159380) ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md --- .../flutter/lib/src/material/list_tile.dart | 42 +++++++++++++------ .../flutter/test/material/list_tile_test.dart | 30 +++++++++++++ 2 files changed, 60 insertions(+), 12 deletions(-) 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