Skip to content

InheritedTheme.capture loses ancestor IconButtonThemeData.variant when a closer IconButtonTheme omits variant #196177

Open

Description

@dkwingsmt

In flutter/packages#13263, we updated IconButton to resolve its style variant via IconButtonTheme.variantOf(context), which walks up enclosing IconButtonTheme widgets and returns the first non-null variant. This allows an inner IconButtonTheme with variant: null (such as the internal themes created by AppBar, ListTile, or InputDecorator) to override style without resetting an ancestor's StyleVariant (e.g., StyleVariant.material3Expressive).

However, this approach does not work across routes that capture themes using InheritedTheme.capture or InheritedTheme.captureAll (such as showDialog, showModalBottomSheet, and PopupMenuButton).

InheritedTheme.capture only records the closest InheritedTheme instance for each runtimeType. When a widget tree has an outer IconButtonTheme that sets variant and an inner IconButtonTheme with variant: null, capturing themes from inside the inner theme only captures the inner IconButtonTheme. In the new route, IconButtonTheme.variantOf cannot see the outer IconButtonTheme and falls back to ThemeData.iconButtonTheme.variant or StyleVariant.material3.

Reproduction

The following test demonstrates the issue:

testWidgets('InheritedTheme.captureAll preserves the inherited variant', (
  WidgetTester tester,
) async {
  StyleVariant? capturedVariant;
  await tester.pumpWidget(
    MaterialApp(
      home: IconButtonTheme(
        data: const IconButtonThemeData(variant: StyleVariant.material3Expressive),
        child: IconButtonTheme(
          data: const IconButtonThemeData(
            style: ButtonStyle(foregroundColor: WidgetStatePropertyAll<Color>(Color(0xffff0000))),
          ),
          child: Builder(
            builder: (BuildContext context) {
              return ElevatedButton(
                onPressed: () {
                  Navigator.of(context).push(
                    MaterialPageRoute<void>(
                      builder: (BuildContext routeContext) {
                        return InheritedTheme.captureAll(
                          context,
                          Builder(
                            builder: (BuildContext innerContext) {
                              capturedVariant = IconButtonTheme.variantOf(innerContext);
                              return const SizedBox();
                            },
                          ),
                        );
                      },
                    ),
                  );
                },
                child: const Text('Push'),
              );
            },
          ),
        ),
      ),
    ),
  );

  await tester.tap(find.text('Push'));
  await tester.pumpAndSettle();

  // Fails: capturedVariant is StyleVariant.material3 instead of StyleVariant.material3Expressive.
  expect(capturedVariant, StyleVariant.material3Expressive);
});

Context

See the review comment at flutter/packages#13263 (review) for the original discussion. As more widgets adopt StyleVariant (or if other component themes support partial inheritance across enclosing themes), we need to decide how InheritedTheme.capture should handle properties inherited from multiple ancestors of the same theme type.

Activity

  1. added
    frameworkflutter/packages/flutter repository. See also f: labels.
    p: material_uimaterial_ui package in flutter/packages
    packageflutter/packages repository. See also p: labels.
    has reproducible stepsThe issue has been confirmed reproducible and is ready to work on
    fyi-designFor the attention of Design Languages team
    and removed on Nov 9, 2026
  2. added
    team-designOwned by Design Languages team
    and removed
    fyi-designFor the attention of Design Languages team
    on Nov 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagespackageflutter/packages repository. See also p: labels.team-designOwned by Design Languages team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions