flutter_bottom_sheet_pickers 0.1.4 copy "flutter_bottom_sheet_pickers: ^0.1.4" to clipboard
flutter_bottom_sheet_pickers: ^0.1.4 copied to clipboard

Customizable Flutter bottom sheet pickers for single, multiple, searchable, lazy loaded, cascade, date, date range, and year-month selection.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_bottom_sheet_pickers/flutter_bottom_sheet_pickers.dart';

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

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

  @override
  State<PickerExampleApp> createState() => _PickerExampleAppState();
}

class _PickerExampleAppState extends State<PickerExampleApp> {
  Locale _locale = const Locale("en");

  @override
  void initState() {
    super.initState();
    _applyPickerLocalizations(_locale);
  }

  @override
  void dispose() {
    BottomSheetPickers.clearLocalizations();
    super.dispose();
  }

  void _applyPickerLocalizations(Locale locale) {
    BottomSheetPickers.setLocalizations(
        localizations: BottomPickerLocalizations.byLocale(locale));
  }

  void _toggleLocale() {
    final nextLocale =
        _locale.languageCode == "en" ? const Locale("zh") : const Locale("en");
    _applyPickerLocalizations(nextLocale);
    setState(() => _locale = nextLocale);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: "Flutter Bottom Pickers",
      debugShowCheckedModeBanner: false,
      locale: _locale,
      supportedLocales: const [
        Locale("en"),
        Locale("zh"),
      ],
      localizationsDelegates: const [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: const Color(0xFF1677FF)),
        useMaterial3: true,
      ),
      home: PickerExampleHome(
        locale: _locale,
        onToggleLocale: _toggleLocale,
      ),
    );
  }
}

class PickerExampleHome extends StatefulWidget {
  final Locale locale;
  final VoidCallback onToggleLocale;

  const PickerExampleHome({
    super.key,
    required this.locale,
    required this.onToggleLocale,
  });

  @override
  State<PickerExampleHome> createState() => _PickerExampleHomeState();
}

class _PickerExampleHomeState extends State<PickerExampleHome> {
  String _result = "No selection";
  static const BottomPickerTheme _theme = BottomPickerTheme(
    primaryColor: Color(0xFF1677FF),
    buttonBorderRadius: BorderRadius.all(Radius.circular(16)),
  );

  final List<CascadeOption> _locations = const [
    CascadeOption(
      id: "province_a",
      label: "Province A",
      children: [
        CascadeOption(
          id: "city_a",
          label: "City A",
          children: [
            CascadeOption(id: "town_a", label: "Town A"),
            CascadeOption(id: "town_b", label: "Town B"),
          ],
        ),
        CascadeOption(id: "city_b", label: "City B"),
      ],
    ),
    CascadeOption(
      id: "province_b",
      label: "Province B",
      children: [
        CascadeOption(id: "city_c", label: "City C"),
      ],
    ),
  ];

  final List<String> _stores = const [
    "PVS Store 001",
    "PVS Store 002",
    "FS Store 001",
    "FS Store 002",
    "KIOSK Store 001",
    "KIOSK Store 002",
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text("Flutter Bottom Pickers"),
        actions: [
          TextButton(
            onPressed: widget.onToggleLocale,
            child: Text(widget.locale.languageCode == "en" ? "δΈ­ζ–‡" : "EN"),
          ),
        ],
      ),
      body: ListView(
        padding: const EdgeInsets.all(16),
        children: [
          Text(_result, style: Theme.of(context).textTheme.titleMedium),
          const SizedBox(height: 24),
          FilledButton(
            onPressed: _showSinglePicker,
            child: const Text("Show single picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showMultiplePicker,
            child: const Text("Show multiple picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showCascadePicker,
            child: const Text("Show cascade picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showMultipleCascadePicker,
            child: const Text("Show multiple cascade picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showLazyPicker,
            child: const Text("Show lazy picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showLocalFilterPicker,
            child: const Text("Show local filter picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showRemoteFilterPicker,
            child: const Text("Show remote filter picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showCalendarPicker,
            child: const Text("Show calendar picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showCalendarRangePicker,
            child: const Text("Show calendar range picker"),
          ),
          const SizedBox(height: 12),
          FilledButton(
            onPressed: _showYearMonthPicker,
            child: const Text("Show year month picker"),
          ),
        ],
      ),
    );
  }

  Future<void> _showSinglePicker() async {
    final selected = await BottomSheetPickers.single<String>(
      context,
      title: "Choose a fruit",
      themeData: _theme,
    ).options(["Apple", "Orange", "Banana"], initialValue: "Apple").show();
    _updateResult(selected);
  }

  Future<void> _showMultiplePicker() async {
    final selected = await BottomSheetPickers.multiple<String>(
      context,
      title: "Choose tags",
      themeData: _theme,
    ).options(["New", "Popular", "Recommended", "Archived"],
        initialValue: ["New"]).show();
    _updateResult(selected);
  }

  Future<void> _showCascadePicker() async {
    final selected = await BottomSheetPickers.cascade(
      context,
      title: "Choose location",
      themeData: _theme,
    )
        .options(_locations)
        .initialValue(CascadeSelection.byIds("province_a", "city_a", "town_a"))
        .cascadeAllItemSupported()
        .show();
    if (selected is CascadeSelection) {
      _updateResult(selected.path.map((item) => item.label).join(" / "));
    }
  }

  Future<void> _showMultipleCascadePicker() async {
    final selected = await BottomSheetPickers.cascade(
      context,
      title: "Choose locations",
      themeData: _theme,
    ).options(_locations).multiple().initialValues(
        [CascadeSelection.byIds("province_a", "city_a", "town_a")]).show();
    _updateResult(selected
        ?.map((item) => item.path.map((option) => option.label).join(" / "))
        .join(", "));
  }

  Future<void> _showLazyPicker() async {
    final selected = await BottomSheetPickers.single<String>(
      context,
      title: "Choose remote item",
      themeData: _theme,
    ).lazyLoad(
      lazyRequestFuture: (params) async {
        await Future<void>.delayed(const Duration(milliseconds: 300));
        final int pageIndex = params["page_index"] as int? ?? 1;
        return List<String>.generate(
            10, (index) => "Item ${(pageIndex - 1) * 10 + index + 1}");
      },
    ).show();
    _updateResult(selected);
  }

  Future<void> _showLocalFilterPicker() async {
    final selected = await BottomSheetPickers.multiple<String>(
      context,
      title: "Choose stores",
      themeData: _theme,
    )
        .options(_stores)
        .searchSupported(placeholder: "Search store")
        .withFilterSupported(
          PickerFilter<String, String>.local(
            options: const [
              PickerFilterOption(value: null, label: "All"),
              PickerFilterOption(value: "PVS", label: "PVS"),
              PickerFilterOption(value: "FS", label: "FS"),
              PickerFilterOption(value: "KIOSK", label: "KIOSK"),
            ],
            predicate: (option, filter) =>
                filter == null || option.startsWith(filter),
          ),
        )
        .checkbox()
        .show();
    _updateResult(selected);
  }

  Future<void> _showRemoteFilterPicker() async {
    final selected = await BottomSheetPickers.single<String>(
      context,
      title: "Choose remote store",
      themeData: _theme,
    )
        .searchSupported(placeholder: "Search store")
        .withFilterSupported(
          PickerFilter<String, String>.remote(
            options: const [
              PickerFilterOption(value: null, label: "All"),
              PickerFilterOption(value: "PVS", label: "PVS"),
              PickerFilterOption(value: "FS", label: "FS"),
              PickerFilterOption(value: "KIOSK", label: "KIOSK"),
            ],
            parameterBuilder: (filter) => {
              if (filter != null) "store_type": filter,
            },
          ),
        )
        .lazyLoad(
      lazyRequestFuture: (params) async {
        await Future<void>.delayed(const Duration(milliseconds: 300));
        final int pageIndex = params["page_index"] as int? ?? 1;
        final int pageSize = params["page_size"] as int? ?? 50;
        final String? keyword = params["keyword"] as String?;
        final String? filter = params["store_type"] as String?;
        final data = _stores.where((store) {
          final matchesFilter = filter == null || store.startsWith(filter);
          final matchesKeyword = keyword == null ||
              keyword.isEmpty ||
              store.toLowerCase().contains(keyword.toLowerCase());
          return matchesFilter && matchesKeyword;
        }).toList();
        final start = (pageIndex - 1) * pageSize;
        if (start >= data.length) {
          return <String>[];
        }
        final end =
            start + pageSize > data.length ? data.length : start + pageSize;
        return data.sublist(start, end);
      },
    ).show();
    _updateResult(selected);
  }

  Future<void> _showCalendarPicker() async {
    final selected = await BottomSheetPickers.calendar(
      context,
      title: "Choose date",
      calendarType: CalendarType.buddhist,
      initialDate: DateTime.now(),
      firstDate: DateTime(2020, 1, 1),
      lastDate: DateTime(2030, 12, 31),
      themeData: _theme,
    ).show();
    _updateResult(selected);
  }

  Future<void> _showCalendarRangePicker() async {
    final selected = await BottomSheetPickers.dateRange(
      context,
      title: "Choose date range",
      calendarType: CalendarType.islamic,
      initialDateRange: DateTimeRange(
        start: DateTime.now(),
        end: DateTime.now().add(const Duration(days: 3)),
      ),
      firstDate: DateTime(2020, 1, 1),
      lastDate: DateTime(2030, 12, 31),
      themeData: _theme,
    ).show();
    _updateResult(selected);
  }

  Future<void> _showYearMonthPicker() async {
    final selected = await BottomSheetPickers.yearMonth(
      context,
      title: "Choose year month",
      initialYearMonthRange: YearMonthRange(
        start: YearMonth.fromDateTime(DateTime.now()),
        end: YearMonth.fromDateTime(
            DateTime.now().add(const Duration(days: 90))),
      ),
      firstYearMonth: const YearMonth(2020, 1),
      lastYearMonth: const YearMonth(2030, 12),
      isRange: true,
      themeData: _theme,
    ).show();
    _updateResult(selected);
  }

  void _updateResult(Object? value) {
    if (!mounted) {
      return;
    }
    setState(() => _result = value?.toString() ?? "Cancelled");
  }
}
1
likes
150
points
229
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Customizable Flutter bottom sheet pickers for single, multiple, searchable, lazy loaded, cascade, date, date range, and year-month selection.

Repository (GitHub)
View/report issues

Topics

#picker #bottom-sheet #cascade-picker #bottom-picker #selection

License

MIT (license)

Dependencies

flutter

More

Packages that depend on flutter_bottom_sheet_pickers