Flutter future builder to list
Asked Answered
M

3

5

I have some troubles in understanding how future builder works in flutter. I want to pass a list of String from my future call and I want to display them in a SingleChildScrollView.

The problem is that when I access the snapshot.data I can not access the element of the list. Because in my SingleChildScrollView I have container and in each container I want to display one String from the list.

This is the Future getData method with which I retrieve the data.

Future<List<String>> getData () async {
    List<String> data = [];
    data.add("A");
    data.add("B");
    data.add("C");
    // DEBUG
    await Future.delayed(const Duration(seconds: 2), (){});
    return data;
}

And this is my future builder in which I want to display the data in each container. In the loading I added a shimmer effect.

FutureBuilder(
  builder: (context, snapshot) {
    List<Widget> children;
    if (snapshot.hasData) {
      children = <Widget>[
        SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: [
              Container(
                margin: EdgeInsets.only(left: 5.w),
                width: 40.w,
                height: 20.h,
                decoration: BoxDecoration(
                  color: green400,
                  borderRadius: BorderRadius.all(Radius.circular(5.w)),
                ),
              ),
              Container(
                margin: EdgeInsets.only(left: 5.w),
                width: 40.w,
                height: 20.h,
                decoration: BoxDecoration(
                  color: green400,
                  borderRadius: BorderRadius.all(Radius.circular(5.w)),
                ),
              ),
              Container(
                margin: EdgeInsets.only(left: 5.w),
                width: 40.w,
                height: 20.h,
                decoration: BoxDecoration(
                  color: green400,
                  borderRadius: BorderRadius.all(Radius.circular(5.w)),
                ),
              ),
            ],
          ),
        ),
      ];
    } else if (snapshot.hasError) {
      children = <Widget>[
        const Icon(
          Icons.error_outline,
          color: Colors.red,
          size: 60,
        ),
        Padding(
          padding: const EdgeInsets.only(top: 16),
          child: Text('Error: ${snapshot.error}'),
        )
      ];
    } else {
      children = <Widget>[
        
        SingleChildScrollView(
          scrollDirection: Axis.horizontal,
          child: Row(
            children: [
              Shimmer.fromColors(
                baseColor: Colors.grey.shade200,
                highlightColor: Colors.grey.shade300,
                child: Container(
                  margin: EdgeInsets.only(left: 5.w),
                  width: 40.w,
                  height: 20.h,
                  decoration: BoxDecoration(
                    color: green400,
                    borderRadius: BorderRadius.all(Radius.circular(5.w)),
                  ),
                ),
              ),
              Shimmer.fromColors(
                baseColor: Colors.grey.shade200,
                highlightColor: Colors.grey.shade300,
                child: Container(
                  margin: EdgeInsets.only(left: 5.w),
                  width: 40.w,
                  height: 20.h,
                  decoration: BoxDecoration(
                    color: green400,
                    borderRadius: BorderRadius.all(Radius.circular(5.w)),
                  ),
                ),
              ),
              Shimmer.fromColors(
                baseColor: Colors.grey.shade200,
                highlightColor: Colors.grey.shade300,
                child: Container(
                  margin: EdgeInsets.only(left: 5.w),
                  width: 40.w,
                  height: 20.h,
                  decoration: BoxDecoration(
                    color: green400,
                    borderRadius: BorderRadius.all(Radius.circular(5.w)),
                  ),
                ),
              ),
            ],
          ),
        ),
      ];
    }
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: children,
      ),
    );
  },
  future: getData(),
),

So in this way I can I access the elements of my list of Strings?

Magnify answered 22/2, 2022 at 8:40 Comment(1)
instead SingleChildScrollView why don you use ListView.builder? more hereKirbykirch
U
7

As @piskink mentioned, using ListView.builder is more efficient.

body: FutureBuilder<List<String>?>(
  future: getData(),
  builder: (context, snapshot) {
    if (snapshot.hasData &&
        snapshot.connectionState == ConnectionState.done) {
      return ListView.builder(
        itemCount: snapshot.data!.length,
        itemBuilder: (context, index) {
          return Text(snapshot.data?[index] ?? "got null");
        },
      );
    }

    /// handles others as you did on question
    else {
      return CircularProgressIndicator();
    }
  },

If you still wish to use SingleChildScrollView, you can generate items like

return Column(
  children: List.generate(
    snapshot.data!.length,
    (index) => Text(snapshot.data?[index] ?? "got null"),
  ),
);

Check more about async-await and Future.

Underwater answered 22/2, 2022 at 8:56 Comment(2)
Yes I was missing the Type in the FutureBuilder.Magnify
Yap, you can use data the way you want.Underwater
V
3

When you declare a FutureBuilder you have also to pass it it's data type. In this case it will be:

FutureBuilder<List<String>>(
  future: getData(),
  builder: (context,snapshot){
    return ...;
  }
)

If you don't declare its datatype, your snapshot will be considered as an AsyncDataSnapshot<dynamic> instead of AsyncDataSnapshot<List<String>>.

Velvavelvet answered 22/2, 2022 at 8:56 Comment(0)
F
2

This is a full example for FutureBuilder with Null Safty

if you got these errors:

  • ( The getter 'length' isn't defined for the type 'Object'. )

  • ( The operator '[]' isn't defined for the type 'Object'. )

This is the Fix ↓↓↓↓↓

Fix & Important Note:

You have to select datatype of the Future Data that will be received.

  • Right: FutureBuilder<List>()
  • Wrong: FutureBuilder()

Full Simple Example:

import 'package:flutter/material.dart';

class FutureExample extends StatelessWidget {
  const FutureExample({Key? key}) : super(key: key);

  Future<List<String>> getData() async{
    await Future.delayed(
      const Duration(seconds:2)
    );
    return ["I'm Ramy","I'm Yasser", "I'm Ahmed", "I'm Yossif",];
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: FutureBuilder<List<String>>(
          future: getData(),
          builder: (context, snapshot) {
            return snapshot.connectionState == ConnectionState.waiting
                ? const CircularProgressIndicator()
                : Column(
                  mainAxisAlignment: MainAxisAlignment.center,
                  mainAxisSize: MainAxisSize.min,
              children: List.generate(snapshot.data!.length,
                      (index) {
                return Text(snapshot.data?[index] ?? "null") ;
                      },
              ),
            );
          },
        ),
        ),
    );
  }
}
Faunie answered 12/5, 2022 at 13:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.