i have a weird problem with dio package for flutter on iOS device.
i wrote an app which sends a GET request to a url. everything works perfectly on Android but looks like the request doesn't go thru on iOS.
nothing happens no error nothing at all. i had the same problem on android too but i found out that i forgot to add INTERNET permission into my manifest file. i'm guessing the same situation occurring in iOS.
is there any INTERNET permission in iOS that i need to add info.plist ?
void _checkVersionAndPreferences() async {
SharedPreferences prefs = await SharedPreferences.getInstance();
String prefsRes = prefs.getString('access_token') ?? '';
String buildNumber = _packageInfo.buildNumber ?? '1';
Dio dio = Dio();
_cancelToken = CancelToken();
Future.delayed(Duration(seconds: 10), () {
if (_getRequestSuccess == false) {
_cancelToken.cancel();
_checkVersionAndPreferences();
_showSnackBar(
content: 'تلاش مجدد برای برقراری ارتباط',
duration: Duration(seconds: 3),
leading: Icon(Icons.refresh, color: Colors.black));
}
});
Response response = await dio.get(
'https://snapmelk.com/api/v1/panel/checkVersion/' + buildNumber,
cancelToken: _cancelToken);
try {
Map respJson = jsonDecode(response.data);
setState(() {
_getRequestSuccess = true;
});
if (respJson['error']) {
_showSnackBar(
content:
(respJson['errorMsg'] != null && respJson['errorMsg'] != '')
? respJson['errorMsg']
: 'خطا در اتصال دریافت اطلاعات آخرین نسخه',
leading: Icon(Icons.warning),
backgroundColor: Colors.red,
textColor: Colors.white);
} else {
if (respJson['NewUpdate']) {
_checkDialogAnswer(respJson, prefsRes);
} else {
_checkPrefs(prefsRes);
}
}
} catch (e) {
_showSnackBar(
content: 'خطا در اتصال با سرور. لطفا در زمانی دیگر مراجعه کنید',
leading: Icon(Icons.warning),
backgroundColor: Colors.red,
textColor: Colors.white);
}
}
There's no network permission that you need to define for iOS in Flutter while using dio. To debug, I suggest logging the response from the executed requested with debugPrint(${response.data});, or if the request using dio itself could possibly throw an error, you might want to consider wrapping it inside the try-catch block as well.
Related
I have several separate requests to my websockets and I wanted to get data from all of these using parametrs. AnimalID in getImageAnimal - parametrs for getting image of fixed animals. here is requests I am trying to make
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
List fixationAnimals = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetAnimalFixations')
.then((value) => fixation = value as List)
.then((value1) => hubConnection1.invoke('GetImageAnimal',
args: [imageId ?? '']).then((value2) => imageId == value2));
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(imageId);
print(fixation);
return fixation;
in GetAnimalFixations I have such field:
[
{
"id": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"animalId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"name": "string",
"fixedTimeAnimal": "2022-08-23T07:25:29.898Z",
"imageId": "3fa85f64-5717-4562-b3fc-2c963f66afa6"
}
]
I found out how to do it. Everything i post here is just for testing pupose, just to make sure it is really works. Please do not write like below (as I know it is a bad decision to manipulate FutureBuilder as I did below, but it is ONLY for testing purpose)
I have several requests and one of them are depended on the other one.
GetAnimalFixations (the main request)-> GetImageAnimal (depended one, where I should put arguments imageId from the first request)
STEP 1
Create two separate requests with methods above like that:
Future<List> fetchAnimalFixation() async {
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
List fixationAnimals = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetAnimalFixations')
.then((value) => fixation = value as List);
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(fixation);
return fixation;
}
Second request:
Future<String> fetchAnimalImage(String imageId) async {
final hubConnection1 = HubConnectionBuilder()
.withUrl(
'http://10.10:8000/animalHub',
options: httpConnectionOptions,
)
.build();
await hubConnection1.start();
String image = [];
if (hubConnection1.state == HubConnectionState.Connected) {
await hubConnection1
.invoke('GetImageAnimal', args[imageId])
.then((value) => image= value as String);
}
hubConnection1.onclose(({error}) {
logger?.finer(error);
});
print(image);
return image;
}
STEP 2
First preparation is over, now we a go into the second steps to achive our goal:
You need to create the screen where all data will be displayed. For my testing purpose (again: ONLY for testing and small piece of data) I use future builder:
body: FutureBuilder<List>(
future: AllFixation().fetchAnimalFixation(),
builder: (context, snapshot) {
return ListView.builder(
itemCount: 2,
itemBuilder: (context, index) {
return Card(
child: Column(
children: [
Text(snapshot.data?[index]['name'].toString() ?? ''),
FutureBuilder<String?>(
future: AllFixation().fetchAnimalImage(
snapshot.data?[index]['imageId'].toString() ??
''),
builder: (context, snapshotImage) {
Uint8List bytes =
base64.decode(snapshotImage.data ?? '');
return Image.memory(bytes);
}),
Voila! All is ready. You can use and enjoy the picture and the name of the animal. All this via websocket and using signalR package. I higly recommend to use some state management and think that you are going to do next, because it is the solid piece of code and if someday it will need to change, it turn out to be the biggest pain.
I have the following ad Repository
import 'package:google_mobile_ads/google_mobile_ads.dart';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:my_app/data/api/constants.dart';
class AdMobRepository {
late String liveBannerAdId;
late String liveInterstitualAdId;
late String liveRewardedAdId;
AdMobRepository() {
if (Platform.isAndroid) {
liveBannerAdId = Constants.androidBannedAdId;
liveInterstitualAdId = Constants.androidInterstitualAdId;
liveRewardedAdId = Constants.androidRewardedAdId;
} else if (Platform.isIOS) {
liveBannerAdId = Constants.iosBannerAdId;
liveInterstitualAdId = Constants.iosInterstitualAdId;
liveRewardedAdId = Constants.iosRewardedAdId;
} else {
liveBannerAdId = "";
liveInterstitualAdId = "";
liveRewardedAdId = "";
}
}
BannerAd getBannerAd({
required AdSize size,
void Function(Ad, LoadAdError)? onFailedLoad,
void Function(Ad)? onLoad,
void Function(Ad)? onAdOpened,
void Function(Ad)? onAdImpression,
}) {
return BannerAd(
adUnitId: kReleaseMode ? liveBannerAdId : BannerAd.testAdUnitId,
request: AdRequest(),
size: size,
listener: BannerAdListener(
onAdFailedToLoad: onFailedLoad ?? onFailedLoadFallback,
onAdLoaded: onLoad,
onAdImpression: onAdImpression,
onAdOpened: onAdOpened,
),
);
}
void onFailedLoadFallback(Ad ad, LoadAdError error) {
ad.dispose();
}
void getInterstitualAd({required void Function(LoadAdError) onFailedLoad, void Function(InterstitialAd)? onLoad}) {
InterstitialAd.load(
adUnitId: kReleaseMode ? liveInterstitualAdId : InterstitialAd.testAdUnitId,
request: AdRequest(),
adLoadCallback: InterstitialAdLoadCallback(
onAdLoaded: onLoad ?? onInterstitialAdLoadedFallback,
onAdFailedToLoad: onFailedLoad,
),
);
}
void onInterstitialAdLoadedFallback(InterstitialAd ad) {
ad.fullScreenContentCallback = FullScreenContentCallback(
onAdDismissedFullScreenContent: (ad) => ad.dispose(), onAdFailedToShowFullScreenContent: (ad, error) => ad.dispose());
}
void getRewardAd({required String userId, required void Function(LoadAdError) onFailedLoad, void Function(RewardedAd)? onLoad}) {
RewardedAd.load(
adUnitId: kReleaseMode ? liveRewardedAdId : RewardedAd.testAdUnitId,
request: AdRequest(),
rewardedAdLoadCallback: RewardedAdLoadCallback(
onAdLoaded: onLoad ?? onRewardedAdLoadedFallback,
onAdFailedToLoad: onFailedLoad,
),
serverSideVerificationOptions: ServerSideVerificationOptions(userId: userId),
);
}
void onRewardedAdLoadedFallback(RewardedAd ad) {
ad.fullScreenContentCallback = FullScreenContentCallback(
onAdDismissedFullScreenContent: (ad) => ad.dispose(), onAdFailedToShowFullScreenContent: (ad, error) => ad.dispose());
}
}
And I have the following widget for banner ads
class MyBannerAd extends StatefulWidget {
const MyBannerAd();
#override
_MyBannerAdState createState() => _MyBannerAdState();
}
class _MyBannerAdState extends State<MyBannerAd> {
late AdSize adSize;
late AdMobRepository adRepository;
late AnalyticsRepository analyticsRepository;
bool adLoaded = false;
BannerAd? anchoredBanner;
#override
void initState() {
super.initState();
adRepository = context.read<AdMobRepository>();
analyticsRepository = context.read<AnalyticsRepository>();
if (SizerUtil.deviceType != DeviceType.mobile && SizerUtil.orientation == Orientation.portrait) {
adSize = AdSize.leaderboard;
} else {
adSize = AdSize.largeBanner;
}
final bannerAd = adRepository.getBannerAd(
size: adSize,
onFailedLoad: (ad, error) {
print('banner ad failed to load: $error');
ad.dispose();
},
onLoad: (ad) {
setState(() {
adLoaded = true;
anchoredBanner = ad as BannerAd?;
});
},
onAdImpression: (_) {
analyticsRepository.sendBannerAdShownEvent();
},
onAdOpened: (_) {
analyticsRepository.sendBannerAdClickEvent();
},
);
bannerAd.load();
}
#override
void dispose() {
super.dispose();
anchoredBanner?.dispose();
}
#override
Widget build(BuildContext context) {
return BlocBuilder<SubscriptionBloc, SubscriptionState>(
builder: (context, state) {
final isLoaded = !adLoaded;
if (isLoaded || state.hasSubscribed || anchoredBanner == null) return SizedBox.shrink();
return Container(
color: Colors.transparent,
width: anchoredBanner!.size.width.toDouble(),
height: anchoredBanner!.size.height.toDouble(),
child: Center(
child: Container(
color: Colors.white,
child: AdWidget(
ad: anchoredBanner!,
),
),
),
);
},
);
}
}
But on IOS it is always showing test ads. How can this be when the app is built with flutter release mode with flutter build ios --release? The app is currently in review and I was thinking that these ads would stop being test ads whenever it is live on the app store.
But Apple sent us the following message
We noticed that your app or its screenshots include test
advertisements. Apps or metadata items that include features that are
for test or demonstration purposes are not appropriate for the App
Store.
Next Steps
To resolve this issue, please revise your app to complete, remove, or
fully configure any partially implemented features. Please ensure your
screenshots do not include any images of demo, test, or other
incomplete content
So how do I get rid of the test ads? Did I miss some XCode setting or?
I am using
flutter: 2.5.3
google_mobile_ads: ^0.13.4
And I also added the GADApplicationIdentifier to my info.plist
<key>GADApplicationIdentifier</key>
<string>{here I have the app Id}</string>
And I am testing on a real device with the testflight build
Side Note:
In admob setting I have added the following test IDFA
00000000-0000-0000-0000-000000000000
which seems to work for Test ads on all IOS devices.
Turns out I needed to remove the 00000000-0000-0000-0000-000000000000 from the test settings on admob. I no longer recieve test ads after that, but I do recieve ads in the release build now.
You don't need to make any code changes.
Next Steps
To resolve this issue, please revise your app to complete, remove, or
fully configure any partially implemented features. Please ensure
your screenshots do not include any images of demo, test, or other
incomplete content
To resolve above rejection, all you need to do is remove banner advertisement from your screenshots and submit for approval again.
I'm running into a very odd error on M1 Macbook when developing for iOS devices. This is a simple picture picking functionality within the Flutter template (full code here). This works perfectly fine on Android, but not Apple iPhone simulator (running iOS 14 on simulator), where it gives an error that returns null.
floatingActionButton: FloatingActionButton(
onPressed: () async {
await _getImageFromGallery();
//print('Image selected');
},
tooltip: 'Increment',
child: Icon(Icons.add),
),
Below is the image picking function for image_picker: ^0.8.3+1. I tried a few different versions with minimal success.
final picker = ImagePicker();
File? imagePath;
Future _getImageFromGallery() async {
try {
var pickedFile = await picker.pickImage(source: ImageSource.gallery);
print('pickedFile: ${pickedFile!.path}');
setState(() {
imagePath = File(pickedFile.path);
});
} catch (error) {
print('error: $error');
}
}
If anyone could help that would be much appreciated!
Might be a permission problem, you can check out the permission_handler package and see if that fixes your issue.
Add This Permission in Your Android mainfest File
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /><uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
For IOS Add This Line to /ios/Runner/Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>Microphone Access Required</string>
<key>NSMotionUsageDescription</key>
<string>Motion Usage Required</string>
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Please allow access to photo library so you can easily upload your photos.</string>
<key>NSPhotoLibraryUsageDescription</key>
<string>Please allow access to photo library so you can easily upload your photos.</string>
Add this line in pubspec
image_picker : ^0.8.4
This is code i am currently using for my app.
onTap: () async{
String path = await getImagePath();
print(path);
if(path != "Exceed Limit" && path!="noselect")
{
File imagefile = File(path);
}
}
Future<String> getImagePath() async{
final ImagePicker _picker = ImagePicker();
// Pick an image
final XFile? image = await _picker.pickImage(
source: ImageSource.gallery);
if (image?.path != null) {
print(image!.path);
int size = await image.length();
double sizevalueinmb = size / (1024 * 1024);
if (sizevalueinmb < 5) {
String path = image.path;
return path;
}
else {
print("Your Image File Exceeded Size Limit of 5mb !");
return "Exceed Limit";
}
}
else {
print("User Not Selected Any Image");
return "noselect";
}
}
I tried to share data between Safari browser and standalone PWA on iPhone12 with iOS 14.3.
The information, that this should work are here: https://firt.dev/ios-14/
I#ve tried this: https://www.netguru.com/codestories/how-to-share-session-cookie-or-state-between-pwa-in-standalone-mode-and-safari-on-ios
Without success.
Are there any suggestions to running this? Or is it not possible ...
This is the code
const CACHE_NAME = "auth";
const TOKEN_KEY = "token";
const FAKE_TOKEN = "sRKWQu6hCJgR25lslcf5s12FFVau0ugi";
// Cache Storage was designed for caching
// network requests with service workers,
// mainly to make PWAs work offline.
// You can give it any value you want in this case.
const FAKE_ENDPOINT = "/fake-endpoint";
const saveToken = async (token: string) => {
try {
const cache = await caches.open(CACHE_NAME);
const responseBody = JSON.stringify({
[TOKEN_KEY]: token
});
const response = new Response(responseBody);
await cache.put(FAKE_ENDPOINT, response);
console.log("Token saved! 🎉");
} catch (error) {
// It's up to you how you resolve the error
console.log("saveToken error:", { error });
}
};
const getToken = async () => {
try {
const cache = await caches.open(CACHE_NAME);
const response = await cache.match(FAKE_ENDPOINT);
if (!response) {
return null;
}
const responseBody = await response.json();
return responseBody[TOKEN_KEY];
} catch (error) {
// Gotta catch 'em all
console.log("getToken error:", { error });
}
};
const displayCachedToken = async () => {
const cachedToken = await getToken();
console.log({ cachedToken });
};
// Uncomment the line below to save the fake token
// saveToken(FAKE_TOKEN);
displayCachedToken();
Without success means no result, i've tried to set data in safari and get them in standalone pwa
I want to save user preferences using Flutter's SharedPreference. But the registered preferences are ALL null at new start (when app have been closed, not unistalled).
settings.dart :
import 'package:flutter/material.dart';
import 'package:shared_preferences/shared_preferences.dart';
class SettingsPage extends StatefulWidget{
#override
_SettingsPageState createState() => new _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage>{
bool _param1IsON;
bool _param2IsON;
bool _param3IsON;
#override
void initState() {
super.initState();
_param1IsON = false;
_param2IsON = false;
_param3IsON = false;
loadPreferences();
}
Future<Null> loadPreferences() async {
SharedPreferences _preferences = await SharedPreferences.getInstance();
if(_preferences.getBool('setted') == null || !_preferences.getBool('setted'))
SharedPreferences.setMockInitialValues({}); // Library fix line
bool param1IsON = _preferences.getBool('param1IsON');
bool param2IsON = _preferences.getBool('param2IsON');
bool param3IsON = _preferences.getBool('param3IsON');
setState(() {
_param1IsON = (param1IsON != null)? param1IsON : _param1IsON;
_param2IsON = (param2IsON != null)? param2IsON : _param2IsON;
_param3IsON = (param3IsON != null)? param3IsON : _param3IsON;
});
_preferences.setBool('setted', true);
}
Future<Null> setBoolSettings(String key, bool value) async {
switch(key){
case 'param1IsON':
setState(() {
_param1IsON = value;
});
break;
case 'param2IsON':
setState(() {
_param2IsON = value;
});
break;
case 'param3IsON':
setState(() {
_param3IsON = value;
});
break;
default:
print("Unknown settings '$key'");
}
SharedPreferences _preferences = await SharedPreferences.getInstance();
await _preferences.setBool(key, value);
}
#override
Widget build(BuildContext context) {
return new ListView(
children: <Widget>[
new ListTile(
title: new Text(Param 1'),
trailing: new Switch(value: _param1IsON,
onChanged: (bool newValue) {
setBoolSettings('param1IsON', newValue);
}),
),
new ListTile(
title: new Text('Param 2'),
trailing: new Switch(value: _param2IsON,
onChanged: (bool newValue) {
setBoolSettings('param2IsON', newValue);
}),
),
new ListTile(
title: new Text('Param 3'),
trailing: new Switch(value: _param3IsON,
onChanged:
(bool newValue) {
setBoolSettings('param3IsON', newValue);
}),
),
]
);
}
}
What I get:
At lunch 3 parameters are false. If I turn 'ON' one of them, wait 2s (it is not an async problem), then close the app and Start again... All of my parameters are false.
What I want:
At lunch 3 parameters are false. I turn 'ON' one of them. I close the app. Start again. The previous param I turned 'ON' is still true.
I had the same issue and fixed it in Android/../MainActivity.java by adding at the top:
import io.flutter.plugins.GeneratedPluginRegistrant;
As well as under super.onCreate(savedInstanceState);
GeneratedPluginRegistrant.registerWith(this);
The problem comes from using
SharedPreferences.setMockInitialValues({});
I got it from Flutter Test MissingPluginException but it seems to clear all the shared preferences.
However if you remove SharedPreferences.setMockInitialValues({}); and you don't have the two lines above in MainActivity.java, you'll get:
MissingPluginException(No implementation found for method getAll on
channel flutter: plugins.flutter.io/shared_preferences)
I hope it helps!
Hi I also faced the same issue. Did so many things. nothing helped .This may help someone.First thing ,followed this url and did the changes
1.https://github.com/flutter/flutter/wiki/Upgrading-pre-1.12-Android-projects
2.Run the command
flutter upgrade
3.Changed the shared preferences plugin in pubspec.yaml file
shared_preferences: ">=0.5.10 <2.0.0"
4.Deleted the pub cache from installed flutter location
C:\Users\myuser\AppData\Local\Pub\Cache\hosted\pub.dartlang.org
5.flutter build apk --debug
6.flutter build apk --profile
7.flutter run --release (if I run directly this command its throwing error like debug.jar not found , so I ran command 5 and 6 )
Command 7 is for - To Verify whether its working perfectly in release mode.
Finally I tried to generate app build without shrinking the code. then it worked
flutter build apk --no-shrink
flutter build appbundle --no-shrink