how to listen to two different futures on one screen? - flutter

I am learning flutter and building a simple vocabulary app. I have one future coming from sqlLite database and another from twitter api. I only know how to create one state class with builder.
So the _tweet method, I need to have it show up in the placeholder/hard coded tweet spot. It doesn't work though.
import 'package:flutter/material.dart';
import '../../resources/dbprovider.dart';
import '../../resources/tweetprovider.dart';
import '../../data/models/word.dart';
import 'package:tweet_webview/tweet_webview.dart';
class WordCard extends StatefulWidget {
final int id;
WordCard({
this.id,
});
#override
State<StatefulWidget> createState() {
return _WordCardState();
}
}
class _WordCardState extends State<WordCard> {
#override
Widget build(BuildContext context) {
final dbProvider = DBProvider.get();
return Scaffold(
appBar: AppBar(
title: Text('Meme Vocab'),
),
body: FutureBuilder<Word>(
future: dbProvider.getWordByID(widget.id),
builder: (context, snapshot) {
if (snapshot.hasData) {
Word word = snapshot.data;
return _card(word);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}));
}
Widget _card(Word word) {
return Padding(
padding: EdgeInsets.all(15.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text(
word.word.trim(),
style: new TextStyle(
fontFamily: "Roboto",
fontSize: 40.0,
fontWeight: FontWeight.bold,
color: Colors.black.withOpacity(0.45)),
textAlign: TextAlign.left,
),
Text(
word.definition,
style: new TextStyle(
fontFamily: "Roboto",
fontSize: 20.0,
fontStyle: FontStyle.italic),
textAlign: TextAlign.left,
),
TweetWebView.tweetUrl(
"https://twitter.com/realDonaldTrump/status/1159898199802425344") //placeholder
],
),
);
}
}
//Don't know where to put this widget so it renders, how to return the tweets I fetch.
Future<Widget> _tweet(Word word) async {
final tweetProvider = TweetProvider();
List<int> tweets = await tweetProvider.getRelevantTweetIDs(word);
final list = ListView.builder(
scrollDirection: Axis.vertical,
padding: EdgeInsets.fromLTRB(0.0, 0.0, 0.0, 0.0),
itemCount: tweets.length,
itemBuilder: (context, index) {
var tweetID = tweets[index].toString();
return Card(
child: TweetWebView.tweetID(tweetID),
);
},
);
final container =
Container(color: Colors.black26, child: Center(child: list));
return container;
}

I found that Future Builder widget is the way to go. I have it in the place of the TweetWebView or basically I can place it anywhere there is a widget whose's contents are a future.
FutureBuilder(
future: tweetProvider.getRelevantTweetIDs(word),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
print('something went wrong while fetching tweets');
}
return Expanded(child: _tweetList(snapshot.data));
} else {
return Center(child: CircularProgressIndicator());
}
},
)

Related

How to display images from Frirebase Storage in StreamBuilder

I'm trying to display images from FirebaseStorage in StreamBuilder when user add them after they have been saved in FirebaseStorage
Try #1
It works but only the last added image is shown as stream and I would like to display all the images added.
I guess this try is the better way but I can't add ListView.builder because TaskSnapshot is one data and not a list of data like ListResult that's probably why I can't diplay all images and only the last one.
Widget:
Widget loadingImage(UploadTask uploadTask) => StreamBuilder<TaskSnapshot>(
stream: uploadTask.snapshotEvents,
builder: (context, snapshot) {
if(snapshot.hasData) {
final snap = snapshot.data;
final photoName = snap!.metadata!.name;
final photoType = snap.metadata!.contentType;
final photo = snapshot.data!.ref.getDownloadURL();
return StreamBuilder(
stream: photo.asStream(),
builder: (context, AsyncSnapshot<String> snapshot) {
final image = snapshot.data;
print("Image: $image");
return Row(
children: [
Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
image!,
),
const SizedBox(width: 20,),
Text(
photoName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 20,),
Text(
photoType!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
)
],
);
}
);
}
return Container();
},
);
Try #2
It displays images but not as stream if one is added it's not shown if a second one is added the first one will be displayed etc
DocumentViewModel:
//----------------------------------------------------------------------------
//----------------------------- Upload instructor document list --------------
//----------------------------------------------------------------------------
static Stream<ListResult>? uploadFileList(String uid) {
try {
final result = FirebaseStorage.instance
.ref("instructorDocuments/")
.child("$uid/")
.listAll();
return result.asStream();
} on FirebaseException catch (e) {
return null;
}
}
Widget:
Widget loadingImage(UploadTask uploadTask) => StreamBuilder<ListResult>(
stream: DocumentViewModel.uploadFileList(user!.uid),
builder: (context, snapshot) {
if(snapshot.hasData) {
final snap = snapshot.data;
return ListView.builder(
itemCount: snap!.items.length,
itemBuilder: (context, index) {
final photoName = snap.items[index].name;
final photoType = snap.items[index].getMetadata();
final photo = snap.items[index].getDownloadURL();
return StreamBuilder<FullMetadata>(
stream: photoType.asStream(),
builder: (context, AsyncSnapshot<FullMetadata> snapshot) {
if(snapshot.hasData) {
final type = snapshot.data!.contentType;
return StreamBuilder(
stream: photo.asStream(),
builder: (context, AsyncSnapshot<String> snapshot) {
if(snapshot.hasData) {
final image = snapshot.data;
return Row(
children: [
Image.network(
fit: BoxFit.cover,
width: 100,
height: 100,
image!,
),
const SizedBox(width: 20,),
Text(
photoName,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
const SizedBox(width: 20,),
Text(
type!,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.bold,
),
),
],
);
}
return Container();
},
);
}
return Container();
}
);
},
shrinkWrap: true,
physics: const BouncingScrollPhysics(),
);
}
if(!snapshot.hasData) {
return const Loader();
}
if(snapshot.hasError) {
return Utils.showErrorMessage(snapshot.hasError.toString());
}
else {
return Container();
}
}
);
Thanks in advance
try this without streambuilder for getting images from firebase
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
class BlogView extends StatefulWidget {
const BlogView({Key? key}) : super(key: key);
#override
State<BlogView> createState() => _BlogViewState();
}
var im;
var allimgList = [];
class _BlogViewState extends State<BlogView> {
#override
void initState() {
getData();
super.initState();
setState(() {
});
}
void getData() async {
allimgList=[];
await FirebaseFirestore.instance
.collection('Names')
.snapshots()
.forEach((event) {
var n=event.docs.length;
print(event.docs.length);
for (int i = 0; i <n; i++) {
im = event.docs[i]['Images'];
var nn=event.docs[i]['Images'].length;
for(int j=0;j<nn;j++){
allimgList.add(im[j]);
setState(() {});
}
if(i==n)break;
}
print("=========>" + allimgList.toString());
});
setState(() {
});
}
#override
Widget build(BuildContext context) {
// print("================lllllllll =>" + allimgList.toString());
return Scaffold(
appBar: AppBar(
title: Text("Blog"),
),
body: Container(
child: Center(
child: allimgList.length == 0 ?
Container(
height: 500,
width: 300,
child: Center(child: Text("No Image Added Yet")),
):Container(
margin: EdgeInsets.only(top: 10),
child: GridView.builder(
padding: EdgeInsets.only(left: 10, right: 10),
gridDelegate: const SliverGridDelegateWithMaxCrossAxisExtent(
maxCrossAxisExtent: 150,
childAspectRatio: 4 / 4,
crossAxisSpacing: 10,
mainAxisSpacing: 20),
itemCount: allimgList.length,
itemBuilder: (BuildContext ctx, index) {
return Container(
alignment: Alignment.center,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(15)),
child: Image.network(
allimgList[index],
fit: BoxFit.fill,
),
);
}),
),
),
),
);
}
}

Update Appbar after stream builder is done

After stream builder is done, it has the numitems as number of valid items. This numitems should show on a cart icon in AppBar. Right now the it is showing no number on cart icon as the UI is built before the stream builder is fully done.
The stream builder is inside the body widget as it also displays a list of items on the screen. Thats why I dont prefer to havestream builder within AppBar. I was wondering what is the suitable way to update AppBar with numitems after stream builder is finished.
Here is the code:
class BasketPage extends StatefulWidget {
String title;
UserAccount useraccount;
BasketPage({required this.title, required this.useraccount});
#override
BasketPageState createState() {
return BasketPageState(title: this.title, useraccount: this.useraccount);
}
}
class BasketPageState extends State<BasketPage> {
int numitems = 0;
Stream<QuerySnapshot>? fooditemsStream;
CollectionReference? fooditems;
BasketPageState(){
this.fooditems = FirebaseFirestore.instance.collection('fooditems');
this.fooditemsStream = this.fooditems!
.where("receiever", isEqualTo: this.useraccount.uname)
.where("taken", isNull: true)
.snapshots();
}
BasketBloc? basketBloc;
List<FoodItem> foodItemsList = [];
#override
Widget build(BuildContext context) {
basketBloc = BlocProvider.of<BasketBloc>(context);
return Scaffold(
appBar: AppBar(
actions: [
Stack(
children: <Widget>[
const IconButton(
icon: Icon(
Icons.shopping_cart_sharp,
color: Colors.white,
),
onPressed: null,
),
this.numitems==0 // if numitems is 0 then return empty container else display numitems value
? Container()
: Positioned(
top: 0,
right: 0,
child: Stack(
children: <Widget>[
Container(
height: 20.0,
width: 20.0,
decoration: const BoxDecoration(
color: Colors.red,
shape: BoxShape.circle,
),
child: const Center(
child: Text(
"10",
style: TextStyle(
color: Colors.white,
fontSize: 11.0,
fontWeight: FontWeight.bold,
),),),),],),),],),],),
body: SingleChildScrollView(
child: SafeArea(
child: Column(
children: <Widget>[
BlocListener<BasketBloc,BasketState>(
listener: (context,state){},
child: BlocBuilder<BasketBloc,BasketState>(
builder: (context,state) {
return Container();
}),),
Flexible(
child:
Column(
children: [
SizedBox(
child: StreamBuilder<QuerySnapshot>(
stream: fooditemsStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
if (snapshot.data!.docs.length == 0){
return Text("Currently there are no food items in the list");
}
// else return list and update numitems when building list
return Column(
children: [
ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
final bool isExpired = data['expired'];
if (isExpired) return Container();
// update numitems here
numitems= numitems+1;
return ListTile(
contentPadding:const EdgeInsets.only(top: 10.0, left: 15.0, right: 15.0, bottom:5.0),
title: Padding(
padding: const EdgeInsets.only(bottom:8.0),
child: Text(capitalize(data['item_name']),
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
),
),
);
}).toList(),
),],);},),),],)),],),),);}}
Thanks!
The easiest solution is just to add setState() after you update numitems = numitems + 1. This will make your whole BasketPage to rebuild.
But it's not the best solution, because your it causes your whole page to rebuild, when it is unnecessary. Better solution would be to use ValueNotifier. Wrap your AppBar with ValueListenableBuilder and create ValueNotifier.
As a side note you don't need to pass anything to your BasketPageState. If you need to access them use widget.title

Generate gridView from an http request json response

the aim is to create a gridView of x elements, where x is dynamic because depends from the answer I receive from http request.
I know how to create a base gridView and how to print the result of my http request, but I've not understood how can I do both things: read json response, count how many elements I hava to create, create all elements with their names.
Here is the code:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'utils/Request.dart';
class MyGames extends StatelessWidget {
// ### Sono i parametri che invio al php nella richiesta
static var requestBody = {"action": "getMyGames", "id_utente": "1"};
Future<Response> richiesta = makeRequest(requestBody);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[850],
body:
GridView.count(crossAxisCount: 1, childAspectRatio: (2.5), children: [
FutureBuilder<Response>(
future: richiesta,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Padding(
padding: EdgeInsets.all(10),
child: Container(
decoration: new BoxDecoration(
color: Colors.green[200],
),
child: Center(
child: Text(
snapshot.data.result[0]['name'](),
style: TextStyle(
color: Colors.grey[800],
),
),
),
));
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
]),
);
}
}
Thanks!
I've solved this way:
import 'package:flutter/material.dart';
import 'package:http/http.dart' as http;
import 'utils/Request.dart';
class MyGames extends StatelessWidget {
// ### Sono i parametri che invio al php nella richiesta
static var requestBody = {"action": "getMyGames", "id_utente": "1"};
Future<Response> richiesta = makeRequest(requestBody);
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: Colors.grey[850],
body: FutureBuilder<Response>(
future: richiesta,
builder: (context, snapshot) {
if (snapshot.hasData) {
final List<Widget> listone =
snapshot.data.result.map<Widget>((el) => tile(el)).toList();
return GridView.extent(
maxCrossAxisExtent: 250,
children: listone,
);
} else if (snapshot.hasError) {
return Text("${snapshot.error}");
}
return CircularProgressIndicator();
},
),
);
}
Widget tile(el) {
return Padding(
padding: EdgeInsets.all(10),
child: Container(
decoration: new BoxDecoration(
color: Colors.green[200],
),
child: Center(
child: Text(
el['nomeGioco'],
style: TextStyle(
color: Colors.grey[800],
),
), /*Center(
child: Text('Item $index',
style: Theme.of(context).textTheme.headline5)),*/
),
));
}
}

Flutter Cannot show List item in Drawar

I am new to flutter.
I am trying to create list view dynamically using server response JSON data using futureBuilder. The code writen by me while wathing YouTube videos, but I can't understand what is the mistake.
//It looks like your post is mostly code; please add some more details.//
main.dart
import 'package:flutter/material.dart';
import 'home.dart';
void main() {
runApp(MaterialApp(
debugShowCheckedModeBanner: false,
home: home(),
));
}
home.dart
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'dart:async';
import 'dart:convert';
import 'package:http/http.dart' as http;
class home extends StatefulWidget {
home({Key key}):super();
#override
homepage createState() =>new homepage();
}
class homepage extends State<home>{
Color theme = Colors.lightBlue;
Color theme_text = Colors.white;
Future<List<category>> _getCategory() async {
var data = await http
.get("https://next.json-generator.com/api/json/get/VJ6EHYFO_");
debugPrint(data.body);
var jsondata = json.decode(data.body);
List<category> list = [];
for (var u in jsondata) {
category c = category(u['name'], u['id']);
list.add(c);
}
return list;
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
backgroundColor: theme,
title: Text('SIMS Home Page'),
actions: <Widget>[
IconButton(
icon: Icon(Icons.shopping_cart),
onPressed: () => debugPrint("search pressed"),
)
],
),
drawer: Drawer(
child: ListView(
children: <Widget>[
DrawerHeader(
child: Text(
"Not Signed",
textAlign: TextAlign.center,
style: TextStyle(color: theme_text, fontSize: 20),
),
decoration: BoxDecoration(
color: theme,
),
),
FutureBuilder(
future: _getCategory(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text("Loading..."),
),
);
} else {
return ListView.builder(
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(title: Text(snapshot.data[index]),);
});
}
},
),
],
),
),
);
}
class category {
final name;
final id;
category(this.name, this.id);
}
You have to use shrinkwrap property of listView.builder to get it work. it will allow listview to grow only that much which is require.
Moreover, in text view you are assigning items directly you have to access name and id individually as shown in below code.
Container(
height: 150,
child: DrawerHeader(
child: Text(
"Not Signed",
textAlign: TextAlign.center,
style: TextStyle(color: theme_text, fontSize: 20),
),
decoration: BoxDecoration(
color: theme,
),
),
),
FutureBuilder(
future: _getCategory(),
builder: (BuildContext context, AsyncSnapshot snapshot) {
if (snapshot.data == null) {
return Container(
child: Center(
child: Text("Loading..."),
),
);
} else {
return Container(
height: MediaQuery.of(context).size.height - 150,
child: ListView.builder(
shrinkWrap: true, //added line
itemCount: snapshot.data.length,
itemBuilder: (BuildContext context, int index) {
return ListTile(
title: Text(snapshot.data[index]
.toString()), //accessing name from json
);
}),
);
}
},
),

The getter length was called on null

This is my full class code i am retrieving data from mySQL and showing it on screen but i am continiously getting this error that , The getter 'length' was called on null.
Receiver: null
Tried calling: length , i can't find on which line i am doing wrong and what i am doing wrong everything is looking good , what i am doing wrong please help !
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:http/http.dart' show get;
import 'dart:convert';
class Spacecraft {
final String id;
final String name , experience , img_url , rating, contact, description;
Spacecraft({
this.id,
this.name,
this.experience,
this.img_url,
this.rating,
this.contact,
this.description
});
factory Spacecraft.fromJson(Map<String, dynamic> jsonData) {
return Spacecraft(
id: jsonData['id'],
name: jsonData['name'],
experience: jsonData['experience'],
img_url: "http://sha-way.freeweb.pk/images/"+jsonData['img_url'],
rating: jsonData['rating'],
contact: jsonData['contact'],
description: jsonData['description'],
);
}
}
class CustomListView extends StatelessWidget {
final List<Spacecraft> spacecrafts;
CustomListView([this.spacecrafts]);
Widget build(context) {
return ListView.builder(
itemCount: spacecrafts.length,
itemBuilder: (context, int currentIndex) {
return createViewItem(spacecrafts[currentIndex], context);
},
);
}
Widget createViewItem(Spacecraft spacecraft, BuildContext context) {
return new ListTile(
title: new Card(
elevation: 5.0,
child: new Container(
decoration: BoxDecoration(border: Border.all(color: Colors.orange)),
padding: EdgeInsets.all(20.0),
margin: EdgeInsets.all(20.0),
child: Column(
children: <Widget>[
Padding(
child: Image.network(spacecraft.img_url),
padding: EdgeInsets.only(bottom: 8.0),
),
Row(children: <Widget>[
Padding(
child: Text(
spacecraft.name,
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.right,
),
padding: EdgeInsets.all(1.0)),
Text(" | "),
Padding(
child: Text(
spacecraft.experience,
style: new TextStyle(fontStyle: FontStyle.italic),
textAlign: TextAlign.right,
),
padding: EdgeInsets.all(1.0)),
]),
],
),
),
),
onTap: () {
//We start by creating a Page Route.
//A MaterialPageRoute is a modal route that replaces the entire
//screen with a platform-adaptive transition.
var route = new MaterialPageRoute(
builder: (BuildContext context) =>
new SecondScreen(value: spacecraft),
);
//A Navigator is a widget that manages a set of child widgets with
//stack discipline.It allows us navigate pages.
Navigator.of(context).push(route);
});
}
}
//Future is n object representing a delayed computation.
Future<List<Spacecraft>> downloadJSON() async {
final jsonEndpoint =
"http://sha-way.freeweb.pk";
final response = await get(jsonEndpoint);
if (response.statusCode == 200) {
List spacecrafts = json.decode(response.body);
return spacecrafts
.map((spacecraft) => new Spacecraft.fromJson(spacecraft))
.toList();
} else
throw Exception('We were not able to successfully download the json data.');
}
class SecondScreen extends StatefulWidget {
final Spacecraft value;
SecondScreen({Key key, this.value}) : super(key: key);
#override
_SecondScreenState createState() => _SecondScreenState();
}
class _SecondScreenState extends State<SecondScreen> {
#override
Widget build(BuildContext context) {
return new Scaffold(
appBar: new AppBar(title: new Text('Detail Page')),
body: new Container(
child: new Center(
child: Column(
children: <Widget>[
Padding(
child: new Text(
'BACHAY LOG KI DETAILS',
style: new TextStyle(fontWeight: FontWeight.bold,fontSize: 20.0),
textAlign: TextAlign.center,
),
padding: EdgeInsets.only(bottom: 20.0),
),
Padding(
//`widget` is the current configuration. A State object's configuration
//is the corresponding StatefulWidget instance.
child: Image.network('${widget.value.img_url}'),
padding: EdgeInsets.all(12.0),
),
Padding(
child: new Text(
'NAME : ${widget.value.name}',
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
padding: EdgeInsets.all(20.0),
),
Padding(
child: new Text(
'EXPERIENCE : ${widget.value.experience}',
style: new TextStyle(fontWeight: FontWeight.bold),
textAlign: TextAlign.left,
),
padding: EdgeInsets.all(20.0),
)
], ),
),
),
);
}
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
theme: new ThemeData(
primarySwatch: Colors.deepOrange,
),
home: new Scaffold(
appBar: new AppBar(title: const Text('MySQL Images Text')),
body: new Center(
//FutureBuilder is a widget that builds itself based on the latest snapshot
// of interaction with a Future.
child: new FutureBuilder<List<Spacecraft>>(
future: downloadJSON(),
//we pass a BuildContext and an AsyncSnapshot object which is an
//Immutable representation of the most recent interaction with
//an asynchronous computation.
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
//return a circular progress indicator.
return new CircularProgressIndicator();
},
),
),
),
);
}
}
This is the error i am getting
════════ Exception caught by widgets library ═══════════════════════════════════════════════════════
The following NoSuchMethodError was thrown building CustomListView(dirty):
The getter 'length' was called on null.
Receiver: null
Tried calling: length
You are making a little mistake.
Here you are defining spacecrafts as optional:
CustomListView([this.spacecrafts]);
But when you call it here:
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts);
You are calling it like it was obligatory parameter, so you are not really passing any parameter actually since you are not assign it at the correct variable.
If you want to keep that optional, you should call your CustomListView like:
List<Spacecraft> spacecrafts = snapshot.data;
return new CustomListView(spacecrafts:spacecrafts);
Otherwise, if there could be not case where the list could be omitted, change this:
CustomListView([this.spacecrafts]);
to this:
CustomListView(this.spacecrafts);
This is the error:
itemCount: spacecrafts.length,
Why?
Because it uses a null initialization
final List<Spacecraft> spacecrafts; **<-- this is null(empty) - no length**
CustomListView([this.spacecrafts]);
Widget build(context) {
return ListView.builder(
itemCount: spacecrafts.length, **<-- spacecrafts is null, has no data**
itemBuilder: (context, int currentIndex) {
return createViewItem(spacecrafts[currentIndex], context);
},
);
}
and you are updating the new spacecrafts List, in a different class
builder: (context, snapshot) {
if (snapshot.hasData) {
List<Spacecraft> spacecrafts = snapshot.data; **<-- new spacecrafts initialization in a dofferent Class**
return new CustomListView(spacecrafts);
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
//return a circular progress indicator.
return new CircularProgressIndicator();
},