read user info from FirebaseFirestore in StreamBuilder - flutter

I'm trying to read document info from Database this my code
abstract class Database{
Future<void> sendText(SendOrder textData);
Stream<QuerySnapshot<Map<String, dynamic>>> readDataStream();
}
class FirestoreDatabase implements Database{
FirestoreDatabase({required this.uid});
final String uid;
#override
Future<void> sendText(SendOrder textData) async {
final documentRefer = FirebaseFirestore.instance.collection('userInfo').doc(uid);
await documentRefer.set(textData.toMap());
}
#override
Stream<QuerySnapshot<Map<String, dynamic>>> readDataStream() {
return FirebaseFirestore.instance.collection('userInfo').where('uid', isEqualTo: uid).snapshots();
}
}
with send text I got no problem but read data I got problem
getData() async{
return database.readDataStream().map((snapshot) => snapshot.docs);
}
Future<void> _dataSend(BuildContext context) async {
await database.sendText(SendOrder(userName: 'name', coins: '12', level: '12'));
}
body: StreamBuilder<Database>(
builder: (context, snapshot){
return Column(
children: [
ElevatedButton(onPressed: () {
_dataSend(context);
}, child:
const Text('Click')),
Text(
getData().toString()
, style: const TextStyle(color: Colors.white),
)
],
);
},
)
I need to read map data but I have problem becuase I'm new flutter developer , don't know how to convert data or where comes from
so how to read data?
Is my code right to read as StreamBuilder?

You had not set stream for your StreamBuilder. Check below code for simple StreamBuilder:
class HomePage extends StatelessWidget {
const HomePage({Key? key}) : super(key: key);
Stream<String> _generateNumbers() async* {
for (int i = 1; i <= 10; i++) {
await Future<void>.delayed(const Duration(seconds: 1));
yield "$i";
}
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: StreamBuilder<String>(
stream: _generateNumbers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const CircularProgressIndicator();
} else if (snapshot.connectionState == ConnectionState.active
|| snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
return const Text('Error');
} else if (snapshot.hasData) {
return Text(
snapshot.data ?? '',
style: const TextStyle(color: Colors.red, fontSize: 40)
);
} else {
return const Text('Empty data');
}
} else {
return Text('State: ${snapshot.connectionState}');
}
},
),
),
);
}
}

Related

is there a better way to write a document stream builder?

Is there a better way to write this code?
The application is about jobs and job applications.
Each job can have multiple job applications.
if the job is fulfilled, all the job applications should be closed so that people are not applying to a job that is closed.
I don't like the way the code is written but it achieves the functionality that I wanted.
to get to the 'JobApplicationView' I have to go through a page that displays all the current job applications, once I click on one of the job application, the job application.
here is the snip of code from the 'job application list view'
StreamBuilder(
stream: _jobsService.allJobApplications(userId),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
case ConnectionState.active:
if (snapshot.hasData) {
final allJobApplications =
snapshot.data as Iterable<CloudJobApplication>;
return JobApplicationsListView(
allowScroll: false,
jobApplications: allJobApplications,
onTap: (job) {
Navigator.of(context).pushNamed(
myJobApplicationsRoute,
arguments: job,
);
},
);
} else {
return const CircularProgressIndicator();
}
default:
return const CircularProgressIndicator();
}
},
),
Below is the really ugly code that performs the functionality that i want
class JobApplicationView extends StatefulWidget {
const JobApplicationView({Key? key}) : super(key: key);
#override
_JobApplicationViewState createState() => _JobApplicationViewState();
}
class _JobApplicationViewState extends State<JobApplicationView> {
final _formKey = GlobalKey<FormState>();
late final FirebaseCloudStorage _cloudFunctions;
final currentUser = AuthService.firebase().currentUser!;
#override
void initState() {
super.initState();
_cloudFunctions = FirebaseCloudStorage();
}
getExistingJobApplication(BuildContext context) {
return FirebaseFirestore.instance
.collection('job application')
.doc(context.getArgument<CloudJobApplication>()!.documentId)
.snapshots();
}
submitProposal(localStates) {
submitProposal() async {
await _cloudFunctions.updateJobApplicationColumn(
documentId: context.getArgument<CloudJobApplication>()!.documentId,
fieldNameColumn: jobApplicationStateColumn,
fieldNameColumnValue: jobApplicationStateOpen);
await _cloudFunctions.updateJobApplicationColumn(
documentId: context.getArgument<CloudJobApplication>()!.documentId,
fieldNameColumn: jobApplicationSubStateColumn,
fieldNameColumnValue: 'Awaiting client proposal');
}
// job applicator can submit proposal if the state is new
if (localStates['jobApplicatotIsSameAsCurrentUser'] &&
localStates['jobApplication'][jobApplicationStateColumn] ==
jobApplicationStateNew) {
return ElevatedButton(
style: ElevatedButton.styleFrom(
textStyle: const TextStyle(fontSize: 13),
backgroundColor: Colors.blue,
),
onPressed: () => submitProposal(),
child: const Text('Submit proposal'),
);
// job creator can accept the proposal if state is open
// job creator can deny job proposal if state is open
} else {
return Container();
}
}
#override
void dispose() {
super.dispose();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('update job application'),
actions: [],
),
body: StreamBuilder(
stream: getExistingJobApplication(context),
builder: (context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (!snapshot.hasData) {
return const CircularProgressIndicator();
}
// this is where I saved the local states
var localStates = {};
localStates['width'] = MediaQuery.of(context).size.width;
localStates['jobApplication'] = snapshot.data;
localStates['formFieldsEditable'] = localStates['jobApplication']
[jobApplicationStateColumn] ==
jobApplicationStateNew &&
currentUser.id ==
localStates['jobApplication'][jobApplicatorIdColumn]
? true
: false;
localStates['jobApplicatotIsSameAsCurrentUser'] = currentUser.id ==
localStates['jobApplication'][jobApplicatorIdColumn];
localStates['jobCreatorIsSameAsCurrentUser'] = currentUser.id ==
localStates['jobApplication'][jobCreatorIdColumn];
return Form(
key: _formKey,
child: ListView(
padding: const EdgeInsets.all(32.0),
children: [
submitProposal(localStates),
],
),
);
}),
);
}
}

rebuilding listview.builder every time I scroll , and 'Stream has already been listened to' error

I am using a stream builder which has another stream builder inside it. Every time I get data from the first stream I use some of this data in the other stream to finally build a list view (POSTS), but I have a problem every time I scroll down I have this error:
if (!_isInitialState) {
throw StateError("Stream has already been listened to.");
}
I tried to listen to the second stream asBroadcastStream(), and I added the case that there is no data and every time I scroll I get the notification I made that there is no data any ideas?
This is my code:
StreamBuilder<QuerySnapshot>(
stream: posts.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: MediaQuery.of(context).size.height * 0.69,
child: ListView(
scrollDirection: Axis.vertical,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return StreamBuilder<DocumentSnapshot>(
stream: users
.doc(data['Uid'])
.get()
.asStream()
.asBroadcastStream(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {}
if (!(snapshot.hasData)) {
print("no data");
return SizedBox(
width: 0,
);
}
if (snapshot.connectionState ==
ConnectionState.done) {
Map<String, dynamic> daata = snapshot.data!
.data() as Map<String, dynamic>;
String username = daata['Username'];
String userimage = daata['Userimage'];
return mypost(
context,
data['title'],
data['ImageUrl'],
data['context'],
username,
userimage,
data['nlikes'],
data['ncomments'],
data['date']
.toDate()
.toString()
.split(' ')
.first);
}
return const Text("loading");
});
}).toList(),
),
);
}),
if any could help I would be happy with that.
It might interest you to know that when I run the below code (basically your code, but with my streams and mypost() function) I don't get any errors!... It scrolls fine!
import 'package:firebase_core/firebase_core.dart';
import 'package:my_app/firebase_labels.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:flutter/material.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
runApp(MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
home: Screen(),
);
}
}
class MyFirebase {
static FirebaseFirestore storeObject = FirebaseFirestore.instance;
}
class Screen extends StatelessWidget {
Screen({Key? key}) : super(key: key);
// Let me just define some streams here, from the same CollectionReference:
final CollectionReference posts = MyFirebase.storeObject
.collection(kCollectionConversations);
final CollectionReference users = MyFirebase.storeObject
.collection(kCollectionConversations);
#override
Widget build(BuildContext context) {
return Scaffold(
body: StreamBuilder<QuerySnapshot>(
stream: posts.snapshots(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(child: CircularProgressIndicator());
}
return SizedBox(
height: MediaQuery.of(context).size.height * 0.69,
child: ListView(
scrollDirection: Axis.vertical,
children:
snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data =
document.data()! as Map<String, dynamic>;
return StreamBuilder<DocumentSnapshot>(
stream: users
.doc(data['Uid'])
.get()
.asStream()
.asBroadcastStream(),
builder: (BuildContext context,
AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("Something went wrong");
}
if (snapshot.hasData && !snapshot.data!.exists) {}
if (!(snapshot.hasData)) {
print("no data");
return SizedBox(
width: 0,
);
}
if (snapshot.connectionState ==
ConnectionState.done) {
Map<String, dynamic>? daata = snapshot.data!
.data() as Map<String, dynamic>?;
String username = '';
String userimage = '';
if (daata != null) {
username = daata['Username'];
userimage = daata['Userimage'];
}
return mypost(
data,
// context,
data['title'],
data['ImageUrl'],
data['context'],
username,
userimage,
data['nlikes'],
data['ncomments'],
// data['date']
// .toDate()
// .toString()
// .split(' ')
// .first
);
}
return const Text("loading");
});
}
).toList(),
),
);
}),
);
}
}
Widget mypost(var data1, var data2, var data3, var data4, var data5, var data6, var data7, var data8/*, var data9,*/) {
return Container(
// height: 50,
child: Text('$data1'),
decoration: BoxDecoration(border: Border.all(color: Colors.blue)),
);
}
If you copy-paste this code into yours, do you get errors?
What if you change the streams for yours and the mypost() for yours? Do you get errors then?

Use the futureBuilder for main route

I set up my app with named routes:
class App extends StatelessWidget {
const App({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
initialRoute: "/",
routes: {
"/": (context) =>
FutureBuilder(
future: landingPage(),
builder: (BuildContext context, AsyncSnapshot<Widget> widget) {
if(widget.connectionState == ConnectionState.done){
if(!widget.hasData) {
return const Center(
child: Text("No Data exists"),
);
}
return widget.data!;
}
return const Center(
child: CircularProgressIndicator(
color: Colors.white,
),
);
},
),
"/email-verification":(context) => const EmailVerificationPage(),
"/login":(context) => const LoginPage(),
"/name-age":(context) => const AgePage(),
"/signup":(context) => const SignupPage(),
},
);
}
}
Then I have my functions where I decide which page should be the landing page depending on how far the user has added information to their account:
Future<Widget> landingPage() async {
if(FirebaseAuth.instance.currentUser == null){
return const LoginPage();
}
if(!FirebaseAuth.instance.currentUser!.emailVerified){
return const EmailVerificationPage();
}
if(! await checkAge(FirebaseAuth.instance.currentUser!.uid)){
return const AgePage();
}
return const HomePage();
}
Future<bool> checkAge(String uid) async {
QuerySnapshot snapshot = await FirebaseFirestore.instance.collection("users")
.where("uid", isEqualTo: FirebaseAuth.instance.currentUser!.uid)
.where("birtDate", isNull: false)
.get();
if(snapshot.size > 0){
return true;
}
return false;
}
However the futureBuilder always returns no data for some reason. Does anyone know why?

How can I fetch images from api into card widget in flutter?

I'm trying to fetch api images into the card widgets in flutter. I have services, model and photos_page class. I tried lot's of ways but still doesn't work.
final photoItem = snapshot.data[index].previewURL;
I think problem is that snapshot can't get data
that's my photos_page:
import 'dart:convert';
import 'package:abcde/services/photos_model.dart';
import 'package:abcde/services/photos_service.dart';
import 'package:abcde/widgets/card_widget.dart';
import 'package:flutter/material.dart';
class PhotosPage extends StatefulWidget {
PhotosPage({Key? key}) : super(key: key);
static const String pathId = 'Photos page';
#override
State<PhotosPage> createState() => _PhotosPageState();
}
class _PhotosPageState extends State<PhotosPage> {
PhotosService photosService = PhotosService();
#override
void initState() {
// TODO: implement initState
super.initState();
photosService.getPhotos();
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Photos'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
_getProductTypeList(),
]),
),
);
}
Widget _buildListItem(
BuildContext context, AsyncSnapshot<dynamic> snapshot, int index) {
final photoItem = snapshot.data[index].previewURL;
print('photoItem is $photoItem');
return photoCard(photoItem);
}
Widget _buildList(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
var values = snapshot.data;
return ListView.builder(
// itemCount: snapshot.length,
itemBuilder: (context, index) {
return _buildListItem(context, snapshot, index);
},
);
/* return ListView(
children: snapshot!.map((data) => _buildListItem(context, data)).toList(),
);*/
}
//PhotosModel photosModel = PhotosModel();
Widget _getProductTypeList() {
return Expanded(
child: FutureBuilder(
future: photosService.getPhotos(),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: LinearProgressIndicator(),
);
}
return _buildList(context, snapshot);
},
),
);
}
}
that's my model class:
class PhotosModel {
String previewURL;
PhotosModel({required this.previewURL});
/* factory PhotosModel.fromJson(Map<dynamic, dynamic> json) => PhotosModel(
previewURL: json['previewURL'] as String,
);
Map<String, dynamic> toJson() {
return <String, dynamic>{'previewURL': previewURL};
}*/
/* #override
String toString() {
return 'PhotosModel{url: $previewURL}';
}*/
/*factory PhotosModel.fromJson(Map<String, dynamic> json) {
return PhotosModel(url: json['url'] as String);
}*/
factory PhotosModel.fromJson(Map<dynamic, dynamic> json) =>
_commentFromJson(json);
Map<dynamic, dynamic> toJson() => photosModelToJson(this);
}
PhotosModel _commentFromJson(Map<dynamic, dynamic> json) {
return PhotosModel(
previewURL: json['previewURL'],
);
}
Map<dynamic, dynamic> photosModelToJson(PhotosModel instance) => <dynamic, dynamic>{
'previewURL': instance.previewURL,
};
and that's my service class. I make a list getPhotos() method but doesn't work anyway.
import 'dart:convert';
import 'package:abcde/services/photos_model.dart';
import 'package:http/http.dart';
const MY_API_KEY = '26711456-bde74f403cb42e77029bc1678';
const appUrl = 'https://pixabay.com/api/';
class PhotosService {
Future getData(String url) async {
print('Calling url: $url');
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
return response.body;
} else {
print(response.statusCode);
}
}
List<PhotosModel> dataList = [];
Future<List<PhotosModel>> getPhotos({String? query}) async {
final photosData = await getData(
'$appUrl?&key=$MY_API_KEY&q=$query');
var data =json.decode(photosData);
data.forEach((element) {
dataList.add(PhotosModel.fromJson(element));
});
print('this is photos data: $photosData');
return dataList;
}
}
that's the error it shows me
The following NoSuchMethodError was thrown building:
The method '[]' was called on null.
Receiver: null
Tried calling:
and that's my card widget
import 'package:flutter/material.dart';
Widget photoCard(String url) {
return Card(
child: Image(
image: NetworkImage(url),
),
);
}
for api I use this web-page:
https://pixabay.com/api/docs/#api_search_images
thank you very much in advance
Working Example
main.dart
===============
import 'package:demo_app/photo_page.dart';
import 'package:flutter/material.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const PhotosPage(),
);
}
}
==============
photo_service.dart
====================
import 'dart:convert';
import 'package:http/http.dart';
const apiKey = '26711456-bde74f403cb42e77029bc1678';
const appUrl = 'https://pixabay.com/api/';
class PhotosService {
Future getData(String url) async {
print('Calling url: $url');
final response = await get(Uri.parse(url));
if (response.statusCode == 200) {
return response.body;
} else {
print(response.statusCode);
}
}
List<PhotosModel> dataList = [];
Future<List<PhotosModel>> getPhotos(String query) async {
final photosData = await getData('$appUrl?key=$apiKey&q=$query');
var data = json.decode(photosData);
var items = data["hits"];
items.forEach((element) {
dataList.add(PhotosModel.fromJson(element));
});
print('this is photos data: $photosData');
return dataList;
}
}
class PhotosModel {
String previewURL;
PhotosModel({required this.previewURL});
factory PhotosModel.fromJson(Map<dynamic, dynamic> json) =>
_commentFromJson(json);
Map<dynamic, dynamic> toJson() => photosModelToJson(this);
}
PhotosModel _commentFromJson(Map<dynamic, dynamic> json) {
return PhotosModel(
previewURL: json['previewURL'],
);
}
Map<dynamic, dynamic> photosModelToJson(PhotosModel instance) =>
<dynamic, dynamic>{
'previewURL': instance.previewURL,
};
=========================
photo_page.dart
=============
import 'package:demo_app/photo_service.dart';
import 'package:flutter/material.dart';
class PhotosPage extends StatefulWidget {
const PhotosPage({Key? key}) : super(key: key);
static const String pathId = 'Photos page';
#override
State<PhotosPage> createState() => _PhotosPageState();
}
class _PhotosPageState extends State<PhotosPage> {
PhotosService photosService = PhotosService();
#override
void initState() {
super.initState();
photosService.getPhotos("flower");
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Photos'),
),
body: Padding(
padding: const EdgeInsets.all(16),
child: Column(children: [
_getProductTypeList(),
]),
),
);
}
Widget _buildListItem(
BuildContext context, AsyncSnapshot<dynamic> snapshot, int index) {
final photoItem = snapshot.data[index].previewURL;
print('photoItem is $photoItem');
return photoCard(photoItem);
}
Widget _buildList(BuildContext context, AsyncSnapshot<dynamic> snapshot) {
var values = snapshot.data;
return ListView.builder(
itemCount: snapshot.hasData ? snapshot.data.length : 0,
itemBuilder: (context, index) {
return _buildListItem(context, snapshot, index);
},
);
}
Widget _getProductTypeList() {
return Expanded(
child: FutureBuilder(
future: photosService.getPhotos("flower"),
builder: (BuildContext context, AsyncSnapshot<dynamic> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return const Center(
child: LinearProgressIndicator(),
);
}
return _buildList(context, snapshot);
},
),
);
}
}
Widget photoCard(String url) {
return Card(
child: Image(
image: NetworkImage(url),
),
);
}

Flutter FutureBuilder snapshot returns Instance of 'Object' instead of data

i am new to flutter and trying to display data from a http post
referencing from [1]https://flutter.dev/docs/cookbook/networking/background-parsing and [2]https://flutter.dev/docs/cookbook/networking/fetch-data
i tried to display data on a futurebuilder but it keeps displaying this from the Text('${snapshot.data}')
[Instance of 'DashBoardBanner', Instance of 'DashBoardBanner', Instance of 'DashBoardBanner']
Builder
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
late Future<List<DashBoardBanner>> futureBanner;
#override
void initState() {
super.initState();
futureBanner = getBannerDataFromServer();
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: ListView(
children: [
Card(
child: FutureBuilder(
future: getBannerDataFromServer(),
builder: (context,snapshot){
if(snapshot.connectionState == ConnectionState.done){
if (snapshot.hasData) {
return Text('${snapshot.data}');
} else if (snapshot.hasError) {
return Text('${snapshot.error}');
}
}
return const CircularProgressIndicator();
},
),
)
],
)),
);
}
}
Class and postreq
class DashBoardBanner {
final String MsgId;
final String MsgKey;
final String MsgPic;
const DashBoardBanner(
{required this.MsgId, required this.MsgKey, required this.MsgPic});
factory DashBoardBanner.fromJson(Map<String, dynamic> json) {
return DashBoardBanner(
MsgId: json['MsgId'] as String,
MsgKey: json['MsgKey'] as String,
MsgPic: json['MsgPic'] as String,
);
}
}
Future<List<DashBoardBanner>> getBannerDataFromServer() async {
final queryParameters = {
"ApiFunc": 'Banner',
"UserKey": getDeviceKey(),
"Token": getDeviceToken(),
"SubmitContent": json.encode({"MobileNo": getMobileNo1()})
};
final response = await http.post(
Uri.http('somesite.net', '/capi.aspx', queryParameters),
);
if (response.statusCode == 200) {
Map<String, dynamic> data = jsonDecode(response.body);
final splitoff = jsonEncode(data['RespContent']);
return compute(parseBanner, splitoff);
} else {
throw Exception('Failed to load Data');
}
}
List<DashBoardBanner> parseBanner(String responseBody) {
final parsed = jsonDecode(responseBody).cast<Map<String, dynamic>>();
return parsed
.map<DashBoardBanner>((json) => DashBoardBanner.fromJson(json))
.toList();
}
Edit : i rebuilt the file replicating reference[1] and it finally displayed the data i needed, it seems the issue stem from not having this 2nd widget which return the obj back , however how do i combine the 2nd build widget into the first without needing the whole widget as having a whole build widget to return 1 line seems pointless?
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return Scaffold(
body:Container(
child: FutureBuilder<List<DashBoardBanner>>(
future: getBannerDataFromServer(http.Client()),
builder: (context, snapshot) {
if (snapshot.hasError) {
return const Center(
child: Text('An error has occurred!'),
);
} else if (snapshot.hasData) {
print(snapshot.data!.length);
return DashBoardBannersList(dashboardBanners: snapshot.data!); <--- original issue due to not having this
} else {
return CircularProgressIndicator();
}
},
),
),
);
}
}
class DashBoardBannersList extends StatelessWidget {
const DashBoardBannersList({Key? key, required this.dashboardBanners}) : super(key: key);
final List<DashBoardBanner> dashboardBanners;
#override
Widget build(BuildContext context) {
return Text(dashboardBanners[0].MsgId);
}
}
This error is caused because of the sound null safety
snapshot.data might be null for some requests so you can't access the array at a certain index cause it can be null.
If you know for sure snapshot.data exists you can use the ! operator to tell dart the variable is not null for sure like that:
snapshot.data![index];
You can also check if the data is null before accessing it like that:
if (snapshot.data != null) {
// do something with snapshot.data[index]
}
I recommed to read more about sound null safety here
Check the Firestore docs.
Inside snapshot.data, there's docs (every document of your collection).
The code is from there:
#override
Widget build(BuildContext context) {
return StreamBuilder<QuerySnapshot>(
stream: _usersStream,
builder: (BuildContext context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasError) {
return Text('Something went wrong');
}
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("Loading");
}
return ListView(
children: snapshot.data!.docs.map((DocumentSnapshot document) {
Map<String, dynamic> data = document.data()! as Map<String, dynamic>;
return ListTile(
title: Text(data['full_name']),
subtitle: Text(data['company']),
);
}).toList(),
);
},
);
}
The code above shows how to convert every doc (type DocumentSnapshot) to a JSON format (that can be represented with Map<String, dynamic>). To access to the doc id, you'll access with document.id, because it isn't inside the document.data() method.
You wanna retrieve a list of DashBoardBanner but you forget initialize the futurebuilder by adding a ListView.builder().
Try to use the following code idea :
FutureBuilder(
future: getBannerDataFromServer(http.Client()),
builder: (context, AsyncSnapshot snapshot) {
print(snapshot.hasData);
if (snapshot.hasError) {
return CircularProgressIndicator();
} else if (snapshot.hasData) {
return Expanded(
child: ListView.builder(
scrollDirection: Axis.vertical,
itemCount: snapshot.data!.length,
itemBuilder: (BuildContext context, int index) {
var data = snapshot.data![index];
return DashBoardBannersList(dashboardBanners: data);
},),
),},
},)