Rasmus Christensen
Rasmus Christensen

Reputation: 8531

Flutter ListView EmptyState with refresh

I have a page in my app with a ListView. I'm using a StreamBuilder to receive data. When there is data I build the listview children and the listview is wrapped inside a RefreshIndicator. This is working fine.

Now I want to handle the situation when there is no data aka EmptyState. Be able to display a Container with some content, while still be able to support pull to refresh to force the page/Listview to check for data.

This is how I'm currently doing it

return RefreshIndicator(
            key: upcomingRefreshIndicatorKey,
            onRefresh: handleRefresh,
            child: snapshot.hasData ? list : Stack(children: <Widget>[list, emptystate],) ,

        );

I'm not sure this is the way to handle EmptyState with ListView, but so far this is the only way I can see to still support refresh when the list is empty. So my main question is how you best handle empty state with LIstView and still supporting PullToRefresh?

Upvotes: 3

Views: 9825

Answers (1)

NiklasPor
NiklasPor

Reputation: 9775

To support the RefreshIndicator inside an empty ListView you can set physics: AlwaysScrollableScrollPhysics(). This makes the ListView always scrolalble. Therefore, we can always trigger the RefreshIndicator.

The Container you want to display when the ListView is empty can be accomplished by two methods:

  1. If your data is empty (or null), replace the ListView with a SingleChildScrollView with physics set to AlwaysScrollableScrollPhysics(). Then you can place your Container as the child of the SingleChildScrollView.
  2. Instead of replacing the ListView you can simply replace the children with your Container, given your data is empty (or null).

In the follwing standalone example, I took the second approach:

Demo

import 'dart:async';

import 'package:flutter/material.dart';

void main() => runApp(MyApp());

// dummy stream
StreamController<List<String>> yourStream = StreamController<List<String>>();

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        body: Center(
          child: RefreshIndicator(
            onRefresh: refresh,
            child: StreamBuilder<List<String>>(
              stream: yourStream.stream,
              builder: (_, snapshot) => ListView(
                  physics: AlwaysScrollableScrollPhysics(), // This makes it always scrollable!
                  children: snapshot.data == null
                      ? [Text('Nothing here :(')]
                      : snapshot.data
                          .map((text) => ListTile(
                                title: Text(text),
                              ))
                          .toList()),
            ),
          ),
        ),
      ),
    );
  }

  Future<void> refresh() {
    // dummy code
    yourStream.add(['Quick', 'Brown', 'Fox']);
    return Future.value();
  }
}

Upvotes: 18

Related Questions