Skip to content

Fix TabBar assertion to allow indicatorWeight: 0 with a themed indicator - #189157

Closed
Massinissa-Mouhoub wants to merge 6 commits into
flutter:masterfrom
Massinissa-Mouhoub:issue_188837
Closed

Massinissa-Mouhoub wants to merge 6 commits into
flutter:masterfrom
Massinissa-Mouhoub:issue_188837

Conversation

@Massinissa-Mouhoub

Copy link
Copy Markdown

This PR moves the assertion that checks indicator != null || (indicatorWeight > 0.0) from the TabBar constructors into the Decoration _getIndicator(TabBarIndicatorSize indicatorSize) method. During the first phase of creating the TabBar widget, Dart just fills in the fields, there is no BuildContext available yet, so the widget has no way of access the TabBarTheme via context.dependOnInheritedWidgetOfExactType<TabBarTheme>(). As a result, the constructor's assertion can only see the indicator passed directly to the widget, and it fails even when a valid indicator is supplied through the TabBarTheme.

By moving the assertion into the method mentionned above, which runs at build time, the TabBar now has access to the TabBarTheme and can validate the indicator correctly, accepting indicatorWeight: 0 when a themed indicator is present, while still asserting when the default underline indicator is used.

Fixes #188837

Pre-launch Checklist

  • I read the [Contributor Guide] and followed the process outlined there for submitting PRs.
  • I read the [AI contribution guidelines] and understand my responsibilities, or I am not using AI tools.
  • I read the [Tree Hygiene] wiki page, which explains my responsibilities.
  • I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement].
  • I signed the [CLA].
  • I listed at least one issue that this PR fixes in the description above.
  • I updated/added relevant documentation (doc comments with ///).
  • 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.
  • All existing and new tests are passing.

@Massinissa-Mouhoub Massinissa-Mouhoub changed the title Issue 188837 Fix TabBar assertion to allow indicatorWeight: 0 with a themed indicator Jul 8, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request updates the TabBar and TabBar.secondary constructors by removing the assertion that requires indicatorWeight to be greater than 0.0 when a custom indicator is provided. Instead, the assertion is now deferred to the state build phase, where it is only triggered if the default underline indicator is drawn. New widget tests have been added to verify that indicatorWeight can be 0.0 when a custom indicator is supplied via the widget or TabBarThemeData, and that it still asserts when using the default indicator. There are no review comments, and I have no feedback to provide.

@github-actions github-actions Bot added framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages labels Jul 8, 2026
@Massinissa-Mouhoub

Massinissa-Mouhoub commented Jul 9, 2026 •

Copy link
Copy Markdown
Author

The Check Code Freeze job is failing because this PR touches frozen paths. Both files it changes are covered:

  • packages/flutter/lib/src/material/tabs.dart
  • packages/flutter/test/material/tabs_test.dart

so there's no subset of the change I can split out to get the gate green.

For context on what the change does: TabBar's constructor asserts indicator != null || indicatorWeight > 0.0. That assert runs before there's a BuildContext, so it can't see an indicator supplied via TabBarThemeData, which means indicatorWeight: 0 throws even when the theme provides a decoration (#188837). This moves the assertion into void _getIndicator(TabBarIndicatorSize indicatorSize), where the theme has already been resolved and the check can be made against the indicator the TabBar will actually paint. Behavior is otherwise unchanged: the default underline indicator still requires a positive weight. Regression tests cover all three resolution paths (widget indicator, themed indicator, default underline).

I read #184093 as saying PRs like this should stay open and then be ported to flutter/packages once material_ui ships, so I'm happy to leave it here and do the port when the time comes. I don't think this is urgent enough to warrant override code freeze, there are workarounds (pass indicator directly to the TabBar, or leave indicatorWeight at its default), but I'll defer to the team if you'd rather it land now.

@Piinks
Piinks requested a review from QuncCccccc July 15, 2026 21:22

@dkwingsmt dkwingsmt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you also fix the doc for indicatorWeight, just like https://github.com/flutter/flutter/pull/188967/changes#diff-b90db0bc5b533dbd8ef4c5099057b292200e0aa96125e74ab3646c783b468bbdR1140 ? Here's my proposal:

  /// If [indicator] is specified or provided from [TabBarThemeData],
  /// this property is ignored. Otherwise, when drawing the default underline
  /// indicator, the value of this parameter must be greater than zero.

Otherwise LGTM.

Comment on lines +1593 to +1598
assert(
widget.indicatorWeight > 0.0,
'The indicatorWeight must be greater than 0 when the TabBar '
'draws its default underline indicator (i.e. when no indicator is '
'provided by the TabBar or the TabBarTheme).',
);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The detailed message is great! I wonder if the displayed error screen can be even more understandable if you throw a FlutterError.fromParts from within an assert, something like

    assert(() {
      throw FlutterError.fromParts(<DiagnosticsNode>[
        ErrorSummary('Invalid indicatorWeight for TabBar.'),
        ErrorDescription('indicatorWeight must be greater than zero when using the default underline indicator.'),
        ErrorHint('To fix this, set indicatorWeight > 0 or specify an indicator in TabBarTheme.'),
      ]);
      return true;
    }());

Can you try?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried your approach with a small modification:

assert(() {
  if (widget.indicatorWeight <= 0.0) { // This was added to prevent throwing unconditionaly
    throw FlutterError.fromParts(<DiagnosticsNode>[
      ErrorSummary('Invalid indicatorWeight for TabBar.'),
      ErrorDescription(
        'The indicatorWeight must be greater than zero when the TabBar '
        'draws its default underline indicator, that is when no indicator '
        'is provided by the TabBar or the TabBarTheme.',
      ),
      ErrorHint(
        'To fix this, set indicatorWeight to a value greater than zero, '
        'or provide an indicator with the TabBar.indicator or '
        'TabBarThemeData.indicator property.',
      ),
    ]);
  }
  return true;
}());

Below is the displayed error on-screen before and after:

Before After
image image

Below is the error on console:
Before

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building MediaQuery(MediaQueryData(size: Size(440.0, 956.0), devicePixelRatio: 3.0, textScaler: SystemTextScaler (no scaling), platformBrightness: Brightness.light, padding: EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, systemGestureInsets: EdgeInsets.zero, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast: false, onOffSwitchLabels: false, disableAnimations: false, invertColors: false, boldText: false, navigationMode: traditional, gestureSettings: DeviceGestureSettings(touchSlop: null), displayFeatures: [], supportsShowingSystemContextMenu: true, lineHeightScaleFactorOverride: null, letterSpacingOverride: null, wordSpacingOverride: null, paragraphSpacingOverride: null, displayCornerRadii: null)):
The indicatorWeight must be greater than 0 when the TabBar draws its default underline indicator (i.e. when no indicator is provided by the TabBar or the TabBarTheme).
'package:flutter/src/material/tabs.dart':
Failed assertion: line 1595 pos 7: 'widget.indicatorWeight > 0.0'

After

════════ Exception caught by widgets library ═══════════════════════════════════
The following assertion was thrown building MediaQuery(MediaQueryData(size: Size(440.0, 956.0), devicePixelRatio: 3.0, textScaler: SystemTextScaler (no scaling), platformBrightness: Brightness.light, padding: EdgeInsets.zero, viewPadding: EdgeInsets.zero, viewInsets: EdgeInsets.zero, systemGestureInsets: EdgeInsets.zero, alwaysUse24HourFormat: false, accessibleNavigation: false, highContrast: false, onOffSwitchLabels: false, disableAnimations: false, invertColors: false, boldText: false, navigationMode: traditional, gestureSettings: DeviceGestureSettings(touchSlop: null), displayFeatures: [], supportsShowingSystemContextMenu: true, lineHeightScaleFactorOverride: null, letterSpacingOverride: null, wordSpacingOverride: null, paragraphSpacingOverride: null, displayCornerRadii: null)):
Invalid indicatorWeight for TabBar.
The indicatorWeight must be greater than zero when the TabBar draws its default underline indicator, that is when no indicator is provided by the TabBar or the TabBarTheme.
To fix this, set indicatorWeight to a value greater than zero, or provide an indicator with the TabBar.indicator or TabBarThemeData.indicator property.

I don't see an improvement on the displayed error, but I largly prefer the console error after the fix. I think it's better if we go with this new approach.

@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

dkwingsmt Please check the latest commit, I updated the docs and applied your suggestion for throwing the assertion.

@QuncCccccc QuncCccccc added the CICD Run CI/CD label Jul 22, 2026

@QuncCccccc QuncCccccc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for your contribution! The code fix looks great. Just left some comments in tests.

Comment thread packages/flutter/test/material/tabs_test.dart Outdated
Comment thread packages/flutter/test/material/tabs_test.dart Outdated
Comment thread packages/flutter/test/material/tabs_test.dart Outdated
Comment thread packages/flutter/test/material/tabs_test.dart Outdated
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Jul 22, 2026
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Jul 22, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Jul 23, 2026
@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

@QuncCccccc I applied your comments, please check.

QuncCccccc
QuncCccccc previously approved these changes Jul 23, 2026

@QuncCccccc QuncCccccc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks a lot for your contribution:)

@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

@QuncCccccc, could you please add autosubmit to this PR since it's been approved?

dkwingsmt
dkwingsmt previously approved these changes Jul 27, 2026

@dkwingsmt dkwingsmt left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@dkwingsmt dkwingsmt added the CICD Run CI/CD label Jul 27, 2026
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Jul 27, 2026
@dkwingsmt

Copy link
Copy Markdown
Contributor

@Massinissa-Mouhoub Since this PR affects material, it's under the effect of the code freeze. Let's get the checks passed, then we'll archive this PR until the code freeze is lifted, at which time we'll provide a guide to port this PR to the new packages.

@dkwingsmt dkwingsmt added the CICD Run CI/CD label Jul 27, 2026
@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

@Massinissa-Mouhoub Since this PR affects material, it's under the effect of the code freeze. Let's get the checks passed, then we'll archive this PR until the code freeze is lifted, at which time we'll provide a guide to port this PR to the new packages.

Alright perfect, thank you!

@dkwingsmt

Copy link
Copy Markdown
Contributor

There's a leakage failure:


04:21 +9484 ~15 -1: C:/b/s/w/ir/x/w/flutter/packages/flutter/test/material/tabs_test.dart: (tearDownAll) [E]
  Expected: leak free
    Actual: <Instance of 'Leaks'>
     Which: contains leaks:
            # The text is generated by leak_tracker.
            # For leak troubleshooting tips open:
            # https://github.com/flutter/flutter/blob/main/docs/contributing/testing/Leak-tracking.md
            notDisposed:
              total: 6
              objects:
                StatefulElement:
                  test: TabBar throws indicatorWeight: 0 with the default underline indicator
                  identityHashCode: 271796273
                _TabBarState:
                  test: TabBar throws indicatorWeight: 0 with the default underline indicator
                  identityHashCode: 683157412
                TabBarScrollController:
                  test: TabBar throws indicatorWeight: 0 with the default underline indicator
                  identityHashCode: 732348325
                StatefulElement:
                  test: TabBar throws indicatorWeight: 0 with the default underline indicator
                  identityHashCode: 992881765
                _TabBarState:
                  test: TabBar throws indicatorWeight: 0 with the default underline indicator
                  identityHashCode: 858281407
                TabBarScrollController:
                  test: TabBar throws indicatorWeight: 0 with the default underline indicator
                  identityHashCode: 175261228
            
            
  
  package:matcher                                              expect
  package:leak_tracker_testing/src/leak_testing.dart 69:24     LeakTesting.collectedLeaksReporter.<fn>
  package:leak_tracker_flutter_testing/src/testing.dart 74:37  maybeTearDownLeakTrackingForAll
  ===== asynchronous gap ===========================
  dart:async                                                   Zone.registerBinaryCallback
  package:flutter_test/src/test_compat.dart 302:3              _tearDownForTestFile

…indicator

Covers flutter#188837:
- themed indicator (TabBarThemeData.indicator) + indicatorWeight: 0 no longer
  asserts and renders the themed indicator (primary and secondary);
- widget-level indicator + indicatorWeight: 0 does not assert;
- default underline indicator + indicatorWeight: 0 still asserts.
…rWeight

Per review feedback, replace the plain assert message with a
FlutterError.fromParts thrown from within an assert, so the debug-mode
error screen shows a structured summary, description, and fix hint.

Also update the TabBar.indicatorWeight API documentation to state that
the value must be greater than zero when the default underline
indicator is used, and update the regression test to match the new
FlutterError.
@Massinissa-Mouhoub

Massinissa-Mouhoub commented Jul 29, 2026 •

Copy link
Copy Markdown
Author

@dkwingsmt can you please check? It's my first time dealing with leakage, so I'm not really sure of the way I solved it.

Comment thread packages/flutter/test/material/tabs_test.dart
@dkwingsmt
dkwingsmt self-requested a review August 5, 2026 18:19
@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

@dkwingsmt can you take a look at this when you are free please ?

dkwingsmt
dkwingsmt previously approved these changes Aug 6, 2026

@dkwingsmt dkwingsmt left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Thank you for the PR and investigation on the leak! (And sorry for late review)

Comment thread packages/flutter/test/material/tabs_test.dart Outdated
@QuncCccccc QuncCccccc added the CICD Run CI/CD label Aug 6, 2026
QuncCccccc
QuncCccccc previously approved these changes Aug 6, 2026

@QuncCccccc QuncCccccc left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-LGTM. Thank you:)

@dkwingsmt

Copy link
Copy Markdown
Contributor

Thanks for your contribution! This PR has reached completion, however we're not able to merge this PR for the moment due to the Material/Cupertino code freeze (#184093). As soon as the decoupling is complete, we will post a way to port this PR to the new packages to land. In the meantime, I'll convert this PR to a draft and apply a label so that we can quickly find it when the freeze is lifted.

@dkwingsmt
dkwingsmt marked this pull request as draft August 6, 2026 19:59
@dkwingsmt dkwingsmt added the waiting for code freeze This PR is waiting for a code freeze to resolve. label Aug 6, 2026
@Massinissa-Mouhoub
Massinissa-Mouhoub dismissed stale reviews from QuncCccccc and dkwingsmt via ceff3ed August 6, 2026 23:44
@flutter-dashboard flutter-dashboard Bot removed the CICD Run CI/CD label Aug 6, 2026
@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

@dkwingsmt Alright thank you! I applied your comment suggestion in the last commit.

@Massinissa-Mouhoub

Copy link
Copy Markdown
Author

@dkwingsmt @QuncCccccc this PR was ported in this PR please check it there.

@QuncCccccc

Copy link
Copy Markdown
Contributor

Since we have a port PR for this fix, I'm closing this PR. Thanks a lot for porting it over!

@QuncCccccc QuncCccccc closed this Sep 2, 2026
auto-submit Bot pushed a commit to flutter/packages that referenced this pull request Oct 6, 2026
…th a themed `indicator` (#12696)

This is the port of this [PR](flutter/flutter#189157) after decoupling.

Fixes flutter/flutter#188837.

## Original PR Description
This PR moves the assertion that checks indicator != null || (indicatorWeight > 0.0) from the TabBar constructors into the Decoration _getIndicator(TabBarIndicatorSize indicatorSize) method. During the first phase of creating the TabBar widget, Dart just fills in the fields, there is no BuildContext available yet, so the widget has no way of access the TabBarTheme via context.dependOnInheritedWidgetOfExactType<TabBarTheme>(). As a result, the constructor's assertion can only see the indicator passed directly to the widget, and it fails even when a valid indicator is supplied through the TabBarTheme.

By moving the assertion into the method mentionned above, which runs at build time, the TabBar now has access to the TabBarTheme and can validate the indicator correctly, accepting indicatorWeight: 0 when a themed indicator is present, while still asserting when the default underline indicator is used.

Fixes flutter/flutter#188837

## Pre-Review Checklist
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

framework flutter/packages/flutter repository. See also f: labels. p: material_ui material_ui package in flutter/packages waiting for code freeze This PR is waiting for a code freeze to resolve.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TabBar throws assertion indicator != null || indicatorWeight > 0.0 despite a TabBarThemeData.indicator

3 participants