responsive_gridview 2.0.0 copy "responsive_gridview: ^2.0.0" to clipboard
responsive_gridview: ^2.0.0 copied to clipboard

A flexible and responsive GridView for Flutter that adapts to screen sizes, breakpoints, custom column counts, and auto-fit item widths.

example/lib/main.dart

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

/// Main entry point for the Responsive GridView example application.
void main() {
  runApp(const ResponsiveGridViewApp());
}

/// The root widget of the example application.
class ResponsiveGridViewApp extends StatelessWidget {
  /// Creates the root application widget.
  const ResponsiveGridViewApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Responsive GridView Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF6366F1),
          brightness: Brightness.light,
        ),
      ),
      darkTheme: ThemeData(
        useMaterial3: true,
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF6366F1),
          brightness: Brightness.dark,
        ),
      ),
      themeMode: ThemeMode.system,
      home: const ExampleHomePage(),
    );
  }
}

/// The home page showcasing multiple ResponsiveGridView use cases and interactive playgrounds.
class ExampleHomePage extends StatefulWidget {
  /// Creates the home page widget.
  const ExampleHomePage({super.key});

  @override
  State<ExampleHomePage> createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage> {
  int _selectedTabIndex = 0;

  // Interactive playground state
  int _columns = 3;
  double _horizontalSpacing = 16.0;
  double _verticalSpacing = 16.0;
  double _padding = 16.0;
  int _itemCount = 8;
  double? _aspectRatio = 1.2;

  // Auto-fit state
  double _minItemWidth = 180.0;

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Responsive GridView'),
        elevation: 1,
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(48),
          child: SingleChildScrollView(
            scrollDirection: Axis.horizontal,
            padding: const EdgeInsets.symmetric(horizontal: 8),
            child: Row(
              children: [
                _buildTabButton(0, 'Playground', Icons.tune),
                _buildTabButton(1, 'Breakpoints', Icons.devices),
                _buildTabButton(2, 'Auto-Fit Width', Icons.auto_awesome),
                _buildTabButton(3, 'Builder Pattern', Icons.list_alt),
              ],
            ),
          ),
        ),
      ),
      body: IndexedStack(
        index: _selectedTabIndex,
        children: [
          _buildPlaygroundTab(),
          _buildBreakpointsTab(),
          _buildAutoFitTab(),
          _buildBuilderTab(),
        ],
      ),
    );
  }

  Widget _buildTabButton(int index, String label, IconData icon) {
    final isSelected = _selectedTabIndex == index;
    return Padding(
      padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
      child: FilterChip(
        selected: isSelected,
        label: Row(
          mainAxisSize: MainAxisSize.min,
          children: [
            Icon(icon, size: 18),
            const SizedBox(width: 6),
            Text(label),
          ],
        ),
        onSelected: (_) => setState(() => _selectedTabIndex = index),
      ),
    );
  }

  // --- TAB 1: INTERACTIVE PLAYGROUND ---
  Widget _buildPlaygroundTab() {
    return Column(
      children: [
        _buildPlaygroundControls(),
        const Divider(height: 1),
        Expanded(
          child: SingleChildScrollView(
            child: ResponsiveGridView(
              column: _columns,
              horizontalSpacing: _horizontalSpacing,
              verticalSpacing: _verticalSpacing,
              padding: EdgeInsets.all(_padding),
              childAspectRatio: _aspectRatio,
              children: List.generate(
                _itemCount,
                (index) => _buildGridCard(
                  title: 'Item #${index + 1}',
                  subtitle:
                      'Col $_columns • Spacing ${_horizontalSpacing.toInt()}',
                  index: index,
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }

  Widget _buildPlaygroundControls() {
    return ExpansionTile(
      initiallyExpanded: true,
      leading: const Icon(Icons.settings),
      title: Text(
        'Controls: Columns: $_columns | Spacing: ${_horizontalSpacing.toInt()}px | Items: $_itemCount',
        style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 13),
      ),
      children: [
        Padding(
          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
          child: Column(
            children: [
              Row(
                children: [
                  Expanded(
                    child: _buildSlider(
                      label: 'Columns: $_columns',
                      value: _columns.toDouble(),
                      min: 1,
                      max: 6,
                      divisions: 5,
                      onChanged: (val) =>
                          setState(() => _columns = val.round()),
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: _buildSlider(
                      label: 'Items: $_itemCount',
                      value: _itemCount.toDouble(),
                      min: 1,
                      max: 24,
                      divisions: 23,
                      onChanged: (val) =>
                          setState(() => _itemCount = val.round()),
                    ),
                  ),
                ],
              ),
              Row(
                children: [
                  Expanded(
                    child: _buildSlider(
                      label: 'Horiz Spacing: ${_horizontalSpacing.toInt()}px',
                      value: _horizontalSpacing,
                      min: 0,
                      max: 32,
                      divisions: 8,
                      onChanged: (val) =>
                          setState(() => _horizontalSpacing = val),
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: _buildSlider(
                      label: 'Vert Spacing: ${_verticalSpacing.toInt()}px',
                      value: _verticalSpacing,
                      min: 0,
                      max: 32,
                      divisions: 8,
                      onChanged: (val) =>
                          setState(() => _verticalSpacing = val),
                    ),
                  ),
                ],
              ),
              Row(
                children: [
                  Expanded(
                    child: _buildSlider(
                      label: 'Padding: ${_padding.toInt()}px',
                      value: _padding,
                      min: 0,
                      max: 32,
                      divisions: 8,
                      onChanged: (val) => setState(() => _padding = val),
                    ),
                  ),
                  const SizedBox(width: 16),
                  Expanded(
                    child: Row(
                      mainAxisAlignment: MainAxisAlignment.spaceBetween,
                      children: [
                        const Text('Aspect Ratio:',
                            style: TextStyle(fontSize: 12)),
                        DropdownButton<double?>(
                          value: _aspectRatio,
                          isDense: true,
                          items: const [
                            DropdownMenuItem(
                                value: null, child: Text('Auto/Content')),
                            DropdownMenuItem(
                                value: 1.0, child: Text('1:1 Square')),
                            DropdownMenuItem(
                                value: 1.2, child: Text('1.2 Rect')),
                            DropdownMenuItem(
                                value: 16 / 9, child: Text('16:9 Wide')),
                          ],
                          onChanged: (val) =>
                              setState(() => _aspectRatio = val),
                        ),
                      ],
                    ),
                  ),
                ],
              ),
            ],
          ),
        ),
      ],
    );
  }

  Widget _buildSlider({
    required String label,
    required double value,
    required double min,
    required double max,
    required int divisions,
    required ValueChanged<double> onChanged,
  }) {
    return Column(
      crossAxisAlignment: CrossAxisAlignment.start,
      children: [
        Text(label, style: const TextStyle(fontSize: 12)),
        Slider(
          value: value,
          min: min,
          max: max,
          divisions: divisions,
          onChanged: onChanged,
        ),
      ],
    );
  }

  // --- TAB 2: RESPONSIVE BREAKPOINTS ---
  Widget _buildBreakpointsTab() {
    return LayoutBuilder(
      builder: (context, constraints) {
        final width = constraints.maxWidth;
        String activeBp;
        int activeCols;

        if (width >= 1536) {
          activeBp = 'XL (>= 1536px)';
          activeCols = 6;
        } else if (width >= 1200) {
          activeBp = 'LG (>= 1200px)';
          activeCols = 4;
        } else if (width >= 900) {
          activeBp = 'MD (>= 900px)';
          activeCols = 3;
        } else if (width >= 600) {
          activeBp = 'SM (>= 600px)';
          activeCols = 2;
        } else {
          activeBp = 'XS (< 600px)';
          activeCols = 1;
        }

        return SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.stretch,
            children: [
              Card(
                margin: const EdgeInsets.all(16),
                color: Theme.of(context).colorScheme.primaryContainer,
                child: Padding(
                  padding: const EdgeInsets.all(16),
                  child: Column(
                    children: [
                      Text(
                        'Current Width: ${width.toStringAsFixed(1)}px',
                        style: Theme.of(context).textTheme.titleMedium,
                      ),
                      const SizedBox(height: 4),
                      Text(
                        'Active Breakpoint: $activeBp ➔ $activeCols Columns',
                        style: Theme.of(context)
                            .textTheme
                            .bodyMedium
                            ?.copyWith(fontWeight: FontWeight.bold),
                      ),
                      const SizedBox(height: 8),
                      const Text(
                        'Resize your browser window or rotate your device to see columns automatically adapt!',
                        textAlign: TextAlign.center,
                        style: TextStyle(fontSize: 12),
                      ),
                    ],
                  ),
                ),
              ),
              ResponsiveGridView(
                columns: const ResponsiveColumns(
                  xs: 1,
                  sm: 2,
                  md: 3,
                  lg: 4,
                  xl: 6,
                ),
                horizontalSpacing: 16,
                verticalSpacing: 16,
                padding: const EdgeInsets.symmetric(horizontal: 16),
                childAspectRatio: 1.3,
                children: List.generate(
                  12,
                  (index) => _buildGridCard(
                    title: 'Tile #${index + 1}',
                    subtitle: 'Breakpoint Layout',
                    index: index,
                  ),
                ),
              ),
              const SizedBox(height: 24),
            ],
          ),
        );
      },
    );
  }

  // --- TAB 3: AUTO-FIT MIN ITEM WIDTH ---
  Widget _buildAutoFitTab() {
    return Column(
      children: [
        Card(
          margin: const EdgeInsets.all(16),
          child: Padding(
            padding: const EdgeInsets.all(16),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Row(
                  mainAxisAlignment: MainAxisAlignment.spaceBetween,
                  children: [
                    const Text('Auto-Fit Minimum Item Width:',
                        style: TextStyle(fontWeight: FontWeight.bold)),
                    Text('${_minItemWidth.toInt()} px',
                        style: const TextStyle(fontWeight: FontWeight.bold)),
                  ],
                ),
                Slider(
                  value: _minItemWidth,
                  min: 100,
                  max: 400,
                  divisions: 30,
                  onChanged: (val) => setState(() => _minItemWidth = val),
                ),
                const Text(
                  'The grid automatically calculates how many items fit based on available container width.',
                  style: TextStyle(fontSize: 12, color: Colors.grey),
                ),
              ],
            ),
          ),
        ),
        Expanded(
          child: SingleChildScrollView(
            child: ResponsiveGridView(
              minItemWidth: _minItemWidth,
              horizontalSpacing: 16,
              verticalSpacing: 16,
              padding: const EdgeInsets.symmetric(horizontal: 16),
              childAspectRatio: 1.2,
              children: List.generate(
                12,
                (index) => _buildGridCard(
                  title: 'Product #${index + 1}',
                  subtitle: 'Min width ${_minItemWidth.toInt()}px',
                  index: index,
                ),
              ),
            ),
          ),
        ),
      ],
    );
  }

  // --- TAB 4: BUILDER PATTERN ---
  Widget _buildBuilderTab() {
    return ResponsiveGridView.builder(
      minItemWidth: 200,
      horizontalSpacing: 16,
      verticalSpacing: 16,
      padding: const EdgeInsets.all(16),
      itemCount: 40,
      childAspectRatio: 1.4,
      itemBuilder: (context, index) => _buildGridCard(
        title: 'Lazy Item #$index',
        subtitle: 'Indexed item from builder',
        index: index,
      ),
    );
  }

  Widget _buildGridCard({
    required String title,
    required String subtitle,
    required int index,
  }) {
    final colors = [
      Colors.indigo,
      Colors.teal,
      Colors.deepOrange,
      Colors.purple,
      Colors.blue,
      Colors.pink,
      Colors.amber.shade900,
      Colors.cyan.shade800,
    ];
    final color = colors[index % colors.length];

    return Card(
      elevation: 2,
      shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
      child: Container(
        padding: const EdgeInsets.all(12),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(12),
          gradient: LinearGradient(
            begin: Alignment.topLeft,
            end: Alignment.bottomRight,
            colors: [
              color.withAlpha(40),
              color.withAlpha(15),
            ],
          ),
        ),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            CircleAvatar(
              radius: 16,
              backgroundColor: color,
              child: Text(
                '${index + 1}',
                style: const TextStyle(
                  color: Colors.white,
                  fontWeight: FontWeight.bold,
                  fontSize: 12,
                ),
              ),
            ),
            const Spacer(),
            Text(
              title,
              style: const TextStyle(fontWeight: FontWeight.bold, fontSize: 14),
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
            ),
            const SizedBox(height: 2),
            Text(
              subtitle,
              style: TextStyle(
                fontSize: 11,
                color: Theme.of(context).textTheme.bodySmall?.color,
              ),
              maxLines: 1,
              overflow: TextOverflow.ellipsis,
            ),
          ],
        ),
      ),
    );
  }
}
11
likes
160
points
192
downloads

Documentation

API reference

Publisher

verified publishertherohitsoni.in

Weekly Downloads

A flexible and responsive GridView for Flutter that adapts to screen sizes, breakpoints, custom column counts, and auto-fit item widths.

Homepage
Repository (GitHub)
View/report issues

License

BSD-3-Clause (license)

Dependencies

flutter

More

Packages that depend on responsive_gridview