From f2be9bcad54ef152d023139bea6d6e92f0bb9416 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Wed, 17 Apr 2024 10:59:07 +0300 Subject: [PATCH] Fix `Tab` indicator image configuration doesn't inherit device pixel ratio (#146812) fixes [The image for the indicator of the TabBar does not auto-adapt to different resolutions](https://github.com/flutter/flutter/issues/145204) ### Description This PR provides device pixel ratio to the tab indicator painter. --- packages/flutter/lib/src/material/tabs.dart | 4 ++ packages/flutter/test/material/tabs_test.dart | 48 +++++++++++++++++++ .../flutter/test/material/tabs_utils.dart | 21 ++++++++ 3 files changed, 73 insertions(+) diff --git a/packages/flutter/lib/src/material/tabs.dart b/packages/flutter/lib/src/material/tabs.dart index 7917707cc7..6685a608ad 100644 --- a/packages/flutter/lib/src/material/tabs.dart +++ b/packages/flutter/lib/src/material/tabs.dart @@ -442,6 +442,7 @@ class _IndicatorPainter extends CustomPainter { this.dividerColor, this.dividerHeight, required this.showDivider, + this.devicePixelRatio, }) : super(repaint: controller.animation) { // TODO(polina-c): stop duplicating code across disposables // https://github.com/flutter/flutter/issues/137435 @@ -466,6 +467,7 @@ class _IndicatorPainter extends CustomPainter { final Color? dividerColor; final double? dividerHeight; final bool showDivider; + final double? devicePixelRatio; // _currentTabOffsets and _currentTextDirection are set each time TabBar // layout is completed. These values can be null when TabBar contains no @@ -562,6 +564,7 @@ class _IndicatorPainter extends CustomPainter { final ImageConfiguration configuration = ImageConfiguration( size: _currentRect!.size, textDirection: _currentTextDirection, + devicePixelRatio: devicePixelRatio, ); if (showDivider && dividerHeight !> 0) { final Paint dividerPaint = Paint()..color = dividerColor!..strokeWidth = dividerHeight!; @@ -1433,6 +1436,7 @@ class _TabBarState extends State { dividerColor: widget.dividerColor ?? tabBarTheme.dividerColor ?? _defaults.dividerColor, dividerHeight: widget.dividerHeight ?? tabBarTheme.dividerHeight ?? _defaults.dividerHeight, showDivider: theme.useMaterial3 && !widget.isScrollable, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), ); oldPainter?.dispose(); diff --git a/packages/flutter/test/material/tabs_test.dart b/packages/flutter/test/material/tabs_test.dart index d7e6a653ae..a4ba9d7777 100644 --- a/packages/flutter/test/material/tabs_test.dart +++ b/packages/flutter/test/material/tabs_test.dart @@ -50,6 +50,7 @@ Widget buildFrame({ TextDirection textDirection = TextDirection.ltr, TabAlignment? tabAlignment, TabBarTheme? tabBarTheme, + Decoration? indicator, bool? useMaterial3, }) { if (secondaryTabBar) { @@ -88,6 +89,7 @@ Widget buildFrame({ indicatorColor: indicatorColor, padding: padding, tabAlignment: tabAlignment, + indicator: indicator, ), ), ); @@ -7075,4 +7077,50 @@ void main() { expect(find.text('View 0'), findsNothing); expect(find.text('View 2'), findsOneWidget); }); + + testWidgets('Tab indicator painter image configuration', (WidgetTester tester) async { + final List tabs = ['A', 'B']; + final TestIndicatorDecoration decoration = TestIndicatorDecoration(); + + Widget buildTabs({ + TextDirection textDirection = TextDirection.ltr, + double ratio = 1.0, + }) { + return MaterialApp( + home: MediaQuery( + data: MediaQueryData(devicePixelRatio: ratio), + child: Directionality( + textDirection: textDirection, + child: DefaultTabController( + length: tabs.length, + child: Scaffold( + appBar: AppBar( + bottom: TabBar( + indicator: decoration, + tabs: tabs.map((String tab) => Tab(text: tab)).toList(), + ), + ), + ), + ), + ), + ), + ); + } + + await tester.pumpWidget(buildTabs()); + + ImageConfiguration config = decoration.painters.last.lastConfiguration!; + expect(config.size?.width, closeTo(14.1, 0.1)); + expect(config.size?.height, equals(48.0)); + expect(config.textDirection, TextDirection.ltr); + expect(config.devicePixelRatio, 1.0); + + await tester.pumpWidget(buildTabs(textDirection: TextDirection.rtl, ratio: 2.33)); + + config = decoration.painters.last.lastConfiguration!; + expect(config.size?.width, closeTo(14.1, 0.1)); + expect(config.size?.height, equals(48.0)); + expect(config.textDirection, TextDirection.rtl); + expect(config.devicePixelRatio, 2.33); + }); } diff --git a/packages/flutter/test/material/tabs_utils.dart b/packages/flutter/test/material/tabs_utils.dart index 62f6362c7c..dfb0233be1 100644 --- a/packages/flutter/test/material/tabs_utils.dart +++ b/packages/flutter/test/material/tabs_utils.dart @@ -228,3 +228,24 @@ class _TabAlwaysKeepAliveWidgetState extends State wit return Text(TabAlwaysKeepAliveWidget.text); } } + +// This decoration is used to test the indicator decoration image configuration. +class TestIndicatorDecoration extends Decoration { + final List painters = []; + + @override + BoxPainter createBoxPainter([VoidCallback? onChanged]) { + final TestIndicatorBoxPainter painter = TestIndicatorBoxPainter(); + painters.add(painter); + return painter; + } +} + +class TestIndicatorBoxPainter extends BoxPainter { + ImageConfiguration? lastConfiguration; + + @override + void paint(Canvas canvas, Offset offset, ImageConfiguration configuration) { + lastConfiguration = configuration; + } +}