how to migrate snapshot.data.data(); to null safety - flutter

this is the code can any help me how to change this to null safety, I am getting errors in .data(). i changed my project to null-safety, and then i am facing this issue
this is the error
The method 'data' can't be unconditionally invoked because the receiver can be 'null'.
Try making the call conditional (using '?.') or adding a null check to the target ('!').
Map<String, dynamic>? documentData = snapshot.data.data();
class HomeSlider extends StatefulWidget {
final String? doc_id;
HomeSlider({this.doc_id});
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<HomeSlider> {
FirebaseServices _firebaseServices = FirebaseServices();
int activeIndex = 1;
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
// height: 200,
child: FutureBuilder(
future: _firebaseServices.sliderRef
.doc(widget.doc_id == null ? "Slider" : widget.doc_id)
.get(),
builder: (context, snapshot) {
if (snapshot.hasError) {
return Center(
child: Text("Error: ${snapshot.error}"),
);
}
if (snapshot.connectionState == ConnectionState.done) {
// the error is here in data()
// Firebase Document Data Map
Map<String, dynamic> documentData = snapshot.data.data();
List? imageList = documentData['images'];
List? suid = documentData['suid'];
return SliderBody(
imageList: imageList,
suid: suid,
);
}
return Center(
child: CircularProgressIndicator(),
);
}));
}}

you should go with
Map<String, dynamic>? documentData = snapshot!.data.data();

The problem was in builder: (context, snapshot) { after adding the AsyncSnapshot and finally like this. builder: (context, AsyncSnapshot snapshot) { and also add ! before .data()
class HomeSlider extends StatefulWidget {
final String? doc_id;
HomeSlider({this.doc_id});
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<HomeSlider> {
FirebaseServices _firebaseServices = FirebaseServices();
int activeIndex = 1;
#override
Widget build(BuildContext context) {
super.build(context);
return Container(
// height: 200,
child: FutureBuilder(
future: _firebaseServices.sliderRef
.doc(widget.doc_id == null ? "Slider" : widget.doc_id)
.get(),
builder: (context,AsyncSnapshot snapshot) {
if (snapshot.hasError) {
return Center(
child: Text("Error: ${snapshot.error}"),
);
}
if (snapshot.connectionState == ConnectionState.done) {
// the error is here in data()
// Firebase Document Data Map
Map<String, dynamic> documentData = snapshot.data!.data();
List? imageList = documentData['images'];
List? suid = documentData['suid'];
return SliderBody(
imageList: imageList,
suid: suid,
);
}
return Center(
child: CircularProgressIndicator(),
);
}));
}}

Related

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?

snapshot.data returing null in Flutter. Using in Translator package

I am using translator package for automatically translate the String value in the Text widget, but my codes are not working.. just returning null.
Codes:
Future<String> translate(String input, String to) async {
final translator = GoogleTranslator();
var translation = await translator.translate(input, from: 'en', to: to);
return translation.text;
}
class TranslatedText extends StatefulWidget {
String data;
String to;
TranslatedText({Key? key, required this.data, required this.to}) : super(key: key);
#override
State<TranslatedText> createState() => _TranslatedTextState();
}
class _TranslatedTextState extends State<TranslatedText> {
#override
Widget build(BuildContext context) {
return FutureBuilder(
future: MainController().translate(widget.data, widget.to),
builder: (BuildContext c, AsyncSnapshot<String> snapshot) {
return Text('${snapshot.data}');
});
}
}
TranslatedText(data: "Egg", to: "es"),
Welcome to StackOverflow!
It's a common behavior for FutureBuilder. It resolves in the future and returns a response (snapshot) after a while, so you need to check the snapshot first and structure your code accordingly. Like:
FutureBuilder(
future: MainController().translate(widget.data, widget.to),
builder: (BuildContext c, AsyncSnapshot<String> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Center(
child: CircularProgressIndicator(),
);
} else {
if (snapshot.hasData) {
var result = snapshot.data as String;
return Text(result);
} else if (snapshot.hasError) {
return Text('An error occurred');
} else {
return Text('No data returned from API');
}
}
});
FutureBuilder doc

The argument type 'Future<ListView>' can't be assigned to the parameter type 'Widget?'

I'm trying to create a ListView with some data received from Firebase, but I keep getting this message. I have tried with FutureBuilder but nothing was useful
The argument type 'Future' can't be assigned to the parameter type 'Widget?'
Code is here:
class CityScreen extends StatelessWidget {
const CityScreen({Key? key}) : super(key: key);
Future<ListView> CreateList() async {
List<CityButton>? listButtons;
final FirebaseAuth auth = FirebaseAuth.instance;
final User? user = auth.currentUser;
final uid = user!.uid;
CollectionReference trips = FirebaseFirestore.instance.collection('events');
QuerySnapshot eventsQuery =
await trips.where("uid", isEqualTo: uid).orderBy('initDate').get();
// ignore: avoid_function_literals_in_foreach_calls
eventsQuery.docs.forEach((element) {
listButtons!.add(CityButton(
element['city'],
DateTime.parse(element['initDate']),
DateTime.parse(element['endDate'])));
});
return ListView(
children: listButtons!,
);
}
#override
Widget build(BuildContext context) {
Future<ListView> lista = CreateList();
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
body: lista,
CreateList() method is a future, use FutureBuilder
#override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: const Color(0xFFF5F5F5),
body: FutureBuilder<ListView>(
future: CreateList(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done &&
snapshot.hasData) {
return snapshot.data!;
} else {
/// you handle others state like error while it will a widget no matter what, you can skip it
return const CircularProgressIndicator();
}
},
),
When you want to render widget after async then use FutureBuilder()
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<ListView>(
future: CreateList(), // async work
builder: (BuildContext context, AsyncSnapshot<ListView> snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError)
return Text('Error: ${snapshot.error}');
else
return snapshot.data;
}
},
),
);
}

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);
},),
),},
},)

StreamBuilder - Bad state: Use multiple StreamBuilder on one screen

Since I use multiple StreamBuilder in my screen I get a Bad state error.
I know that I have to use a StreamController and use it with .broadcast().
Because I dont create the streams by myself I dont know how to change the controller of these streams.
This is my code:
class MyScreen extends StatefulWidget {
#override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [
StreamBuilder<List<int>>(
stream: streamOne?.value,
builder: (c, snapshot) {
final newValueOne = snapshot.data;
return Text(newValueOne);
}),
StreamBuilder<List<int>>(
stream: streamTwo?.value,
builder: (c, snapshot) {
final newValueTwo = snapshot.data;
return Text(newValueTwo);
}),
StreamBuilder<List<int>>(
stream: streamThree?.value,
builder: (c, snapshot) {
final newValueThree = snapshot.data;
return Text(newValueThree);
}),
],
),
),
);
}
}
I tried to have it as BroadcastStreams:
class MyScreen extends StatefulWidget {
#override
_MyScreenState createState() => _MyScreenState();
}
class _MyScreenState extends State<MyScreen> {
#override
Widget build(BuildContext context) {
return SafeArea(
child: Scaffold(
body: Column(
children: [
StreamBuilder<List<int>>(
stream: streamOne?.asBroadcastStream(),
builder: (c, snapshot) {
final newValueOne = snapshot.data;
return Text(newValueOne);
}),
StreamBuilder<List<int>>(
stream: streamTwo?.asBroadcastStream(),
builder: (c, snapshot) {
final newValueTwo = snapshot.data;
return Text(newValueTwo);
}),
StreamBuilder<List<int>>(
stream: streamThree?.asBroadcastStream(),
builder: (c, snapshot) {
final newValueThree = snapshot.data;
return Text(newValueThree);
}),
],
),
),
);
}
}
This didnt work and gave me still a bad state error.
Would be great if somone could help me here.
Thank you very much!
Inside your streamBuilder builder, you have to check that the snapshot has actually received the data, otherwise your Text widget is receiving null, thus, throwing a bad state error:
StreamBuilder<List<int>>(
stream: streamThree.asBroadcastStream(),
builder: (c, snapshot) {
if(snapshot.hasData){
final newValueThree = snapshot.data;
return Text(newValueThree);
} else {
// return any other widget like CircularProgressIndicator
}
}),
You can also check on
snpashot.connectionState == ConnectionState.done
and
snpashot.connectionState == ConnectionState.active
and
snpashot.connectionState == ConnectionState.waiting
Thank you #Arnaud Delubac. I also had to check if the array I get from the stream is not empty:
StreamBuilder<List<int>>(
stream: streamThree.asBroadcastStream(),
builder: (c, snapshot) {
if (snapshot.hasData && snapshot.data.isNotEmpty && snapshot.connectionState == ConnectionState.active) {
final newValueThree = snapshot.data;
return Text(newValueThree);
} else {
// return any other widget like CircularProgressIndicator
}
}),