fluxy 0.1.3 copy "fluxy: ^0.1.3" to clipboard
fluxy: ^0.1.3 copied to clipboard

A complete Flutter platform unifying reactive state, declarative UI, styling, animation, routing, and tooling. Build production-grade apps faster and at scale.

example/lib/main.dart

// ignore_for_file: prefer_const_constructors, prefer_const_literals_to_create_immutables
import 'package:flutter/material.dart';
import 'package:fluxy/fluxy.dart';

// --- Global Reactive State (Fintech Design Hub) ---
final balance = flux(42840.50);
final income = flux(15400.00);
final expense = flux(6200.00);

final transactions = flux(<Map<String, dynamic>>[
  {
    'title': 'Amazon India',
    'subtitle': 'Electronics & Gadgets',
    'amount': -12499.00,
    'icon': Icons.shopping_bag_rounded,
    'color': const Color(0xFFFF9900)
  },
  {
    'title': 'HDFC Salary Deposit',
    'subtitle': 'Monthly Payout',
    'amount': 85000.00,
    'icon': Icons.account_balance_wallet_rounded,
    'color': const Color(0xFF10B981)
  },
  {
    'title': 'Zomato UX',
    'subtitle': 'Dining & Delivery',
    'amount': -840.50,
    'icon': Icons.fastfood_rounded,
    'color': const Color(0xFFEF4444)
  },
  {
    'title': 'Netflix Premium',
    'subtitle': 'Entertainment',
    'amount': -649.00,
    'icon': Icons.play_circle_fill,
    'color': const Color(0xFFE50914)
  },
]);

final filter = flux("All");

void main() => runApp(const MaterialApp(
      debugShowCheckedModeBanner: false,
      home: FluxyFintechApp(),
    ));

class FluxyFintechApp extends StatefulWidget {
  const FluxyFintechApp({super.key});

  @override
  State<FluxyFintechApp> createState() => _FluxyFintechAppState();
}

class _FluxyFintechAppState extends State<FluxyFintechApp> {
  int _activePage = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      backgroundColor: const Color(0xFFF8FAFC),
      body: _buildPage(),
      bottomNavigationBar: _buildPremiumNav(),
    );
  }

  Widget _buildPage() {
    switch (_activePage) {
      case 0:
        return const WalletDashboard();
      case 1:
        return const PortfolioStats();
      default:
        return const WalletDashboard();
    }
  }

  Widget _buildPremiumNav() {
    return FxBottomBar(
      currentIndex: _activePage,
      onTap: (i) => setState(() => _activePage = i),
      activeColor: const Color(0xFF6366F1),
      baseColor: const Color(0xFF94A3B8),
      items: const [
        FxBottomBarItem(icon: Icons.grid_view_rounded, label: "Home"),
        FxBottomBarItem(icon: Icons.auto_graph_rounded, label: "Stats"),
        FxBottomBarItem(icon: Icons.account_balance_wallet_rounded, label: "Wallet"),
        FxBottomBarItem(icon: Icons.person_rounded, label: "Profile"),
      ],
    );
  }
}

// --- SCREEN 1: WALLET DASHBOARD ---
class WalletDashboard extends StatelessWidget {
  const WalletDashboard({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SingleChildScrollView(
        padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 20),
        child: Column(
          children: [
            // Branding & Profile Header
            Fx.row(
              children: [
                Fx.column(
                  gap: 4,
                  children: [
                    Fx.text("Welcome back,").fontSize(14).color(const Color(0xFF64748B)).weight(FontWeight.w500),
                    Fx.text("Alex Riviera").fontSize(26).bold().color(const Color(0xFF0F172A)),
                  ],
                ).expand(),
                Fx.avatar(
                  fallback: "AR",
                  size: FxAvatarSize.lg,
                  shape: FxAvatarShape.rounded,
                  onTap: () => Fx.toast(context, "Profile tapped"),
                ).shadow(
                  color: const Color(0xFF6366F1).withValues(alpha: 0.3),
                  blur: 20
                ),
              ],
            ),
            const SizedBox(height: 36),

            // Main Balance Gradient Card
            Fx.box(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Fx.row(
                    children: [
                      const Text("AVAILABLE BALANCE", style: TextStyle(fontSize: 11, fontWeight: FontWeight.bold, letterSpacing: 1.5, color: Color(0xFF94A3B8))),
                      const Spacer(),
                      Fx.box(
                        child: const Icon(Icons.nfc_rounded, color: Colors.white, size: 20)
                      ).size(36, 36).background(Colors.white.withValues(alpha: 0.1)).borderRadius(10).center(),
                    ],
                  ),
                  const SizedBox(height: 12),
                  Fx.text(() => "₹ ${balance.value.toStringAsFixed(2)}")
                      .fontSize(38) // Reduced from 44
                      .bold()
                      .color(Colors.white),
                  const SizedBox(height: 24),
                  Fx.row(
                    gap: 20, // Reduced from 24
                    children: [
                      _miniStat("Income", "₹ ${income.value.toInt()}", const Color(0xFF10B981)),
                      _miniStat("Expense", "₹ ${expense.value.toInt()}", const Color(0xFFEF4444)),
                    ],
                  ),
                ],
              ),
            )
            .background(const Color(0xFF1E293B))
            .borderRadius(36)
            .padding(24) // Reduced from 32
            .shadow(color: const Color(0xFF000000).withValues(alpha: 0.15), blur: 40),
            
            const SizedBox(height: 36),

            // Quick Actions Grid-like row
            Fx.row(
              style: const FxStyle(justifyContent: MainAxisAlignment.spaceBetween),
              children: [
                _actionCircle(Icons.send_rounded, "Send", const Color(0xFF6366F1)),
                _actionCircle(Icons.qr_code_scanner_rounded, "Scan", const Color(0xFFF59E0B)),
                _actionCircle(Icons.account_balance_rounded, "Bank", const Color(0xFF10B981)),
                _actionCircle(Icons.more_horiz_rounded, "More", const Color(0xFF64748B)),
              ],
            ),

            const SizedBox(height: 24),
            // Example Button as requested
            Fx.button("Add New Card", onTap: () {})
                .fullWidth()
                .sizeLg()
                .shadowMedium(),

            const SizedBox(height: 48),

            // Transactions Header with Dropdown
            Fx.row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Fx.text("Recent Transactions").fontSize(20).bold().color(const Color(0xFF0F172A)),
                // Reactive Dropdown: Automatically updates 'filter' signal
                Fx.dropdown<String>(
                  signal: filter, // Pass the signal directly
                  items: ["All", "Credit", "Debit"],
                  itemLabel: (s) => s,
                ).w(100),
              ],
            ),
            const SizedBox(height: 24),
            
            Fx.column(
              gap: 16,
              children: transactions.value.map((tx) => 
                _transactionTile(tx['title'], tx['subtitle'], tx['amount'], tx['icon'], tx['color'])
              ).toList(),
            ),
            
            const SizedBox(height: 48),
            Fx.text("Quick Actions").fontSize(18).bold().color(Colors.grey[800]!),
            const SizedBox(height: 16),
            
            // Redesigned Action Row
            Fx.row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                _actionButton(
                  Icons.notifications_active_outlined, 
                  "Toast", 
                  () => Fx.toast(context, "Fluxy toast message!"),
                  badgeCount: 3
                ),
                _actionButton(Icons.window_outlined, "Modal", () => Fx.modal(context, 
                    child: Fx.box(
                        child: Fx.column(children: [
                            Fx.text("Fluxy Modal").bold().fontSize(18),
                            const SizedBox(height: 12),
                            Fx.text("Declarative & Simple.").textCenter().color(Colors.grey),
                            const SizedBox(height: 24),
                            Fx.button("Close", onTap: () => Navigator.pop(context)).fullWidth(),
                        ]).pack()
                    ).p(24).backgroundWhite().rounded(24).w(300)
                )),
                _actionButton(Icons.vertical_align_bottom_outlined, "Sheet", () => Fx.bottomSheet(context, 
                    child: Fx.box(
                      child: Fx.column(children: [
                          Fx.box().w(40).h(4).bg(Colors.grey[300]!).roundedFull(),
                          const SizedBox(height: 24),
                          Fx.text("Bottom Sheet").bold().fontSize(20),
                          const SizedBox(height: 12),
                          Fx.text("Swipe down to close.").color(Colors.grey),
                          const SizedBox(height: 40),
                          Fx.button("Got it", onTap: () => Navigator.pop(context)).fullWidth(),
                      ]).pack()
                    ).p(24).h(300).wFull()
                )),
              ],
            ).p(16).bg(Colors.white).rounded(20).shadowMedium(),
            
            const SizedBox(height: 32),
            Fx.button("View Full Layout Demo", onTap: () => Navigator.push(context, MaterialPageRoute(builder: (_) => const LayoutShowcase())))
                .secondary.fullWidth(),
            
            const SizedBox(height: 100), // Bottom padding
          ],
        ),
      ),
    );
  }

  Widget _actionButton(IconData icon, String label, VoidCallback onTap, {int? badgeCount}) {
    Widget iconWidget = Fx.icon(icon, size: 28, color: const Color(0xFF2563EB));
    
    if (badgeCount != null && badgeCount > 0) {
      iconWidget = Fx.badge(
        child: iconWidget,
        label: badgeCount.toString(),
        color: Colors.red,
        offset: const Offset(6, -6),
      );
    }

    return Fx.box(
      onTap: onTap,
      child: Fx.column(children: [
        iconWidget.p(12).bg(const Color(0xFFEFF6FF)).roundedFull(),
        const SizedBox(height: 8),
        Fx.text(label).fontSize(12).semiBold().color(Colors.grey[700]!)
      ]),
    ).pointer(); 
  }


  Widget _miniStat(String label, String value, Color col) {
    return Fx.column(
      gap: 6,
      children: [
        Fx.text(label).fontSize(11).color(const Color(0xFF94A3B8)).weight(FontWeight.w600),
        Fx.text(value).fontSize(17).bold().color(col),
      ],
    );
  }

  Widget _actionCircle(IconData icon, String label, Color color) {
    return Fx.column(
      gap: 10,
      children: [
        Fx.box(
          child: Icon(icon, color: color, size: 28)
        ).size(68, 68).background(Colors.white).borderRadius(22).shadow(color: Colors.black.withValues(alpha: 0.03), blur: 15).center().pointer(),
        Fx.text(label).fontSize(13).bold().color(const Color(0xFF64748B)),
      ],
    ).expand();
  }

  Widget _transactionTile(String title, String subtitle, double amount, IconData icon, Color col) {
    final isNegative = amount < 0;
    return Fx.box(
        child: Fx.row(
            children: [
              Fx.box(
                child: Icon(icon, color: col, size: 26)
              ).size(54, 54).background(col.withValues(alpha: 0.12)).borderRadius(18).center(),
              const SizedBox(width: 18),
              Fx.column(
                gap: 4,
                children: [
                  Fx.text(title).fontSize(16).bold().color(const Color(0xFF0F172A)),
                  Fx.text(subtitle).fontSize(12).color(const Color(0xFF94A3B8)).weight(FontWeight.w500),
                ],
              ).expand(),
              Fx.text("${isNegative ? '' : '+'}${isNegative ? '-' : ''} ₹ ${amount.abs().toStringAsFixed(2)}")
                  .fontSize(16)
                  .bold()
                  .color(isNegative ? const Color(0xFF0F172A) : const Color(0xFF10B981)),
            ],
          ),
        )
        .background(Colors.white)
        .borderRadius(28)
        .padding(18)
        .shadow(color: Colors.black.withValues(alpha: 0.02), blur: 10).mb(12);
  }
}

class LayoutShowcase extends StatefulWidget {
  const LayoutShowcase({super.key});

  @override
  State<LayoutShowcase> createState() => _LayoutShowcaseState();
}

class _LayoutShowcaseState extends State<LayoutShowcase> {
  int _index = 0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: Fx.appBar(
        title: "Fluxy Layouts",
        centerTitle: true,
        actions: [
          IconButton(icon: const Icon(Icons.search), onPressed: () {}),
          IconButton(icon: const Icon(Icons.more_vert), onPressed: () {}),
        ],
      ),
      drawer: Fx.drawer(
        width: 280,
        child: Fx.column(children: [
          Fx.box().h(200).bg(const Color(0xFF1E293B)).wFull().child(
            Fx.column(mainAxisAlignment: MainAxisAlignment.center, children: [
              Fx.icon(Icons.person, size: 64, color: Colors.white).p(16).bg(Colors.white24).roundedFull(),
              const SizedBox(height: 16),
              Fx.text("Fluxy User").color(Colors.white).bold().fontSize(18),
            ])
          ),
          ListTile(leading: const Icon(Icons.home), title: const Text("Home"), onTap: () {}),
          ListTile(leading: const Icon(Icons.settings), title: const Text("Settings"), onTap: () {}),
          const Spacer(),
          ListTile(leading: const Icon(Icons.logout), title: const Text("Logout"), onTap: () => Navigator.pop(context)),
          const SizedBox(height: 24),
        ])
      ),
      body: splitLayout(),
      bottomNavigationBar: Fx.bottomNav(
        currentIndex: _index,
        onTap: (i) => setState(() => _index = i),
        items: const [
          BottomNavigationBarItem(icon: Icon(Icons.home_outlined), activeIcon: Icon(Icons.home), label: "Home"),
          BottomNavigationBarItem(icon: Icon(Icons.explore_outlined), activeIcon: Icon(Icons.explore), label: "Explore"),
          BottomNavigationBarItem(icon: Icon(Icons.person_outline), activeIcon: Icon(Icons.person), label: "Profile"),
        ],
      ),
    );
  }

  Widget splitLayout() {
    return Fx.row(children: [
      Fx.column(mainAxisAlignment: MainAxisAlignment.center, children: [
        Fx.text("Page $_index Content").bold().fontSize(24),
        const SizedBox(height: 16),
        Fx.text("Try opening the sidebar!").color(Colors.grey),
      ]).expand(),
    ]);
  }
}

// --- SCREEN 2: PORTFOLIO STATS ---
class PortfolioStats extends StatelessWidget {
  const PortfolioStats({super.key});

  @override
  Widget build(BuildContext context) {
    return SafeArea(
      child: SingleChildScrollView(
        padding: const EdgeInsets.all(24),
        child: Column(
          children: [
            Fx.text("Market Insights").fontSize(30).bold().color(const Color(0xFF0F172A)),
            const SizedBox(height: 48),

            // Dynamic Chart Card
            Fx.box(
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  const Text("PORTFOLIO PERFORMANCE", style: TextStyle(fontSize: 10, fontWeight: FontWeight.bold, letterSpacing: 1.5, color: Color(0xFF94A3B8))),
                  const SizedBox(height: 12),
                  Fx.row(
                    children: [
                      Fx.text("+ ₹ 12,400.00").fontSize(28).bold().color(const Color(0xFF10B981)),
                      const SizedBox(width: 12),
                      Fx.box(
                        child: Fx.text("↑ 14.2%").color(const Color(0xFF10B981)).fontSize(12).bold()
                      ).background(const Color(0xFF10B981).withValues(alpha: 0.1)).borderRadius(8).paddingX(8).paddingY(4),
                    ],
                  ),
                  const Spacer(),
                  Fx.row(
                    gap: 8, // Reduced from 12
                    children: [
                      _chartBar(60), _chartBar(90), _chartBar(70), _chartBar(130), 
                      _chartBar(110), _chartBar(80), _chartBar(120), _chartBar(105),
                    ],
                  ).center(),
                ],
              ),
            )
            .height(280)
            .background(Colors.white)
            .borderRadius(36)
            .padding(28)
            .shadow(color: Colors.black.withValues(alpha: 0.04), blur: 30),

            const SizedBox(height: 40),

            // Reactive Signal Adjustment Control
            Fx.box(
              child: Column(
                children: [
                  Fx.text("Live Budget Control").fontSize(18).bold().color(const Color(0xFF1E293B)),
                  const SizedBox(height: 8),
                  Fx.text("Simulate instant financial adjustments").fontSize(13).color(const Color(0xFF64748B)),
                  const SizedBox(height: 32),
                  Fx.row(
                    gap: 24, // Reduced from 40
                    children: [
                      Fx.box(
                        onTap: () => balance.value -= 500,
                        child: const Icon(Icons.remove_rounded, color: Colors.white, size: 28),
                      ).background(const Color(0xFF1E293B)).size(56, 56).borderRadius(18).shadow(color: Colors.black.withValues(alpha: 0.2), blur: 15).pointer(),
                      
                      Fx.text(() => "₹ ${balance.value.toInt()}").fontSize(28).bold().color(const Color(0xFF0F172A)),
                      
                      Fx.box(
                        onTap: () => balance.value += 500,
                        child: const Icon(Icons.add_rounded, color: Colors.white, size: 28),
                      ).background(const Color(0xFF6366F1)).size(56, 56).borderRadius(18).shadow(color: const Color(0xFF6366F1).withValues(alpha: 0.4), blur: 20).pointer(),
                    ],
                  ).center(),
                ],
              ),
            )
            .background(const Color(0xFFF1F5F9))
            .borderRadius(36)
            .padding(32),

            const SizedBox(height: 40),

            _fancyCard("Mutual Fund SIP", Icons.trending_up_rounded, "Active • ₹ 5,000/mo", const Color(0xFF6366F1)),
            const SizedBox(height: 18),
            _fancyCard("Crypto Assets", Icons.currency_bitcoin_rounded, "Balanced Portfolio", const Color(0xFFF59E0B)),
          ],
        ),
      ),
    );
  }

  Widget _chartBar(double height) {
    return Fx.box(
        child: Align(
            alignment: Alignment.bottomCenter,
            child: AnimBar(height: height),
        )
    )
    .height(height)
    .background(const Color(0xFF6366F1).withValues(alpha: 0.1))
    .borderRadius(8)
    .expand();
  }

  Widget _fancyCard(String title, IconData icon, String detail, Color accent) {
    return Fx.box(
      child: Fx.row(
        children: [
          Fx.box(
            child: Icon(icon, color: accent, size: 28)
          ).size(50, 50).background(accent.withValues(alpha: 0.1)).borderRadius(16).center(),
          const SizedBox(width: 20),
          Fx.column(
            gap: 4,
            children: [
              Fx.text(title).bold().fontSize(17).color(const Color(0xFF0F172A)),
              Fx.text(detail).fontSize(13).color(const Color(0xFF64748B)).weight(FontWeight.w500),
            ],
          ).expand(),
          const Icon(Icons.chevron_right_rounded, color: Color(0xFFCBD5E1), size: 30),
        ],
      ),
    )
    .background(Colors.white)
    .borderRadius(28)
    .padding(24)
    .shadow(color: Colors.black.withValues(alpha: 0.03), blur: 20);
  }
}

class AnimBar extends StatefulWidget {
  final double height;
  const AnimBar({super.key, required this.height});

  @override
  State<AnimBar> createState() => _AnimBarState();
}

class _AnimBarState extends State<AnimBar> {
  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: const Duration(seconds: 1),
      curve: Curves.elasticOut,
      height: widget.height * 0.7,
      width: 30,
      decoration: BoxDecoration(
        color: const Color(0xFF6366F1),
        borderRadius: BorderRadius.circular(10),
        gradient: LinearGradient(
          begin: Alignment.topCenter,
          end: Alignment.bottomCenter,
          colors: [
            const Color(0xFF818CF8),
            const Color(0xFF6366F1),
          ],
        ),
      ),
    );
  }
}
1
likes
0
points
867
downloads

Publisher

unverified uploader

Weekly Downloads

A complete Flutter platform unifying reactive state, declarative UI, styling, animation, routing, and tooling. Build production-grade apps faster and at scale.

Repository (GitHub)
View/report issues

Documentation

Documentation

License

unknown (license)

Dependencies

args, flutter, flutter_secure_storage, path, path_provider, shared_preferences

More

Packages that depend on fluxy