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

A universal, adaptive list view widget with automatic layout switching, skeleton loading, drag & drop, A-Z jump bar, selection modes, swipe actions, and responsive design.

example/main.dart

import 'package:flutter/material.dart';
import 'package:ajit_listview/advanced_list_view.dart';

void main() {
  runApp(const MyApp());
}

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Advanced ListView Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
        useMaterial3: true,
      ),
      home: const DemoHomePage(),
    );
  }
}

class Contact {
  final String name;
  final String email;
  final String avatar;
  final String initial;

  Contact({
    required this.name,
    required this.email,
    required this.avatar,
  }) : initial = name.isNotEmpty ? name[0].toUpperCase() : '#';

  @override
  String toString() => name;
}

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

  @override
  State<DemoHomePage> createState() => _DemoHomePageState();
}

class _DemoHomePageState extends State<DemoHomePage> {
  LayoutMode _layoutMode = LayoutMode.auto;
  bool _isLoading = false;
  bool _showSelectionMode = false; // For UI only
  bool _singleSelectEnabled = false;
  Set<Contact> _selectedItems = {};
  Contact? _selectedItem;

  final List<Contact> _contacts = List.generate(
    100,
    (index) => Contact(
      name: 'Contact ${index + 1}',
      email: 'contact${index + 1}@example.com',
      avatar: 'https://i.pravatar.cc/150?img=${index + 1}',
    ),
  );

  @override
  void initState() {
    super.initState();
    // Sort contacts by name for better demo
    _contacts.sort((a, b) => a.name.compareTo(b.name));
  }

  void _toggleLayoutMode() {
    setState(() {
      switch (_layoutMode) {
        case LayoutMode.auto:
          _layoutMode = LayoutMode.list;
          break;
        case LayoutMode.list:
          _layoutMode = LayoutMode.grid;
          break;
        case LayoutMode.grid:
          _layoutMode = LayoutMode.auto;
          break;
      }
    });
  }

  String _getLayoutModeText() {
    switch (_layoutMode) {
      case LayoutMode.auto:
        return 'Auto';
      case LayoutMode.list:
        return 'List';
      case LayoutMode.grid:
        return 'Grid';
    }
  }

  Future<void> _handleRefresh() async {
    setState(() {
      _isLoading = true;
    });
    await Future.delayed(const Duration(seconds: 2));
    setState(() {
      _isLoading = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: _showSelectionMode
            ? IconButton(
                icon: const Icon(Icons.close),
                onPressed: () {
                  setState(() {
                    _showSelectionMode = false;
                    _selectedItems.clear();
                  });
                },
                tooltip: 'Exit Selection Mode',
              )
            : null,
        title: _showSelectionMode
            ? Text('${_selectedItems.length} selected')
            : const Text('Advanced ListView Demo'),
        actions: [
          if (!_showSelectionMode) ...[
            IconButton(
              icon: const Icon(Icons.swap_horiz),
              onPressed: _toggleLayoutMode,
              tooltip: 'Toggle Layout Mode',
            ),
            PopupMenuButton<String>(
              onSelected: (value) {
                setState(() {
                  _showSelectionMode = value == 'multi';
                  _singleSelectEnabled = value == 'single';
                  if (value == 'none') {
                    _selectedItems.clear();
                    _selectedItem = null;
                    _showSelectionMode = false;
                  }
                });
              },
              itemBuilder: (context) => [
                const PopupMenuItem(
                  value: 'none',
                  child: Text('No Selection'),
                ),
                const PopupMenuItem(
                  value: 'single',
                  child: Text('Single Select'),
                ),
                const PopupMenuItem(
                  value: 'multi',
                  child: Text('Multi Select'),
                ),
              ],
            ),
          ],
          if (_showSelectionMode && _selectedItems.isNotEmpty) ...[
            IconButton(
              icon: const Icon(Icons.delete),
              onPressed: () {
                setState(() {
                  for (var contact in _selectedItems) {
                    _contacts.remove(contact);
                  }
                  _selectedItems.clear();
                  _showSelectionMode = false;
                });
                ScaffoldMessenger.of(context).showSnackBar(
                  const SnackBar(
                    content: Text('Selected items deleted'),
                    duration: Duration(seconds: 2),
                  ),
                );
              },
              tooltip: 'Delete Selected',
            ),
          ],
        ],
        bottom: PreferredSize(
          preferredSize: const Size.fromHeight(40.0),
          child: Container(
            padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
            child: Row(
              children: [
                Text(
                  'Mode: ${_getLayoutModeText()}',
                  style: Theme.of(context).textTheme.bodySmall,
                ),
                const Spacer(),
                if (_showSelectionMode || _singleSelectEnabled)
                  Text(
                    _showSelectionMode
                        ? 'Selected: ${_selectedItems.length}'
                        : 'Selected: ${_selectedItem?.name ?? "None"}',
                    style: Theme.of(context).textTheme.bodySmall,
                  ),
              ],
            ),
          ),
        ),
      ),
      body: AdvancedListView<Contact>(
        items: _contacts,
        layoutMode: _layoutMode,
        minItemWidth: 300.0,
        gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent(
          maxCrossAxisExtent: 400.0,
          childAspectRatio: 3.0,
          mainAxisSpacing: 12.0,
          crossAxisSpacing: 12.0,
        ),
        isLoading: _isLoading,
        skeletonCount: 10,
        onRefresh: _handleRefresh,
        multiSelectEnabled: true, // Always enabled for the library
        singleSelectEnabled: _singleSelectEnabled,
        onSelectionChanged: (selected) {
          setState(() {
            _selectedItems = selected;
            // Auto-show/hide selection mode based on selection
            _showSelectionMode = selected.isNotEmpty;
          });
        },
        onSelected: (selected) {
          setState(() {
            _selectedItem = selected;
          });
        },
        showIndexBar: true,
        sortComparator: (a, b) => a.name.compareTo(b.name),
        sortOrderAscending: true,
        onIndexTap: (letter) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(content: Text('Jumped to: $letter')),
          );
        },
        // Swipe actions - leading (swipe right)
        leadingActions: (context, contact, index) => [
          Icon(Icons.star, color: Colors.white),
        ],
        // Swipe actions - trailing (swipe left)
        trailingActions: (context, contact, index) => [
          Icon(Icons.delete, color: Colors.white),
        ],
        // Handle swipe events
        onLeadingSwipe: (contact, index) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('Starred: ${contact.name}'),
              duration: const Duration(seconds: 1),
            ),
          );
        },
        onTrailingSwipe: (contact, index) {
          setState(() {
            _contacts.removeAt(index);
          });
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('Deleted: ${contact.name}'),
              duration: const Duration(seconds: 2),
              action: SnackBarAction(
                label: 'UNDO',
                onPressed: () {
                  setState(() {
                    _contacts.insert(index, contact);
                  });
                },
              ),
            ),
          );
        },
        // Handle item tap (when not in selection mode)
        onItemTap: (contact) {
          // Only show message if no items are selected and not in single-select mode
          if (_selectedItems.isEmpty && !_singleSelectEnabled) {
            ScaffoldMessenger.of(context).showSnackBar(
              SnackBar(
                content: Text('Tapped: ${contact.name}. Long press to select.'),
                duration: const Duration(seconds: 1),
              ),
            );
          }
        },
        itemBuilder: (context, contact, index, constraints) {
          final isGrid = constraints.maxWidth > 300;
          final isSelected = _showSelectionMode
              ? _selectedItems.contains(contact)
              : _selectedItem == contact;

          return Card(
            elevation: isSelected ? 8 : 2,
            margin: EdgeInsets.all(isGrid ? 8.0 : 4.0),
            child: Padding(
              padding: EdgeInsets.all(isGrid ? 16.0 : 12.0),
              child: Row(
                children: [
                  CircleAvatar(
                    radius: isGrid ? 30.0 : 24.0,
                    backgroundColor: Colors.grey.shade300,
                    backgroundImage: NetworkImage(contact.avatar),
                  ),
                  SizedBox(width: isGrid ? 16.0 : 12.0),
                  Expanded(
                    child: Column(
                      crossAxisAlignment: CrossAxisAlignment.start,
                      mainAxisSize: MainAxisSize.min,
                      children: [
                        Text(
                          contact.name,
                          style: TextStyle(
                            fontSize: isGrid ? 18.0 : 16.0,
                            fontWeight: FontWeight.bold,
                          ),
                        ),
                        const SizedBox(height: 4.0),
                        Text(
                          contact.email,
                          style: TextStyle(
                            fontSize: isGrid ? 14.0 : 12.0,
                            color: Colors.grey.shade600,
                          ),
                        ),
                      ],
                    ),
                  ),
                  if (isSelected)
                    Icon(
                      _showSelectionMode
                          ? Icons.check_circle
                          : Icons.radio_button_checked,
                      color: Theme.of(context).colorScheme.primary,
                    ),
                ],
              ),
            ),
          );
        },
        emptyWidget: const Center(
          child: Text('No contacts available'),
        ),
        padding: const EdgeInsets.all(16.0),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: () {
          setState(() {
            _isLoading = !_isLoading;
          });
        },
        tooltip: 'Toggle Loading',
        child: Icon(_isLoading ? Icons.stop : Icons.refresh),
      ),
    );
  }
}
0
likes
150
points
52
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

A universal, adaptive list view widget with automatic layout switching, skeleton loading, drag & drop, A-Z jump bar, selection modes, swipe actions, and responsive design.

Repository (GitHub)
View/report issues

License

MIT (license)

Dependencies

flutter

More

Packages that depend on ajit_listview