Popular Searches
Popular Course Categories
Popular Courses

ListView.builder

Flutter Lists & Collections

ListView.builder in Flutter – Detailed Notes

ListView.builder is a Flutter widget constructor used to create scrollable lists dynamically. It is especially useful when an application needs to display a large or dynamically changing number of items. Unlike a regular ListView with an explicit children list, ListView.builder creates list children on demand as they become visible. This makes it suitable for large or potentially infinite lists. :contentReference[oaicite:0]{index=0}

1. What is ListView.builder?

ListView.builder creates a scrollable, linear collection of widgets using an itemBuilder callback. The callback receives the current index and returns the widget that should appear at that position.

It is commonly used for:

  • Student lists
  • Employee lists
  • Product lists
  • Contact lists
  • Chat messages
  • Notifications
  • News feeds
  • Shopping cart items
  • Search results
  • Data received from APIs

2. Why Use ListView.builder?

When a list contains a large number of items, creating every widget at once is unnecessary. ListView.builder builds children lazily, meaning Flutter creates the children that are needed for the visible portion of the list rather than constructing the entire collection at once. :contentReference[oaicite:1]{index=1}

  • Efficient for large lists.
  • Suitable for dynamic data.
  • Supports potentially infinite lists.
  • Creates children on demand.
  • Works with local and remote data.
  • Supports vertical and horizontal scrolling.
  • Can be combined with user interaction and navigation.

3. Basic Syntax

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return Widget();
  },
)

The two most important properties are itemCount and itemBuilder.

4. Understanding itemCount

itemCount specifies the number of items that the list should represent.

ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return Text(students[index]);
  },
)

If students contains five elements, itemCount will be five and the builder receives indexes from 0 through 4. Providing a non-null itemCount also helps Flutter estimate the maximum scroll extent more accurately. :contentReference[oaicite:2]{index=2}

5. Understanding itemBuilder

itemBuilder is a callback that creates the widget for each list position.

itemBuilder: (context, index) {
  return ListTile(
    title: Text('Item ${index + 1}'),
  );
}

The index tells you which item is currently being built.

6. Simple ListView.builder Example

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(
      debugShowCheckedModeBanner: false,
      home: Scaffold(
        appBar: AppBar(
          title: const Text('ListView.builder'),
        ),
        body: ListView.builder(
          itemCount: 20,
          itemBuilder: (context, index) {
            return ListTile(
              leading: const Icon(Icons.list),
              title: Text('Item ${index + 1}'),
            );
          },
        ),
      ),
    );
  }
}

7. How ListView.builder Works

  1. Create or receive a data source.
  2. Pass the number of items to itemCount.
  3. Provide an itemBuilder callback.
  4. Flutter provides the current index to the callback.
  5. The callback creates the corresponding widget.
  6. As the user scrolls, additional visible children are built as required.
ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    final student = students[index];

    return ListTile(
      title: Text(student),
    );
  },
)

8. ListView.builder with a List of Strings

final List students = [
  'Rahul',
  'Priya',
  'Amit',
  'Neha',
  'Pooja',
];

ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: const Icon(Icons.person),
      title: Text(students[index]),
    );
  },
)

9. ListView.builder with List of Maps

final List> products = [
  {
    'name': 'Laptop',
    'price': 55000,
  },
  {
    'name': 'Smartphone',
    'price': 25000,
  },
  {
    'name': 'Headphones',
    'price': 3000,
  },
];

ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, index) {
    final product = products[index];

    return ListTile(
      leading: const Icon(Icons.shopping_bag),
      title: Text(product['name']),
      subtitle: Text('₹${product['price']}'),
    );
  },
)

10. ListView.builder with Model Classes

For larger Flutter applications, model classes make structured data easier to manage.

class Product {
  final String name;
  final double price;
  final String category;

  Product({
    required this.name,
    required this.price,
    required this.category,
  });
}

Create the data:

final List products = [
  Product(
    name: 'Laptop',
    price: 55000,
    category: 'Electronics',
  ),
  Product(
    name: 'Phone',
    price: 25000,
    category: 'Electronics',
  ),
  Product(
    name: 'Headphones',
    price: 3000,
    category: 'Accessories',
  ),
];

Display it using ListView.builder:

ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, index) {
    final product = products[index];

    return Card(
      child: ListTile(
        leading: const Icon(Icons.shopping_bag),
        title: Text(product.name),
        subtitle: Text(product.category),
        trailing: Text(
          '₹${product.price.toStringAsFixed(0)}',
        ),
      ),
    );
  },
)

11. ListView.builder with Cards

ListView.builder(
  padding: const EdgeInsets.all(12),
  itemCount: products.length,
  itemBuilder: (context, index) {
    final product = products[index];

    return Card(
      margin: const EdgeInsets.only(bottom: 12),
      elevation: 3,
      child: ListTile(
        leading: const CircleAvatar(
          child: Icon(Icons.shopping_bag),
        ),
        title: Text(product.name),
        subtitle: Text(product.category),
        trailing: Text(
          '₹${product.price.toStringAsFixed(0)}',
        ),
      ),
    );
  },
)

12. Horizontal ListView.builder

By default, ListView.builder scrolls vertically. Set scrollDirection: Axis.horizontal to create a horizontal list.

final List categories = [
  'Electronics',
  'Fashion',
  'Books',
  'Sports',
  'Shoes',
];

ListView.builder(
  scrollDirection: Axis.horizontal,
  itemCount: categories.length,
  itemBuilder: (context, index) {
    return Container(
      width: 140,
      margin: const EdgeInsets.all(8),
      child: Card(
        child: Center(
          child: Text(categories[index]),
        ),
      ),
    );
  },
)

13. Adding Padding

ListView.builder(
  padding: const EdgeInsets.all(16),
  itemCount: 10,
  itemBuilder: (context, index) {
    return Card(
      margin: const EdgeInsets.only(bottom: 10),
      child: ListTile(
        title: Text('Item ${index + 1}'),
      ),
    );
  },
)

14. Adding Separators

If every item needs a separator, ListView.separated can be used instead of manually adding separator widgets. It provides an itemBuilder for items and a separatorBuilder for separators. :contentReference[oaicite:3]{index=3}

ListView.separated(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(students[index]),
    );
  },
  separatorBuilder: (context, index) {
    return const Divider();
  },
)

15. onTap with ListView.builder

List items can respond to user interaction using onTap.

ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return ListTile(
      leading: const Icon(Icons.person),
      title: Text(students[index]),
      onTap: () {
        print('Selected: ${students[index]}');
      },
    );
  },
)

16. Navigating to a Detail Screen

A common pattern is to pass the selected list item to another screen.

ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, index) {
    final product = products[index];

    return ListTile(
      title: Text(product.name),
      subtitle: Text('₹${product.price}'),
      trailing: const Icon(Icons.arrow_forward_ios),
      onTap: () {
        Navigator.push(
          context,
          MaterialPageRoute(
            builder: (context) {
              return ProductDetails(product: product);
            },
          ),
        );
      },
    );
  },
)

17. Detail Screen Example

class ProductDetails extends StatelessWidget {
  final Product product;

  const ProductDetails({
    super.key,
    required this.product,
  });

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(product.name),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              product.name,
              style: const TextStyle(
                fontSize: 24,
                fontWeight: FontWeight.bold,
              ),
            ),
            const SizedBox(height: 10),
            Text('Category: ${product.category}'),
            const SizedBox(height: 10),
            Text('Price: ₹${product.price}'),
          ],
        ),
      ),
    );
  }
}

18. Dynamic List with Add Item

ListView.builder works well with setState() when list data changes.

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

  @override
  State createState() => _StudentPageState();
}

class _StudentPageState extends State {
  final List students = [
    'Rahul',
    'Priya',
    'Amit',
  ];

  void addStudent() {
    setState(() {
      students.add(
        'Student ${students.length + 1}',
      );
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Students'),
      ),
      body: ListView.builder(
        itemCount: students.length,
        itemBuilder: (context, index) {
          return ListTile(
            leading: const Icon(Icons.person),
            title: Text(students[index]),
          );
        },
      ),
      floatingActionButton: FloatingActionButton(
        onPressed: addStudent,
        child: const Icon(Icons.add),
      ),
    );
  }
}

19. Removing Items

void removeStudent(int index) {
  setState(() {
    students.removeAt(index);
  });
}

Use the function with an icon button:

ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(students[index]),
      trailing: IconButton(
        icon: const Icon(Icons.delete),
        onPressed: () {
          removeStudent(index);
        },
      ),
    );
  },
)

20. Swipe to Delete

The Dismissible widget can be combined with ListView.builder to provide swipe-to-delete functionality.

ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return Dismissible(
      key: ValueKey(students[index]),
      onDismissed: (direction) {
        setState(() {
          students.removeAt(index);
        });
      },
      background: Container(
        color: Colors.red,
        alignment: Alignment.centerLeft,
        padding: const EdgeInsets.only(left: 20),
        child: const Icon(Icons.delete),
      ),
      child: ListTile(
        title: Text(students[index]),
      ),
    );
  },
)

21. Checkbox List with ListView.builder

final List tasks = [
  'Learn Dart',
  'Learn Flutter',
  'Build a Flutter project',
  'Practice ListView.builder',
];

final List completed = [
  false,
  false,
  false,
  false,
];

ListView.builder(
  itemCount: tasks.length,
  itemBuilder: (context, index) {
    return CheckboxListTile(
      title: Text(tasks[index]),
      value: completed[index],
      onChanged: (value) {
        setState(() {
          completed[index] = value ?? false;
        });
      },
    );
  },
)

22. ListView.builder with Images

ListView.builder(
  itemCount: 10,
  itemBuilder: (context, index) {
    return ListTile(
      leading: CircleAvatar(
        backgroundImage: NetworkImage(
          'https://example.com/image.jpg',
        ),
      ),
      title: Text('User ${index + 1}'),
      subtitle: const Text('Flutter Developer'),
    );
  },
)

23. reverse Property

The reverse property reverses the scroll direction. It can be useful for chat-style interfaces.

ListView.builder(
  reverse: true,
  itemCount: messages.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(messages[index]),
    );
  },
)

24. Horizontal Product Categories

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

  final List categories = const [
    'All',
    'Electronics',
    'Fashion',
    'Books',
    'Sports',
  ];

  @override
  Widget build(BuildContext context) {
    return SizedBox(
      height: 60,
      child: ListView.builder(
        scrollDirection: Axis.horizontal,
        itemCount: categories.length,
        itemBuilder: (context, index) {
          return Padding(
            padding: const EdgeInsets.symmetric(
              horizontal: 6,
            ),
            child: Chip(
              label: Text(categories[index]),
            ),
          );
        },
      ),
    );
  }
}

25. ScrollController

ScrollController allows an application to control and observe the scroll position.

final ScrollController controller = ScrollController();

ListView.builder(
  controller: controller,
  itemCount: 100,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Item ${index + 1}'),
    );
  },
)

Scrolling to the Top

controller.animateTo(
  0,
  duration: const Duration(milliseconds: 500),
  curve: Curves.easeInOut,
);

Disposing the Controller

@override
void dispose() {
  controller.dispose();
  super.dispose();
}

26. shrinkWrap

shrinkWrap allows the scrollable to size itself based on its contents along the scrolling direction. It can be useful when a ListView needs to exist inside another layout that imposes a different sizing requirement.

Column(
  children: [
    const Text('Students'),
    ListView.builder(
      shrinkWrap: true,
      physics: const NeverScrollableScrollPhysics(),
      itemCount: students.length,
      itemBuilder: (context, index) {
        return ListTile(
          title: Text(students[index]),
        );
      },
    ),
  ],
)

Do not use shrinkWrap: true unnecessarily because content-based sizing can require additional layout work.

27. physics Property

The physics property controls how the list responds to scrolling gestures.

ListView.builder(
  physics: const BouncingScrollPhysics(),
  itemCount: 30,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Item ${index + 1}'),
    );
  },
)

Scrolling can also be disabled:

ListView.builder(
  physics: const NeverScrollableScrollPhysics(),
  itemCount: 10,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Item ${index + 1}'),
    );
  },
)

28. itemExtent

If every item has a known fixed extent in the scrolling direction, itemExtent can be specified. Knowing the child extent can allow Flutter's scrolling machinery to perform less work. :contentReference[oaicite:4]{index=4}

ListView.builder(
  itemCount: 100,
  itemExtent: 60,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Item ${index + 1}'),
    );
  },
)

29. prototypeItem

prototypeItem can be used when list children have the same extent as a representative widget.

ListView.builder(
  itemCount: 100,
  prototypeItem: const ListTile(
    title: Text('Prototype'),
  ),
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Item ${index + 1}'),
    );
  },
)

Flutter documents prototypeItem, itemExtent, and itemExtentBuilder as ways to provide information about child extent. Only one of these extent mechanisms should be supplied for a given ListView.builder. :contentReference[oaicite:5]{index=5}

30. itemExtentBuilder

itemExtentBuilder can be useful when list items have different extents that can be calculated from the index.

ListView.builder(
  itemCount: 20,
  itemExtentBuilder: (index, dimensions) {
    return index.isEven ? 60 : 90;
  },
  itemBuilder: (context, index) {
    return ListTile(
      title: Text('Item ${index + 1}'),
    );
  },
)

31. Large Lists

ListView.builder is particularly useful for large lists. Flutter's official long-list recipe demonstrates generating 10,000 items and displaying them with ListView.builder. :contentReference[oaicite:6]{index=6}

final List items = List.generate(
  10000,
  (index) => 'Item $index',
);

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
    );
  },
)

32. API Data with ListView.builder

In real-world applications, list data frequently comes from an API or database. After retrieving and parsing the data, ListView.builder can convert each data object into a Flutter widget.

class User {
  final String name;
  final String email;

  User({
    required this.name,
    required this.email,
  });
}

final List users = [
  User(
    name: 'Rahul Sharma',
    email: '[email protected]',
  ),
  User(
    name: 'Priya Singh',
    email: '[email protected]',
  ),
];

ListView.builder(
  itemCount: users.length,
  itemBuilder: (context, index) {
    final user = users[index];

    return ListTile(
      leading: const CircleAvatar(
        child: Icon(Icons.person),
      ),
      title: Text(user.name),
      subtitle: Text(user.email),
    );
  },
)

33. Loading State

When list data is being loaded from a remote source, the application can display a loading indicator before building the list.

if (isLoading) {
  return const Center(
    child: CircularProgressIndicator(),
  );
}

return ListView.builder(
  itemCount: users.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(users[index].name),
    );
  },
);

34. Empty State

If there are no items, it is better to display a meaningful empty state.

if (users.isEmpty) {
  return const Center(
    child: Text('No users found'),
  );
}

return ListView.builder(
  itemCount: users.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(users[index].name),
    );
  },
);

Flutter's ListView documentation recommends conditionally replacing the list with an empty-state widget when there are no items. :contentReference[oaicite:7]{index=7}

35. Error State

if (hasError) {
  return Center(
    child: Column(
      mainAxisAlignment: MainAxisAlignment.center,
      children: [
        const Text('Something went wrong'),
        const SizedBox(height: 10),
        ElevatedButton(
          onPressed: loadUsers,
          child: const Text('Retry'),
        ),
      ],
    ),
  );
}

36. ListView.builder with FutureBuilder

A common Flutter pattern is to retrieve asynchronous data using FutureBuilder and display the resulting collection with ListView.builder.

Future> fetchStudents() async {
  await Future.delayed(
    const Duration(seconds: 1),
  );

  return [
    'Rahul',
    'Priya',
    'Amit',
    'Neha',
  ];
}

FutureBuilder>(
  future: fetchStudents(),
  builder: (context, snapshot) {
    if (snapshot.connectionState == ConnectionState.waiting) {
      return const Center(
        child: CircularProgressIndicator(),
      );
    }

    if (snapshot.hasError) {
      return Center(
        child: Text('Error: ${snapshot.error}'),
      );
    }

    final students = snapshot.data ?? [];

    if (students.isEmpty) {
      return const Center(
        child: Text('No students found'),
      );
    }

    return ListView.builder(
      itemCount: students.length,
      itemBuilder: (context, index) {
        return ListTile(
          leading: const Icon(Icons.person),
          title: Text(students[index]),
        );
      },
    );
  },
)

37. Avoid Calling API Directly Inside itemBuilder

The itemBuilder should normally build a widget from already available data. Avoid starting a network request for every list item from inside itemBuilder. Fetch and prepare the data separately, then use the builder to render it.

ListView.builder(
  itemCount: users.length,
  itemBuilder: (context, index) {
    final user = users[index];

    return ListTile(
      title: Text(user.name),
      subtitle: Text(user.email),
    );
  },
)

38. ListView.builder with Search Results

final List allStudents = [
  'Rahul',
  'Priya',
  'Amit',
  'Neha',
  'Pooja',
];

String searchText = '';

List get filteredStudents {
  return allStudents
      .where(
        (student) => student
            .toLowerCase()
            .contains(searchText.toLowerCase()),
      )
      .toList();
}

ListView.builder(
  itemCount: filteredStudents.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(filteredStudents[index]),
    );
  },
)

39. ListView.builder with Selection

Selection can be implemented by storing the selected index or selected item in application state.

int selectedIndex = -1;

ListView.builder(
  itemCount: students.length,
  itemBuilder: (context, index) {
    return ListTile(
      selected: selectedIndex == index,
      title: Text(students[index]),
      onTap: () {
        setState(() {
          selectedIndex = index;
        });
      },
    );
  },
)

40. ListView.builder and Keys

Keys help Flutter identify widgets when the order or identity of list items changes. For example, ValueKey can be useful when building dismissible or reorderable list items.

ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, index) {
    final product = products[index];

    return ListTile(
      key: ValueKey(product.name),
      title: Text(product.name),
    );
  },
)

When the order of children can change and state needs to remain associated with the correct item, Flutter's API documentation notes that findChildIndexCallback may be needed to map an existing child to its new index. :contentReference[oaicite:8]{index=8}

41. Child Lifecycle

ListView.builder creates visible child subtrees lazily. When a child scrolls out of view, its element subtree, state, and render objects may be destroyed and recreated when it becomes visible again. For important application state, keep the source-of-truth data outside the individual list child or use an appropriate state-preservation strategy. :contentReference[oaicite:9]{index=9}

42. ListView.builder vs ListView

FeatureListViewListView.builder
DataExplicit widgetsDynamic data
Child creationChildren are provided explicitlyChildren are built on demand
Large listsLess suitableSuitable
Infinite listsNot the typical approachSuitable
itemBuilderNoYes
itemCountNoOptional but recommended when known

Flutter's documentation recommends the standard constructor for small lists and ListView.builder for large or potentially infinite lists. :contentReference[oaicite:10]{index=10}

43. ListView.builder vs ListView.separated

WidgetPurpose
ListView.builderBuilds dynamic list items on demand.
ListView.separatedBuilds dynamic list items with separators.

44. ListView.builder vs GridView.builder

WidgetLayoutTypical Usage
ListView.builderOne-dimensional linear listContacts, messages, products, notifications
GridView.builderTwo-dimensional gridProduct galleries, photo grids, dashboards

45. ListView.builder Inside Column

When a ListView needs to occupy the remaining available space inside a Column, wrap it with Expanded.

Column(
  children: [
    const Text(
      'Student List',
      style: TextStyle(fontSize: 20),
    ),
    Expanded(
      child: ListView.builder(
        itemCount: students.length,
        itemBuilder: (context, index) {
          return ListTile(
            title: Text(students[index]),
          );
        },
      ),
    ),
  ],
)

46. Common Mistakes

Mistake 1: Creating all widgets manually

For large dynamic data, use ListView.builder rather than manually creating a huge children list.

Mistake 2: Forgetting itemCount

ListView.builder(
  itemCount: products.length,
  itemBuilder: (context, index) {
    return Text(products[index].name);
  },
)

Mistake 3: Invalid index access

itemBuilder: (context, index) {
  return Text(products[index].name);
}

Make sure the index is within the bounds of the data collection.

Mistake 4: Unnecessary shrinkWrap

Do not use shrinkWrap: true automatically. Use it when the layout actually requires content-based sizing.

Mistake 5: Starting expensive operations for every item

Keep network requests, complex calculations, and other expensive operations outside the item builder whenever possible.

Mistake 6: Forgetting to dispose ScrollController

@override
void dispose() {
  controller.dispose();
  super.dispose();
}

47. Performance Best Practices

  • Use ListView.builder for large or dynamic collections.
  • Provide itemCount when the number of items is known.
  • Use const widgets where possible.
  • Avoid unnecessary shrinkWrap.
  • Use itemExtent when all items have a known fixed extent.
  • Use prototypeItem when a representative fixed-size item is appropriate.
  • Use itemExtentBuilder when child extents can be determined from their indexes.
  • Keep expensive data processing outside itemBuilder.
  • Use stable keys when item identity matters.
  • Keep important application state in a source-of-truth model rather than relying on a list child remaining mounted.
  • Dispose manually created controllers.

48. Complete Student List Example

import 'package:flutter/material.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      home: const StudentPage(),
    );
  }
}

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

  final List> students = const [
    {
      'name': 'Rahul Sharma',
      'course': 'Flutter Development',
    },
    {
      'name': 'Priya Singh',
      'course': 'UI/UX Design',
    },
    {
      'name': 'Amit Kumar',
      'course': 'Backend Development',
    },
    {
      'name': 'Neha Verma',
      'course': 'Flutter Development',
    },
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Students'),
      ),
      body: ListView.builder(
        padding: const EdgeInsets.all(12),
        itemCount: students.length,
        itemBuilder: (context, index) {
          final student = students[index];

          return Card(
            margin: const EdgeInsets.only(bottom: 10),
            child: ListTile(
              leading: CircleAvatar(
                child: Text(
                  student['name']![0],
                ),
              ),
              title: Text(student['name']!),
              subtitle: Text(student['course']!),
              trailing: const Icon(
                Icons.arrow_forward_ios,
                size: 16,
              ),
            ),
          );
        },
      ),
    );
  }
}

49. Practical Example – Product List

class Product {
  final String name;
  final double price;

  Product({
    required this.name,
    required this.price,
  });
}

class ProductPage extends StatelessWidget {
  ProductPage({super.key});

  final List products = [
    Product(name: 'Laptop', price: 55000),
    Product(name: 'Mobile Phone', price: 25000),
    Product(name: 'Headphones', price: 3000),
    Product(name: 'Keyboard', price: 1500),
    Product(name: 'Mouse', price: 800),
  ];

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Products'),
      ),
      body: ListView.builder(
        itemCount: products.length,
        itemBuilder: (context, index) {
          final product = products[index];

          return Card(
            margin: const EdgeInsets.symmetric(
              horizontal: 12,
              vertical: 6,
            ),
            child: ListTile(
              leading: const CircleAvatar(
                child: Icon(Icons.shopping_bag),
              ),
              title: Text(product.name),
              subtitle: Text(
                '₹${product.price.toStringAsFixed(0)}',
              ),
              onTap: () {
                print(product.name);
              },
            ),
          );
        },
      ),
    );
  }
}

50. Practical Example – Chat Messages

final List messages = [
  'Hello!',
  'How are you?',
  'I am learning Flutter.',
  'That is great!',
  'Keep practicing.',
];

ListView.builder(
  reverse: true,
  padding: const EdgeInsets.all(12),
  itemCount: messages.length,
  itemBuilder: (context, index) {
    return Align(
      alignment: Alignment.centerRight,
      child: Container(
        margin: const EdgeInsets.only(bottom: 8),
        padding: const EdgeInsets.all(12),
        decoration: BoxDecoration(
          borderRadius: BorderRadius.circular(12),
          color: Colors.blue,
        ),
        child: Text(
          messages[index],
          style: const TextStyle(
            color: Colors.white,
          ),
        ),
      ),
    );
  },
)

51. Practical Example – Notification List

final List> notifications = [
  {
    'title': 'New Message',
    'description': 'You received a new message.',
  },
  {
    'title': 'Order Confirmed',
    'description': 'Your order has been confirmed.',
  },
  {
    'title': 'Payment Successful',
    'description': 'Your payment was successful.',
  },
];

ListView.builder(
  itemCount: notifications.length,
  itemBuilder: (context, index) {
    final notification = notifications[index];

    return ListTile(
      leading: const Icon(Icons.notifications),
      title: Text(notification['title']!),
      subtitle: Text(notification['description']!),
    );
  },
)

52. Practical Example – 10,000 Items

The following example demonstrates the main advantage of ListView.builder for a large collection.

final List items = List.generate(
  10000,
  (index) => 'Item $index',
);

ListView.builder(
  itemCount: items.length,
  itemBuilder: (context, index) {
    return ListTile(
      title: Text(items[index]),
    );
  },
)

This follows the same pattern as Flutter's official long-list example. :contentReference[oaicite:11]{index=11}

53. ListView.builder Constructor – Important Properties

PropertyPurpose
itemBuilderBuilds each list item.
itemCountSpecifies the number of items.
scrollDirectionControls vertical or horizontal scrolling.
reverseReverses the scroll direction.
controllerControls and observes scrolling.
physicsControls scroll behavior.
shrinkWrapAllows content-based sizing when required.
paddingAdds space around the list.
itemExtentSpecifies a fixed item extent.
prototypeItemUses a representative widget to determine item extent.
itemExtentBuilderCalculates item extent based on index.
findChildIndexCallbackHelps map an existing child to its new index when child order changes.
addAutomaticKeepAlivesControls automatic keep-alive behavior for children.
addRepaintBoundariesControls automatic repaint boundaries for children.
addSemanticIndexesControls automatic semantic indexes.

These properties are part of the current Flutter ListView.builder API. :contentReference[oaicite:12]{index=12}

54. When Should You Use ListView.builder?

  • When the number of items is dynamic.
  • When displaying a large collection.
  • When displaying API results.
  • When displaying database records.
  • When implementing infinite or very long lists.
  • When items need to be created according to their indexes.
  • When you want Flutter to create children on demand.

55. When Should You Use Normal ListView?

For a small number of fixed widgets, a normal ListView can be simpler.

ListView(
  children: const [
    ListTile(title: Text('Home')),
    ListTile(title: Text('Profile')),
    ListTile(title: Text('Settings')),
  ],
)

Flutter's documentation describes the regular constructor as appropriate for small lists and ListView.builder as appropriate for large or potentially infinite lists. :contentReference[oaicite:13]{index=13}

56. Practice Exercises

  1. Create a ListView.builder containing 20 student names.
  2. Create a product list containing product name, category, and price.
  3. Create a horizontal ListView.builder for product categories.
  4. Create a notification list using ListView.builder.
  5. Create a contact list with profile icons.
  6. Create a shopping cart with delete buttons.
  7. Implement swipe-to-delete using Dismissible.
  8. Create a chat interface using reverse: true.
  9. Create a list with checkboxes for a task-management application.
  10. Create a search interface that filters ListView.builder items.
  11. Create a list that navigates to a detail page when an item is tapped.
  12. Create an API-based ListView with loading, error, empty, and success states.
  13. Create a 10,000-item list using List.generate() and ListView.builder.

57. Quick Revision

  • ListView.builder: Creates a scrollable list whose children are built on demand.
  • itemBuilder: Builds the widget for each index.
  • itemCount: Specifies the number of items when known.
  • scrollDirection: Controls vertical or horizontal scrolling.
  • reverse: Reverses the scrolling direction.
  • controller: Provides programmatic scroll control.
  • physics: Controls scrolling behavior.
  • shrinkWrap: Allows the list to size itself around its contents when required.
  • itemExtent: Specifies a fixed item extent.
  • prototypeItem: Provides a representative item for determining extent.
  • itemExtentBuilder: Calculates item extent based on the item index.
  • findChildIndexCallback: Helps preserve child identity when item order changes.

58. Key Takeaways

ListView.builder is one of the most important Flutter widgets for displaying dynamic and large collections. Its itemBuilder callback creates widgets on demand, making it appropriate for long or potentially infinite lists. Providing itemCount when the total number of items is known helps Flutter estimate scroll extent. Properties such as scrollDirection, reverse, controller, physics, shrinkWrap, itemExtent, prototypeItem, and itemExtentBuilder provide control over scrolling and item layout. :contentReference[oaicite:14]{index=14}

59. Official Flutter Resources

60. Flutter Training Resources

For structured Flutter training covering Dart, Flutter widgets, layouts, scrolling, application development, and practical projects, visit:

JustAcademy Flutter Training Course

To register for a Flutter course demo:

Register for Flutter Course Demo

whatsapp