问题
I am writing a very basic flutter app for reading public domain books. I included a .txt file containing a book in my app's assets. since a book is quite long and would take time to load I was trying to use a FutureBuilder that will display a circular progress indicator while the book is loading, however, when I click on the button to open the book the app freezes until the book is loaded instead of transitioning to the book page and showing a progress indicator while the book is loading as I would like.
I checked this for a smaller file and it didn't freeze. I tried to just tell the FutureBuilder to show the progress indicator and again, it didn't freeze.
FutureBuilder(
future: text, //Future<String>
builder: (context,snapshot) {
if (snapshot.connectionState==ConnectionState.done) {
return Text(
snapshot.data,
style: TextStyle(fontSize: 20),);
}
else {
return CircularProgressIndicator();
}
},
)
It looks like the FutureBuilder is just trying to build with the text instead of building without it and adding it later like it is supposed to do. How do I tell it to do that?
回答1:
Dart is mostly single-threaded. So when you're reading the text, it's doing it in the same thread as the UI and that's why it's slowing it down. Using a future means that the call may be delegated to a later time (depending on how you schedule it), but it is still running in the same thread.
What you want to do is use an Isolate, and do the file reading within that. Once you have the file (and do any processing you need to do), you should be able to pass it back to the Text class and it should be faster - although if you're dealing with a very large amount of text it may still stutter a bit as the Text class still has to process all the text. If it does stutter, I'd recommend breaking the text into parts (chapter/paragraph?) and using multiple text and/or richtext objects to display it in a list.
The easiest way to use an Isolate in flutter is the `compute' function.
Then your future will have something like this in it:
await compute(readFile, <path to file>);
Note that the input and output of compute
have some limitations since it uses an Isolate's SendPort under the hood:
The content of message can be: primitive values (null, num, bool, double, String), instances of SendPort, and lists and maps whose elements are any of these. List and maps are also allowed to be cyclic.
回答2:
I think the problem is here:
if (snapshot.connectionState == ConnectionState.done) {
return Text(snapshot.data, style: TextStyle(fontSize: 20));
}
The ConnectionState.done is set just after the stream is connected (I assume you're using a stream) so that condition is always true. I think is better if you use the snapshot.hasData as validation like so:
FutureBuilder<String>(
future: futureStream,
builder: (context, snapshot) {
if (snapshot.hasData) {
return Text(snapshot.data);
} else {
return CircularProgressIndicator();
}
},
);
来源:https://stackoverflow.com/questions/56484082/futurebuilder-makes-my-app-freeze-because-it-waits-for-a-file-to-load-before-bui