fix: Init Notification Push iOS Ok
This commit is contained in:
parent
460ad3bbbd
commit
87094d39c3
|
@ -8,6 +8,7 @@
|
||||||
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
|
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"/>
|
||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
<uses-permission android:name="android.permission.READ_PHONE_STATE"/>
|
||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="FREHub"
|
android:label="FREHub"
|
||||||
|
@ -22,7 +23,10 @@
|
||||||
android:theme="@style/LaunchTheme"
|
android:theme="@style/LaunchTheme"
|
||||||
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
|
||||||
android:hardwareAccelerated="true"
|
android:hardwareAccelerated="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize"
|
||||||
|
android:showWhenLocked="true"
|
||||||
|
android:turnScreenOn="true">
|
||||||
|
|
||||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||||
the Android process has started. This theme is visible to the user
|
the Android process has started. This theme is visible to the user
|
||||||
while the Flutter UI initializes. After that, this theme continues
|
while the Flutter UI initializes. After that, this theme continues
|
||||||
|
@ -52,6 +56,7 @@
|
||||||
android:resource="@drawable/ic_fre_black" />
|
android:resource="@drawable/ic_fre_black" />
|
||||||
|
|
||||||
<intent-filter android:autoVerify="true">
|
<intent-filter android:autoVerify="true">
|
||||||
|
<action android:name="FLUTTER_NOTIFICATION_CLICK" />
|
||||||
<action android:name="android.intent.action.VIEW" />
|
<action android:name="android.intent.action.VIEW" />
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
<category android:name="android.intent.category.BROWSABLE" />
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
|
@ -193,6 +193,7 @@ PODS:
|
||||||
- Flutter
|
- Flutter
|
||||||
- local_auth_darwin (0.0.1):
|
- local_auth_darwin (0.0.1):
|
||||||
- Flutter
|
- Flutter
|
||||||
|
- FlutterMacOS
|
||||||
- nanopb (2.30910.0):
|
- nanopb (2.30910.0):
|
||||||
- nanopb/decode (= 2.30910.0)
|
- nanopb/decode (= 2.30910.0)
|
||||||
- nanopb/encode (= 2.30910.0)
|
- nanopb/encode (= 2.30910.0)
|
||||||
|
@ -346,7 +347,7 @@ SPEC CHECKSUMS:
|
||||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||||
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
||||||
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
image_picker_ios: c560581cceedb403a6ff17f2f816d7fea1421fc1
|
||||||
local_auth_darwin: 4d56c90c2683319835a61274b57620df9c4520ab
|
local_auth_darwin: 66e40372f1c29f383a314c738c7446e2f7fdadc3
|
||||||
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
||||||
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
OrderedSet: aaeb196f7fef5a9edf55d89760da9176ad40b93c
|
||||||
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
path_provider_foundation: 2b6b4c569c0fb62ec74538f866245ac84301af46
|
||||||
|
@ -360,7 +361,7 @@ SPEC CHECKSUMS:
|
||||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||||
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
url_launcher_ios: 5334b05cef931de560670eeae103fd3e431ac3fe
|
||||||
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
video_player_avfoundation: 7c6c11d8470e1675df7397027218274b6d2360b3
|
||||||
webview_flutter_wkwebview: be0f0d33777f1bfd0c9fdcb594786704dbf65f36
|
webview_flutter_wkwebview: 2a23822e9039b7b1bc52e5add778e5d89ad488d1
|
||||||
|
|
||||||
PODFILE CHECKSUM: d7f4d1b71f8c708247c1078c4aec33a28c763405
|
PODFILE CHECKSUM: d7f4d1b71f8c708247c1078c4aec33a28c763405
|
||||||
|
|
||||||
|
|
|
@ -1,351 +0,0 @@
|
||||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:hub/backend/notification/message.dart';
|
|
||||||
import 'package:hub/backend/notification/message_list.dart';
|
|
||||||
import 'package:hub/backend/notification/permissions.dart';
|
|
||||||
import 'package:hub/backend/notification/token_monitor.dart';
|
|
||||||
import 'package:hub/firebase_options.dart';
|
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
|
||||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
||||||
await setupFlutterNotifications();
|
|
||||||
showFlutterNotification(message);
|
|
||||||
// If you're going to use other Firebase services in the background, such as Firestore,
|
|
||||||
// make sure you call `initializeApp` before using other Firebase services.
|
|
||||||
print('Handling a background message ${message.messageId}');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a [AndroidNotificationChannel] for heads up notifications
|
|
||||||
late AndroidNotificationChannel channel;
|
|
||||||
|
|
||||||
bool isFlutterLocalNotificationsInitialized = false;
|
|
||||||
|
|
||||||
Future<void> setupFlutterNotifications() async {
|
|
||||||
if (isFlutterLocalNotificationsInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
channel = const AndroidNotificationChannel(
|
|
||||||
'high_importance_channel', // id
|
|
||||||
'High Importance Notifications', // title
|
|
||||||
description:
|
|
||||||
'This channel is used for important notifications.', // description
|
|
||||||
importance: Importance.high,
|
|
||||||
);
|
|
||||||
|
|
||||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
|
||||||
|
|
||||||
/// Create an Android Notification Channel.
|
|
||||||
///
|
|
||||||
/// We use this channel in the `AndroidManifest.xml` file to override the
|
|
||||||
/// default FCM channel to enable heads up notifications.
|
|
||||||
await flutterLocalNotificationsPlugin
|
|
||||||
.resolvePlatformSpecificImplementation<
|
|
||||||
AndroidFlutterLocalNotificationsPlugin>()
|
|
||||||
?.createNotificationChannel(channel);
|
|
||||||
|
|
||||||
/// Update the iOS foreground notification presentation options to allow
|
|
||||||
/// heads up notifications.
|
|
||||||
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
|
|
||||||
alert: true,
|
|
||||||
badge: true,
|
|
||||||
sound: true,
|
|
||||||
);
|
|
||||||
isFlutterLocalNotificationsInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void showFlutterNotification(RemoteMessage message) {
|
|
||||||
RemoteNotification? notification = message.notification;
|
|
||||||
AndroidNotification? android = message.notification?.android;
|
|
||||||
if (notification != null && android != null && !kIsWeb) {
|
|
||||||
flutterLocalNotificationsPlugin.show(
|
|
||||||
notification.hashCode,
|
|
||||||
notification.title,
|
|
||||||
notification.body,
|
|
||||||
NotificationDetails(
|
|
||||||
android: AndroidNotificationDetails(
|
|
||||||
channel.id,
|
|
||||||
channel.name,
|
|
||||||
channelDescription: channel.description,
|
|
||||||
// TODO add a proper drawable resource to android, for now using
|
|
||||||
// one that already exists in example app.
|
|
||||||
icon: 'launch_background',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the [FlutterLocalNotificationsPlugin] package.
|
|
||||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
||||||
// Set the background messaging handler early on, as a named top-level function
|
|
||||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
||||||
|
|
||||||
if (!kIsWeb) {
|
|
||||||
await setupFlutterNotifications();
|
|
||||||
}
|
|
||||||
|
|
||||||
runApp(MessagingExampleApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entry point for the example application.
|
|
||||||
class MessagingExampleApp extends StatelessWidget {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Messaging Example App',
|
|
||||||
theme: ThemeData.dark(),
|
|
||||||
routes: {
|
|
||||||
'/': (context) => Application(),
|
|
||||||
'/message': (context) => MessageView(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crude counter to make messages unique
|
|
||||||
int _messageCount = 0;
|
|
||||||
|
|
||||||
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
|
|
||||||
String constructFCMPayload(String? token) {
|
|
||||||
_messageCount++;
|
|
||||||
return jsonEncode({
|
|
||||||
'token': token,
|
|
||||||
'data': {
|
|
||||||
'via': 'FlutterFire Cloud Messaging!!!',
|
|
||||||
'count': _messageCount.toString(),
|
|
||||||
},
|
|
||||||
'notification': {
|
|
||||||
'title': 'Hello FlutterFire!',
|
|
||||||
'body': 'This notification (#$_messageCount) was created via FCM!',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders the example application.
|
|
||||||
class Application extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _Application();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Application extends State<Application> {
|
|
||||||
String? _token;
|
|
||||||
String? initialMessage;
|
|
||||||
bool _resolved = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
FirebaseMessaging.instance.getInitialMessage().then(
|
|
||||||
(value) => setState(
|
|
||||||
() {
|
|
||||||
_resolved = true;
|
|
||||||
initialMessage = value?.data.toString();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
FirebaseMessaging.onMessage.listen(showFlutterNotification);
|
|
||||||
|
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
||||||
print('A new onMessageOpenedApp event was published!');
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
'/message',
|
|
||||||
arguments: MessageArguments(message, true),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> sendPushMessage() async {
|
|
||||||
if (_token == null) {
|
|
||||||
print('Unable to send FCM message, no token exists.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await http.post(
|
|
||||||
Uri.parse('https://api.rnfirebase.io/messaging/send'),
|
|
||||||
headers: <String, String>{
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: constructFCMPayload(_token),
|
|
||||||
);
|
|
||||||
print('FCM request for device sent!');
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> onActionSelected(String value) async {
|
|
||||||
switch (value) {
|
|
||||||
case 'subscribe':
|
|
||||||
{
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Subscribing to topic "fcm_test".',
|
|
||||||
);
|
|
||||||
await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'unsubscribe':
|
|
||||||
{
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".',
|
|
||||||
);
|
|
||||||
await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'get_apns_token':
|
|
||||||
{
|
|
||||||
if (defaultTargetPlatform == TargetPlatform.iOS ||
|
|
||||||
defaultTargetPlatform == TargetPlatform.macOS) {
|
|
||||||
print('FlutterFire Messaging Example: Getting APNs token...');
|
|
||||||
String? token = await FirebaseMessaging.instance.getAPNSToken();
|
|
||||||
print('FlutterFire Messaging Example: Got APNs token: $token');
|
|
||||||
} else {
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Cloud Messaging'),
|
|
||||||
actions: <Widget>[
|
|
||||||
PopupMenuButton(
|
|
||||||
onSelected: onActionSelected,
|
|
||||||
itemBuilder: (BuildContext context) {
|
|
||||||
return [
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'subscribe',
|
|
||||||
child: Text('Subscribe to topic'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'unsubscribe',
|
|
||||||
child: Text('Unsubscribe to topic'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'get_apns_token',
|
|
||||||
child: Text('Get APNs token (Apple only)'),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
floatingActionButton: Builder(
|
|
||||||
builder: (context) => FloatingActionButton(
|
|
||||||
onPressed: sendPushMessage,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
child: const Icon(Icons.send),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
MetaCard('Permissions', Permissions()),
|
|
||||||
MetaCard(
|
|
||||||
'Initial Message',
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Text(_resolved ? 'Resolved' : 'Resolving'),
|
|
||||||
Text(initialMessage ?? 'None'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MetaCard(
|
|
||||||
'FCM Token',
|
|
||||||
TokenMonitor((token) {
|
|
||||||
_token = token;
|
|
||||||
return token == null
|
|
||||||
? const CircularProgressIndicator()
|
|
||||||
: SelectableText(
|
|
||||||
token,
|
|
||||||
style: const TextStyle(fontSize: 12),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
FirebaseMessaging.instance
|
|
||||||
.getInitialMessage()
|
|
||||||
.then((RemoteMessage? message) {
|
|
||||||
if (message != null) {
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
'/message',
|
|
||||||
arguments: MessageArguments(message, true),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: const Text('getInitialMessage()'),
|
|
||||||
),
|
|
||||||
MetaCard('Message Stream', MessageList()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// UI Widget for displaying metadata.
|
|
||||||
class MetaCard extends StatelessWidget {
|
|
||||||
final String _title;
|
|
||||||
final Widget _children;
|
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
|
||||||
MetaCard(this._title, this._children);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
|
|
||||||
child: Card(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
|
||||||
child: Text(_title, style: const TextStyle(fontSize: 18)),
|
|
||||||
),
|
|
||||||
_children,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,101 +0,0 @@
|
||||||
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// File generated by FlutterFire CLI.
|
|
||||||
// ignore_for_file: lines_longer_than_80_chars, avoid_classes_with_only_static_members
|
|
||||||
import 'package:firebase_core/firebase_core.dart' show FirebaseOptions;
|
|
||||||
import 'package:flutter/foundation.dart'
|
|
||||||
show defaultTargetPlatform, kIsWeb, TargetPlatform;
|
|
||||||
|
|
||||||
/// Default [FirebaseOptions] for use with your Firebase apps.
|
|
||||||
///
|
|
||||||
/// Example:
|
|
||||||
/// ```dart
|
|
||||||
/// import 'firebase_options.dart';
|
|
||||||
/// // ...
|
|
||||||
/// await Firebase.initializeApp(
|
|
||||||
/// options: DefaultFirebaseOptions.currentPlatform,
|
|
||||||
/// );
|
|
||||||
/// ```
|
|
||||||
class DefaultFirebaseOptions {
|
|
||||||
static FirebaseOptions get currentPlatform {
|
|
||||||
if (kIsWeb) {
|
|
||||||
return web;
|
|
||||||
}
|
|
||||||
switch (defaultTargetPlatform) {
|
|
||||||
case TargetPlatform.android:
|
|
||||||
return android;
|
|
||||||
case TargetPlatform.iOS:
|
|
||||||
return ios;
|
|
||||||
case TargetPlatform.macOS:
|
|
||||||
return macos;
|
|
||||||
case TargetPlatform.windows:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions have not been configured for windows - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
case TargetPlatform.linux:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions have not been configured for linux - '
|
|
||||||
'you can reconfigure this by running the FlutterFire CLI again.',
|
|
||||||
);
|
|
||||||
default:
|
|
||||||
throw UnsupportedError(
|
|
||||||
'DefaultFirebaseOptions are not supported for this platform.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static const FirebaseOptions web = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyB7wZb2tO1-Fs6GbDADUSTs2Qs3w08Hovw',
|
|
||||||
appId: '1:406099696497:web:87e25e51afe982cd3574d0',
|
|
||||||
messagingSenderId: '406099696497',
|
|
||||||
projectId: 'flutterfire-e2e-tests',
|
|
||||||
authDomain: 'flutterfire-e2e-tests.firebaseapp.com',
|
|
||||||
databaseURL:
|
|
||||||
'https://flutterfire-e2e-tests-default-rtdb.europe-west1.firebasedatabase.app',
|
|
||||||
storageBucket: 'flutterfire-e2e-tests.appspot.com',
|
|
||||||
measurementId: 'G-JN95N1JV2E',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions android = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyCdRjCVZlhrq72RuEklEyyxYlBRCYhI2Sw',
|
|
||||||
appId: '1:406099696497:android:74ebb073d7727cd43574d0',
|
|
||||||
messagingSenderId: '406099696497',
|
|
||||||
projectId: 'flutterfire-e2e-tests',
|
|
||||||
databaseURL:
|
|
||||||
'https://flutterfire-e2e-tests-default-rtdb.europe-west1.firebasedatabase.app',
|
|
||||||
storageBucket: 'flutterfire-e2e-tests.appspot.com',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions ios = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyDooSUGSf63Ghq02_iIhtnmwMDs4HlWS6c',
|
|
||||||
appId: '1:406099696497:ios:1b423b89c63b82053574d0',
|
|
||||||
messagingSenderId: '406099696497',
|
|
||||||
projectId: 'flutterfire-e2e-tests',
|
|
||||||
databaseURL:
|
|
||||||
'https://flutterfire-e2e-tests-default-rtdb.europe-west1.firebasedatabase.app',
|
|
||||||
storageBucket: 'flutterfire-e2e-tests.appspot.com',
|
|
||||||
androidClientId:
|
|
||||||
'406099696497-17qn06u8a0dc717u8ul7s49ampk13lul.apps.googleusercontent.com',
|
|
||||||
iosClientId:
|
|
||||||
'406099696497-irb7edfevfkhi6t5s9kbuq1mt1og95rg.apps.googleusercontent.com',
|
|
||||||
iosBundleId: 'io.flutter.plugins.firebase.messaging',
|
|
||||||
);
|
|
||||||
|
|
||||||
static const FirebaseOptions macos = FirebaseOptions(
|
|
||||||
apiKey: 'AIzaSyDooSUGSf63Ghq02_iIhtnmwMDs4HlWS6c',
|
|
||||||
appId: '1:406099696497:ios:1b423b89c63b82053574d0',
|
|
||||||
messagingSenderId: '406099696497',
|
|
||||||
projectId: 'flutterfire-e2e-tests',
|
|
||||||
databaseURL:
|
|
||||||
'https://flutterfire-e2e-tests-default-rtdb.europe-west1.firebasedatabase.app',
|
|
||||||
storageBucket: 'flutterfire-e2e-tests.appspot.com',
|
|
||||||
androidClientId:
|
|
||||||
'406099696497-17qn06u8a0dc717u8ul7s49ampk13lul.apps.googleusercontent.com',
|
|
||||||
iosClientId:
|
|
||||||
'406099696497-irb7edfevfkhi6t5s9kbuq1mt1og95rg.apps.googleusercontent.com',
|
|
||||||
iosBundleId: 'io.flutter.plugins.firebase.messaging',
|
|
||||||
);
|
|
||||||
}
|
|
|
@ -1,163 +0,0 @@
|
||||||
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// ignore_for_file: require_trailing_commas
|
|
||||||
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// Message route arguments.
|
|
||||||
class MessageArguments {
|
|
||||||
/// The RemoteMessage
|
|
||||||
final RemoteMessage message;
|
|
||||||
|
|
||||||
/// Whether this message caused the application to open.
|
|
||||||
final bool openedApplication;
|
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
|
||||||
MessageArguments(this.message, this.openedApplication);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Displays information about a [RemoteMessage].
|
|
||||||
class MessageView extends StatelessWidget {
|
|
||||||
/// A single data row.
|
|
||||||
Widget row(String title, String? value) {
|
|
||||||
return Padding(
|
|
||||||
padding: const EdgeInsets.only(left: 8, right: 8, top: 8),
|
|
||||||
child: Row(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Text('$title: '),
|
|
||||||
Expanded(child: Text(value ?? 'N/A')),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
final MessageArguments args =
|
|
||||||
ModalRoute.of(context)!.settings.arguments! as MessageArguments;
|
|
||||||
RemoteMessage message = args.message;
|
|
||||||
RemoteNotification? notification = message.notification;
|
|
||||||
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: Text(message.messageId ?? 'N/A'),
|
|
||||||
),
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(8),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
row('Triggered application open',
|
|
||||||
args.openedApplication.toString()),
|
|
||||||
row('Message ID', message.messageId),
|
|
||||||
row('Sender ID', message.senderId),
|
|
||||||
row('Category', message.category),
|
|
||||||
row('Collapse Key', message.collapseKey),
|
|
||||||
row('Content Available', message.contentAvailable.toString()),
|
|
||||||
row('Data', message.data.toString()),
|
|
||||||
row('From', message.from),
|
|
||||||
row('Message ID', message.messageId),
|
|
||||||
row('Sent Time', message.sentTime?.toString()),
|
|
||||||
row('Thread ID', message.threadId),
|
|
||||||
row('Time to Live (TTL)', message.ttl?.toString()),
|
|
||||||
if (notification != null) ...[
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 16),
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
const Text(
|
|
||||||
'Remote Notification',
|
|
||||||
style: TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Title',
|
|
||||||
notification.title,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Body',
|
|
||||||
notification.body,
|
|
||||||
),
|
|
||||||
if (notification.android != null) ...[
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
const Text(
|
|
||||||
'Android Properties',
|
|
||||||
style: TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Channel ID',
|
|
||||||
notification.android!.channelId,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Click Action',
|
|
||||||
notification.android!.clickAction,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Color',
|
|
||||||
notification.android!.color,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Count',
|
|
||||||
notification.android!.count?.toString(),
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Image URL',
|
|
||||||
notification.android!.imageUrl,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Link',
|
|
||||||
notification.android!.link,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Priority',
|
|
||||||
notification.android!.priority.toString(),
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Small Icon',
|
|
||||||
notification.android!.smallIcon,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Sound',
|
|
||||||
notification.android!.sound,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Ticker',
|
|
||||||
notification.android!.ticker,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Visibility',
|
|
||||||
notification.android!.visibility.toString(),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (notification.apple != null) ...[
|
|
||||||
const Text(
|
|
||||||
'Apple Properties',
|
|
||||||
style: TextStyle(fontSize: 18),
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Subtitle',
|
|
||||||
notification.apple!.subtitle,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Badge',
|
|
||||||
notification.apple!.badge,
|
|
||||||
),
|
|
||||||
row(
|
|
||||||
'Sound',
|
|
||||||
notification.apple!.sound?.name,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,53 +0,0 @@
|
||||||
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// ignore_for_file: require_trailing_commas
|
|
||||||
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
import 'message.dart';
|
|
||||||
|
|
||||||
/// Listens for incoming foreground messages and displays them in a list.
|
|
||||||
class MessageList extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _MessageList();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _MessageList extends State<MessageList> {
|
|
||||||
List<RemoteMessage> _messages = [];
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
FirebaseMessaging.onMessage.listen((RemoteMessage message) {
|
|
||||||
setState(() {
|
|
||||||
_messages = [..._messages, message];
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (_messages.isEmpty) {
|
|
||||||
return const Text('No messages received');
|
|
||||||
}
|
|
||||||
|
|
||||||
return ListView.builder(
|
|
||||||
shrinkWrap: true,
|
|
||||||
itemCount: _messages.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
RemoteMessage message = _messages[index];
|
|
||||||
|
|
||||||
return ListTile(
|
|
||||||
title: Text(
|
|
||||||
message.messageId ?? 'no RemoteMessage.messageId available'),
|
|
||||||
subtitle:
|
|
||||||
Text(message.sentTime?.toString() ?? DateTime.now().toString()),
|
|
||||||
onTap: () => Navigator.pushNamed(context, '/message',
|
|
||||||
arguments: MessageArguments(message, false)),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,383 +0,0 @@
|
||||||
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
||||||
// Use of this source code is governed by a BSD-style license that can be
|
|
||||||
// found in the LICENSE file.
|
|
||||||
|
|
||||||
import 'dart:async';
|
|
||||||
import 'dart:convert';
|
|
||||||
|
|
||||||
import 'package:firebase_core/firebase_core.dart';
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
|
||||||
import 'package:http/http.dart' as http;
|
|
||||||
import 'package:hub/backend/notification/message.dart';
|
|
||||||
import 'package:hub/backend/notification/message_list.dart';
|
|
||||||
import 'package:hub/backend/notification/permissions.dart';
|
|
||||||
import 'package:hub/backend/notification/token_monitor.dart';
|
|
||||||
import 'package:hub/firebase_options.dart';
|
|
||||||
|
|
||||||
// import 'firebase_options.dart';
|
|
||||||
// import 'message.dart';
|
|
||||||
// import 'message_list.dart';
|
|
||||||
// import 'permissions.dart';
|
|
||||||
// import 'token_monitor.dart';
|
|
||||||
|
|
||||||
/// Working example of FirebaseMessaging.
|
|
||||||
/// Please use this in order to verify messages are working in foreground, background & terminated state.
|
|
||||||
/// Setup your app following this guide:
|
|
||||||
/// https://firebase.google.com/docs/cloud-messaging/flutter/client#platform-specific_setup_and_requirements):
|
|
||||||
///
|
|
||||||
/// Once you've completed platform specific requirements, follow these instructions:
|
|
||||||
/// 1. Install melos tool by running `flutter pub global activate melos`.
|
|
||||||
/// 2. Run `melos bootstrap` in FlutterFire project.
|
|
||||||
/// 3. In your terminal, root to ./packages/firebase_messaging/firebase_messaging/example directory.
|
|
||||||
/// 4. Run `flutterfire configure` in the example/ directory to setup your app with your Firebase project.
|
|
||||||
/// 5. Open `token_monitor.dart` and change `vapidKey` to yours.
|
|
||||||
/// 6. Run the app on an actual device for iOS, android is fine to run on an emulator.
|
|
||||||
/// 7. Use the following script to send a message to your device: scripts/send-message.js. To run this script,
|
|
||||||
/// you will need nodejs installed on your computer. Then the following:
|
|
||||||
/// a. Download a service account key (JSON file) from your Firebase console, rename it to "google-services.json" and add to the example/scripts directory.
|
|
||||||
/// b. Ensure your device/emulator is running, and run the FirebaseMessaging example app using `flutter run`.
|
|
||||||
/// c. Copy the token that is printed in the console and paste it here: https://github.com/firebase/flutterfire/blob/01b4d357e1/packages/firebase_messaging/firebase_messaging/example/lib/main.dart#L32
|
|
||||||
/// c. From your terminal, root to example/scripts directory & run `npm install`.
|
|
||||||
/// d. Run `npm run send-message` in the example/scripts directory and your app will receive messages in any state; foreground, background, terminated.
|
|
||||||
/// Note: Flutter API documentation for receiving messages: https://firebase.google.com/docs/cloud-messaging/flutter/receive
|
|
||||||
/// Note: If you find your messages have stopped arriving, it is extremely likely they are being throttled by the platform. iOS in particular
|
|
||||||
/// are aggressive with their throttling policy.
|
|
||||||
///
|
|
||||||
/// To verify that your messages are being received, you ought to see a notification appearon your device/emulator via the flutter_local_notifications plugin.
|
|
||||||
/// Define a top-level named handler which background/terminated messages will
|
|
||||||
/// call. Be sure to annotate the handler with `@pragma('vm:entry-point')` above the function declaration.
|
|
||||||
@pragma('vm:entry-point')
|
|
||||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
||||||
await setupFlutterNotifications();
|
|
||||||
showFlutterNotification(message);
|
|
||||||
// If you're going to use other Firebase services in the background, such as Firestore,
|
|
||||||
// make sure you call `initializeApp` before using other Firebase services.
|
|
||||||
print('Handling a background message ${message.messageId}');
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Create a [AndroidNotificationChannel] for heads up notifications
|
|
||||||
late AndroidNotificationChannel channel;
|
|
||||||
|
|
||||||
bool isFlutterLocalNotificationsInitialized = false;
|
|
||||||
|
|
||||||
Future<void> setupFlutterNotifications() async {
|
|
||||||
if (isFlutterLocalNotificationsInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
channel = const AndroidNotificationChannel(
|
|
||||||
'high_importance_channel', // id
|
|
||||||
'High Importance Notifications', // title
|
|
||||||
description:
|
|
||||||
'This channel is used for important notifications.', // description
|
|
||||||
importance: Importance.high,
|
|
||||||
);
|
|
||||||
|
|
||||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
|
||||||
|
|
||||||
/// Create an Android Notification Channel.
|
|
||||||
///
|
|
||||||
/// We use this channel in the `AndroidManifest.xml` file to override the
|
|
||||||
/// default FCM channel to enable heads up notifications.
|
|
||||||
await flutterLocalNotificationsPlugin
|
|
||||||
.resolvePlatformSpecificImplementation<
|
|
||||||
AndroidFlutterLocalNotificationsPlugin>()
|
|
||||||
?.createNotificationChannel(channel);
|
|
||||||
|
|
||||||
/// Update the iOS foreground notification presentation options to allow
|
|
||||||
/// heads up notifications.
|
|
||||||
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
|
|
||||||
alert: true,
|
|
||||||
badge: true,
|
|
||||||
sound: true,
|
|
||||||
);
|
|
||||||
isFlutterLocalNotificationsInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void showFlutterNotification(RemoteMessage message) {
|
|
||||||
RemoteNotification? notification = message.notification;
|
|
||||||
AndroidNotification? android = message.notification?.android;
|
|
||||||
if (notification != null && android != null && !kIsWeb) {
|
|
||||||
flutterLocalNotificationsPlugin.show(
|
|
||||||
notification.hashCode,
|
|
||||||
notification.title,
|
|
||||||
notification.body,
|
|
||||||
NotificationDetails(
|
|
||||||
android: AndroidNotificationDetails(
|
|
||||||
channel.id,
|
|
||||||
channel.name,
|
|
||||||
channelDescription: channel.description,
|
|
||||||
// TODO add a proper drawable resource to android, for now using
|
|
||||||
// one that already exists in example app.
|
|
||||||
icon: 'launch_background',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Initialize the [FlutterLocalNotificationsPlugin] package.
|
|
||||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
|
||||||
|
|
||||||
Future<void> main() async {
|
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
||||||
// Set the background messaging handler early on, as a named top-level function
|
|
||||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
|
||||||
|
|
||||||
if (!kIsWeb) {
|
|
||||||
await setupFlutterNotifications();
|
|
||||||
}
|
|
||||||
|
|
||||||
runApp(MessagingExampleApp());
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Entry point for the example application.
|
|
||||||
class MessagingExampleApp extends StatelessWidget {
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return MaterialApp(
|
|
||||||
title: 'Messaging Example App',
|
|
||||||
theme: ThemeData.dark(),
|
|
||||||
routes: {
|
|
||||||
'/': (context) => Application(),
|
|
||||||
'/message': (context) => MessageView(),
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Crude counter to make messages unique
|
|
||||||
int _messageCount = 0;
|
|
||||||
|
|
||||||
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
|
|
||||||
String constructFCMPayload(String? token) {
|
|
||||||
_messageCount++;
|
|
||||||
return jsonEncode({
|
|
||||||
'token': token,
|
|
||||||
'data': {
|
|
||||||
'via': 'FlutterFire Cloud Messaging!!!',
|
|
||||||
'count': _messageCount.toString(),
|
|
||||||
},
|
|
||||||
'notification': {
|
|
||||||
'title': 'Hello FlutterFire!',
|
|
||||||
'body': 'This notification (#$_messageCount) was created via FCM!',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Renders the example application.
|
|
||||||
class Application extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _Application();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Application extends State<Application> {
|
|
||||||
String? _token;
|
|
||||||
String? initialMessage;
|
|
||||||
bool _resolved = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
|
|
||||||
FirebaseMessaging.instance.getInitialMessage().then(
|
|
||||||
(value) => setState(
|
|
||||||
() {
|
|
||||||
_resolved = true;
|
|
||||||
initialMessage = value?.data.toString();
|
|
||||||
},
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
FirebaseMessaging.onMessage.listen(showFlutterNotification);
|
|
||||||
|
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
||||||
print('A new onMessageOpenedApp event was published!');
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
'/message',
|
|
||||||
arguments: MessageArguments(message, true),
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> sendPushMessage() async {
|
|
||||||
if (_token == null) {
|
|
||||||
print('Unable to send FCM message, no token exists.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await http.post(
|
|
||||||
Uri.parse('https://api.rnfirebase.io/messaging/send'),
|
|
||||||
headers: <String, String>{
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: constructFCMPayload(_token),
|
|
||||||
);
|
|
||||||
print('FCM request for device sent!');
|
|
||||||
} catch (e) {
|
|
||||||
print(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> onActionSelected(String value) async {
|
|
||||||
switch (value) {
|
|
||||||
case 'subscribe':
|
|
||||||
{
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Subscribing to topic "fcm_test".',
|
|
||||||
);
|
|
||||||
await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'unsubscribe':
|
|
||||||
{
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".',
|
|
||||||
);
|
|
||||||
await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'get_apns_token':
|
|
||||||
{
|
|
||||||
if (defaultTargetPlatform == TargetPlatform.iOS ||
|
|
||||||
defaultTargetPlatform == TargetPlatform.macOS) {
|
|
||||||
print('FlutterFire Messaging Example: Getting APNs token...');
|
|
||||||
String? token = await FirebaseMessaging.instance.getAPNSToken();
|
|
||||||
print('FlutterFire Messaging Example: Got APNs token: $token');
|
|
||||||
} else {
|
|
||||||
print(
|
|
||||||
'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Scaffold(
|
|
||||||
appBar: AppBar(
|
|
||||||
title: const Text('Cloud Messaging'),
|
|
||||||
actions: <Widget>[
|
|
||||||
PopupMenuButton(
|
|
||||||
onSelected: onActionSelected,
|
|
||||||
itemBuilder: (BuildContext context) {
|
|
||||||
return [
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'subscribe',
|
|
||||||
child: Text('Subscribe to topic'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'unsubscribe',
|
|
||||||
child: Text('Unsubscribe to topic'),
|
|
||||||
),
|
|
||||||
const PopupMenuItem(
|
|
||||||
value: 'get_apns_token',
|
|
||||||
child: Text('Get APNs token (Apple only)'),
|
|
||||||
),
|
|
||||||
];
|
|
||||||
},
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
floatingActionButton: Builder(
|
|
||||||
builder: (context) => FloatingActionButton(
|
|
||||||
onPressed: sendPushMessage,
|
|
||||||
backgroundColor: Colors.white,
|
|
||||||
child: const Icon(Icons.send),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
body: SingleChildScrollView(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
MetaCard('Permissions', Permissions()),
|
|
||||||
MetaCard(
|
|
||||||
'Initial Message',
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Text(_resolved ? 'Resolved' : 'Resolving'),
|
|
||||||
Text(initialMessage ?? 'None'),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
MetaCard(
|
|
||||||
'FCM Token',
|
|
||||||
TokenMonitor((token) {
|
|
||||||
_token = token;
|
|
||||||
return token == null
|
|
||||||
? const CircularProgressIndicator()
|
|
||||||
: SelectableText(
|
|
||||||
token,
|
|
||||||
style: const TextStyle(fontSize: 12),
|
|
||||||
);
|
|
||||||
}),
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
FirebaseMessaging.instance
|
|
||||||
.getInitialMessage()
|
|
||||||
.then((RemoteMessage? message) {
|
|
||||||
if (message != null) {
|
|
||||||
Navigator.pushNamed(
|
|
||||||
context,
|
|
||||||
'/message',
|
|
||||||
arguments: MessageArguments(message, true),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
child: const Text('getInitialMessage()'),
|
|
||||||
),
|
|
||||||
MetaCard('Message Stream', MessageList()),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// UI Widget for displaying metadata.
|
|
||||||
class MetaCard extends StatelessWidget {
|
|
||||||
final String _title;
|
|
||||||
final Widget _children;
|
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
|
||||||
MetaCard(this._title, this._children);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
|
|
||||||
child: Card(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
|
||||||
child: Text(_title, style: const TextStyle(fontSize: 18)),
|
|
||||||
),
|
|
||||||
_children,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,120 +0,0 @@
|
||||||
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// ignore_for_file: require_trailing_commas
|
|
||||||
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// Requests & displays the current user permissions for this device.
|
|
||||||
class Permissions extends StatefulWidget {
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _Permissions();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _Permissions extends State<Permissions> {
|
|
||||||
bool _requested = false;
|
|
||||||
bool _fetching = false;
|
|
||||||
late NotificationSettings _settings;
|
|
||||||
|
|
||||||
Future<void> requestPermissions() async {
|
|
||||||
setState(() {
|
|
||||||
_fetching = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
NotificationSettings settings =
|
|
||||||
await FirebaseMessaging.instance.requestPermission(
|
|
||||||
announcement: true,
|
|
||||||
carPlay: true,
|
|
||||||
criticalAlert: true,
|
|
||||||
);
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_requested = true;
|
|
||||||
_fetching = false;
|
|
||||||
_settings = settings;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> checkPermissions() async {
|
|
||||||
setState(() {
|
|
||||||
_fetching = true;
|
|
||||||
});
|
|
||||||
|
|
||||||
NotificationSettings settings =
|
|
||||||
await FirebaseMessaging.instance.getNotificationSettings();
|
|
||||||
|
|
||||||
setState(() {
|
|
||||||
_requested = true;
|
|
||||||
_fetching = false;
|
|
||||||
_settings = settings;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
Widget row(String title, String value) {
|
|
||||||
return Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 8),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text('$title:', style: const TextStyle(fontWeight: FontWeight.bold)),
|
|
||||||
Text(value),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
if (_fetching) {
|
|
||||||
return const CircularProgressIndicator();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_requested) {
|
|
||||||
return ElevatedButton(
|
|
||||||
onPressed: requestPermissions,
|
|
||||||
child: const Text('Request Permissions'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return Column(children: [
|
|
||||||
row('Authorization Status', statusMap[_settings.authorizationStatus]!),
|
|
||||||
if (defaultTargetPlatform == TargetPlatform.iOS) ...[
|
|
||||||
row('Alert', settingsMap[_settings.alert]!),
|
|
||||||
row('Announcement', settingsMap[_settings.announcement]!),
|
|
||||||
row('Badge', settingsMap[_settings.badge]!),
|
|
||||||
row('Car Play', settingsMap[_settings.carPlay]!),
|
|
||||||
row('Lock Screen', settingsMap[_settings.lockScreen]!),
|
|
||||||
row('Notification Center', settingsMap[_settings.notificationCenter]!),
|
|
||||||
row('Show Previews', previewMap[_settings.showPreviews]!),
|
|
||||||
row('Sound', settingsMap[_settings.sound]!),
|
|
||||||
],
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: checkPermissions, child: const Text('Reload Permissions')),
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Maps a [AuthorizationStatus] to a string value.
|
|
||||||
const statusMap = {
|
|
||||||
AuthorizationStatus.authorized: 'Authorized',
|
|
||||||
AuthorizationStatus.denied: 'Denied',
|
|
||||||
AuthorizationStatus.notDetermined: 'Not Determined',
|
|
||||||
AuthorizationStatus.provisional: 'Provisional',
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Maps a [AppleNotificationSetting] to a string value.
|
|
||||||
const settingsMap = {
|
|
||||||
AppleNotificationSetting.disabled: 'Disabled',
|
|
||||||
AppleNotificationSetting.enabled: 'Enabled',
|
|
||||||
AppleNotificationSetting.notSupported: 'Not Supported',
|
|
||||||
};
|
|
||||||
|
|
||||||
/// Maps a [AppleShowPreviewSetting] to a string value.
|
|
||||||
const previewMap = {
|
|
||||||
AppleShowPreviewSetting.always: 'Always',
|
|
||||||
AppleShowPreviewSetting.never: 'Never',
|
|
||||||
AppleShowPreviewSetting.notSupported: 'Not Supported',
|
|
||||||
AppleShowPreviewSetting.whenAuthenticated: 'Only When Authenticated',
|
|
||||||
};
|
|
|
@ -1,50 +0,0 @@
|
||||||
// Copyright 2022, the Chromium project authors. Please see the AUTHORS file
|
|
||||||
// for details. All rights reserved. Use of this source code is governed by a
|
|
||||||
// BSD-style license that can be found in the LICENSE file.
|
|
||||||
|
|
||||||
// ignore_for_file: require_trailing_commas
|
|
||||||
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
|
||||||
|
|
||||||
/// Manages & returns the users FCM token.
|
|
||||||
///
|
|
||||||
/// Also monitors token refreshes and updates state.
|
|
||||||
class TokenMonitor extends StatefulWidget {
|
|
||||||
// ignore: public_member_api_docs
|
|
||||||
const TokenMonitor(this._builder, {super.key});
|
|
||||||
|
|
||||||
final Widget Function(String? token) _builder;
|
|
||||||
|
|
||||||
@override
|
|
||||||
State<StatefulWidget> createState() => _TokenMonitor();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _TokenMonitor extends State<TokenMonitor> {
|
|
||||||
String? _token;
|
|
||||||
late Stream<String> _tokenStream;
|
|
||||||
|
|
||||||
void setToken(String? token) {
|
|
||||||
print('FCM Token: $token');
|
|
||||||
setState(() {
|
|
||||||
_token = token;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
super.initState();
|
|
||||||
FirebaseMessaging.instance
|
|
||||||
.getToken(
|
|
||||||
vapidKey:
|
|
||||||
'BNKkaUWxyP_yC_lki1kYazgca0TNhuzt2drsOrL6WrgGbqnMnr8ZMLzg_rSPDm6HKphABS0KzjPfSqCXHXEd06Y')
|
|
||||||
.then(setToken);
|
|
||||||
_tokenStream = FirebaseMessaging.instance.onTokenRefresh;
|
|
||||||
_tokenStream.listen(setToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return widget._builder(_token);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,157 @@
|
||||||
|
import 'dart:convert';
|
||||||
|
import 'dart:developer';
|
||||||
|
|
||||||
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
import 'package:hub/app_state.dart';
|
||||||
|
import 'package:hub/backend/api_requests/api_calls.dart';
|
||||||
|
import 'package:hub/backend/api_requests/api_manager.dart';
|
||||||
|
import 'package:hub/components/templates_components/message_notificaion_modal_template_component/message_notification_widget.dart';
|
||||||
|
import 'package:hub/shared/utils/log_util.dart';
|
||||||
|
|
||||||
|
|
||||||
|
class PushNotification {
|
||||||
|
|
||||||
|
static final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
|
||||||
|
static final FlutterLocalNotificationsPlugin _flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
|
Future<void> initialize() async {
|
||||||
|
await _requestPermissions();
|
||||||
|
|
||||||
|
// Mensagens
|
||||||
|
_backgroundMessage();
|
||||||
|
_forgroundMessage();
|
||||||
|
_openMessage();
|
||||||
|
|
||||||
|
// Token
|
||||||
|
_refreshToken();
|
||||||
|
_updateDeviceToken();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _updateDeviceToken() async {
|
||||||
|
try {
|
||||||
|
final String? deviceToken = await _firebaseMessaging.getToken();
|
||||||
|
|
||||||
|
if (deviceToken != null) {
|
||||||
|
AppState().token = deviceToken;
|
||||||
|
|
||||||
|
final ApiCallResponse? response = await PhpGroup.updToken.call(token: AppState().token, devid: AppState().devUUID, useruuid: AppState().userUUID);
|
||||||
|
|
||||||
|
if (PhpGroup.updToken.error((response?.jsonBody ?? '')) == false) {
|
||||||
|
log('Token Atualizado com Sucesso!');
|
||||||
|
} else {
|
||||||
|
log('Falha ao Atualizar Token: ${response?.jsonBody}');
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log('Falha ao Pegar Token do Firebase');
|
||||||
|
}
|
||||||
|
} catch (e, s) {
|
||||||
|
LogUtil.requestAPIFailed("updToken.php", "", "Atualizar Token", e, s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _requestPermissions() async {
|
||||||
|
NotificationSettings settings = await _firebaseMessaging.requestPermission(
|
||||||
|
alert: true,
|
||||||
|
badge: true,
|
||||||
|
sound: true,
|
||||||
|
provisional: true,
|
||||||
|
criticalAlert: false,
|
||||||
|
carPlay: false,
|
||||||
|
announcement: false
|
||||||
|
);
|
||||||
|
log('Permisão de Notificação: ${settings.authorizationStatus == AuthorizationStatus.authorized ? "Liberado" : "Negado"}');
|
||||||
|
}
|
||||||
|
|
||||||
|
void _backgroundMessage() {
|
||||||
|
FirebaseMessaging.onBackgroundMessage((RemoteMessage message) => _printNotification(message, 'background'));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _forgroundMessage() {
|
||||||
|
FirebaseMessaging.onMessage.listen((RemoteMessage message) => _printNotification(message, 'forground'));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _openMessage() {
|
||||||
|
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) => _processNotification(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
void _refreshToken() {
|
||||||
|
_firebaseMessaging.onTokenRefresh.listen((token) => log('Novo Token: $token'));
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _printNotification(RemoteMessage message, String type) async {
|
||||||
|
print("Tipo da Notificação: $type");
|
||||||
|
print("Mensagem: $message");
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _processNotification(RemoteMessage message) async {
|
||||||
|
|
||||||
|
switch (message.category) {
|
||||||
|
case 'mensagem':
|
||||||
|
_showMessageNotificationDialog(message.data, AppState().context!, message.notification?.body ?? '');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void _showMessageNotificationDialog(Map<String, dynamic> message, BuildContext context, String extra) {
|
||||||
|
showDialog(
|
||||||
|
useSafeArea: true,
|
||||||
|
barrierDismissible: true,
|
||||||
|
context: context,
|
||||||
|
builder: (BuildContext context) {
|
||||||
|
String localId = '';
|
||||||
|
try {
|
||||||
|
localId = jsonDecode(message['local'])['CLI_ID'];
|
||||||
|
} catch (e) {
|
||||||
|
localId = message['local']['CLI_ID'].toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return GestureDetector(
|
||||||
|
onTap: () => Navigator.of(context).pop(),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
child: Dialog(
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
child: GestureDetector(
|
||||||
|
onTap: () => Navigator.of(context).pop(),
|
||||||
|
child: SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: MediaQuery.of(context).size.height,
|
||||||
|
child: MessageNotificationModalTemplateComponentWidget(
|
||||||
|
id: localId,
|
||||||
|
from: message['remetente'].toString(),
|
||||||
|
to: message['destinatario'].toString() == 'O'
|
||||||
|
? 'Morador'
|
||||||
|
: 'Visitante',
|
||||||
|
message: extra.isEmpty ? 'Unknown' : extra.toString(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Future<void> _showMessage(RemoteMessage message) async {
|
||||||
|
print("Dados: ${message.data}");
|
||||||
|
print("From: ${message.from}");
|
||||||
|
print("Notification: ${message.notification?.body}");
|
||||||
|
print("Category: ${message.category}");
|
||||||
|
|
||||||
|
showDialog(
|
||||||
|
context: AppState().context!,
|
||||||
|
builder: (context) => Dialog(
|
||||||
|
child: Container(
|
||||||
|
child: Text("Notificação"),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
128
lib/main.dart
128
lib/main.dart
|
@ -10,6 +10,7 @@ import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||||
import 'package:flutter_web_plugins/url_strategy.dart';
|
import 'package:flutter_web_plugins/url_strategy.dart';
|
||||||
import 'package:hub/app_state.dart';
|
import 'package:hub/app_state.dart';
|
||||||
|
import 'package:hub/backend/push_notification/push_notification.dart';
|
||||||
import 'package:hub/firebase_options.dart';
|
import 'package:hub/firebase_options.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/flutter_flow/internationalization.dart';
|
import 'package:hub/flutter_flow/internationalization.dart';
|
||||||
|
@ -18,105 +19,22 @@ import 'package:provider/provider.dart';
|
||||||
import 'package:responsive_framework/responsive_framework.dart';
|
import 'package:responsive_framework/responsive_framework.dart';
|
||||||
|
|
||||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||||
@pragma('vm:entry-point')
|
|
||||||
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
|
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
|
||||||
await setupFlutterNotifications();
|
|
||||||
showFlutterNotification(message);
|
|
||||||
log('Handling a background message ${message.messageId}');
|
|
||||||
}
|
|
||||||
|
|
||||||
late AndroidNotificationChannel channel;
|
|
||||||
late DarwinInitializationSettings iosSettings;
|
|
||||||
late InitializationSettings initializationSettings;
|
|
||||||
late AndroidInitializationSettings initializationSettingsAndroid;
|
|
||||||
|
|
||||||
bool isFlutterLocalNotificationsInitialized = false;
|
|
||||||
|
|
||||||
@pragma('vm:entry-point')
|
|
||||||
Future<void> setupFlutterNotifications() async {
|
|
||||||
if (isFlutterLocalNotificationsInitialized) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
channel = const AndroidNotificationChannel(
|
|
||||||
'high_importance_channel',
|
|
||||||
'High Importance Notifications',
|
|
||||||
description: 'This channel is used for important notifications.',
|
|
||||||
importance: Importance.high,
|
|
||||||
);
|
|
||||||
|
|
||||||
initializationSettingsAndroid =
|
|
||||||
const AndroidInitializationSettings('mipmap/ic_fre_black');
|
|
||||||
iosSettings = const DarwinInitializationSettings(
|
|
||||||
requestAlertPermission: true,
|
|
||||||
requestBadgePermission: true,
|
|
||||||
requestSoundPermission: true,
|
|
||||||
);
|
|
||||||
initializationSettings = InitializationSettings(
|
|
||||||
android: initializationSettingsAndroid,
|
|
||||||
iOS: iosSettings,
|
|
||||||
);
|
|
||||||
|
|
||||||
flutterLocalNotificationsPlugin = FlutterLocalNotificationsPlugin();
|
|
||||||
|
|
||||||
await flutterLocalNotificationsPlugin
|
|
||||||
.resolvePlatformSpecificImplementation<
|
|
||||||
AndroidFlutterLocalNotificationsPlugin>()
|
|
||||||
?.createNotificationChannel(channel)
|
|
||||||
.then((_) async => await flutterLocalNotificationsPlugin.initialize(
|
|
||||||
initializationSettings,
|
|
||||||
onDidReceiveNotificationResponse: (response) async {
|
|
||||||
log('Notification: ${response.payload}');
|
|
||||||
},
|
|
||||||
onDidReceiveBackgroundNotificationResponse: (response) async {
|
|
||||||
log('Notification: ${response.payload}');
|
|
||||||
},
|
|
||||||
));
|
|
||||||
|
|
||||||
await FirebaseMessaging.instance.setForegroundNotificationPresentationOptions(
|
|
||||||
alert: true,
|
|
||||||
badge: true,
|
|
||||||
sound: true,
|
|
||||||
);
|
|
||||||
isFlutterLocalNotificationsInitialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void showFlutterNotification(RemoteMessage message) {
|
|
||||||
log('Handling a background message ${message.messageId}');
|
|
||||||
RemoteNotification? notification = message.notification;
|
|
||||||
AndroidNotification? android = message.notification?.android;
|
|
||||||
if (notification != null && android != null && !kIsWeb) {
|
|
||||||
flutterLocalNotificationsPlugin.show(
|
|
||||||
notification.hashCode,
|
|
||||||
notification.title,
|
|
||||||
notification.body,
|
|
||||||
NotificationDetails(
|
|
||||||
android: AndroidNotificationDetails(
|
|
||||||
channel.id,
|
|
||||||
channel.name,
|
|
||||||
channelDescription: channel.description,
|
|
||||||
icon: 'launch_background',
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
late FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin;
|
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform);
|
||||||
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
// FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
|
||||||
|
|
||||||
|
// if (!kIsWeb) {
|
||||||
|
// await setupFlutterNotifications();
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
if (!kIsWeb) {
|
|
||||||
await setupFlutterNotifications();
|
|
||||||
}
|
|
||||||
SystemChrome.setPreferredOrientations([
|
SystemChrome.setPreferredOrientations([
|
||||||
DeviceOrientation.portraitUp,
|
DeviceOrientation.portraitUp,
|
||||||
DeviceOrientation.portraitDown,
|
DeviceOrientation.portraitDown,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
await init().then((_) {
|
await init().then((_) {
|
||||||
runApp(ChangeNotifierProvider(
|
runApp(ChangeNotifierProvider(
|
||||||
create: (context) => AppState(),
|
create: (context) => AppState(),
|
||||||
|
@ -126,42 +44,30 @@ void main() async {
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> init() async {
|
Future<void> init() async {
|
||||||
log('Setting up Crashlytics...');
|
WidgetsBinding.instance.addPostFrameCallback((_) async => await PushNotification().initialize());
|
||||||
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
|
||||||
if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
if (kDebugMode) {
|
||||||
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
log("Aplicativo em Debug Mode, crashlytics desabilitado!");
|
||||||
} else {
|
} else {
|
||||||
log('Crashlytics instance is null');
|
final crashlyticsInstance = FirebaseCrashlytics.instance;
|
||||||
|
if (crashlyticsInstance.isCrashlyticsCollectionEnabled) {
|
||||||
|
FlutterError.onError = crashlyticsInstance.recordFlutterError;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
log('Crashlytics set up.');
|
|
||||||
|
|
||||||
log('Initializing FlutterFlowTheme...');
|
|
||||||
await FlutterFlowTheme.initialize();
|
await FlutterFlowTheme.initialize();
|
||||||
log('FlutterFlowTheme initialized.');
|
|
||||||
|
|
||||||
log('Initializing FFLocalizations...');
|
|
||||||
await FFLocalizations.initialize();
|
await FFLocalizations.initialize();
|
||||||
log('FFLocalizations initialized.');
|
|
||||||
|
|
||||||
log('Initializing app state...');
|
|
||||||
final appState = AppState();
|
final appState = AppState();
|
||||||
await appState.initializePersistedState();
|
await appState.initializePersistedState();
|
||||||
log('App state initialized.');
|
|
||||||
|
|
||||||
log('Initializing GoRouter...');
|
|
||||||
GoRouter.optionURLReflectsImperativeAPIs = true;
|
GoRouter.optionURLReflectsImperativeAPIs = true;
|
||||||
usePathUrlStrategy();
|
usePathUrlStrategy();
|
||||||
log('GoRouter initialized.');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MyApp extends StatefulWidget {
|
class MyApp extends StatefulWidget {
|
||||||
const MyApp({super.key});
|
const MyApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<MyApp> createState() => _MyAppState();
|
State<MyApp> createState() => _MyAppState();
|
||||||
|
static _MyAppState of(BuildContext context) => context.findAncestorStateOfType<_MyAppState>()!;
|
||||||
static _MyAppState of(BuildContext context) =>
|
|
||||||
context.findAncestorStateOfType<_MyAppState>()!;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class _MyAppState extends State<MyApp> {
|
class _MyAppState extends State<MyApp> {
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
// import 'package:hub/backend/push_notification/pushNotification.dart';
|
|
||||||
import 'dart:developer';
|
import 'dart:developer';
|
||||||
|
|
||||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
|
||||||
import 'package:flutter/foundation.dart';
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:google_fonts/google_fonts.dart';
|
import 'package:google_fonts/google_fonts.dart';
|
||||||
import 'package:http/http.dart';
|
|
||||||
import 'package:hub/actions/actions.dart';
|
import 'package:hub/actions/actions.dart';
|
||||||
import 'package:hub/backend/schema/enums/enums.dart';
|
import 'package:hub/backend/schema/enums/enums.dart';
|
||||||
import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart';
|
import 'package:hub/components/organism_components/bottom_arrow_linked_locals_component/bottom_arrow_linked_locals_component_widget.dart';
|
||||||
|
@ -18,28 +14,9 @@ import 'package:hub/flutter_flow/flutter_flow_theme.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
import 'package:hub/flutter_flow/flutter_flow_util.dart';
|
||||||
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
|
import 'package:hub/flutter_flow/flutter_flow_widgets.dart';
|
||||||
import 'package:hub/flutter_flow/nav/nav.dart';
|
import 'package:hub/flutter_flow/nav/nav.dart';
|
||||||
import 'package:hub/main.dart';
|
|
||||||
import 'package:hub/pages/home_page/home_page_model.dart';
|
import 'package:hub/pages/home_page/home_page_model.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
// Crude counter to make messages unique
|
|
||||||
int _messageCount = 0;
|
|
||||||
|
|
||||||
/// The API endpoint here accepts a raw FCM payload for demonstration purposes.
|
|
||||||
String constructFCMPayload(String? token) {
|
|
||||||
_messageCount++;
|
|
||||||
return jsonEncode({
|
|
||||||
'token': token,
|
|
||||||
'data': {
|
|
||||||
'via': 'FlutterFire Cloud Messaging!!!',
|
|
||||||
'count': _messageCount.toString(),
|
|
||||||
},
|
|
||||||
'notification': {
|
|
||||||
'title': 'Hello FlutterFire!',
|
|
||||||
'body': 'This notification (#$_messageCount) was created via FCM!',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
class HomePageWidget extends StatefulWidget {
|
class HomePageWidget extends StatefulWidget {
|
||||||
const HomePageWidget({super.key});
|
const HomePageWidget({super.key});
|
||||||
|
@ -52,9 +29,6 @@ class _HomePageWidgetState extends State<HomePageWidget> {
|
||||||
late HomePageModel _model;
|
late HomePageModel _model;
|
||||||
bool localStatus = false;
|
bool localStatus = false;
|
||||||
final scaffoldKey = GlobalKey<ScaffoldState>();
|
final scaffoldKey = GlobalKey<ScaffoldState>();
|
||||||
String? _token;
|
|
||||||
String? initialMessage;
|
|
||||||
bool _resolved = false;
|
|
||||||
|
|
||||||
Future<void> checkLocalStatus() async {
|
Future<void> checkLocalStatus() async {
|
||||||
localStatus = await checkLocals(
|
localStatus = await checkLocals(
|
||||||
|
@ -69,25 +43,6 @@ class _HomePageWidgetState extends State<HomePageWidget> {
|
||||||
super.initState();
|
super.initState();
|
||||||
_model = createModel(context, () => HomePageModel());
|
_model = createModel(context, () => HomePageModel());
|
||||||
|
|
||||||
FirebaseMessaging.instance
|
|
||||||
.getInitialMessage()
|
|
||||||
.then(
|
|
||||||
(value) => setState(
|
|
||||||
() {
|
|
||||||
_resolved = true;
|
|
||||||
initialMessage = value?.data.toString();
|
|
||||||
log('getInitialMessage resolved');
|
|
||||||
},
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.whenComplete(() => log('getInitialMessage completed'));
|
|
||||||
FirebaseMessaging.onMessage.listen(showFlutterNotification).onDone(() {
|
|
||||||
log('onMessage completed');
|
|
||||||
});
|
|
||||||
FirebaseMessaging.onMessageOpenedApp.listen((RemoteMessage message) {
|
|
||||||
log('A new onMessageOpenedApp event was published!');
|
|
||||||
});
|
|
||||||
|
|
||||||
AppState().context = context;
|
AppState().context = context;
|
||||||
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
WidgetsBinding.instance.addPostFrameCallback((_) async {
|
||||||
|
@ -133,69 +88,6 @@ class _HomePageWidgetState extends State<HomePageWidget> {
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> sendPushMessage() async {
|
|
||||||
if (_token == null) {
|
|
||||||
log('Unable to send FCM message, no token exists.');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
await post(
|
|
||||||
Uri.parse('https://api.rnfirebase.io/messaging/send'),
|
|
||||||
headers: <String, String>{
|
|
||||||
'Content-Type': 'application/json; charset=UTF-8',
|
|
||||||
},
|
|
||||||
body: constructFCMPayload(_token),
|
|
||||||
);
|
|
||||||
log('FCM request for device sent!');
|
|
||||||
} catch (e) {
|
|
||||||
log(e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Future<void> onActionSelected(String value) async {
|
|
||||||
switch (value) {
|
|
||||||
case 'subscribe':
|
|
||||||
{
|
|
||||||
log(
|
|
||||||
'FlutterFire Messaging Example: Subscribing to topic "fcm_test".',
|
|
||||||
);
|
|
||||||
await FirebaseMessaging.instance.subscribeToTopic('fcm_test');
|
|
||||||
log(
|
|
||||||
'FlutterFire Messaging Example: Subscribing to topic "fcm_test" successful.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'unsubscribe':
|
|
||||||
{
|
|
||||||
log(
|
|
||||||
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test".',
|
|
||||||
);
|
|
||||||
await FirebaseMessaging.instance.unsubscribeFromTopic('fcm_test');
|
|
||||||
log(
|
|
||||||
'FlutterFire Messaging Example: Unsubscribing from topic "fcm_test" successful.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'get_apns_token':
|
|
||||||
{
|
|
||||||
if (defaultTargetPlatform == TargetPlatform.iOS ||
|
|
||||||
defaultTargetPlatform == TargetPlatform.macOS) {
|
|
||||||
log('FlutterFire Messaging Example: Getting APNs token...');
|
|
||||||
String? token = await FirebaseMessaging.instance.getAPNSToken();
|
|
||||||
log('FlutterFire Messaging Example: Got APNs token: $token');
|
|
||||||
} else {
|
|
||||||
log(
|
|
||||||
'FlutterFire Messaging Example: Getting an APNs token is only supported on iOS and macOS platforms.',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
context.watch<AppState>();
|
context.watch<AppState>();
|
||||||
|
@ -691,233 +583,6 @@ class _HomePageWidgetState extends State<HomePageWidget> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
||||||
// body: Container(
|
|
||||||
// decoration: BoxDecoration(
|
|
||||||
// color: FlutterFlowTheme.of(context).primaryBackground,
|
|
||||||
// ),
|
|
||||||
// child: SingleChildScrollView(
|
|
||||||
// child: Column(
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.start,
|
|
||||||
// children: [
|
|
||||||
// Wrap(
|
|
||||||
// spacing: 0.0,
|
|
||||||
// runSpacing: 0.0,
|
|
||||||
// alignment: WrapAlignment.start,
|
|
||||||
// crossAxisAlignment: WrapCrossAlignment.start,
|
|
||||||
// direction: Axis.horizontal,
|
|
||||||
// runAlignment: WrapAlignment.start,
|
|
||||||
// verticalDirection: VerticalDirection.down,
|
|
||||||
// clipBehavior: Clip.none,
|
|
||||||
// children: [
|
|
||||||
// Row(
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
// Expanded(
|
|
||||||
// child: Container(
|
|
||||||
// width: 100.0,
|
|
||||||
// height: 100.0,
|
|
||||||
// decoration: const BoxDecoration(
|
|
||||||
// color: Color(0xFF1AAB5F),
|
|
||||||
// ),
|
|
||||||
// child: Row(
|
|
||||||
// mainAxisSize: MainAxisSize.min,
|
|
||||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
// children: [
|
|
||||||
// Align(
|
|
||||||
// alignment: const AlignmentDirectional(0.0, 1.0),
|
|
||||||
// child: Container(
|
|
||||||
// height: 50.0,
|
|
||||||
// decoration: const BoxDecoration(),
|
|
||||||
// child: Align(
|
|
||||||
// alignment: const AlignmentDirectional(0.0, 0.0),
|
|
||||||
// child: Row(
|
|
||||||
// mainAxisSize: MainAxisSize.max,
|
|
||||||
// children: [
|
|
||||||
// Align(
|
|
||||||
// alignment:
|
|
||||||
// const AlignmentDirectional(-1.0, 0.0),
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsetsDirectional
|
|
||||||
// .fromSTEB(
|
|
||||||
// 10.0, 0.0, 0.0, 0.0),
|
|
||||||
// child: FlutterFlowIconButton(
|
|
||||||
// borderRadius: 20.0,
|
|
||||||
// borderWidth: 1.0,
|
|
||||||
// buttonSize: 40.0,
|
|
||||||
// fillColor:
|
|
||||||
// FlutterFlowTheme.of(context)
|
|
||||||
// .primary,
|
|
||||||
// icon: const Icon(
|
|
||||||
// Icons.menu_rounded,
|
|
||||||
// color: Colors.white,
|
|
||||||
// size: 28.0,
|
|
||||||
// ),
|
|
||||||
// onPressed: () async {
|
|
||||||
// scaffoldKey.currentState!
|
|
||||||
// .openDrawer();
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Align(
|
|
||||||
// alignment:
|
|
||||||
// const AlignmentDirectional(-1.0, 0.0),
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsetsDirectional
|
|
||||||
// .fromSTEB(
|
|
||||||
// 60.0, 15.0, 0.0, 0.0),
|
|
||||||
// child: ClipRRect(
|
|
||||||
// borderRadius:
|
|
||||||
// BorderRadius.circular(8.0),
|
|
||||||
// child: Image.network(
|
|
||||||
// 'https://storage.googleapis.com/flutterflow-io-6f20.appspot.com/projects/flutter-freaccess-hub-0xgz9q/assets/8r2vsbd9i03k/logo.png',
|
|
||||||
// width: 50.0,
|
|
||||||
// height: 200.0,
|
|
||||||
// fit: BoxFit.none,
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Align(
|
|
||||||
// alignment:
|
|
||||||
// const AlignmentDirectional(0.0, 0.0),
|
|
||||||
// child: Padding(
|
|
||||||
// padding: const EdgeInsetsDirectional
|
|
||||||
// .fromSTEB(
|
|
||||||
// 0.0, 15.0, 0.0, 0.0),
|
|
||||||
// child: Text(
|
|
||||||
// FFLocalizations.of(context)
|
|
||||||
// .getText(
|
|
||||||
// 'rg9pzkpz' /* FRE ACCESS */,
|
|
||||||
// ),
|
|
||||||
// style:
|
|
||||||
// FlutterFlowTheme.of(context)
|
|
||||||
// .bodyMedium
|
|
||||||
// .override(
|
|
||||||
// fontFamily:
|
|
||||||
// FlutterFlowTheme.of(
|
|
||||||
// context)
|
|
||||||
// .bodyMediumFamily,
|
|
||||||
// color: FlutterFlowTheme
|
|
||||||
// .of(context)
|
|
||||||
// .info,
|
|
||||||
// letterSpacing: 0.0,
|
|
||||||
// useGoogleFonts: GoogleFonts
|
|
||||||
// .asMap()
|
|
||||||
// .containsKey(
|
|
||||||
// FlutterFlowTheme.of(
|
|
||||||
// context)
|
|
||||||
// .bodyMediumFamily),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// Align(
|
|
||||||
// alignment: const AlignmentDirectional(0.0, 1.0),
|
|
||||||
// child: Container(
|
|
||||||
// width: 100.0,
|
|
||||||
// height: 50.0,
|
|
||||||
// decoration: const BoxDecoration(),
|
|
||||||
// child: Align(
|
|
||||||
// alignment: const AlignmentDirectional(1.0, 1.0),
|
|
||||||
// child: FlutterFlowIconButton(
|
|
||||||
// borderRadius: 20.0,
|
|
||||||
// borderWidth: 1.0,
|
|
||||||
// buttonSize: 40.0,
|
|
||||||
// icon: Icon(
|
|
||||||
// Icons.notifications_sharp,
|
|
||||||
// color:
|
|
||||||
// FlutterFlowTheme.of(context).info,
|
|
||||||
// size: 24.0,
|
|
||||||
// ),
|
|
||||||
// onPressed: () {
|
|
||||||
// print('IconButton pressed ...');
|
|
||||||
// },
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// wrapWithModel(
|
|
||||||
// model: _model.localComponentModel,
|
|
||||||
// updateCallback: () => setState(() {}),
|
|
||||||
// child: const LocalProfileComponentWidget(),
|
|
||||||
// ),
|
|
||||||
// Wrap(
|
|
||||||
// spacing: 0.0,
|
|
||||||
// runSpacing: 0.0,
|
|
||||||
// alignment: WrapAlignment.start,
|
|
||||||
// crossAxisAlignment: WrapCrossAlignment.start,
|
|
||||||
// direction: Axis.horizontal,
|
|
||||||
// runAlignment: WrapAlignment.start,
|
|
||||||
// verticalDirection: VerticalDirection.down,
|
|
||||||
// clipBehavior: Clip.none,
|
|
||||||
// children: [
|
|
||||||
// wrapWithModel(
|
|
||||||
// model: _model.menuComponentModel,
|
|
||||||
// updateCallback: () => setState(() {}),
|
|
||||||
// child: const MenuComponentWidget(),
|
|
||||||
// ),
|
|
||||||
// Align(
|
|
||||||
// alignment: const AlignmentDirectional(0.0, 0.0),
|
|
||||||
// child: wrapWithModel(
|
|
||||||
// model: _model.messageWellComponentModel,
|
|
||||||
// updateCallback: () => setState(() {}),
|
|
||||||
// child: const MessageWellComponentWidget(),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ],
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// ),
|
|
||||||
// );
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UI Widget for displaying metadata.
|
|
||||||
class MetaCard extends StatelessWidget {
|
|
||||||
final String _title;
|
|
||||||
final Widget _children;
|
|
||||||
|
|
||||||
// ignore: public_member_api_docs
|
|
||||||
MetaCard(this._title, this._children);
|
|
||||||
|
|
||||||
@override
|
|
||||||
Widget build(BuildContext context) {
|
|
||||||
return Container(
|
|
||||||
width: double.infinity,
|
|
||||||
margin: const EdgeInsets.only(left: 8, right: 8, top: 8),
|
|
||||||
child: Card(
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.only(bottom: 16),
|
|
||||||
child: Text(_title, style: const TextStyle(fontSize: 18)),
|
|
||||||
),
|
|
||||||
_children,
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -421,30 +421,30 @@ Widget liberationHistoryItemCard(
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
).then((_) {
|
).then((_) {
|
||||||
PushNotificationManager _pushNotificationService =
|
// PushNotificationManager _pushNotificationService =
|
||||||
PushNotificationManager();
|
// PushNotificationManager();
|
||||||
|
//
|
||||||
_pushNotificationService.onMessageReceived.listen((received) {
|
// _pushNotificationService.onMessageReceived.listen((received) {
|
||||||
if (received.data['click_action'] == 'cancel_request') {
|
// if (received.data['click_action'] == 'cancel_request') {
|
||||||
_pushNotificationService.dispose();
|
// _pushNotificationService.dispose();
|
||||||
showSnackbar(
|
// showSnackbar(
|
||||||
context,
|
// context,
|
||||||
FFLocalizations.of(context).getVariableText(
|
// FFLocalizations.of(context).getVariableText(
|
||||||
enText: 'Successfully resolved visit',
|
// enText: 'Successfully resolved visit',
|
||||||
ptText: 'Visita resolvida com sucesso'),
|
// ptText: 'Visita resolvida com sucesso'),
|
||||||
false);
|
// false);
|
||||||
context.pushReplacementNamed(
|
// context.pushReplacementNamed(
|
||||||
'liberationHistory',
|
// 'liberationHistory',
|
||||||
extra: <String, dynamic>{
|
// extra: <String, dynamic>{
|
||||||
kTransitionInfoKey: const TransitionInfo(
|
// kTransitionInfoKey: const TransitionInfo(
|
||||||
hasTransition: true,
|
// hasTransition: true,
|
||||||
transitionType: PageTransitionType.scale,
|
// transitionType: PageTransitionType.scale,
|
||||||
alignment: Alignment.bottomCenter,
|
// alignment: Alignment.bottomCenter,
|
||||||
),
|
// ),
|
||||||
},
|
// },
|
||||||
);
|
// );
|
||||||
}
|
// }
|
||||||
});
|
// });
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
30
pubspec.lock
30
pubspec.lock
|
@ -825,18 +825,18 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker
|
name: leak_tracker
|
||||||
sha256: "3f87a60e8c63aecc975dda1ceedbc8f24de75f09e4856ea27daf8958f2f0ce05"
|
sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "10.0.5"
|
version: "10.0.4"
|
||||||
leak_tracker_flutter_testing:
|
leak_tracker_flutter_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: leak_tracker_flutter_testing
|
name: leak_tracker_flutter_testing
|
||||||
sha256: "932549fb305594d82d7183ecd9fa93463e9914e1b67cacc34bc40906594a1806"
|
sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.0.5"
|
version: "3.0.3"
|
||||||
leak_tracker_testing:
|
leak_tracker_testing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -913,10 +913,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: material_color_utilities
|
name: material_color_utilities
|
||||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
sha256: "0e0a020085b65b6083975e499759762399b4475f766c21668c4ecca34ea74e5a"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.11.1"
|
version: "0.8.0"
|
||||||
maybe_just_nothing:
|
maybe_just_nothing:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -929,10 +929,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: meta
|
name: meta
|
||||||
sha256: bdb68674043280c3428e9ec998512fb681678676b3c54e773629ffe74419f8c7
|
sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.15.0"
|
version: "1.12.0"
|
||||||
mime:
|
mime:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1294,10 +1294,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: sqflite_common
|
name: sqflite_common
|
||||||
sha256: c5e5b2a142a893a752cb36ae5888680248686725a54afceff31f9a3a76bc53c2
|
sha256: "3da423ce7baf868be70e2c0976c28a1bb2f73644268b7ffa7d2e08eab71f16a4"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.5.4+1"
|
version: "2.5.4"
|
||||||
stack_trace:
|
stack_trace:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1350,10 +1350,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "5b8a98dafc4d5c4c9c72d8b31ab2b23fc13422348d2997120294d3bac86b4ddb"
|
sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.2"
|
version: "0.7.0"
|
||||||
timeago:
|
timeago:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description:
|
description:
|
||||||
|
@ -1526,10 +1526,10 @@ packages:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vm_service
|
name: vm_service
|
||||||
sha256: f652077d0bdf60abe4c1f6377448e8655008eef28f128bc023f7b5e8dfeb48fc
|
sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "14.2.4"
|
version: "14.2.1"
|
||||||
web:
|
web:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
|
@ -1611,5 +1611,5 @@ packages:
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "3.1.2"
|
version: "3.1.2"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.5.0 <4.0.0"
|
dart: ">=3.4.0 <4.0.0"
|
||||||
flutter: ">=3.22.0"
|
flutter: ">=3.22.0"
|
||||||
|
|
Loading…
Reference in New Issue