I need help, i tried to translate a list of strings in flutter with translator package, but it keep displaying _instance of 'Future<translation>'
this is my code:
ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
translated((docs[index]['tweet']));
return ListTile(
title: Text(
docs[index]['name'],
style:
TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
),
subtitle: Text(
translator.translate(docs[index]['tweet']).toString()),
);
},
);
i tried to use async
and await
with setState()
in a sperated method like this:
String output = "";
Future<void> translated(String post) async {
final Translation translation = await translator.translate(post, to: 'en');
final String out = translation.toString();
print(out);
setState(() {
output = out;
});
}
ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
translated((docs[index]['tweet']));
return ListTile(
title: Text(
docs[index]['name'],
style:
TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
),
subtitle: Text(output),
);
},
);
It does translate but the value keep on changing infinitly while displaying . this is the full body:
body: Container(
child: StreamBuilder(
stream: FirebaseFirestore.instance
.collection('favorite')
.doc(userID)
.collection('Publication')
.snapshots(),
builder: (context, AsyncSnapshot<QuerySnapshot> snapshot) {
if (snapshot.hasData) {
List<dynamic> docs = snapshot.data.docs;
return FutureBuilder<List<String>>(
future: translated(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<String> data = snapshot.data ?? [];
return ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
docs[index]['name'],
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold),
),
subtitle: Text(data[index]),
);
},
);
}
}
},
);
} else {
return Center(
child: CircularProgressIndicator(),
);
}
},
)),
- List item
CodePudding user response:
When you call a function in the build method of a widget, this function gets called infinitely, as long as the widget appears in the tree. No matter if the operation is asynchronous or not, you should call it in your initState() method - official initState docs. If the action is async, you should make sure the widget that uses the data from the operation is called AFTER the operation is completed. This can be made in a variety of ways - for example: create a variable: bool translationIsFinished = false;
, then, at the bottom of your translate function, call setState(()=>translationIsFinished = true);
. And in your build method where your List Widget lies, use this:if(translationIsFinished)...[// your widget here]
Or alternatively, you can use the ternary operator like this:translationIsFinished ? YourWidget() : Container()
CodePudding user response:
When you call translated
inside build method
, and call setState
inside translated
, means that you are calling setState
inside build method
, so you are rebuilding you widget again and again. Change you translated
to this:
Future<String> translated(String post) async {
final Translation translation = await translator.translate(post, to: 'en');
final String out = translation.toString();
print(out);
return out;
}
and use it like this:
ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
String output = await translated((docs[index]['tweet']));
return ListTile(
title: Text(
docs[index]['name'],
style:
TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
),
subtitle: Text(output),
);
},
);
I recommended that you fallow this approach:
FutureBuilder<List<String>>(
future: translated(),
builder: (context, snapshot) {
switch (snapshot.connectionState) {
case ConnectionState.waiting:
return Text('Loading....');
default:
if (snapshot.hasError) {
return Text('Error: ${snapshot.error}');
} else {
List<String> data = snapshot.data ?? [];
return ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
return ListTile(
title: Text(
docs[index]['name'],
style: TextStyle(
fontSize: 15.0,
fontWeight: FontWeight.bold),
),
subtitle: Text(data[index]),
);
},
);
}
}
},
)
and change your translated
to this:
Future<List<String>> translated() async{
List<String> result = [];
for (var element in docs) {
String output = await translated((element['tweet']));
result.add(output);
}
return result;
}
CodePudding user response:
In my opinion, it's better to translate the fetched posts before showing them so you can do it like this:
Future<List<Map<String, dynamic>>> translatePosts(List<Map<String, dynamic>> docs) async {
for (var doc in docs) {
final post = doc['tweet'];
final Translation translation = await translator.translate(post, to: 'en');
doc['tweet'] = translation.toString();
}
return docs;
}
Then, to show that you can use the FutureBuilder:
FutureBuilder(
future: translatePosts,
builder: (BuildContext context, AsyncSnapshot<List<Map<String, dynamic>>> snapshot) {
if (snapshot.hasData) {
return ListView.builder(
itemCount: docs.length,
itemBuilder: (context, index) {
translated((snapshot[index]['tweet']));
return ListTile(
title: Text(
snapshot[index]['name'],
style:
TextStyle(fontSize: 15.0, fontWeight: FontWeight.bold),
),
subtitle: Text(output),
);
},
);
}
}
)