Flutter - Firebase push notification using firebase messaging success but not get notification - android

I just try using firebase push notification and messaging. I got an issue which is when I tried send message via console it showed completed but I do not get the notification. So can you guys explain my coding mistake. What must I do?
Local notification was fine.
local notification
Here the message that I tried send on console but i dont get any notification on the phone.
firebase console
this is my code
import 'package:flutter/material.dart';
import 'package:firebase_core/firebase_core.dart';
import 'firebase_options.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
const AndroidNotificationChannel channel = AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description: 'This channel is used for important notifications.', // description
importance: Importance.high,
playSound: true);
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('A bg message just showed up : ${message.messageId}');
}
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key key}) : super(key: key);
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// Try running your application with "flutter run". You'll see the
// application has a blue toolbar. Then, without quitting the app, try
// changing the primarySwatch below to Colors.green and then invoke
// "hot reload" (press "r" in the console where you ran "flutter run",
// or simply save your changes to "hot reload" in a Flutter IDE).
// Notice that the counter didn't reset back to zero; the application
// is not restarted.
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key key, this.title}) : super(key: key);
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
#override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
#override
void initState() {
super.initState();
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription : channel.description,
color: Colors.blue,
playSound: true,
icon: '#mipmap/ic_launcher',
),
));
}
});
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
print('A new onMessageOpenedApp event was published!');
RemoteNotification notification = message.notification;
AndroidNotification android = message.notification?.android;
if (notification != null && android != null) {
showDialog(
context: context,
builder: (_) {
return AlertDialog(
title: Text(notification.title),
content: SingleChildScrollView(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [Text(notification.body)],
),
),
);
});
}
});
}
void showNotification() {
setState(() {
_counter++;
});
flutterLocalNotificationsPlugin.show(
0,
"Testing $_counter",
"How you doin ?",
NotificationDetails(
android: AndroidNotificationDetails(channel.id, channel.name, channelDescription: channel.description,
importance: Importance.high,
color: Colors.blue,
playSound: true,
icon: '#mipmap/ic_launcher')));
}
#override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Invoke "debug painting" (press "p" in the console, choose the
// "Toggle Debug Paint" action from the Flutter Inspector in Android
// Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
// to see the wireframe for each widget.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text(
'You have pushed the button this many times:',
),
Text(
'$_counter',
style: Theme.of(context).textTheme.headline4,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: showNotification,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
Is there any error on firebase connection to device?
SOLVED
Coding work fined.This occur because of I am using emulator instead of real device to tested.Thanks to u guys who answered my question.Those also help me a lot to understand.

Please refer the below code
class name FCM
import 'dart:async';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
Future<void> onBackgroundMessage(RemoteMessage message) async {
await Firebase.initializeApp();
if (message.data.containsKey('data')) {
// Handle data message
final data = message.data['data'];
}
if (message.data.containsKey('notification')) {
// Handle notification message
final notification = message.data['notification'];
}
// Or do other work.
}
class FCM {
final _firebaseMessaging = FirebaseMessaging.instance;
final streamCtlr = StreamController<String>.broadcast();
final titleCtlr = StreamController<String>.broadcast();
final bodyCtlr = StreamController<String>.broadcast();
setNotifications() {
FirebaseMessaging.onBackgroundMessage(onBackgroundMessage);
FirebaseMessaging.onMessage.listen(
(message) async {
if (message.data.containsKey('data')) {
// Handle data message
streamCtlr.sink.add(message.data['data']);
}
if (message.data.containsKey('notification')) {
// Handle notification message
streamCtlr.sink.add(message.data['notification']);
}
// Or do other work.
titleCtlr.sink.add(message.notification!.title!);
bodyCtlr.sink.add(message.notification!.body!);
},
);
// With this token you can test it easily on your phone
final token =
_firebaseMessaging.getToken().then((value) => print('Token: $value'));
}
dispose() {
streamCtlr.close();
bodyCtlr.close();
titleCtlr.close();
}
}
And Main Class
void main() async {
await init();
runApp(const MyApp());
}
Future init() async {
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
#override
Widget build(BuildContext context) {
return const MaterialApp(
home: HomePage(),
);
}
}
class HomePage extends StatefulWidget {
const HomePage({Key? key}) : super(key: key);
#override
State<HomePage> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
String notificationTitle = 'No Title';
String notificationBody = 'No Body';
String notificationData = 'No Data';
#override
void initState() {
final firebaseMessaging = FCM();
firebaseMessaging.setNotifications();
firebaseMessaging.streamCtlr.stream.listen(_changeData);
firebaseMessaging.bodyCtlr.stream.listen(_changeBody);
firebaseMessaging.titleCtlr.stream.listen(_changeTitle);
super.initState();
}
_changeData(String msg) => setState(() => notificationData = msg);
_changeBody(String msg) => setState(() => notificationBody = msg);
_changeTitle(String msg) => setState(() => notificationTitle = msg);
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
notificationTitle,
style: Theme.of(context).textTheme.headline4,
),
Text(
notificationBody,
style: Theme.of(context).textTheme.headline6,
),
Text(
notificationData,
style: Theme.of(context).textTheme.headline6,
),
],
),
),
);
}
}

Try by calling _firebasebackgroundhanler function as below in code
void main() async {
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
WidgetsFlutterBinding.ensureInitialized();
await Firebase.initializeApp();
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
alert: true,
badge: true,
sound: true,
);
Firebasebackground handler function :
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print("Handling a background message: ${message.messageId}");
}

Related

Agora in Flutter- navigating to the video chat screen more than one time keeps local video loading forever

I am using Agora for a one-to-one video chat purpose in Flutter.
User1 has an app to go online and user2 has another app to go online. After both of them go online, they can do video chat with one another. Both apps have almost similar codebase.
I have a screen or activity (say screen1) where an alert dialog is shown on tapping a button (say button1). On tapping the Continue button in the alert dialog, the dialog disappears and the user is taken to the screen (say screen2) where the video chat takes place. But after going to the video chat screen successfully, if the user taps on the back button on the mobile set then s/he is taken to screen1 and after tapping on button1, if the user taps on the Continue button in the popped up alert dialog, the user is again taken to screen2 but this time the local video (i.e. video of the user using the app) keeps loading for ever. Obviously I want the local video to load as it did for the first time.
I am gonna put my code here in such a way that you can easily run that.
Following code is for user1. For user2, no alert box is there in the app. Same code from user1 is used for user2 app, except the value of remoteUid is set to be 2 for user2 while this value is set to be 1 for user1. These are just two values identifying 2 users.
For user1:
main.dart:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'livesession1to1.dart';
void main() {
runApp(MessagingExampleApp());
}
class NavigationService {
static GlobalKey<NavigatorState> navigatorKey =
GlobalKey<NavigatorState>();
}
/// Entry point for the example application.
class MessagingExampleApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Messaging Example App',
navigatorKey: NavigationService.navigatorKey, // set property
theme: ThemeData.dark(),
routes: {
'/': (context) => Application(),
'/liveSession1to1': (context) =>LiveSession1to1(),
},
);
}
}
int _messageCount = 0;
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
String constructFCMPayload(String? token, String server_key) {
_messageCount++;
return jsonEncode({
'token': token,
'to':token,
'data': {
'via': 'FlutterFire Cloud Messaging!!!',
'count': _messageCount.toString(),
},
'notification': {
'title': 'Hello FlutterFire!',
'body': 'This notification (#$_messageCount) was created via FCM! =============',
},
"delay_while_idle" : false,
"priority" : "high",
"content_available" : true
});
}
/// Renders the example application.
class Application extends StatefulWidget {
#override
State<StatefulWidget> createState() => _Application();
}
class _Application extends State<Application> {
String? _token;
#override
void initState() {
super.initState();
}
showAlertDialog() {
BuildContext context=NavigationService.navigatorKey.currentContext!;
// set up the buttons
Widget cancelButton = TextButton(
child: Text("Cancel"),
onPressed: () {},
);
Widget continueButton = TextButton(
child: Text("Continue"),
onPressed: () {
Navigator.of(context, rootNavigator: true).pop();
Navigator.of(context).pushNamed('/liveSession1to1');
},
);
Timer? timer = Timer(Duration(milliseconds: 5000), (){
Navigator.of(context, rootNavigator: true).pop();
});
showDialog(
context: context,
builder: (BuildContext builderContext) {
return AlertDialog(
backgroundColor: Colors.black26,
title: Text('One to one live session'),
content: SingleChildScrollView(
child: Text('Do you want to connect for a live session ?'),
),
actions: [
cancelButton,
continueButton,
],
);
}
).then((value){
// dispose the timer in case something else has triggered the dismiss.
timer?.cancel();
timer = null;
});
}
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('My App'),
),
floatingActionButton: Builder(
builder: (context) => FloatingActionButton(
onPressed: showAlertDialog,
backgroundColor: Colors.white,
child: const Icon(Icons.send),
),
),
body: SingleChildScrollView(
child: Text(
'Trigger Alert'
),
),
);
}
}
livesession1to1.dart:
import 'dart:async';
import 'package:agora_rtc_engine/rtc_engine.dart';
import 'package:agora_rtc_engine/rtc_local_view.dart' as RtcLocalView;
import 'package:agora_rtc_engine/rtc_remote_view.dart' as RtcRemoteView;
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
// const appId = "<-- Insert App Id -->";
// const token = "<-- Insert Token -->";
const appId = "......";// Put Agora App ID from Agora site here
const token = "....";// Put token ( temporary token avilable from Agora site)
void main() => runApp(MaterialApp(home: LiveSession1to1()));
class LiveSession1to1 extends StatefulWidget {
#override
_LiveSession1to1State createState() => _LiveSession1to1State();
}
class _LiveSession1to1State extends State<LiveSession1to1> {
int _remoteUid=1;
bool _localUserJoined = false;
late RtcEngine _engine;
#override
void initState() {
super.initState();
setState(() {});
initAgora();
}
Future<void> initAgora() async {
// retrieve permissions
await [Permission.microphone, Permission.camera].request();
// Create RTC client instance
RtcEngineContext context = RtcEngineContext(appId);
_engine = await RtcEngine.createWithContext(context);
await _engine.enableVideo();
_engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) {
print("local user $uid joined");
setState(() {
_localUserJoined = true;
});
},
userJoined: (int uid, int elapsed) {
print("remote user $uid joined");
setState(() {
_remoteUid = uid;
});
},
userOffline: (int uid, UserOfflineReason reason) {
print("remote user $uid left channel");
setState(() {
// _remoteUid = null;
_remoteUid = 0;
});
},
),
);
try {
await _engine.joinChannel(token, "InstaClass", null, 0);
} catch (e) {
print("error with agora = ");
print("$e");
print("error printeddddd");
}
}
// Create UI with local view and remote view
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Agora Video Call'),
),
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? RtcLocalView.SurfaceView()
: CircularProgressIndicator(),
),
),
),
],
),
);
}
// Display remote user's video
Widget _remoteVideo() {
if (_remoteUid != 0) {
return RtcRemoteView.SurfaceView(
uid: _remoteUid,
channelId: "InstaClass",
);
}else {
print("'Please wait for remote user to join',");
return Text(
'Please wait for remote user to join',
textAlign: TextAlign.center,
);
}
}
}
For user2:
main.dart:
import 'dart:async';
import 'package:agora_rtc_engine/rtc_engine.dart';
import 'package:agora_rtc_engine/rtc_local_view.dart' as RtcLocalView;
import 'package:agora_rtc_engine/rtc_remote_view.dart' as RtcRemoteView;
import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
const appId = "....."; // Same as user1 app
const token = "....."; // same as user1 app
void main() => runApp(MaterialApp(home: MyApp()));
class MyApp extends StatefulWidget {
#override
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
// int? _remoteUid=1;
int _remoteUid=2;
bool _localUserJoined = false;
late RtcEngine _engine;
#override
void initState() {
super.initState();
initAgora();
}
Future<void> initAgora() async {
// retrieve permissions
await [Permission.microphone, Permission.camera].request();
//create the engine
_engine = await RtcEngine.create(appId);
await _engine.enableVideo();
_engine.setEventHandler(
RtcEngineEventHandler(
joinChannelSuccess: (String channel, int uid, int elapsed) {
print("local user $uid joined");
setState(() {
_localUserJoined = true;
});
},
userJoined: (int uid, int elapsed) {
print("remote user $uid joined");
setState(() {
_remoteUid = uid;
});
},
userOffline: (int uid, UserOfflineReason reason) {
print("remote user $uid left channel");
setState(() {
// _remoteUid = null;
_remoteUid = 0;
});
},
),
);
// await _engine.joinChannel(token, "test", null, 0);
await _engine.joinChannel(token, "InstaClass", null, 0);
}
// Create UI with local view and remote view
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Agora Video Call'),
),
body: Stack(
children: [
Center(
child: _remoteVideo(),
),
Align(
alignment: Alignment.topLeft,
child: Container(
width: 100,
height: 150,
child: Center(
child: _localUserJoined
? RtcLocalView.SurfaceView()
: CircularProgressIndicator(),
),
),
),
],
),
);
}
// Display remote user's video
Widget _remoteVideo() {
/*if (_remoteUid != null) {
return RtcRemoteView.SurfaceView(uid: _remoteUid!);
}*/
if (_remoteUid != 0) {
return RtcRemoteView.SurfaceView(
uid: _remoteUid,
channelId: "InstaClass",
);
}else {
return Text(
'Please wait for remote user to join',
textAlign: TextAlign.center,
);
}
}
}
In order to get the app ID and token, login to Agora site. After logging in, go to the 'Project Management' section to see the projects already created there. Under the Functions column, click on the key symbol and you will be taken to a page where you can generate a temporary token. On that page, give the channel name input the value 'InstaClass' as I have used this name in my code.
How to make the video chat work smoothly after the first time it works well ?
I think the problem is that when pressing back button you are just being taken to the previous screen and the call session is not being end. You can try by leaving the channel when pressing back button like :
_engine.leaveChannel();
End Call button sample
ElevatedButton(
onPressed: () {
_rtcEngine.leaveChannel();
Navigator.pop(context);
},
style: ButtonStyle(
shape: MaterialStateProperty.all(CircleBorder()),
backgroundColor: MaterialStateProperty.all(Colors.red),
padding: MaterialStateProperty.all(
EdgeInsets.fromLTRB(15, 15, 15, 12)),
),
child: Icon(
Icons.phone,
size: 30,
),
)
Back Button override using WillPopScope
return WillPopScope(
onWillPop: () async {
_rtcEngine.leaveChannel();
return true;
},
child: Scaffold(
body: Container(),
),
);

Methods are not getting executed in the written order inside onPressed

Im trying to create a flutter app with a simple raised button that does the following:
sends an sms in the background using the sms package opens a webpage
2. in the app(only for 5 seconds) using url_launcher opens the phones
3. native app for making a voice call with the onPressed property.
And I wanted it to be in this order so that I can make the phone call at the end. However, the inside the onPressed opens the native phone call app first, which doesnt let my web page open unless I exit out of the phone call app.
Im having a hard time understanding why the phone call native app is opened first, even though I make the call the _makePhoneCall() method only after I make the _launchInApp(toLaunch) call. sendSMS() is being called correctly
How can I set this in a way that the phone call native app is called only after the webpage is opened in the app and follows the order? Any help would be great
Below is the piece of code:
import 'package:flutter/material.dart';
import 'package:url_launcher/url_launcher.dart';
import 'package:sms/sms.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Packages testing',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Packages testing'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
String _phone = '';
_launchInApp(String url) async {
if (await canLaunch(url)) {
await launch(
url,
forceSafariVC: true,
forceWebView: true,
headers: <String, String>{'my_header_key': 'my_header_value'},
);
} else {
throw 'Could not launch $url';
}
}
_makePhoneCall(String url) async {
if (await canLaunch(url)) {
await launch(url);
} else {
throw 'Could not launch $url';
}
}
void sendSMS() {
SmsSender sender = new SmsSender();
sender.sendSms(new SmsMessage(_phone, 'Testing Handset'));
}
#override
Widget build(BuildContext context) {
const String toLaunch = 'https://flutter.dev/';
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: ListView(
children: <Widget>[
Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.all(16.0),
child: TextField(
onChanged: (String text) => _phone = text,
decoration:
const InputDecoration(hintText: 'Phone Number')),
),
FlatButton(
onPressed: () => setState(() {
sendSMS();
_launchInApp(toLaunch);
_makePhoneCall('tel:$_phone');
}),
child: const Text('Run All'),
),
const Padding(padding: EdgeInsets.all(16.0)),
],
),
],
),
);
}
}
You will have to use the await keyword before the _launchInApp function to make it work properly. Try the following code.
FlatButton(
onPressed: () aync {
sendSMS();
await _launchInApp(toLaunch);
_makePhoneCall('tel:$_phone');
}),
child: const Text('Run All'),
),
You created async functions but when you called them you did not specify that you want to wait for them to complete. Add the await keyword in OnPressed

Show FCM notification thoughout the app when the app is open (Flutter)

I'm using FCM to send push notifications to my device right now and it's working perfectly. However when the app is open, I only get the onResume to be executed when I'm in that particular page. I want to display the notification on the top regardless of which page(or class) the user is on. Basically I want the notifications to be displayed globally (Show popup). Any help would be appreciated. Here is the code from the page that displays the notifications.
if (Platform.isIOS) {
iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
_saveDeviceToken();
});
_fcm.requestNotificationPermissions(IosNotificationSettings());
} else {
_saveDeviceToken();
}
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print("onMessage: $message");
var temp = message['notification'];
setState(() {
title.add(temp['title']);
body.add(temp['body']);
});
showDialog(
context: context,
builder: (context) => AlertDialog(
content: ListTile(
title: Text(message['notification']['title']),
subtitle: Text(message['notification']['body']),
),
actions: <Widget>[
FlatButton(
color: const Color(0xFF650572),
child: Text('Ok'),
onPressed: () => Navigator.of(context).pop(),
),
],
),
);
},
onLaunch: (Map<String, dynamic> message) async {
print("onLaunch: $message");
Navigator.push(
context, MaterialPageRoute(builder: (context) => MessageHandler()));
// TODO optional
},
onResume: (Map<String, dynamic> message) async {
print("onResume: $message");
Navigator.push(context,
MaterialPageRoute(builder: (context) => MessageHandler()));
// TODO optional
},
);
}
Wrap your MaterialApp in a wrapper class ... lets call that FCMWrapper.
class FCMWrapper extends StatefulWidget {
final Widget child;
const FCMWrapper({Key key, this.child}) : super(key: key);
#override
_FCMWrapperState createState() => _FCMWrapperState();
}
class _FCMWrapperState extends State<FCMWrapper> {
#override
Widget build(BuildContext context) {
return Consumer<YourObservable>(
builder: (context, yourObservable, _) {
if (yourObservable != null && yourObservable.isNotEmpty) {
Future(
() => navigatorKey.currentState.push(
PushNotificationRoute(
child: YourViewOnNotification(),
)),
),
);
}
return widget.child;
},
);
}
}
I have stored my data in an observable from a separate class. So when I receive a notification I update my observable. Since we are consuming on that observable the PushNotificationRoute would be called.
PushNotificationRoute is simply a class which extends ModalRoute.
class PushNotificationRoute extends ModalRoute {
final Widget child;
PushNotificationRoute({this.child});
... //Override other methods to your requirement
//This is important
#override
Widget buildPage(BuildContext context, Animation<double> animation,
Animation<double> secondaryAnimation) {
return SafeArea(
child: Builder(builder: (BuildContext context) {
return child;
}),
);
}
...
#override
Duration get transitionDuration => Duration(milliseconds: 200);
}
Now in main.dart declare a global key like
var navigatorKey = GlobalKey<NavigatorState>();
and wrap your MaterialApp like
...
FCMWrapper(
child: MaterialApp(
navigatorKey: navigatorKey,
title: 'Your App',
...
So now every time a notification comes in your observable should update and push a modal route which would be shown over anywhere in the app.

Flutter - download file with android download indicator

I am trying to download an attachment for a mailing system.
To do that I am using Flutter downloader but I need to pass my token with my http client.
I think this plugin doesn't take care of it.
I have tried to do this using dio.
I can download files but I don't know how I can display the Android download indicator (cf. image)
Does somebody have any idea of a plug-in or something to display this Android indicator ?
EDIT: I finally have found a solution.
Actually, there are nothing to display the download indicator but Flutter_downloader. So I have kept this plugin and I have passed my token in headers.
Like this :
Map<String, String> requestHeaders = {
'Authorization': 'Bearer ' + http.cookie,
};
final assetsDir = documentsDirectory.path + '/';
final taskId = await FlutterDownloader.enqueue(
url: url,
savedDir: assetsDir,
fileName: attachment.name,
headers: requestHeaders,
showNotification: true, // show download progress in status bar (for Android)
openFileFromNotification: true, // click on notification to open downloaded file (for Android)
);
Sorry for my english and thanks to Ryan for the correction
One way to display download progress notification on Android with Flutter is by using flutter_local_notifications plugin. Here's a sample that you can try out.
import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
final flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
String selectedNotificationPayload;
class ReceivedNotification {
ReceivedNotification({
#required this.id,
#required this.title,
#required this.body,
#required this.payload,
});
final int id;
final String title;
final String body;
final String payload;
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('#mipmap/ic_launcher');
final InitializationSettings initializationSettings = InitializationSettings(
android: initializationSettingsAndroid);
await flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: (String payload) async {
if (payload != null) {
debugPrint('notification payload: $payload');
}
});
runApp(MyApp());
}
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
#override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Text(
'Download Progress Notification',
),
),
floatingActionButton: FloatingActionButton(
onPressed: () async {
await _showProgressNotification();
},
tooltip: 'Download Notification',
child: Icon(Icons.download_sharp),
),
);
}
Future<void> _showProgressNotification() async {
const int maxProgress = 5;
for (int i = 0; i <= maxProgress; i++) {
await Future<void>.delayed(const Duration(seconds: 1), () async {
final AndroidNotificationDetails androidPlatformChannelSpecifics =
AndroidNotificationDetails('progress channel', 'progress channel',
'progress channel description',
channelShowBadge: false,
importance: Importance.max,
priority: Priority.high,
onlyAlertOnce: true,
showProgress: true,
maxProgress: maxProgress,
progress: i);
final NotificationDetails platformChannelSpecifics =
NotificationDetails(android: androidPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0,
'progress notification title',
'progress notification body',
platformChannelSpecifics,
payload: 'item x');
});
}
}
}
Here's how the app looks like running
For Flutter_Downloader
If you are using API 29+(ANDROID 10 and Above) add the below code in AndriodManifest.xml
android:requestLegacyExternalStorage="true"
Like this
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
...........
<application
..............
<!-- Add This line -->
tools:replace="android:label"
android:requestLegacyExternalStorage="true"> <!-- Add This line if you are targeting android API 29+-->
<activity>
...............
</activity>
</application>

Flutter FCM notfications + text to speech plugin

i am trying to make an app that reads notifications from firebase cloud messaging
i am sending notifications with a node program and reading it in flutter
import 'package:flutter/material.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_tts/flutter_tts.dart';
void main(){runApp(MyApp());}
class MyApp extends StatelessWidget {
#override
Widget build(BuildContext context) {
return new MaterialApp(
title: 'Flutter Demo',
theme: new ThemeData(
primarySwatch: Colors.blue,
),
home: new MyHomePage(title: ' Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key, this.title}) : super(key: key);
final String title;
#override
_MyHomePageState createState() => new _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
FirebaseMessaging _firebaseMessaging = new FirebaseMessaging();
FlutterTts flutterTts = new FlutterTts();
int _counter = 0;
String notificationtext;
void _incrementCounter() {
setState(() {
_counter++;
});
}
#override
void initState() {
super.initState();
_firebaseMessaging.subscribeToTopic("bavo");
_firebaseMessaging.configure(
onMessage : (Map<String,dynamic> message) {
print('on message $message');
setState((){
_counter = _counter + 100;
notificationtext = message.toString();
});
},
onResume : (Map<String, dynamic> message) {
print('on resume $message');
setState((){
_counter = _counter + 100;
notificationtext = message.toString();
});
},
onLaunch : (Map<String, dynamic> message) {
print('on launch $message');
setState((){
_counter = _counter + 100;
notificationtext = message.toString();
});
},
);
_firebaseMessaging.getToken().then((token){
print(token);
});
}
speak() async {
flutterTts.speak(notificationtext);
}
#override
Widget build(BuildContext context) {
flutterTts.speak(notificationtext);
return new Scaffold(
appBar: new AppBar(
title: new Text(widget.title),
),
body: new Center(
child: new Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
new Text(
'You have pushed the button this many times:',
),
new RaisedButton(
onPressed: speak,
child: new Text('Say Hello'),
),
new Text(
'$_counter',
style: Theme.of(context).textTheme.display1,
),
],
),
),
floatingActionButton: new FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: new Icon(Icons.add),
),
);
}
}
as you can see in onmessage i set a variable equal to the datapayload of the message and the i let it play with the initstate ( this reloads the widget so it runs the text to speech code, this was to only working way i found)
my problem is how can i make it so if the variable changes it plays it with the text to speech even if the phone is off ,
now it tells it when the app is open and only then
hope you guyss can help me
I'm not sure how firebase messaging works, but why not just run the flutterTts.speak() method inside your listener.
Or you can call the speak method within the listener.
I haven't tested this, but might be worth a shot.
example:
onMessage : (Map<String,dynamic> message) {
print('on message $message');
setState((){
_counter = _counter + 100;
notificationtext = message.toString();
});
flutterTts.speak(message.toString());
},

Categories

Resources