I have the code below that I thought would run whenever the given screen is reached, i.e. whenever the user goes to this screen. This screen creates some temporary files for the user. I don't need them after the user leaves the screen, so I wanted to flush them everytime the user reaches this screen. However, the line with await cleanupTempAudioFiles(); doesn't seem to be doing its job.
#override
void initState() {
super.initState();
initialize();
}
void initialize() async {
uid = auth.currentUser;
filesInProgressFileDirString = systemTempDir.path + '/App/AppAudioFiles/FilesInProgress/';
fileInProgressFileDir = await Directory(fileInProgressFileDirString).create(recursive: true);
myRecorder = await FlutterSoundRecorder().openRecorder();
myPlayer = await FlutterSoundPlayer().openPlayer();
controller = AnimationController(
vsync: this,
duration: const Duration(seconds: 10),
)..addListener(() {
setState(() {});
});
controller?.reset();
await cleanupTempAudioFiles(); //This code seems to not be running every time the screen is opened
setState(() {
sendableFileExists = 0;
});
}
Future<void> cleanupTempAudioFiles() async {
final dir = Directory(filesInProgressFileDir.path);
final List<FileSystemEntity> files = await dir.list().toList();
files.forEach((file) async {
if (file.path == filesInProgressFileDir.path + currentAppFilename) {
await file.delete();
}
if (file.path == filesInProgressFileDir.path + currentAppFilename + 'High.mp3') {
await file.delete();
}
if (file.path == filesInProgressFileDir.path + currentAppFilename + 'Low.mp3') {
await file.delete();
}
});
}
The initState() function is called when your object is put into the widget tree. This is not the same as every time it is displayed. Navigator.pop() for example will show the screen without re-inserting the widget into the tree. see https://api.flutter.dev/flutter/widgets/State/initState.html
To run every time the user sees a widget you should put the code into the build function or consider #overriding dispose(), didChangeDependencies() or
didUpdateWidget() instead to get the right part of the widget lifecycle.
Related
I am developing an application in Flutter where I need to implement an image selection function like in instagram.
But there is an issue, my app UI is freezing when trying to get and compress files from user phone gallery.
This is my first experience with flutter isolates, but as far as i know it should work without freezes.
Here is an image for a better understanding of what i want to do.
This is a function that calls getFiles function in isolation.
Here i get paths of user phone gallery files and pass them to another function in order to compress and get files for rendering.
Future fetchImages({ bool fetchMore = false, bool force = false }) async {
if (!fetchMore) {
setState(() => fetched = false);
}
if (force) {
assetsCount = await assetPathEntity!.assetCountAsync;
page = 0;
files.clear();
}
if (assetsCount == 0 || page >= (assetsCount / pageSize)) {
return setState(() => fetched = true);
}
final assetEntities = await assetPathEntity!.getAssetListPaged(page: page++, size: pageSize);
lastCompletedIndex = files.length;
final receivePort = ReceivePort();
final completer = Completer();
getFiles(receivePort.sendPort, assetEntities);
try {
receivePort.listen((filesPaths) {
for (final filePath in filesPaths) {
files.add({
"path": filePath,
"compressedFile": null,
});
}
if (scaledFile == null && files.isNotEmpty) {
scaledFile = File(files.first["path"]);
}
compressAlbumImages();
completer.complete();
setState(() => fetched = true);
}).onError((_) {
compressAlbumImages();
completer.complete();
setState(() => fetched = true);
});
await Future.wait([completer.future]);
} catch (_) { }
finally {
receivePort.close();
}
}
This is getFiles function that runs in isolation
void getFiles(SendPort sendPort, List<AssetEntity> assetEntities) async {
final List<String> filesPaths = [];
for (final assetEntity in assetEntities) {
try {
final file = await assetEntity.file;
if (file != null) {
filesPaths.add(file.path);
}
} catch (_) { }
}
sendPort.send(filesPaths);
}
This is a function that calls compressImages function and adds any value to refresh the list of images
Here i pass the paths and get compressed files for rendering.
void compressAlbumImages() async {
final receivePort = ReceivePort();
final completer = Completer();
compressImages(receivePort.sendPort, files, lastCompletedIndex);
try {
receivePort.listen((compressedFilesWithPath) {
files = compressedFilesWithPath;
fileStreamCt.sink.add(1);
completer.complete();
}).onError((_) {
completer.complete();
});
await Future.wait([completer.future]);
} catch (_) {}
finally {
receivePort.close();
}
return;
}
This is an image compression function that runs in isolation
void compressImages(SendPort sendPort, List<Map<String, dynamic>> files, int startFromIndex) async {
final List<String> filesToBeRemoved = [];
for (int idx = startFromIndex; idx < files.length; idx++) {
final file = files[idx];
try {
final compressedFile = await FlutterNativeImage.compressImage(
file["path"],
quality: 20,
percentage: 20,
targetHeight: 300,
targetWidth: 300,
);
file["compressedFile"] = compressedFile;
} catch (_) {
filesToBeRemoved.add(file["path"]);
}
}
if (filesToBeRemoved.isEmpty) {
return sendPort.send(files);
}
final compressedFilesWithPaths = files
.whereNot((element) => filesToBeRemoved.contains(element["path"]))
.toList();
sendPort.send(compressedFilesWithPaths);
}
And finally i render compressed images
return StreamBuilder(
stream: fileStreamCt.stream,
builder: (ctx, AsyncSnapshot<int> snapshot) {
if (snapshot.connectionState == ConnectionState.waiting) {
return Text("LOADING...");
}
if (snapshot.hasError) {
return Text("AN ERROR OCCURRED");
}
return buildAlbumImages();
}
);
Render like this
Image.file(compressedImage);
If in short - When i'm getting user phone gallery files && compress them, my app UI starts freezing.
I don't know why it's freezing.
I tried the same process but with file.readAsBytes() and to render like Image.memory(compressedFileBytes), but it was useless.
I would be very grateful for any help.
Thanks in advance.
In your code you never actually create an isolate. The 'isolate code' in getFile, compressImages etc simply runs on the main isolate and indeed will block the UI.
Per documentation, you create an isolate with Isolate.spawn and pass only the sendPort. The isolate then must send back a receivePort, and the main thread uses that port to send the data you want to isolate to process (like assetEntities), processes it and sends the results back to the main thread. It's a bit complicated, and requires different function signatures than you have here.
Fortunately, a much easier way to accomplish what you want (still using Isolates that won't block the UI) is to use the compute function from the dart:async package:
Change the signature of your getFiles function to Future<List<String>> getFiles(List<AssetEntity> assetEntities) async and do in it what you need to do, returning the list of filesPaths as you do now. Importantly, getFiles must be a top level or a static function, it cannot be a regular class method. Then, where you need the calculation done you use something like var filesPaths = await compute(getFiles, assetEntities). Now, the getFiles function is called in an isolate, and the return value is given back to you on the main isolate. The nice thing is that now this looks a lot like regular await call, no need for sendPorts etc. You can do the same thing for your other heavy calculation methods.
One (big) constraint with isolates is the type of argument you can pass to and from an isolate, see here. Those same constraints apply here, because under the hood the compute function also uses sendPorts etc.
for my app I've been trying to make a bookmarking feature that will save listings and display them on the bookmark page. I'm currently having two issues with this, firstly that whenever I try to save data, for example, ListingID, I'm able to grab it and store it perfectly fine. But when I try to save another listing, it replaces the old ListingID with the new one. I've tried turning the saved variable into a string and doing something like
await _preferences?.setString(_keybookmarks, savedID + grabbedID);
But the + variable doesn't change anything, not sure if it's because of how I've structured my code or it's not how SharedPreferences work.
And then for displaying the whole listing on the bookmark page, I want to be able to grab all the important data such as ListingID, ListingName, ListingDescription, etc, and save it, perhaps in a map and be able to have multiple of these listings in one table, and hopefully be able to delete/unbookmark the listings based on their index or ListingID.
Thanks for any advice or help!
Here's some of my app's code.
user_simple_preferences.dart
import 'package:shared_preferences/shared_preferences.dart';
class UserSimplePreferences {
static SharedPreferences? _preferences;
static String _keyBookmarks = '';
static Future init() async =>
_preferences = await SharedPreferences.getInstance();
static Future setBookmarks(String grabbedID) async =>
await _preferences?.setString(_keyBookmarks, grabbedID);
static String? getBookmarks() => _preferences?.getString(_keyBookmarks);
}
listingDetail.dart
//Triggers on button press
bool isBookmarked = false;
void toggleBookmark() {
setState(() async {
if (isBookmarked) {
isBookmarked = false;
print('IsFalse');
} else {
isBookmarked = true;
print('isTrue');
String grabbedID = widget.ListingID.toString();
await UserSimplePreferences.setBookmarks(grabbedID);
Navigator.push(
context,
MaterialPageRoute(
builder: (context) =>
bookmarkPage(bookmarkedID: widget.ListingID),
));
}
});
}
//My button for bookmarks
FavoriteButton(
iconDisabledColor: Colors.grey,
iconSize: 50,
isFavorite: isBookmarked,
valueChanged: (_) {
toggleBookmark();
},
),
bookmarks.dart
class bookmarkPage extends StatefulWidget {
final int bookmarkedID;
const bookmarkPage({
Key? key,
required this.bookmarkedID,
}) : super(key: key);
//rest of code...
//This grabs the saved bookmark and works on app restart.
Text(UserSimplePreferences.getBookmarks() ?? ''),
I want to execute some task (e.g. fetch data from server) in background even app is closed in Flutter App.
So How can I achive this?
Its' better If anyone provide example for that.
I am trying using android_alarm_manager but facing below issue:
I have cloned ahttps://github.com/jsoref/flutter-plugins/tree/master/packages/android_alarm_manager/example
Modified code as below:
void printPeriodic() => printMessage("Periodic!");
void printonDelayed() async {
int i = 0;
while(i < 50) {
printMessage("printonDelayed:" + i.toString());
await sleep1();
i++;
}
Future<String> sleep1() {
return new Future.delayed(const Duration(seconds: 1), () => "1");
}
await AndroidAlarmManager.oneShot(
const Duration(seconds: 1), oneShotID, printOneShot, exact: true);
await AndroidAlarmManager.oneShot(
const Duration(seconds: 1), 2, printonDelayed, wakeup: true, exact: true);
Periodic! is printing even if I close app.
printonDelayed is not printing if I close app
Android Version: 8.1.0
I update a timestamp in a Firebase server every time I click a button on my react-native app (android).
The problem is that, after updating it, I want to read it and set my state with this timestamp.
Here is some code I found on the Internet, it updates the date on Firebase, reach the first 'then' block but then never go on, so never reach the second 'then' block and never show the alert(onlyDate);
I have no error about rules on Firebase, read and write are both available.
getDateServerFirebase = () => {
let fb_currentTime = firebase.database().ref('currentTime/');
fb_currentTime.update({ time: firebase.database.ServerValue.TIMESTAMP })
.then(() => {
fb_currentTime.once('value').then(function (data) {
//console.log(data);
let timestamp = data._value.time;
let fullDate = new Date(timestamp);
let onlyDate = fullDate.getDate() + '/' + (fullDate.getMonth()+1) + '/' + fullDate.getFullYear();
alert(onlyDate);
}, function serverTimeErr(err) {
console.log('Could not reach to the server time !');
});
}, function (err) {
console.log ('set time error:', err)
});
}
I tried this and still the same problem: writing works, reading not.
setData = () =>{
firebase.database().ref('prova/').set({
name:'luis',
surname:'rew'
}, function(error) {
if (error) {
// The write failed...
} else {
// Data saved successfully!
}
});
}
readData = () =>{
firebase.database().ref('prova/')
.once('value')
.then((data)=>{
alert(data);
})
}
Can someone help me??
The way you access response from firebase function is through running the val(); function on the result.
change this line
let timestamp = data._value.time;
into this:
let timestamp = data.val().time;
I intend to get users geolocation even when the app sits dormant in the background and store the same in the database.
I'm using katzer's Cordova Background Plug-in,
When I try to access navigator.geolocation.getCurrentPosition inside backgroundMode.onactivate function, nothing happens, Whereas when I try passing hard coded values api is called, data is stored in database.
following is my code
document.addEventListener('deviceready', function() {
// Android customization
cordova.plugins.backgroundMode.setDefaults({
text: 'Doing heavy tasks.'
});
// Enable background mode
cordova.plugins.backgroundMode.enable();
// Called when background mode has been activated
cordova.plugins.backgroundMode.onactivate = function() {
console.log('inside background')
a();
}
var a = function() {
console.log('a called')
navigator.geolocation.getCurrentPosition(function(pos) {
console.log('inside navigate');
var data = {
Lati: '123456',
Longi: '132456',
//LoginID: JSON.parse(window.localStorage.getItem('LoginId'))
EmpCode: localStorage.getItem('LoginId')
};
$http.post("https://app.sbismart.com/bo/ContactManagerApi/UpdateEmployeeLoc", data).success(function(rsdata, status) {
console.log('inside rsdata');
console.log(data.Lati + "," + data.Longi);
})
}, function(error) {
alert('Unable to get location: ' + error.message);
});
}
}, false);
cordova.plugins.backgroundMode.onfailure = function(errorCode) {
console.log(errorCode)
};`
and check as to why is it failing....then again u need to run the locationService function in a timeout function in the background to get updated about the location and check the location from previously got location.
Something like this...
cordova.plugins.backgroundMode.onactivate = function () {
setTimeout(function () {
a();
}, 5000);
}
Hope this helps.