What is the "best practice" way to implement a background downloading of a file while displaying the CircularProgressIndicator()
and using the Navigator.pushNamedAndRemoveUntil
afterwards on successful file loading?
This is a sample of my code:
class Downloader extends StatefulWidget {
const Downloader({Key? key}) : super(key: key);
@override
DownloaderState createState() => DownloaderState();
}
class DownloaderState extends State<Downloader> {
Future<Directory?>? _appDocumentsDirectory;
bool _isDownloaded = false;
@override
void initState() {
super.initState();
_initDownloader();
}
_initDownloader() async {
setState(() {
_appDocumentsDirectory = getApplicationDocumentsDirectory();
});
}
Future<void> _downloadJson(String localPath) async {
if ( === WE CHECK FOR CREDENTIALS HERE === ) {
FTPConnect ftpConnect = FTPConnect(
BaseAPI.ftpServerAddress,
port: BaseAPI.ftpServerPort,
user: _login,
pass: _passwd);
try {
String fileName = 'filename.json';
String localFileName = localPath "/filename.json";
print(" localFileName: $localFileName");
await ftpConnect.connect();
await ftpConnect.changeDirectory('ftp');
bool res = await ftpConnect.downloadFileWithRetry(
fileName, File(localFileName));
await ftpConnect.disconnect();
print("is downloaded? $res");
} catch (e) {
print("=== ERROR ===\n${e.toString()}");
}
_isDownloaded = true;
} else {
_isDownloaded = false;
}
}
Widget _buildDirectory(
BuildContext context, AsyncSnapshot<Directory?> snapshot) {
Text text = const Text('');
if (snapshot.connectionState == ConnectionState.done) {
if (snapshot.hasError) {
text = Text('Error: ${snapshot.error}');
} else if (snapshot.hasData) {
String _s = snapshot.data!.path "/filename.json";
String _z = "File downloaded";
_downloadJson(snapshot.data!.path); ////////////// <= downloading here
if (!_isDownloaded) {
_z = "Failed to download file";
}
text = Text('Path to a file: \n$_s\n\npath: \n${snapshot.data!.path}\n\nFile downloaded: $_isDownloaded');
} else {
text = const Text('path unavailable');
}
}
return text;
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text("Loading data"),
foregroundColor: Colors.black,
backgroundColor: Colors.redAccent,
),
body: Center(
child: Column(
children: <Widget>[
FutureBuilder<Directory?>(
future: _appDocumentsDirectory,
builder: _buildDirectory,
/// I need to redirect to another page here
),
],
),
),
);
}
}
So my main problems are:
- How to properly 'catch' the moment when file is downloaded? I can only see in console (Android Studio) when "is downloaded? true" is printed but I fail to understand how to catch this moment and link some actions to it...
The workflow, as far as I understand, is like that:
1. initialize the `_appDocumentsDirectory`;
2. on initialization start the `FutureBuilder` logic:
2.1 - checking if `_appDocumentsDirectory` has data and
2.2 - calling the `_downloadJson`
Can (or should) I wrap the logic inside the
_downloadJson
function in the FutureBuilder too?If I will resolve somehow first and second questions - how do you usually perform the required
Navigator.pushNamedAndRemoveUntil()
when needed?
CodePudding user response:
First to answer the following question,
How to display a CircularProgressIndicator while the file is downloading, you can simply introduce a variable isDownloading
like below,
bool isDownloading = false;
And while downloading i.e.,
setState(() => isDownloading = true);
bool res = await ftpConnect.downloadFileWithRetry(
fileName, File(localFileName));
setState(() => isDownloading = false);
and since setState once called will rebuild the body, in the body use something like this,
Scaffold(
body: isDownloading ? CircularProgressIndicator() : ..[your code],
);
Next to get the local push notification you can simple use the flutter_local_notifications: ^9.5.3 1
This should definitely resolve the issue