screenx 1.0.0 copy "screenx: ^1.0.0" to clipboard
screenx: ^1.0.0 copied to clipboard

Centralised responsive configuration for Flutter — adaptive breakpoints, sp/dp converters, and ScreenXLayout widget for Android, iOS, and Web. One init, fully responsive.

example/lib/main.dart

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

void main() => runApp(const ScreenXExampleApp());

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'screenx example',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.indigo),
        useMaterial3: true,
      ),
      // ── Single init: wrap MaterialApp.builder once ────────────────
      builder: (context, child) => ScreenXLayout(child: child!),
      home: const ExampleHome(),
    );
  }
}

// ── Home ──────────────────────────────────────────────────────────────────────

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(
          'screenx demo',
          // sp() — scales with the user's system text-size preference
          style: TextStyle(fontSize: ScreenX.sp(20)),
        ),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
      ),
      body: ListView(
        padding: EdgeInsets.all(ScreenX.dp(16)),
        children: const [
          _CurrentBreakpointCard(),
          SizedBox(height: 16),
          _UnitConverterCard(),
          SizedBox(height: 16),
          _BuilderPatternCard(),
          SizedBox(height: 16),
          _SwitchPatternCard(),
          SizedBox(height: 16),
          _BreakpointReferenceCard(),
        ],
      ),
    );
  }
}

// ── Current breakpoint ────────────────────────────────────────────────────────

class _CurrentBreakpointCard extends StatelessWidget {
  const _CurrentBreakpointCard();

  @override
  Widget build(BuildContext context) {
    // ScreenXBuilder rebuilds only when the breakpoint tier changes.
    return ScreenXBuilder(
      builder: (context, bp) {
        final color = _tierColor(bp);
        return _Card(
          title: 'Active breakpoint',
          child: Row(
            children: [
              Container(
                width: ScreenX.dp(12),
                height: ScreenX.dp(12),
                decoration: BoxDecoration(color: color, shape: BoxShape.circle),
              ),
              SizedBox(width: ScreenX.dp(8)),
              Text(
                bp.tier.name.toUpperCase(),
                style: TextStyle(
                  fontSize: ScreenX.sp(28),
                  fontWeight: FontWeight.bold,
                  color: color,
                ),
              ),
              const Spacer(),
              Text(
                _tierDescription(bp),
                style: TextStyle(
                  fontSize: ScreenX.sp(14),
                  color: Colors.grey[600],
                ),
              ),
            ],
          ),
        );
      },
    );
  }

  Color _tierColor(ScreenBreakpoint bp) {
    if (bp.isXS) return Colors.red;
    if (bp.isSM) return Colors.orange;
    if (bp.isMD) return Colors.amber;
    if (bp.isLG) return Colors.green;
    return Colors.indigo;
  }

  String _tierDescription(ScreenBreakpoint bp) {
    if (bp.isMobile) return 'mobile';
    if (bp.isTablet) return 'tablet';
    return 'desktop';
  }
}

// ── Unit converter showcase ───────────────────────────────────────────────────

class _UnitConverterCard extends StatelessWidget {
  const _UnitConverterCard();

  @override
  Widget build(BuildContext context) {
    final width = MediaQuery.sizeOf(context).width;
    final height = MediaQuery.sizeOf(context).height;

    return _Card(
      title: 'Unit converters',
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          _Row('sp(16)', '${ScreenX.sp(16).toStringAsFixed(1)} px',
              'scales with text accessibility setting'),
          _Row('dp(16)', '${ScreenX.dp(16).toStringAsFixed(1)} px',
              'logical pixel pass-through'),
          _Row('wp(50)', '${ScreenX.wp(50).toStringAsFixed(1)} px',
              '50 % of ${width.toStringAsFixed(0)} px width'),
          _Row('hp(25)', '${ScreenX.hp(25).toStringAsFixed(1)} px',
              '25 % of ${height.toStringAsFixed(0)} px height'),
        ],
      ),
    );
  }
}

class _Row extends StatelessWidget {
  const _Row(this.method, this.value, this.description);
  final String method;
  final String value;
  final String description;

  @override
  Widget build(BuildContext context) {
    return Padding(
      padding: EdgeInsets.symmetric(vertical: ScreenX.dp(4)),
      child: Row(
        children: [
          SizedBox(
            width: ScreenX.wp(22),
            child: Text(
              method,
              style: TextStyle(
                fontSize: ScreenX.sp(13),
                fontFamily: 'monospace',
                fontWeight: FontWeight.w600,
              ),
            ),
          ),
          SizedBox(
            width: ScreenX.wp(18),
            child: Text(
              value,
              style: TextStyle(fontSize: ScreenX.sp(13)),
            ),
          ),
          Expanded(
            child: Text(
              description,
              style: TextStyle(
                fontSize: ScreenX.sp(12),
                color: Colors.grey[600],
              ),
            ),
          ),
        ],
      ),
    );
  }
}

// ── Builder pattern ───────────────────────────────────────────────────────────

class _BuilderPatternCard extends StatelessWidget {
  const _BuilderPatternCard();

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: 'ScreenXBuilder — builder pattern',
      child: ScreenXBuilder(
        builder: (context, bp) {
          if (bp.isMobile) {
            return const _LayoutChip(
              label: 'Mobile layout',
              icon: Icons.phone_android,
              color: Colors.orange,
            );
          }
          if (bp.isTablet) {
            return const _LayoutChip(
              label: 'Tablet layout',
              icon: Icons.tablet,
              color: Colors.amber,
            );
          }
          return const _LayoutChip(
            label: 'Desktop layout',
            icon: Icons.desktop_mac,
            color: Colors.indigo,
          );
        },
      ),
    );
  }
}

class _LayoutChip extends StatelessWidget {
  const _LayoutChip({
    required this.label,
    required this.icon,
    required this.color,
  });
  final String label;
  final IconData icon;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return Container(
      padding: EdgeInsets.symmetric(
        horizontal: ScreenX.dp(16),
        vertical: ScreenX.dp(12),
      ),
      decoration: BoxDecoration(
        color: color.withValues(alpha: 0.1),
        borderRadius: BorderRadius.circular(ScreenX.dp(8)),
        border: Border.all(color: color.withValues(alpha: 0.4)),
      ),
      child: Row(
        mainAxisSize: MainAxisSize.min,
        children: [
          Icon(icon, color: color, size: ScreenX.dp(20)),
          SizedBox(width: ScreenX.dp(8)),
          Text(
            label,
            style: TextStyle(
              fontSize: ScreenX.sp(15),
              color: color,
              fontWeight: FontWeight.w600,
            ),
          ),
        ],
      ),
    );
  }
}

// ── Switch pattern ────────────────────────────────────────────────────────────

class _SwitchPatternCard extends StatelessWidget {
  const _SwitchPatternCard();

  @override
  Widget build(BuildContext context) {
    return const _Card(
      title: 'ScreenXBuilder.switch_ — switch pattern',
      child: ScreenXBuilder.switch_(
        mobile: _SwitchChip(
          label: 'Single-column (mobile)',
          columns: 1,
          color: Colors.orange,
        ),
        tablet: _SwitchChip(
          label: 'Two-column (tablet)',
          columns: 2,
          color: Colors.amber,
        ),
        desktop: _SwitchChip(
          label: 'Three-column (desktop)',
          columns: 3,
          color: Colors.indigo,
        ),
      ),
    );
  }
}

class _SwitchChip extends StatelessWidget {
  const _SwitchChip({
    required this.label,
    required this.columns,
    required this.color,
  });
  final String label;
  final int columns;
  final Color color;

  @override
  Widget build(BuildContext context) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label, style: TextStyle(fontSize: ScreenX.sp(14), color: color)),
        SizedBox(height: ScreenX.dp(8)),
        Row(
          children: List.generate(
            columns,
            (i) => Expanded(
              child: Container(
                margin: EdgeInsets.only(
                  right: i < columns - 1 ? ScreenX.dp(8) : 0,
                ),
                height: ScreenX.dp(40),
                decoration: BoxDecoration(
                  color: color.withValues(alpha: 0.15),
                  borderRadius: BorderRadius.circular(ScreenX.dp(6)),
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }
}

// ── Breakpoint reference (five tiers side-by-side) ────────────────────────────

class _BreakpointReferenceCard extends StatelessWidget {
  const _BreakpointReferenceCard();

  static const _tiers = [
    _TierInfo('xs', '0 px', 'Phone', Colors.red),
    _TierInfo('sm', '480 px', 'Phone+', Colors.orange),
    _TierInfo('md', '768 px', 'Tablet', Colors.amber),
    _TierInfo('lg', '1024 px', 'Desktop', Colors.green),
    _TierInfo('xl', '1280 px', 'Wide', Colors.indigo),
  ];

  @override
  Widget build(BuildContext context) {
    return _Card(
      title: 'Breakpoint scale — resize to see active tier highlight',
      child: ScreenXBuilder(
        builder: (context, bp) {
          return Row(
            children: _tiers
                .map(
                  (t) => Expanded(
                    child: _TierCell(info: t, active: t.name == bp.tier.name),
                  ),
                )
                .toList(),
          );
        },
      ),
    );
  }
}

class _TierInfo {
  const _TierInfo(this.name, this.minWidth, this.label, this.color);
  final String name;
  final String minWidth;
  final String label;
  final Color color;
}

class _TierCell extends StatelessWidget {
  const _TierCell({required this.info, required this.active});
  final _TierInfo info;
  final bool active;

  @override
  Widget build(BuildContext context) {
    return AnimatedContainer(
      duration: const Duration(milliseconds: 200),
      margin: EdgeInsets.symmetric(horizontal: ScreenX.dp(2)),
      padding: EdgeInsets.symmetric(vertical: ScreenX.dp(10)),
      decoration: BoxDecoration(
        color: active ? info.color : info.color.withValues(alpha: 0.08),
        borderRadius: BorderRadius.circular(ScreenX.dp(6)),
        border: Border.all(
          color: active ? info.color : info.color.withValues(alpha: 0.2),
          width: active ? 2 : 1,
        ),
      ),
      child: Column(
        children: [
          Text(
            info.name,
            style: TextStyle(
              fontSize: ScreenX.sp(13),
              fontWeight: FontWeight.bold,
              color: active ? Colors.white : info.color,
            ),
          ),
          SizedBox(height: ScreenX.dp(2)),
          Text(
            info.minWidth,
            style: TextStyle(
              fontSize: ScreenX.sp(9),
              color: active ? Colors.white70 : Colors.grey[500],
            ),
          ),
          SizedBox(height: ScreenX.dp(2)),
          Text(
            info.label,
            style: TextStyle(
              fontSize: ScreenX.sp(9),
              color: active ? Colors.white70 : Colors.grey[500],
            ),
          ),
        ],
      ),
    );
  }
}

// ── Shared card shell ─────────────────────────────────────────────────────────

class _Card extends StatelessWidget {
  const _Card({required this.title, required this.child});
  final String title;
  final Widget child;

  @override
  Widget build(BuildContext context) {
    return Card(
      elevation: 0,
      shape: RoundedRectangleBorder(
        borderRadius: BorderRadius.circular(ScreenX.dp(12)),
        side: BorderSide(color: Colors.grey.withValues(alpha: 0.2)),
      ),
      child: Padding(
        padding: EdgeInsets.all(ScreenX.dp(16)),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: TextStyle(
                fontSize: ScreenX.sp(12),
                fontWeight: FontWeight.w600,
                color: Colors.grey[500],
                letterSpacing: 0.5,
              ),
            ),
            SizedBox(height: ScreenX.dp(12)),
            child,
          ],
        ),
      ),
    );
  }
}
1
likes
160
points
6
downloads

Documentation

API reference

Publisher

verified publisherappamania.in

Weekly Downloads

Centralised responsive configuration for Flutter — adaptive breakpoints, sp/dp converters, and ScreenXLayout widget for Android, iOS, and Web. One init, fully responsive.

Repository (GitHub)
View/report issues

Topics

#responsive #layout #ui #screen #flutter

License

MIT (license)

Dependencies

flutter

More

Packages that depend on screenx