I have installed the Android Alarm Manager plugin in my Flutter v1.0.0 app by following the instructions at the link. But when I try to use AndroidAlarmManager.oneShot(...) nothing happens. There's not even an error in the console.
I am using a FutureBuilder widget to wait on the AndroidAlarmManager.initialize() future and one other future before my app begins rendering:
final combinedFutures = Future.wait<void>([
gameService.asyncLoad(),
AndroidAlarmManager.initialize(),
]);
FutureBuilder<void>(
future: combinedFutures,
builder: (context, snapshot) {
...
}
)
The FutureBuilder does end up rendering what it should so I know that the AndroidAlarmManager.initialize() future returns correctly.
Then in a button's onPressed function I do this:
AndroidAlarmManager.oneShot(
Duration(seconds: 5),
0,
() {
print("test");
},
wakeup: true,
);
The above should be calling print(test) after 5 seconds and waking up my phone but nothing is printed to the console and nothing happens on my phone. What is going wrong?
After debugging the library code I found that the AndroidAlarmManager.oneShot function calls the method PluginUtilities.getCallbackHandle(callback) where callback is the function passed in. In my case the callback function was:
() {
print("test");
}
For some reason PluginUtilities.getCallbackHandle was returning null when it should be returning a value of type CallbackHandle.
Looking at the PluginUtilities.getCallbackHandle documentation it says:
Get a handle to a named top-level or static callback function which can be easily passed between isolates.
The function I was passing is not top-level, static, or named. So I did the following inside my class and everything works fine now:
static void alarmTest() {
print("test");
}
void runAlarm() {
AndroidAlarmManager.oneShot(
Duration(seconds: 10),
0,
alarmTest,
wakeup: true,
).then((val) => print(val));
}
Related
I've got some Future code that looks like this:
return login().then((user) {
print("Logged in user ${user.name}");
return user;
}).catchError(this.displayError);
Where I'm trying to pass a function to the catchError(...) function because it's an error handler I'm reusing across a number of calls. I've tried using this:
Future<void> displayError(Error error) {
return showDialog(context: context, builder: (context) {
return AlertDialog(...);
});
}
But when I run the app I get this error:
Invalid argument (onError): Error handler must accept one Object or one Object and a StackTrace as arguments, and return a a valid result: Closure: (Error) => Future<void> from Function 'displayError':.
If I change my catchError(...) to this:
.catchError((error) {
displayError(error);
})
Everything then works. My question is does anyone know why I've not been successful passing the displayError function to catchError instead of call it within a closure?
Change the signature of the handler – it must be an Object not an Error. There are no promises in Dart that thrown objects are of type Error. They could be anything.
Future<void> displayError(Object error) {
return showDialog(context: context, builder: (context) {
return AlertDialog(...);
});
}
How to add something like isClickable() in my appium native app tests. I have written my tests, however they are very flaky and fail sometimes because it cannot find the element. I am thinking about making custom click and set value functions with the implicit wait times.
I thought about using isClickable() but the appium documentation says - Please note that isClickable works only in web and webviews, it doesn't work in mobile app native context.
Is there any other alternative i can use? can i use smartwait? if yes how can i implement that
Here is how i am defining home.screen.js
import AppScreen from './app.screen';
const SELECTORS = {
HOME_SCREEN: '~homeBarButton',
PRODUCTSEARCH_SCREEN: '~productSearchBarButton',
CUSTOMERSEARCH_SCREEN: '~customersBarButton',
STOREHUB_SCREEN: '~storeHubBarButton',
SETTING_ICON: '~SettingsIcon',
LOGOUT_BUTTON: '~settingsMainLogoutButton'
};
class HomeScreen extends AppScreen {
constructor () {
super(SELECTORS.HOME_SCREEN);
}
get homescreenButton () {
return $(SELECTORS.HOME_SCREEN);
}
get productsearchField () {
return $(SELECTORS.PRODUCTSEARCH_SCREEN);
}
get customersearchButon () {
return $(SELECTORS.CUSTOMERSEARCH_SCREEN);
}
get storehubButon () {
return $(SELECTORS.STOREHUB_SCREEN);
}
get settingIcon () {
return $(SELECTORS.SETTING_ICON);
}
get logoutButton () {
return $(SELECTORS.LOGOUT_BUTTON);
}
}
export default new HomeScreen();
And i am writing my test like this test.js:
import HomeScreen from '../screenobjects/home.screen';
import FormScreen from '../screenobjects/forms.screen';
import CommonPage from '../pageobjects/common.page';
describe('Sending item successfullt,', () => {
beforeEach(() => {
CommonPage.login()
});
afterEach(() => {
CommonPage.logout()
});
it('should be able to send the item to the mirror', () => {
driver.pause(3000)
HomeScreen.productsearchField.click();
driver.pause(3000)
HomeScreen.customersearchButon.click();
});
});
As you can see above, I have to add driver.pause otherwise my tests would fail because of button not clickable or typeable.
My suggestion is that you can get your elements attribute clickable and if its true keep doing your things
public boolean isClickable(String element) {
return androidDriver.findElementByAccessibilityId(element).getAttribute("clickable").equals("true");
}
You can use any method to find your element.
Best approach is to stop using implicit waits and do an explicit wait before each driver UI interaction.
You should do some reading on waitUntil / WebDriverWait (not sure if you have that in node.js implementation).
Then create functions for interacting with all types of elements in your app that perform an explicit wait before execution.
Pseudo code:
get clickButton (Selector element) {
waitUntil(clickable(element),...);
return $(driver.click(element));
}
Write generic methods for all type of elements in your app (button, textfield, dropdown...) and remove implicit waits from driver. You will see a big difference in your test stability.
In a scenario like the following, how to pass a variable List<Document> itemList by reference ?
Future.wait([futDocs]).then((dataRet){
dataRet.forEach((doco) {
var docList = doco.documents;
docList.forEach((doc){
var docTitle = doc['title'];
print("data is $docTitle");
itemList.add(docTitle); <--- change not reflected outside this function
The itemList does not change when itemList.add(docTitle) is performed in the above Future.wait(). I believe the reason is because itemList is not passed by reference. If I cannot pass itemList by reference, how can I make this work? Can I return dataRet as a list and use it outside Future.wait()?
Adding more info...
Above call is performed within the constructor of class ListBuilder as shown in the following code:
class ListBuilder {
List<Document> itemList = new List<Document>();
ListBuilder(){
var futDocs = Firestore.instance.collection('Data').orderBy('time').limit(10).getDocuments();
Future.wait([futDocs]).then((dataRet) {
dataRet.forEach((doco) {
var docList = doco.documents;
docList.forEach((doc){
var docTitle = doc['title'];
print("data is $docTitle");
itemList.add(docTitle); <--- change not reflected outside this function
[...]
While waiting for an answer, i have tried custom setters and getters as well as the use of this.itemList.add(...) but without success.
Any help, it is much appreciated.
Thanks in advance!
All objects in Dart are passed by reference.
The problem is likely [itemList] isn't updated immediately because Future.wait is an async function. Any code dealing with itemList has to run inside the callback function ( .then((dataret) { ... }); ).
class ListBuilder {
List<Document> itemList = new List<Document>();
ListBuilder() {
var Docs =
Firestore.instance.collection('Data').orderBy('time').limit(10).getDocuments();
Future.wait([futDocs]).then((dataRet) {
dataRet.forEach((doco) {
var docList = doco.documents;
docList.forEach((doc) {
var docTitle = doc['title'];
itemList.add(docTitle);
});
});
/*
itemList has been updated here because we are inside a callback function that
runs _after_ Future.wait has completed.
*/
});
/*
itemList hasn't been updated here _yet_ because this code runs immediately,
regardless how long the Future.wait call takes to complete.
*/
}
}
If you want you can use async/await logic instead of a callback function, which is often easier to read and maintain. More info here: https://www.dartlang.org/tutorials/language/futures
More code would be helpful, but the first things that come to mind are these:
It will probably work to call await before your Future.wait so that the asynchronous code is for sure run
This seems less likely, but you may have to call setState when adding your to your list
Edit:
I would suggest code like this:
class ListBuilder {
List<Document> itemList = new List<Document>();
Future<void> myFunc(var futDocs) async {
await Future.wait([futDocs]).then((dataRet){
[...] *Rest of your Future code here*
}
ListBuilder() {
var futDocs = Firestore.instance.collection('Data').orderBy('time').limit(10).getDocuments();
myFunc(futDocs)
[...]
I have solved the problem however it was not related to pass value by reference. Thank you for your answers, they actually got me thinking and pointed me in the right direction.
The following was the solution to a problem which is not the one raised above :D LOL
Tl;dr; answer:
Change the widget to use streamBuilder
Long answer
The problem was due to the fact that changes to a list were not reflected outside the .then() function. Well, the issue itself is not related to how the list was passed to the function but to the use of async/await logic. When the app is executed, it will populate the itemList in Widget with the current items in the list - this is empty when it starts. In the meanwhile, with an async call, firebase fetch the data and list is updated. However, the UI is not changed. Now, setState gave me an idea but was not the solution. In my case, I don't need a stateful Widget but rather the use of StreamBuilder within the creation of the widget. This bind the list to the async call with the use of stream
new StreamBuilder(
stream: Firestore.instance.collection('data').orderBy('time').limit(10).snapshots(),
builder: (context, snapshot) {
return itemList...
Thanks again!
Is it possible to construct a closure in objective-c and pass it to javascript where it can be invoked? The specific problem I am trying to solve is adding support for changing shipping methods and contacts in Apple Pay as part of the tipsi-stripe react-native module (something it doesn't do yet). This is basically what I have so far, but the callback in javascript gets 'null'.
- (void) paymentAuthorizationViewController:(PKPaymentAuthorizationViewController *)controller
didSelectShippingMethod: (PKShippingMethod *) shippingMethod
completion:(nonnull void (^)(PKPaymentAuthorizationStatus, NSArray<PKPaymentSummaryItem *> * _Nonnull))completion {
id callback = (void (^)(NSArray* summaryItems)) {
completion(PKPaymentAuthorizationStatusSuccess, nil, summaryItems);
}
[self sendEventWithName: "#ShippingMethodChanged" body:#{#"selectedMethod": #"someMethodDetails", #"callback": callback}];
}
In javascript, I have something like this:
import { NativeEventEmitter, NativeModules } from 'react-native'
const { TPSStripeManager } = NativeModules;
const stripeEventEmitter = new NativeEventEmitter(TPSStripeManager);
componentWillMount() {
this.stripeOnShippingMethodChanged = stripeEventEmitter.addListener(
'ShippingMethodChanged',
(method, callback) => {
// async compute some value then
let summaryItems = await computeItemsWithMethod(method);
callback(summaryItems);
}
);
}
componentWillUnmount() {
this.stripeOnShippingMethodChanged.remove();
}
I'm assuming I have to somehow wrap the Objective-C closure so javascript knows how to invoke it but I can't find anything. Any help appreciated!
It is not possible to do what I was attempting to do here, the react-native bindings only support simple types that can be encoded as a string. The solution that I ended up with based on patterns I found elsewhere is to store the completion as a property on the object, trigger an event that can be seen in js-land and provide another exported method that can be called to trigger the completion. Code is here:
https://github.com/tipsi/tipsi-stripe/pull/244/files
i have following dart code:
#override
void attached() {
document.onCopy.listen(_onCopyHandler);
}
void _onCopyHandler(Event event) {
// Here i'd like to access event.clipboardData but it
// does not work for Firefox using Dart code so i need to
// get access to underlying javascript event object
}
i've already tried following without any success on Firefox (for Chrome it worked):
var jsEvent = new JsObject.fromBrowserObject(event);
as soon as i'll have a reference to JsObject i know how to call the method, i just need to get an access to that reference.