Home > Software design >  Flutter translate List of strings
Flutter translate List of strings

Time:09-21

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

  }
)
  • Related