fluxy 0.1.7 copy "fluxy: ^0.1.7" to clipboard
fluxy: ^0.1.7 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(
  MaterialApp(
    navigatorKey: FluxyRouter.navigatorKey,
    debugShowCheckedModeBanner: false,
    home: const 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.col(
                  gap: 4,
                  children: [
                    Fx.text("Welcome back,").font.sm().muted().semiBold(),
                    Fx.text("Alex Riviera").font.xxl().bold().primary(),
                  ],
                ).expanded(),
                Fx.avatar(
                  fallback: "AR",
                  size: FxAvatarSize.lg,
                  onTap: () => Fx.toast("Profile tapped"),
                ).shadow.md,
              ],
            ),
            const SizedBox(height: 36),

            // Main Balance Card
            Fx.box()
                .bg(const Color(0xFF1E293B))
                .rounded(32)
                .p(24)
                .shadow
                .lg
                .children([
                  Fx.row(
                    children: [
                      Fx.text("AVAILABLE BALANCE").font
                          .xs()
                          .bold()
                          .color(const Color(0xFF94A3B8))
                          .spacing(1.5),
                      const Spacer(),
                      Fx.icon(
                        Icons.nfc_rounded,
                        color: Colors.white,
                        size: 20,
                      ).p(8).bg(Colors.white.withOpacity(0.1)).rounded(10),
                    ],
                  ),
                  const SizedBox(height: 12),
                  Fx.text(() => "₹ ${balance.value.toStringAsFixed(2)}")
                      .font
                      .xxl()
                      .bold()
                      .whiteText()
                      .animate(fade: 0, slide: const Offset(0, 10)),
                  const SizedBox(height: 24),
                  Fx.row(
                    gap: 20,
                    children: [
                      _miniStat(
                        "Income",
                        "₹ ${income.value.toInt()}",
                        FxTokens.colors.success,
                      ),
                      _miniStat(
                        "Expense",
                        "₹ ${expense.value.toInt()}",
                        FxTokens.colors.error,
                      ),
                    ],
                  ),
                ]),

            const SizedBox(height: 36),

            // Quick Actions
            Fx.row(
              justify: 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),
                ),
              ],
            ).stagger(0.1),

            const SizedBox(height: 24),
            "Add New Card".primaryBtn().wFull().shadow.md,

            const SizedBox(height: 48),

            // Transactions Header with Dropdown
            Fx.row(
              justify: 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(
              justify: MainAxisAlignment.spaceEvenly,
              children: [
                _actionButton(
                  Icons.notifications_active_outlined,
                  "Toast",
                  () => Fx.toast("Fluxy toast message!"),
                  badgeCount: 3,
                ).animate(fade: 0, scale: 0.8),
                _actionButton(
                  Icons.window_outlined,
                  "Modal",
                  () => Fx.modal(
                    context,
                    child: Fx.box()
                        .p(24)
                        .backgroundWhite()
                        .rounded(24)
                        .w(300)
                        .children([
                          Fx.col(
                            children: [
                              Fx.text("Fluxy Modal").bold().font.xl(),
                              const SizedBox(height: 12),
                              Fx.text("Declarative & Simple.").center().muted(),
                              const SizedBox(height: 24),
                              "Close"
                                  .primaryBtn(
                                    onTap: () => Navigator.pop(context),
                                  )
                                  .wFull(),
                            ],
                          ),
                        ]),
                  ),
                ).animate(fade: 0, scale: 0.8, delay: 0.1),
                _actionButton(
                  Icons.vertical_align_bottom_outlined,
                  "Sheet",
                  () => Fx.bottomSheet(
                    context,
                    child: Fx.box().p(24).h(300).wFull().children([
                      Fx.col(
                        children: [
                          Fx.box().w(40).h(4).bg(Colors.grey[300]!).circle(),
                          const SizedBox(height: 24),
                          Fx.text("Bottom Sheet").bold().font.xxl(),
                          const SizedBox(height: 12),
                          Fx.text("Swipe down to close.").muted(),
                          const SizedBox(height: 40),
                          "Got it"
                              .primaryBtn(onTap: () => Navigator.pop(context))
                              .wFull(),
                        ],
                      ),
                    ]),
                  ),
                ).animate(fade: 0, scale: 0.8, delay: 0.2),
              ],
            ).p(16).bg(Colors.white).rounded(24).shadow.sm,

            Fx.gap(32),
            "View Full Layout Demo"
                .secondaryBtn(
                  onTap: () => Navigator.push(
                    context,
                    MaterialPageRoute(builder: (_) => const LayoutShowcase()),
                  ),
                )
                .wFull(),

            const SizedBox(height: 100), // Bottom padding
          ],
        ),
      ),
    );
  }

  Widget _actionButton(
    IconData icon,
    String label,
    VoidCallback onTap, {
    int? badgeCount,
  }) {
    // 1. Create the base icon widget with the circular background
    Widget buttonContent = Fx.icon(icon, size: 28, color: const Color(0xFF2563EB))
        .p(12)
        .bg(const Color(0xFFEFF6FF))
        .circle();

    // 2. Add badge if needed relative to the circle
    if (badgeCount != null && badgeCount > 0) {
      buttonContent = Fx.badge(
        child: buttonContent,
        label: badgeCount.toString(),
        color: Colors.red,
        offset: const Offset(4, -4), // Adjusted for circular offset
      );
    }

    return Fx.box().onTap(onTap).children([
      buttonContent,
      const SizedBox(height: 8),
      Fx.text(label).font.xs().semiBold().muted(),
    ]);
  }

  Widget _miniStat(String label, String value, Color col) {
    return Fx.col(
      gap: 6,
      children: [
        Fx.text(label).font.xs().muted().bold(),
        Fx.text(value).font.md().bold().color(col),
      ],
    );
  }

  Widget _actionCircle(IconData icon, String label, Color color) {
    return Fx.col(
      gap: 12,
      children: [
        Fx.box()
            .size(68)
            .bg(color.withOpacity(0.1))
            .circle()
            .center()
            .child(Icon(icon, color: color, size: 28))
            .onHover((s) => s.scale(1.15).bg(color.withOpacity(0.15)))
            .transition(300.ms),
        Fx.text(label).font.sm().bold().color(const Color(0xFF1E293B)),
      ],
    ).expanded();
  }

  Widget _transactionTile(
    String title,
    String subtitle,
    double amount,
    IconData icon,
    Color col,
  ) {
    final isNegative = amount < 0;
    return Fx.box()
        .bg(Colors.white)
        .rounded(28)
        .p(18)
        .shadow
        .sm
        .mb(12)
        .child(
          Fx.row(
            children: [
              Fx.icon(
                icon,
                color: col,
                size: 26,
              ).p(14).bg(col.withOpacity(0.12)).rounded(18),
              const SizedBox(width: 18),
              Fx.col(
                gap: 4,
                children: [
                  Fx.text(title).font.md().bold().primary(),
                  Fx.text(subtitle).font.xs().muted().semiBold(),
                ],
              ).expanded(),
              Fx.text(
                "${isNegative ? '' : '+'}${isNegative ? '-' : ''} ₹ ${amount.abs().toStringAsFixed(2)}",
              ).font.md().bold().color(
                isNegative ? const Color(0xFF0F172A) : FxTokens.colors.success,
              ),
            ],
          ),
        );
  }
}

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.col(
          children: [
            Fx.box()
                .h(200)
                .bg(const Color(0xFF1E293B))
                .wFull()
                .center()
                .child(
                  Fx.col(
                    children: [
                      Fx.icon(
                        Icons.person,
                        size: 64,
                        color: Colors.white,
                      ).p(16).bg(Colors.white24).circle(),
                      const SizedBox(height: 16),
                      Fx.text("Fluxy User").whiteText().bold().font.xl(),
                    ],
                  ),
                ),
            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(
          justify: 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").font.xxxl().bold().primary(),
            const SizedBox(height: 48),

            // Dynamic Chart Card
            Fx.box()
                .height(280)
                .bg(Colors.white)
                .rounded(36)
                .p(28)
                .shadow
                .sm
                .children([
                  Fx.text(
                    "PORTFOLIO PERFORMANCE",
                  ).font.xs().bold().muted().spacing(1.5),
                  const SizedBox(height: 12),
                  Fx.row(
                    children: [
                      Fx.text("+ ₹ 12,400.00").font.xl().bold().success(),
                      const SizedBox(width: 12),
                      Fx.box()
                          .bg(FxTokens.colors.success.withOpacity(0.1))
                          .rounded(8)
                          .px(8)
                          .py(4)
                          .child(Fx.text("↑ 14.2%").font.xs().bold().success()),
                    ],
                  ),
                  const Spacer(),
                  Fx.row(
                    gap: 8,
                    children: [
                      _chartBar(60),
                      _chartBar(90),
                      _chartBar(70),
                      _chartBar(130),
                      _chartBar(110),
                      _chartBar(80),
                      _chartBar(120),
                      _chartBar(105),
                    ],
                  ).center(),
                ]),

            const SizedBox(height: 40),

            // Reactive Signal Adjustment Control
            Fx.box().bg(const Color(0xFFF1F5F9)).rounded(36).p(32).children([
              Fx.col(
                children: [
                  Fx.text("Live Budget Control").font.lg().bold().primary(),
                  const SizedBox(height: 8),
                  Fx.text(
                    "Simulate instant financial adjustments",
                  ).font.sm().muted(),
                  const SizedBox(height: 32),
                  Fx.row(
                    gap: 24,
                    children: [
                      Fx.box()
                          .onTap(() => balance.value -= 500)
                          .bg(const Color(0xFF1E293B))
                          .size(56)
                          .rounded(18)
                          .shadow
                          .md
                          .center()
                          .child(
                            const Icon(
                              Icons.remove_rounded,
                              color: Colors.white,
                              size: 28,
                            ),
                          ),

                      Fx.text(
                        () => "₹ ${balance.value.toInt()}",
                      ).font.xxl().bold().primary(),

                      Fx.box()
                          .onTap(() => balance.value += 500)
                          .bg(const Color(0xFF6366F1))
                          .size(56)
                          .rounded(18)
                          .shadow
                          .md
                          .center()
                          .child(
                            const Icon(
                              Icons.add_rounded,
                              color: Colors.white,
                              size: 28,
                            ),
                          ),
                    ],
                  ).center(),
                ],
              ),
            ]),

            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()
        .height(height)
        .bg(FxTokens.colors.primary.withOpacity(0.1))
        .rounded(8)
        .expanded()
        .child(
          Align(
            alignment: Alignment.bottomCenter,
            child: AnimBar(height: height),
          ),
        );
  }

  Widget _fancyCard(String title, IconData icon, String detail, Color accent) {
    return Fx.box()
        .bg(Colors.white)
        .rounded(28)
        .p(24)
        .shadow
        .sm
        .child(
          Fx.row(
            children: [
              Fx.icon(
                icon,
                color: accent,
                size: 28,
              ).p(11).bg(accent.withOpacity(0.1)).rounded(16),
              const SizedBox(width: 20),
              Fx.col(
                gap: 4,
                children: [
                  Fx.text(title).font.md().bold().primary(),
                  Fx.text(detail).font.xs().muted().semiBold(),
                ],
              ).expanded(),
              const Icon(
                Icons.chevron_right_rounded,
                color: Color(0xFFCBD5E1),
                size: 30,
              ),
            ],
          ),
        );
  }
}

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
916
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