push notifications
This commit is contained in:
@@ -3,6 +3,8 @@ plugins {
|
|||||||
id("kotlin-android")
|
id("kotlin-android")
|
||||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||||
id("dev.flutter.flutter-gradle-plugin")
|
id("dev.flutter.flutter-gradle-plugin")
|
||||||
|
// Firebase Cloud Messaging: legge android/app/google-services.json.
|
||||||
|
id("com.google.gms.google-services")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -11,6 +13,9 @@ android {
|
|||||||
ndkVersion = flutter.ndkVersion
|
ndkVersion = flutter.ndkVersion
|
||||||
|
|
||||||
compileOptions {
|
compileOptions {
|
||||||
|
// Richiesto da flutter_local_notifications (notifiche mostrate quando
|
||||||
|
// l'app e' aperta).
|
||||||
|
isCoreLibraryDesugaringEnabled = true
|
||||||
sourceCompatibility = JavaVersion.VERSION_17
|
sourceCompatibility = JavaVersion.VERSION_17
|
||||||
targetCompatibility = JavaVersion.VERSION_17
|
targetCompatibility = JavaVersion.VERSION_17
|
||||||
}
|
}
|
||||||
@@ -42,3 +47,8 @@ android {
|
|||||||
flutter {
|
flutter {
|
||||||
source = "../.."
|
source = "../.."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
// Libreria di supporto richiesta dal desugaring (flutter_local_notifications).
|
||||||
|
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,4 +4,9 @@
|
|||||||
to allow setting breakpoints, to provide hot reload, etc.
|
to allow setting breakpoints, to provide hot reload, etc.
|
||||||
-->
|
-->
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
|
|
||||||
|
<!-- Solo per lo sviluppo: il backend locale (Docker) risponde in HTTP,
|
||||||
|
e da Android 9 il traffico in chiaro e' bloccato di default.
|
||||||
|
Questo file vale solo per la build di debug: la release resta in HTTPS. -->
|
||||||
|
<application android:usesCleartextTraffic="true" />
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<!-- Notifiche push: da Android 13 (API 33) il permesso va chiesto a runtime.
|
||||||
|
Lo fa PushService.registerAfterLogin() dopo il login. -->
|
||||||
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:label="yogibook_app"
|
android:label="yogibook_app"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ plugins {
|
|||||||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||||
id("com.android.application") version "8.11.1" apply false
|
id("com.android.application") version "8.11.1" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
|
||||||
|
// Necessario per Firebase (legge android/app/google-services.json).
|
||||||
|
id("com.google.gms.google-services") version "4.4.2" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
include(":app")
|
include(":app")
|
||||||
|
|||||||
@@ -4,11 +4,15 @@ import 'package:intl/date_symbol_data_local.dart';
|
|||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
|
|
||||||
import 'screens/login_page.dart';
|
import 'screens/login_page.dart';
|
||||||
|
import 'services/push_service.dart';
|
||||||
import 'state/app_state.dart';
|
import 'state/app_state.dart';
|
||||||
|
|
||||||
Future<void> main() async {
|
Future<void> main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
await initializeDateFormatting('it_IT');
|
await initializeDateFormatting('it_IT');
|
||||||
|
|
||||||
|
await PushService.initFirebase();
|
||||||
|
await PushService.enableForegroundNotifications();
|
||||||
runApp(
|
runApp(
|
||||||
ChangeNotifierProvider(create: (_) => AppState(), child: const MyApp()),
|
ChangeNotifierProvider(create: (_) => AppState(), child: const MyApp()),
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:image_picker/image_picker.dart';
|
import 'package:image_picker/image_picker.dart';
|
||||||
|
|
||||||
import '../models/school.dart';
|
import '../models/school.dart';
|
||||||
|
import '../services/push_api.dart';
|
||||||
import '../services/vanguard_api.dart';
|
import '../services/vanguard_api.dart';
|
||||||
import '../widgets/yogibook_background.dart';
|
import '../widgets/yogibook_background.dart';
|
||||||
|
|
||||||
@@ -41,12 +42,54 @@ class _AccountPageState extends State<AccountPage> {
|
|||||||
bool _obscurePw = true;
|
bool _obscurePw = true;
|
||||||
bool _obscurePw2 = true;
|
bool _obscurePw2 = true;
|
||||||
|
|
||||||
|
bool? _pushEnabled;
|
||||||
|
bool _savingPush = false;
|
||||||
|
|
||||||
final _picker = ImagePicker();
|
final _picker = ImagePicker();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadProfile();
|
_loadProfile();
|
||||||
|
_loadPushPreference();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _loadPushPreference() async {
|
||||||
|
try {
|
||||||
|
final attive = await PushApi.fetchPreference(token: widget.token);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _pushEnabled = attive);
|
||||||
|
} catch (_) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _pushEnabled = null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _togglePush(bool valore) async {
|
||||||
|
final precedente = _pushEnabled;
|
||||||
|
|
||||||
|
setState(() {
|
||||||
|
_pushEnabled = valore;
|
||||||
|
_savingPush = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
final salvato = await PushApi.updatePreference(
|
||||||
|
token: widget.token,
|
||||||
|
enabled: valore,
|
||||||
|
);
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _pushEnabled = salvato);
|
||||||
|
_snack(
|
||||||
|
salvato ? 'Notifiche attivate.' : 'Notifiche disattivate.',
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
setState(() => _pushEnabled = precedente);
|
||||||
|
_snack('Errore nel salvataggio: $e');
|
||||||
|
} finally {
|
||||||
|
if (mounted) setState(() => _savingPush = false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@@ -252,6 +295,8 @@ class _AccountPageState extends State<AccountPage> {
|
|||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
_profileCard(),
|
_profileCard(),
|
||||||
const SizedBox(height: 16),
|
const SizedBox(height: 16),
|
||||||
|
_notificationsCard(),
|
||||||
|
const SizedBox(height: 16),
|
||||||
_passwordCard(),
|
_passwordCard(),
|
||||||
const SizedBox(height: 8),
|
const SizedBox(height: 8),
|
||||||
],
|
],
|
||||||
@@ -409,6 +454,47 @@ class _AccountPageState extends State<AccountPage> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _notificationsCard() {
|
||||||
|
if (_pushEnabled == null) {
|
||||||
|
return _card(
|
||||||
|
title: 'Notifiche',
|
||||||
|
children: const [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
SizedBox(
|
||||||
|
width: 18,
|
||||||
|
height: 18,
|
||||||
|
child: CircularProgressIndicator(strokeWidth: 2),
|
||||||
|
),
|
||||||
|
SizedBox(width: 12),
|
||||||
|
Expanded(child: Text('Carico le impostazioni...')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _card(
|
||||||
|
title: 'Notifiche',
|
||||||
|
children: [
|
||||||
|
SwitchListTile(
|
||||||
|
contentPadding: EdgeInsets.zero,
|
||||||
|
activeThumbColor: kGreen,
|
||||||
|
value: _pushEnabled!,
|
||||||
|
onChanged: _savingPush ? null : _togglePush,
|
||||||
|
title: const Text(
|
||||||
|
'Notifiche push',
|
||||||
|
style: TextStyle(fontWeight: FontWeight.w700),
|
||||||
|
),
|
||||||
|
subtitle: const Text(
|
||||||
|
'Promemoria delle lezioni del giorno, scadenza abbonamento e '
|
||||||
|
'certificato medico.',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
Widget _passwordCard() {
|
Widget _passwordCard() {
|
||||||
return _card(
|
return _card(
|
||||||
title: 'Cambia password',
|
title: 'Cambia password',
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
|
import 'dart:async';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import '../services/push_service.dart';
|
||||||
import '../services/vanguard_api.dart';
|
import '../services/vanguard_api.dart';
|
||||||
import '../models/school.dart';
|
import '../models/school.dart';
|
||||||
import 'home_page.dart';
|
import 'home_page.dart';
|
||||||
@@ -47,6 +50,8 @@ class _LoginPageState extends State<LoginPage> {
|
|||||||
password: passwordController.text.trim(),
|
password: passwordController.text.trim(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
unawaited(PushService.registerAfterLogin(token));
|
||||||
|
|
||||||
if (!mounted) return;
|
if (!mounted) return;
|
||||||
|
|
||||||
// Bypass SelectSchoolPage: scuola unica, si va dritti alla Home.
|
// Bypass SelectSchoolPage: scuola unica, si va dritti alla Home.
|
||||||
|
|||||||
@@ -0,0 +1,92 @@
|
|||||||
|
import 'dart:convert';
|
||||||
|
import 'package:http/http.dart' as http;
|
||||||
|
|
||||||
|
import '../config/api_config.dart';
|
||||||
|
|
||||||
|
class PushApi {
|
||||||
|
static Map<String, String> _headers(String token) => {
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
'Authorization': 'Bearer $token',
|
||||||
|
};
|
||||||
|
|
||||||
|
static Future<String> registerDevice({
|
||||||
|
required String token,
|
||||||
|
required String deviceToken,
|
||||||
|
required String platform,
|
||||||
|
String? appVersion,
|
||||||
|
}) async {
|
||||||
|
final uri = Uri.parse('${ApiConfig.laravelApiBase}/register_device.php');
|
||||||
|
|
||||||
|
final res = await http.post(
|
||||||
|
uri,
|
||||||
|
headers: _headers(token),
|
||||||
|
body: jsonEncode({
|
||||||
|
'device_token': deviceToken,
|
||||||
|
'platform': platform,
|
||||||
|
if (appVersion != null) 'app_version': appVersion,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = _decode(res.body, res.statusCode);
|
||||||
|
|
||||||
|
return (data['push_enabled'] ?? 'Y').toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> unregisterDevice({
|
||||||
|
required String token,
|
||||||
|
required String deviceToken,
|
||||||
|
}) async {
|
||||||
|
final uri = Uri.parse('${ApiConfig.laravelApiBase}/register_device.php');
|
||||||
|
|
||||||
|
final res = await http.delete(
|
||||||
|
uri,
|
||||||
|
headers: _headers(token),
|
||||||
|
body: jsonEncode({'device_token': deviceToken}),
|
||||||
|
);
|
||||||
|
|
||||||
|
_decode(res.body, res.statusCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> fetchPreference({required String token}) async {
|
||||||
|
final uri = Uri.parse('${ApiConfig.laravelApiBase}/push_preference.php');
|
||||||
|
|
||||||
|
final res = await http.get(uri, headers: _headers(token));
|
||||||
|
final data = _decode(res.body, res.statusCode);
|
||||||
|
|
||||||
|
return (data['push_enabled'] ?? 'Y').toString().toUpperCase() == 'Y';
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<bool> updatePreference({
|
||||||
|
required String token,
|
||||||
|
required bool enabled,
|
||||||
|
}) async {
|
||||||
|
final uri = Uri.parse('${ApiConfig.laravelApiBase}/push_preference.php');
|
||||||
|
|
||||||
|
final res = await http.post(
|
||||||
|
uri,
|
||||||
|
headers: _headers(token),
|
||||||
|
body: jsonEncode({'enabled': enabled}),
|
||||||
|
);
|
||||||
|
|
||||||
|
final data = _decode(res.body, res.statusCode);
|
||||||
|
|
||||||
|
return (data['push_enabled'] ?? 'Y').toString().toUpperCase() == 'Y';
|
||||||
|
}
|
||||||
|
|
||||||
|
static Map<String, dynamic> _decode(String body, int statusCode) {
|
||||||
|
Map<String, dynamic> data;
|
||||||
|
|
||||||
|
try {
|
||||||
|
data = jsonDecode(body) as Map<String, dynamic>;
|
||||||
|
} catch (_) {
|
||||||
|
throw Exception('Risposta non valida ($statusCode): $body');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statusCode != 200 || data['success'] != true) {
|
||||||
|
throw Exception(data['error'] ?? data['message'] ?? 'Errore notifiche');
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
import 'dart:io' show Platform;
|
||||||
|
|
||||||
|
import 'package:firebase_core/firebase_core.dart';
|
||||||
|
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
|
||||||
|
|
||||||
|
import 'push_api.dart';
|
||||||
|
|
||||||
|
class PushService {
|
||||||
|
static String? _deviceToken;
|
||||||
|
|
||||||
|
static bool _firebaseReady = false;
|
||||||
|
|
||||||
|
static final FlutterLocalNotificationsPlugin _locali =
|
||||||
|
FlutterLocalNotificationsPlugin();
|
||||||
|
|
||||||
|
static const AndroidNotificationChannel _canale = AndroidNotificationChannel(
|
||||||
|
'yogibook_notifiche',
|
||||||
|
'Notifiche YogiBook',
|
||||||
|
description: 'Promemoria delle lezioni, scadenze abbonamento e certificato',
|
||||||
|
importance: Importance.high,
|
||||||
|
);
|
||||||
|
|
||||||
|
static Future<void> initFirebase() async {
|
||||||
|
if (_firebaseReady) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await Firebase.initializeApp();
|
||||||
|
FirebaseMessaging.onBackgroundMessage(_backgroundHandler);
|
||||||
|
await _preparaNotificheLocali();
|
||||||
|
_firebaseReady = true;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Firebase non inizializzato: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> _preparaNotificheLocali() async {
|
||||||
|
const impostazioni = InitializationSettings(
|
||||||
|
android: AndroidInitializationSettings('@mipmap/ic_launcher'),
|
||||||
|
iOS: DarwinInitializationSettings(
|
||||||
|
requestAlertPermission: false,
|
||||||
|
requestBadgePermission: false,
|
||||||
|
requestSoundPermission: false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
await _locali.initialize(settings: impostazioni);
|
||||||
|
|
||||||
|
await _locali
|
||||||
|
.resolvePlatformSpecificImplementation<
|
||||||
|
AndroidFlutterLocalNotificationsPlugin
|
||||||
|
>()
|
||||||
|
?.createNotificationChannel(_canale);
|
||||||
|
|
||||||
|
FirebaseMessaging.onMessage.listen(_mostraInPrimoPiano);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> _mostraInPrimoPiano(RemoteMessage messaggio) async {
|
||||||
|
if (kIsWeb || !Platform.isAndroid) return;
|
||||||
|
|
||||||
|
final notifica = messaggio.notification;
|
||||||
|
if (notifica == null) return;
|
||||||
|
|
||||||
|
await _locali.show(
|
||||||
|
id: notifica.hashCode,
|
||||||
|
title: notifica.title,
|
||||||
|
body: notifica.body,
|
||||||
|
notificationDetails: NotificationDetails(
|
||||||
|
android: AndroidNotificationDetails(
|
||||||
|
_canale.id,
|
||||||
|
_canale.name,
|
||||||
|
channelDescription: _canale.description,
|
||||||
|
importance: Importance.high,
|
||||||
|
priority: Priority.high,
|
||||||
|
icon: '@mipmap/ic_launcher',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> registerAfterLogin(String authToken) async {
|
||||||
|
if (!_firebaseReady) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
final messaging = FirebaseMessaging.instance;
|
||||||
|
|
||||||
|
final settings = await messaging.requestPermission();
|
||||||
|
|
||||||
|
if (settings.authorizationStatus == AuthorizationStatus.denied) {
|
||||||
|
debugPrint('Permesso notifiche negato dall\'utente.');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final deviceToken = await messaging.getToken();
|
||||||
|
if (deviceToken == null) return;
|
||||||
|
|
||||||
|
_deviceToken = deviceToken;
|
||||||
|
|
||||||
|
await PushApi.registerDevice(
|
||||||
|
token: authToken,
|
||||||
|
deviceToken: deviceToken,
|
||||||
|
platform: _platformName(),
|
||||||
|
);
|
||||||
|
|
||||||
|
messaging.onTokenRefresh.listen((nuovo) async {
|
||||||
|
_deviceToken = nuovo;
|
||||||
|
try {
|
||||||
|
await PushApi.registerDevice(
|
||||||
|
token: authToken,
|
||||||
|
deviceToken: nuovo,
|
||||||
|
platform: _platformName(),
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Aggiornamento token fallito: $e');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Registrazione push fallita: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> unregisterOnLogout(String authToken) async {
|
||||||
|
final deviceToken = _deviceToken;
|
||||||
|
if (deviceToken == null) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await PushApi.unregisterDevice(
|
||||||
|
token: authToken,
|
||||||
|
deviceToken: deviceToken,
|
||||||
|
);
|
||||||
|
_deviceToken = null;
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Disattivazione dispositivo fallita: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static Future<void> enableForegroundNotifications() async {
|
||||||
|
if (!_firebaseReady) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await FirebaseMessaging.instance
|
||||||
|
.setForegroundNotificationPresentationOptions(
|
||||||
|
alert: true,
|
||||||
|
badge: true,
|
||||||
|
sound: true,
|
||||||
|
);
|
||||||
|
} catch (e) {
|
||||||
|
debugPrint('Impostazione notifiche in foreground fallita: $e');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String _platformName() {
|
||||||
|
if (kIsWeb) return 'web';
|
||||||
|
return Platform.isIOS ? 'ios' : 'android';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@pragma('vm:entry-point')
|
||||||
|
Future<void> _backgroundHandler(RemoteMessage message) async {
|
||||||
|
debugPrint('Notifica in background: ${message.messageId}');
|
||||||
|
}
|
||||||
@@ -8,6 +8,9 @@ import Foundation
|
|||||||
import audioplayers_darwin
|
import audioplayers_darwin
|
||||||
import file_picker
|
import file_picker
|
||||||
import file_selector_macos
|
import file_selector_macos
|
||||||
|
import firebase_core
|
||||||
|
import firebase_messaging
|
||||||
|
import flutter_local_notifications
|
||||||
import google_sign_in_ios
|
import google_sign_in_ios
|
||||||
import path_provider_foundation
|
import path_provider_foundation
|
||||||
import shared_preferences_foundation
|
import shared_preferences_foundation
|
||||||
@@ -17,6 +20,9 @@ func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
|
|||||||
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
AudioplayersDarwinPlugin.register(with: registry.registrar(forPlugin: "AudioplayersDarwinPlugin"))
|
||||||
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
FilePickerPlugin.register(with: registry.registrar(forPlugin: "FilePickerPlugin"))
|
||||||
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
|
||||||
|
FLTFirebaseCorePlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseCorePlugin"))
|
||||||
|
FLTFirebaseMessagingPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseMessagingPlugin"))
|
||||||
|
FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin"))
|
||||||
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
|
FLTGoogleSignInPlugin.register(with: registry.registrar(forPlugin: "FLTGoogleSignInPlugin"))
|
||||||
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
|
||||||
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin"))
|
||||||
|
|||||||
+142
-6
@@ -1,6 +1,22 @@
|
|||||||
# Generated by pub
|
# Generated by pub
|
||||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||||
packages:
|
packages:
|
||||||
|
_flutterfire_internals:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: _flutterfire_internals
|
||||||
|
sha256: "6727cf2ced9b104abca9daa278380be2eca2b98ce33d4b46f11708e387dc6b4d"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.3.76"
|
||||||
|
args:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: args
|
||||||
|
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "2.7.0"
|
||||||
async:
|
async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -121,6 +137,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.0.8"
|
version: "1.0.8"
|
||||||
|
dbus:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: dbus
|
||||||
|
sha256: "792974a4007974fbc5c1b5433eb2330a9db3e368c3f906253af4c007d0f49a91"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.7.13"
|
||||||
fake_async:
|
fake_async:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -185,6 +209,54 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.9.3+5"
|
version: "0.9.3+5"
|
||||||
|
firebase_core:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_core
|
||||||
|
sha256: "9478ca6700c02d315c6aba37e206e612317f98bb4f335bb1cbd6e0ce67dcf764"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.13.0"
|
||||||
|
firebase_core_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_core_platform_interface
|
||||||
|
sha256: e28f9afdcb5b0f0a8ea74ea3b322f5a7592c81cb45dd9d189913bdae08a2089a
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.1.0"
|
||||||
|
firebase_core_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_core_web
|
||||||
|
sha256: f471a288b0101a45567548322ac4a5ad31e3ecbf87a576dcc0e424e6a11e04ea
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.10.0"
|
||||||
|
firebase_messaging:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: firebase_messaging
|
||||||
|
sha256: "55cf1000dd72d229ebff3ce6a399bd35705a7675275478b31308afd90ac85751"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "16.5.0"
|
||||||
|
firebase_messaging_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_messaging_platform_interface
|
||||||
|
sha256: e5fbb62bd23a27c983825277877ac91fdfdb6bfb31c6e5399700963ae586e339
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.9.3"
|
||||||
|
firebase_messaging_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: firebase_messaging_web
|
||||||
|
sha256: "663d320e90a41fe57d651ec79df2bc831bf29e051bc0c2cd41faf3106eb412d9"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "4.2.4"
|
||||||
fixnum:
|
fixnum:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -206,6 +278,46 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "6.0.0"
|
version: "6.0.0"
|
||||||
|
flutter_local_notifications:
|
||||||
|
dependency: "direct main"
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications
|
||||||
|
sha256: "1447ba911c60f2ba3f25dae1af151ec187162566b0f57e37771bf0b400f013ad"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "22.3.0"
|
||||||
|
flutter_local_notifications_linux:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_linux
|
||||||
|
sha256: "9ca97e63776f29ab1b955725c09999fc2c150523269db150c39274f2a43c5a8b"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "8.0.1"
|
||||||
|
flutter_local_notifications_platform_interface:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_platform_interface
|
||||||
|
sha256: "43c3761d916c9bd3d5c7ebbc44d82f4990329840c0c5d62ad5260cc1b5d399bd"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "12.2.0"
|
||||||
|
flutter_local_notifications_web:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_web
|
||||||
|
sha256: "516afaf97a2d1e67a036c6617321b00d205d72f7a67b6eccf936cd565f985878"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "1.0.0"
|
||||||
|
flutter_local_notifications_windows:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: flutter_local_notifications_windows
|
||||||
|
sha256: "6f43bdd03b171b7a90f22647506fea33e2bb12294b7c7c7a3d690e960a382945"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "3.1.1"
|
||||||
flutter_localizations:
|
flutter_localizations:
|
||||||
dependency: "direct main"
|
dependency: "direct main"
|
||||||
description: flutter
|
description: flutter
|
||||||
@@ -493,6 +605,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.3.0"
|
version: "2.3.0"
|
||||||
|
petitparser:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: petitparser
|
||||||
|
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.2"
|
||||||
platform:
|
platform:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -630,10 +750,18 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: test_api
|
name: test_api
|
||||||
sha256: "93167629bfc610f71560ab9312acdda4959de4df6fac7492c89ff0d3886f6636"
|
sha256: "2a122cbe059f8b610d3a5415f42e255b6c17b1f21eee1d960f31080237fb4f11"
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "0.7.9"
|
version: "0.7.12"
|
||||||
|
timezone:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: timezone
|
||||||
|
sha256: "981d1020d6ef8fe1e7b3de5054e5b25579ae7c403d7734adc508ffc47668e9cb"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "0.11.1"
|
||||||
typed_data:
|
typed_data:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -718,10 +846,10 @@ packages:
|
|||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
name: vector_math
|
name: vector_math
|
||||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
sha256: f36f9f3be64c6198714492bb455c11056e33e2f85d9a0b676a48301e44fdcf47
|
||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "2.2.0"
|
version: "2.4.2"
|
||||||
vm_service:
|
vm_service:
|
||||||
dependency: transitive
|
dependency: transitive
|
||||||
description:
|
description:
|
||||||
@@ -754,6 +882,14 @@ packages:
|
|||||||
url: "https://pub.dev"
|
url: "https://pub.dev"
|
||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
|
xml:
|
||||||
|
dependency: transitive
|
||||||
|
description:
|
||||||
|
name: xml
|
||||||
|
sha256: "67f0aff7be013d107995e9b75bf4e7f2c3ef2dfdb2c8e68024bba0a7fd5756a4"
|
||||||
|
url: "https://pub.dev"
|
||||||
|
source: hosted
|
||||||
|
version: "7.0.1"
|
||||||
sdks:
|
sdks:
|
||||||
dart: ">=3.10.4 <4.0.0"
|
dart: ">=3.11.0 <4.0.0"
|
||||||
flutter: ">=3.35.0"
|
flutter: ">=3.38.1"
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ dependencies:
|
|||||||
mime: ^2.0.0
|
mime: ^2.0.0
|
||||||
provider: ^6.1.5+1
|
provider: ^6.1.5+1
|
||||||
shared_preferences: ^2.2.3
|
shared_preferences: ^2.2.3
|
||||||
|
firebase_core: ^4.13.0
|
||||||
|
firebase_messaging: ^16.5.0
|
||||||
|
flutter_local_notifications: ^22.3.0
|
||||||
|
|
||||||
dev_dependencies:
|
dev_dependencies:
|
||||||
flutter_test:
|
flutter_test:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
#include <audioplayers_windows/audioplayers_windows_plugin.h>
|
||||||
#include <file_selector_windows/file_selector_windows.h>
|
#include <file_selector_windows/file_selector_windows.h>
|
||||||
|
#include <firebase_core/firebase_core_plugin_c_api.h>
|
||||||
#include <url_launcher_windows/url_launcher_windows.h>
|
#include <url_launcher_windows/url_launcher_windows.h>
|
||||||
|
|
||||||
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
void RegisterPlugins(flutter::PluginRegistry* registry) {
|
||||||
@@ -15,6 +16,8 @@ void RegisterPlugins(flutter::PluginRegistry* registry) {
|
|||||||
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin"));
|
||||||
FileSelectorWindowsRegisterWithRegistrar(
|
FileSelectorWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
registry->GetRegistrarForPlugin("FileSelectorWindows"));
|
||||||
|
FirebaseCorePluginCApiRegisterWithRegistrar(
|
||||||
|
registry->GetRegistrarForPlugin("FirebaseCorePluginCApi"));
|
||||||
UrlLauncherWindowsRegisterWithRegistrar(
|
UrlLauncherWindowsRegisterWithRegistrar(
|
||||||
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
registry->GetRegistrarForPlugin("UrlLauncherWindows"));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,12 @@
|
|||||||
list(APPEND FLUTTER_PLUGIN_LIST
|
list(APPEND FLUTTER_PLUGIN_LIST
|
||||||
audioplayers_windows
|
audioplayers_windows
|
||||||
file_selector_windows
|
file_selector_windows
|
||||||
|
firebase_core
|
||||||
url_launcher_windows
|
url_launcher_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
list(APPEND FLUTTER_FFI_PLUGIN_LIST
|
||||||
|
flutter_local_notifications_windows
|
||||||
)
|
)
|
||||||
|
|
||||||
set(PLUGIN_BUNDLED_LIBRARIES)
|
set(PLUGIN_BUNDLED_LIBRARIES)
|
||||||
|
|||||||
Reference in New Issue
Block a user