Skip to content

Flutter 3.27 and later breaks past styling and theming of icon color on buttons with icons #162839

@rydmike

Description

@rydmike

Steps to reproduce

When you theme or style a FilledButton.icon, ElevatedButton.icon, OutlinedButton.icon, TextButton.icon or a SegmentedButton with icons, and give them a foreground color, the text color and the used icon color both use the specified foreground color.

This was the case in all versions prior to Flutter 3.27.0.

In Flutter 3.27.0 and later, all the way to latest master version, this is no longer the case. The styling and theming is broken and the icon color keep default colors.

To get the correct and same color that you got in previous versions, you have to explicitly set the icon color in versions 3.27.0 and later. In versions before, if icon color was not specified and foreground was specified, the icon color used the same specified foreground color.

There is no mentioned of this radically breaking change in the Flutter 3.27.0 release notes, the new behavior is a bug. This bug caused icon color breaking changes in a production app with millions of users.

Expected results

When theming and styling FilledButton.icon, ElevatedButton.icon, OutlinedButton.icon, TextButton.icon or a SegmentedButton with icons and specifying only foreground color:

EXPECT the icon to use the same color.

Image

The above CORRECT and EXPECTED result is obtained with Flutter 3.24.5 and earlier stable versions.

Actual results

When theming and styling FilledButton.icon, ElevatedButton.icon, OutlinedButton.icon, TextButton.icon or a SegmentedButton with icons and specifying only foreground color,

ACTUAL result is that the icon uses default colors.

Image

The above INCORRECT and ACTUAL result is obtained with Flutter 3.27.x and later versions all the way up to latest master.

Code sample

Code sample
import 'package:flutter/material.dart';

void main() => runApp(const MyApp());

final ButtonStyle filledButtonStyle = FilledButton.styleFrom(
  foregroundColor: Colors.red,
  backgroundColor: Colors.grey,
);

final ButtonStyle elevatedButtonStyle = ElevatedButton.styleFrom(
  foregroundColor: Colors.orange.shade600,
  backgroundColor: Colors.blueGrey,
);

final ButtonStyle outlinedButtonStyle = OutlinedButton.styleFrom(
  foregroundColor: Colors.lightBlue,
);

final ButtonStyle textButtonStyle = TextButton.styleFrom(
  foregroundColor: Colors.green,
);

final ButtonStyle segmentedButtonStyle = SegmentedButton.styleFrom(
  selectedForegroundColor: Colors.tealAccent.shade700,
);

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const HomePage(),
      theme: ThemeData(
        filledButtonTheme: FilledButtonThemeData(
          style: filledButtonStyle,
        ),
        elevatedButtonTheme: ElevatedButtonThemeData(
          style: elevatedButtonStyle,
        ),
        outlinedButtonTheme: OutlinedButtonThemeData(
          style: outlinedButtonStyle,
        ),
        textButtonTheme: TextButtonThemeData(
          style: textButtonStyle,
        ),
        segmentedButtonTheme: SegmentedButtonThemeData(
          style: segmentedButtonStyle,
        ),
      ),
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Button Icon Color Issue')),
      body: Center(
        child: Column(
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                FilledButton.icon(
                  label: const Text('Filled Themed'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
                const SizedBox(width: 8),
                FilledButton.icon(
                  style: filledButtonStyle.copyWith(
                    foregroundColor: WidgetStateProperty.all(Colors.yellow),
                  ),
                  label: const Text('Filled Styled'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
              ],
            ),
            const SizedBox(height: 8),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                ElevatedButton.icon(
                  label: const Text('Elevated Themed'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
                const SizedBox(width: 8),
                ElevatedButton.icon(
                  style: elevatedButtonStyle.copyWith(
                    foregroundColor: WidgetStateProperty.all(Colors.lime),
                  ),
                  label: const Text('Elevated Styled'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
              ],
            ),
            const SizedBox(height: 8),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                OutlinedButton.icon(
                  label: const Text('Outlined Themed'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
                const SizedBox(width: 8),
                OutlinedButton.icon(
                  style: outlinedButtonStyle.copyWith(
                    foregroundColor: WidgetStateProperty.all(Colors.deepOrange),
                  ),
                  label: const Text('Outlined Styled'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
              ],
            ),
            const SizedBox(height: 8),
            Row(
              mainAxisAlignment: MainAxisAlignment.center,
              children: [
                TextButton.icon(
                  label: const Text('Text Themed'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
                const SizedBox(width: 8),
                TextButton.icon(
                  style: textButtonStyle.copyWith(
                    foregroundColor: WidgetStateProperty.all(Colors.pink),
                  ),
                  label: const Text('Text Styled'),
                  icon: const Icon(Icons.add),
                  onPressed: () {},
                ),
              ],
            ),
            const SizedBox(height: 8),
            const SegmentedButtonShowcase(),
          ],
        ),
      ),
    );
  }
}

class SegmentedButtonShowcase extends StatefulWidget {
  const SegmentedButtonShowcase({this.showOutlinedButton, super.key});
  final bool? showOutlinedButton;

  @override
  State<SegmentedButtonShowcase> createState() =>
      _SegmentedButtonShowcaseState();
}

enum Calendar { day, week, month, year }

class _SegmentedButtonShowcaseState extends State<SegmentedButtonShowcase> {
  Calendar _selected = Calendar.day;

  @override
  Widget build(BuildContext context) {
    return SegmentedButton<Calendar>(
      segments: const <ButtonSegment<Calendar>>[
        ButtonSegment<Calendar>(
          value: Calendar.day,
          label: Text('Day'),
          icon: Icon(Icons.calendar_view_day),
        ),
        ButtonSegment<Calendar>(
          value: Calendar.week,
          icon: Icon(Icons.calendar_view_week),
          label: Text('Week'),
        ),
        ButtonSegment<Calendar>(
          value: Calendar.month,
          icon: Icon(Icons.calendar_view_month),
          label: Text('Mont'),
        ),
        ButtonSegment<Calendar>(
          value: Calendar.year,
          icon: Icon(Icons.calendar_today),
          label: Text('Year'),
        ),
      ],
      selected: <Calendar>{_selected},
      onSelectionChanged: (Set<Calendar> selected) {
        setState(() {
          _selected = selected.first;
        });
      },
    );
  }
}

Flutter Doctor output

Doctor output
flutter doctor -v
[✓] Flutter (Channel master, 3.29.0-1.0.pre.281, on macOS 15.3 24D60 darwin-arm64, locale en-US) [1,763ms]
    • Flutter version 3.29.0-1.0.pre.281 on channel master at /Users/rydmike/fvm/versions/master
    • Upstream repository https://github.com/flutter/flutter.git
    • Framework revision 1b095a030d (2 hours ago), 2025-02-06 12:11:37 -0800
    • Engine revision 1b095a030d
    • Dart version 3.8.0 (build 3.8.0-70.0.dev)
    • DevTools version 2.43.0

[✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) [1,720ms]
    • Android SDK at /Users/rydmike/Library/Android/sdk
    • Platform android-34, build-tools 34.0.0
    • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java
      This is the JDK bundled with the latest Android Studio installation on this machine.
      To manually set the JDK path, use: `flutter config --jdk-dir="path/to/jdk"`.
    • Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS (Xcode 16.2) [951ms]
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Build 16C5032a
    • CocoaPods version 1.16.2

[✓] Chrome - develop for the web [94ms]
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 2023.2) [93ms]
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 17.0.9+0-17.0.9b1087.7-11185874)

[✓] IntelliJ IDEA Community Edition (version 2024.3.1.1) [91ms]
    • IntelliJ at /Applications/IntelliJ IDEA CE.app
    • Flutter plugin version 83.0.4
    • Dart plugin version 243.23177

[✓] VS Code (version 1.97.0) [11ms]
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.104.0

[✓] Connected device (3 available) [6.1s]
    • MrPinkPro (wireless) (mobile) • 74120d6ef6769c3a2e53d61051da0147d0279996 • ios            • iOS 17.7.2 21H221
    • macOS (desktop)               • macos                                    • darwin-arm64   • macOS 15.3 24D60 darwin-arm64
    • Chrome (web)                  • chrome                                   • web-javascript • Google Chrome 132.0.6834.160

[✓] Network resources [691ms]
    • All expected network resources are available.

• No issues found!

Metadata

Metadata

Assignees

Labels

P1High-priority issues at the top of the work listc: regressionIt was better in the past than it is nowf: material designflutter/packages/flutter/material repository.frameworkflutter/packages/flutter repository. See also f: labels.r: fixedIssue is closed as already fixed in a newer versionteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

Type

No type

Projects

Status

Done (PR merged)

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions