image

24 Dec 2025

9K

35K

Building an Expandable List Widget with Provider in Flutter

Expandable list widgets are a common and useful UI pattern, allowing users to reveal or hide detailed content without navigating to a new screen. They are excellent for FAQs, menus, or displaying categorized information efficiently. In Flutter, building such a widget with proper state management is crucial for performance and maintainability. This article will guide you through creating an expandable list using the Provider package, a simple yet powerful solution for state management.

Prerequisites

Before you begin, ensure you have Flutter installed and set up correctly. We will be using the provider package, so add it to your pubspec.yaml file:


dependencies:
  flutter:
    sdk: flutter
  provider: ^6.0.5 # Use the latest version

Run flutter pub get to fetch the new dependency.

1. Define the Data Model

First, let's create a simple data model for our list items. Each item will have an ID, a title, and content.


class Item {
  final int id;
  final String title;
  final String content;

  Item({required this.id, required this.title, required this.content});
}

2. Create the Provider for State Management

Next, we'll implement a ChangeNotifier that will manage the expansion state of our items. This provider will hold a list of all items and a Set of IDs for currently expanded items. When an item's expansion state changes, it will notify its listeners.


import 'package:flutter/foundation.dart';

class ExpandableListProvider extends ChangeNotifier {
  // Our initial list of items
  final List _items = [
    Item(id: 1, title: 'What is Flutter?', content: 'Flutter is an open-source UI software development kit created by Google. It is used to develop cross-platform applications from a single codebase.'),
    Item(id: 2, title: 'Why use Provider?', content: 'Provider is a simple yet powerful state management solution in Flutter. It simplifies accessing and managing state across the widget tree.'),
    Item(id: 3, title: 'How to install Flutter?', content: 'You can install Flutter by downloading the SDK from the official Flutter website and setting up your environment variables.'),
    Item(id: 4, title: 'What is a Widget?', content: 'In Flutter, everything is a widget. Widgets are the basic building blocks of the UI. They describe how your app\'s view should look like with its current configuration and state.'),
  ];

  // A set to keep track of item IDs that are currently expanded
  final Set _expandedItemIds = {};

  // Getter to access the list of items
  List get items => _items;

  // Method to check if an item is expanded
  bool isExpanded(int itemId) => _expandedItemIds.contains(itemId);

  // Method to toggle the expansion state of an item
  void toggleExpansion(int itemId) {
    if (_expandedItemIds.contains(itemId)) {
      _expandedItemIds.remove(itemId);
    } else {
      _expandedItemIds.add(itemId);
    }
    notifyListeners(); // Notify all listening widgets to rebuild
  }
}

3. Build the Expandable List Item Widget

Now, let's create a widget for a single expandable item. This widget will display the item's title and, when expanded, its content. It will interact with our ExpandableListProvider to toggle its state.


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

// Ensure your Item class and ExpandableListProvider are imported or defined in scope
// e.g., import '../models/item.dart';
//        import '../providers/expandable_list_provider.dart';

class ExpandableListItem extends StatelessWidget {
  final Item item;

  const ExpandableListItem({Key? key, required this.item}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    // We use context.watch to listen to changes in ExpandableListProvider
    // specifically to check if THIS item is expanded.
    final bool isItemExpanded = context.watch().isExpanded(item.id);

    return Card(
      margin: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0),
      child: Column(
        children: [
          ListTile(
            title: Text(item.title, style: const TextStyle(fontWeight: FontWeight.bold)),
            trailing: Icon(
              isItemExpanded ? Icons.expand_less : Icons.expand_more,
            ),
            onTap: () {
              // Use context.read to call methods that modify the state
              // without causing the widget that calls it to rebuild.
              context.read().toggleExpansion(item.id);
            },
          ),
          // Only show content if the item is expanded
          if (isItemExpanded)
            Padding(
              padding: const EdgeInsets.fromLTRB(16.0, 0.0, 16.0, 16.0),
              child: Text(item.content),
            ),
        ],
      ),
    );
  }
}

4. Create the Main Expandable List Screen

Finally, we'll assemble our main screen. This screen will set up the ChangeNotifierProvider and display a ListView of our ExpandableListItem widgets.


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

// Ensure your Item class, ExpandableListProvider, and ExpandableListItem are imported
// e.g., import '../models/item.dart';
//        import '../providers/expandable_list_provider.dart';
//        import '../widgets/expandable_list_item.dart';

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

  @override
  Widget build(BuildContext context) {
    // context.watch is used here to get the list of items from the provider.
    // If the _items list inside the provider were to change (e.g., dynamic loading),
    // this widget would rebuild to reflect those changes.
    final items = context.watch().items;

    return Scaffold(
      appBar: AppBar(
        title: const Text('FAQ with Expandable List'),
      ),
      body: ListView.builder(
        itemCount: items.length,
        itemBuilder: (context, index) {
          final item = items[index];
          return ExpandableListItem(item: item);
        },
      ),
    );
  }
}

5. Integrate with main.dart

To run the application, wrap your ExpandableListScreen with a ChangeNotifierProvider in your main.dart file. This makes your ExpandableListProvider instance available to all widgets below it in the widget tree.


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

// Import all your defined classes
// If you've organized them into folders, adjust paths accordingly:
// import 'models/item.dart';
// import 'providers/expandable_list_provider.dart';
// import 'widgets/expandable_list_item.dart';
// import 'screens/expandable_list_screen.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return ChangeNotifierProvider(
      // The create callback initializes an instance of ExpandableListProvider
      // when it's first accessed in the widget tree.
      create: (context) => ExpandableListProvider(),
      child: MaterialApp(
        title: 'Expandable List Demo',
        theme: ThemeData(
          primarySwatch: Colors.blue,
          visualDensity: VisualDensity.adaptivePlatformDensity,
        ),
        home: const ExpandableListScreen(),
      ),
    );
  }
}

Conclusion

By leveraging Flutter's Provider package, we've successfully built a clean, maintainable, and efficient expandable list widget. This approach elegantly separates state management logic from the UI, making your code easier to test, understand, and scale. The use of ChangeNotifier and notifyListeners() ensures that only the necessary parts of your UI rebuild when the state changes, contributing to a smooth user experience. You can extend this further by adding animations for expansion/collapse, dynamic data loading, or implementing multi-selection features based on the same principles.

Related Articles

May 14, 2026

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter

Building a Multi-Event Countdown Timer Widget with Reminders, Notifications, Repeat, and Custom Labels in Flutter Countdown timers are essential in many applic

May 11, 2026

Unleashing Dynamic UIs: Flutter's Animation Prowess

Unleashing Dynamic UIs: Flutter's Animation Prowess for Slide & Scale Effects Flutter's declarative UI framework, combined with its powerful animation capabilit

May 11, 2026

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy

Building a Product Detail Page Widget in Flutter with Related Items, Review Carousel, Promo Badges, and Quick Buy A well-designed Product Detail Page (PDP) is