flutterflutter-futurebuilder

Flutter SliverGrid with Future Builder, undefined name: snapshot issue


`Hi, I'm trying to show a list of country flags using Flutter, SliverGrid and FutureBuilder.

In the showCountries(), 'childCount: snapshot.data.length' line, getting an error: undefined 'snapshot' and not compiling. If commented out, the list is building good, but adding many additional tiles with 'Another exception was thrown: RangeError (index): Index out of range: index should be less than 14: 15'.

Any help is much appreciated. Thanks.`

class CountryListScreen extends StatelessWidget {
  final String prefLang;
  const CountryListScreen({super.key, required this.prefLang});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
          leading: BackButton(
            onPressed: () => Navigator.of(context).pop(),
          ),
          title: const Text(AppConstants.appName),
          elevation: 2),
      body: SafeArea(
        child: CustomScrollView(
          slivers: [
            showCountries(),
          ],
        ),
      ),
    );
  }

  Widget showCountries() {
    return SliverGrid(
        gridDelegate:
            const SliverGridDelegateWithFixedCrossAxisCount(crossAxisCount: 3),
        delegate: SliverChildBuilderDelegate(
          (BuildContext context, int index) {
            return FutureBuilder(
              future: fetchCountries(),
              builder: (context, AsyncSnapshot snapshot) {
                if (!snapshot.hasData) {
                  return const Center(child: CircularProgressIndicator());
                } else {
                  return _buildCountryFlag(snapshot.data[index], context);
                }
              },
            );
          },
          **childCount: snapshot.data.length,**
        ));
  }

  Future<List<Country>> fetchCountries() async {
    final response =
        await Utilities().consumeApi('/countries?language=$prefLang');

    return (json.decode(response.body) as List)
        .map((i) => Country.fromJson(i))
        .toList();
  }

  Widget _buildCountryFlag(Country country, BuildContext context) {
    return InkWell(
      onTap: () {
       
      },
      child: _FlagItem(
          flag: CountryFlag.fromCountryCode(country.code),
          countryCode: country.code,
          countryName: country.name),
    );
  }
}

class _FlagItem extends StatelessWidget {
  const _FlagItem(
      {required this.flag,
      required this.countryCode,
      required this.countryName});

  final CountryFlag flag;
  final String countryCode;
  final String countryName;

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        const SizedBox(height: 10),
        Expanded(child: flag),
        const SizedBox(height: 8),
        Expanded(
          child: Text(
            countryName,
            style: Theme.of(context).textTheme.titleMedium,
          ),
        ),
      ],
    );
  }
}

Solution

  • I think the problem is the visibility of the snapshot object. It is defined in FutureBuilder but you are trying to use it outside of that block.

    I think that the possible solutions can be:

    1. set childCount = null (as reported in documentation: If null, the number of children is determined by the least index for which builder returns null)

    or

    1. set childCount = (await fetchCountries()).length

    The second solution will call fetchCountries() twice but you can solve saving the result in a new variable and using it both in FutureBuilder and in childCount.

    I hope this will help you! ;)