stryder-dev / flutter_platform_widgets

Target the specific design of Material for Android and Cupertino for iOS widgets through a common set of Platform aware widgets
MIT License
1.57k stars 171 forks source link

`ThemeData` does not apply to various defaults on iOS #457

Closed martin-braun closed 3 months ago

martin-braun commented 3 months ago

Part of my ThemeData is setting bottomNavigationBarTheme:

    bottomNavigationBarTheme: BottomNavigationBarThemeData(
      backgroundColor: envPrimaryColor,
      selectedItemColor: Colors.white,
      unselectedItemColor: Colors.grey,
    ),

but it's not applied in the bottom navigation of PlatformTabScaffold. I have to modify backgroundColor and the icons themselves explicitly. A respected declarative ThemeData configuration would be much better. if you have to maintain various bottom navigations in your app.

I see the same happening for the appBarTheme and the PlatformAppBar. And the list goes on. For instance with the icons:

final ThemeData _defaultThemeLight = ThemeData.light();
final ThemeData themeLight = _defaultThemeLight.copyWith(
    appBarTheme: _defaultThemeLight.appBarTheme.copyWith(
        backgroundColor: envPrimaryColor,
        foregroundColor: envPrimaryContrastColor,
        iconTheme: IconThemeData(
            color: envPrimaryContrastColor,
            size: _defaultThemeDark.textTheme.headlineSmall!.fontSize)),
    iconTheme: IconThemeData(
        color: envPrimaryContrastColor,
        size: _defaultThemeDark.textTheme.headlineSmall!.fontSize),
    colorScheme: ColorScheme.fromSeed(
        seedColor: envPrimaryColor,
        primary: envPrimaryColor,
        onPrimary: envPrimaryContrastColor,
        brightness: Brightness.dark), // describes the contrast color
// ....

In the Icon.color description it says "Defaults to the nearest [IconTheme]'s [IconThemeData.color].", yet my iconTheme will be ignored when I use Icon in the appBarBuilder.

Thinking I do something wrong, I double checked everything and it looks good:

  @override
  Widget build(BuildContext context) {
    return PlatformProvider(
      builder: (context) => PlatformTheme(
        themeMode: ThemeMode.system,
        materialLightTheme: themeLight,
        materialDarkTheme: themeDark,
        cupertinoLightTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeLight),
        cupertinoDarkTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeDark),
        builder: (context) => PlatformApp(
          localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
            DefaultMaterialLocalizations.delegate,
            DefaultWidgetsLocalizations.delegate,
            DefaultCupertinoLocalizations.delegate,
          ],
          title: envProductName,
          home: Observer(
              builder: (context) =>
                  stateAppStatusStore.blockingErrorMessage != null
                      ? ErrorScreen(
                          message: stateAppStatusStore.blockingErrorMessage!)
                      : HomeScreen(title: envProductName)),
        ),
      ),
    );
  }

And I can confirm the theming is working, because when I don't set PlatformTabScaffold.tabsBackgroundColor and toggle dark mode in the iOS simulator, my bottom bar and background changes in color:

2024-05-20 09 10 29

I then simplified even further:

  @override
  Widget build(BuildContext context) {
    return PlatformProvider(
      builder: (context) => PlatformTheme(
        themeMode: ThemeMode.system,
        materialLightTheme: themeLight,
        materialDarkTheme: themeDark,
        cupertinoLightTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeLight),
        cupertinoDarkTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeDark),
        builder: (context) => PlatformApp(
            localizationsDelegates: const <LocalizationsDelegate<dynamic>>[
              DefaultMaterialLocalizations.delegate,
              DefaultWidgetsLocalizations.delegate,
              DefaultCupertinoLocalizations.delegate,
            ],
            title: envProductName,
            home: const Icon(Icons
                .wifi)
            ),
      ),
    );
  }

See how my home is just an icon. envPrimaryContrastColor is white, but the icon has always the primaryColor from the colorScheme, which is green in my case:

image

So what is going on?? I can also load the theme data fine with final ThemeData theme = Theme.of(context);, so I can fix this by being explicit and setting all those things directly on the widgets as I use them ... but ... why would I want to do that.

I want to obey the Flutter theme concept and be very declarative about my theme, but this isn't working. Can somebody please tell me I am doing anything wrong here?

martin-braun commented 3 months ago

Testing further to nail down the issue:

    return MaterialApp(
      theme: themeLight,
      darkTheme: themeDark,
      themeMode: ThemeMode.system,
      home: const Icon(Icons.wifi),
    );

This works, my icon is white.

However this will not work:

    return PlatformProvider(
      builder: (context) => PlatformTheme(
        themeMode: ThemeMode.system,
        materialLightTheme: themeLight,
        materialDarkTheme: themeDark,
        cupertinoLightTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeLight),
        cupertinoDarkTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeDark),
        builder: (context) => const PlatformApp(home: Icon(Icons.wifi)),
      ),
    );

iconTheme will not be used, but it will get the color of colorScheme.

Wiki says "from v3.3 onwards a PlatformTheme widget can be added between PlatformProvider and PlatformApp to control the light and dark themes of both the material and cupertino style independently of each other.", but trying to leave out the PlatformTheme did not help either:

    return PlatformProvider(
        builder: (context) => PlatformApp(
              material: (_, __) => MaterialAppData(
                theme: themeLight,
                darkTheme: themeDark,
                themeMode: ThemeMode.system,
              ),
              cupertino: (_, __) => CupertinoAppData(
                theme:
                    MaterialBasedCupertinoThemeData(materialTheme: themeLight),
              ),
              home: const Icon(Icons.wifi),
            ));

Not working.

I also tried to set iosUsesMaterialWidgets: true on the provider without luck.

martin-braun commented 3 months ago

So the reason is my platformStyle is Cupertino not Material. I was thinking that colors and everything get transferred to Cupertino by doing:

        cupertinoLightTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeLight),
        cupertinoDarkTheme:
            MaterialBasedCupertinoThemeData(materialTheme: themeDark),

Apparently, not. It seems to apply only some colors from the colorScheme, but my iconThemes are not part of the Cupertino theme, which starts to make sense. The iconTheme or appTheme are material theme configurations that get applied to material widgets, but I am getting Cupertino widgets, because I am on iOS.

The reason for my wrong thinking was that I thought the MaterialBasedCupertinoThemeData would also apply icon themes and whatnot, but those themes are simply not part of the Cupertino theme data as far as I can tell.