image

05 Dec 2025

9K

35K

Creating Scrollable Lists in Flutter

Scrollable lists are a fundamental component of almost any modern mobile application, allowing users to browse through extensive collections of data without cluttering the screen. Flutter, Google's UI toolkit for building natively compiled applications for mobile, web, and desktop from a single codebase, provides highly efficient and flexible widgets to create various types of scrollable lists. This article will guide you through the process of implementing scrollable lists in Flutter, from basic setups to more advanced techniques.

The Basics: ListView

The most common and straightforward way to create a scrollable list in Flutter is by using the ListView widget. It's suitable for displaying a linear array of widgets. There are a few constructors for ListView, each serving a slightly different purpose.

1. Simple ListView (for a small, static number of items)

For lists with a small, fixed number of items, you can pass a direct list of widgets to the children property of ListView. This approach builds all children at once, which can be inefficient for very long lists.


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(
      home: Scaffold(
        appBar: AppBar(title: const Text('Simple ListView')),
        body: ListView(
          children: const [
            ListTile(title: Text('Item 1')),
            ListTile(title: Text('Item 2')),
            ListTile(title: Text('Item 3')),
            // ... more items
            ListTile(title: Text('Item 10')),
          ],
        ),
      ),
    );
  }
}
    

2. ListView.builder (for dynamic or long lists)

For lists with a large or dynamic number of items, ListView.builder is the preferred choice. It constructs children lazily, meaning it only builds the widgets that are currently visible on the screen or are about to become visible. This approach is highly performant and memory-efficient.


import 'package:flutter/material.dart';

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

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

  final List items = const [
    'Apple', 'Banana', 'Orange', 'Grape', 'Strawberry', 'Blueberry', 'Raspberry', 'Pineapple',
    'Mango', 'Kiwi', 'Watermelon', 'Melon', 'Peach', 'Plum', 'Cherry', 'Lemon', 'Lime', 'Coconut',
    'Avocado', 'Pomegranate', 'Fig', 'Date', 'Apricot', 'Pear', 'Guava', 'Passion Fruit', 'Papaya',
    'Blackberry', 'Cranberry', 'Lychee', 'Dragon Fruit', 'Star Fruit', 'Persimmon', 'Quince',
    'Durian', 'Jackfruit', 'Rambutan', 'Mangosteen', 'Acai', 'Elderberry', 'Gooseberry',
    'Honeydew', 'Cantaloupe', 'Nectarine', 'Tangerine', 'Clementine', 'Kumquat', 'Longan'
  ]; // Example: A long list of fruits

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ListView.builder')),
        body: ListView.builder(
          itemCount: items.length,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text(items[index]),
              subtitle: Text('A delicious fruit: ${items[index]}'),
              leading: const Icon(Icons.arrow_right),
              onTap: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Tapped on ${items[index]}')),
                );
              },
            );
          },
        ),
      ),
    );
  }
}
    

In the example above, itemCount specifies the total number of items in the list, and itemBuilder is a callback function that is invoked for each item to build its corresponding widget.

3. ListView.separated (for lists with dividers)

ListView.separated is similar to ListView.builder but includes a separatorBuilder callback to build a divider widget between each item. This is particularly useful for visually separating list entries.


import 'package:flutter/material.dart';

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

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

  final List items = const [
    'Dashboard', 'Profile', 'Settings', 'Notifications', 'Messages',
    'Friends', 'Gallery', 'Favorites', 'Help', 'Logout'
  ];

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: const Text('ListView.separated')),
        body: ListView.separated(
          itemCount: items.length,
          itemBuilder: (BuildContext context, int index) {
            return ListTile(
              title: Text(items[index]),
              leading: const Icon(Icons.folder_open),
              onTap: () {
                ScaffoldMessenger.of(context).showSnackBar(
                  SnackBar(content: Text('Selected ${items[index]}')),
                );
              },
            );
          },
          separatorBuilder: (BuildContext context, int index) {
            return const Divider(color: Colors.grey, height: 1); // Custom divider
          },
        ),
      ),
    );
  }
}
    

Other Scrollable Widgets

SingleChildScrollView

While ListView is for a list of multiple items, SingleChildScrollView is used when you have a single widget (like a Column or Row) that needs to scroll if its content exceeds the available space. This is commonly used for forms, detailed view screens, or any layout where a single block of content might overflow.


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(
      home: Scaffold(
        appBar: AppBar(title: const Text('SingleChildScrollView Example')),
        body: SingleChildScrollView(
          child: Padding(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                const Text(
                  'This is a long piece of text that describes the usage of SingleChildScrollView. '
                  'It\'s ideal when you have a column or row of widgets that might exceed the '
                  'screen height. Instead of wrapping individual widgets in scroll views, '
                  'you wrap the entire layout container.',
                  style: TextStyle(fontSize: 16),
                ),
                const SizedBox(height: 20),
                const TextField(
                  decoration: InputDecoration(
                    labelText: 'Full Name',
                    border: OutlineInputBorder(),
                  ),
                ),
                const SizedBox(height: 20),
                const TextField(
                  decoration: InputDecoration(
                    labelText: 'Email Address',
                    border: OutlineInputBorder(),
                  ),
                  keyboardType: TextInputType.emailAddress,
                ),
                const SizedBox(height: 20),
                // Add many more widgets to force scrolling
                ...List.generate(
                  20,
                  (index) => Padding(
                    padding: const EdgeInsets.symmetric(vertical: 8.0),
                    child: Text(
                      'Detail Section ${index + 1}: Some additional content here to ensure the page scrolls.',
                      style: const TextStyle(fontSize: 14),
                    ),
                  ),
                ),
                const SizedBox(height: 20),
                Center(
                  child: ElevatedButton(
                    onPressed: () {},
                    child: const Text('Submit Information'),
                  ),
                ),
                const SizedBox(height: 10),
              ],
            ),
          ),
        ),
      ),
    );
  }
}
    

CustomScrollView and Slivers (Advanced)

For highly customized scroll effects, such as a collapsing app bar, combining different scroll effects (like a list and a grid) within a single scrollable area, or implementing complex parallax scrolling, Flutter offers CustomScrollView along with "Sliver" widgets. Slivers are portions of a scrollable area. They allow for intricate control over how widgets react to scrolling.

  • SliverAppBar: An app bar that can interact with the scroll view, often collapsing or expanding.
  • SliverList: A sliver that displays its children in a linear array, similar to ListView.
  • SliverGrid: A sliver that displays its children in a two-dimensional array, similar to GridView.
  • SliverToBoxAdapter: A sliver that contains a single box widget, useful for inserting non-sliver widgets into a CustomScrollView.

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(
      home: Scaffold(
        body: CustomScrollView(
          slivers: [
            SliverAppBar(
              expandedHeight: 200.0,
              floating: false, // AppBar floats when scrolling down
              pinned: true,   // AppBar remains visible at the top
              snap: false,    // Requires floating to be true
              flexibleSpace: FlexibleSpaceBar(
                title: const Text('Custom Scroll View'),
                background: Image.network(
                  'https://picsum.photos/800/400?random=1', // Placeholder image
                  fit: BoxFit.cover,
                ),
              ),
            ),
            SliverToBoxAdapter(
              child: Padding(
                padding: const EdgeInsets.all(16.0),
                child: Text(
                  'This is some introductory content displayed using a SliverToBoxAdapter. '
                  'It blends seamlessly into the custom scroll effect.',
                  style: Theme.of(context).textTheme.titleLarge,
                ),
              ),
            ),
            SliverGrid(
              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(
                crossAxisCount: 2, // Two items per row
                crossAxisSpacing: 8.0,
                mainAxisSpacing: 8.0,
                childAspectRatio: 1.5, // Item width to height ratio
              ),
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return Card(
                    color: Colors.lightBlue[100 * (index % 9)],
                    child: Center(
                      child: Text(
                        'Grid Item $index',
                        style: const TextStyle(fontSize: 18, color: Colors.white),
                      ),
                    ),
                  );
                },
                childCount: 10, // Number of grid items
              ),
            ),
            SliverList(
              delegate: SliverChildBuilderDelegate(
                (BuildContext context, int index) {
                  return ListTile(
                    title: Text('List Item $index'),
                    leading: const Icon(Icons.check_circle_outline),
                    trailing: const Icon(Icons.arrow_forward_ios),
                  );
                },
                childCount: 20, // Number of list items
              ),
            ),
          ],
        ),
      ),
    );
  }
}
    

Performance Considerations

Always prioritize ListView.builder (or SliverList with SliverChildBuilderDelegate / SliverGrid with SliverGridDelegate) when dealing with lists that have a large or potentially infinite number of items. These "builder" constructors implement lazy loading, meaning they only create widgets for items that are currently visible on the screen or are about to become visible.

Creating all widgets at once (as with a simple ListView or a Column directly inside a SingleChildScrollView with many children) can lead to significant performance issues and increased memory consumption, especially on lower-end devices. Lazy loading ensures that only the visible parts of your list consume resources, resulting in a smoother user experience.

Conclusion

Flutter offers a robust and flexible set of widgets for creating scrollable lists, catering to a wide range of requirements. From the simple ListView for static content to the highly optimized ListView.builder for dynamic and extensive datasets, and the powerful CustomScrollView for intricate scroll effects, you have all the tools necessary to build responsive and performant user interfaces. Understanding which widget to use for a specific scenario is key to developing efficient and smooth Flutter applications. By leveraging these widgets effectively, developers can create rich and interactive user experiences that gracefully handle varying amounts of content.

Join our newsletter!

Enter your email to receive our latest newsletter.

Don't worry, we don't spam

Categories

Related Articles

Dec 06, 2025

Integrating Google Analytics into Flutter Applications

Integrating Google Analytics into Flutter Applications In the evolving landscape of mobile application development, understanding user behavior is paramount fo

Dec 05, 2025

Flutter & JSON Parsing with Model Classes

Flutter & JSON Parsing with Model Classes Introduction In modern mobile application development, consuming data from web services is a fundamental requirement.

Dec 05, 2025

Creating Scrollable Lists in Flutter

Creating Scrollable Lists in Flutter Scrollable lists are a fundamental component of almost any modern mobile application, allowing users to