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

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

Related

Error: A value of type 'Future<ListView>' can't be returned from a function with return type 'Widget'

I'm tryng to read data from Cloud Firestore (working) and put the data in a ListView containing a graphic widget (Order) that shows price, date and product, but when I'm trying to return the data from the method this error shows up.
The target is to return the data of the user passed to the db_utility constructor.
import 'package:flutter/material.dart';
import 'package:cloud_firestore/cloud_firestore.dart';
import 'order.dart';
class db_utility extends StatelessWidget {
final String userID;
const db_utility(this.userID, {Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
CollectionReference users = FirebaseFirestore.instance.collection('users');
return FutureBuilder(
future: users.doc(userID).get(),
builder:
(BuildContext context, AsyncSnapshot<DocumentSnapshot> snapshot) {
if (snapshot.hasError) {
return const Text("error");
} else if (snapshot.hasData && !snapshot.data!.exists) {
return const Text("no data found");
} else if (snapshot.connectionState == ConnectionState.done) {
Map<String, dynamic> data =
snapshot.data!.data() as Map<String, dynamic>;
return getListOfOrders();
}
return const Center(
child: CircularProgressIndicator(
color: Colors.red,
),
);
},
);
}
And this is the method that returns the ListView:
Future<ListView> getListOfOrders() async {
QuerySnapshot snapshot = await FirebaseFirestore.instance
.collection('completed_orders')
.where('user', isEqualTo: userID)
.orderBy('timestamp')
.get();
final data = snapshot.docs.map((doc) => doc.data()).toList();
List<Order> orders = <Order>[];
for (var o in data) {
orders.add(Order((o as Map)['price'], 'date of today', o['product']));
}
return ListView(children: orders,);
}
}
getListOfOrders() is another future method, you can use nested FutureBuilder .
In that case, instead of return getListOfOrders(); use another FutureBuilder like previous one. But you can do the operation on single future method.
You can also check multiple method on a future builder

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

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

How to get result of FutureBuilder from the parent FutureBuilder?

The entry point is _processError function. I expect to get a widdet there. And this _processError runs from a parent FutureBuilder.
Then another Future builder should be executed, at least I think it should... But it seems there is no result from there. Whats wrong with it?
FutureBuilder<List<ShortLetter>>(
future: fetchMessages(),
builder: (BuildContext context, AsyncSnapshot<List<ShortLetter>> snapshot) {
...
} else if (snapshot.hasError) {
return _processError(snapshot, context); // I want to get a widget when an error happens
...
},
);
Future<bool> checkConnection() async {
debugPrint('---checkConnection---');
var connectivityResult = await (Connectivity().checkConnectivity());
...
// and returs true or false
}
Widget _processError(AsyncSnapshot snapshot, BuildContext context) {
var errorType = snapshot.error.runtimeType;
debugPrint('AllMessagesView, snapshot error: $errorType');
debugPrint(snapshot.error.toString());
if (errorType == TimeoutException) {
debugPrint('0000000000000000');
//////////////////////////////////////////////////////
// there is any output in console from the FutureBuilder below
// but checkConnection() was executed
FutureBuilder<bool>(
future: checkConnection(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData) {
debugPrint('11111111111111 snapshot data: ${snapshot.data}');
if (snapshot.data == true) {
...
}
...
} else if (snapshot.hasError) {
debugPrint('2222222222222');
...
} else {
debugPrint('Error. This should not happen.');
...
}
},
);
...
}
...
}
here is a sample console output and any result from the second FutureBuilder
I/flutter (10556): AllMessagesView, snapshot error: TimeoutException
I/flutter (10556): TimeoutException after 0:00:10.000000: Future not completed
I/flutter (10556): 0000000000000000
I/flutter (10556): ---checkConnection---
Parent FutureBuilder is already been processed, I think we don't need to pass Async data.
This demo widget may help.
class MyApp extends StatefulWidget {
const MyApp({Key? key}) : super(key: key);
#override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
Future<int> parentF() async {
return await Future.delayed(Duration(seconds: 2), () => 4);
}
Future<String> childF(int sec) async {
return await Future.delayed(Duration(seconds: sec), () => "got the child");
}
Widget childFB(final data) {
print(data.runtimeType);
return FutureBuilder(
future: childF(4),
builder: (context, snapshot) => snapshot.hasData
? Text("${snapshot.data!} parent data: $data ")
: const Text("loading second child"));
}
#override
Widget build(BuildContext context) {
return Scaffold(
body: Column(
children: [
FutureBuilder(
future: parentF(),
builder: (context, parentSnapshot) {
return parentSnapshot.hasData
? FutureBuilder<String>(
future: childF(3),
builder: (context, snapshot) {
return snapshot.hasData
? Text(
"${snapshot.data!} parent data: ${parentSnapshot.data} ")
: const Text("loading child");
},
)
: const Text("loading parent");
},
),
FutureBuilder(
future: parentF(),
builder: (context, parentSnapshot) {
return parentSnapshot.hasData
? childFB(parentSnapshot
.data) // it already have normal data, not async
: const Text("loading parent");
},
),
],
));
}
}

How to return boolean based on firestore value

I have a boolean value in firestore, i need to set a condition based on this boolean. I am trying to get this boolean from firestore and then using it in my futurebuilder but i am always getting null even if i can see values are there in firestore. Please help.
Future<bool> getUser() async {
dynamic data;
bool isUser=false;
final DocumentReference document =
FirebaseFirestore.instance.collection('users').doc(uida).collection('pre').doc();
isUser = await document.get().then<dynamic>(( DocumentSnapshot snapshot) async{
data =snapshot.data;
final data1 = data.map((doc) => doc['enrolled']);
print(data1.toString());
if (data1==true){
setState(() {
isUser = true;
});}
});
return isUser;
}
Widget
return FutureBuilder(
future: getUser(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.data == true)
return Text(snapshot.data.toString());
When using asynchronous code, it is better to either use async and await or use then. In your case async and await will give a more readable code.
For your getUser function this would be something like:
Future<bool> getUser() async {
dynamic data;
bool isUser=false;
final DocumentReference document =
FirebaseFirestore.instance.collection('users').doc(uida).collection('pre').doc();
isUser = await document.get()
data = snapshot.data;
final data1 = data['enrolled'];
print(data1.toString());
if (data1==true){
setState(() {
isUser = true;
});}
});
return data1;
}
And for the Futurebuilder, it is better to check whether the snapshot contains data, by using snapshot.hasData like this:
return FutureBuilder(
future: getUser(),
builder: (BuildContext context, AsyncSnapshot<bool> snapshot) {
if (snapshot.hasData)
if (snapshot.data ==true){
return Text(snapshot.data.toString());
} else {
return Text('still waiting for data...')
See https://api.flutter.dev/flutter/widgets/FutureBuilder-class.html for a much better explanation.
You can use the code below it will solve your issue, but check two things do you have all required permissions to fetch data from firebase and also check if the document id is correct ? you can use use if(snapshot.data!.exists) to check if the document exist in the current collection
import 'package:cloud_firestore/cloud_firestore.dart';
import 'package:firebase_auth/firebase_auth.dart';
import 'package:flutter/material.dart';
class TestClass extends StatefulWidget {
const TestClass({Key? key}) : super(key: key);
#override
_TestClassState createState() => _TestClassState();
}
class _TestClassState extends State<TestClass> {
final _auth = FirebaseAuth.instance;
final FirebaseFirestore _fireStore = FirebaseFirestore.instance;
#override
Widget build(BuildContext context) {
return Scaffold(
body: FutureBuilder<DocumentSnapshot>(
future: _fireStore
.collection('users').doc(uida).collection('pre').doc(),
builder: (context, snapshot) {
if (snapshot.hasData) {
return Center(child: Text(snapshot.data!['enrolled'].toString()));
} else {
return Center(
child: CircularProgressIndicator(),
);
}
}),
);
}
}

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