Aakash
Aakash

Reputation: 41

Flutter ListTile text does not change theme/color when switched from light to dark

I have a ListView with ListTile in my screen and I have define two theme in materialApp and changing the themeMode based on the state of the themeBloc like this.

 MaterialApp(

        theme: AppTheme.light,
        darkTheme: AppTheme.dark,
        themeMode: context.watch<ThemeBloc>().state,
        
      ),

But the color in the list tile trailing text widget is not changing when I switch the theme.

ListTile(
    leadingAndTrailingTextStyle : Theme.of(context).textTheme.labelMedium,
    title: Text(state.data.elementAt(index).stockName),
    trailing: Text("My text which should change color based on theme"),
  ),

The labelMedium has two diffrent colors in light and dark theme

static ThemeData light = ThemeData(
    useMaterial3: true,
    colorScheme: lightColorScheme,
    brightness: Brightness.light,
    fontFamily: GoogleFonts.montserrat().fontFamily,
    textTheme: TextTheme(
        labelMedium: TextStyle(
          color: lightColorScheme.onPrimaryContainer,
          fontSize: 16,
          fontWeight: FontWeight.w500,
        ),
        ),
  );
  static ThemeData dark = ThemeData(
    useMaterial3: true,
    colorScheme: darkColorScheme,
    brightness: Brightness.dark,
    fontFamily: GoogleFonts.montserrat().fontFamily,
    textTheme: TextTheme(
        labelMedium: TextStyle(
          color: darkColorScheme.onPrimaryContainer,
          fontSize: 16,
          fontWeight: FontWeight.w500,
        ),
        ),
  );

I have a switch in app bar to toggle the theme from light to dark which adds an event in ThemeBloc. The color of the list tile changes from black to white if i dont give any textStyle to trailing widget, but when i give my style it does not work.

And one more thing if i switch to dark theme the color does not change but when i go to another screen and come back to this screen the color is changed.

I have tried to give same style to the Text widget but it also doesn't work.

EDIT : Check this example https://zapp.run/edit/flutter-zr3806pdr390?entry=lib/main.dart&file=lib/main.dart

The Second screen does not work but third screen works.

Upvotes: 2

Views: 411

Answers (1)

I had the same problem, so I tried two things and I'm not sure exactly which one worked.

I don’t know the logic you used to change the theme, but here’s how I did it:

 class AppWidget extends StatelessWidget {
  AppWidget({super.key});

  final themeProvider = ThemeProvider.getInstance();

  @override
  Widget build(BuildContext context) {
    final appRouter = router();
    //themeProvider.loadTheme();
    return ListenableBuilder(
      builder: (context, child) {
        return MaterialApp.router(
          title: 'Chess versus',
          localizationsDelegates: AppLocalizations.localizationsDelegates,
          supportedLocales: AppLocalizations.supportedLocales,
          localeResolutionCallback: (locale, supportedLocales) {
            if (locale == null) return const Locale('en');

            for (var supportedLocale in supportedLocales) {
              if (supportedLocale.languageCode == locale.languageCode) {
                return supportedLocale;
              }
            }
            return const Locale('en');
          },
          theme: lightTheme,
          darkTheme: darkTheme,
          themeMode: themeProvider.themeMode,
          routerConfig: appRouter,
        );
      },
      listenable: themeProvider,
    );
  }
}

my state manager class for theme switch:

ignore the variable themeService

    class ThemeProvider extends ChangeNotifier {
       final themeService = ThemeService();
       static ThemeProvider? _instance;

       ThemeProvider._();

       ThemeState state = LightThemeState();

       ThemeMode get themeMode => state.themeMode;

       switchTheme() {
         if (state is LightThemeState) {
          state = DarkThemeState();
         } else if (state is DarkThemeState) {
          state = LightThemeState();
       }
        themeService.saveLocalTheme(state.themeMode);
        notifyListeners();
      }
    }
    sealed class ThemeState {
     late final ThemeMode themeMode;
   }

   class LightThemeState extends ThemeState {
     @override
     final ThemeMode themeMode = ThemeMode.light;
   }

   class DarkThemeState extends ThemeState {
     @override
     final ThemeMode themeMode = ThemeMode.dark;
   }

1 - I was using ListView.builder and switched it to a regular ListView. They have a difference that involves displaying and building lists with many items, which could be related to this color change bug. In my case, when I scrolled the page to view hidden ListTile items, they didn’t have the bug. Also, the buggy ones that disappeared during scrolling were fixed and displayed the correct color when they reappeared.

                           ListView(
                            children: state.tournaments.map((tournament) {
                              return Align(
                                alignment: Alignment.center,
                                child: Container(
                                  margin: EdgeInsets.symmetric(
                                      vertical: 8,
                                      horizontal: Dimens.of(context)
                                          .paddingScreenHorizontal),
                                  child: ListTile(
                                      horizontalTitleGap: Dimens.of(context)
                                          .paddingScreenHorizontal,
                                      minVerticalPadding: Dimens.of(context)
                                          .paddingScreenVertical,
                                      tileColor: Theme.of(context)
                                          .colorScheme
                                          .secondary,
                                      title: Text(
                                        tournament.getName,
                                        style: Theme.of(context)
                                            .textTheme
                                            .titleMedium,
                                      ),
                                      subtitle: Column(
                                          crossAxisAlignment:
                                              CrossAxisAlignment.start,
                                          children: [
                                            Text(
                                              tournament.getDescription ??
                                                  AppLocalizations.of(context)!
                                                      .tournamentNullDescription,
                                            ),
                                            Text(
                                              tournament.getTypeName.toString(),
                                            ),
                                            Text(
                                              DateFormat('dd/MM/yyyy').format(
                                                  tournament.getStartedAt),
                                            )
                                          ])),
                                ),
                              );
                            }).toList(),
                          ),

2 - I set the default theme for ListTile in the file where the themes are configured. This way, I don’t need to clutter the widget with properties, keeping the code cleaner. Here it's messy, but in the project, this is split into separate files (laughs).

ThemeData get lightTheme => ThemeHelper().lightThemeData;

ThemeData get darkTheme => ThemeHelper().darkThemeData;

    class ThemeHelper {
      final Map<String, ColorScheme> _supportedColors = {
        'light': AppColors.lightCodeScheme,
        'dark': AppColors.darkCodeScheme,
      };
    
      ThemeData get lightThemeData => _getTheme('light');
      ThemeData get darkThemeData => _getTheme('dark');
    
      ThemeData _getTheme(String theme) {
        ColorScheme colorScheme =
            _supportedColors[theme] ?? AppColors.lightCodeScheme;
        return ThemeData(
          iconTheme: iconTheme(colorScheme),
          dialogTheme: dialogTheme(colorScheme),
          colorScheme: _supportedColors[theme],
          textTheme: TextThemes.textTheme(colorScheme),
          appBarTheme: appBarTheme(colorScheme),
          listTileTheme: listTileTheme(colorScheme),
          switchTheme: switchTheme(colorScheme),
          inputDecorationTheme: inputDecorationTheme(colorScheme),
        );
      }
    }


class AppColors {
  static const Color darkBlueGray = Color(0xFF2C3E50);
  static const Color blueGray = Color(0xFF375A7F);
  static const Color drakGray = Color(0xFF222222);
  static const Color gray = Color(0xFF3A3A3A);
  static const Color lightGray200 = Color(0xFF75818C);
  static const Color lightGray100 = Color(0xFF7F8C8D);
  static const Color lightGray = Color(0xFFB9C4C4);

  static const Color lightBlue = Color(0XFFD7E3FF);
  static const Color blue = Color(0XFF055AB2);
  static const Color white = Color(0XFFFFFFFF);
  static const Color red = Color(0XFFBA1A1A);
  static const Color purple = Color(0XFF810081);
  static const Color pink = Color(0XFFFDAFF2);
  static const Color lightRedPink = Color(0XFFFFB4AB);

  static const Color redOrange = Color(0XFFC44133);
  static const Color lightPurple = Color(0XFFEBDCFF);

  // Esquema de cores claro
  static const lightCodeScheme = ColorScheme.light(
    brightness: Brightness.light,
    primary: darkBlueGray,
    onPrimary: white,
    //surface: lightGray,
    secondary: lightGray,
    error: white,
    onError: red,
  );

  // Esquema de cores escuro
  static const darkCodeScheme = ColorScheme.dark(
    brightness: Brightness.dark,
    surface: drakGray,
    primary: blueGray,
    onPrimary: white,
    secondary: gray,
    error: redOrange,
    onError: white,
  );
}

ListTileThemeData listTileTheme(ColorScheme colorScheme) => ListTileThemeData(
      tileColor: colorScheme.secondary,
      textColor: colorScheme.onPrimary,
      iconColor: colorScheme.onPrimary,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)),
    );

Upvotes: 0

Related Questions