flutter_scalify 3.1.0 copy "flutter_scalify: ^3.1.0" to clipboard
flutter_scalify: ^3.1.0 copied to clipboard

The Ultimate Responsive Layout System. Features smart scaling, 6-tier grids, adaptive flex layouts, container queries, and 4K protection with zero-allocation math.

example/lib/main.dart

// ignore_for_file: deprecated_member_use

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

void main() {
  // Initialize the design token spacing system
  ScalifySpacing.init(const SpacingScale(
    xs: 4,
    sm: 8,
    md: 16,
    lg: 24,
    xl: 32,
    xxl: 48,
  ));
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return ScalifyProvider(
      config: const ScalifyConfig(
        designWidth: 375,
        designHeight: 812,
        minScale: 0.5,
        maxScale: 3.0,
        debounceWindowMillis: 16,
        minWidth: 160,
        enableGranularNotifications: true,
        memoryProtectionThreshold: 1920.0,
        highResScaleFactor: 0.60,
        autoSwapDimensions: true,
      ),
      builder: (context, child) {
        final base = ThemeData(
          useMaterial3: true,
          scaffoldBackgroundColor: const Color(0xFFF8FAFC),
          primaryColor: const Color(0xFF0F172A),
          colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF0F172A)),
          appBarTheme: const AppBarTheme(
            backgroundColor: Color(0xFF0F172A),
            foregroundColor: Colors.white,
            elevation: 0,
          ),
        );

        final TextTheme concreteBaseTextTheme =
            Typography.material2021().englishLike.merge(base.textTheme);

        final scale = ScalifyProvider.of(context, aspect: ScalifyAspect.scale)
            .scaleFactor;

        final theme = base.copyWith(
          textTheme: concreteBaseTextTheme.apply(fontSizeFactor: scale),
        );

        return MaterialApp(
          title: 'Scalify Ultimate Showcase',
          debugShowCheckedModeBanner: false,
          themeAnimationDuration: Duration.zero,
          theme: theme,
          home: child,
        );
      },
      child: const ScalifyDebugOverlay(
        child: AppWidthLimiter(
          maxWidth: 1200,
          horizontalPadding: 16,
          minWidth: 230,
          backgroundColor: Color(0xFFE2E8F0),
          child: ScalifyShowcaseScreen(),
        ),
      ),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    final data = ScalifyProvider.of(context, aspect: ScalifyAspect.scale);

    final double dynamicAspectRatio = context.valueByScreen(
      mobile: 2,
      tablet: 1.5,
      smallDesktop: 0.8,
      desktop: 0.8,
    );

    // compute expandedHeight safely as double
    final double expandedHeight = ((80.0).fz).clamp(60.0, 100.0);

    return Scaffold(
      body: CustomScrollView(
        slivers: [
          SliverAppBar(
            floating: true,
            pinned: true,
            expandedHeight: expandedHeight,
            title: FittedBox(
              fit: BoxFit.scaleDown,
              alignment: Alignment.centerLeft,
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Semantics(
                    label: 'App icon',
                    child: Icon(Icons.layers, size: 28.iz),
                  ),
                  14.sbw,
                  Text(
                    "Scalify UI Kit",
                    style:
                        TextStyle(fontSize: 28.fz, fontWeight: FontWeight.bold),
                  ),
                ],
              ),
            ),
            actions: [
              Center(
                child: Padding(
                  padding: 16.pr,
                  child: Container(
                    padding: [8, 4].p,
                    decoration: BoxDecoration(
                      color: Colors.white10,
                      borderRadius: 4.br,
                    ),
                    child: Text(
                      "W: ${data.size.width.toInt()}",
                      style: TextStyle(fontSize: 18.fz, color: Colors.white),
                    ),
                  ),
                ),
              )
            ],
          ),
          SliverToBoxAdapter(
            child: Padding(
              padding: 20.p,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  const _SectionHeader(title: "NEW v3.0.0 FEATURES"),
                  Container(
                    padding: 12.p,
                    margin: 10.pb,
                    decoration: BoxDecoration(
                        color: Colors.green.shade50,
                        border: Border.all(color: Colors.green.shade200),
                        borderRadius: 8.br),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text("1. Auto-Swap Dimensions: ENABLED ✅",
                            style: TextStyle(
                                color: Colors.green.shade800,
                                fontWeight: FontWeight.bold,
                                fontSize: 16.fz)),
                        4.sbh,
                        Text(
                            "Rotate your device! Design width/height will utilize landscape space intelligently.",
                            style: TextStyle(
                                color: Colors.green.shade700, fontSize: 14.fz)),
                      ],
                    ),
                  ),
                  Text("2. Fractional Scaling (42.pw)",
                      style: TextStyle(
                          fontWeight: FontWeight.bold, fontSize: 16.fz)),
                  8.sbh,
                  Row(
                    children: [
                      Expanded(
                        child: Container(
                          height: 50.h,
                          decoration: BoxDecoration(
                              color: Colors.purple.shade300,
                              borderRadius: 8.br),
                          alignment: Alignment.center,
                          child: Text("42% Width",
                              style: TextStyle(
                                  color: Colors.white,
                                  fontWeight: FontWeight.bold,
                                  fontSize: 14.fz)),
                        ),
                      ),
                      12.sbw,
                      Expanded(
                        child: Container(
                          height: 50.h,
                          decoration: BoxDecoration(
                              color: Colors.purple.shade300,
                              borderRadius: 8.br),
                          alignment: Alignment.center,
                          child: Text("42% Width",
                              style: TextStyle(
                                  color: Colors.white,
                                  fontWeight: FontWeight.bold,
                                  fontSize: 14.fz)),
                        ),
                      ),
                    ],
                  ),
                  16.sbh,
                  Text("3. Context API (Reactive)",
                      style: TextStyle(
                          fontWeight: FontWeight.bold, fontSize: 16.fz)),
                  8.sbh,
                  Container(
                    width: context.w(375),
                    padding: 16.p,
                    decoration: BoxDecoration(
                        color: Colors.teal.shade100, borderRadius: 8.br),
                    child: Row(
                      children: [
                        Icon(Icons.monitor_heart, color: Colors.teal.shade800),
                        8.sbw,
                        Expanded(
                          child: Text(
                            "I use context.w()! Resize window to see me adapt instantly.",
                            style: TextStyle(
                                color: Colors.teal.shade900, fontSize: 15.fz),
                          ),
                        ),
                      ],
                    ),
                  ),
                  20.sbh,
                  ResponsiveVisibility(
                    visibleOn: const [ScreenType.mobile],
                    child: Container(
                      padding: 12.p,
                      margin: 10.pb,
                      decoration: BoxDecoration(
                          color: Colors.orange.shade100, borderRadius: 8.br),
                      child: Row(
                        children: [
                          const Icon(Icons.phone_android, color: Colors.orange),
                          8.sbw,
                          Expanded(
                            child: Text("Visible only on Mobile!",
                                style: TextStyle(
                                    color: Colors.orange.shade900,
                                    fontSize: 16.fz)),
                          ),
                        ],
                      ),
                    ),
                  ),
                  Container(
                    padding: 16.p,
                    decoration: BoxDecoration(
                        color: Colors.blue.shade50, borderRadius: 12.br),
                    child: ResponsiveLayout(
                      portrait: Column(
                        mainAxisSize: MainAxisSize.min,
                        children: [
                          Icon(Icons.screen_lock_portrait, size: 40.iz),
                          4.sbh,
                          Text(
                            "Portrait Mode Active",
                            style: TextStyle(
                                fontWeight: FontWeight.bold, fontSize: 20.fz),
                            textAlign: TextAlign.center,
                          ),
                        ],
                      ),
                      landscape: Row(
                        mainAxisAlignment: MainAxisAlignment.center,
                        children: [
                          Icon(Icons.screen_lock_landscape, size: 30.iz),
                          8.sbw,
                          Expanded(
                            child: Text(
                              "Landscape Mode Active",
                              style: TextStyle(
                                  fontWeight: FontWeight.bold, fontSize: 20.fz),
                              maxLines: 1,
                              overflow: TextOverflow.ellipsis,
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),
                  20.sbh,
                  ResponsiveBuilder(
                    builder: (context, data) {
                      return Card(
                        elevation: 2,
                        shape: RoundedRectangleBorder(borderRadius: 12.br),
                        child: Padding(
                          padding: 12.p,
                          child: Row(
                            children: [
                              CircleAvatar(
                                radius: 20.s,
                                backgroundColor: Colors.indigo.shade100,
                                child: Text(
                                  data.screenType.name[0].toUpperCase(),
                                  style: TextStyle(
                                    fontSize: 14.fz,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.indigo,
                                  ),
                                ),
                              ),
                              12.sbw,
                              Expanded(
                                child: Column(
                                  crossAxisAlignment: CrossAxisAlignment.start,
                                  mainAxisSize: MainAxisSize.min,
                                  children: [
                                    Text(
                                      "ResponsiveBuilder Logic",
                                      style: TextStyle(
                                          fontSize: 21.fz,
                                          fontWeight: FontWeight.bold),
                                      maxLines: 1,
                                      overflow: TextOverflow.ellipsis,
                                    ),
                                    4.sbh,
                                    Text(
                                      "W: ${data.size.width.toInt()}px | Type: ${data.screenType.name}",
                                      style: TextStyle(
                                          fontSize: 18.fz,
                                          color: Colors.grey.shade600),
                                      maxLines: 1,
                                      overflow: TextOverflow.ellipsis,
                                    ),
                                  ],
                                ),
                              ),
                            ],
                          ),
                        ),
                      );
                    },
                  ),
                  Divider(height: 40.h),
                  const _SectionHeader(title: "1. RESPONSIVE FLEX (PROFILE)"),
                  _buildProfileHeader(context),
                  30.sbh,
                  const _SectionHeader(
                      title: "2. ADAPTIVE CARDS (LAYOUT CHANGE)"),
                  Text(
                    "Cards change layout (Row/Column) based on their own width.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 19.fz),
                  ),
                  10.sbh,
                ],
              ),
            ),
          ),
          ResponsiveGrid(
            useSliver: true,
            padding: 20.ph,
            watch: 1,
            mobile: 1,
            tablet: 2,
            smallDesktop: 4,
            desktop: 4,
            childAspectRatio: dynamicAspectRatio,
            spacing: 16,
            runSpacing: 16,
            itemCount: 4,
            itemBuilder: (context, index) {
              return KeyedSubtree(
                key: ValueKey('adaptive_card_$index'),
                child: _AdaptiveProductCard(index: index),
              );
            },
          ),
          SliverToBoxAdapter(
            child: Padding(
              padding: 20.p,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  30.sbh,
                  const _SectionHeader(
                      title: "3. SCALIFYBOX GRID (PERFECT SCALE)"),
                  Text(
                    "Items scale geometrically. Ideal for complex UI that shouldn't break.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 19.fz),
                  ),
                  10.sbh,
                ],
              ),
            ),
          ),
          ResponsiveGrid(
            useSliver: true,
            padding: 20.ph,
            watch: 1,
            mobile: 2,
            tablet: 3,
            desktop: 4,
            spacing: 12,
            runSpacing: 12,
            itemCount: 6,
            itemBuilder: (context, index) {
              return KeyedSubtree(
                key: ValueKey('scalify_box_item_$index'),
                child: _ScalifyBoxGridItem(index: index),
              );
            },
          ),
          SliverToBoxAdapter(
            child: Padding(
              padding: 20.p,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  30.sbh,
                  const _SectionHeader(
                      title: "4. AUTO-FIT GRID (API & LAZY LOAD)"),
                  Text(
                    "Items lazy load and wrap automatically based on minWidth.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 19.fz),
                  ),
                  10.sbh,
                ],
              ),
            ),
          ),
          ResponsiveGrid(
            useSliver: true,
            padding: 20.ph,
            minItemWidth: 300,
            scaleMinItemWidth: false,
            spacing: 10,
            runSpacing: 10,
            itemCount: 20,
            itemBuilder: (context, index) {
              return Container(
                key: ValueKey('api_item_$index'),
                decoration: BoxDecoration(
                  color: Colors.white,
                  borderRadius: BorderRadius.circular(8),
                  border: Border.all(color: Colors.grey.shade200),
                ),
                alignment: Alignment.center,
                child: FittedBox(
                  fit: BoxFit.scaleDown,
                  child: Padding(
                    padding: const EdgeInsets.all(8),
                    child: Column(
                      mainAxisAlignment: MainAxisAlignment.center,
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        const Icon(Icons.cloud_download,
                            color: Colors.blueGrey, size: 28),
                        const SizedBox(height: 4),
                        Text("API Item $index",
                            style: const TextStyle(fontSize: 15)),
                      ],
                    ),
                  ),
                ),
              );
            },
          ),
          SliverToBoxAdapter(
            child: Padding(
              padding: 20.p,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  30.sbh,
                  const _SectionHeader(
                      title: "5. SCALIFY SECTION (SPLIT SCALING)"),
                  Text(
                    "Each panel scales independently based on its own width, not the screen width.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 19.fz),
                  ),
                  10.sbh,
                  const _SplitSectionDemo(),
                  30.sbh,
                  const _SectionHeader(title: "6. BEST PRACTICE: .s vs .h"),
                  Text(
                    "Use .s for button & input heights to keep UI consistent on all screens.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 19.fz),
                  ),
                  10.sbh,
                  const _ScaleComparisonDemo(),
                ],
              ),
            ),
          ),
          // ═══════════════════════════════════════════════════
          // NEW v3.1.0 FEATURES SHOWCASE
          // ═══════════════════════════════════════════════════
          SliverToBoxAdapter(
            child: Padding(
              padding: 20.p,
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Divider(height: 40.h),
                  const _SectionHeader(title: "NEW v3.1.0 FEATURES"),

                  // ── 7. ResponsiveText ─────────────────────────
                  const _SectionHeader(title: "7. RESPONSIVE TEXT"),
                  Text(
                    "Auto-resize text & short text for small screens.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  Container(
                    padding: 16.p,
                    decoration: BoxDecoration(
                      color: Colors.deepPurple.shade50,
                      borderRadius: 12.br,
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text("Auto-Resize (shrinks to fit):",
                            style: TextStyle(
                                fontSize: 13.fz,
                                fontWeight: FontWeight.bold,
                                color: Colors.deepPurple.shade700)),
                        8.sbh,
                        Container(
                          width: double.infinity,
                          padding: 12.p,
                          decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: 8.br,
                          ),
                          child: ResponsiveText(
                            'This is a very long heading that will automatically shrink to fit the available width',
                            style: TextStyle(
                                fontSize: 22.fz,
                                fontWeight: FontWeight.bold,
                                color: Colors.deepPurple),
                            autoResize: true,
                            minFontSize: 10,
                            maxLines: 1,
                          ),
                        ),
                        16.sbh,
                        Text("Short Text for Mobile:",
                            style: TextStyle(
                                fontSize: 13.fz,
                                fontWeight: FontWeight.bold,
                                color: Colors.deepPurple.shade700)),
                        8.sbh,
                        Container(
                          padding: 12.p,
                          decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: 8.br,
                          ),
                          child: ResponsiveText(
                            'Welcome to our Premium Shopping Experience',
                            shortText: 'Welcome! 👋',
                            style: TextStyle(
                                fontSize: 18.fz,
                                color: Colors.deepPurple.shade800),
                          ),
                        ),
                      ],
                    ),
                  ),

                  // ── 8. ResponsiveSpacing ──────────────────────
                  30.sbh,
                  const _SectionHeader(title: "8. RESPONSIVE SPACING (TOKENS)"),
                  Text(
                    "Unified spacing system: xs=4, sm=8, md=16, lg=24, xl=32, xxl=48",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  Container(
                    padding: 16.p,
                    decoration: BoxDecoration(
                      color: Colors.amber.shade50,
                      borderRadius: 12.br,
                    ),
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      children: [
                        Text("Spacing.gap (SizedBox shortcuts):",
                            style: TextStyle(
                                fontSize: 13.fz,
                                fontWeight: FontWeight.bold,
                                color: Colors.amber.shade900)),
                        Spacing.sm.gap,
                        const _SpacingDemoRow(label: 'xs', spacing: Spacing.xs),
                        Spacing.xs.gap,
                        const _SpacingDemoRow(label: 'sm', spacing: Spacing.sm),
                        Spacing.xs.gap,
                        const _SpacingDemoRow(label: 'md', spacing: Spacing.md),
                        Spacing.xs.gap,
                        const _SpacingDemoRow(label: 'lg', spacing: Spacing.lg),
                        Spacing.xs.gap,
                        const _SpacingDemoRow(label: 'xl', spacing: Spacing.xl),
                        Spacing.md.gap,
                        Text("Spacing.insets (Padding shortcuts):",
                            style: TextStyle(
                                fontSize: 13.fz,
                                fontWeight: FontWeight.bold,
                                color: Colors.amber.shade900)),
                        Spacing.sm.gap,
                        Container(
                          padding: Spacing.lg.insets,
                          decoration: BoxDecoration(
                            color: Colors.white,
                            borderRadius: 8.br,
                            border: Border.all(color: Colors.amber.shade200),
                          ),
                          child: Text(
                            "Spacing.lg.insets → EdgeInsets.all(24 * scale)",
                            style: TextStyle(
                                fontSize: 13.fz, color: Colors.amber.shade800),
                          ),
                        ),
                      ],
                    ),
                  ),

                  // ── 9. ResponsiveWrap ─────────────────────────
                  30.sbh,
                  const _SectionHeader(title: "9. RESPONSIVE WRAP"),
                  Text(
                    "Smart auto-wrapping layout for chips, tags, and buttons.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  Container(
                    padding: 16.p,
                    decoration: BoxDecoration(
                      color: Colors.cyan.shade50,
                      borderRadius: 12.br,
                    ),
                    child: ResponsiveWrap(
                      spacing: 10,
                      runSpacing: 10,
                      children: [
                        for (final label in [
                          '🎨 Design',
                          '⚡ Performance',
                          '📱 Mobile',
                          '💻 Desktop',
                          '🌐 Web',
                          '🧪 Testing',
                          '🔄 Auto-Wrap',
                          '📏 Scaled Spacing',
                        ])
                          Chip(
                            label:
                                Text(label, style: TextStyle(fontSize: 12.fz)),
                            backgroundColor: Colors.white,
                            side: BorderSide(color: Colors.cyan.shade200),
                          ),
                      ],
                    ),
                  ),

                  // ── 10. ResponsiveImage ───────────────────────
                  30.sbh,
                  const _SectionHeader(title: "10. RESPONSIVE IMAGE"),
                  Text(
                    "Different images per screen type with fallback chain.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  Container(
                    height: 150.s,
                    decoration: BoxDecoration(
                      borderRadius: 12.br,
                    ),
                    clipBehavior: Clip.antiAlias,
                    child: ResponsiveImage(
                      mobile: const NetworkImage(
                        'https://picsum.photos/400/200',
                      ),
                      desktop: const NetworkImage(
                        'https://picsum.photos/1200/400',
                      ),
                      fit: BoxFit.cover,
                      width: double.infinity,
                      height: 150.s,
                      placeholder: Container(
                        color: Colors.grey.shade200,
                        child: Center(
                          child: Column(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Icon(Icons.image,
                                  size: 32.iz, color: Colors.grey),
                              4.sbh,
                              Text("Loading...",
                                  style: TextStyle(
                                      fontSize: 12.fz, color: Colors.grey)),
                            ],
                          ),
                        ),
                      ),
                      errorWidget: Container(
                        color: Colors.red.shade50,
                        child: Center(
                          child: Icon(Icons.broken_image,
                              size: 32.iz, color: Colors.red.shade300),
                        ),
                      ),
                      borderRadius: BorderRadius.circular(12),
                    ),
                  ),

                  // ── 11. AnimatedResponsiveTransition ──────────
                  30.sbh,
                  const _SectionHeader(
                      title: "11. ANIMATED RESPONSIVE TRANSITION"),
                  Text(
                    "Smooth animations between responsive layouts on resize.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  AnimatedResponsiveTransition(
                    duration: const Duration(milliseconds: 400),
                    transition: ResponsiveTransitionType.fadeSlide,
                    mobile: Container(
                      padding: 16.p,
                      decoration: BoxDecoration(
                        color: Colors.pink.shade50,
                        borderRadius: 12.br,
                        border: Border.all(color: Colors.pink.shade200),
                      ),
                      child: Row(
                        children: [
                          Icon(Icons.phone_android,
                              size: 28.iz, color: Colors.pink),
                          12.sbw,
                          Expanded(
                            child: Text("📱 Mobile Layout",
                                style: TextStyle(
                                    fontSize: 16.fz,
                                    fontWeight: FontWeight.bold,
                                    color: Colors.pink.shade800)),
                          ),
                        ],
                      ),
                    ),
                    tablet: Container(
                      padding: 20.p,
                      decoration: BoxDecoration(
                        color: Colors.blue.shade50,
                        borderRadius: 12.br,
                        border: Border.all(color: Colors.blue.shade200),
                      ),
                      child: Row(
                        children: [
                          Icon(Icons.tablet_mac,
                              size: 36.iz, color: Colors.blue),
                          16.sbw,
                          Expanded(
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Text("📱 Tablet Layout",
                                    style: TextStyle(
                                        fontSize: 18.fz,
                                        fontWeight: FontWeight.bold,
                                        color: Colors.blue.shade800)),
                                4.sbh,
                                Text("Extra details visible on tablet!",
                                    style: TextStyle(
                                        fontSize: 13.fz,
                                        color: Colors.blue.shade600)),
                              ],
                            ),
                          ),
                        ],
                      ),
                    ),
                    desktop: Container(
                      padding: 24.p,
                      decoration: BoxDecoration(
                        gradient: LinearGradient(
                          colors: [
                            Colors.green.shade50,
                            Colors.teal.shade50,
                          ],
                        ),
                        borderRadius: 12.br,
                        border: Border.all(color: Colors.green.shade200),
                      ),
                      child: Row(
                        children: [
                          Icon(Icons.desktop_mac,
                              size: 42.iz, color: Colors.green),
                          20.sbw,
                          Expanded(
                            child: Column(
                              crossAxisAlignment: CrossAxisAlignment.start,
                              mainAxisSize: MainAxisSize.min,
                              children: [
                                Text("🖥️ Desktop Layout",
                                    style: TextStyle(
                                        fontSize: 20.fz,
                                        fontWeight: FontWeight.bold,
                                        color: Colors.green.shade800)),
                                4.sbh,
                                Text(
                                    "Full desktop experience with extra content area!",
                                    style: TextStyle(
                                        fontSize: 14.fz,
                                        color: Colors.green.shade600)),
                              ],
                            ),
                          ),
                          Container(
                            padding: 12.p,
                            decoration: BoxDecoration(
                              color: Colors.green.shade100,
                              borderRadius: 8.br,
                            ),
                            child: Text("Extra Panel",
                                style: TextStyle(
                                    fontSize: 12.fz,
                                    color: Colors.green.shade700)),
                          ),
                        ],
                      ),
                    ),
                  ),

                  // ── 12. ResponsiveTable ───────────────────────
                  30.sbh,
                  const _SectionHeader(title: "12. RESPONSIVE TABLE"),
                  Text(
                    "DataTable on desktop → Cards on mobile. With column hiding.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  Container(
                    decoration: BoxDecoration(
                      color: Colors.white,
                      borderRadius: 12.br,
                      border: Border.all(color: Colors.grey.shade200),
                    ),
                    clipBehavior: Clip.antiAlias,
                    child: ResponsiveTable(
                      columns: const ['Product', 'Price', 'Status', 'Category'],
                      rows: const [
                        ['iPhone 15 Pro', '\$999', 'Available', 'Electronics'],
                        ['MacBook Air', '\$1,199', 'Sold Out', 'Laptops'],
                        ['AirPods Pro', '\$249', 'Available', 'Audio'],
                        ['iPad Pro', '\$799', 'Pre-Order', 'Tablets'],
                        ['Apple Watch', '\$399', 'Available', 'Wearables'],
                      ],
                      hiddenColumnsOnMobile: const [3],
                      onRowTap: (index, row) {},
                    ),
                  ),

                  // ── 13. ResponsiveConstraints ─────────────────
                  30.sbh,
                  const _SectionHeader(title: "13. RESPONSIVE CONSTRAINTS"),
                  Text(
                    "Different BoxConstraints per screen type with alignment.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  ResponsiveConstraints(
                    alignment: Alignment.center,
                    mobile: const BoxConstraints(maxWidth: 300),
                    tablet: const BoxConstraints(maxWidth: 450),
                    desktop: const BoxConstraints(maxWidth: 600),
                    child: Container(
                      padding: 16.p,
                      decoration: BoxDecoration(
                        gradient: LinearGradient(
                          colors: [
                            Colors.orange.shade100,
                            Colors.deepOrange.shade100,
                          ],
                        ),
                        borderRadius: 12.br,
                        border: Border.all(color: Colors.orange.shade300),
                      ),
                      child: Column(
                        children: [
                          Icon(Icons.aspect_ratio,
                              size: 32.iz, color: Colors.deepOrange),
                          8.sbh,
                          ResponsiveText(
                            'Constrained to max-width per screen type',
                            style: TextStyle(
                                fontSize: 14.fz,
                                fontWeight: FontWeight.bold,
                                color: Colors.deepOrange.shade800),
                            autoResize: true,
                            minFontSize: 10,
                            maxLines: 1,
                          ),
                          4.sbh,
                          ResponsiveBuilder(
                            builder: (ctx, data) => Text(
                              'mobile: 300px | tablet: 450px | desktop: 600px',
                              style: TextStyle(
                                  fontSize: 11.fz,
                                  color: Colors.deepOrange.shade600),
                            ),
                          ),
                        ],
                      ),
                    ),
                  ),

                  30.sbh,
                  const _SectionHeader(title: "14. RESPONSIVE NAVIGATION DEMO"),
                  Text(
                    "Bottom → Rail → Sidebar. See NavigationDemoPage below.",
                    style: TextStyle(color: Colors.grey[600], fontSize: 14.fz),
                  ),
                  10.sbh,
                  const _NavigationPreview(),

                  30.sbh,
                ],
              ),
            ),
          ),
          SliverToBoxAdapter(child: 50.sbh),
        ],
      ),
    );
  }

  Widget _buildProfileHeader(BuildContext context) {
    final isMobile = context.responsiveData.isSmallScreen;

    return Container(
      padding: 20.p,
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: 16.br,
        boxShadow: const [
          BoxShadow(color: Colors.black12, blurRadius: 10, offset: Offset(0, 4))
        ],
      ),
      child: ResponsiveFlex(
        switchOn: ScreenType.mobile,
        spacing: 16,
        rowCrossAxisAlignment: CrossAxisAlignment.center,
        children: [
          Center(
            child: CircleAvatar(
              radius: 40.s,
              backgroundColor: Colors.indigo.shade50,
              child: Icon(Icons.person, size: 40.iz, color: Colors.indigo),
            ),
          ),
          if (!isMobile)
            Expanded(child: _buildInfoColumn(context))
          else
            _buildInfoColumn(context),
          if (!isMobile)
            Flexible(
              child: Container(
                alignment: isMobile ? Alignment.center : Alignment.centerRight,
                child: Tooltip(
                  message: 'Contact user',
                  child: Semantics(
                      button: true,
                      label: 'Contact the user',
                      child: ElevatedButton(
                        onPressed: () {},
                        style: ElevatedButton.styleFrom(
                          padding: [16, 12].p,
                          backgroundColor: Colors.indigo,
                          foregroundColor: Colors.white,
                        ),
                        child: FittedBox(
                          fit: BoxFit.scaleDown,
                          child: Row(
                            mainAxisSize: MainAxisSize.min,
                            children: [
                              Icon(Icons.message, size: 16.iz),
                              6.sbw,
                              Text(
                                "Contact",
                                style: TextStyle(fontSize: 16.fz),
                              ),
                            ],
                          ),
                        ),
                      )),
                ),
              ),
            )
          else
            Container(
              alignment: isMobile ? Alignment.center : Alignment.centerRight,
              child: Tooltip(
                message: 'Contact user',
                child: Semantics(
                  button: true,
                  label: 'Contact the user',
                  child: ElevatedButton.icon(
                    onPressed: () {},
                    icon: Icon(Icons.message, size: 18.iz),
                    label: Text("Contact", style: TextStyle(fontSize: 20.fz)),
                    style: ElevatedButton.styleFrom(
                      padding: [20, 12].p,
                      backgroundColor: Colors.indigo,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
              ),
            )
        ],
      ),
    );
  }

  Widget _buildInfoColumn(BuildContext context) {
    final isMobile = context.responsiveData.isSmallScreen;

    return Column(
      crossAxisAlignment:
          isMobile ? CrossAxisAlignment.center : CrossAxisAlignment.start,
      mainAxisSize: MainAxisSize.min,
      children: [
        Text(
          "Alaa Hassan",
          style: TextStyle(fontSize: 24.fz, fontWeight: FontWeight.bold),
        ),
        4.sbh,
        Text(
          "Senior Flutter Developer & Expert",
          style: TextStyle(fontSize: 16.fz, color: Colors.grey),
        ),
        8.sbh,
        Wrap(
          alignment: isMobile ? WrapAlignment.center : WrapAlignment.start,
          spacing: 8.s,
          runSpacing: 4.s,
          children: const [
            _Badge(text: "Pro Member"),
            _Badge(text: "Available for Hire")
          ],
        )
      ],
    );
  }
}

class _AdaptiveProductCard extends StatelessWidget {
  final int index;
  const _AdaptiveProductCard({required this.index});

  @override
  Widget build(BuildContext context) {
    return Container(
      decoration: BoxDecoration(
        color: Colors.white,
        borderRadius: BorderRadius.circular(12),
        border: Border.all(color: Colors.grey.shade200),
      ),
      padding: const EdgeInsets.all(12),
      child: ContainerQuery(
        breakpoints: const [200, 350],
        builder: (context, query) {
          // حالة الحاوية الصغيرة جداً
          if (query.tier == QueryTier.xs) {
            return FittedBox(
              fit: BoxFit.scaleDown,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Icon(Icons.shopping_bag,
                      color: Colors.indigo, size: 32),
                  const SizedBox(height: 8),
                  Text("Product $index",
                      style: const TextStyle(
                          fontSize: 16, fontWeight: FontWeight.bold)),
                  const Text("\$99",
                      style: TextStyle(fontSize: 14, color: Colors.green)),
                ],
              ),
            );
          }

          if (query.tier == QueryTier.sm) {
            return FittedBox(
              fit: BoxFit.scaleDown,
              child: Column(
                mainAxisAlignment: MainAxisAlignment.center,
                mainAxisSize: MainAxisSize.min,
                children: [
                  const Icon(Icons.shopping_bag,
                      color: Colors.indigo, size: 40),
                  const SizedBox(height: 8),
                  Text("Product $index",
                      style: const TextStyle(
                          fontSize: 18, fontWeight: FontWeight.bold)),
                  const Text("\$99.00",
                      style: TextStyle(fontSize: 15, color: Colors.green)),
                ],
              ),
            );
          }

          return Row(
            children: [
              Container(
                width: 50,
                height: 50,
                decoration: BoxDecoration(
                    color: Colors.indigo.shade50,
                    borderRadius: BorderRadius.circular(8)),
                child: const Icon(Icons.shopping_bag,
                    color: Colors.indigo, size: 24),
              ),
              const SizedBox(width: 16),
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisAlignment: MainAxisAlignment.center,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text("Premium Product $index",
                        style: const TextStyle(
                            fontSize: 16, fontWeight: FontWeight.bold),
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis),
                    const Text("High quality item description...",
                        style: TextStyle(fontSize: 13, color: Colors.grey),
                        maxLines: 1,
                        overflow: TextOverflow.ellipsis),
                  ],
                ),
              ),
              const SizedBox(width: 12),
              Tooltip(
                message: 'Buy product',
                child: ElevatedButton(
                  onPressed: () {},
                  child: const Text('Buy'),
                ),
              ),
            ],
          );
        },
      ),
    );
  }
}

class _ScalifyBoxGridItem extends StatelessWidget {
  final int index;
  const _ScalifyBoxGridItem({required this.index});

  @override
  Widget build(BuildContext context) {
    return ScalifyBox(
      referenceWidth: 100,
      referenceHeight: 120,
      fit: ScalifyFit.contain,
      builder: (context, ls) {
        return Container(
          width: ls.w(120),
          height: ls.h(120),
          decoration: BoxDecoration(
            color: Colors.white,
            borderRadius: ls.br(12),
            boxShadow: [
              BoxShadow(
                  color: Colors.black.withOpacity(0.04),
                  blurRadius: ls.s(8),
                  offset: Offset(0, ls.s(4)))
            ],
          ),
          padding: ls.p(8),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              Icon(Icons.dashboard_customize_rounded,
                  color: Colors.indigo, size: ls.iz(40)),
              ls.sbh(8),
              Text("Item #${index + 1}",
                  style: TextStyle(
                      fontWeight: FontWeight.bold, fontSize: ls.fz(12)),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis),
              ls.sbh(2),
              Text("ScalifyBox",
                  style: TextStyle(color: Colors.grey, fontSize: ls.fz(9)),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis),
            ],
          ),
        );
      },
    );
  }
}

class _Badge extends StatelessWidget {
  final String text;
  const _Badge({required this.text});
  @override
  Widget build(BuildContext context) => Container(
        padding: [8, 4].p,
        decoration: BoxDecoration(
            color: Colors.grey.shade100,
            borderRadius: 4.br,
            border: Border.all(color: Colors.grey.shade300)),
        child: Text(text,
            style: TextStyle(fontSize: 10.fz, color: Colors.grey.shade700)),
      );
}

class _SectionHeader extends StatelessWidget {
  final String title;
  const _SectionHeader({required this.title});
  @override
  Widget build(BuildContext context) => Padding(
        padding: 10.pb,
        child: Text(title,
            style: TextStyle(
                fontSize: context.fz(24),
                fontWeight: FontWeight.bold,
                letterSpacing: 1.2,
                color: Colors.blueGrey)),
      );
}

/// Demonstrates ScalifySection — responsive split with nested navigation.
class _SplitSectionDemo extends StatefulWidget {
  const _SplitSectionDemo();

  @override
  State<_SplitSectionDemo> createState() => _SplitSectionDemoState();
}

class _SplitSectionDemoState extends State<_SplitSectionDemo> {
  int _mobileTab = 0; // 0 = sidebar, 1 = content
  int? _selectedItem; // null = list, non-null = detail

  @override
  Widget build(BuildContext context) {
    final screenWidth = MediaQuery.sizeOf(context).width;
    final isWide = screenWidth >= 600;

    return Container(
      height: 280.s,
      decoration: BoxDecoration(
        borderRadius: 12.br,
        border: Border.all(color: Colors.grey.shade300),
      ),
      clipBehavior: Clip.antiAlias,
      child: isWide ? _buildSplitView() : _buildMobileView(),
    );
  }

  Widget _buildSplitView() {
    return Row(
      children: [
        Expanded(
          flex: 3,
          child: ScalifySection(child: _buildSidebar()),
        ),
        Container(width: 1, color: Colors.grey.shade300),
        Expanded(
          flex: 7,
          child: ScalifySection(child: _buildMainContent()),
        ),
      ],
    );
  }

  Widget _buildMobileView() {
    return Column(
      children: [
        Container(
          color: Colors.grey.shade100,
          child: Row(
            children: [
              Expanded(
                child: _TabBtn(
                  label: "Sidebar",
                  icon: Icons.view_sidebar,
                  isActive: _mobileTab == 0,
                  onTap: () => setState(() => _mobileTab = 0),
                ),
              ),
              Expanded(
                child: _TabBtn(
                  label: "Content",
                  icon: Icons.dashboard,
                  isActive: _mobileTab == 1,
                  onTap: () => setState(() => _mobileTab = 1),
                ),
              ),
            ],
          ),
        ),
        Expanded(
          child: _mobileTab == 0 ? _buildSidebar() : _buildMainContent(),
        ),
      ],
    );
  }

  Widget _buildSidebar() {
    return Builder(builder: (context) {
      final data = ScalifyProvider.of(context);
      return Container(
        color: Colors.indigo.shade50,
        padding: EdgeInsets.all(context.s(12)),
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(Icons.view_sidebar,
                size: context.iz(28), color: Colors.indigo),
            SizedBox(height: context.s(8)),
            FittedBox(
              fit: BoxFit.scaleDown,
              child: Text("Sidebar Panel",
                  style: TextStyle(
                    fontSize: context.fz(16),
                    fontWeight: FontWeight.bold,
                    color: Colors.indigo.shade800,
                  )),
            ),
            SizedBox(height: context.s(6)),
            Container(
              padding: EdgeInsets.symmetric(
                  horizontal: context.s(8), vertical: context.s(4)),
              decoration: BoxDecoration(
                color: Colors.indigo.shade100,
                borderRadius: BorderRadius.circular(context.r(6)),
              ),
              child: FittedBox(
                fit: BoxFit.scaleDown,
                child: Text("W: ${data.size.width.toInt()}px",
                    style: TextStyle(
                      fontSize: context.fz(13),
                      fontWeight: FontWeight.w600,
                      color: Colors.indigo.shade700,
                    )),
              ),
            ),
            SizedBox(height: context.s(4)),
            FittedBox(
              fit: BoxFit.scaleDown,
              child: Text("Scale: ${data.scaleWidth.toStringAsFixed(2)}",
                  style: TextStyle(
                    fontSize: context.fz(11),
                    color: Colors.indigo.shade400,
                  )),
            ),
          ],
        ),
      );
    });
  }

  Widget _buildMainContent() {
    return Builder(builder: (context) {
      final data = ScalifyProvider.of(context);
      return Container(
        color: Colors.teal.shade50,
        padding: EdgeInsets.all(context.s(12)),
        child: _selectedItem == null
            ? _listView(context, data)
            : _detailView(context, data),
      );
    });
  }

  Widget _listView(BuildContext context, ResponsiveData data) {
    return Column(
      children: [
        FittedBox(
          fit: BoxFit.scaleDown,
          alignment: Alignment.centerLeft,
          child: Text("Content — ${data.size.width.toInt()}px",
              style: TextStyle(
                fontSize: context.fz(15),
                fontWeight: FontWeight.bold,
                color: Colors.teal.shade800,
              )),
        ),
        SizedBox(height: context.s(8)),
        Expanded(
          child: ListView.separated(
            padding: EdgeInsets.zero,
            itemCount: 3,
            separatorBuilder: (_, __) => SizedBox(height: context.s(6)),
            itemBuilder: (ctx, index) {
              return Material(
                color: Colors.white,
                borderRadius: BorderRadius.circular(context.r(8)),
                child: InkWell(
                  borderRadius: BorderRadius.circular(context.r(8)),
                  onTap: () => setState(() => _selectedItem = index),
                  child: Padding(
                    padding: EdgeInsets.all(context.s(10)),
                    child: Row(
                      children: [
                        Icon(Icons.article_outlined,
                            size: context.iz(20), color: Colors.teal),
                        SizedBox(width: context.s(8)),
                        Expanded(
                          child: Text("Item ${index + 1} — Tap for detail",
                              style: TextStyle(
                                  fontSize: context.fz(13),
                                  color: Colors.teal.shade700)),
                        ),
                        Icon(Icons.chevron_right,
                            size: context.iz(18), color: Colors.teal.shade300),
                      ],
                    ),
                  ),
                ),
              );
            },
          ),
        ),
      ],
    );
  }

  Widget _detailView(BuildContext context, ResponsiveData data) {
    return Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        Row(
          children: [
            GestureDetector(
              onTap: () => setState(() => _selectedItem = null),
              child: Row(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.arrow_back,
                      size: context.iz(18), color: Colors.teal.shade700),
                  SizedBox(width: context.s(4)),
                  Text("Back",
                      style: TextStyle(
                          fontSize: context.fz(13),
                          color: Colors.teal.shade700,
                          fontWeight: FontWeight.w600)),
                ],
              ),
            ),
            const Spacer(),
            Container(
              padding: EdgeInsets.symmetric(
                  horizontal: context.s(6), vertical: context.s(2)),
              decoration: BoxDecoration(
                color: Colors.teal.shade100,
                borderRadius: BorderRadius.circular(context.r(4)),
              ),
              child: Text("W: ${data.size.width.toInt()}px",
                  style: TextStyle(
                      fontSize: context.fz(11), color: Colors.teal.shade600)),
            ),
          ],
        ),
        const Spacer(),
        Icon(Icons.check_circle,
            size: context.iz(40), color: Colors.teal.shade400),
        SizedBox(height: context.s(8)),
        FittedBox(
          fit: BoxFit.scaleDown,
          child: Text("Detail: Item ${_selectedItem! + 1}",
              style: TextStyle(
                fontSize: context.fz(18),
                fontWeight: FontWeight.bold,
                color: Colors.teal.shade800,
              )),
        ),
        SizedBox(height: context.s(6)),
        FittedBox(
          fit: BoxFit.scaleDown,
          child: Text("Split stays fixed! ✅",
              style: TextStyle(
                fontSize: context.fz(14),
                color: Colors.teal.shade600,
              )),
        ),
        const Spacer(),
      ],
    );
  }
}

class _TabBtn extends StatelessWidget {
  final String label;
  final IconData icon;
  final bool isActive;
  final VoidCallback onTap;

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

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      onTap: onTap,
      child: Container(
        padding: EdgeInsets.symmetric(vertical: 8.s),
        decoration: BoxDecoration(
          color: isActive ? Colors.indigo.shade50 : Colors.transparent,
          border: Border(
            bottom: BorderSide(
              color: isActive ? Colors.indigo : Colors.transparent,
              width: 2,
            ),
          ),
        ),
        child: Row(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Icon(icon,
                size: 16.iz, color: isActive ? Colors.indigo : Colors.grey),
            SizedBox(width: 4.s),
            Text(label,
                style: TextStyle(
                  fontSize: 13.fz,
                  fontWeight: isActive ? FontWeight.bold : FontWeight.normal,
                  color: isActive ? Colors.indigo : Colors.grey,
                )),
          ],
        ),
      ),
    );
  }
}

/// Demonstrates .s (correct) vs .h (problematic) for button & input heights.
class _ScaleComparisonDemo extends StatelessWidget {
  const _ScaleComparisonDemo();

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        Row(
          children: [
            Expanded(
              child: Column(
                children: [
                  FittedBox(
                    fit: BoxFit.scaleDown,
                    child: Text("✅ height: 48.s",
                        style: TextStyle(
                            fontSize: 13.fz,
                            fontWeight: FontWeight.bold,
                            color: Colors.green.shade700)),
                  ),
                  6.sbh,
                  SizedBox(
                    height: 48.s,
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {},
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.green.shade600,
                        foregroundColor: Colors.white,
                      ),
                      child: FittedBox(
                        fit: BoxFit.scaleDown,
                        child:
                            Text("Buy Now", style: TextStyle(fontSize: 16.fz)),
                      ),
                    ),
                  ),
                ],
              ),
            ),
            12.sbw,
            Expanded(
              child: Column(
                children: [
                  FittedBox(
                    fit: BoxFit.scaleDown,
                    child: Text("⚠️ height: 48.h",
                        style: TextStyle(
                            fontSize: 13.fz,
                            fontWeight: FontWeight.bold,
                            color: Colors.orange.shade700)),
                  ),
                  6.sbh,
                  SizedBox(
                    height: 48.h,
                    width: double.infinity,
                    child: ElevatedButton(
                      onPressed: () {},
                      style: ElevatedButton.styleFrom(
                        backgroundColor: Colors.orange.shade600,
                        foregroundColor: Colors.white,
                      ),
                      child: FittedBox(
                        fit: BoxFit.scaleDown,
                        child:
                            Text("Buy Now", style: TextStyle(fontSize: 16.fz)),
                      ),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
        16.sbh,
        Row(
          children: [
            Expanded(
              child: Column(
                children: [
                  FittedBox(
                    fit: BoxFit.scaleDown,
                    child: Text("✅ height: 48.s",
                        style: TextStyle(
                            fontSize: 13.fz,
                            fontWeight: FontWeight.bold,
                            color: Colors.green.shade700)),
                  ),
                  6.sbh,
                  SizedBox(
                    height: 48.s,
                    child: TextField(
                      decoration: InputDecoration(
                        hintText: "Search...",
                        prefixIcon: const Icon(Icons.search),
                        border: OutlineInputBorder(borderRadius: 8.br),
                        contentPadding: EdgeInsets.symmetric(
                            horizontal: 12.w, vertical: 8.s),
                      ),
                      style: TextStyle(fontSize: 14.fz),
                    ),
                  ),
                ],
              ),
            ),
            12.sbw,
            Expanded(
              child: Column(
                children: [
                  FittedBox(
                    fit: BoxFit.scaleDown,
                    child: Text("⚠️ height: 48.h",
                        style: TextStyle(
                            fontSize: 13.fz,
                            fontWeight: FontWeight.bold,
                            color: Colors.orange.shade700)),
                  ),
                  6.sbh,
                  SizedBox(
                    height: 48.h,
                    child: TextField(
                      decoration: InputDecoration(
                        hintText: "Search...",
                        prefixIcon: const Icon(Icons.search),
                        border: OutlineInputBorder(borderRadius: 8.br),
                        contentPadding: EdgeInsets.symmetric(
                            horizontal: 12.w, vertical: 8.h),
                      ),
                      style: TextStyle(fontSize: 14.fz),
                    ),
                  ),
                ],
              ),
            ),
          ],
        ),
        12.sbh,
        Container(
          padding: 10.p,
          decoration: BoxDecoration(
            color: Colors.blue.shade50,
            borderRadius: 8.br,
          ),
          child: Row(
            children: [
              Icon(Icons.info_outline,
                  color: Colors.blue.shade700, size: 18.iz),
              8.sbw,
              Expanded(
                child: Text(
                  "Resize the window — .s stays proportional while .h may shrink on wide screens.",
                  style:
                      TextStyle(fontSize: 12.fz, color: Colors.blue.shade800),
                ),
              ),
            ],
          ),
        ),
      ],
    );
  }
}

// ═══════════════════════════════════════════════════════════════════════
// NEW v3.1.0 FEATURE DEMO WIDGETS
// ═══════════════════════════════════════════════════════════════════════

/// Row showing a spacing tier with a colored bar.
class _SpacingDemoRow extends StatelessWidget {
  final String label;
  final Spacing spacing;
  const _SpacingDemoRow({required this.label, required this.spacing});

  @override
  Widget build(BuildContext context) {
    return Row(
      children: [
        SizedBox(
          width: 30.w,
          child: Text(label,
              style: TextStyle(
                  fontSize: 12.fz,
                  fontWeight: FontWeight.bold,
                  color: Colors.amber.shade800)),
        ),
        Expanded(
          child: Container(
            height: spacing.value.clamp(4, 48),
            decoration: BoxDecoration(
              color: Colors.amber.shade200,
              borderRadius: BorderRadius.circular(4),
            ),
          ),
        ),
        8.sbw,
        Text('${spacing.value.toStringAsFixed(0)}px',
            style: TextStyle(fontSize: 11.fz, color: Colors.amber.shade700)),
      ],
    );
  }
}

/// Preview of the ResponsiveNavigation widget embedded in a container.
class _NavigationPreview extends StatefulWidget {
  const _NavigationPreview();

  @override
  State<_NavigationPreview> createState() => _NavigationPreviewState();
}

class _NavigationPreviewState extends State<_NavigationPreview> {
  int _navIndex = 0;

  // Each tab gets its own GlobalKey<NavigatorState> for independent navigation
  final _navigatorKeys = List.generate(4, (_) => GlobalKey<NavigatorState>());

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 350.s,
      decoration: BoxDecoration(
        borderRadius: 12.br,
        border: Border.all(color: Colors.grey.shade300),
      ),
      clipBehavior: Clip.antiAlias,
      child: ResponsiveNavigation(
        destinations: const [
          NavDestination(icon: Icons.home, label: 'Home'),
          NavDestination(
              icon: Icons.search, selectedIcon: Icons.search, label: 'Search'),
          NavDestination(
              icon: Icons.notifications_outlined,
              selectedIcon: Icons.notifications,
              label: 'Alerts',
              badge: 5),
          // Profile: shown in bottom nav, hidden in sidebar (moved to footer)
          NavDestination(
              icon: Icons.person_outline,
              selectedIcon: Icons.person,
              label: 'Profile',
              showInSidebar: false),
        ],
        selectedIndex: _navIndex,
        onChanged: (i) => setState(() => _navIndex = i),
        // ── Nested Navigation: sidebar stays fixed, body navigates ──
        body: IndexedStack(
          index: _navIndex,
          children: [
            _NestedNav(
                navKey: _navigatorKeys[0],
                tabLabel: 'Home',
                tabColor: Colors.blue,
                tabIcon: Icons.home),
            _NestedNav(
                navKey: _navigatorKeys[1],
                tabLabel: 'Search',
                tabColor: Colors.green,
                tabIcon: Icons.search),
            _NestedNav(
                navKey: _navigatorKeys[2],
                tabLabel: 'Alerts',
                tabColor: Colors.orange,
                tabIcon: Icons.notifications),
            _NestedNav(
                navKey: _navigatorKeys[3],
                tabLabel: 'Profile',
                tabColor: Colors.purple,
                tabIcon: Icons.person),
          ],
        ),

        // ── Custom bottom nav (full UI control) ──
        bottomNavBuilder: (context, destinations, selected, onChanged) {
          return Container(
            decoration: BoxDecoration(
              color: Colors.white,
              boxShadow: [
                BoxShadow(
                    color: Colors.black.withAlpha(20),
                    blurRadius: 10,
                    offset: const Offset(0, -2))
              ],
            ),
            child: Row(
              mainAxisAlignment: MainAxisAlignment.spaceAround,
              children: List.generate(destinations.length, (i) {
                final d = destinations[i];
                final isActive = i == selected;
                return GestureDetector(
                  onTap: () => onChanged(i),
                  behavior: HitTestBehavior.opaque,
                  child: Padding(
                    padding:
                        EdgeInsets.symmetric(vertical: 8.s, horizontal: 12.w),
                    child: Column(
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        AnimatedContainer(
                          duration: const Duration(milliseconds: 200),
                          padding: EdgeInsets.symmetric(
                              horizontal: isActive ? 16.w : 0),
                          decoration: BoxDecoration(
                            color: isActive
                                ? Colors.indigo.withAlpha(25)
                                : Colors.transparent,
                            borderRadius: BorderRadius.circular(16),
                          ),
                          child: Padding(
                            padding: EdgeInsets.symmetric(vertical: 4.s),
                            child: Icon(
                              isActive ? (d.selectedIcon ?? d.icon) : d.icon,
                              color: isActive ? Colors.indigo : Colors.grey,
                              size: 22.iz,
                            ),
                          ),
                        ),
                        SizedBox(height: 2.s),
                        Text(
                          d.label,
                          style: TextStyle(
                            fontSize: 10.fz,
                            fontWeight:
                                isActive ? FontWeight.bold : FontWeight.normal,
                            color: isActive ? Colors.indigo : Colors.grey,
                          ),
                        ),
                      ],
                    ),
                  ),
                );
              }),
            ),
          );
        },

        // ── Sidebar header ──
        sidebarHeader: const Padding(
          padding: EdgeInsets.all(16),
          child: Row(
            children: [
              Icon(Icons.layers, size: 24, color: Colors.indigo),
              SizedBox(width: 8),
              Expanded(
                child: Text('Scalify App',
                    style: TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                        color: Colors.indigo),
                    maxLines: 1,
                    overflow: TextOverflow.ellipsis),
              ),
            ],
          ),
        ),

        // ── Sidebar footer (Profile card replaces the button) ──
        sidebarFooter: Container(
          padding: const EdgeInsets.all(12),
          decoration: BoxDecoration(
            color: _navIndex == 3
                ? Colors.indigo.withAlpha(25)
                : Colors.grey.shade100,
            borderRadius: BorderRadius.circular(12),
          ),
          child: InkWell(
            onTap: () => setState(() => _navIndex = 3),
            borderRadius: BorderRadius.circular(12),
            child: Row(
              children: [
                CircleAvatar(
                  radius: 16,
                  backgroundColor: Colors.indigo.shade100,
                  child:
                      const Icon(Icons.person, size: 18, color: Colors.indigo),
                ),
                const SizedBox(width: 10),
                const Expanded(
                  child: Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Text('Alaa Hassan',
                          style: TextStyle(
                              fontSize: 13, fontWeight: FontWeight.w600),
                          maxLines: 1,
                          overflow: TextOverflow.ellipsis),
                      Text('Pro Member',
                          style: TextStyle(fontSize: 10, color: Colors.grey)),
                    ],
                  ),
                ),
                const Icon(Icons.settings, size: 16, color: Colors.grey),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

/// Each tab has its own Navigator for independent nested navigation.
class _NestedNav extends StatelessWidget {
  final GlobalKey<NavigatorState> navKey;
  final String tabLabel;
  final MaterialColor tabColor;
  final IconData tabIcon;

  const _NestedNav({
    required this.navKey,
    required this.tabLabel,
    required this.tabColor,
    required this.tabIcon,
  });

  @override
  Widget build(BuildContext context) {
    return Navigator(
      key: navKey,
      onGenerateRoute: (settings) {
        return MaterialPageRoute(
          builder: (_) => _TabListPage(
            label: tabLabel,
            color: tabColor,
            icon: tabIcon,
            navKey: navKey,
          ),
        );
      },
    );
  }
}

/// Root list page for each tab — shows items that push to detail page.
class _TabListPage extends StatelessWidget {
  final String label;
  final MaterialColor color;
  final IconData icon;
  final GlobalKey<NavigatorState> navKey;

  const _TabListPage({
    required this.label,
    required this.color,
    required this.icon,
    required this.navKey,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color.shade50,
      child: Column(
        children: [
          Padding(
            padding: EdgeInsets.all(12.s),
            child: Row(
              children: [
                Icon(icon, size: 24.iz, color: color),
                SizedBox(width: 8.w),
                Text(label,
                    style: TextStyle(
                        fontSize: 16.fz,
                        fontWeight: FontWeight.bold,
                        color: color.shade800)),
                const Spacer(),
                Text('Tap item → push detail',
                    style: TextStyle(fontSize: 10.fz, color: color.shade400)),
              ],
            ),
          ),
          Expanded(
            child: ListView.builder(
              padding: EdgeInsets.symmetric(horizontal: 12.w),
              itemCount: 5,
              itemBuilder: (ctx, index) {
                return Padding(
                  padding: EdgeInsets.only(bottom: 6.s),
                  child: Material(
                    color: Colors.white,
                    borderRadius: BorderRadius.circular(8),
                    child: InkWell(
                      borderRadius: BorderRadius.circular(8),
                      onTap: () {
                        // Push detail page — sidebar stays fixed!
                        navKey.currentState?.push(MaterialPageRoute(
                          builder: (_) => _TabDetailPage(
                            parentLabel: label,
                            itemIndex: index,
                            color: color,
                            navKey: navKey,
                          ),
                        ));
                      },
                      child: Padding(
                        padding: EdgeInsets.all(10.s),
                        child: Row(
                          children: [
                            CircleAvatar(
                              radius: 14.s,
                              backgroundColor: color.shade100,
                              child: Text('${index + 1}',
                                  style: TextStyle(
                                      fontSize: 11.fz,
                                      color: color.shade800,
                                      fontWeight: FontWeight.bold)),
                            ),
                            SizedBox(width: 8.w),
                            Expanded(
                              child: Text('$label Item ${index + 1}',
                                  style: TextStyle(
                                      fontSize: 13.fz, color: color.shade700)),
                            ),
                            Icon(Icons.chevron_right,
                                size: 18.iz, color: color.shade300),
                          ],
                        ),
                      ),
                    ),
                  ),
                );
              },
            ),
          ),
        ],
      ),
    );
  }
}

/// Detail page pushed inside a tab — sidebar stays visible!
class _TabDetailPage extends StatelessWidget {
  final String parentLabel;
  final int itemIndex;
  final MaterialColor color;
  final GlobalKey<NavigatorState> navKey;

  const _TabDetailPage({
    required this.parentLabel,
    required this.itemIndex,
    required this.color,
    required this.navKey,
  });

  @override
  Widget build(BuildContext context) {
    return Container(
      color: color.shade50,
      child: Column(
        children: [
          // Back bar
          Container(
            padding: EdgeInsets.all(8.s),
            color: color.shade100,
            child: Row(
              children: [
                GestureDetector(
                  onTap: () => navKey.currentState?.pop(),
                  child: Row(
                    mainAxisSize: MainAxisSize.min,
                    children: [
                      Icon(Icons.arrow_back,
                          size: 18.iz, color: color.shade800),
                      SizedBox(width: 4.w),
                      Text('Back',
                          style: TextStyle(
                              fontSize: 13.fz,
                              fontWeight: FontWeight.w600,
                              color: color.shade800)),
                    ],
                  ),
                ),
              ],
            ),
          ),
          // Detail content
          Expanded(
            child: Center(
              child: Column(
                mainAxisSize: MainAxisSize.min,
                children: [
                  Icon(Icons.check_circle, size: 40.iz, color: color),
                  SizedBox(height: 8.s),
                  Text('$parentLabel — Detail #${itemIndex + 1}',
                      style: TextStyle(
                          fontSize: 16.fz,
                          fontWeight: FontWeight.bold,
                          color: color.shade800)),
                  SizedBox(height: 4.s),
                  Text('Sidebar stays fixed! ✅',
                      style: TextStyle(fontSize: 12.fz, color: color.shade600)),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }
}
23
likes
160
points
397
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

The Ultimate Responsive Layout System. Features smart scaling, 6-tier grids, adaptive flex layouts, container queries, and 4K protection with zero-allocation math.

Repository (GitHub)
View/report issues

Topics

#responsive #scaling #grid #layout #container-queries

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_scalify