Added Light/Dark theme toggle feature#146
Added Light/Dark theme toggle feature#146dhruvi-16-me wants to merge 2 commits intoAOSSIE-Org:mainfrom
Conversation
📝 WalkthroughWalkthroughAdds provider-backed dynamic theming: new AppThemeMode, ThemeController (with SharedPreferences persistence), light/dark ThemeData, provider injection in main, and widespread replacement of hard-coded colors with theme-aware values across UI screens. Changes
Sequence DiagramsequenceDiagram
actor User
participant UI as Profile / Settings UI
participant ThemeController as ThemeController
participant SharedPrefs as SharedPreferences
participant App as MaterialApp
User->>UI: choose theme (light/dark/system)
UI->>ThemeController: setThemeMode(AppThemeMode)
activate ThemeController
ThemeController->>SharedPrefs: persist theme key
activate SharedPrefs
SharedPrefs-->>ThemeController: persisted
deactivate SharedPrefs
ThemeController->>ThemeController: update state & notifyListeners()
deactivate ThemeController
App->>ThemeController: watch via Provider/Consumer
ThemeController-->>App: new flutterThemeMode
App->>App: rebuild with lightTheme/darkTheme
App-->>User: UI updates instantly
Note over App,SharedPrefs: On app start
App->>ThemeController: ThemeController.create()
activate ThemeController
ThemeController->>SharedPrefs: load saved theme
activate SharedPrefs
SharedPrefs-->>ThemeController: return saved mode
deactivate SharedPrefs
ThemeController-->>App: initialized themeMode
deactivate ThemeController
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 10
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (8)
lib/screens/meetings/meeting_insights_screen.dart (1)
330-348:⚠️ Potential issue | 🟠 MajorHardcoded text colors will break in light mode.
The transcript view uses hardcoded
Colors.white,Colors.white70for speaker name and text content. When the user switches to light mode, white text on a light surface will be invisible or barely readable.Replace with theme-aware colors:
🎨 Proposed fix for theme-aware text colors
Text( speaker, - style: const TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold), ), const SizedBox(height: 4), Text( text, - style: - const TextStyle(color: Colors.white70, height: 1.3), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + height: 1.3), ),lib/screens/tasks/create_task_screen.dart (1)
157-159:⚠️ Potential issue | 🟠 MajorInput text color hardcoded to white will be invisible in light mode.
The
TextFormFieldstyle usesColors.whitewhich won't be readable on light theme surfaces. Use the theme'sonSurfacecolor instead.🎨 Proposed fix
TextFormField( controller: _titleController, - style: const TextStyle(color: Colors.white), + style: TextStyle(color: Theme.of(context).colorScheme.onSurface), decoration: InputDecoration(Apply the same fix to the description field at line 194.
lib/screens/tickets/create_ticket_screen.dart (1)
168-175:⚠️ Potential issue | 🟠 MajorInconsistent theming: hardcoded text colors may cause visibility issues in light mode.
While
fillColoruses theme-basedcolorScheme.surface, the input text style at Line 175 uses hardcodedColors.white. In light mode, white text on a light surface will be invisible.Apply theme-based text color consistently:
♻️ Suggested fix
style: const TextStyle(color: Colors.white), + style: TextStyle(color: Theme.of(context).colorScheme.onSurface),lib/screens/meetings/meeting_detail_screen.dart (1)
1110-1113:⚠️ Potential issue | 🟡 MinorHardcoded dialog content color breaks light mode.
The dialog content uses
Colors.white70, which will have poor contrast on light theme backgrounds. Use theme-based color instead.🎨 Proposed fix
content: const Text( 'Are you sure you want to delete this meeting? This action cannot be undone.', - style: TextStyle(color: Colors.white70), + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), ),Note: Remove
constsinceTheme.of(context)requires runtime evaluation.lib/screens/meetings/create_meeting_screen.dart (1)
199-270:⚠️ Potential issue | 🟠 MajorForm inputs use hardcoded colors that break light mode.
The form fields use
Colors.whitefor text andColors.greyfor labels/borders, which will have poor or no visibility on the light theme's white/light background. This significantly impacts usability when the user switches to light mode.🎨 Proposed fix pattern
Replace hardcoded colors with theme-aware values. Example for the title field:
TextFormField( controller: _titleController, - style: const TextStyle(color: Colors.white), - decoration: const InputDecoration( + style: TextStyle(color: Theme.of(context).colorScheme.onSurface), + decoration: InputDecoration( labelText: 'Title *', - labelStyle: TextStyle(color: Colors.grey), - enabledBorder: OutlineInputBorder( - borderSide: BorderSide(color: Colors.grey), + labelStyle: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), + enabledBorder: OutlineInputBorder( + borderSide: BorderSide(color: Theme.of(context).colorScheme.outline), ),Apply this pattern to all form fields (title, description, date/time selectors, duration, URL).
lib/screens/tasks/task_screen.dart (1)
930-936:⚠️ Potential issue | 🟡 MinorInconsistent color: icon themed but text hardcoded.
Line 926 uses
colorScheme.onSurfaceVariantfor the drag indicator icon, but line 933 uses hardcodedColors.grey.shade500for the adjacent text. These should match for visual consistency.🎨 Proposed fix
Text( 'Drag to change status', style: TextStyle( - color: Colors.grey.shade500, + color: Theme.of(context).colorScheme.onSurfaceVariant, fontSize: 12, ), ),lib/screens/profile/profile_screen.dart (2)
860-885:⚠️ Potential issue | 🟡 MinorAvoid no‑op tap targets in settings items.
'Notifications' and 'Security' items are called withoutonTap, but the tile still shows a chevron and accepts taps with a default no-op handler. Disable the tile and hide the chevron whenonTapis null.Suggested fix
- trailing: Icon(Icons.chevron_right, color: colorScheme.onSurfaceVariant), - onTap: onTap ?? - () { - // Default implementation if no specific onTap is provided - // TODO: Implement settings navigation - }, + trailing: onTap == null + ? null + : Icon(Icons.chevron_right, color: colorScheme.onSurfaceVariant), + onTap: onTap, + enabled: onTap != null,
895-926:⚠️ Potential issue | 🟡 MinorSwitch is hardcoded and non-functional.
The Push Notifications switch is always "on" with a TODO in the handler, creating misleading UI. Either wire this to real preference state or disable it until implemented.💡 Minimal stopgap (disable until wired)
- trailing: isSwitch - ? Switch( - value: true, - onChanged: (value) { - // TODO: Implement preference toggle - }, - activeColor: Colors.green.shade400, - ) + trailing: isSwitch + ? Switch( + value: false, + onChanged: null, + activeColor: Colors.green.shade400, + ) : Icon(Icons.chevron_right, color: colorScheme.onSurfaceVariant),
🤖 Fix all issues with AI agents
In `@lib/screens/auth/team_selection_dialog.dart`:
- Around line 188-189: The widget currently uses the deprecated WillPopScope
with onWillPop; replace WillPopScope with PopScope, set canPop: false, and
rename the callback from onWillPop to onPop (keeping the same Future<bool>
signature and logic). Update the return site that uses WillPopScope and its
onWillPop handler so it becomes PopScope(canPop: false, onPop: /* same async
bool callback */, child: ...).
In `@lib/screens/meetings/meeting_insights_screen.dart`:
- Around line 393-396: Replace the hardcoded Colors.white70 in the two Text
widgets inside _buildSummaryView with a theme-aware color lookup so the bullet
and text respect light/dark modes; for example, retrieve a color via
Theme.of(context).colorScheme.onSurfaceVariant (or
Theme.of(context).textTheme.bodyMedium?.color) and use that value for both the
bullet Text('• ') and the Expanded child Text(text) so they follow the app's
color scheme.
- Around line 475-476: Replace the hard-coded color in the Text widget that
renders overall (child: Text(overall, style: const TextStyle(color:
Colors.white70, height: 1.4))) with a theme-based TextStyle: obtain a base style
from Theme.of(context).textTheme (e.g., bodySmall or bodyMedium) and call
copyWith(height: 1.4) so the text follows the app theme (for example, style:
Theme.of(context).textTheme.bodySmall?.copyWith(height: 1.4)). Update the Text
widget where overall is rendered to use this themed style instead of
Colors.white70.
- Around line 424-432: The two Text widgets in MeetingInsightsScreen that render
map['item'] and the metadata currently use hardcoded Colors.white and
Colors.grey.shade400; replace those hardcoded colors with theme-aware colors
(e.g., use Theme.of(context).textTheme.bodyLarge/bodySmall or
Theme.of(context).colorScheme.onBackground/onSurface) so the text adapts to
light/dark modes and preserves contrast—update the TextStyle for the map['item']
Text and the metadata Text (the lines that build 'Owner: ${map['owner']...}' and
map['item']?.toString()) to derive color from Theme.of(context) instead of
hardcoded Colors.*.
In `@lib/screens/profile/profile_screen.dart`:
- Around line 822-850: In _buildThemePreferenceItem (which uses ThemeController
and _themeModeLabel and calls _showThemePicker), change the title text from the
hardcoded 'Dark Mode' to a neutral label like 'Theme' or 'Appearance' so it
accurately reflects Light/Dark/System choices; update any related
accessibility/semantics if present to match the new label.
In `@lib/screens/tasks/task_detail_screen.dart`:
- Around line 456-481: Several Text widgets in the task header (e.g., the title
Text using variable title, the description Text using variable description, and
the Text widgets that render creator/assignee names) use hardcoded Colors.white;
replace those color usages with Theme.of(context).colorScheme.onSurface to
respect light/dark themes, and remove the surrounding const on the TextStyle so
the theme expression can be evaluated (update the Text widgets that reference
title, description, creator/assignee names accordingly).
- Around line 155-163: The dialog's content Text in task_detail_screen.dart uses
a hardcoded Colors.white70 which can have poor contrast in light mode; update
the content Text in the AlertDialog (the widget rendering 'Are you sure you want
to delete this task?') to use a theme-aware color instead (e.g., derive color
from Theme.of(context).textTheme.bodyMedium?.color or
Theme.of(context).colorScheme.onSurface with appropriate opacity) so it adapts
to light/dark themes and preserves contrast.
In `@lib/screens/tickets/create_ticket_screen.dart`:
- Around line 195-204: The description TextFormField in
create_ticket_screen.dart hardcodes TextStyle color to Colors.white (style
property) causing poor visibility in non-dark themes; update the style to use
the theme-aware text color (e.g., Theme.of(context).colorScheme.onSurface or
Theme.of(context).textTheme.bodyMedium?.color) instead of Colors.white so the
field respects light/dark themes and matches the title field fix.
- Around line 355-356: The assignee dropdown currently hardcodes its text color
via "style: const TextStyle(color: Colors.white)"; replace that constant with a
theme-aware color (for example use Theme.of(context).colorScheme.onSurface or
the appropriate text color from Theme.of(context).textTheme like bodyMedium) so
the dropdown follows light/dark themes—update the "style" property in the
assignee dropdown widget to use Theme.of(context) instead of Colors.white
(mirror the change you made for the category dropdown).
- Around line 267-268: The dropdown's TextStyle currently hardcodes Colors.white
(in the category dropdown inside create_ticket_screen.dart), which breaks
visibility in light mode; change the TextStyle to use the current theme color
(e.g., replace const TextStyle(color: Colors.white) with TextStyle(color:
Theme.of(context).colorScheme.onSurface) or TextStyle(color:
Theme.of(context).textTheme.bodyText1?.color)) so the dropdown text adapts to
light/dark themes and uses the app's color scheme.
🧹 Nitpick comments (9)
lib/screens/tasks/create_task_screen.dart (2)
135-138: Consider using theme-aware AppBar color for consistency.The AppBar uses hardcoded
Colors.green.shade800while other parts of the screen use theme colors. For full theme consistency, consider using the theme's primary color or a color from the colorScheme.♻️ Suggested change
appBar: AppBar( title: const Text('Create New Task'), - backgroundColor: Colors.green.shade800, + backgroundColor: Theme.of(context).colorScheme.primary, ),
75-75: Date picker builder does nothing - consider removing or applying theme.The builder
(context, child) => child!is a no-op. Either remove it entirely, or use it to apply theme-specific styling to the date picker dialog.♻️ Options
Option 1: Remove the no-op builder
lastDate: DateTime.now().add(const Duration(days: 365)), - builder: (context, child) => child!, );Option 2: Apply theme wrapping if needed
- builder: (context, child) => child!, + builder: (context, child) => Theme( + data: Theme.of(context), + child: child!, + ),lib/screens/profile/team_members_screen.dart (1)
79-83: Consider using theme-based AppBar color for consistency.The scaffold background correctly uses
Theme.of(context).scaffoldBackgroundColor, but the AppBar still uses a hardcodedColors.green.shade800. For full theme consistency, consider usingTheme.of(context).colorScheme.surfaceor defining a semantic color in the theme.♻️ Optional: Theme-based AppBar
appBar: AppBar( title: Text('${widget.teamName} Members'), - backgroundColor: Colors.green.shade800, + backgroundColor: Theme.of(context).colorScheme.primary, ),lib/theme/app_themes.dart (1)
4-4: Consider caching theme instances for efficiency.Using getters recreates
ThemeDataobjects on each access. WhileMaterialAppcaches these, consider usingfinalvariables if the themes are accessed elsewhere frequently.♻️ Optional: Cache theme instances
-ThemeData get lightTheme { +final ThemeData lightTheme = _buildLightTheme(); + +ThemeData _buildLightTheme() { const surface = Color(0xFFFFFFFF); // ... rest of implementation } -ThemeData get darkTheme { +final ThemeData darkTheme = _buildDarkTheme(); + +ThemeData _buildDarkTheme() { const onSurfaceDark = Color(0xFFE8E8E8); // ... rest of implementation }Also applies to: 142-142
lib/screens/tickets/ticket_detail_screen.dart (2)
874-981: Incomplete theme migration in Comments section.The comments section still uses hardcoded colors that won't adapt properly to light mode:
- Line 877:
Colors.whitefor section header- Lines 893-910:
Colors.grey.shade400/600for empty state- Lines 952-956:
Colors.whitefor comment author name- Lines 963, 973:
Colors.grey.shade300/500for comment content/dateThese should use theme-based colors like other parts of this screen.
Suggested theme-aware colors
// Comments section Text( 'Comments (${_comments.length})', - style: const TextStyle( - color: Colors.white, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, fontSize: 18, fontWeight: FontWeight.bold, ), ),Apply similar changes to empty state text and comment card content using
colorScheme.onSurface,colorScheme.onSurfaceVariant, etc.
67-85: SnackBar colors remain hardcoded throughout.SnackBars use hardcoded
Colors.redandColors.greenfor error/success states. While this is common practice for semantic coloring, consider usingcolorScheme.errorfor errors to maintain theme consistency.lib/screens/home/dashboard_screen.dart (1)
729-948: Chart styling uses hardcoded greys.The analytics chart uses hardcoded
Colors.grey.shade800andColors.grey.shade400for grid lines and labels. These won't adapt well to light mode. Consider using theme-based colors for better theme support.Based on learnings, this can be deferred as the project prioritizes visual design consistency during development.
lib/screens/profile/edit_profile_screen.dart (1)
158-181: Avatar container uses hardcoded white.The avatar container at lines 163-164 uses hardcoded
Colors.whitefor background and border. In dark mode this should work fine, but in light mode the white border on white/light surface may not be visible.Consider using theme-aware colors or a contrasting border.
lib/screens/calendar/calendar_screen.dart (1)
93-128: Cache loading with proper fallback.The
_loadEventsWithCachemethod correctly:
- Clears existing events before loading
- Falls back to network if cache is invalid or fails
- Ensures loading state is reset in
finallyblockOne consideration: after a successful network load following cache failure (line 120), the events aren't saved to cache.
Consider saving to cache after fallback network load
} catch (e) { debugPrint('Error loading events with cache: $e'); // Fallback to network if cache fails await _loadEventsFromNetwork(); + await _saveEventsToCache(); } finally {
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (4)
lib/screens/tickets/create_ticket_screen.dart (2)
160-176:⚠️ Potential issue | 🟠 MajorFix title field text color for light theme readability.
Hardcoded white text will be unreadable on light surfaces. Use the theme's semantic color for text on surface backgrounds instead.
🛠️ Suggested change
- style: const TextStyle(color: Colors.white), + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + ),
132-142:⚠️ Potential issue | 🟡 MinorRemove hardcoded white color from AppBar spinner to ensure theme compatibility.
Using
Colors.whiteon an AppBar with a lightsurfacebackground may have insufficient contrast. However, the suggested implementation needs adjustment: the widget is markedconst, which prevents accessingTheme.of(context)at runtime. Either remove theconstkeyword from the widget tree to enable dynamic theming, or use a color that works reliably across light and dark themes.🛠️ Suggested fix approach
- const Padding( - padding: EdgeInsets.all(16), - child: SizedBox( - height: 24, - width: 24, - child: CircularProgressIndicator( - strokeWidth: 2, - color: Colors.white, + Padding( + padding: EdgeInsets.all(16), + child: SizedBox( + height: 24, + width: 24, + child: CircularProgressIndicator( + strokeWidth: 2, + color: Theme.of(context).colorScheme.onSurface, ), ),lib/screens/meetings/meeting_insights_screen.dart (1)
303-349:⚠️ Potential issue | 🟠 MajorTranscript text still uses hardcoded light-on-dark colors.
This will be low-contrast in light mode. Please switch speaker/body/empty-state text to theme colors.🎨 Suggested fix
- return Center( - child: Text( - 'No transcription available', - style: TextStyle(color: Colors.grey.shade400), - ), - ); + final muted = Theme.of(context).colorScheme.onSurfaceVariant; + return Center( + child: Text( + 'No transcription available', + style: TextStyle(color: muted), + ), + ); @@ - Text( - speaker, - style: const TextStyle( - color: Colors.white, fontWeight: FontWeight.bold), - ), + Text( + speaker, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurface, + fontWeight: FontWeight.bold), + ), @@ - Text( - text, - style: - const TextStyle(color: Colors.white70, height: 1.3), - ), + Text( + text, + style: TextStyle( + color: Theme.of(context).colorScheme.onSurfaceVariant, + height: 1.3), + ),lib/screens/profile/profile_screen.dart (1)
888-925:⚠️ Potential issue | 🟡 MinorPush Notifications toggle is non-functional.
The Switch is hardcoded tovalue: truewith an emptyonChangedhandler (lines 917-921). Wire it to actual preference state or disable it until implemented.
🤖 Fix all issues with AI agents
In `@lib/screens/profile/profile_screen.dart`:
- Around line 35-47: The teams fetch can leave stale data in _userTeams when
getUserTeams fails; before calling
_supabaseService.getUserTeams(profile['email']) or immediately when
teamsResponse['success'] != true (or teamsResponse['teams'] == null), reset
_userTeams = [] so the UI doesn't show stale teams; update the logic inside the
try block that calls getUserTeams in profile_screen.dart (referencing _userTeams
and getUserTeams) to clear the list on failure (or pre-clear it right before the
await).
🧹 Nitpick comments (2)
lib/screens/tickets/create_ticket_screen.dart (1)
97-101: Consider theme error colors for SnackBars.Align error UI with the app’s ColorScheme for consistency and contrast.
♻️ Suggested change
- ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Failed to create ticket: ${result['error']}'), - backgroundColor: Colors.red, - ), - ); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Failed to create ticket: ${result['error']}', + style: TextStyle( + color: Theme.of(context).colorScheme.onErrorContainer, + ), + ), + backgroundColor: + Theme.of(context).colorScheme.errorContainer, + ), + );- ScaffoldMessenger.of(context).showSnackBar( - SnackBar( - content: Text('Error creating ticket: $e'), - backgroundColor: Colors.red, - ), - ); + ScaffoldMessenger.of(context).showSnackBar( + SnackBar( + content: Text( + 'Error creating ticket: $e', + style: TextStyle( + color: Theme.of(context).colorScheme.onErrorContainer, + ), + ), + backgroundColor: + Theme.of(context).colorScheme.errorContainer, + ), + );Also applies to: 112-116
lib/screens/profile/profile_screen.dart (1)
675-703: Make dividers theme-aware for better light/dark contrast.
The hard-codedColors.greydividers can clash in dark mode. ConsidercolorScheme.outlineVariantfor consistency.♻️ Suggested change
- const Divider(color: Colors.grey), + Divider(color: colorScheme.outlineVariant), ... - const Divider(color: Colors.grey), + Divider(color: colorScheme.outlineVariant), ... - const Divider(color: Colors.grey), + Divider(color: colorScheme.outlineVariant), ... - const Divider(color: Colors.grey), + Divider(color: colorScheme.outlineVariant),
|
impressive |
|
@aniket866 Thanks for the comment |
Closes #62
📝 Description
This PR implements app-wide light/dark theme support for Ell-ena.
Previously, the app was hardcoded to dark mode and the theme toggle in the
Profile screen only affected that screen. This change introduces a global
theme mechanism so that theme changes apply consistently across the entire app.
🔧 Changes Made
ThemeData/ColorScheme📷 Screenshots or Visual Changes (if applicable)
WhatsApp.Video.2026-02-02.at.10.53.06.PM.mp4
🤝 Collaboration
Collaborated with:
@username(optional)✅ Checklist
Summary by CodeRabbit
New Features
Improvements
Bug Fixes