Flutter Local Notifications Plugin: createNotificationChannel function not working - flutter

I am trying to create an android notification channel with flutter_local_notifications: ^5.0.0+4
Put it looks like it does not work as should be.
I wrote a code using getNotificationChannels to verify if the channel was created successfully
Please take a look to my code
Future<void> _getNotificationChannels() async {
await flutterLocalNotificationsPlugin.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()?.createNotificationChannel(AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
'This channel is used for important notifications', // description
importance: Importance.max,
));
final Widget notificationChannelsDialogContent =
await _getNotificationChannelsDialogContent();
await showDialog<void>(
context: context,
builder: (BuildContext context) => AlertDialog(
content: notificationChannelsDialogContent,
actions: <Widget>[
TextButton(
onPressed: () {
Navigator.of(context).pop();
},
child: const Text('OK'),
),
],
),
);
}
Future<Widget> _getNotificationChannelsDialogContent() async {
try {
final List<AndroidNotificationChannel>? channels =
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()!
.getNotificationChannels();
return Container(
width: double.maxFinite,
child: ListView(
children: <Widget>[
const Text(
'Notifications Channels',
style: TextStyle(fontWeight: FontWeight.bold),
),
const Divider(color: Colors.black),
if (channels?.isEmpty ?? true)
const Text('No notification channels')
else
for (AndroidNotificationChannel channel in channels!)
Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: <Widget>[
Text('id: ${channel.id}\n'
'name: ${channel.name}\n'
'description: ${channel.description}\n'
'groupId: ${channel.groupId}\n'
'importance: ${channel.importance.value}\n'
'playSound: ${channel.playSound}\n'
'sound: ${channel.sound?.sound}\n'
'enableVibration: ${channel.enableVibration}\n'
'vibrationPattern: ${channel.vibrationPattern}\n'
'showBadge: ${channel.showBadge}\n'
'enableLights: ${channel.enableLights}\n'
'ledColor: ${channel.ledColor}\n'),
const Divider(color: Colors.black),
],
),
],
),
);
} on PlatformException catch (error) {
return Text(
'Error calling "getNotificationChannels"\n'
'code: ${error.code}\n'
'message: ${error.message}',
);
}
}
As you can see I am using createNotificationChannel to create a notifications channel then I am using getNotificationChannels to see if the channel was created successfully.
I am getting always "No notification channels" as a result in the dialog.
I do not know what is wrong with my code,
Thanks for your help

The problem was the Android version that I am using for testing,
Notification channels is a concept that is specific Android 8 or newer, which is why the API docs state that the method to create channels is only for those versions of Android

Related

PlatformException(multiple_request, Cancelled by a second request, null, null) in imagePicker

I am using a riverpod provider class to handle picking of image from gallery. However, once an image is picked, I get the error: PlatformException(multiple_request, Cancelled by a second request null, null). Not sure where a second request is coming from. More importantly, no image is applied to my placeholder (CircleAvartar) due to this unknown cancellation.
Here are the two dart files in question and thanks for the help.
imageProvider file:
final myImageProvider =
ChangeNotifierProvider<ImageNotifier>((ref) => ImageNotifier());
class ImageNotifier extends ChangeNotifier {
ImageNotifier() : super();
final file = useState<File?>(null);
final imageFile = useState<XFile?>(null);
final imagePicker = ImagePicker();
Future<void> _pickImage(int type) async {
try {
XFile? userImage = await imagePicker.pickImage(
source: type == 1 ? ImageSource.gallery : ImageSource.camera,
imageQuality: 50,
);
imageFile.value = userImage;
// imageFile.value = XFile(userImage!.path);
} catch (e) {
print(e);
}
notifyListeners();
}
void showPicker(context) {
showModalBottomSheet(
backgroundColor: Theme.of(context).primaryColor,
context: context,
builder: (BuildContext bc) {
return SafeArea(
child: Wrap(
children: [
ListTile(
leading: const Icon(
Icons.photo_library,
color: Colors.white,
),
title: const Text(
'Photo Gallery',
style: TextStyle(fontSize: 22),
),
onTap: () => _pickImage(1),
),
ListTile(
leading: const Icon(
Icons.photo_camera,
color: Colors.white,
),
title: const Text(
'Camera',
style: TextStyle(fontSize: 22),
),
onTap: () => _pickImage(2),
),
ListTile(
leading: const Icon(
Icons.close,
color: Colors.white,
),
title: const Text(
'Cancel',
style: TextStyle(fontSize: 22),
),
onTap: () {
imageFile.value = null;
Navigator.of(context).pop();
},
),
],
),
);
},
);
notifyListeners();
}
AuthScreen dart file:
Widget build(BuildContext context, WidgetRef ref) {
final _passwordController = useTextEditingController();
final _passwordFocusScope = useFocusNode();
final _emailFocusScope = useFocusNode();
final _phoneFocusScope = useFocusNode();
final _confirmFocusScope = useFocusNode();
final _isVisible = useState<bool>(true);
var _authMode = useState<AuthMode>(AuthMode.login);
final imageProviderState = ref.watch(myImageProvider.notifier);
final deviceSize = MediaQuery.of(context).size;
final authMode = ModalRoute.of(context)?.settings.arguments as String;
switch (authMode) {
case 'login':
_authMode.value = AuthMode.login;
break;
case 'register':
_authMode.value = AuthMode.register;
break;
case 'google':
_authMode.value = AuthMode.google;
break;
case 'guest':
_authMode.value = AuthMode.guest;
break;
}
return Scaffold(
body: Stack(
children: [
Padding(
padding: const EdgeInsets.all(8.0),
child: Form(
key: _formKey,
child: SingleChildScrollView(
child: Column(
mainAxisAlignment: MainAxisAlignment.start,
children: [
const SizedBox(
height: 80,
),
Center(
child: _authMode.value == AuthMode.login
? const Text(
'Access Your Account',
style: TextStyle(
fontSize: 25,
),
)
: Row(
children: [
InkWell(
onTap: () =>
imageProviderState.showPicker(context),
// () => ref
// .read(myImageProvider.notifier)
// .showPicker(context),
child: CircleAvatar(
radius: 50,
backgroundImage:
imageProviderState.imageFile.value !=
null
? FileImage(
// File(ref
// .read(imageProvider.notifier)
// .imageFile
// .value!
// .path),
// )
File(imageProviderState
.imageFile.value!.path),
)
: null,
child: imageProviderState.imageFile.value ==
null
? const Icon(
Icons.camera,
// Icons.add_photo_alternate,
size: 30,
color: Colors.white,
)
: null,
),
),
After testing the code on a real device (iPhone and Android) I was able to select and attach a photo from gallery and camera to my form. The issue is with trying to do this task on a simulator even though one was able to do so once upon a time. Don't even bother anymore until Apple fixes this trouble. My advice is that you debug on a real device to ensure things are working as coded and you can return to the simulator/emulator afterwards. I lost a lot of time trying to make tis work on a simulator to no avail.
I have the latest Flutter 3.3.9 and Xcode 14.1 and this is still a problem. The workaround is very simple though after reading this issue. When using the image_picker, DO NOT pick the first image (with pink flowers):
In addition to my earlier answer and further tweaking with the dev in simulator environment, I just discovered that the uploaded image does show up upon a reload/restart. Weird but works if you must test in simulation mode. Simply restart and the uploaded image will show up. It is still a simulator issue though, IMHO.
It can help to double-click on the image you are selecting from the gallery instead of clicking only once.
For whatever reason, if I clicked only once, it would not show up and the same error as yours appeared.
If I clicked twice there was a short lag, but the image showed up.
Tested on iOS simulator - don't get this issue personally on my Android emulator.
I had this issue picking one of the default album images on my iOS simulator.
I was able to get around this by going to Safari, saving a png to Photos and then selecting that downloaded png in my Flutter app.
Thanks to Marc's post which pointed me in the right direction regarding HEIC support
Hi please have a look at this discussion:
https://github.com/flutter/flutter/issues/70436
on on the image picker package site we can see that it is a well known apple simulator issue. I would say that it should work for you on real devices (or try to test it only with particular pictures from iOS simulator photos)
Make sure ALLOW PHOTO ACCESS permission is set to either Selected Photos or All Photos. In my case, I had denied the permission so there was no error log on the console and the image picker was not opening.
PS I know it's not directly related to the SO's question but might be helpful if someone comes across this.
Don't bother about this issue much. This is just a simulator issue(mostly on iOS). Testing this on a real device is advisable.
I think it because it using 'pickimage' instead of 'pickMultiImage', so u are only allow to pick 1 image at a time, try to make ur 'imageFile' to null first before you pick another image.

Value detected of type null when passing data via routes in flutter

This error just been a while before the data passed successfully from loading screen.
The loading screen that passing data :
if i print the instance on here, the error is not appeared
void setupWorldTime() async {
WorldTime instance = WorldTime(location: 'Jawa Timur', flag: 'jakarta.png', url: 'Asia/Jakarta');
await instance.getTime();
Navigator.pushReplacementNamed(context, '/home', arguments: {
'location': instance.location,
'flag': instance.flag,
'time': instance.time,
});
}
The home screen which is receiving data :
Map data = {};
#override
Widget build(BuildContext context) {
data = ModalRoute.of(context)!.settings.arguments as Map;
print(data['location']);
return Scaffold(
body: SafeArea(
child: Padding(
padding: const EdgeInsets.only(top: 50),
child: Column(
children: [
TextButton.icon(
onPressed: () {
Navigator.pushNamed(context, '/location');
},
icon: const Icon(Icons.edit_location),
label: const Text('Edit Location'),
),
const SizedBox(
height: 20,
),
Row(mainAxisAlignment: MainAxisAlignment.center, children: [
Text(
data['time'],
style: const TextStyle(
fontSize: 30,
),
),
]),
],
),
),
),
);
}
print(data['location']); printing the data perfectly, but before it the error above showing instantly, is that mean the print method expected the value of data is null before it is receive the value? how to fix it
Your method setupWorldTime is an async function and will not call the Navigator.pushReplacementNamed bit until instance.getTime() has returned. Because your home screen starts without its arguments set, data = ModalRoute.of(context)!.settings.arguments as Map will set data to null, leading to your error. Only once instance.getTime() has returned will build() be called again, this time with data != null and your message disappears.
To fix this, in your build function you should test for data == null and show something else (like a loading indicator) if data is indeed still null, or use a FutureBuilder (preferred).

I want to update the app version in playstore to show a message dialog to user

I am new flutter .I want to update new version app in playstore to show a message dialog to user to update the new version and I used the plugin version_check 0.2.0.
When the user has already updated, but it still displays Message dialog the same. How not to show message dialog after update.Who can help me?
This my Code
This my Code
This my Code
As everything is not clear in the question, you should follow given steps to achieve the same.
Step 1. Go to Remote Config in firebase and add few parameters shown in the image and then publish it.
Step 2. Create a function VersionCheck and _showVersionDialog as follows:
versionCheck(){
//Get Current installed version of app
WidgetsBinding.instance.addPostFrameCallback((_) async {
final PackageInfo info = await PackageInfo.fromPlatform();
double currentVersion = double.parse(info.version.trim().replaceAll(".", ""));
//Get Latest version info from firebase config
final RemoteConfig remoteConfig = await RemoteConfig.instance;
try {
// Using default duration to force fetching from remote server.
await remoteConfig.fetch(expiration: const Duration(seconds: 0));
await remoteConfig.activateFetched();
remoteConfig.getString('force_update_current_version');
double newVersion = double.parse(remoteConfig
.getString('force_update_current_version')
.trim()
.replaceAll(".", ""));
if (newVersion > currentVersion) {
setState(() {
versionCode = remoteConfig.getString('force_update_current_version');
aboutVersion = remoteConfig.getString('update_feature');
});
_showVersionDialog(context);
}
} on FetchThrottledException catch (exception) {
// Fetch throttled.
print(exception);
} catch (exception) {
print('Unable to fetch remote config. Cached or default values will be '
'used');
}
});
}
_showVersionDialog(context) async {
await showDialog<String>(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
String title = "Update Available";
String message =
"About Update: \n";
return ButtonBarTheme(
data: ButtonBarThemeData(alignment: MainAxisAlignment.center),
child: new AlertDialog(
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(30)),
title: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(title),
Text("v"+versionCode),
],
),
content: Container(
height: 80,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(message,style: TextStyle(fontWeight: FontWeight.bold),),
Text(aboutVersion),
],
),
),
actions: <Widget>[
Padding(
padding: const EdgeInsets.all(8.0),
child: RaisedButton(
child: new Text(
'Update',
style: TextStyle(color: Colors.white),
),
color: Color(0xFF121A21),
shape: new RoundedRectangleBorder(
borderRadius: new BorderRadius.circular(30.0),
),
onPressed: () {
_launchURL(PLAY_STORE_URL);
},
),
),
],
),
);
},
);
}
Step 3. Call VersionCheck in init function of your main screen as follows.
#override
void initState() {
Future.delayed(const Duration(milliseconds: 5000), () {
if(mounted){
setState(() {
versionCheck();
});
}
});
super.initState();
}
Step 4. Whenever you want the update dialog to appear on screen just increase the version code value in remote config of firebase than your actual version code value.
This will help you to achieve what you want.

Flutter FlushBar does not work if I have to pre-process data

I am trying to build a form in Flutter. A user enters a value and clicks on a button, I run some basic validation on that value then show an AlertDialogue as a confirmation. If the user clicks on confirm I want to attempt an API call, get the result and display the result of that API call to let the user know what happened.If I display a Flushbar with hardcoded values it works. But If I try to do some string manipulation on the object first the Flushbar does not display. If I try to print the response from the function right into the Flushbar that also does not work.
Any advice on why this problem is occurring in the first place, and what would be the best way to solve it?
Padding(
padding: const EdgeInsets.symmetric(vertical: 15.0),
child: ElevatedButton(
style: ElevatedButton.styleFrom(
textStyle: TextStyle(
fontSize: 30,
),
primary: Colors.lightGreen, // background
onPrimary: Colors.white, // foreground
),
onPressed: () {
// Validate returns true if the form is valid, or false otherwise.
if (_formKey.currentState.validate())
{
return showDialog<void>(
context: context,
barrierDismissible: false, // user must tap button!
builder: (BuildContext context) {
return AlertDialog(
title: Text('Confirmation'),
content: SingleChildScrollView(
child: ListBody(
children: <Widget>[
Text('Please confirm your action'),
Text('Would you like to buy ' + finalValue.toString() + ' worth of merchandise'),
],
),
),
actions: <Widget>[
TextButton(
child: Text('No'),
onPressed: () {
Navigator.of(context).pop();
},
),
TextButton(
child: Text('Confirm'),
onPressed: () {
Navigator.of(context).pop();
var response = bb.trade(_character, finalValue, true); //API CALL
*//String resp = response.body.split(',')[1].split(':')[1];
//resp = resp.substring(1);
//resp = resp.split('.')[0];
//print(resp);* //The real string manipulation we want to do but then flushbar does not display
Flushbar(
title: "Result",
message: "lol"+response.body.toString(), //FLUSHBAR DOES NOT DISPLAY AT ALL
duration: Duration(seconds: 5),
)..show(context);
},
),
],
);
},
);
}
},
child: Text('Buy'),
),
),
I Think the problem is that you are trying to access data from API call immediately , however data coming over API call must be awaited for.
and it is preferred to pop after showing the flush bar .
so if bb.trade(_character, finalValue, true); return future you should do like this
onPressed: () async {
var response = await bb.trade(_character, finalValue, true);
Flushbar(
title: "Result",
message: "lol"+response.body.toString(),
duration: Duration(seconds: 5),
)..show(context).then((value) => Navigator.pop(context));
},

I'd like to add a like button to this flutter blog app

This project can be found here:https://github.com/Santos-Enoque/flutter_blog_app
So far I have this connected to firebase realtime database and it works well. I'm trying to add a like button to the Home page(lib/screens/home.dart) where all the posts are listed.
The Homepage displays the blog results using a Card with a ListTile. The trailing property of the ListTile card is already used so I'd like to use the leading property of the ListTile card to display a favourite icon which would increment a counter++ when tapped and also save the results to Firebase. Just like Facebook's like button.
Here's the code below:
child: Card(
child: ListTile(
title: ListTile(
onTap: (){
_incrementCounter();
},
leading: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
children: <Widget>[
Icon(Icons.favorite),
Text(postsList[index].counter.toString()
),
],
),
),
title: Text(
postsList[index].title,
style: TextStyle(
fontSize: 16.0, fontWeight: FontWeight.bold),
),
trailing: Text(
timeago.format(DateTime.fromMillisecondsSinceEpoch(postsList[index].date)),
style: TextStyle(fontSize: 12.0, color: Colors.grey),
),
),
subtitle: Padding(
padding: const EdgeInsets.only(bottom: 14.0),
child: Text(postsList[index].body, style: TextStyle(fontSize: 14.0),),
),
),
),
Here's the _increment counter code:
try {
var ref = FirebaseDatabase.instance.reference().child('posts/{postId}/counter');
await ref.once().then((data) async => {
await ref.set(data.value + 1),
});
} catch (e) {
print(e.message);
}
}
![Home page of blog]https://photos.google.com/share/AF1QipMK6C-Wx2vZHHbE2jDMQsfYNnwl9OWK_5W8OKOfiIChcXt-gnWnCH7ba_EpyRnRAA?key=cGxkRkVSSk9PQTdtTXB0MzZBRDNHNUVzSGxlcDVB
The blog posts are displayed as cards as in the image ... I'm trying to add an icon on the left side of the card(leading) plus an incrementing value everytime someone taps the icon. Something like the like button on facebook. And also save the data to firebase realtime database.
Any help is much appreciated ... Thank you all!
I think what you may want to do is add this function to your onPressed. You will also need to set the text of the next to the icon equal to the new value read.
void like() async {
try {
var ref = FirebaseDatabase.instance.reference().child('path to likes for a post');
await ref.once().then((data) async => {
await ref.set(data.value + 1);
});
} catch (e) {
print(e.message);
}
}
Hope this helps.
P.S.- You may find this video of use: https://www.youtube.com/watch?v=l8_7RTRRmHo
Thanks to everyone who helped with this:
Code for UI:
leading: FittedBox(
fit: BoxFit.fitWidth,
child: Row(
children: <Widget>[
Icon(Icons.favorite),
Text(postsList[index].counter.toString()
),
],
),
),
Code for Function:
onTap: (){
_incrementCounter(postsList[index].key);
}
...
void _incrementCounter(key) async {
try {
var ref = FirebaseDatabase.instance.reference().child('posts/'+ key +'/counter');
await ref.once().then((data) async => {
await ref.set(data.value + 1),
});
} catch (e) {
print(e.message);
}
}
}