Kinda Code
Home/Flutter/Using Chip widget in Flutter: Tutorial & Examples

Using Chip widget in Flutter: Tutorial & Examples

Last updated: January 13, 2023

This article is about the Chip widget in Flutter. We’ll have a glance at the fundamentals of the widget and then walk through a few examples of implementing it in practice. Without any further ado, let’s get started.

Overview

A typical chip is a small box with 4 rounded corners. It has a text label and displays information in a meaningful and compact manner. Chips can show multiple interactive elements together in the same area. Some popular real-world use cases of chips are:

  • Post tags (you can see them on many WordPress blogs or big platforms like GitHub, StackOverflow, Medium, IMDB, etc.)
  • A list of removable things (a series of email contacts, a list of favorite music genres, etc.)
Tags on GitHub

In Flutter, you can implement a Chip widget by using the following constructor:

Chip({
  Key? key, 
  Widget? avatar, 
  required Widget label, 
  TextStyle? labelStyle, 
  EdgeInsetsGeometry? labelPadding, 
  Widget? deleteIcon, 
  VoidCallback? onDeleted, 
  Color? deleteIconColor, 
  String? deleteButtonTooltipMessage, 
  BorderSide? side, 
  OutlinedBorder? shape, 
  Clip clipBehavior = Clip.none, 
  FocusNode? focusNode, 
  bool autofocus = false, 
  Color? backgroundColor, 
  EdgeInsetsGeometry? padding, 
  VisualDensity? visualDensity, 
  MaterialTapTargetSize? materialTapTargetSize, 
  double? elevation, 
  Color? shadowColor, 
  Color? surfaceTintColor, 
  IconThemeData? iconTheme, 
  bool useDeleteButtonTooltip = true
})

Only the label property is required; the others are optional. Some commonly used ones are:

  • avatar: Displays an icon or a small image before the label.
  • backgroundColor: Background color of the chip.
  • padding: The padding around the contents of the chip.
  • deleteIcon: The widget that lets the user delete the chip.
  • onDeleted: The function that is called when the deleteIcon gets tapped.

You can find more details about other properties in the official docs. However, for most applications, we don’t need more than half of them.

Simple Example

This small example shows you a simple and efficient way to display multiple chips together. We will use a Wrap widget as the parent of a list of chips. When the free space in the current line runs out, the chips will automatically go down the line. We can also conveniently set the distance between the chips thanks to the spacing property of the Wrap widget.

Screenshot:

The code:

// kindacode.com
// main.dart
import 'package:flutter/material.dart';

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

class MyApp extends StatelessWidget {
  const MyApp({super.key});
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      // hide debug banner
      debugShowCheckedModeBanner: false,
      title: 'KindaCode.com',
      theme: ThemeData(
        // enable Material 3
        useMaterial3: true,
        primarySwatch: Colors.blue,
      ),
      home: const KindaCodeDemo(),
    );
  }
}

class KindaCodeDemo extends StatelessWidget {
  const KindaCodeDemo({super.key});
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('KindaCode.com'),
      ),
      body: Padding(
        padding: const EdgeInsets.symmetric(vertical: 30, horizontal: 10),
        child: Wrap(
            // space between chips
            spacing: 10,
            // list of chips
            children: const [
              Chip(
                label: Text('Working'),
                avatar: Icon(
                  Icons.work,
                  color: Colors.red,
                ),
                backgroundColor: Colors.amberAccent,
                padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
              ),
              Chip(
                label: Text('Music'),
                avatar: Icon(Icons.headphones),
                backgroundColor: Colors.lightBlueAccent,
                padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
              ),
              Chip(
                label: Text('Gaming'),
                avatar: Icon(
                  Icons.gamepad,
                  color: Colors.white,
                ),
                backgroundColor: Colors.pinkAccent,
                padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
              ),
              Chip(
                label: Text('Cooking & Eating'),
                avatar: Icon(
                  Icons.restaurant,
                  color: Colors.pink,
                ),
                backgroundColor: Colors.greenAccent,
                padding: EdgeInsets.symmetric(vertical: 5, horizontal: 10),
              )
            ]),
      ),
    );
  }
}

In this example, chips only present information. In the next example, chips are interactable.

Complex Example: Dynamically Adding & Removing Chips

App Preview

The app we are going to build contains a floating action button. When this button is pressed, a dialog will show up to let us add a new chip. Each chip can be removed by tapping the delete icon associated with it.

Here’s how our app works:

The Complete Code

The final code in main.dart with explanations:

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

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

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'KindaCode.com',
      theme: ThemeData(
        // use Material 3
        useMaterial3: true,
        primarySwatch: Colors.amber,
      ),
      home: const HomePage(),
    );
  }
}

// Data model for a chip
class ChipData {
  // an id is useful when deleting chip
  final String id;
  final String name;
  ChipData({required this.id, required this.name});
}

// HomePage widget
class HomePage extends StatefulWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  State<HomePage> createState() => _HomePageState();
}

class _HomePageState extends State<HomePage> {
  // list of chips
  final List<ChipData> _allChips = [];

  // Text controller (that will be used for the TextField shown in the dialog)
  final TextEditingController _textController = TextEditingController();
  // This function will be triggered when the floating actiong button gets pressed
  void _addNewChip() async {
    await showDialog(
        context: context,
        builder: (_) {
          return AlertDialog(
            title: const Text('Add a thing:'),
            content: TextField(
              controller: _textController,
            ),
            actions: [
              ElevatedButton(
                  onPressed: () {
                    setState(() {
                      _allChips.add(ChipData(
                          id: DateTime.now().toString(),
                          name: _textController.text));
                    });

                    // reset the TextField
                    _textController.text = '';

                    // Close the dialog
                    Navigator.of(context).pop();
                  },
                  child: const Text('Submit'))
            ],
          );
        });
  }

  // This function will be called when a delete icon associated with a chip is tapped
  void _deleteChip(String id) {
    setState(() {
      _allChips.removeWhere((element) => element.id == id);
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('KindaCode.com'),
      ),
      body: Padding(
        padding: const EdgeInsets.all(15),
        child: Wrap(
          spacing: 10,
          children: _allChips
              .map((chip) => Chip(
                    key: ValueKey(chip.id),
                    label: Text(chip.name),
                    backgroundColor: Colors.amber.shade200,
                    padding:
                        const EdgeInsets.symmetric(vertical: 7, horizontal: 10),
                    deleteIconColor: Colors.red,
                    onDeleted: () => _deleteChip(chip.id),
                  ))
              .toList(),
        ),
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: _addNewChip,
        child: const Icon(Icons.add),
      ),
    );
  }
}

Conclusion

We’ve explored essential aspects of the Chip widget and gone through more than one example of using that thing in action. How gorgeous and convenient it is! If you’d like to learn more new and awesome things about modern Flutter, just take a look at the following articles:

You can also take a tour around our Flutter topic page and Dart topic page to see the latest tutorials and examples.