How to properly use future builder inside a column - flutter

Hello i'm using a future builder inside my column widget( i have other widgets sadly ).
Here is my code :
FutureBuilder(
future: loadStudent(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return (snapshot.data.student)
.map<Widget>((std) => StudentProfile(
std.StudentName,
std.StudentPicture,
std.speciality,
std.badge))
.toList();
} else {
return CircularProgressIndicator();
}
}),
well it throws this error:
type 'List' is not a subtype of type 'Widget'.
I understand what the error mean ( basicly column takes individual widgets as children ) , yet i don't know how to fix it .
Please note that i tried to use the '...' operator in my return statement it threw another error :
Expected an identifier, but got '...'

You are returning a List of Widget instead of a Widget.
You need to put your List on a Column Widget like this :
List students = (snapshot.data.student)
.map<Widget>((std) => StudentProfile(
std.StudentName,
std.StudentPicture,
std.speciality,
std.badge)).toList();
return Column(
children: students,
)

Related

i checked for null and still the null safety is bugging me in flutter

case ConnectionState.done:
{
if (taskData.data != Null) {
return Padding(
padding: EdgeInsets.all(8.0),
child: ListView.builder(
itemCount : taskData.data.length,
itemBuilder: (context, index) {
String task =
taskData.data[index]['task'].toString();
String day = DateTime.parse(
taskData.data[index]['creationDate'])
.day
.toString();
},
),
);
The property 'length' can't be unconditionally accessed because the receiver can be 'null'.
Try making the access conditional (using '?.') or adding a null check to the target ('!').
Missing case clause for 'none'.
Try adding a case clause for the missing constant, or adding a default clause.dart
The body might complete normally, causing 'null' to be returned, but the return type, 'Widget', is a potentially non-nullable type.
Try adding either a return or a throw statement at the end.
im getting these errors
i checked in an if statement that supposed to be a solution but it didnt fix it
tried Null and null same issue
im still noob im sorry if its simple but couldnt find an answer
edit
child: FutureBuilder(
future: getTasks(),
builder: (_, taskData) {
if (taskData.data != null){
print(taskData.data);
}
}
The body might complete normally, causing 'null' to be returned, but the return type, 'Widget', is a potentially non-nullable type.
Try adding either a return or a throw statement at the end.
the error after making taskData
Provide datatype on FutureBuilder/StreamBuilder and use taskData.data?.length on itemCount.
FutureBuilder<List<ModelClass>?>(
builder: (context, taskData) {
//... others
if (taskData.data != null) {
return ListView.builder(
itemCount: taskData.data?.length,
itemBuilder: (context, index) => Text(""),
);
}
// To handle body retun null
return Text("");
},
),
This error message show because you are your FutureBuilder is missing default return.
The body might complete normally, causing 'null' to be returned,

Getting a field value from known document in firebase flutter

I want a user to give a value in my app and other user to see it. But i can upload data to firebase but not get it on the other side. I have null safety enabled on flutter. The code is :-
child: StreamBuilder(
stream:FirebaseFirestore.instance.collection('Collection name').doc('doc id').snapshots()
builder: (context, snapshot){
if(snapshot.hasData){
return Text(snapshot.data['Field name'].toString()); // here my editor shows error saying 'The method '[]' can't be unconditionally invoked because the receiver can be 'null'.' and asks for a null check and then shows error again
}
return Text('Nothing');
}
),
Edit: I am getting only 'type '_JsonDocumentSnapshot' is not a subtype of type 'Map<String, dynamic>' in type cast' as the error
And if I change
if(snapshot.hasData){
return Text(snapshot.data['Field name'].toString());
to
if (snapshot.hasData) {
return Text(snapshot.data.toString());
}
i get output as
'Instance of '_JsonDocumentSnapshot''
where the data should be.
I am using null check dart
Thannks for everyone's support, but i have accidently found and answer. I read that the data type of that is DocumentSnapshot and this worked for me
builder: (context, snapshot) {
if (snapshot.hasData) {
var numGuess = snapshot.data as DocumentSnapshot;
return Text(numGuess['Field name'].toString());
}
return Text('Nothing');
}
This works for null safety.
Since you are retrieving a collection then try the following:
return Text(snapshot.data!.docs[0].data()["Field Name"]);
A collection will return a list of documents, therefore use the docs property to be able to access the document. The above code will show the field in the first document in the list, it's better if you use a listview to return all documents.
Check:
https://api.flutter.dev/flutter/widgets/ListView-class.html
Give datatype to your StreamBuilder like this
StreamBuilder<DocumentSnapshot<Map<String, dynamic>>>(
stream:
FirebaseFirestore.instance.collection('Collection name').doc('doc id').snapshots(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data?.data()?['Field name']);
}
return Text('Nothing');
},
),

Flutter StreamBuilder with initialData and null-awareness

I have some problems understanding the StreamBuilder using the new null-aware operators.
As a learning project I am implementing a sign-in flow with the BloC pattern. For my email sign-in form I have created a model class which I access through a StreamBuilder. Without the use of initialData it makes complete sense that snapshot.data can be null. However, setting the initialData to a predefined empty model, snapshot.data could never be null right? Here is my code snippet:
#override
Widget build(BuildContext context) {
return StreamBuilder<EmailSignInModel>(
stream: widget.bloc.modelStream,
initialData: EmailSignInModel(),
builder: (context, snapshot) {
final EmailSignInModel model = snapshot.data;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
mainAxisSize: MainAxisSize.min,
children: _buildChildren(),
),
);
});
}
The compiler warns me that snapshot.data is of type <EmailSignModel?> which is not equal to . I could fix this with snapshot.data ?? EmailSignModel() but this would be redundant with initialData right?
What is the proper way of handling this situation and taken care of the null-awareness of Dart?
Diving into the source code, I found the following:
https://github.com/flutter/flutter/blob/master/packages/flutter/lib/src/widgets/async.dart
/// The latest data received by the asynchronous computation.
///
/// If this is non-null, [hasData] will be true.
///
/// If [error] is not null, this will be null. See [hasError].
///
/// If the asynchronous computation has never returned a value, this may be
/// set to an initial data value specified by the relevant widget. See
/// [FutureBuilder.initialData] and [StreamBuilder.initialData].
final T? data;
/// Returns latest data received, failing if there is no data.
///
/// Throws [error], if [hasError]. Throws [StateError], if neither [hasData]
/// nor [hasError].
T get requireData {
if (hasData)
return data!;
if (hasError)
throw error!;
throw StateError('Snapshot has neither data nor error');
}
AsyncSnapshot actually has a requireData getter, which will ensure non-null or will throw an error. So just replace snapshot.data with snapshot.requireData
This still requires some manual work where the use of initialData and requireData need to be kept in sync. You could also just use snapshot.data! which basically does the same.
I just replace the snapshot.data! with snapshot.requireData

Two StreamBuilderon on one screen with default empty screen shown when neither has data

I'm trying to create a Split View with two ListViews, one of them showing Tasks which are not completed and the second one containing completed tasks. I managed to make this work with a Column containing two Streambuilder. The problem I don't know how to solve is how to show a single default empty screen when neither of the two Streams have any values, due to the way Flutter works, I can't just return null. So I end up having two Empty default screens in my Column and on my screen.
What would be the right approach to make something like shown in the GIF with Firestore?
If I need to work with a single Stream and filter it inside dart, how can I make it work with ListView ?
I'd highly appreciate an example.
My Job class contains a boolean value jobDone which tells me in which list the Job has to go.
My current code:
return Column(
children: [
StreamBuilder<List<Job>>(
stream: getPendingJobsStream(database),
builder: (context, snapshot) {
return ListItemsBuilder<Job>(
...
);
},
),
ExpansionTile(
title: Text('Completed Tasks'),
children: [
StreamBuilder<List<Job>>(
stream: getCompletedJobsStream(database),
builder: (context, snapshot) {
return ListItemsBuilder<Job>(
...
);
},
),
],
),
],
);
You can check for snapshot state
builder: (context, snapshot) {
if(snapshot.connectionState ==ConnectionState.waiting){
return Center(child:CircularProgressIndicator();
}
if(snapshot.connectionState ==ConnectionState.done){
//check if snapshot has data
if(snapshot.hasData){
return ListItemsBuilder<Job>( ..

Flutter StreamProvider 'List<dynamic>' is not a subtype of type 'List<??>' in type cast

I'm trying to user StreamProvider to make data from a firestore doc stream available throughout my app. Its a recipe app and this is the shopping list.
I have a model RecipeItem that contains details about the item from the recipe. The firestore doc holds one value which is an array, called 'list', containing maps for each item that is in the list.
Below is my connection to firestore and setting up the stream. I try to get the doc, then user map to create a RecipeItem instance for each item in the list. Here is the method:
Stream<List<RecipeItem>> getPersonalList() {
print('Fetching personal list');
return _db.collection('shopping_lists').document(userId).snapshots().map(
(DocumentSnapshot documentSnapshot) => documentSnapshot.data['list']
.map(
(item) =>
// print(item);
RecipeItem(
category: item['category'],
wholeLine: item['wholeLine'],
recipeTitle: item['recipeTitle'],
recipeId: item['recipeId'],
purchased: item['purchased'],
),
)
.toList(),
);
}
Now in main.dart I have a StreamProvider that looks for type <List<RecipeItem>>
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MultiProvider(
providers: [
StreamProvider<FirebaseUser>(
//Access withing the app -> var user = Provider.of<FirebaseUser>(context);
create: (_) => AuthService().user),
StreamProvider<List<RecipeItem>>(
create: (_) => PersonalListDB().getPersonalList(),
catchError: (context, error) {
print('This is the error from stream provider *** $error');
},
),
ChangeNotifierProvider(
create: (_) => RecipesDB(),
)
],
child: MaterialApp(
etc etc...
When I run this I get this error:
type 'List' is not a subtype of type 'List' in type cast
The only way I can fix this is if I change everywhere I have List<RecipeItem> to List<dynamic>. This works but it doesn't seem like the right solution.
I have tried a few (a million) things.
I found this post here: Getting type 'List<dynamic>' is not a subtype of type 'List<...>' error in JSON
This tells me that .toList() might be the problem because it creates List. So I have tried using List.from and using .cast<List> but have had no luck. What is further confusing me is that I pretty closely follow other tutorials doing similar things.
Any help to resolve this and help me understand the problem is greatly appreciated.
The List of Firestore (db, httpRequest, etc) are of type dynamic to avoid problems when calling them, you can try to tell the map of what type are you trying to cast your object
return _db.collection('shopping_lists').document(userId).snapshots().map<List<RecipeItem>>( //I'm not sure what kind of object this map should return, is this the method map part of the stream? if it is then it should be of type List<RecipeItem>
(DocumentSnapshot documentSnapshot) => documentSnapshot.data['list']
.map<RecipeItem>( //now it will know it's not a dynamic value but of type RecipeItem
(item) =>
// print(item);
RecipeItem(
category: item['category'],
wholeLine: item['wholeLine'],
recipeTitle: item['recipeTitle'],
recipeId: item['recipeId'],
purchased: item['purchased'],
),
)
.toList(),
);