This might come up as a bit vague, but when I try to trigger a notification on my app by making API request, my functions for the notifications don't seems to be listening to the notifications. The token is sent through, the notifications are received on the firebase side, so the problem might be on the side of the app where the functions don't return anything. I tried updating to the latest version of the firebase tools needed and the local notifications plugin. What could I be doing wrong? I already checked if the token is the same and had to change to a new one (I've been switching through physical and emulator device during the development).
pubspec.yaml
environment:
sdk: ">=2.18.1 <3.0.0"
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.2
http: ^0.13.5
hive: ^2.2.3
hive_flutter: ^1.1.0
build_runner: ^2.2.0
hive_generator: ^1.1.3
path_provider: ^2.0.11
shared_preferences: ^2.0.15
sms_autofill: ^2.2.0
in_app_review: ^2.0.4
datetime_picker_formfield: ^2.0.1
intl: ^0.17.0
firebase_core: ^1.21.1
firebase_analytics: ^9.3.3
firebase_messaging: ^13.0.0
flutter_local_notifications: ^11.0.1
device_info_plus: ^4.1.2
package_info_plus: ^1.4.3+1
infinite_listview: ^1.1.0
infinite_scroll_pagination: ^3.2.0
timezone: ^0.9.0
rxdart: ^0.27.5
easy_localization: ^3.0.1
volume_control: ^0.1.5
flutter_native_splash: ^2.2.10
flutter doctor -v
[✓] Flutter (Channel stable, 3.3.2, on macOS 12.4 21F79 darwin-arm, locale en-PT)
• Flutter version 3.3.2 on channel stable at /Users/henriqueguima/work/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision e3c29ec00c (2 weeks ago), 2022-09-14 08:46:55 -0500
• Engine revision a4ff2c53d8
• Dart version 2.18.1
• DevTools version 2.15.0
[✓] Android toolchain - develop for Android devices (Android SDK version 33.0.0)
• Android SDK at /Users/henriqueguima/Library/Android/sdk
• Platform android-33, build-tools 33.0.0
• Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 13.4.1)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Build 13F100
• CocoaPods version 1.11.3
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2021.2)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.12+0-b1504.28-7817840)
[✓] VS Code (version 1.71.2)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.48.0
[✓] Connected device (3 available)
• RMX2202 (mobile) • f4a92fd4 • android-arm64 • Android 12 (API 31)
• macOS (desktop) • macos • darwin-arm64 • macOS 12.4 21F79 darwin-arm
• Chrome (web) • chrome • web-javascript • Google Chrome 105.0.5195.125
[✓] HTTP Host Availability
• All required HTTP hosts are available
• No issues found!
firebase_messaging_service.dart
class PushNotificationService {
int counter = 5;
Future<void> setupInteractedMessage() async {
await Firebase.initializeApp();
getDeviceFirebaseToken();
await enableIOSNotifications();
await registerNotificationListeners();
FirebaseMessaging.onMessageOpenedApp.listen(
//is skipped
(RemoteMessage message) {
log('test');
},
);
FirebaseMessaging messaging = FirebaseMessaging.instance;
NotificationSettings settings = await messaging.requestPermission();
FirebaseMessaging.instance.getInitialMessage().then(
//is skipped
(message) {
if (message != null) {
log('asfasfa');
}
},
);
}
//code block runs with success
registerNotificationListeners() async {
AndroidNotificationChannel channel = androidNotificationChannel();
final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
await flutterLocalNotificationsPlugin
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
var androidSettings =
const AndroidInitializationSettings('#mipmap/ic_launcher');
var iOSSettings = const DarwinInitializationSettings(
requestSoundPermission: false,
requestBadgePermission: false,
requestAlertPermission: false,
);
var initSetttings =
InitializationSettings(android: androidSettings, iOS: iOSSettings);
flutterLocalNotificationsPlugin.initialize(
initSetttings,
onDidReceiveBackgroundNotificationResponse: (message) async {
log('notification action');
},
);
FirebaseMessaging.onMessage.listen(
//is skipped
(message) {
log('Got a message whilst in the foreground!');
if (message.notification != null) {
log('message');
final snackBar = SnackBar(
content: Text(message.notification?.title ?? '', maxLines: 2),
);
flutterLocalNotificationsPlugin.show(
message.notification.hashCode,
message.notification!.title,
message.notification!.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
playSound: true,
),
),
);
}
},
);
// onMessage is called when the app is in foreground and a notification is received
FirebaseMessaging.onMessage.listen(
//is skipped
(RemoteMessage? message) {
RemoteNotification? notification = message!.notification;
AndroidNotification? android = message.notification?.android;
// If `onMessage` is triggered with a notification, construct our own
// local notification to show to users using the created channel.
if (message.notification != null) {
final snackBar = SnackBar(
content: Text(message.notification?.title ?? '', maxLines: 2),
);
log('message!!!!!!');
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification!.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: android!.smallIcon,
playSound: true,
),
),
);
}
if (notification != null && android != null) {
flutterLocalNotificationsPlugin.show(
notification.hashCode,
notification.title,
notification.body,
NotificationDetails(
android: AndroidNotificationDetails(
channel.id,
channel.name,
channelDescription: channel.description,
icon: android.smallIcon,
playSound: true,
),
),
);
}
});
}
enableIOSNotifications() async {
await FirebaseMessaging.instance
.setForegroundNotificationPresentationOptions(
alert: true, // Required to display a heads up notification
badge: true,
sound: true,
);
getDeviceFirebaseToken();
}
getDeviceFirebaseToken() async {
final token = await FirebaseMessaging.instance.getToken();
log('FIREBASE TOKEN -----> $token');
firebaseToken = token.toString();
}
androidNotificationChannel() => const AndroidNotificationChannel(
'high_importance_channel', // id
'High Importance Notifications', // title
description:
'This channel is used for important notifications.', // description
importance: Importance.max,
);
}
Related
I'm trying to fetch the names of countries from an API and display it in a drop-down box. I'm using the bloc library for state management.
Here's my Code.
select_input_field.dart - Custom wrapper widget for DropdownButtonField
import 'package:flutter/material.dart';
class SelectInputField extends StatelessWidget {
final String label;
final List<String> options;
final TextEditingController controller;
final EdgeInsets padding;
const SelectInputField(this.label, this.options,
{ Key? key,
required this.controller,
this.padding =
const EdgeInsets.only(top: 8, bottom: 8, left: 30, right: 30)})
: super(key: key);
#override
Widget build(BuildContext context) {
return Padding(
padding: padding,
child: DropdownButtonFormField(
decoration: InputDecoration(
labelText: label,
labelStyle: Theme.of(context).textTheme.bodyMedium,
border: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColorDark,
width: 1,
),
),
enabledBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).primaryColorDark,
width: 1,
),
),
focusedBorder: OutlineInputBorder(
borderSide: BorderSide(
color: Theme.of(context).colorScheme.secondary,
width: 1,
),
),
fillColor: Theme.of(context).primaryColorLight),
style: Theme.of(context).textTheme.bodyMedium,
items: options.map((String value) {
print(value);
return DropdownMenuItem<String>(
value: value,
child: Text(value),
);
}).toList(),
onChanged: (String? value) {
controller.text = value ?? '';
},
),
);
}
}
This is the method I'm using to build it.
Widget _countriesDropDown(BuildContext context, TextEditingController controller){
return BlocProvider(
create: (context) => _signupBloc,
child: BlocBuilder<SignupBloc,SignupState>(
buildWhen: ((previous, current) => previous is! Loaded && current is Loaded),
builder: (context, state){
if (state is Initial){
_signupBloc.add(LoadEvent());
}
else if (state is Loaded){
countries = state.countries;
return SelectInputField("Country", countries, controller: controller);
}
return CircularProgressIndicator(color: Theme.of(context).colorScheme.secondary,);
},
)
);
}
And my bloc class is as follows.
class SignupBloc extends Bloc<SignupEvent, SignupState> {
static const String countriesUrl = "https://api.first.org/data/v1/countries";
SignupBloc() : super(Initial()) {
on<LoadEvent>((event, emit) async{
emit(Loading());
List<String> countries = [];
try{
final response = await http.get(Uri.parse(countriesUrl));
if(response.statusCode == 200){
var responseJson = jsonDecode(response.body);
responseJson['data'].values.forEach((element) {
countries.add(element['country']);
});
emit(Loaded(countries));
}
else{
emit(Failed("Error in getting data"));
}
}
catch(e){
print(e);
emit(Failed("Failed to access internet. Try again later!"));
}
});
}
The error appears to look like this. I cannot understand where this error occurs
======== Exception caught by rendering library =====================================================
The following assertion was thrown during paint():
Tried to paint a RenderObject reentrantly.
The following RenderObject was already being painted when it was painted again: RenderPointerListener#c100e relayoutBoundary=up20 NEEDS-PAINT
... needs compositing
... parentData: <none> (can use size)
... constraints: BoxConstraints(0.0<=w<=351.4, 0.0<=h<=Infinity)
... size: Size(351.4, 64.0)
... behavior: opaque
... listeners: down, panZoomStart
Since this typically indicates an infinite recursion, it is disallowed.
The relevant error-causing widget was:
DropdownButtonFormField<String> DropdownButtonFormField:file:///D:/Projects/sem5/iblock/lib/widgets/forms/select_input_field.dart:20:14
When the exception was thrown, this was the stack:
#0 RenderObject._paintWithContext.<anonymous closure> (package:flutter/src/rendering/object.dart:2693:9)
#1 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2706:6)
#2 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#3 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#4 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
#5 PaintingContext.paintChild (package:flutter/src/rendering/object.dart:239:13)
#6 RenderProxyBoxMixin.paint (package:flutter/src/rendering/proxy_box.dart:144:15)
#7 RenderObject._paintWithContext (package:flutter/src/rendering/object.dart:2796:7)
I need an explanation about what's wrong with my code and how to fix this.
Output of flutter doctor
[√] Flutter (Channel stable, 3.3.2, on Microsoft Windows [Version 10.0.22621.608], locale en-US)
• Flutter version 3.3.2 on channel stable at C:\flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision e3c29ec00c (3 weeks ago), 2022-09-14 08:46:55 -0500
• Engine revision a4ff2c53d8
• Dart version 2.18.1
• DevTools version 2.15.0
[√] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
• Android SDK at C:\Users\ISHAD\AppData\Local\Android\sdk
• Platform android-32, build-tools 30.0.3
• Java binary at: C:\Program Files\Android\Android Studio\jre\bin\java
• Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
• All Android licenses accepted.
[√] Chrome - develop for the web
• CHROME_EXECUTABLE = C:\Program Files\BraveSoftware\Brave-Browser\Application\brave.exe
[√] Visual Studio - develop for Windows (Visual Studio Build Tools 2019 16.11.18)
• Visual Studio at C:\Program Files (x86)\Microsoft Visual Studio\2019\BuildTools
• Visual Studio Build Tools 2019 version 16.11.32802.440
• Windows 10 SDK version 10.0.19041.0
[√] Android Studio (version 2021.3)
• Android Studio at C:\Program Files\Android\Android Studio
• Flutter plugin can be installed from:
https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.13+0-b1751.21-8125866)
[√] IntelliJ IDEA Ultimate Edition (version 2022.2)
• IntelliJ at C:\Program Files\JetBrains\IntelliJ IDEA 2022.1.2
• Flutter plugin version 70.2.5
• Dart plugin version 222.4167.21
[√] VS Code (version 1.71.2)
• VS Code at C:\Users\ISHAD\AppData\Local\Programs\Microsoft VS Code
• Flutter extension version 3.48.0
[√] Connected device (3 available)
• Windows (desktop) • windows • windows-x64 • Microsoft Windows [Version 10.0.22621.608]
• Chrome (web) • chrome • web-javascript • unknown
• Edge (web) • edge • web-javascript • Microsoft Edge 105.0.1343.53
! Device DRGGAM5890805202 is not authorized.
You might need to check your device for an authorization dialog.
[√] HTTP Host Availability
• All required HTTP hosts are available
• No issues found!
PS:
I'm not much familiar with Flutter/ bloc library.
Not sure if you still having this issue but I had a similar issue and I added isExpanded: true to my DropdownButtonFormField which resolved the error. My problem was caused by the length of the words in my DropdownMenuItem. When I look at the response of your API call I see some very long names. I could be wrong though
Converting the SelectInputField to a StatefulWidget solved the issue.
I think DropdownButtonFormField need to be rendered itself upon activities on it. So, StatelessWidget cannot rerender itself causing this problem.
I'm trying to simulate a tap event in flutter using hitTest as described in this post: How can I simulate a tap event on a Flutter widget?
Even though the hitTest returns true, the onTap event of the GestureDetector is not triggered
heres my code:
import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart';
class TestHitTest extends StatefulWidget {
const TestHitTest({Key? key}) : super(key: key);
#override
State<TestHitTest> createState() => _TestHitTestState();
}
class _TestHitTestState extends State<TestHitTest> {
bool _testSuccessfull = false;
final GlobalKey _globalKey = GlobalKey();
#override
Widget build(BuildContext context) {
return Scaffold(
body: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
GestureDetector(
key: _globalKey,
onTap: (){
setState(() {
_testSuccessfull = true;
});
},
child: Container(
width: 200,
height: 100,
color: _testSuccessfull? Colors.green:Colors.red,
),
),
SizedBox(height: 100,),
GestureDetector(
onTap: () {
final RenderObject? renderObject = _globalKey.currentContext?.findRenderObject();
final RenderBox? renderBox = renderObject is RenderBox? renderObject:null;
final hitTestResult = BoxHitTestResult();
print(renderBox?.hitTest(hitTestResult, position: Offset(renderBox.size.width/2,renderBox.size.height/2)));
},
child: Container(
width: 200,
height: 100,
color: Colors.blue,
child: Text(
'Perform hittest',
),
),
),
],
),
),
);
}
}
Since my final use case involves triggering onTap methods of widgets that might be covered by other widgets, I can't just use GestureBinding. The problem also occurs when obtaining the context directly instead of via the global key.
Note: I don't want to call the onTap method directly
output of flutter doctor -v:
[✓] Flutter (Channel stable, 2.10.4, on macOS 12.3.1 21E258 darwin-x64, locale de-DE)
• Flutter version 2.10.4 at /Users/test/Developer/flutter
• Upstream repository https://github.com/flutter/flutter.git
• Framework revision c860cba910 (2 weeks ago), 2022-03-25 00:23:12 -0500
• Engine revision 57d3bac3dd
• Dart version 2.16.2
• DevTools version 2.9.2
[✓] Android toolchain - develop for Android devices (Android SDK version 32.1.0-rc1)
• Android SDK at /Users/test/Library/Android/sdk
• Platform android-32, build-tools 32.1.0-rc1
• Java binary at: /Applications/Development/Android Studio.app/Contents/jre/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 13.3)
• Xcode at /Applications/Xcode.app/Contents/Developer
• CocoaPods version 1.11.2
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 2021.1)
• Android Studio at /Applications/Development/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 11.0.11+0-b60-7590822)
[✓] IntelliJ IDEA Ultimate Edition (version 2020.3.3)
• IntelliJ at /Applications/Development/IntelliJ IDEA.app
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
[✓] VS Code (version 1.66.1)
• VS Code at /Applications/Development/Visual Studio Code.app/Contents
• Flutter extension version 3.38.1
[✓] Connected device (3 available)
• iPhone 13 (mobile) • 8EB86ECB-B851-40C7-B70B-3E93A91646B6 • ios • com.apple.CoreSimulator.SimRuntime.iOS-15-4 (simulator)
• macOS (desktop) • macos • darwin-x64 • macOS 12.3.1 21E258 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 100.0.4896.75
[✓] HTTP Host Availability
• All required HTTP hosts are available
• No issues found!
Finally figured it out: it is possible to use GestureBindings.handleEvent() instead of GestureBindings.handlePointerEvent(). That way, the specific HitTestResult can be passed and no Widgets that are before that widget can absorb the event.
Example:
//get render Box and perform hitTest
final RenderObject? renderObject = _globalKey.currentContext?.findRenderObject();
final RenderBox? renderBox = renderObject is RenderBox? renderObject:null;
final hitTestResult = BoxHitTestResult();
renderBox?.hitTest(hitTestResult, position: Offset(renderBox.size.width/2,renderBox.size.height/2));
//get BoxHitTestEntry
BoxHitTestEntry entry = hitTestResult.path.firstWhere((element) => element is BoxHitTestEntry) as BoxHitTestEntry;
//create Events and get GestureBinding Instance
GestureBinding instance = GestureBinding.instance!;
var event1 = PointerDownEvent(position: renderBox!.localToGlobal(Offset(renderBox.size.width/2,renderBox.size.height/2)));
var event2 = PointerUpEvent(position: renderBox.localToGlobal(Offset(renderBox.size.width/2,renderBox.size.height/2)));
//dispatch and handle events using GestureBinding
instance.dispatchEvent(event1, hitTestResult);
instance.dispatchEvent(event2, hitTestResult);
instance.handleEvent(event1, entry);
instance.handleEvent(event2, entry);
What am I trying to do?
As usual, I am trying to sending data from one screen to another onGenerateRoute() but I screen does not accept arguments type and showing error The argument type 'Object?' can't be assigned to the parameter type 'Map<String, dynamic>'.
I also tried to change the argument type Object? on the receiver screen but now I am not able to extract data from Object to Map<String, dynamic>. I think both are the same data type but the null-safety version is treated differently. I think this is a bug.
I have seen flutter official documentation for navigate-with-arguments and when I switched to null-safety in Interactive example section then it also showing error. See this screenshot or try it yourself.
It's working properly in the non-null-safety version of flutter
Here's the snippet
RouteGenerator Class
class RouteGenerator {
static Route<dynamic> generateRoute(RouteSettings settings) {
// Getting arguments passed while calling Navigator.pushNamed
final args = settings.arguments;
switch (settings.name) {
case HomeScreen.routeName:
return MaterialPageRoute(
builder: (context) => HomeScreen(),
);
case LoginScreen.routeName:
return MaterialPageRoute(
builder: (context) => LoginScreen(),
);
case VerifyFirebaseOtpScreen.routeName:
return MaterialPageRoute(
builder: (context) => VerifyFirebaseOtpScreen(data: args), // Here is the error: The argument type 'Object?' can't be assigned to the parameter type 'Map<String, dynamic>'.
);
case AboutScreen.routeName:
return MaterialPageRoute(
builder: (context) => AboutScreen(),
);
default:
return MaterialPageRoute(
builder: (context) => Scaffold(
body: SafeArea(
child: Center(
child: Text('No route defined for ${settings.name}'),
),
),
),
);
}
}
}
VerifyFirebaseOtpScreen
class VerifyFirebaseOtpScreen extends StatelessWidget {
static const String routeName = '/verify_firebase_otp_screen';
final Map<String, dynamic> data;
const VerifyFirebaseOtpScreen({
Key? key,
required this.data,
}) : super(key: key);
#override
Widget build(BuildContext context) {
return GestureDetector(
onTap: () {
FocusScope.of(context).unfocus();
},
child: Scaffold(
body: Center(
child: SingleChildScrollView(
padding: const EdgeInsets.all(8.0),
child: Container(
width: double.infinity,
child: VerifyFirebaseOtpScreenDataSection(
mobile: '${data['mobile']}',
),
),
),
),
),
);
}
}
Logs
abhishekkumar#Abhisheks-MacBook-Air ~ % flutter doctor -v
[✓] Flutter (Channel beta, 2.1.0-12.2.pre, on macOS 11.2.3 20D91 darwin-x64, locale en-IN)
• Flutter version 2.1.0-12.2.pre at /Users/abhishekkumar/flutter
• Framework revision 5bedb7b1d5 (13 days ago), 2021-03-17 17:06:30 -0700
• Engine revision 711ab3fda0
• Dart version 2.13.0 (build 2.13.0-116.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.2)
• Android SDK at /Users/abhishekkumar/Library/Android/sdk
• Platform android-30, build-tools 30.0.2
• ANDROID_HOME = /Users/abhishekkumar/Library/Android/sdk
• Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.1
[✓] Chrome - develop for the web
• Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
[✓] Android Studio (version 4.1)
• Android Studio at /Applications/Android Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] VS Code (version 1.51.1)
• VS Code at /Applications/Visual Studio Code.app/Contents
• Flutter extension version 3.17.0
[✓] Connected device (3 available)
• iPhone SE (1st generation) (mobile) • 035FA189-09FF-46B5-96AC-C34E8D068C21 • ios • com.apple.CoreSimulator.SimRuntime.iOS-14-4 (simulator)
• macOS (desktop) • macos • darwin-x64 • macOS 11.2.3 20D91 darwin-x64
• Chrome (web) • chrome • web-javascript • Google Chrome 89.0.4389.90
• No issues found!
Answer in short
Use typecast operator as
so above question's answer will be like final args = settings.arguments as Map<String, dynamic>;
Answer explanation
I also filed this question as an issue on GitHub and thanks to goderbauer (A Flutter Team Member) who identify correctly this issue and closed it by giving an appropriate solution.
goderbauer says
In your example settings.arguments is typed as Object? and you're passing it to VerifyFirebaseOtpScreen.data which is typed as Map<String, dynamic>. Prior to null-safety this was legal and is called an implicit downcast. But with null safety Dart has removed implicit downcast altogether (you can read more about that here https://dart.dev/null-safety/understanding-null-safety, just search for "implicit downcast" on the page). So now, if you're sure that settings.arguments is in deed of type Map<String, dynamic> you need to do an explicit cast, something like: settings.arguments as Map<String, dynamic>.
He also said
(The example on the page will have to be updated as well once we migrate them to null safety)
dart.dev explanation and example
Referred documentation page Understanding null-safety & using-nullable-types explanation also covering this.
Their below example is explain enough
// Without null safety:
requireStringNotObject(String definitelyString) {
print(definitelyString.length);
}
main() {
Object maybeString = 'it is';
requireStringNotObject(maybeString);
}
// Using null safety:
requireStringNotObject(String definitelyString) {
print(definitelyString.length);
}
main() {
Object maybeString = 'it is';
requireStringNotObject(maybeString as String);
}
Hi I have spent a couple of days on trying to figure out why my user looses there "session" on my app when I close the app. How do I keep them signed in or what might cause this?
I am running the app on my google pixel 5 using the simulator option on android studio.
yaml file:
firebase_core: ^0.4.0+9
firebase_auth: ^0.14.0+9
cloud_firestore: ^0.13.6
firebase_storage: 3.1.6
firebase_messaging: 6.0.16
firebase_crashlytics: 0.1.4+1
This is what I can see in my logs (Not helpful much):
D/FlutterLocationService(32338): Unbinding from location service.
D/FlutterLocationService(32338): Destroying service.
D/FlutterLocationService(32338): Creating service.
D/FlutterLocationService(32338): Binding to location service.
flutter doctor -v:
(base) darrendupreez#Darrens-MacBook-Pro unavine_app % flutter doctor
-v [✓] Flutter (Channel dev, 1.26.0-12.0.pre, on macOS 11.1 20C69 darwin-x64, locale en-CA)
• Flutter version 1.26.0-12.0.pre at /Applications/flutter
• Framework revision a706cd2112 (2 weeks ago), 2021-01-14 18:20:26 -0500
• Engine revision effb529ece
• Dart version 2.12.0 (build 2.12.0-224.0.dev)
[✓] Android toolchain - develop for Android devices (Android SDK
version 30.0.3)
• Android SDK at /Users/darrendupreez/Library/Android/sdk
• Platform android-30, build-tools 30.0.3
• Java binary at: /Users/darrendupreez/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7042882/Android
Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 12.4, Build version 12D4e
• CocoaPods version 1.10.1
[✓] Android Studio (version 4.1)
• Android Studio at /Users/darrendupreez/Library/Application Support/JetBrains/Toolbox/apps/AndroidStudio/ch-0/201.7042882/Android
Studio.app/Contents
• Flutter plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/9212-flutter
• Dart plugin can be installed from:
🔨 https://plugins.jetbrains.com/plugin/6351-dart
• Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
[✓] Connected device (1 available)
• Pixel 5 (mobile) • 0C161FDD4000DT • android-arm64 • Android 11 (API 30)
• No issues found!
Providers and AuthCreditental should work here is an example for google login with firebase
main.dart
class MyApp extends StatelessWidget {
// This widget is the root of your application.
#override
Widget build(BuildContext context) {
return Provider(
create: (context) => AuthBloc(),
child: MaterialApp(
title: 'Your App',
debugShowCheckedModeBanner: false,
theme: ThemeData(
primarySwatch: MaterialColor(0xff360A36, color),
accentColor: Colors.limeAccent,
),
//darkTheme: ThemeData.dark(),
home: LogInPage(),
),
);
}
}
auth_bloc.dart
class AuthBloc {
final authService = AuthService();
final googleSignin = GoogleSignIn(scopes: ['email']);
Stream<User> get currentUser => authService.currentUser;
loginGoogle() async {
try {
final GoogleSignInAccount googleUser = await googleSignin.signIn();
final GoogleSignInAuthentication googleAuth =
await googleUser.authentication;
final AuthCredential credential = GoogleAuthProvider.credential(
idToken: googleAuth.idToken, accessToken: googleAuth.accessToken);
//Firebase Sign in
final result = await authService.signInWithCredential(credential);
print('${result.user.displayName}');
} catch (error) {
print(error);
}
}
logout() {
authService.logout();
googleSignin.signOut();
}
sign in button
SignInButton(
Buttons.Google,
text: "Sign in with Google",
onPressed: () => authBloc.loginGoogle(),
),
Also you can watch this tutorial for using credentials and providers
https://www.youtube.com/watch?v=_uYO2ht5Nl4&list=PL19w2HZFNl2BsftambzhFE7tVrqGEVwzy&index=1&t=1924s
You can use shared_preferences: ^0.5.12+4 to save the session.Just check whether it is null or not. And when you need you can update the data with new login.
Sample:
SharedPreferences prefs = await SharedPreferences.getInstance(); int counter = (prefs.getInt('counter') ?? 0) + 1; print('Pressed $counter times.');
Firebase auth automatically manage the auth state for your project but you have to access that auth state in your project to keep user data in the application.
You can use the authStateChange function to make it work.
Stream<User> get currentUser => _auth.authStateChanges();
And the user state in your main function using the Provider Package. Also wrap your `MaterialApp()' function by the following code:
MultiProvider(
providers: [
StreamProvider<User>.value(
value: Auth().currentUser,
)
],
child: GetMaterialApp()
)
I-am developing an app for pushing notifications using FCM, when the app in background or terminated and receiving new notification i need to save the data of this notification local in (SQLITE), without click on the notification or re-open the app again, the notification not read in the app unless clicked. Any suggestions?
This my NotificationHandler.dart
import 'dart:async';
import 'dart:io';
import 'package:eshaar/model/message.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:intl/intl.dart';
import 'package:eshaar/model/database_helper.dart';
FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
new FlutterLocalNotificationsPlugin();
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
print("onBackgroundMessage: $message");
//_showBigPictureNotification(message);
return Future<void>.value();
}
Future onSelectNotification(String payload) async {
}
class NotificationHandler {
FirebaseMessaging _fcm = FirebaseMessaging();
StreamSubscription iosSubscription;
DatabaseHelper db = new DatabaseHelper();
static final NotificationHandler _singleton =
new NotificationHandler._internal();
factory NotificationHandler() {
return _singleton;
}
NotificationHandler._internal();
initializeFcmNotification() async {
var initializationSettingsAndroid =
new AndroidInitializationSettings('mipmap/ic_launcher');
var initializationSettingsIOS = new IOSInitializationSettings(
onDidReceiveLocalNotification: onDidReceiveLocalNotification);
var initializationSettings = new InitializationSettings(
initializationSettingsAndroid, initializationSettingsIOS);
flutterLocalNotificationsPlugin.initialize(initializationSettings,
onSelectNotification: onSelectNotification);
if (Platform.isIOS) {
iosSubscription = _fcm.onIosSettingsRegistered.listen((data) {
// save the token OR subscribe to a topic here
});
_fcm.requestNotificationPermissions(IosNotificationSettings());
} else {
_saveDeviceToken();
}
_fcm.configure(
onMessage: (Map<String, dynamic> message) async {
print("---------------------On Message--------------------");
displayNotification(message['data']['title'],message['data']['body']);
_saveNotificationToLocal(message);
},
onBackgroundMessage: Platform.isIOS ? null : myBackgroundMessageHandler ,
onLaunch: (Map<String, dynamic> message) async {
print("---------------------On Lunch--------------------");
_saveNotificationToLocal(message);
},
onResume: (Map<String, dynamic> message) async {
print("---------------------On Resume--------------------");
_saveNotificationToLocal(message);
},
);
}
_saveNotificationToLocal(Map<String, dynamic> message){
String sender_id = message['data']['sender_id'];
String message_id = message['data']['message_id'];
String title = message['data']['title'];
String body = message['data']['body'];
String del_time = getCurrentDateTime();
Messages msg = new Messages(sender_id,message_id,title,body,del_time);
db.saveMessages(msg);
}
String getCurrentDateTime(){
var now = new DateTime.now();
var formatter = new DateFormat('MMM d yy h:mm a');
String formatted = formatter.format(now);
return formatted;
}
/// Get the token, save it to the database for current user
_saveDeviceToken() async {
String fcmToken = await _fcm.getToken();
}
Future<void> onDidReceiveLocalNotification(
int id, String title, String body, String payload) async {
// display a dialog with the notification details, tap ok to go to another page
}
void displayNotification(String title,String body) async{
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(
androidPlatformChannelSpecifics, iOSPlatformChannelSpecifics);
await flutterLocalNotificationsPlugin.show(
0, '$title', '$body', platformChannelSpecifics,
payload: 'item x');
}
}
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="ot.eshaar.eshaar">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.VIBRATE" />
<!-- io.flutter.app.FlutterApplication is an android.app.Application that
calls FlutterMain.startInitialization(this); in its onCreate method.
In most cases you can leave this as-is, but you if you want to provide
additional functionality it is fine to subclass or reimplement
FlutterApplication and put your custom class here. -->
<application
android:name=".Application"
android:label="eshaar"
android:icon="#mipmap/ic_launcher">
<activity
android:name=".MainActivity"
android:launchMode="singleTop"
android:theme="#style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<!-- This keeps the window background of the activity showing
until Flutter renders its first frame. It can be removed if
there is no splash screen (such as the default splash screen
defined in #style/LaunchTheme). -->
<meta-data
android:name="io.flutter.app.android.SplashScreenUntilFirstFrame"
android:value="true" />
<meta-data
android:name="com.google.firebase.messaging.android.channel_id"
android:value="mmne" />
<meta-data android:name="com.google.firebase.messaging.default_notification_channel_id" android:value="#string/default_notification_channel_id"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationBootReceiver">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"></action>
</intent-filter>
</receiver>
<receiver android:name="com.dexterous.flutterlocalnotifications.ScheduledNotificationReceiver" />
</application>
</manifest>
Flutter doctor -v
[✓] Flutter (Channel stable, v1.9.1+hotfix.6, on Mac OS X 10.14.4 18E226, locale
en-US)
• Flutter version 1.9.1+hotfix.6 at /Users/mahmoudabdelaziz/Desktop/flutter
• Framework revision 68587a0916 (3 months ago), 2019-09-13 19:46:58 -0700
• Engine revision b863200c37
• Dart version 2.5.0
[✓] Android toolchain - develop for Android devices (Android SDK version 28.0.3)
• Android SDK at /Users/mahmoudabdelaziz/Library/Android/sdk
• Android NDK location not configured (optional; useful for native profiling
support)
• Platform android-28, build-tools 28.0.3
• Java binary at: /Applications/Android
Studio.app/Contents/jre/jdk/Contents/Home/bin/java
• Java version OpenJDK Runtime Environment (build
1.8.0_152-release-1343-b01)
• All Android licenses accepted.
[✓] Xcode - develop for iOS and macOS (Xcode 11.0)
• Xcode at /Applications/Xcode.app/Contents/Developer
• Xcode 11.0, Build version 11A420a
• CocoaPods version 1.8.4
[!] Android Studio (version 3.4)
• Android Studio at /Applications/Android Studio.app/Contents
✗ Flutter plugin not installed; this adds Flutter specific functionality.
✗ Dart plugin not installed; this adds Dart specific functionality.
• Java version OpenJDK Runtime Environment (build
1.8.0_152-release-1343-b01)
[✓] IntelliJ IDEA Community Edition (version 2018.3.3)
• IntelliJ at /Applications/IntelliJ IDEA CE.app
• Flutter plugin version 31.3.4
• Dart plugin version 183.5153.38
[✓] Connected device (1 available)
• Android SDK built for x86 • emulator-5554 • android-x86 • Android 9 (API
28) (emulator)
In my case, FCM onBackgroundMessage is triggered when only there is no notification on the payload. if there a notification on the payload what it does is it sends shows you notification on the system tray.
But if you want to do some compilation on the data sent then remove the notification on the payload and send data only.
{
"to" : "Place the token of the test device here",
"click_action": "FLUTTER_NOTIFICATION_CLICK",
"priority": "high",
"data" : {
"type" : "anything",
"title": "Title of Your Notification in Title",
"key_1" : "Value for key_2"
}
}
UPDATED 2021
Make sure to add content_available on the notification this worked for me now. I am using firebase_messaging prereplase. On this release they have enabled background handler out of the box.
https://pub.dev/packages/firebase_messaging/versions/8.0.0-dev.14
{
"data":{
"title":"mytitle",
"body":"mybody",
"url":"myurl"
},
"notification":{
"title":"mytitle",
"body":"mybody",
"content_available": true
},
"to":"/topics/topic"
"click_action": "FLUTTER_NOTIFICATION_CLICK",
}
It's late but still I'll respond for others to be helpful.
The issue here isn't with the handling but with type of message you are sending from the Firebase console.
onBackgroundMessage callBack is called only if the message is a data message or a notification message with a data payload.
And make sure your message also have click_action: 'FLUTTER_NOTIFICATION_CLICK' added.
And there as well is some issue with generating messages from Firebase console, so try to generate messages directly from your server.
For more details please checkout this thread.
please make sure that you have added click_action property while sending the notification.
make sure its value matches with the one you have provided in the android intent.
{
notification: {
title: 'Title',
body: 'Body',
click_action: 'FLUTTER_NOTIFICATION_CLICK'
} }
In myBackgroundMessageHandler you need to call that function to save the notification to your local storage.
For example
Future<dynamic> myBackgroundMessageHandler(Map<String, dynamic> message) async {
print("onBackgroundMessage: $message");
//_showBigPictureNotification(message);
NotificationHandler()._saveNotificationToLocal(message);
return Future<void>.value();
}
Hope that helps.
With postman send this and "content_available": true trigger _firebaseMessagingBackgroundHandler
{
"to": "_Token",
"notification": {
"title": "Title",
"body": "body body body"
},"data": {
"title": "Title",
"message": "body body body"
},
"apns": {
"headers": {
"apns-priority": "5"
}
},
"priority": "high",
"content_available": true
}