FINTECH & WALLET

Balance Hero

A polished wallet hero card inspired by modern finance apps. Combines a currency switcher chip, a prominent total-balance figure, and a row of quick-action tiles (Transfer, Top Up, Exchange, Pay Later) on top of a clean white surface. Drop it on your home screen and wire the actions to your routes.

fintechwalletdashboard
Preview4 variants
Total balance61.246.180
Transfer
Top Up
Exchange
Pay Later

Default (IDR)

IDR currency with 4 quick actions

Total balance4,128.50
Transfer
Top Up
Exchange
Pay Later

USD

Alternate currency chip

Total balance61.246.180
Transfer
Top Up
Exchange
Pay Later

Gradient

Brand-blue gradient background

Total balance61.246.180
Transfer
Top Up
Exchange
Pay Later

Compact

Without the More features row

What's Included

  • Default variant — IDR currency, 4 quick actions, More features footer
  • USD variant — alternate currency chip with flag and code
  • Gradient variant — vibrant brand-blue gradient background with light text
  • Compact variant — without the More features footer row
  • Reusable currency chip with flag + code + chevron
  • Quick-action grid using a generic data list for easy customization

Use Cases

  • Banking and wallet apps — hero card on the home/dashboard tab
  • Crypto wallets — show fiat balance with currency switcher
  • Remittance and FX apps — surface Transfer, Top Up, Exchange front-and-center
  • Loyalty and points apps — display point balance with quick redeem actions

PRO TIP

Reach for AnimatedSwitcher around the balance Text so balance updates fade-cross when you swap currency or refresh from the API. Render the formatted balance with NumberFormat.currency from the intl package so locale-aware separators (61.246.180 in id-ID vs 61,246,180 in en-US) come for free.

Complete Dart Code

Copy this into your Flutter project. No external packages required.

dart
import 'package:flutter/material.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        backgroundColor: Colors.white,
        body: Center(
          child: BalanceHero(),
        ),
      ),
    );
  }
}

/// Flutter Balance Hero Widget — All Variants
/// Wallet hero card with currency selector + quick actions.
/// rohansurve.in/flutterkit/balance-hero

enum BalanceHeroStyle { standard, gradient, compact }

class BalanceAction {
  final IconData icon;
  final String label;
  final VoidCallback? onTap;

  const BalanceAction({
    required this.icon,
    required this.label,
    this.onTap,
  });
}

class BalanceHero extends StatelessWidget {
  final BalanceHeroStyle style;
  final String currencyFlag;
  final String currencyCode;
  final String balanceLabel;
  final String balance;
  final List<BalanceAction> actions;
  final String moreLabel;
  final VoidCallback? onCurrencyTap;
  final VoidCallback? onMoreTap;

  const BalanceHero({
    super.key,
    this.style = BalanceHeroStyle.standard,
    this.currencyFlag = '🇮🇩',
    this.currencyCode = 'IDR',
    this.balanceLabel = 'Total balance',
    this.balance = '61.246.180',
    this.moreLabel = 'More features',
    this.onCurrencyTap,
    this.onMoreTap,
    this.actions = const [
      BalanceAction(icon: Icons.arrow_upward_rounded, label: 'Transfer'),
      BalanceAction(icon: Icons.arrow_downward_rounded, label: 'Top Up'),
      BalanceAction(icon: Icons.sync_alt_rounded, label: 'Exchange'),
      BalanceAction(icon: Icons.credit_card_rounded, label: 'Pay Later'),
    ],
  });

  static const _accent = Color(0xFF2563EB);
  static const _gray900 = Color(0xFF111827);
  static const _gray500 = Color(0xFF6B7280);
  static const _gray200 = Color(0xFFE5E7EB);
  static const _gray100 = Color(0xFFF3F4F6);

  bool get _isGradient => style == BalanceHeroStyle.gradient;

  Color get _textColor => _isGradient ? Colors.white : _gray900;
  Color get _mutedColor =>
      _isGradient ? Colors.white.withValues(alpha: 0.7) : _gray500;
  Color get _chipBg => _isGradient ? Colors.white.withValues(alpha: 0.15) : Colors.white;
  Color get _chipBorder => _isGradient ? Colors.transparent : _gray200;
  Color get _actionsBg => _isGradient ? Colors.white.withValues(alpha: 0.1) : Colors.white;
  Color get _actionsBorder => _isGradient ? Colors.transparent : _gray200;

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 340,
      padding: const EdgeInsets.all(20),
      decoration: BoxDecoration(
        color: _isGradient ? null : Colors.white,
        gradient: _isGradient
            ? const LinearGradient(
                begin: Alignment.topLeft,
                end: Alignment.bottomRight,
                colors: [Color(0xFF2563EB), Color(0xFF1E3A8A)],
              )
            : null,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: _isGradient ? Colors.transparent : _gray200),
        boxShadow: const [
          BoxShadow(
            color: Color(0x0A000000),
            blurRadius: 40,
          ),
        ],
      ),
      child: Column(
        mainAxisSize: MainAxisSize.min,
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          _buildHeader(),
          const SizedBox(height: 20),
          _buildActions(),
        ],
      ),
    );
  }

  Widget _buildHeader() {
    return Row(
      crossAxisAlignment: CrossAxisAlignment.end,
      children: [
        _currencyChip(),
        const Spacer(),
        Column(
          crossAxisAlignment: CrossAxisAlignment.end,
          children: [
            Text(balanceLabel,
                style: TextStyle(
                    fontSize: 12, color: _mutedColor)),
            const SizedBox(height: 4),
            Text(balance,
                style: TextStyle(
                    fontSize: 24,
                    fontWeight: FontWeight.w500,
                    color: _textColor)),
          ],
        ),
      ],
    );
  }

  Widget _currencyChip() {
    return InkWell(
      onTap: onCurrencyTap,
      borderRadius: BorderRadius.circular(80),
      child: Container(
        height: 36,
        padding: const EdgeInsets.fromLTRB(8, 0, 10, 0),
        decoration: BoxDecoration(
          color: _chipBg,
          borderRadius: BorderRadius.circular(80),
          border: Border.all(color: _chipBorder),
        ),
        child: Row(
          children: [
            Container(
              width: 20,
              height: 20,
              alignment: Alignment.center,
              decoration: BoxDecoration(
                color: _isGradient
                    ? Colors.white.withValues(alpha: 0.2)
                    : _gray100,
                borderRadius: BorderRadius.circular(80),
              ),
              child: Text(currencyFlag,
                  style: const TextStyle(fontSize: 12)),
            ),
            const SizedBox(width: 8),
            Text(currencyCode,
                style: TextStyle(
                    fontSize: 14,
                    fontWeight: FontWeight.w500,
                    color: _textColor)),
            const SizedBox(width: 8),
            Icon(Icons.keyboard_arrow_down_rounded,
                size: 16, color: _textColor),
          ],
        ),
      ),
    );
  }

  Widget _buildActions() {
    return Container(
      decoration: BoxDecoration(
        color: _actionsBg,
        borderRadius: BorderRadius.circular(16),
        border: Border.all(color: _actionsBorder),
      ),
      padding: const EdgeInsets.symmetric(horizontal: 16),
      child: Column(
        children: [
          Container(
            padding: const EdgeInsets.symmetric(vertical: 20),
            decoration: BoxDecoration(
              border: style == BalanceHeroStyle.compact
                  ? null
                  : Border(
                      bottom: BorderSide(
                          color: _isGradient
                              ? Colors.white.withValues(alpha: 0.15)
                              : _gray200)),
            ),
            child: Row(
              children: [
                for (final a in actions)
                  Expanded(
                    child: _actionItem(a),
                  ),
              ],
            ),
          ),
          if (style != BalanceHeroStyle.compact)
            InkWell(
              onTap: onMoreTap,
              child: Padding(
                padding: const EdgeInsets.symmetric(vertical: 12),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  children: [
                    Text(moreLabel,
                        style: TextStyle(
                            fontSize: 12,
                            fontWeight: FontWeight.w500,
                            color: _textColor)),
                    const SizedBox(width: 6),
                    Icon(Icons.keyboard_arrow_down_rounded,
                        size: 12, color: _textColor),
                  ],
                ),
              ),
            ),
        ],
      ),
    );
  }

  Widget _actionItem(BalanceAction a) {
    return InkWell(
      onTap: a.onTap,
      child: Column(
        children: [
          Container(
            width: 40,
            height: 40,
            decoration: BoxDecoration(
              color: _isGradient ? Colors.white : _accent,
              borderRadius: BorderRadius.circular(80),
            ),
            child: Icon(a.icon,
                color: _isGradient ? _accent : Colors.white, size: 20),
          ),
          const SizedBox(height: 8),
          Text(a.label,
              style: TextStyle(fontSize: 12, color: _textColor)),
        ],
      ),
    );
  }
}

// ─── USAGE ───────────────────────────────────────────────────────

// Default (IDR)
// BalanceHero()

// USD variant
// BalanceHero(
//   currencyFlag: '🇺🇸',
//   currencyCode: 'USD',
//   balance: '4,128.50',
// )

// Gradient background
// BalanceHero(
//   style: BalanceHeroStyle.gradient,
// )

// Compact (no More features)
// BalanceHero(
//   style: BalanceHeroStyle.compact,
// )

How to use this widget

  1. 1Copy the Dart code above
  2. 2Create a new file — balance_hero.dart in your Flutter project
  3. 3Use BalanceHero() — pass currencyCode, balance, and actions to customize. Switch styles via the style: BalanceHeroStyle.gradient parameter

Related components

Data Display

Stat Card

Dashboard metric cards in 4 styles — Simple, Icon, Trend, and Progress

View component →
Fintech & Wallet

Code Scan

Payment QR + barcode card with header, tabs, and security footer

View component →
Feedback & Alerts

Alert Widget

Dismissible alert banners in 5 styles — Info, Success, Warning, and Danger

View component →