Cupertino lists, the missing widgets from the Flutter Cupertino collection
Tags: flutter, ios, mobile
Published: 2022-10-24 14:18:16

Although Flutter provides an easy mechanism to be able to create the UI once for all platforms, there are times when it is desirable to closely tweak the visuals to match the native elements of the platform. We might want to do this so the user feels at home and thus prevent your hybrid application from being the 'weird one' on your users phones. This is especially important if you include the entire collection of [Material](https://docs.flutter.dev/development/ui/widgets/material) widgets on iOS, as iOS users are not used to its visual style, its color system, and its animations.

In order to provide tools to mitigate this to developers, Flutter provides the [Cupertino](https://docs.flutter.dev/development/ui/widgets/cupertino) collection of widgets that try to mimic the visual style and behavior of native iOS elements. If you look carefully at the list of Cupertino widgets you will see that there is a huge lists of elements; CupertinoPageScaffold as the main parent of the screens, CupertinoButton for the regular buttons, CupertinoDatePicker for the date selection, etc. However, there is one type of element that does not exist: **lists**.

And why they didn't include such an important element? The truth is that I don't know the reason why this was not included from the beginning, given that almost every app out there have some kind of list. However, this will be fixed soon, since the _CupertinoListTile_ and _CupertinoListSection_ widgets have already been merged in the _master_ branch. These widgets will allow us to completely replace the [ListTile](https://api.flutter.dev/flutter/material/ListTile-class.html) widget to give it that iOS-style touch that some applications need.

Let's see what these new widgets look like and what possibilities they give us.

> 📽 Video version available on [YouTube](https://youtu.be/php2p8fqW8w) and [Odysee](https://odysee.com/@svprdga:d/cupertino-lists-the-missing-widgets-from-the-flutter-cupertino-collection)

{% embed https://youtu.be/php2p8fqW8w %}

### CupertinoListTile & CupertinoListSection

If you want to try these widgets for yourself, the first thing you have to do is switch to the _master_ channel. This is because these widgets have not yet been included in any official Flutter release:

`flutter channel master && flutter upgrade`

Now we will create a new application whose only target is iOS:

`flutter create --platforms ios cupertino_lists`

Open `main.dart` and remove the boilerplate code, we will also take the opportunity to replace the Material widgets with the Cupertino ones:

```dart
import 'package:flutter/cupertino.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return const CupertinoApp(
      title: 'Cupertino Lists',
      home: MainScreen(),
    );
  }
}

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

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('Cupertino Lists'),
      ),
      child: Container(),
    );
  }
}
```

We are left with a simple empty screen:

![Empty screen](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/kxcbmm2drmfwmhjew71r.png)

We are going to include a list of elements of type `CupertinoListTile`. These elements must be encapsulated within a `CupertinoListSection`; if you try to add them in a `ListView` or similar you will see that they don't look as expected:

```dart
class MainScreen extends StatelessWidget {
  const MainScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('Cupertino Lists'),
      ),
      child: SafeArea(
        child: CupertinoListSection(
          children: List.generate(
            10,
            (index) => CupertinoListTile(
              title: Text('Element $index'),
            ),
          ),
        ),
      ),
    );
  }
}
```

Notice that I have included a [SafeArea](https://api.flutter.dev/flutter/widgets/SafeArea-class.html) widget before the CupertinoListSection. This widget is especially useful in the Cupertino environment so that space is automatically left for the navigation bar and for the bottom edge of the screen on the latest iPhone models that do not have a physical button. We are left with an iOS-style list:

![Simple list](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/beq5vq7hl5bnermdhtxe.png)

If you try to add more elements to the list (by modifying the first parameter of the `generate()` method) you will see that the rendering fails. This is because this list is not yet scrollable. We can easily fix this if we wrap the CupertinoListSection inside a widget of the type [SingleChildScrollView](https://api.flutter.dev/flutter/widgets/SingleChildScrollView-class.html):

```dart
class MainScreen extends StatelessWidget {
  const MainScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('Cupertino Lists'),
      ),
      child: SafeArea(
        child: SingleChildScrollView(   // Make the list scrollable
          child: CupertinoListSection(
            children: List.generate(
              50,
              (index) => CupertinoListTile(
                title: Text('Element $index'),
              ),
            ),
          ),
        ),
      ),
    );
  }
}
```

CupertinoListTile has several configuration options to be able to apply different effects to each element of the list:

```dart
class MainScreen extends StatelessWidget {
  const MainScreen({super.key});

  @override
  Widget build(BuildContext context) {
    return CupertinoPageScaffold(
      navigationBar: const CupertinoNavigationBar(
        middle: Text('Cupertino Lists'),
      ),
      child: SafeArea(
        child: SingleChildScrollView(
          child: CupertinoListSection(
            children: const [
              CupertinoListTile(title: Text('Simple tile')),
              CupertinoListTile(
                title: Text('Title of the tile'),
                subtitle: Text('Subtitle of the tile'),
              ),
              CupertinoListTile(
                title: Text('With additional info'),
                additionalInfo: Text('Info'),
              ),
              CupertinoListTile(
                title: Text('With leading & trailing'),
                leading: Icon(CupertinoIcons.add_circled_solid),
                trailing: Icon(CupertinoIcons.chevron_forward),
              ),
              CupertinoListTile(
                title: Text('Different background color'),
                backgroundColor: CupertinoColors.activeGreen,
              ),
            ],
          ),
        ),
      ),
    );
  }
}
```

![Tile effects](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/744t4kqws68yhq1zvho9.png)

If you have seen the latest visual styles of the lists of native iOS applications (especially system applications) you will have noticed that these include side margins and rounded edges. Fortunately, CupertinoListSection includes a secondary constructor to achieve this effect:

`CupertinoListSection.insetGrouped()`

![Inset grouped](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f1b5natu2848lrsz7smh.png)

To finish, we can add a widget as a header and also one as a footer:

```dart
// [...]
child: CupertinoListSection.insetGrouped(
  header: const Text('List header'),
  footer: const Text('List footer'),
// [...]
```

![Header and footer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/y4bvlid2y6ksro7rbzwp.png)

### When will it be available on the stable channel?

Judging by the information in the [commit](https://github.com/flutter/flutter/commit/0c40945a67dc936f7eb61b7f752d2cec5fbe4055) where it was merged, everything seems to indicate that both widgets will arrive in the next Flutter release, until then we will have to continue depending on _ListTile_, or the existing plugins that have already tried to solve this problem.

That's all for today, I hope you found it useful, thanks for reading this far and happy coding :)