Picking Geolocation every after certain time interval - dart

Am new to Dart-flutter, and I need to write an app that can pick user location in the background after say an hour.
I have tried to work things around the geolocation plugin and Timer class, which worked fine but only picks the location once.
I need to know if there is a service workaround or the best way to go about this.
thank you
class HomePage extends StatefulWidget{
static const timeout = const Duration(seconds: 5);
#override
State createState() => new HomePageState();
HomePage(){
startTimeout();
}
startTimeout()async {
final GeolocationResult result = await
Geolocation.isLocationOperational();
if(result.isSuccessful) {
return new Timer(timeout, handleTimeout);
}
else {
debugPrint(result.error.toString());
GeolocationResult res = await
Geolocation.requestLocationPermission(const LocationPermission(
android: LocationPermissionAndroid.fine,
ios: LocationPermissionIOS.always,
));
if(res.isSuccessful) {
new Timer.periodic(timeout,(Timer t)=> handleTimeout);
} else {
debugPrint(res.error.toString());
}
}
}
void handleTimeout(){
debugPrint("Print after 10 seconds");
Geolocation.currentLocation(accuracy:
LocationAccuracy.best).listen((result) {
if(result.isSuccessful) {
var posts = new postservice.Post();
var userid=100;
posts.sendLocation(result.location.latitude,result.location.longitude,userid);
}
});
}
}

Related

Is there a way to load async data on InitState method?

I'm a looking for a way to load async data on InitState method, I need some data before build method runs. I'm using a GoogleAuth code, and I need to execute build method 'till a Stream runs.
My initState method is:
#override
void initState () {
super.initState();
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
setState(() {
_currentUser = account;
});
});
_googleSignIn.signInSilently();
}
I will appreciate any feedback.
You can create an async method and call it inside your initState
#override
void initState () {
super.initState();
WidgetsBinding.instance.addPostFrameCallback((_){
_asyncMethod();
});
}
_asyncMethod() async {
_googleSignIn.onCurrentUserChanged.listen((GoogleSignInAccount account) {
setState(() {
_currentUser = account;
});
});
_googleSignIn.signInSilently();
}
As of now using .then notation seems to work:
// ...
#override
initState() {
super.initState();
myAsyncFunction
// as suggested in the comment
// .whenComplete() {
// or
.then((result) {
print("result: $result");
setState(() {});
});
}
//...
Method 1 : You can use StreamBuilder to do this. This will run the builder method whenever the data in stream changes.
Below is a code snippet from one of my sample projects:
StreamBuilder<List<Content>> _getContentsList(BuildContext context) {
final BlocProvider blocProvider = BlocProvider.of(context);
int page = 1;
return StreamBuilder<List<Content>>(
stream: blocProvider.contentBloc.contents,
initialData: [],
builder: (context, snapshot) {
if (snapshot.data.isNotEmpty) {
return ListView.builder(itemBuilder: (context, index) {
if (index < snapshot.data.length) {
return ContentBox(content: snapshot.data.elementAt(index));
} else if (index / 5 == page) {
page++;
blocProvider.contentBloc.index.add(index);
}
});
} else {
return Center(
child: CircularProgressIndicator(),
);
}
});
}
In the above code StreamBuilder listens for any change in contents, initially its an empty array and shows the CircularProgressIndicator. Once I make API call the data fetched is added to contents array, which will run the builder method.
When the user scrolls down, more content is fetched and added to contents array which will again run builder method.
In your case only initial loading will be required. But this provides you an option to display something else on the screen till the data is fetched.
Hope this is helpful.
EDIT:
In your case I am guessing it will look something like shown below:
StreamBuilder<List<Content>>(
stream: account, // stream data to listen for change
builder: (context, snapshot) {
if(account != null) {
return _googleSignIn.signInSilently();
} else {
// show loader or animation
}
});
Method 2: Another method would be to create an async method and call it from you initState() method like shown below:
#override
void initState() {
super.initState();
asyncMethod();
}
void asyncMethod() async {
await asyncCall1();
await asyncCall2();
// ....
}
Create anonymous function inside initState like this:
#override
void initState() {
super.initState();
// Create anonymous function:
() async {
await _performYourTask();
setState(() {
// Update your UI with the desired changes.
});
} ();
}
#override
void initState() {
super.initState();
asyncInitState(); // async is not allowed on initState() directly
}
void asyncInitState() async {
await yourAsyncCalls();
}
Previous Answer!!
You can set a Boolean value like loaded and set it to true in your listen function and make your build function return your data when loaded is set to true otherwise just throw a CircularProgressIndicator
Edited --
I would not suggest calling setState in a method you call in initState. If the widget is not mounted while the setState is called (as the async operation completes) an error will be reported. I suggest you use a package after_layout
Take a look at this answer for better understanding setState in initState : https://stackoverflow.com/a/53373017/9206337
This post will give you an idea to know when the app finishes the build method. So that you can wait for your async method to setState after widget is mounted : https://stackoverflow.com/a/51273797/9206337
You can create an async method and call it inside your initState
#override
void initState() {
super.initState();
asyncMethod(); ///initiate your method here
}
Future<void> asyncMethod async{
await ///write your method body here
}
Per documentation at https://pub.dev/packages/provider
initState() {
super.initState();
Future.microtask(() =>
context.read<MyNotifier>(context).fetchSomething(someValue);
);
}
Sample code:
#override
void initState() {
super.initState();
asyncOperation().then((val) {
setState(() {});
print("success");
}).catchError((error, stackTrace) {
print("outer: $error");
});
//or
asyncOperation().whenComplete(() {
setState(() {});
print("success");
}).catchError((error, stackTrace) {
print("outer: $error");
});
}
Future<void> asyncOperation() async {
await ... ;
}
As loading or waiting for initial state is a (generally) aone off event FutureBuilder would seem to be a good option as it blocks once on an async method; where the async method could be the loading of json config, login etc. There is an post on it [here] in stack.(Flutter StreamBuilder vs FutureBuilder)
How about this?
#override
void initState() {
//you are not allowed to add async modifier to initState
Future.delayed(Duration.zero,() async {
//your async 'await' codes goes here
});
super.initState();
}
initState() and build cannot be async; but in these, you can call a function that is async without waiting for that function.
#override
void initState() {
super.initState();
_userStorage.getCurrentUser().then((user) {
setState(() {
if (user.isAuthenticated) {
Timer.run(() {
redirectTo();
});
}
});
});
}
void redirectTo() {
Navigator.push(context,
MaterialPageRoute(builder: (BuildContext context) => new ShopOrders()));
}
I would strongly suggest using a FutureBuilder. It takes care of executing the async function and building the widget according to the result!
Here's a link to a short intro video and the documentation.
Code Example:
Future<void> initControllers() async {
for (var filePath in widget.videoFilePaths) {
var val = VideoPlayerController.file(File(filePath));
await val.initialize();
controllers.add(val);
}
}
#override
Widget build(BuildContext context) {
FutureBuilder(
future: initControllers(),
builder: (context, snapshot) {
if (snapshot.connectionState == ConnectionState.done) {
return YourWidget();
} else {
return const ProgressIndicator();
}
},
));}
Tried all suggestions, none would keep my build from starting after the async method that I need in initState() finish, except one: the trick of having a a bool variable in the State class (let's call it _isDataLoaded) that is initialized to false upon definition, set to true inside a setState() that is invoked when the async function finishes inside initState(). In the build, condition a CircleProcessIndicator() or your Widget depending on the value of this variable.
I know it's dirty because it could break the build, but honestly nothing else that would make more sense -such as running super.initState() upon completion of the async function- has worked for me.
I came here because I needed to fetch some files from FTP on program start. My project is a flutter desktop application. The main thread download the last file added to the FTP server, decrypts it and displays the encrypted content, this method is called from initState(). I wanted to have all the other files downloaded in background after the GUI shows up.
None of the above mentioned methods worked. Constructing an Isolate is relatively complex.
The easy way was to use the "compute" method:
move the method downloading all files from the FTP out of the class.
make it an int function with an int parameter (I do not use the int parameter or the result)
call it from the initState() method
In that way, the GUI shows and the program downloads the files in background.
void initState() {
super.initState();
_retrieveFileList(); // this gets the first file and displays it
compute(_backgroundDownloader, 0); // this gets all the other files so that they are available in the local directory
}
int _backgroundDownloader(int value) {
var i = 0;
new Directory('data').createSync();
FTPClient ftpClient = FTPClient('www.guckguck.de',
user: 'maxmusterman', pass: 'maxmusterpasswort');
try {
ftpClient.connect();
var directoryContent = ftpClient.listDirectoryContent();
// .. here, fileNames list is reconstructed from the directoryContent
for (i = 0; i < fileNames.length; i++) {
var dirName = "";
if (Platform.isLinux)
dirName = 'data/';
else
dirName = r'data\';
var filePath = dirName + fileNames[i];
var myDataFile = new File(filePath);
if (!myDataFile.existsSync())
ftpClient.downloadFile(fileNames[i], File(filePath));
}
} catch (err) {
throw (err);
} finally {
ftpClient.disconnect();
}
return i;
I have used timer in initState
Timer timer;
#override
void initState() {
super.initState();
timer = new Timer.periodic(new Duration(seconds: 1), (Timer timer) async {
await this.getUserVerificationInfo();
});
}
#override
void dispose() {
super.dispose();
timer.cancel();
}
getUserVerificationInfo() async {
await someAsyncFunc();
timer.cancle();
}

Request data from a stream one item at time?

I'm trying to fetch data from a REST endpoint that serves a paginated response. On a button click in flutter, I would like to get the next item in the response. I think I want to use a Stream that abstracts away the paginated nature of the request, and automatically polls the next result.
Something like this dartish pseudo-code:
Stream<String> nextUrl(int lastPage=0)
{
// Get paginated response
batch = server.getResponse(lastPage)
for (item in batch)
{
yield item;
}
// Recurse automatically
// Not a problem if the stream is suspended
// after every item request. A big problem
// if the stream never pauses.
await for (String item in nextUrl(lastPage++)
{
yield item;
}
}
// In flutter
class WidgetState extends state<MyPage> {
Stream<String> urlStream = new nextUrl(0);
String currentItem;
...
#override
Widget build(BuildContext context) {
return new InkWell(
onTap: () {
(() async {
// This is the call I haven't figured out.
await item = urlStream().getNext();
setState(() { currentItem = item; });
})();
}
);
}
}
I get the feeling that maybe something like Stream.getNext() doesn't exist? That I should be pausing / unpausing this stream? But I'm not sure how that would return only a single item at a time.
The async package provide StreamQueue that allows to do that
var events = new StreamQueue<String>(yourStream);
var first = await events.next;
while (...) {
...
first = await events.next;
}

Flutter Camera Plugin

I'm new to both Flutter and Dart, and I'm trying to use the Camera Plugin to understand how things work. All examples I find have this part:
List<CameraDescription> cameras;
Future<Null> main() async {
cameras = await availableCameras();
runApp(new CameraApp());
}
Is there some way I could do this inside the initState() method? I guess this is also a more general question regarding async work required before the initState-method is run. (As the initState-method cannot be async).
My goal is to create a StatefulWidget containing a feed from the camera, that is used from another file. Here's what I have so far. Any help appreciated!
List<CameraDescription> cameras;
#override
void initState() {
super.initState();
getCameras();
controller = new CameraController(cameras[0], ResolutionPreset.medium);
controller.initialize().then( (_) {
if (!mounted) {
return;
}
setState(() {});
});
}
Future<Null> getCameras() async {
cameras = await availableCameras();
}
You can't do async work in initState, but you can kick-off async work done in other functions, and then signal when you are done with a setState call. Using await you can make sure the cameras and controller are setup in the correct order. calling setState at the end will make sure the widget gets rebuilt at the end, where you can pass your initialized camera controller wherever you want.
class _CameraState extends State<CameraWidget> {
List<CameraDescription> cameras;
CameraController controller;
bool _isReady = false;
#override
void initState() {
super.initState();
_setupCameras();
}
Future<void> _setupCameras() async {
try {
// initialize cameras.
cameras = await availableCameras();
// initialize camera controllers.
controller = new CameraController(cameras[0], ResolutionPreset.medium);
await controller.initialize();
} on CameraException catch (_) {
// do something on error.
}
if (!mounted) return;
setState(() {
_isReady = true;
});
}
Widget build(BuildContext context) {
if (!_isReady) return new Container();
return ...
}
}
You also want to make sure you handle any errors, the package includes a CameraException which is thrown when the platform specific code fails.

JavaFX: scrolling vs. focus traversal with arrow keys

I got a ScrollPane containing focusable Nodes.
The current default behaviour is:
Shift + ←, ↑, →, ↓ moves the focus
←, ↑, →, ↓ scrolls the view
I want it the other way around.
How can I accomplish this or where should I start?
[EDIT] Well, there is another fragile approach.
Instead of messing around with the events, one could mess around with the KeyBindings.
scrollPane.skinProperty().addListener(new ChangeListener<Skin<?>>() {
#Override
public void changed(ObservableValue<? extends Skin<?>> observable, Skin<?> oldValue, Skin<?> newValue) {
ScrollPaneSkin scrollPaneSkin = (ScrollPaneSkin) scrollPane.getSkin();
ScrollPaneBehavior scrollPaneBehavior = scrollPaneSkin.getBehavior();
try {
Field keyBindingsField = BehaviorBase.class.getDeclaredField("keyBindings");
keyBindingsField.setAccessible(true);
List<KeyBinding> keyBindings = (List<KeyBinding>) keyBindingsField.get(scrollPaneBehavior);
List<KeyBinding> newKeyBindings = new ArrayList<>();
for (KeyBinding keyBinding : keyBindings) {
KeyCode code = keyBinding.getCode();
newKeyBindings.add(code == KeyCode.LEFT || code == KeyCode.RIGHT || code == KeyCode.UP || code == KeyCode.DOWN ? keyBinding.shift() : keyBinding);
}
keyBindingsField.set(scrollPaneBehavior, newKeyBindings);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
LOGGER.warn("private api changed.", e);
}
}
});
I think, that could be the cleaner way, if KeyBindings were more non-static, modifyable and public.
Use an event filter to capture the relevant key events and remap them to different key events before the events start to bubble.
Re-mapping default keys is a tricky thing which:
Can confuse the user.
May have unexpected side effects (e.g. TextFields may no longer work as you expect).
So use with care:
import javafx.application.*;
import javafx.event.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.TilePane;
import javafx.stage.Stage;
import java.util.*;
public class ScrollInterceptor extends Application {
#Override
public void start(Stage stage) {
ScrollPane scrollPane = new ScrollPane(
createScrollableContent()
);
Scene scene = new Scene(
scrollPane,
300, 200
);
remapArrowKeys(scrollPane);
stage.setScene(scene);
stage.show();
hackToScrollToTopLeftCorner(scrollPane);
}
private void remapArrowKeys(ScrollPane scrollPane) {
List<KeyEvent> mappedEvents = new ArrayList<>();
scrollPane.addEventFilter(KeyEvent.ANY, new EventHandler<KeyEvent>() {
#Override
public void handle(KeyEvent event) {
if (mappedEvents.remove(event))
return;
switch (event.getCode()) {
case UP:
case DOWN:
case LEFT:
case RIGHT:
KeyEvent newEvent = remap(event);
mappedEvents.add(newEvent);
event.consume();
Event.fireEvent(event.getTarget(), newEvent);
}
}
private KeyEvent remap(KeyEvent event) {
KeyEvent newEvent = new KeyEvent(
event.getEventType(),
event.getCharacter(),
event.getText(),
event.getCode(),
!event.isShiftDown(),
event.isControlDown(),
event.isAltDown(),
event.isMetaDown()
);
return newEvent.copyFor(event.getSource(), event.getTarget());
}
});
}
private TilePane createScrollableContent() {
TilePane tiles = new TilePane();
tiles.setPrefColumns(10);
tiles.setHgap(5);
tiles.setVgap(5);
for (int i = 0; i < 100; i++) {
Button button = new Button(i + "");
button.setMaxWidth(Double.MAX_VALUE);
button.setMaxHeight(Double.MAX_VALUE);
tiles.getChildren().add(button);
}
return tiles;
}
private void hackToScrollToTopLeftCorner(final ScrollPane scrollPane) {
Platform.runLater(new Runnable() {
#Override
public void run() {
scrollPane.setHvalue(scrollPane.getHmin());
scrollPane.setVvalue(0);
}
});
}
public static void main(String[] args) {
launch(args);
}
}

BlackBerry - Simulate a KeyPress event

I have a BlackBerry application that needs to take pictures from the camera and send them to a server. In order to do this i invoke the native camera application and listen to the filesystem. Once an image is captured and saved as a new jpeg file i get notified, resume foreground control and go about my business. The problem starts occurring after the first time this cycle is completed because now when i decide to call the camera application again it is already opened, and now the user is seeing a thumbnail of the last picture that was taken and several buttons allowing him to manipulate/manage it. naturally what i want the user to see is a preview of what the camera is "seeing" before he snaps another photo as he did before.
I have thought of various ways to solve this including killing the camera app each time (I understand this cannot be done programatically?), sending CameraArguments when invoking the app (which appears to be useless), and now i was thinking a solution could be as simple generating a "Back" key event before switching back to my app which would theoretically dismiss the annoying edit screen. Could this really be done? and if not is there any other possible solution you may think of?
A kind of hack...
start Camera App
in TimerTask check if Camera App started and if it need to be closed (some flag)
if yes, invoke it(so it will became active) and push ESC keypress event injection to close it
Take a look at this:
class Scr extends MainScreen {
boolean killCameraApp = false;
final String mCameraModuleName = "net_rim_bb_camera";
final CameraArguments args = new CameraArguments();
public Scr() {
super();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
if (isCameraRunning() && killCameraApp) {
getApplication().invokeAndWait(callCamera);
getApplication().invokeAndWait(killCamera);
}
}
}, 0, 100);
}
Runnable callCamera = new Runnable() {
public void run() {
callCamera();
}
};
Runnable killCamera = new Runnable() {
public void run() {
injectKey(Characters.ESCAPE);
killCameraApp = false;
}
};
private boolean isCameraRunning() {
boolean result = false;
ApplicationManager appMan =
ApplicationManager.getApplicationManager();
ApplicationDescriptor[] appDes = appMan.getVisibleApplications();
for (int i = 0; i < appDes.length; i++) {
result = mCameraModuleName.equalsIgnoreCase(appDes[i]
.getModuleName());
if (result)
break;
}
return result;
}
private void callCamera() {
Invoke.invokeApplication(Invoke.APP_TYPE_CAMERA,
new CameraArguments());
}
private void injectKey(char key) {
KeyEvent inject = new KeyEvent(KeyEvent.KEY_DOWN, key, 0);
inject.post();
}
protected void makeMenu(Menu menu, int instance) {
menu.add(new MenuItem("start camera", 0, 0) {
public void run() {
callCamera();
killCameraApp = false;
}
});
menu.add(new MenuItem("kill app", 0, 0) {
public void run() {
killCameraApp = true;
}
});
super.makeMenu(menu, instance);
}
}
EDIT: Don't forget to set permissions for device release:
Options => Advanced Options => Applications => [Your Application] =>Edit Default permissions =>Interactions =>key stroke Injection

Resources