Flutterexperts

Empowering Vision with FlutterExperts' Expertise

FlutterFlow is a powerful visual app builder that allows you to create fully functional mobile and web applications without writing extensive code. While it provides many built-in components, sometimes you need a more tailored solution. In this blog, we’ll walk you through the process of creating a custom multi-item picker widget in FlutterFlow.

Why a Multi-Item Picker?

A multi-item picker is essential when users need to select more than one option from a list. Common use cases include selecting interests, tags, categories, or filtering items in e-commerce and survey apps.

FlutterFlow doesn’t have a built-in multi-select component, but with the help of custom widgets, you can easily build one that integrates seamlessly into your app.

Step 1: Set Up Your Custom Widget in FlutterFlow

To start, create a new custom widget in FlutterFlow:

  1. Go to the Custom Code section in the left panel.
  2. Under Widgets, click + Add.
  3. Name it something like MultiItemPickerComponent.
  4. Define the parameters:
  • items (List, required): The list of all items to display.
  • initialSelectedItems (List, optional): The list of items that should appear pre-selected.
  • title (String, optional): Title of the picker UI.
  • searchHintText (String, optional): Placeholder for the search input field.
  • width, height, maxHeight (optional): For layout customization.

Step 2: Writing the Widget Code

Here’s a simplified version of a FlutterFlow-compliant widget (no function callbacks):

// Automatic FlutterFlow imports
import '/flutter_flow/flutter_flow_theme.dart';
import '/flutter_flow/flutter_flow_util.dart';
import '/custom_code/widgets/index.dart'; 
import '/flutter_flow/custom_functions.dart'; 
import 'package:flutter/material.dart';
// Begin custom widget code
// DO NOT REMOVE OR MODIFY THE CODE ABOVE!

class MultiItemPickerComponent extends StatefulWidget {
  const MultiItemPickerComponent({
    Key? key,
    this.width,
    this.height,
    required this.items,
    this.initialSelectedItems,
    this.searchHintText = 'Search items...',
    this.title = 'Select Items',
    // this.buttonText = 'Done',
    this.maxHeight,
  }) : super(key: key);

  final double? width;
  final double? height;
  final double? maxHeight;
  final List<String> items;
  final List<String>? initialSelectedItems;
  final String searchHintText;
  final String title;
  // final String buttonText;

  @override
  _MultiItemPickerComponentState createState() =>
      _MultiItemPickerComponentState();
}

class _MultiItemPickerComponentState extends State<MultiItemPickerComponent> {
  final TextEditingController _searchController = TextEditingController();
  List<String> _filteredItems = [];
  List<String> _selectedItems = [];

  @override
  void initState() {
    super.initState();
    _filteredItems = widget.items;
    _selectedItems = widget.initialSelectedItems ?? [];
    _searchController.addListener(_filterItems);
  }

  @override
  void dispose() {
    _searchController.removeListener(_filterItems);
    _searchController.dispose();
    super.dispose();
  }

  void _filterItems() {
    final query = _searchController.text.toLowerCase();
    setState(() {
      _filteredItems = widget.items
          .where((item) => item.toLowerCase().contains(query))
          .toList();
    });
  }

  void _toggleItemSelection(String item) {
    setState(() {
      if (_selectedItems.contains(item)) {
        _selectedItems.remove(item);
      } else {
        _selectedItems.add(item);
      }
    });
  }

  @override
  Widget build(BuildContext context) {
    return Container(
      width: widget.width ?? double.infinity,
      height: widget.height,
      constraints: BoxConstraints(
        maxHeight: widget.maxHeight ?? 400,
      ),
      decoration: BoxDecoration(
        color: Theme.of(context).cardColor,
        borderRadius: BorderRadius.circular(8),
        boxShadow: [
          BoxShadow(
            blurRadius: 4,
            color: Color(0x33000000),
            offset: Offset(0, 2),
          ),
        ],
      ),
      child: Padding(
        padding: EdgeInsets.all(16),
        child: Column(
          mainAxisSize: MainAxisSize.min,
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceBetween,
              children: [
                Text(widget.title,
                    style: Theme.of(context).textTheme.titleMedium),
                Text('${_selectedItems.length} selected',
                    style: Theme.of(context).textTheme.bodySmall),
              ],
            ),
            SizedBox(height: 12),
            TextFormField(
              controller: _searchController,
              decoration: InputDecoration(
                hintText: widget.searchHintText,
                border: OutlineInputBorder(
                  borderRadius: BorderRadius.circular(8),
                ),
                prefixIcon: Icon(Icons.search),
                suffixIcon: _searchController.text.isNotEmpty
                    ? InkWell(
                        onTap: () {
                          _searchController.clear();
                        },
                        child: Icon(Icons.clear),
                      )
                    : null,
              ),
            ),
            SizedBox(height: 12),
            Text('Selected Items:',
                style: TextStyle(fontWeight: FontWeight.bold)),
            Wrap(
              spacing: 8,
              runSpacing: 8,
              children: _selectedItems.map((item) {
                return Chip(
                  label: Text(item),
                  onDeleted: () => _toggleItemSelection(item),
                );
              }).toList(),
            ),
            SizedBox(height: 12),
            Text('Available Items:',
                style: TextStyle(fontWeight: FontWeight.bold)),
            Expanded(
              child: ListView.builder(
                itemCount: _filteredItems.length,
                itemBuilder: (context, index) {
                  final item = _filteredItems[index];
                  final isSelected = _selectedItems.contains(item);
                  return ListTile(
                    title: Text(item),
                    trailing: Icon(
                      isSelected
                          ? Icons.check_box
                          : Icons.check_box_outline_blank,
                      color: isSelected
                          ? Theme.of(context).colorScheme.primary
                          : null,
                    ),
                    onTap: () => _toggleItemSelection(item),
                  );
                },
              ),
            ),
            SizedBox(height: 16),
          ],
        ),
      ),
    );
  }
}

Step 3: Use the Widget in Your UI

Now that your widget is added:

  1. Drag the MultiItemPickerComponent widget from the Custom Widgets tab.
  2. Pass the items list and optionally initialSelectedItems, title, or searchHintText.
  3. To retrieve selected items, bind the selected items to a local state or AppState variable using Actions in FlutterFlow.

For example:

  • On done/tap, update an AppState list variable with selected items using a custom action or logic.

Conclusion

With this custom widget, you now have a robust multi-item picker that works beautifully within the FlutterFlow environment. This approach offers flexibility, search capability, and a user-friendly interface.

This pattern — building custom widgets without callback parameters — can be reused for other advanced components. Happy building!

From Our Parent Company Aeologic

Aeologic Technologies is a leading AI-driven digital transformation company in India, helping businesses unlock growth with AI automation, IoT solutions, and custom web & mobile app development. We also specialize in AIDC solutions and technical manpower augmentation, offering end-to-end support from strategy and design to deployment and optimization.

Trusted across industries like manufacturing, healthcare, logistics, BFSI, and smart cities, Aeologic combines innovation with deep industry expertise to deliver future-ready solutions.

Feel free to connect with us:
And read more articles from FlutterDevs.com.

FlutterDevs team of Flutter developers to build high-quality and functionally-rich apps. Hire Flutterflow developer for your cross-platform Flutter mobile app project on an hourly or full-time basis as per your requirement! For any flutter-related queries, you can connect with us on Facebook, GitHub, Twitter, and LinkedIn.

We welcome feedback and hope that you share what you’re working on using #FlutterDevs. We truly enjoy seeing how you use Flutter to build beautiful, interactive web experiences.

Leave comment

Your email address will not be published. Required fields are marked with *.