Skip to content

Redesign home screen#447

Open
dewabisma wants to merge 21 commits intofeat/v3from
beast/redesign-home-screen
Open

Redesign home screen#447
dewabisma wants to merge 21 commits intofeat/v3from
beast/redesign-home-screen

Conversation

@dewabisma
Copy link
Copy Markdown
Collaborator

Summary

  • Update home screen the same look as latest figma
  • Move away from liquid glass button -> quantus button
  • Move away from liquid glass icon button -> quantus icon button
  • Updated font family to Geist and Geist Mono
  • Standardize base background
  • Removed unused colors
  • Updated send and receive icon
  • Updated tx item styling
  • Refactor tx item super nested ternary (I really don't like reading nested ternary)

Main idea for buttons and base background is to have stable filename that we can update as we update design so we don't have to modify many files, we just update the styling in respective icon button, button, or base background. I think this way we can be more efficient. Previously we have to always delete or copy or rename, there is friction doing this every design update.

About the symbol for Q or Qu or Quan, I just abritrary did it. Will update once Sejal designed it.

Screenshots

  • Homepage balance shown
image
  • Home balance hidden
image

- remove gradient background
- create base background widget, this will be the main background widget where we update background implementation so we don't have to ever touch other file and all change can be centralized here. Kinda painful everytime need to change background have to go through different files.
- Removed backgroundAlt theme color
- Removed unused glass circle icon button
- Update and rename glass icon button to quantus icon button, we don't want to always create new widget for every style change. Hence generic naming is needed.
- Added new button border color
- Rename button component to quantus button
- Update button styling
- Add success variant
- Updated styling for tx item
- Update color text
@n13
Copy link
Copy Markdown
Collaborator

n13 commented Apr 12, 2026

First comment: Lets either use QUAN trailing for now, or wait for Sejal design update, which won't take long

@dewabisma dewabisma changed the base branch from main to feat/v3 April 12, 2026 09:46
@n13
Copy link
Copy Markdown
Collaborator

n13 commented Apr 14, 2026

Here's my review of PR #447 - Redesign home screen.


Overview

This is a well-scoped design system refresh: rename "Glass" components to "Quantus", swap Inter for Geist fonts, update the color palette (pink -> orange, new success/checksum colors), replace the gradient background with a flat one, and clean up the tx item ternary nesting. 47 files changed, +366/-399 lines -- net reduction, which is great.

The rename was almost fully carried through and the overall direction is solid. A few issues worth addressing:


Issues

1. Incomplete method rename in swap_screen.dart

The class/import rename is done, but the private method name _smallGlassIconButton was left behind. It still internally uses QuantusButton, so it compiles fine, but it's inconsistent with the rest of the rename.

  Widget _smallGlassIconButton({required AppColorsV2 colors, required String iconAsset, VoidCallback? onTap}) {

Should be something like _smallIconButton.

2. Missing type annotation on variant parameter

In quantus_button.dart, _buildContent declares variant without a type:

  Widget _buildContent(BuildContext context, {variant = ButtonVariant.primary}) {

This means the switch expression needs a catch-all _ case (line 121) instead of being exhaustive. Should be {ButtonVariant variant = ButtonVariant.primary}. Also, ButtonVariant.success is missing from the switch -- its text color falls through to the default. If primary buttons get dark text (background color), success buttons probably should too, but this should be an explicit choice, not an accident.

3. Hardcoded colors in tx_item.dart

The ternary refactor is a big readability win, but the 8 hardcoded Color(0x...) values in getIconBg, getIconColor, and getBorderColor go against the centralization philosophy described in the PR. These should live in AppColorsV2 (e.g. txPendingSendBg, txPendingReceiveBorder, etc.) so they update in one place if the palette changes again.

    Color getIconBg() {
      if (isHighlighted && !isSend) {
        return const Color(0x14408C6B);
      }
      // ... more hardcoded colors
    }

4. Hardcoded Color(0xFF888888) in activity_section.dart

The "View All" link uses const Color(0xFF888888) twice (lines 215, 217). Same idea -- should be a theme color to stay consistent with the centralization approach.

            style: text.smallTitle?.copyWith(
              color: const Color(0xFF888888),
              decoration: TextDecoration.underline,
              decorationColor: const Color(0xFF888888),
              decorationStyle: TextDecorationStyle.dotted,

5. textTertiary changed from transparent-white to opaque gray

# Before: semi-transparent white -- adapts to any background
textTertiary: Color(0x52FFFFFF)

# After: solid dark gray -- only works on dark backgrounds
textTertiary: Color(0xFF3D3D3D)

This is a subtle but significant behavioral change. The old value (white @ 32% opacity) would naturally adapt if a background was ever non-black. The new value is a fixed gray that will be invisible on dark surfaces. Worth confirming this is intentional -- it affects timestamp text, tertiary labels, etc. across the whole app.


Minor / Nits

  • _fontFamilySecondary declared unused: Geist Mono is registered in pubspec.yaml and the constant is defined but immediately suppressed with // ignore: unused_element. Fine as prep work, but could be removed until actually needed to avoid lint noise.
  • LiquidGlassBase / InsetButtonContainer possibly orphaned: The PR removes their usage from buttons but doesn't delete the files. May want to check if they're used elsewhere -- if not, they're dead code now.
  • Font licensing: Geist fonts are SIL OFL which is fine for bundling, but consider adding a LICENSE file in assets/fonts/ for compliance clarity.

What looks great

  • The stable naming convention (QuantusButton / QuantusIconButton / BaseBackground) is a smart move -- decouples the component name from the visual style so future design updates don't require another mass rename.
  • Nested ternary refactor in tx_item.dart is much more readable.
  • Net code reduction despite adding new features.
  • The isActive state on QuantusIconButton is a clean addition for the visibility toggle.
  • Loading skeleton for balance is improved (shows two separate skeleton lines instead of one inline row).

@dewabisma
Copy link
Copy Markdown
Collaborator Author

Summary

  • Fixed review issues
  • Add Geist font license
  • Updated home screen to follow latest update from figma
  • Added currency fiat system

It's kind of weird to work with hardcoded currency conversion, so in this PR added the currency displaying system. Which we can easily extend and update later when we have real exchange rate.

Screenshots

  • Homepage default
Simulator Screenshot - iPhone 8 - 2026-04-15 at 19 27 10
  • Homepage flip currency
Simulator Screenshot - iPhone 8 - 2026-04-15 at 19 27 16
  • Homepage hide amount
Simulator Screenshot - iPhone 8 - 2026-04-15 at 19 27 24

@n13
Copy link
Copy Markdown
Collaborator

n13 commented Apr 17, 2026

Re-review of PR #447 — Redesign home screen

7 new commits since my last review. flutter analyze passes clean. Highlights below; previous review at PR #447 review thread.


Previous review follow-up

Resolved

  1. _smallGlassIconButton rename — fixed, now _smallIconButton in swap_screen.dart
  2. Missing variant type annotation — fixed in quantus_button.dart. ButtonVariant is now explicit and the switch is exhaustive (no more catch-all _); ButtonVariant.success correctly handled
  3. Hardcoded tx_item colors — moved into AppColorsV2 as txItemIncomingHighlightBg, txItemOutgoingHighlightBg, txItemOutgoingHighlight, txItemIconDefault, txItemIncomingHighlightBorder, txItemBorderDefault.
  4. Hardcoded Color(0xFF888888) in activity_section.dart — replaced with new colors.textMuted (also added to AppColorsV2)
  5. Geist font licensemobile-app/assets/fonts/LICENSE.txt added (SIL OFL 1.1)
  6. _fontFamilySecondary ignore lint — gone; renamed to public fontFamilySecondary and now actually used in tx_item.dart and the home screen for the QUAN symbol & secondary amount

Not addressed (confirm if intentional)

  • textTertiary still Color(0xFF3D3D3D) (opaque dark gray, not the prior 0x52FFFFFF). Fine for the dark-only theme today, but it'll be invisible on any non-dark surface. Worth a one-line note that this was intentional.
  • Dead files/assets:
    • mobile-app/lib/v2/components/inset_button_container.dart — only references itself
    • assets/v2/glass_circle_icon_button_bg.png — still listed in pubspec.yaml but no widget uses it after glass_circle_icon_button.dart was removed
    • assets/v2/glass_button_wide_340_bg.png — present in assets/v2/ but no longer referenced
    • (liquid_glass_base.dart is keptmnemonic_grid.dart still uses it. Good catch leaving that one.)

New code in latest commits

Currency display system (6af5a54, 7effe0f) — strong design overall

The architecture is clean: FiatCurrency enum → ExchangeRateService for rates → CurrencyDisplayState for resolved view → currencyDisplayProvider / txAmountFormatterProvider for widgets. Widgets contain zero conversion math. Adding a new currency really is a one-line enum + one-line rate. Settings persisted via SettingsService.

Issues

1. Silent fallback in ExchangeRateService.getRate violates project rule

  double getRate(FiatCurrency fiat) => _rates[fiat] ?? 1.0;

Per the "fail early — never have a silent failure, never add fallback code for unexpected failures" rule, this ?? 1.0 should throw or assert. Since _rates is keyed by the closed FiatCurrency enum, the fallback is unreachable today — but if a new currency case is added without a matching rate, you'd silently render USD numbers labeled as the new currency. Better:

double getRate(FiatCurrency fiat) {
  final rate = _rates[fiat];
  if (rate == null) throw StateError('No rate for ${fiat.code}');
  return rate;
}

2. Precision loss in _toFiatNumeric

String _toFiatNumeric(BigInt rawBalance, FiatCurrency fiat, ExchangeRateService xRate) {
  final scaleFactorDouble = BigInt.from(10).pow(AppConstants.decimals).toDouble();
  final quanDouble = rawBalance.toDouble() / scaleFactorDouble;
  return xRate.convert(quanDouble, fiat).toStringAsFixed(2);
}

BigInt.toDouble() quietly loses precision past 2^53. NumberFormattingService.formatBalance already uses Decimal for the same conversion — this should match, otherwise the QUAN amount and the fiat amount can disagree on the lower-order digits for big balances.

3. DRY violation — hidden-balance literal duplicated

'- - - - -' appears in both currencyDisplayProvider (in the if (isHidden) branch) and txAmountFormatterProvider (returned directly). One named constant (e.g. kHiddenAmountPlaceholder) would keep them in sync.

4. Dead SymbolPosition.suffix branch

All five enum cases use prefix. The suffix branches in format / formatSigned are unreachable today. Fine as forward-looking design, but per the same "no fallback for unexpected" thinking, you might want to drop it until a real suffix currency lands.

5. _circleIconButton wrapper is dead weight in home_screen.dart

  Widget _circleIconButton({
    required IconData icon,
    required AppColorsV2 colors,
    required VoidCallback onTap,
    bool isActive = false,
  }) {
    return QuantusIconButton.circular(icon: icon, onTap: onTap, isActive: isActive);
  }

It takes a colors param it doesn't use, and otherwise just forwards to QuantusIconButton.circular. Inline the calls.

Migration dialog (a9f2870)

Trivial rename GlassButtonQuantusButton. This should have ridden along with the original rename commit, but easy to miss across packages.


What I really like

  • CurrencyDisplayState — clean separation of resolved presentation state from raw data. Widgets render strings, never compute.
  • txAmountFormatterProvider returning a function with all dependencies baked in is a nice pattern — avoids re-watching the same providers in every tx item.
  • TxItemData.amount is now BigInt (raw) instead of pre-formatted string — formatting moved to the provider. Consistent with the new architecture.
  • Loading skeleton for balance stacks two boxes (200×36 + 100×18) — much closer to the final layout than the old single inline skeleton.
  • isBalanceHidden plumbing removed from tx_item.dart — centralized in the formatter provider. Net DRY win.
  • Forward-looking docs in fiat_currency.dart ("Adding a new currency only requires...") — the kind of comment future-you will thank present-you for.

Verdict

Solid follow-up. All five flagged review issues are addressed cleanly, the currency system is a genuinely nice piece of architecture, and flutter analyze is green. The remaining items are small: one rule violation (?? 1.0 silent fallback), one precision concern (BigInt.toDouble), the duplicated '- - - - -' literal, the dead wrapper _circleIconButton, and the dead asset/file cleanup. Nothing blocking.

- remove unused glass button assets
- fix DRY violation
- fix precision loss in convert fiat
- remove silent fallback
@n13
Copy link
Copy Markdown
Collaborator

n13 commented Apr 20, 2026

Re-review of PR #447 — Redesign home screen

3 new commits since my last review (53238bf, 71a1999, 9601a80). Ran the same checks (dart format --line-length=120 --set-exit-if-changed as CI does, plus code read) — nothing else landed.

Previous review follow-up — all five resolved

  1. Silent ?? 1.0 fallback in ExchangeRateService.getRate — fixed cleanly:

    Decimal getRate(FiatCurrency fiat) {
      final rate = _rates[fiat];
      if (rate == null) throw StateError('No rate for ${fiat.code}');
      return rate;
    }

    Honors the project's "fail early, no silent fallback" rule.

  2. Precision loss in _toFiatNumeric — fixed properly, and then some. The whole conversion pipeline was migrated from double to Decimal:

    • _rates map values are now Decimal
    • ExchangeRateService.convert returns Decimal
    • _toFiatNumeric does Decimal.fromBigInt(rawBalance) / Decimal.fromBigInt(scaleFactor) → no toDouble() anywhere on raw balances

    Division by a power of 10 is always a terminating decimal, so .toDecimal() is safe.

  3. DRY - - - - - literal — fixed: file-level _hiddenAmountText now shared between currencyDisplayProvider and txAmountFormatterProvider.

  4. Dead _circleIconButton wrapper in home_screen.dart — fixed: both callsites now go directly to QuantusIconButton.circular(...), and _buildTopBar's unused active/colors params went away with it. Nice tidy-up.

  5. Dead assets — fixed in two commits:

    • inset_button_container.dart, glass_circle_icon_button_bg.png, glass_button_wide_340_bg.png deleted (71a1999)
    • Asset declaration also removed from pubspec.yaml (9601a80) — good follow-up.

Not addressed (explicitly optional last round)

  • textTertiary behavioral change (semi-transparent white → opaque gray) — no doc note added, but this is a dark-only theme today so functionally fine.
  • Dead SymbolPosition.suffix branches in format/formatSigned — still there, still unused. Fine as forward-looking API.

Checks

  • dart format . --line-length=120 --set-exit-if-changed on mobile-app/lib/clean, 0 files changed
  • Code read — no regressions introduced by the fixes

Tiny nits (non-blocking)

  • final _hiddenAmountText = '- - - - -'; could be const since it's a compile-time constant.

Verdict

Approve. All previously-flagged blocking issues are resolved cleanly, the precision fix went beyond what I asked for (full Decimal pipeline), the dead-code cleanup is thorough, and format is green. Ready to merge to feat/v3.

Copy link
Copy Markdown
Collaborator

@n13 n13 left a comment

Choose a reason for hiding this comment

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

GTG!

///
/// QUAN itself is not listed here — it is always the native side.
/// Adding a new currency only requires a new enum case here and a matching
/// rate in [ExchangeRateService]. No widget or provider changes are needed.
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Could you add MYR here - that's actually what we need for the Cafe test!

The currency symbol for the Malaysian Ringgit is RM. It is often placed directly before the numerical amount without a space (e.g., RM50). Internationally, the currency is represented by the ISO 4217 code MYR.

/// respecting the current hidden state, flip state, and selected fiat.
///
/// Usage:
/// final formatted = ref.watch(txAmountFormatterProvider)(amount, isSend: true);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Not sure we ever want to show transaction items as fiat currency?!


/// Fixed rates: 1 QUAN in each fiat currency.
/// When a live price feed is integrated, populate this map from the API.
static final Map<FiatCurrency, Decimal> _rates = {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please populate these with the current exchange rates - they don't change that much anyway, it's good enough

1 doesn't allow any testing and hides bugs

Copy link
Copy Markdown
Collaborator

@n13 n13 left a comment

Choose a reason for hiding this comment

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

Made some comments, please address these

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.

2 participants