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

A highly customizable Flutter bottom sheet with built-in search functionality, perfect for item selection, country pickers, and searchable lists.

example/lib/main.dart

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

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Searchable Bottom Sheet Demo',
      theme: ThemeData(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue), useMaterial3: true),
      home: const ExampleHomePage(),
    );
  }
}

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

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

class _ExampleHomePageState extends State<ExampleHomePage> {
  String? selectedFruit;
  Country? selectedCountry;
  final List<SelectableItem> items = [SelectableItem('Item 1'), SelectableItem('Item 2'), SelectableItem('Item 3'), SelectableItem('Item 4'), SelectableItem('Item 5')];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Searchable Bottom Sheet Examples'), backgroundColor: Theme.of(context).colorScheme.inversePrimary),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          _buildExampleCard(context, title: 'Basic Example', description: 'Simple list selection', onPressed: _showBasicExample, result: selectedFruit),
          const SizedBox(height: 16),
          _buildExampleCard(context, title: 'Country Picker', description: 'With flags and search', onPressed: _showCountryPicker, result: selectedCountry?.name),
          const SizedBox(height: 16),
          _buildExampleCard(context, title: 'Multi-Select with Checkboxes', description: 'Select multiple items', onPressed: _showMultiSelect, result: items.where((i) => i.isSelected).length.toString() + ' selected'),
          const SizedBox(height: 16),
          _buildExampleCard(context, title: 'With Edit/Delete Actions', description: 'Edit and delete buttons', onPressed: _showWithActions),
          const SizedBox(height: 16),
          _buildExampleCard(context, title: 'Custom Styling', description: 'Customized colors and styles', onPressed: _showCustomStyling),
          const SizedBox(height: 16),
          _buildExampleCard(context, title: 'Empty States', description: 'Custom empty and no results', onPressed: _showEmptyStates),
        ],
      ),
    );
  }

  Widget _buildExampleCard(BuildContext context, {required String title, required String description, required VoidCallback onPressed, String? result}) {
    return Card(
      child: InkWell(
        onTap: onPressed,
        borderRadius: BorderRadius.circular(12),
        child: Padding(
          padding: const EdgeInsets.all(16),
          child: Row(
            children: [
              Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  children: [
                    Text(title, style: Theme.of(context).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold)),
                    const SizedBox(height: 4),
                    Text(description, style: Theme.of(context).textTheme.bodySmall?.copyWith(color: Colors.grey.shade600)),
                    if (result != null) ...[
                      const SizedBox(height: 8),
                      Container(
                        padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 4),
                        decoration: BoxDecoration(color: Theme.of(context).primaryColor.withOpacity(0.1), borderRadius: BorderRadius.circular(12)),
                        child: Text(
                          result,
                          style: TextStyle(color: Theme.of(context).primaryColor, fontWeight: FontWeight.w600, fontSize: 12),
                        ),
                      ),
                    ],
                  ],
                ),
              ),
              const Icon(Icons.arrow_forward_ios, size: 16),
            ],
          ),
        ),
      ),
    );
  }

  Future<void> _showBasicExample() async {
    final fruits = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape', 'Honeydew'];

    final result = await CustomizableBottomSheet.show<String>(context: context, data: fruits, hint: 'Search fruit', title: 'Select Fruit', itemBuilder: (fruit) => Text(fruit), searchFilter: (fruit, query) => fruit.toLowerCase().contains(query.toLowerCase()));

    if (result != null) {
      setState(() {
        selectedFruit = result;
      });
    }
  }

  Future<void> _showCountryPicker() async {
    final countries = [Country('United States', 'US', '🇺🇸'), Country('United Kingdom', 'GB', '🇬🇧'), Country('Canada', 'CA', '🇨🇦'), Country('Australia', 'AU', '🇦🇺'), Country('Germany', 'DE', '🇩🇪'), Country('France', 'FR', '🇫🇷'), Country('Japan', 'JP', '🇯🇵'), Country('India', 'IN', '🇮🇳'), Country('Brazil', 'BR', '🇧🇷'), Country('China', 'CN', '🇨🇳')];

    final result = await CustomizableBottomSheet.show<Country>(
      context: context,
      data: countries,
      hint: 'Search country',
      title: 'Select Country',
      itemBuilder: (country) => Row(
        children: [
          Text(country.flag, style: const TextStyle(fontSize: 24)),
          const SizedBox(width: 12),
          Text(country.name),
        ],
      ),
      searchFilter: (country, query) => country.name.toLowerCase().contains(query.toLowerCase()) || country.code.toLowerCase().contains(query.toLowerCase()),
      sortItems: (countries) => countries..sort((a, b) => a.name.compareTo(b.name)),
    );

    if (result != null) {
      setState(() {
        selectedCountry = result;
      });
    }
  }

  Future<void> _showMultiSelect() async {
    await CustomizableBottomSheet.show<SelectableItem>(
      context: context,
      data: items,
      hint: 'Search items',
      title: 'Select Items',
      showCheckbox: true,
      isChecked: (item) => item.isSelected,
      itemBuilder: (item) => Text(item.name),
      searchFilter: (item, query) => item.name.toLowerCase().contains(query.toLowerCase()),
      headerAction: TextButton(
        onPressed: () {
          Navigator.pop(context);
        },
        child: const Text('Done'),
      ),
    );

    setState(() {});
  }

  Future<void> _showWithActions() async {
    final users = [User('John Doe', 'john@example.com'), User('Jane Smith', 'jane@example.com'), User('Bob Johnson', 'bob@example.com'), User('Alice Williams', 'alice@example.com')];

    await CustomizableBottomSheet.show<User>(
      context: context,
      data: users,
      hint: 'Search users',
      title: 'Manage Users',
      itemBuilder: (user) => Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          Text(user.name, style: const TextStyle(fontWeight: FontWeight.bold)),
          Text(user.email, style: TextStyle(color: Colors.grey.shade600, fontSize: 12)),
        ],
      ),
      searchFilter: (user, query) => user.name.toLowerCase().contains(query.toLowerCase()) || user.email.toLowerCase().contains(query.toLowerCase()),
      onEdit: (user, index) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Edit ${user.name}')));
      },
      onDelete: (user, index) {
        ScaffoldMessenger.of(context).showSnackBar(SnackBar(content: Text('Delete ${user.name}')));
      },
    );
  }

  Future<void> _showCustomStyling() async {
    final colors = ['Red', 'Blue', 'Green', 'Yellow', 'Purple', 'Orange'];

    await CustomizableBottomSheet.show<String>(
      context: context,
      data: colors,
      hint: 'Search color',
      title: 'Custom Styled Sheet',
      itemBuilder: (color) => Text(color),
      searchFilter: (color, query) => color.toLowerCase().contains(query.toLowerCase()),
      backgroundColor: Colors.blue.shade50,
      borderRadius: 30.0,
      maxHeightFraction: 0.75,
      titleStyle: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.blue),
      itemTextStyle: const TextStyle(fontSize: 16, color: Colors.black87),
      searchDecoration: InputDecoration(
        hintText: 'Type to search...',
        prefixIcon: const Icon(Icons.search, color: Colors.blue),
        filled: true,
        fillColor: Colors.white,
        border: OutlineInputBorder(
          borderRadius: BorderRadius.circular(20),
          borderSide: const BorderSide(color: Colors.blue),
        ),
      ),
      dragHandle: Container(
        width: 60,
        height: 6,
        margin: const EdgeInsets.only(bottom: 20),
        decoration: BoxDecoration(color: Colors.blue, borderRadius: BorderRadius.circular(3)),
      ),
    );
  }

  Future<void> _showEmptyStates() async {
    await CustomizableBottomSheet.show<String>(
      context: context,
      data: [],
      hint: 'Search',
      title: 'Empty List Example',
      itemBuilder: (item) => Text(item),
      searchFilter: (item, query) => item.contains(query),
      emptyWidget: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          const Icon(Icons.inbox, size: 64, color: Colors.grey),
          const SizedBox(height: 16),
          const Text('No items available'),
          const SizedBox(height: 16),
          ElevatedButton.icon(onPressed: () => Navigator.pop(context), icon: const Icon(Icons.add), label: const Text('Add Item')),
        ],
      ),
    );
  }
}

// Models
class Country {
  final String name;
  final String code;
  final String flag;

  Country(this.name, this.code, this.flag);
}

class SelectableItem {
  final String name;
  bool isSelected;

  SelectableItem(this.name, {this.isSelected = false});
}

class User {
  final String name;
  final String email;

  User(this.name, this.email);
}
3
likes
130
points
91
downloads

Publisher

verified publisherdhananjayafernando.online

Weekly Downloads

A highly customizable Flutter bottom sheet with built-in search functionality, perfect for item selection, country pickers, and searchable lists.

Topics

#bottom-sheet #search #searchable #picker #selection

Documentation

API reference

License

MIT (license)

Dependencies

flutter

More

Packages that depend on customizable_bottom_sheet