Skip to content

Conversation

@erskingardner
Copy link
Member

@erskingardner erskingardner commented Jan 23, 2026

Summary

  • Add WnIcon widget with WnIcons enum for type-safe icon usage
  • Replace all Icons.xxx with WnIcon(WnIcons.xxx, ...) across codebase
  • Add 70+ new SVG icons to assets/svgs/
  • Update WnWarningBox to accept WnIcons instead of IconData
  • Refactor _SettingsTile to use WnIcons for consistency

Test plan

  • flutter analyze - no issues
  • flutter test - all tests pass
  • dart format - all files formatted
  • Manual verification of icons in app

Closes #14

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Style

    • Rolled out a unified custom icon system for consistent visuals, sizing, and theming across the app.
  • UI

    • Replaced numerous navigation, action, and tile icons (back, close, send, search, copy, visibility, info, add, etc.) for a cohesive look; refreshed chat button asset and settings entries now use the centralized icon set.
  • Tests

    • Added and updated tests to validate the new icon components and interactive icon state behavior.

✏️ Tip: You can customize this high-level summary in your review settings.

- Add WnIcon widget with WnIcons enum for type-safe icon usage
- Replace all Icons.xxx with WnIcon(WnIcons.xxx, ...) across codebase
- Add 70+ new SVG icons to assets/svgs/
- Update WnWarningBox to accept WnIcons instead of IconData
- Refactor _SettingsTile to use WnIcons for consistency
- Update test files to use WnIcon-based finders

Closes #14

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link

coderabbitai bot commented Jan 23, 2026

Walkthrough

Adds a new WnIcon widget and WnIcons enum and migrates the codebase from Material Icons / inline SVG usage to the new SVG-based WnIcon system across many screens, widgets, and tests. Adjusts some private widget constructors to accept WnIcons instead of svg path or IconData.

Changes

Cohort / File(s) Summary
New icon system
lib/widgets/wn_icon.dart
Adds WnIcons enum (filename + path getter) and WnIcon StatelessWidget that renders assets/svgs/<filename>.svg with default size (24.w) and optional color via ColorFilter.
Screen icon replacements
lib/screens/...
lib/screens/chat_screen.dart, lib/screens/login_screen.dart, lib/screens/signup_screen.dart, lib/screens/onboarding_screen.dart, lib/screens/developer_settings_screen.dart, lib/screens/network_screen.dart, lib/screens/share_profile_screen.dart, lib/screens/edit_profile_screen.dart
Replaced Icon() usages with WnIcon(WnIcons.*) and added wn_icon.dart imports. No control-flow or navigation changes.
Settings screen refactor
lib/screens/settings_screen.dart
Replaced per-tile SvgPicture.asset with WnIcon; changed _SettingsTile constructor/field from svgPath: Stringicon: WnIcons (constructor signature changed). Updated all tile invocations.
Widget icon migrations
lib/widgets/...
lib/widgets/wn_chat_header.dart, lib/widgets/wn_screen_header.dart, lib/widgets/wn_search_field.dart, lib/widgets/wn_warning_box.dart, lib/widgets/wn_copyable_field.dart, lib/widgets/wn_dropdown_selector.dart, lib/widgets/wn_message_menu.dart, lib/widgets/wn_relay_tile.dart, lib/widgets/wn_text_form_field.dart, lib/widgets/wn_account_bar.dart
Replaced Icon() and some inline SvgPicture uses with WnIcon(WnIcons.*); updated field/constructor types where needed (e.g., WnWarningBox now accepts WnIcons). Updated one asset path: assets/svgs/chat-add.svgassets/svgs/new_chat.svg.
Tests updated / new tests
test/screens/*, test/widgets/*, test/widgets/wn_icon_test.dart, test/theme_test.dart
Tests updated to use key-based selectors and assert on WnIcon/WnIcons instead of Material Icons. Adds a comprehensive wn_icon test suite and many theme/color tests.
Manifest
pubspec.yaml
Updated (likely to register new/renamed SVG assets referenced by WnIcons).

Sequence Diagram(s)

(omitted — changes are icon/widget replacements without multi-component control-flow requiring a sequence diagram)

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • untreu2
  • josefinalliende
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: replacing Material icons with custom SVG icons throughout the codebase.
Linked Issues check ✅ Passed The PR fully addresses issue #14 by replacing all Material icons with custom SVG icons via WnIcon widget and providing a unified, type-safe API.
Out of Scope Changes check ✅ Passed All changes are focused on icon replacement. The addition of comprehensive tests (wn_icon_test.dart, theme_test.dart) and test updates align with the scope of validating the new icon system.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions
Copy link

➡️ Coverage: 99.62% → 99.62% (0.00%)

1 similar comment
@github-actions
Copy link

➡️ Coverage: 99.62% → 99.62% (0.00%)

- Test WnIcons enum path generation
- Test unique filenames across all icons
- Test widget rendering, sizing, and color filtering

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
lib/widgets/wn_account_bar.dart (1)

36-49: Use WnIcon here to keep the icon system centralized.

This button still renders the SVG directly; swapping to WnIcon(WnIcons.newChat) keeps the icon system consistent and removes duplicated color-filter logic.

♻️ Proposed refactor
-import 'package:flutter_svg/flutter_svg.dart';
+import 'package:sloth/widgets/wn_icon.dart';
...
-          icon: SvgPicture.asset(
-            'assets/svgs/new_chat.svg',
-            width: 24.w,
-            height: 24.w,
-            colorFilter: ColorFilter.mode(
-              colors.backgroundContentPrimary,
-              BlendMode.srcIn,
-            ),
-          ),
+          icon: WnIcon(
+            WnIcons.newChat,
+            size: 24.w,
+            color: colors.backgroundContentPrimary,
+          ),
lib/widgets/wn_chat_header.dart (2)

30-36: Remove tooltip from IconButton.

The back button icon migration to WnIcon(WnIcons.chevronLeft) is correct. However, based on learnings, tooltips should not be used for UI elements in this project. The tooltip: 'Back' on line 35 should be removed.

Proposed fix
         IconButton(
           key: const Key('back_button'),
           onPressed: onBack,
           icon: WnIcon(
             WnIcons.chevronLeft,
             color: colors.backgroundContentTertiary,
             size: 28.w,
           ),
-          tooltip: 'Back',
         ),

55-65: TODO acknowledged; also remove tooltip from menu button.

The TODO comment for the pending SVG icon is appropriate. However, the tooltip: 'Menu' on line 64 should also be removed per the project guideline against tooltips.

Proposed fix
         // TODO: Replace with SVG icon when more_horizontal.svg is available
         IconButton(
           key: const Key('menu_button'),
           onPressed: onMenuTap,
           icon: Icon(
             Icons.more_horiz,
             color: colors.backgroundContentTertiary,
             size: 24.w,
           ),
-          tooltip: 'Menu',
         ),
🤖 Fix all issues with AI agents
In `@lib/screens/chat_screen.dart`:
- Around line 238-242: The icon inside the fixed-size send button is not
centered because WnIcon (WnIcons.arrowUp) is placed directly in the 44.w×44.h
Container; wrap the WnIcon in a Center widget (the same pattern used in
wn_text_form_field.dart paste button) so the SvgPicture returned by WnIcon is
horizontally and vertically centered within the button container.

In `@lib/screens/settings_screen.dart`:
- Around line 144-149: The _SettingsTile constructor lacks a Key parameter;
update the const _SettingsTile({...}) constructor to accept an optional Key
(e.g. Key? key) and forward it to the superclass via super.key to enable widget
testing with find.byKey; update any instantiations of _SettingsTile if necessary
to pass keys for tests (refer to class name _SettingsTile and its constructor
parameters icon, label, onTap).

In `@lib/widgets/wn_icon.dart`:
- Around line 101-109: The build method in wn_icon.dart currently only applies a
ColorFilter when the local color is set, losing IconTheme behavior; update the
color selection to fall back to IconTheme.of(context).color (i.e., use final
resolvedColor = color ?? IconTheme.of(context).color) and then pass colorFilter:
resolvedColor != null ? ColorFilter.mode(resolvedColor, BlendMode.srcIn) : null
when creating the SvgPicture.asset (refer to build, size, icon.path, and color
variables).

In `@test/screens/edit_profile_screen_test.dart`:
- Around line 133-134: Replace the fragile finder that uses
find.byType(WnIcon).first with a key-based finder: add a Key (e.g.,
Key('close_button')) to the close button widget in the UI code, then in this
test replace the closeIcon assignment to use find.byKey(const
Key('close_button')) and tap that; reference the WnIcon close button instance to
add the key and update this test (and mirror the same change you applied in
profile_keys_screen_test.dart).

In `@test/screens/profile_keys_screen_test.dart`:
- Around line 89-90: The test is using a fragile selector
find.byType(WnIcon).first for the close button; add a stable key to the close
button widget (assign Key('close_button') to the WnIcon instance used as the
close button in the screen code) and update the test to use find.byKey(const
Key('close_button')) (replace the closeIcon finder and the tester.tap call
accordingly) so the test targets the explicit close_button key instead of the
first WnIcon.

In `@test/screens/sign_out_screen_test.dart`:
- Around line 146-147: The test is using a fragile selector
(find.byType(WnIcon).first) to tap the close icon; add a stable Key to the close
button widget (e.g., Key('sign_out_close_button')) in the UI code where the
WnIcon close control is created, then update the test to locate it via
find.byKey(const Key('sign_out_close_button')) instead of
find.byType(WnIcon).first (replace the closeIcon variable and the tester.tap
call accordingly).

Comment on lines 144 to 149
class _SettingsTile extends StatelessWidget {
const _SettingsTile({
required this.svgPath,
required this.icon,
required this.label,
required this.onTap,
});
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Consider adding super.key for testability.

The constructor lacks key support. While this private widget can be tested via label text, adding key support would improve consistency with the project's testing approach.

🔧 Suggested improvement
 class _SettingsTile extends StatelessWidget {
   const _SettingsTile({
+    super.key,
     required this.icon,
     required this.label,
     required this.onTap,
   });

Based on learnings, the project prefers using find.byKey() in tests.

🤖 Prompt for AI Agents
In `@lib/screens/settings_screen.dart` around lines 144 - 149, The _SettingsTile
constructor lacks a Key parameter; update the const _SettingsTile({...})
constructor to accept an optional Key (e.g. Key? key) and forward it to the
superclass via super.key to enable widget testing with find.byKey; update any
instantiations of _SettingsTile if necessary to pass keys for tests (refer to
class name _SettingsTile and its constructor parameters icon, label, onTap).

@erskingardner erskingardner requested review from josefinalliende and untreu2 and removed request for josefinalliende January 23, 2026 10:34
@github-actions
Copy link

➡️ Coverage: 99.62% → 99.62% (0.00%)

… test finders

- Wrap send button WnIcon in Center widget for proper positioning
- Add IconTheme.of(context).color fallback for theme-aware icon coloring
- Update test files to use key-based finders instead of fragile type finders
- Add tests for IconTheme behavior in WnIcon widget

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@github-actions
Copy link

➡️ Coverage: 99.62% → 99.62% (0.00%)

Apply disabled-aware color to copy icon so it dims when npub is null,
matching standard IconButton behavior.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@test/screens/profile_keys_screen_test.dart`:
- Around line 187-210: The test currently inspects all WnIcon widgets which can
yield false positives; narrow the assertions to the icon inside the visibility
toggle: use the existing visibilityToggle Finder (Key 'visibility_toggle') and
locate its descendant WnIcon (e.g., via find.descendant or
find.byWidgetPredicate scoped to visibilityToggle), then read the icon value on
that specific WnIcon and assert equality with WnIcons.view and WnIcons.viewOff
during the two toggle states (use the widget obtained from tester.widget or
.evaluate().single.widget cast to WnIcon).

In `@test/widgets/wn_icon_test.dart`:
- Around line 99-105: The test name is misleading: it says “has no color filter”
but only checks rendering; update the test to either assert the absence of a
color filter on the SvgPicture or rename the test to reflect its actual check.
For a minimal change, rename the test registered by testWidgets to something
like "renders when neither color nor IconTheme is set" and keep the existing
verification that WnIcon(WnIcons.warning) mounts and findsOneWidget of
SvgPicture; alternatively, if you prefer to assert behavior, add an assertion
that the found SvgPicture's color filter (or equivalent property) is null/absent
after mounting WnIcon to confirm no color filtering is applied.

@github-actions
Copy link

❌ Coverage: 99.62% → 99.59% (-0.03%)

- WnIcon: size edge cases, color variations, nested IconTheme inheritance
- Theme: SemanticColors copyWith/lerp, AccentColorSet operations
- SemanticAccentColors: all 12 colors, lerp transitions
- BuildContext extension: light/dark themes, fallback behavior
- Profile keys test: scope visibility icon finder with find.descendant
- wn_icon test: rename misleading test name

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/widgets/wn_icon_test.dart`:
- Around line 201-202: The variables parentIcon and childIcon in the test are
fetched via tester.widget<WnIcon>(find.byKey(...)) but never used except to
assert their .icon properties later; remove these unused local variables and
instead perform the icon assertions inline (e.g.,
assertEquals(tester.widget<WnIcon>(find.byKey(const Key('parent_icon'))).icon,
...)) or delete the fetch lines if the key-based find already suffices; update
references to parentIcon/childIcon to use inline calls to
tester.widget<WnIcon>(find.byKey(...)) or remove them entirely.

Comment on lines +201 to +202
final parentIcon = tester.widget<WnIcon>(find.byKey(const Key('parent_icon')));
final childIcon = tester.widget<WnIcon>(find.byKey(const Key('child_icon')));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Unused variables parentIcon and childIcon.

These variables are fetched but only their .icon property is asserted (lines 218-219). The assertions could be done inline without storing the intermediate variables, or these lines could be removed if .icon checks are sufficient given the key-based lookup already confirms identity.

♻️ Optional cleanup
-      final parentIcon = tester.widget<WnIcon>(find.byKey(const Key('parent_icon')));
-      final childIcon = tester.widget<WnIcon>(find.byKey(const Key('child_icon')));
-
       // Find SVG pictures by their parent WnIcon keys
       final parentSvg = tester.widget<SvgPicture>(
         find.descendant(
@@ -215,8 +212,8 @@
         ),
       );

-      expect(parentIcon.icon, WnIcons.warning);
-      expect(childIcon.icon, WnIcons.search);
+      expect(tester.widget<WnIcon>(find.byKey(const Key('parent_icon'))).icon, WnIcons.warning);
+      expect(tester.widget<WnIcon>(find.byKey(const Key('child_icon'))).icon, WnIcons.search);
       expect(
         parentSvg.colorFilter,
         const ColorFilter.mode(parentColor, BlendMode.srcIn),
🤖 Prompt for AI Agents
In `@test/widgets/wn_icon_test.dart` around lines 201 - 202, The variables
parentIcon and childIcon in the test are fetched via
tester.widget<WnIcon>(find.byKey(...)) but never used except to assert their
.icon properties later; remove these unused local variables and instead perform
the icon assertions inline (e.g.,
assertEquals(tester.widget<WnIcon>(find.byKey(const Key('parent_icon'))).icon,
...)) or delete the fetch lines if the key-based find already suffices; update
references to parentIcon/childIcon to use inline calls to
tester.widget<WnIcon>(find.byKey(...)) or remove them entirely.

@github-actions
Copy link

❌ Coverage: 99.62% → 99.59% (-0.03%)

@github-actions
Copy link

❌ Coverage: 99.62% → 99.59% (-0.03%)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@test/widgets/wn_icon_test.dart`:
- Around line 253-286: The orphaned SVG detection fails on Windows because
svgFiles uses f.path.split('/').last; update the test to import package:path
(e.g., import 'package:path/path.dart' as p) and replace the split('/') call
with p.basename(f.path).replaceAll('.svg', '') so paths are normalized
cross-platform, and add path: ^1.9.0 to dev_dependencies in pubspec.yaml; modify
the test group around WnIcons.values/svgFiles/orphanedFiles to use p.basename to
generate enumFilenames comparison.

Copy link
Collaborator

@josefinalliende josefinalliende left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM!

Tried it in my iPhone and all screens look good 👍🏻

@erskingardner erskingardner merged commit 3a91466 into master Jan 23, 2026
3 of 4 checks passed
@erskingardner erskingardner deleted the svg-icons branch January 23, 2026 14:18
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Move all icons to svg

3 participants