While i trying refresh my token with interceptors, its always looping. I dont know why. Firstly i want to show my code;
class ApiService {
final Dio _dio = Dio();
final Dio tokenDio = Dio();
int tryCount = 0;
String? accessToken;
String? refreshToken;
final _storage = const FlutterSecureStorage();
ApiService() {
_dio.options.baseUrl = ApiConstants.baseUrl;
tokenDio.options = _dio.options;
_storage.read(key: "token").then((value) => accessToken = value);
_storage.read(key: "refreshToken").then((value) => refreshToken = value);
_dio.interceptors.add(
QueuedInterceptorsWrapper(
onError: (DioError error, ErrorInterceptorHandler handler) async {
if (error.response?.statusCode == 401) {
log("Hata Aldık! ${error.requestOptions.path}");
RequestOptions options = error.response!.requestOptions;
if ("Bearer $accessToken" != options.headers[HttpHeaders.authorizationHeader]) {
log("token farklıydı! $accessToken / ${options.headers[HttpHeaders.authorizationHeader]} ");
options.headers[HttpHeaders.authorizationHeader] = "Bearer $accessToken";
_dio.fetch(options).then(
(value) => handler.resolve(value),
onError: (e) {
log("error on resolve");
handler.reject(e);
},
);
return;
}
_dio
.get("/Auth/RefreshTokenLogin?refreshToken=${Uri.encodeQueryComponent(refreshToken!)}")
.then((response) async {
log("Token Refreshlendi!");
if (response.statusCode == 200) {
final RefreshTokenResponseModel? model = RefreshTokenResponseModel.fromJson(response.data);
_storage.write(key: "token", value: model!.data!.token);
_storage.write(key: "refreshToken", value: model.data!.refreshToken);
refreshToken = model.data!.refreshToken;
accessToken = model.data!.token;
options.headers[HttpHeaders.authorizationHeader] = "Bearer ${model.data!.token}";
tokenDio.options.headers = options.headers;
// await UserSecureStorage.setField("token", model!.data!.token);
// await UserSecureStorage.setField("refreshToken", model.data!.refreshToken);
// final AuthService _authService = AuthService();
// await _authService.registerDevice();
if (model.data != null && model.data!.contractsToBeApproved!.isNotEmpty) {
final AuthController authController = Get.Get.find();
authController.contractsToBeApproved.value = model.data!.contractsToBeApproved!;
}
_dio.fetch(options).then(
(value) => handler.resolve(value),
onError: (e) {
handler.reject(e);
},
);
return;
}
return;
});
}
return handler.next(error);
},
onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
log("istek atıldı. ${options.path}");
if (accessToken == null) {
_storage.read(key: "token").then((value) {
log("storage readed $value");
accessToken = value;
options.headers[HttpHeaders.authorizationHeader] = "Bearer $value";
tokenDio.options.headers = options.headers;
handler.next(options);
});
} else {
options.headers[HttpHeaders.authorizationHeader] = "Bearer $accessToken";
return handler.next(options);
}
},
),
);
}
Its my ApiService. And my goal is refreshing token. It might have multiple request in same time so I used "QueuedInterceptorsWrapper". After token expire, Its entering to error handler function and logging "Token Refreslendi" after token refresh api call.But after that if i click something for api call its looping infinitely. Whats wrong with my code ? Thanks for help :)
Related
I am working with GraphQL. I need to pass parameters to Http headers. However, I see an option to pass only normal parameters (code below). PLEASE tell me how to do it!!
Query(
options: QueryOptions(
document: gql(userGraphQL),
variables: {
"..." : "..."
}
),
builder: ...
)
I have created this file to related the GraphQL
gql_utils.dart file
class GraphQLUtils {
static final GraphQLUtils _instance = GraphQLUtils._internal();
GraphQLClient? _client;
factory GraphQLUtils() {
return _instance;
}
GraphQLUtils._internal() {
final httpLink = HttpLink(
'your base url',
);
final authLink = AuthLink(
ignore: undefined_identifier
getToken: () async => 'Bearer $YOUR_PERSONAL_ACCESS_TOKEN',
);
var link = authLink.concat(httpLink);
var link = httpLink;
var link = httpLink;
final policies = Policies(
fetch: FetchPolicy.networkOnly,
);
_client = GraphQLClient(
cache: GraphQLCache(),
link: link,
defaultPolicies: DefaultPolicies(
watchQuery: policies,
query: policies,
mutate: policies,
),
);
}
Future<Map<String, dynamic>> queryRepo(
DocumentNode readRepositories, map) async {
final options = WatchQueryOptions(
document: readRepositories,
variables: map,
pollInterval: const Duration(seconds: 4),
fetchResults: true,
);
QueryResult result = await _client!.query(options);
if (result.hasException) {
Map<String, dynamic> response = <String, dynamic>{};
response['success'] = false;
response['message'] = result.exception!.graphqlErrors[0].message;
return response;
} else {
Map<String, dynamic> response = <String, dynamic>{};
response['success'] = true;
response['data'] = result.data;
return response;
}
}
}
this is Example query class
class UserQueries {
static final userInsertQuery = gql(r'''
mutation Insert_users($objects: [users_insert_input!]!, $onConflict: users_on_conflict) {
insert_users(objects: $objects, on_conflict: $onConflict) {
returning {
id
name
timestamp
}
}
}
''');
}
and how to call api
Future insertUserApi(String name) async {
try {
Map<String, dynamic> variables = <String, dynamic>{};
variables = {
"objects": [
{"name": name}
],
"onConflict": {
"constraint": "users_pkey",
"update_columns": [
"id",
"name",
]
}
};
await GraphQLUtils()
.queryRepo(UserQueries.userInsertQuery, variables)
.then((response) async {
if (response["data"] != null) {
print("----Data---:${response["data"]}");
Get.back();
} else {
Get.snackbar(
"Error",
"Something Went wrong",
);
}
});
} catch (e, st) {
Get.snackbar(
"Error",
e.toString(),
);
}
}
I am trying to integrate paytm payment gateway in my flutter application by following all in one sdk developer documentation but getting the error on UI (Access Denied you don't have permission to access https://securegw-stage.paytm.in/theia/api/v1/showPaymentPage?mid=MID&orderId=ORDER_ID on this server)
Here is my payment config class code
import 'dart:convert';
import 'package:flutter/services.dart';
import 'package:http/http.dart' as http;
import 'package:paytm_allinonesdk/paytm_allinonesdk.dart';
class PaytmConfig {
final String _mid = MID;
final String _mKey = MKEY;
final String _website = "WEBSTAGING";
final String _url =
'http://flutter-paytm-backend.great-site.net/generateTxnToken'; //testing server
String get mid => _mid;
String get mKey => _mKey;
String get website => _website;
String get url => _url;
String getMap(double amount, String callbackUrl, String orderId) {
return json.encode({
"mid": mid,
"key_secret": mKey,
"website": website,
"orderId": orderId,
"amount": amount.toString(),
"callbackUrl": callbackUrl,
"custId": "9204473819624814",
});
}
Future<void> generateTxnToken(double amount, String orderId) async {
final callBackUrl =
'https://securegw-stage.paytm.in/theia/paytmCallback?ORDER_ID=$orderId';
final body = getMap(amount, callBackUrl, orderId);
try {
final response = await http.post(
Uri.parse(url),
body: body,
headers: {'Content-type': "application/json"},
);
String txnToken = response.body;
await initiateTransaction(orderId, amount, txnToken, callBackUrl);
} catch (e) {
print(e);
}
}
Future<void> initiateTransaction(String orderId, double amount,
String txnToken, String callBackUrl) async {
String result = '';
try {
var response = AllInOneSdk.startTransaction(
mid,
orderId,
amount.toString(),
txnToken,
callBackUrl,
true,
false,
);
response.then((value) {
// Transaction successfull
print(value);
}).catchError((onError) {
if (onError is PlatformException) {
result = onError.message! + " \n " + onError.details.toString();
print(result);
} else {
result = onError.toString();
print(result);
}
});
} catch (err) {
// Transaction failed
result = err.toString();
print(result);
}
}
}
Calling the function to initiate the transaction when the button is pressed
ElevatedButton(
onPressed: () async => {
if (_formKey.currentState!.validate())
{
await PaytmConfig().generateTxnToken(
double.parse(priceController.text.toString()),
ORDER_ID),
}
},
child: Text('Pay'),
)
And here is my app.js class code in backend
var express = require('express');
var port = process.env.PORT || 3000;
var app = express();
const https = require('https');
const checksum_lib = require('./checksum');
app.use(express.json());
app.use(express.urlencoded());
app.get('/', function(req, res) {
console.log(req);
res.send(JSON.stringify({ Hello: 'World' }));
});
app.post('/generateTxnToken', function(request, res) {
console.log(request.body);
var paytmParams = {};
var MID = request.body.mid;
var orderId = request.body.orderId;
var amount = parseFloat(String(request.body.amount));
var custId = request.body.custId;
var key_secret = request.body.key_secret;
var callbackUrl = request.body.callbackUrl;
var mode = request.body.mode;
var website = request.body.website;
var testing = String(request.body.testing);
console.log(callbackUrl);
console.log(mode);
paytmParams.body = {
/* for custom checkout value is 'Payment' and for intelligent router is 'UNI_PAY' */
"requestType": "Payment",
/* Find your MID in your Paytm Dashboard at https://dashboard.paytm.com/next/apikeys */
"mid": MID,
/* Find your Website Name in your Paytm Dashboard at https://dashboard.paytm.com/next/apikeys */
"websiteName": website == undefined ? "DEFAULT" : website,
/* Enter your unique order id */
"orderId": orderId,
/* on completion of transaction, we will send you the response on this URL */
// "callbackUrl": "https://mrdishant.com",
"callbackUrl": callbackUrl,
/* Order Transaction Amount here */
"txnAmount": {
/* Transaction Amount Value */
"value": amount,
/* Transaction Amount Currency */
"currency": "INR",
},
/* Customer Infomation here */
"userInfo": {
/* unique id that belongs to your customer */
"custId": custId,
},
};
console.log("Mode");
console.log(mode);
if (mode == "1") {
console.log("Mode 1 So Net Banking");
paytmParams.body[
"enablePaymentMode"] = [{
"mode": "NET_BANKING",
}]
} else if (mode == "0") {
console.log("Mode 0 So BALANCE");
paytmParams.body[
"enablePaymentMode"] = [{
"mode": "BALANCE",
}]
} else if (mode == "2") {
console.log("Mode 2 So UPI");
paytmParams.body[
"enablePaymentMode"] = [{
"mode": "UPI",
}]
} else if (mode == "3") {
console.log("Mode 3 So CC");
paytmParams.body[
"enablePaymentMode"] = [{
"mode": "CREDIT_CARD"
}]
}
console.log(JSON.stringify(paytmParams));
/**
* Generate checksum by parameters we have in body
* Find your Merchant Key in your Paytm Dashboard at https://dashboard.paytm.com/next/apikeys
*/
checksum_lib.genchecksumbystring(JSON.stringify(paytmParams.body), key_secret, (err, checksum) => {
if (err) {
return;
}
/* head parameters */
paytmParams.head = {
/* put generated checksum value here */
"signature": checksum
};
/* prepare JSON string for request */
var post_data = JSON.stringify(paytmParams);
var options = {
/* for Staging */
/* for Production */
hostname: testing == "0" ? 'securegw-stage.paytm.in' : 'securegw.paytm.in',
port: 443,
path: '/theia/api/v1/initiateTransaction?mid=' + MID + '&orderId=' + orderId,
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Content-Length': post_data.length
}
};
// Set up the request
var response = "";
var post_req = https.request(options, (post_res) => {
post_res.on('data', (chunk) => {
response += chunk;
});
post_res.on('end', () => {
console.log(orderId);
console.log(MID);
console.log('Response: ', response);
response = JSON.parse(response);
res.send(response.body.txnToken);
return 0;
});
});
// post the data
post_req.write(post_data);
post_req.end();
});
});
app.listen(port, function() {
console.log(`Example app listening on port !`);
});
Via flutter app, I need to send periodic local notifications with callback--where callback is called for each period/interval before sending notification.
So far I have tried Timer to schedule periodic local notifications, but it does not work in background i.e. when app is closed. I have also tried flutterLocalNotificationsPlugin.periodicallyShow, but this one does not have callback, which is called before sending notification. Lastly, I have tried Firebase, but it needs notifications to be set in firebase console. how to get callback notification with completed and pending reports after changes in database.
Please advice a solution for the above given problem.
This is my code :
import 'package:eclarity/pages/radiologist.dart';
import 'package:eclarity/pages/technici.dart';
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:http/http.dart' as http;
import 'dart:convert';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/intl.dart';
import 'package:android_alarm_manager/android_alarm_manager.dart';
import 'package:flutter/cupertino.dart';
import 'dart:isolate';
import 'dart:math';
import 'dart:ui';
// import 'dart:ui';
import 'package:eclarity/pages/local_notifications.dart' as notify;
class HomePage extends StatefulWidget {
#override
_HomePageState createState() => _HomePageState();
}
class _HomePageState extends State {
FlutterLocalNotificationsPlugin fltrNotification;
String _selectedParam;
String task;
String task1;
int task2;
String duration;
String numCompleted;
String numCompleted1;
String numPending;
String numPending1;
String role;
Timer timer;
TextEditingController timeController;
TextEditingController timeController1 = TextEditingController();
int val;
var url = "http://";
var data;
// Future<Radiologist> _future;
//Future<Radiologist> fetchReportStatus() async {
fetchReportStatus() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int userPk = prefs?.getInt("userPk");
String startDate2 = prefs?.getString("notifyDateTime");
String startDate = prefs?.getString("loginDateTime");
var currentTime =
DateFormat('yyyy-MM-dd hh:mm:ss a').format(DateTime.now());
String role = prefs?.getString("role");
bool userIsTechnician = true;
if (role == "ROLE_RADIOLOGIST") {
userIsTechnician = false;
}
//userIsTechnician
Map data1 = {
'authUserPk': userPk,
"startDate": startDate,
"endDate": currentTime,
"userIsTechnician": userIsTechnician
};
String body1 = json.encode(data1);
final response = await http.post(url,
headers: <String, String>{
'Content-Type': 'application/json; charset=UTF-8'
},
body: body1);
if (response.statusCode == 200) {
// If the server did return a 200 OK response,
// then parse the JSON.
if (userIsTechnician) {
Technici r = Technici.fromJson(json.decode(response.body));
this.numCompleted = r.numCompleted;
this.numPending = r.numPending;
print("tech===============" + response.body);
} else {
Radiologist r = Radiologist.fromJson(json.decode(response.body));
this.numCompleted = r.numCompleted;
this.numPending = r.numPending;
print("rad===============" + response.body);
}
//return Radiologist.fromJson(json.decode(response.body));
} else {
// If the server did not return a 200 OK response,
// then throw an exception.
throw Exception('Failed to load report count');
}
}
/// The name associated with the UI isolate's [SendPort].
String isolateName = 'isolate';
// /// A port used to communicate from a background isolate to the UI isolate.
final ReceivePort port = ReceivePort();
static SendPort uiSendPort;
Future<void> showNotification(data) async {
print(data);
var rand = Random();
var hash = rand.nextInt(100);
DateTime now = DateTime.now().toUtc().add(Duration(seconds: 1));
await notify.singleNotification(
now,
"Hello $hash",
"This is hello message",
hash,
);
}
// The callback for our alarm
Future<void> callback() async {
print('Alarm fired!');
// // This will be null if we're running in the background.
uiSendPort ??= IsolateNameServer.lookupPortByName(isolateName);
uiSendPort?.send("hi");
}
#override
void initState() {
super.initState();
// _future =
// fetchReportStatus();
// repeatNotification();
//_showNotification();
port.listen((data) async => await showNotification(data));
runAlarm();
WidgetsBinding.instance.addPostFrameCallback((_) async {
timeController1.text = await getPrefForTimer3();
});
getPrefForTimer2().then((s) {
task1 = s.toString();
});
getPrefForDuration().then((s) {
_selectedParam = s;
});
var androidInitilize = new AndroidInitializationSettings('app_icon');
var iOSinitilize = new IOSInitializationSettings();
var initilizationsSettings = new InitializationSettings(
android: androidInitilize, iOS: iOSinitilize);
fltrNotification = new FlutterLocalNotificationsPlugin();
fltrNotification.initialize(initilizationsSettings,
onSelectNotification: notificationSelected);
setDurationAndShowNotification();
// timerNotification(false);
}
void runAlarm() async {
await AndroidAlarmManager.periodic(
Duration(seconds: 1),
0,
callback,
rescheduleOnReboot: true,
exact: true,
wakeup: true,
);
print("OK");
}
Future<String> getPrefForTimer3() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int taskInt = prefs.getInt("task") ?? 30;
this.task1 = taskInt.toString();
return this.task1;
}
Future<int> getPrefForTimer1() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int taskInt = prefs.getInt("task") ?? 30;
this.task1 = taskInt.toString();
return taskInt;
}
Future<int> getPrefForTimer2() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
int taskInt = prefs.getInt("task") ?? 30;
task1 = taskInt.toString();
return taskInt;
}
setPrefForTimer1(int val) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
// setState(() {
prefs.setInt("task", val);
// });
}
Future<String> getPrefForDuration() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
setState(() {
duration = prefs.getString("duration").toString() ?? "Minutes";
});
return duration;
}
setPrefForDuration(String val) async {
SharedPreferences prefs = await SharedPreferences.getInstance();
//setState(() {
prefs.setString("duration", val);
// });
}
getPrefForNumPending() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
this.numPending1 = prefs.getInt("numPending").toString() ?? "-1";
}
setPrefForNumPending(String val) async {
if (val == null) {return;}
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt("numPending", int.parse(val));
}
getPrefForNumCompleted() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
this.numCompleted1 = prefs.getInt("numCompleted").toString() ?? "0";
}
setPrefForNumCompleted(String val) async {
if (val == null) {return;}
SharedPreferences prefs = await SharedPreferences.getInstance();
prefs.setInt("numCompleted", int.parse(val));
}
Future<bool> getUserIsTechnician() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String role = prefs?.getString("role");
bool userIsTechnician = true;
if (role == "ROLE_RADIOLOGIST") {
userIsTechnician = false;
}
return userIsTechnician;
}
timerNotificationRestart() {
if (this.task1 != null) {
setPrefForTimer1(int.parse(this.task1));
}
//timerNotification(true);
}
timerNotification1(bool stop) {
if (stop && this.timer != null) {
this.timer.cancel();
}
//getPrefForTimer1();
if (this.task1 == null) {
this.task2 = 30;
} else {
this.task2 = int.parse(task1);
}
Duration duration;
if (_selectedParam == "Hour") {
duration = Duration(hours: this.task2);
} else {
duration = Duration(minutes: this.task2);
}
bool isStopped = false;
this.timer = Timer.periodic(duration, (timer1) async {
// Make a local notification
if (isStopped) {
timer1.cancel();
}
var androidPlatformChannelSpecifics = AndroidNotificationDetails(
'your channel id', 'your channel name', 'your channel description',
importance: Importance.max,
priority: Priority.high,
ticker: 'ticker');
var iOSPlatformChannelSpecifics = IOSNotificationDetails();
var platformChannelSpecifics = NotificationDetails(
android: androidPlatformChannelSpecifics,
iOS: iOSPlatformChannelSpecifics);
await fetchReportStatus();
var value = await getUserIsTechnician();
if (!value) {
getPrefForNumPending();
bool sendNotification = false;
if (this.numPending1 != null && this.numPending != null
&& this.numPending != this.numPending1) {
sendNotification = true;
} else if (this.numPending1 == null) {
sendNotification = true;
}
setPrefForNumPending(this.numPending);
if (sendNotification) {
await fltrNotification.show(
0, "",
// "# Report Completed :" +
// this.numCompleted +
// "\n",
"# Report Pending : " +
this.numPending, platformChannelSpecifics,
payload: 'item x');
}
} else {
getPrefForNumCompleted();
bool sendNotification = false;
if (this.numCompleted1 != null && this.numCompleted != null
&& this.numCompleted != this.numCompleted1) {
sendNotification = true;
} else if (this.numCompleted1 == null) {
sendNotification = true;
}
setPrefForNumCompleted(this.numCompleted);
if (sendNotification) {
await fltrNotification.show(
0, "",
// "# Report Completed :" +
// this.numCompleted +
// "\n",
"# Report Completed : " +
this.numCompleted, platformChannelSpecifics,
payload: 'item x');
}
}
});
}
setDurationAndShowNotification() async {
await fltrNotification.cancelAll();
//await fltrNotification.cancel(1);
showNotification1();
}
showNotification1() async {
// var scheduledNotificationDateTime =
// new DateTime.now().add(new Duration(seconds: 5));
// Show a notification every minute with the first appearance happening a minute after invoking the method
var androidPlatformChannelSpecifics =
new AndroidNotificationDetails('repeating channel id 1',
'repeating channel name 1', 'repeating description 1');
var iOSPlatformChannelSpecifics =
new IOSNotificationDetails();
var platformChannelSpecifics = new NotificationDetails(
android: androidPlatformChannelSpecifics, iOS: iOSPlatformChannelSpecifics);
await fetchReportStatus();
var value = await getUserIsTechnician();
var interval;
if (_selectedParam == "Hour") {
interval = RepeatInterval.hourly;
} else {
interval = RepeatInterval.everyMinute;
}
if (!value) {
await getPrefForNumPending();
bool sendNotification = false;
if (this.numPending1 != null && this.numPending != null
&& this.numPending != this.numPending1) {
sendNotification = true;
} else if (this.numPending1 == null) {
sendNotification = true;
}
print("=========== About to send Rad Notification");
print("num pending ===== " + this.numPending);
print("num pending1 ===== " + this.numPending1);
print("sendNotification ===== " + sendNotification.toString());
setPrefForNumPending(this.numPending);
if (sendNotification) {
await fltrNotification.periodicallyShow(
0, "eClarity Reprot Status",
// "# Report Completed :" +
// this.numCompleted +
// "\n",
"# Report Pending : " + this.numPending,
interval, platformChannelSpecifics,
);
}
} else {
getPrefForNumCompleted();
bool sendNotification = false;
if (this.numCompleted1 != null && this.numCompleted != null
&& this.numCompleted != this.numCompleted1) {
sendNotification = true;
} else if (this.numCompleted1 == null) {
sendNotification = true;
}
print("=========== About to send Rad Notification");
print("num completed ===== " + this.numCompleted.toString());
print("num completed1 ===== " + this.numCompleted1.toString());
print("sendNotification ===== " + sendNotification.toString());
setPrefForNumCompleted(this.numCompleted);
if (sendNotification) {
await fltrNotification.periodicallyShow(
0, "eClarity Reprot Status",
// "# Report Completed :" +
// this.numCompleted +
// "\n",
"# Report Completed : " +
this.numCompleted, interval, platformChannelSpecifics,
);
}
}
}```
I have a list which is fetched from the server but when we get outside of the fetching method the list is initialized to default. I have noticed that the problem is with the notifyListeners() as here:
Does notifyListeners() sometimes complete asynchronously?
Interestingly, inside the fetching method the the list is ok.
class Products with ChangeNotifier {
List<Product> _loadedProducts = [];
Future<void> fetchAndSetProducts() async {
try {
var response =
await http.get(StoreServer.serverAddress + '/products.json');
final extractedData = json.decode(response.body) as Map<String, dynamic>;
List<Product> extractedList = [];
extractedData.forEach((key, value) {
_loadedProducts.add(Product(
id: key,
imageUrl: value['imageUrl'],
title: value['title'],
price: value['price'],
description: value['description'],
isFavorite: value['isFavorite']));
});
_loadedProducts = extractedList;
notifyListeners();
await Future.delayed(Duration(seconds: 1));
} catch (error) {
throw error;
}
}
#override
void didChangeDependencies() {
if (!isInit) {
setState(() {
isLoading = true;
});
test = Provider.of<Products>(context);
test.fetchAndSetProducts().then((_) {
print("fetched");
setState(() {
isLoading = false;
isInit = true;
});
});
}
super.didChangeDependencies();
}
You have a logic error. Check my comments..!
class Products with ChangeNotifier {
List<Product> _loadedProducts = [];
Future<void> fetchAndSetProducts() async {
try {
var response =
await http.get(StoreServer.serverAddress + '/products.json');
final extractedData = json.decode(response.body) as Map<String, dynamic>;
List<Product> extractedList = []; // it is an empty list
extractedData.forEach((key, value) {
_loadedProducts.add(Product( // you add elements to _loadedProducts
id: key,
imageUrl: value['imageUrl'],
title: value['title'],
price: value['price'],
description: value['description'],
isFavorite: value['isFavorite']));
});
_loadedProducts = extractedList; // you reassign _loadedProducts to the empty list "extractedList"
notifyListeners();
await Future.delayed(Duration(seconds: 1));
} catch (error) {
throw error;
}
}
Am I right? I think you have that error!
I have created an authentication service based on Jason Watmore's example, and it works fine in the Ripple emulator for Android, logs in, saves the token to jStorage, and uses it to access other web services.
It was also working in the actual Android phone till yesterday.
I have tested to see if jStorage is working in my Android phone (it is), and I have tried removing all the app's data using the Settings.
Any idea why the Android phone is not fetching the token from the Moodle server (but the emulator is fetching it)?
Here's my service:
myApp.factory('AuthenticationService',
['$http', '$cookies', '$rootScope', '$timeout', '$log',
function ($http, $cookies, $rootScope, $timeout, $log) {
var service = {};
service.Login = function (username, password, callback) {
//$log.info('Login function called');
if ((username.length && password.length) && (username !== '' && password != '')) {
var loginUrl = 'https://my.moodle.url/local/token.php';
// use $.param jQuery function to serialize data from JSON
var data = $.param({
username: username,
password: password,
service: 'brookesid_ws'
});
//$log.info(data);
var config = {
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=utf-8;'
}
}
$http.post(loginUrl, data, config)
.success(function (data, status, headers, config) {
$log.info(data);
myToken = data.token;
dataString = JSON.stringify(data);
if (dataString.indexOf('error') > 0) {
$rootScope.className = 'error';
$rootScope.PostDataResponse = 'Invalid user credentials, please try again';
$rootScope.isAuthenticated = false;
$rootScope.dataLoading = false;
}
else {
$.jStorage.set('session', myToken, { TTL: 28800000 });
//$cookies.put('session', myToken);
}
$rootScope.isAuthenticated = true;
// $log.info('isAuthenticated = true');
callback(dataString);
})
.error(function (data, status, header, config) {
$rootScope.isAuthenticated = false;
$rootScope.ResponseDetails = "data: " + data +
"<br />status: " + status +
"<br />headers: " + header +
"<br />config: " + config;
responsedata = JSON.stringify(data);
callback(responsedata);
$log.info('error: '+responsedata);
});
} else {
$rootScope.className = 'error';
$rootScope.isAuthenticated = false;
$rootScope.PostDataResponse = 'Please enter a username and password';
}
};
service.SetCredentials = function (sessionToken) {
var JSONObject = JSON.parse(sessionToken);
var key = 'token';
myToken = JSONObject[key];
$log.info('session Token: ' + sessionToken);
$log.info('myToken: ' + myToken);
$rootScope.globals = {
currentUser: {
token: myToken
}
};
$http.defaults.headers.common['Authorization'] = 'Basic ' + sessionToken; // jshint ignore:line
//retrieve last login date and then update it
$rootScope.lastLogin = $.jStorage.get('lastLogin', '');
var today = new Date();
epochToday = Math.round(today.getTime() / 1000);
$.jStorage.set('lastLogin', epochToday, { TTL: 28800000 });
//$log.info('Rootscope Last Login: '+$rootScope.lastLogin);
$.jStorage.set('globals', $rootScope.globals, { TTL: 28800000 });
$.jStorage.set('session', myToken, { TTL: 28800000 });
$.jStorage.set('loginStatus', 'logged in', { TTL: 28800000 });
$log.info('Token (jStorage) ' + $.jStorage.get('session', ''));
//$log.info('Last login (jStorage) ' + $.jStorage.get('lastLogin', ''));
//$log.info('Login status (jStorage) ' + $.jStorage.get('loginStatus', ''));
};
service.ClearCredentials = function () {
$rootScope.globals = {};
//$cookies.remove('globals');
//$cookies.remove('session');
$.jStorage.deleteKey('globals');
$.jStorage.deleteKey('session');
$http.defaults.headers.common.Authorization = 'Basic ';
};
return service;
}])
Here is my login Controller:
.controller('loginCtrl',
['$scope', '$rootScope', '$location', 'AuthenticationService', '$routeParams', '$http',
function ($scope, $rootScope, $location, AuthenticationService, $routeParams, $http) {
$scope.login = function () {
$scope.dataLoading = true;
AuthenticationService.Login($scope.username, $scope.password, function (response) {
responsedata = JSON.stringify(response);
/* error handling*/
if (responsedata.indexOf('error') > 0 || responsedata.indexOf('invalid') > 0) {
$scope.error = response.message;
$rootScope.className = 'error';
$rootScope.dataLoading = false;
} else {
AuthenticationService.SetCredentials(response);
console.log('response: '+response);
$location.path('/home');
};
});
};
$scope.logout = function () {
$rootScope.dataLoading = false;
$rootScope.hideMe = true;
$rootScope.PostDataResponse = '';
$rootScope.ResponseDetails = '';
//alert('logging out');
AuthenticationService.ClearCredentials();
};
$scope.showMenuPanel = function () {
$scope.hideMenuPanel = false;
};
$scope.doHideMenuPanel = function () {
$scope.hideMenuPanel = true;
$rootScope.PostDataResponse = '';
};
}])
It was actually the Cordova app's URL whitelisting settings that were causing the problem.