Skip to main content

responsive_metrics 0.0.1 copy "responsive_metrics: ^0.0.1" to clipboard
responsive_metrics: ^0.0.1 copied to clipboard

A lightweight, dependency-free responsive design toolkit for Flutter — pixel scaling, breakpoint checks, and per-device values.

example/lib/main.dart

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

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

// Define RV constants once, like you would in a real app's design-tokens
// file, then reuse them anywhere a widget has a BuildContext.
class AppFontSize {
  // A plain double like this just scales normally with .sp — no RV needed.
  static const body = 14.0;

  // These are genuinely different values per device class (not scaled
  // versions of each other), so RV is the right tool here.
  static const cardTitle = RV(mobile: 18.0, tablet: 22.0, desktop: 26.0);
  static const pageHeader = RV(mobile: 22.0, tablet: 30.0); // no desktop
  // value given, so it falls back to tablet's 30.0 on desktop too.
}

class AppSpacing {
  // Padding that should be noticeably roomier on tablet/desktop, not just
  // a proportional scale-up of the mobile value.
  static const cardPadding = RV(mobile: 12.0, tablet: 20.0, desktop: 28.0);
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'responsive_metrics example',
      // Initialize ScreenScaler once here so it stays in sync on resize.
      builder: (context, child) {
        ScreenScaler.instance.init(MediaQuery.sizeOf(context));
        return child!;
      },
      home: const HomePage(),
      routes: {'/rv': (context) => const RvExamplePage()},
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Row(
        children: [
          // Layout decision: only shown on desktop.
          if (context.isDesktop)
            Container(width: 220.w, color: Colors.grey.shade200),
          Expanded(
            child: Padding(
              padding: EdgeInsets.all(16.w),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Hello, responsive_metrics',
                    style: TextStyle(
                      fontSize: 22.sp,
                      fontWeight: FontWeight.bold,
                    ),
                  ),
                  SizedBox(height: 8.h),
                  Container(
                    padding: EdgeInsets.all(12.w),
                    decoration: BoxDecoration(
                      color: Colors.blue.shade50,
                      borderRadius: BorderRadius.circular(12.r),
                    ),
                    child: Text(
                      context.pick(
                        mobile: 'Mobile layout',
                        tablet: 'Tablet layout',
                        desktop: 'Desktop layout',
                      ),
                      style: TextStyle(fontSize: 14.sp),
                    ),
                  ),
                  SizedBox(height: 16.h),
                  OutlinedButton(
                    onPressed: () => Navigator.pushNamed(context, '/rv'),
                    child: const Text('See RV example'),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
      bottomNavigationBar: context.isMobile
          ? BottomNavigationBar(
              items: const [
                BottomNavigationBarItem(
                  icon: Icon(Icons.home),
                  label: 'Home',
                ),
                BottomNavigationBarItem(
                  icon: Icon(Icons.settings),
                  label: 'Settings',
                ),
              ],
            )
          : null,
    );
  }
}

/// Shows RV used the way you'd actually use it in a real screen: constants
/// defined once (see [AppFontSize] / [AppSpacing] above), then resolved
/// against `context` at the point of use via `.sp(context)`, `.w(context)`,
/// `.h(context)`, `.r(context)`, or `.value(context)`.
class RvExamplePage extends StatelessWidget {
  const RvExamplePage({super.key});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('RV example')),
      body: SingleChildScrollView(
        padding: EdgeInsets.all(16.w),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            // Header size genuinely jumps per class, not just scales.
            Text(
              'Page header',
              style: TextStyle(
                fontSize: AppFontSize.pageHeader.sp(context),
                fontWeight: FontWeight.bold,
              ),
            ),
            SizedBox(height: 16.h),

            // A card whose internal padding is deliberately different per
            // device class (roomier on larger screens), combined with a
            // title size that is also RV-driven.
            Container(
              width: double.infinity,
              padding: EdgeInsets.all(AppSpacing.cardPadding.w(context)),
              decoration: BoxDecoration(
                color: Colors.blue.shade50,
                borderRadius: BorderRadius.circular(16.r),
              ),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text(
                    'Card title',
                    style: TextStyle(
                      fontSize: AppFontSize.cardTitle.sp(context),
                      fontWeight: FontWeight.w600,
                    ),
                  ),
                  SizedBox(height: 8.h),
                  Text(
                    'Body text uses a plain 14.sp — it scales normally, '
                    'no RV needed since it is the same value everywhere.',
                    style: TextStyle(fontSize: AppFontSize.body.sp),
                  ),
                ],
              ),
            ),
            SizedBox(height: 16.h),

            // Reading the raw resolved number, e.g. to feed into your own
            // calculation instead of one of the .w/.h/.r/.sp shortcuts.
            Text(
              'Raw pageHeader value for this screen: '
              '${AppFontSize.pageHeader.value(context)}',
              style: TextStyle(fontSize: 12.sp, color: Colors.grey.shade700),
            ),
          ],
        ),
      ),
    );
  }
}
1
likes
0
points
256
downloads

Publisher

unverified uploader

Weekly Downloads

A lightweight, dependency-free responsive design toolkit for Flutter — pixel scaling, breakpoint checks, and per-device values.

License

unknown (license)

Dependencies

flutter

More

Packages that depend on responsive_metrics