screen_detector 0.0.2 copy "screen_detector: ^0.0.2" to clipboard
screen_detector: ^0.0.2 copied to clipboard

A powerful adaptive screen detection and layout system for Flutter (mobile, tablet, desktop, foldables, TV)

example/lib/main.dart

import 'package:example/advanced_features.dart';
import 'package:example/hinge_split.dart';
import 'package:flutter/material.dart';
import 'package:screen_detector/screen_detector.dart';

void main() {
  runApp(const ScreenProvider(child: MyApp()));
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Screen Detector Demo',
      theme: ThemeData(useMaterial3: true),
      home: const HomePage(),
    );
  }
}

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

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  int index = 0;

  @override
  Widget build(BuildContext context) {
    final screen = context.screen;

    return Scaffold(
      appBar: AppBar(title: Text("Adaptive Demo (${screen.screenType.name})")),
      body: AdaptiveNavigation(
        selectedIndex: index,
        onDestinationSelected: (i) => setState(() => index = i),
        destinations: const [
          NavigationDestination(
            icon: Icon(Icons.calendar_today),
            label: "Calendar",
          ),
          NavigationDestination(icon: Icon(Icons.info), label: "Info"),
          NavigationDestination(icon: Icon(Icons.science), label: "Advanced"),
        ],
        body: AdaptiveLayout(
          compact: _MobileView(index: index),
          medium: _TabletView(index: index),
          expanded: _DesktopView(index: index),
          large: _LargeView(index: index),
        ),
      ),
    );
  }
}

//////////////////////////////////////////////////////////////
// 📱 MOBILE
//////////////////////////////////////////////////////////////

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

  @override
  Widget build(BuildContext context) {
    return _Content(title: "📱 Mobile Layout", index: index);
  }
}

//////////////////////////////////////////////////////////////
// 📲 TABLET
//////////////////////////////////////////////////////////////

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

  @override
  Widget build(BuildContext context) {
    return _Content(title: "📲 Tablet Layout", index: index);
  }
}

//////////////////////////////////////////////////////////////
// 🖥 DESKTOP
//////////////////////////////////////////////////////////////

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

  @override
  Widget build(BuildContext context) {
    return _Content(title: "🖥 Desktop Layout", index: index);
  }
}
//////////////////////////////////////////////////////////////
// 📺 LARGE / TV
//////////////////////////////////////////////////////////////

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

  @override
  Widget build(BuildContext context) {
    return _Content(title: "📺 Large / TV Layout", index: index);
  }
}

//////////////////////////////////////////////////////////////
// 🔁 SHARED CONTENT
//////////////////////////////////////////////////////////////

class _Content extends StatelessWidget {
  final String title;
  final int index;

  const _Content({required this.title, required this.index});

  @override
  Widget build(BuildContext context) {
    final screen = context.screen;

    if (index == 0) {
      return _CalendarLayout(screen: screen);
    }

    if (index == 1) {
      return _InfoView(screen: screen);
    }

    return const AdvancedFeaturesPage();
  }
}

class _CalendarLayout extends StatelessWidget {
  final ScreenInfo screen;

  const _CalendarLayout({required this.screen});

  @override
  Widget build(BuildContext context) {
    final hinge = FoldableDetector.detect(context);

    // 🔥 1. TRUE FOLDABLE (UNFOLDED)
    if (hinge != null && screen.posture == DevicePosture.unfolded) {
      return HingeSplitLayout(
        left: Container(
          color: Colors.white, // 👈 important
          child: Column(
            children: const [
              _CalendarHeader(),
              Expanded(child: _MonthGrid()),
            ],
          ),
        ),
        right: Container(
          color: Color(0xFFF7F7F7), // 👈 subtle separation
          child: Column(
            children: const [
              _MiniHeader(),
              Expanded(child: _EventPanel()),
            ],
          ),
        ),
      );
    }

    // 📱 2. MOBILE
    if (screen.isMobile) {
      return Column(
        children: const [
          _EventPanel(),
          _CalendarHeader(),
          Expanded(child: _MonthGrid()),
        ],
      );
    }

    // 📲 3. TABLET (3 pane)
    if (screen.isTablet) {
      return Row(
        children: [
          const _Sidebar(),
          const VerticalDivider(width: 1),
          Expanded(
            child: Column(
              children: const [
                _CalendarHeader(),
                _EventPanel(),
                Expanded(child: _MonthGrid()),
              ],
            ),
          ),
          // const SizedBox(width: 280, child: ),
        ],
      );
    }

    // 🖥 4. DESKTOP (3 pane)
    return Row(
      children: [
        const _Sidebar(),
        const VerticalDivider(width: 1),

        Expanded(
          flex: 2,
          child: Column(
            children: const [
              _CalendarHeader(),
              Expanded(child: _MonthGrid()),
            ],
          ),
        ),

        const VerticalDivider(width: 1),

        const SizedBox(width: 320, child: _EventPanel()),
      ],
    );
  }
}

class _MiniHeader extends StatelessWidget {
  const _MiniHeader();

  @override
  Widget build(BuildContext context) {
    return Container(
      height: 50,
      padding: const EdgeInsets.symmetric(horizontal: 12),
      alignment: Alignment.centerLeft,
      child: const Text(
        "Events",
        style: TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
      ),
    );
  }
}

class _Sidebar extends StatelessWidget {
  const _Sidebar();

  @override
  Widget build(BuildContext context) {
    return Container(
      width: 260,
      color: Colors.grey.shade100,
      padding: const EdgeInsets.all(12),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: const [
          Text("📅 Calendar", style: TextStyle(fontSize: 20)),
          SizedBox(height: 20),
          Text("• My Calendars"),
          SizedBox(height: 8),
          Text("• Tasks"),
          SizedBox(height: 8),
          Text("• Reminders"),
        ],
      ),
    );
  }
}

class _CalendarHeader extends StatelessWidget {
  const _CalendarHeader();

  @override
  Widget build(BuildContext context) {
    return LayoutBuilder(
      builder: (context, constraints) {
        final width = constraints.maxWidth;

        // 🧠 VERY IMPORTANT: adapt to pane width
        if (width < 200) {
          // 🔥 Ultra-compact (foldable narrow pane)
          return SizedBox(
            height: 60,
            child: Row(
              children: const [
                SizedBox(width: 8),
                Icon(Icons.calendar_today, size: 18),
              ],
            ),
          );
        }

        if (width < 400) {
          // 📱 Compact
          return SizedBox(
            height: 60,
            child: Row(
              children: [
                IconButton(
                  onPressed: () {},
                  icon: const Icon(Icons.chevron_left),
                ),
                const Expanded(
                  child: Text("March 2026", overflow: TextOverflow.ellipsis),
                ),
                IconButton(
                  onPressed: () {},
                  icon: const Icon(Icons.chevron_right),
                ),
              ],
            ),
          );
        }

        // 🖥 Full layout
        return SizedBox(
          height: 60,
          child: Row(
            children: [
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.chevron_left),
              ),
              const Text(
                "March 2026",
                style: TextStyle(fontSize: 20, fontWeight: FontWeight.bold),
              ),
              IconButton(
                onPressed: () {},
                icon: const Icon(Icons.chevron_right),
              ),
              const Spacer(),
              const Icon(Icons.search),
            ],
          ),
        );
      },
    );
  }
}

class _MonthGrid extends StatelessWidget {
  const _MonthGrid();

  @override
  Widget build(BuildContext context) {
    final screen = context.screen;

    // 🎯 Adaptive tuning (this is the right way)
    final padding = screen.adaptivePadding;
    final aspectRatio = screen.isMobile
        ? 1.0
        : screen.isTablet
        ? 1.2
        : 1.4;

    final fontSize = screen.isMobile
        ? 12.0
        : screen.isTablet
        ? 14.0
        : 16.0;

    return GridView.builder(
      padding: EdgeInsets.all(padding),
      gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
        crossAxisCount: 7, // ✅ Always 7 for calendar
        childAspectRatio: aspectRatio,
        crossAxisSpacing: padding * 0.3,
        mainAxisSpacing: padding * 0.3,
      ),
      itemCount: 30,
      itemBuilder: (_, i) {
        final isToday = i == DateTime.now().day - 1;

        return Container(
          decoration: BoxDecoration(
            color: isToday
                ? Theme.of(context).colorScheme.primary.withAlpha(39)
                : null,
            borderRadius: BorderRadius.circular(8),
          ),
          child: Stack(
            children: [
              // 📅 Day number
              Positioned(
                top: 6,
                right: 6,
                child: Text(
                  "${i + 1}",
                  style: TextStyle(
                    fontSize: fontSize,
                    fontWeight: isToday ? FontWeight.bold : FontWeight.normal,
                    color: isToday
                        ? Theme.of(context).colorScheme.primary
                        : null,
                  ),
                ),
              ),

              // 📝 Fake event dots (visual polish)
              if (i % 5 == 0)
                Positioned(
                  bottom: 6,
                  left: 6,
                  child: Container(
                    width: 6,
                    height: 6,
                    decoration: BoxDecoration(
                      color: Colors.blue,
                      borderRadius: BorderRadius.circular(3),
                    ),
                  ),
                ),
            ],
          ),
        );
      },
    );
  }
}

class _EventPanel extends StatelessWidget {
  const _EventPanel();

  @override
  Widget build(BuildContext context) {
    return Container(
      color: Colors.grey.shade50,
      padding: const EdgeInsets.all(12),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: const [
          Text("Events", style: TextStyle(fontSize: 18)),
          SizedBox(height: 10),
          Text("• Meeting at 10 AM"),
          Text("• Lunch at 1 PM"),
        ],
      ),
    );
  }
}

class _InfoView extends StatelessWidget {
  final ScreenInfo screen;

  const _InfoView({required this.screen});

  @override
  Widget build(BuildContext context) {
    return Center(
      child: Text(
        "Platform: ${screen.platform.name}\n"
        "Device Type: ${screen.deviceType.name}\n"
        "Category: ${screen.category.name}\n"
        "Screen Type: ${screen.screenType.name}\n"
        "Posture: ${screen.posture.name}\n"
        "Orientation: ${screen.orientation.name}\n"
        "Input Type: ${screen.inputType.name}\n"
        "Is Hybrid Input: ${screen.isHybridInput}\n"
        "Supports Touch: ${screen.supportsTouch}\n"
        "Supports Mouse/Keyboard: ${screen.supportsMouseKeyboard}\n"
        "Resolution: ${screen.width.toInt()} x ${screen.height.toInt()}\n"
        "Aspect Ratio: ${screen.aspectRatio.toStringAsFixed(2)}\n"
        "Aspect Type: ${screen.aspectType.name}\n"
        "Pixel Ratio: ${screen.devicePixelRatio.toStringAsFixed(1)}\n"
        "Density: ${screen.densityType.name}\n"
        "Is Mobile: ${screen.isMobile}\n"
        "Is Tablet: ${screen.isTablet}\n"
        "Is Desktop: ${screen.isDesktop}\n"
        "Is Watch: ${screen.isWatch}\n"
        "Is TV: ${screen.isTV}\n"
        "Is Foldable: ${screen.isFoldable}\n"
        "Is Wearable: ${screen.isWearable}\n"
        "Is Wide: ${screen.isWide}\n"
        "Is Tiny Screen: ${screen.isTinyScreen}\n"
        "Is Minimal UI: ${screen.isMinimalUI}\n"
        "Adaptive Padding: ${screen.adaptivePadding}\n"
        "Adaptive Icon Size: ${screen.adaptiveIconSize}\n"
        "Adaptive Button Height: ${screen.adaptiveButtonHeight}",
        textAlign: TextAlign.center,
      ),
    );
  }
}
2
likes
0
points
196
downloads

Publisher

verified publisherkumpali.com

Weekly Downloads

A powerful adaptive screen detection and layout system for Flutter (mobile, tablet, desktop, foldables, TV)

Repository (GitHub)
View/report issues

Topics

#responsive #adaptive #layout #flutter

License

unknown (license)

Dependencies

flutter

More

Packages that depend on screen_detector