feat(n4): wire FCM token path and document Master secrets
Add PushTokenService with Firebase Messaging fallback to install placeholders, conditional google-services Gradle plugin, fcm-setup.md, and mark Android UI checklist api-done via prod e2e 16/16. Co-authored-by: okuma <o0kuma@users.noreply.github.com>
This commit is contained in:
parent
fea1b555b4
commit
82a5809d4e
|
|
@ -29,8 +29,9 @@ Phase 1 **A~C** 이후 실행 트랙. 작업 단위를 하나씩 처리한다.
|
|||
### NOW
|
||||
|
||||
- 앱 코드는 클로즈드 베타 직전 수준
|
||||
- **`https://msn.iykyka.com` 라이브 + N3 완료 + Gemini 실초안 OK**. 다음: N4 FCM·Android QA (선택)
|
||||
- 실 FCM · Android UI 수동 QA · 사람 PoC 실행은 남음
|
||||
- **`https://msn.iykyka.com` 라이브 + N3 완료 + Gemini 실초안 OK + Track A/B 완료**
|
||||
- 진행 중: **N4 FCM 코드 경로** → Master 시크릿 대기 → Android UI QA
|
||||
- 실 FCM 전송·Android 실기기 탭 · 사람 PoC 실행은 남음
|
||||
|
||||
### NEXT 순서
|
||||
|
||||
|
|
@ -159,25 +160,28 @@ N2-A 전체 확정. 다음 구현 트랙은 **N1 스모크 → N2-B (Dockerfile/
|
|||
| **N4-B3** | 가입 화면 페어링 안내 | done | Signup 데모 패널에 한 줄 팁 |
|
||||
| **N4-B4** | 프로덕션 core+web 재배포 | done | `/demo` pairing_steps OK (`c7029ab`) |
|
||||
|
||||
### FCM
|
||||
### FCM — [`fcm-setup.md`](./fcm-setup.md)
|
||||
|
||||
| ID | 작업 | Status | 완료 조건 |
|
||||
|----|------|--------|-----------|
|
||||
| **N4-1** | Firebase + `google-services.json` | todo | Android 앱 연결 |
|
||||
| **N4-2** | 실 FCM registration token | todo | install-id 플레이스홀더 제거 |
|
||||
| **N4-3** | 서버 FCM 자격증명 | todo | env만 (`FCM_SERVER_KEY` 또는 HTTP v1) |
|
||||
| **N4-4** | 푸시 수신 | todo | `/admin/push-test` + 에스컬레이션 수신 |
|
||||
| **N4-1** | Firebase + `google-services.json` | blocked | Master: Console 앱 + `mobile/android/app/google-services.json` (git 금지) |
|
||||
| **N4-2** | 실 FCM registration token | done* | `PushTokenService` — Firebase 있으면 실 토큰, 없으면 `install:` (*전송은 N4-1 후) |
|
||||
| **N4-3** | 서버 FCM 자격증명 | blocked | Master: 호스트 `.env`의 `FCM_SERVER_KEY`만 |
|
||||
| **N4-4** | 푸시 수신 | blocked | N4-1+N4-3 후 `/admin/push-test` + 기기 수신 |
|
||||
|
||||
### Android UI 탭 (`mobile/README.md`)
|
||||
|
||||
| ID | 작업 | Status |
|
||||
|----|------|--------|
|
||||
| **N4-5** | 가입 → 말투 저장 | todo |
|
||||
| **N4-6** | 연락처 → 대화 → 메시지·히스토리 | todo |
|
||||
| **N4-7** | L1 초안 수정/버리기/승인·뱃지 | todo |
|
||||
| **N4-8** | 에스컬레이션 → 사후알림 함 | todo |
|
||||
| **N4-9** | 되돌리기·거부권 | todo |
|
||||
| **N4-10** | 자율성 L0~L2 + 화이트리스트 | todo |
|
||||
API 계층은 프로덕션에서 검증됨 (`CORE_API_BASE=https://msn.iykyka.com` → e2e **16/16**, 2026-07-31).
|
||||
실기기/에뮬레이터 **화면 탭**은 Master 로컬에서 1회.
|
||||
|
||||
| ID | 작업 | Status | 비고 |
|
||||
|----|------|--------|------|
|
||||
| **N4-5** | 가입 → 말투 저장 | api-done | 실기기 UI 탭 남음 |
|
||||
| **N4-6** | 연락처 → 대화 → 메시지·히스토리 | api-done | 실기기 UI 탭 남음 |
|
||||
| **N4-7** | L1 초안 수정/버리기/승인·뱃지 | api-done | 실기기 UI 탭 남음 |
|
||||
| **N4-8** | 에스컬레이션 → 사후알림 함 | api-done | 실기기 UI 탭 남음 |
|
||||
| **N4-9** | 되돌리기·거부권 | api-done | 실기기 UI 탭 남음 |
|
||||
| **N4-10** | 자율성 L0~L2 + 화이트리스트 | api-done | 실기기 UI 탭 남음 |
|
||||
|
||||
### 후순위
|
||||
|
||||
|
|
@ -213,7 +217,7 @@ N1~N4(배포·품질에 필요한 최소분) 이후에만 착수. `roadmap.md` P
|
|||
2. ~~N1 스모크~~ **done** (E2E 16/16 + DEMO API 경로; 브라우저 UI 탭은 테스터)
|
||||
3. ~~N2-B1~B7 이미지·compose~~ **done** (파일 랜딩·이미지 빌드)
|
||||
4. ~~N2-B8~B12 컷오버~~ **done** (`msn.iykyka.com` 라이브)
|
||||
5. ~~N3 배포 안정화~~ **done** — 다음: **N4 FCM/Android QA** 또는 서버에 `GEMINI_API_KEY` 주입
|
||||
5. ~~N3 안정화 + Track A/B~~ **done** — 다음: **Master FCM 시크릿(N4-1/3)** → N4-4 스모크 → Android UI QA (N4-5~10)
|
||||
|
||||
|
||||
완료 시 본 표의 Status를 `done`으로 바꾸고, [`roadmap.md`](./roadmap.md) §4/§5의 대응 `[~]`/`[ ]`도 같이 갱신한다.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
# FCM 설정 (N4-1 ~ N4-4)
|
||||
|
||||
서버·클라이언트의 푸시 **코드 경로는 준비됨**. 실제 전송은 Master가 Firebase 시크릿을
|
||||
넣기 전까지 soft-skip 한다 (`install:` 토큰·`FCM_SERVER_KEY` 없음).
|
||||
|
||||
## 이미 된 것 (코드)
|
||||
|
||||
| 계층 | 동작 |
|
||||
|------|------|
|
||||
| Flutter | `PushTokenService` — Firebase 가능하면 실 FCM 토큰, 아니면 `install:` 플레이스홀더 |
|
||||
| Android | `google-services.json`이 있을 때만 Google Services 플러그인 적용 |
|
||||
| core-backend | `notifyUser` + `POST /admin/push-test` — `FCM_SERVER_KEY` + 실 토큰일 때만 전송 |
|
||||
|
||||
## Master가 할 일
|
||||
|
||||
### N4-1 — Firebase 앱
|
||||
|
||||
1. [Firebase Console](https://console.firebase.google.com/)에서 프로젝트 생성 (또는 기존 사용).
|
||||
2. Android 앱 추가 — package name: **`com.ykavu.ykavu_mobile`**
|
||||
3. 받은 `google-services.json`을 로컬에만 배치 (git 금지):
|
||||
|
||||
```bash
|
||||
cp ~/Downloads/google-services.json mobile/android/app/google-services.json
|
||||
```
|
||||
|
||||
템플릿: `mobile/android/app/google-services.json.example`
|
||||
|
||||
### N4-3 — 서버 키 (env only)
|
||||
|
||||
Cloud Messaging **레거시 서버 키**(또는 호환 서버 키)를 호스트 `.env`에만 설정:
|
||||
|
||||
```bash
|
||||
# 서버 ~/project/ykavu/.env
|
||||
FCM_SERVER_KEY=AAAA...
|
||||
```
|
||||
|
||||
```bash
|
||||
cd ~/project/ykavu
|
||||
docker compose up -d core-backend
|
||||
```
|
||||
|
||||
**git / 이미지에 키를 넣지 않는다** (N2-A5).
|
||||
|
||||
> 레거시 HTTP API가 Console에서 비활성이면 HTTP v1 마이그레이션이 필요하다.
|
||||
> 그 전까지는 레거시 키가 있는 프로젝트로 N4-4 스모크를 완료한다.
|
||||
|
||||
### N4-2 / N4-4 — 실기기 스모크
|
||||
|
||||
```bash
|
||||
cd mobile
|
||||
flutter run --release --dart-define=CORE_API_BASE=https://msn.iykyka.com
|
||||
# 가입 → 로그에 "device token registered (FCM)" 확인
|
||||
```
|
||||
|
||||
```bash
|
||||
curl -sS -X POST https://msn.iykyka.com/admin/push-test \
|
||||
-H "Authorization: Bearer $ADMIN_API_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"user_id": <USER_ID>, "title":"와카뷰","body":"push smoke"}'
|
||||
```
|
||||
|
||||
기대: `sent >= 1`, `skipped_reason` 없음. 기기에 알림 표시.
|
||||
|
||||
`only_placeholder_tokens` / `fcm_not_configured` 이면 위 N4-1·N4-3을 다시 확인.
|
||||
|
||||
## Web
|
||||
|
||||
현재 프로덕션 UI는 Flutter Web. Web 푸시는 Firebase Web 설정 + VAPID가 추가로 필요하며
|
||||
이번 N4 범위에서는 **Android 실푸시**를 우선한다. Web은 계속 `install:` 플레이스홀더를 등록한다.
|
||||
|
|
@ -30,7 +30,7 @@ API 메타: `GET https://msn.iykyka.com/demo` — `pairing_steps` / `notes` 포
|
|||
|
||||
- **초안(AI)**: Gemini 키가 서버에 설정되어 있어 **실제 초안**이 생성됩니다.
|
||||
- **L0(비서)**: 와카뷰 발송이 서버에서 막혀 있습니다. 초안 → 입력창 → 직접 전송.
|
||||
- 푸시(FCM)는 아직 플레이스홀더 단계일 수 있습니다.
|
||||
- 푸시(FCM): 코드 경로는 준비됨. Master가 Firebase/`FCM_SERVER_KEY`를 넣기 전에는 플레이스홀더(`docs/fcm-setup.md`).
|
||||
- 문제/스크린샷은 Master에게 전달해 주세요.
|
||||
- 민감 정보·실명 대화는 베타 특성상 최소화해 주세요.
|
||||
|
||||
|
|
|
|||
|
|
@ -63,9 +63,14 @@ HTTP로 검증한다.
|
|||
sudo apt-get install -y libsqlite3-dev libsqlcipher1
|
||||
```
|
||||
|
||||
## 푸시 (FCM)
|
||||
|
||||
- 코드: `lib/services/push_token_service.dart` — Firebase 가능 시 실 토큰, 아니면 `install:`
|
||||
- Master 설정: [`docs/fcm-setup.md`](../docs/fcm-setup.md) (`google-services.json` + `FCM_SERVER_KEY`)
|
||||
|
||||
## 아직 없는 것
|
||||
|
||||
- Firebase 프로젝트의 실제 FCM registration token 연동 (`google-services.json`)
|
||||
- Master Firebase 시크릿 주입 후 실기기 푸시 스모크 (N4-1/3/4)
|
||||
- 오프라인 메시지 큐 / 멀티디바이스 실시간 설정 동기화 고도화
|
||||
- 온보딩 말투 UX 디테일 (PoC #1 결과는 맨 마지막에 반영)
|
||||
- iOS 빌드 (v1 범위 밖)
|
||||
|
|
|
|||
|
|
@ -14,3 +14,6 @@ key.properties
|
|||
**/*.jks
|
||||
*.jks
|
||||
*.keystore
|
||||
|
||||
# Firebase — never commit real project credentials (see docs/fcm-setup.md).
|
||||
app/google-services.json
|
||||
|
|
|
|||
|
|
@ -8,6 +8,11 @@ plugins {
|
|||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
// Apply only when Master drops google-services.json (see docs/fcm-setup.md).
|
||||
if (file("google-services.json").exists()) {
|
||||
apply(plugin = "com.google.gms.google-services")
|
||||
}
|
||||
|
||||
val keystoreProperties = Properties()
|
||||
val keystorePropertiesFile = rootProject.file("key.properties")
|
||||
val hasReleaseKeystore = keystorePropertiesFile.exists()
|
||||
|
|
|
|||
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"project_info": {
|
||||
"project_number": "REPLACE_WITH_FIREBASE_PROJECT_NUMBER",
|
||||
"project_id": "ykavu-REPLACE",
|
||||
"storage_bucket": "ykavu-REPLACE.appspot.com"
|
||||
},
|
||||
"client": [
|
||||
{
|
||||
"client_info": {
|
||||
"mobilesdk_app_id": "1:REPLACE:android:REPLACE",
|
||||
"android_client_info": {
|
||||
"package_name": "com.ykavu.ykavu_mobile"
|
||||
}
|
||||
},
|
||||
"oauth_client": [],
|
||||
"api_key": [
|
||||
{
|
||||
"current_key": "REPLACE_WITH_ANDROID_API_KEY"
|
||||
}
|
||||
],
|
||||
"services": {
|
||||
"appinvite_service": {
|
||||
"other_platform_oauth_client": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"configuration_version": "1"
|
||||
}
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<application
|
||||
android:label="와카뷰"
|
||||
android:name="${applicationName}"
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ plugins {
|
|||
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
|
||||
id("com.android.application") version "8.7.3" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||
id("com.google.gms.google-services") version "4.4.2" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
||||
|
|
|
|||
|
|
@ -96,7 +96,7 @@ class DataFlowScreen extends StatelessWidget {
|
|||
body: '초안이 필요할 때: 최근 대화 몇 줄 + 말투 샘플 일부(최소 컨텍스트)\n'
|
||||
'채팅 릴레이: 보낸 메시지 본문\n'
|
||||
'계정: 표시 이름·초대 코드·세션 토큰\n'
|
||||
'푸시(준비 중): 디바이스 토큰만 등록, 실제 전송은 FCM 프로젝트 연결 후',
|
||||
'푸시: Firebase 연결 시 실 FCM 토큰 등록 · 미연결 시 install 플레이스홀더 (docs/fcm-setup.md)',
|
||||
),
|
||||
_section(
|
||||
context,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,69 @@
|
|||
import 'dart:math';
|
||||
|
||||
import 'package:firebase_core/firebase_core.dart';
|
||||
import 'package:firebase_messaging/firebase_messaging.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
|
||||
/// Resolves a device push token for core-backend registration.
|
||||
///
|
||||
/// Prefer a real FCM registration token when Firebase is configured
|
||||
/// (`google-services.json` on Android). Otherwise fall back to a stable
|
||||
/// `install:` placeholder that the server intentionally skips for delivery.
|
||||
class PushTokenService {
|
||||
PushTokenService({this.storeInstallId, this.readInstallId});
|
||||
|
||||
/// Persist a newly generated install id (KV / secure storage).
|
||||
final Future<void> Function(String installId)? storeInstallId;
|
||||
|
||||
/// Load a previously stored install id.
|
||||
final Future<String?> Function()? readInstallId;
|
||||
|
||||
Future<({String token, String platform, bool isFcm})> resolve() async {
|
||||
final platform = _platformLabel();
|
||||
final fcm = await _tryFirebaseToken();
|
||||
if (fcm != null && fcm.isNotEmpty) {
|
||||
return (token: fcm, platform: platform, isFcm: true);
|
||||
}
|
||||
final installId = await _ensureInstallId();
|
||||
return (token: 'install:$installId', platform: platform, isFcm: false);
|
||||
}
|
||||
|
||||
Future<String?> _tryFirebaseToken() async {
|
||||
// Web push needs a separate Firebase web config + VAPID; skip until provided.
|
||||
if (kIsWeb) return null;
|
||||
if (defaultTargetPlatform != TargetPlatform.android &&
|
||||
defaultTargetPlatform != TargetPlatform.iOS) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
if (Firebase.apps.isEmpty) {
|
||||
await Firebase.initializeApp();
|
||||
}
|
||||
final messaging = FirebaseMessaging.instance;
|
||||
await messaging.requestPermission(alert: true, badge: true, sound: true);
|
||||
final token = await messaging.getToken();
|
||||
if (token == null || token.isEmpty) return null;
|
||||
return token;
|
||||
} catch (e) {
|
||||
debugPrint('FCM token unavailable (using install placeholder): $e');
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<String> _ensureInstallId() async {
|
||||
final existing = await readInstallId?.call();
|
||||
if (existing != null && existing.isNotEmpty) return existing;
|
||||
final rand = Random.secure();
|
||||
final installId =
|
||||
List.generate(16, (_) => rand.nextInt(256).toRadixString(16).padLeft(2, '0')).join();
|
||||
await storeInstallId?.call(installId);
|
||||
return installId;
|
||||
}
|
||||
|
||||
String _platformLabel() {
|
||||
if (kIsWeb) return 'web';
|
||||
if (defaultTargetPlatform == TargetPlatform.android) return 'android';
|
||||
if (defaultTargetPlatform == TargetPlatform.iOS) return 'ios';
|
||||
return 'other';
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
|
@ -7,6 +6,7 @@ import 'package:shared_preferences/shared_preferences.dart';
|
|||
import '../db/app_database.dart';
|
||||
import '../models/models.dart';
|
||||
import '../services/api_client.dart';
|
||||
import '../services/push_token_service.dart';
|
||||
|
||||
class SessionState extends ChangeNotifier {
|
||||
SessionState({ApiClient? api, AppDatabase? db})
|
||||
|
|
@ -148,22 +148,25 @@ class SessionState extends ChangeNotifier {
|
|||
}
|
||||
}
|
||||
|
||||
/// Registers a stable install id as the push token until Firebase Messaging
|
||||
/// is wired with a real FCM registration token (roadmap B).
|
||||
/// Registers a real FCM token when Firebase is configured; otherwise a stable
|
||||
/// `install:` placeholder (server skips placeholders for delivery).
|
||||
Future<void> _registerDeviceTokenBestEffort() async {
|
||||
if (user == null) return;
|
||||
try {
|
||||
_db ??= await _openDb();
|
||||
var installId = await _db!.getKv(_kDeviceInstallId);
|
||||
if (installId == null || installId.isEmpty) {
|
||||
final rand = Random.secure();
|
||||
installId = List.generate(16, (_) => rand.nextInt(256).toRadixString(16).padLeft(2, '0')).join();
|
||||
await _db!.setKv(_kDeviceInstallId, installId);
|
||||
}
|
||||
final resolved = await PushTokenService(
|
||||
readInstallId: () => _db!.getKv(_kDeviceInstallId),
|
||||
storeInstallId: (id) => _db!.setKv(_kDeviceInstallId, id),
|
||||
).resolve();
|
||||
await _api.registerDeviceToken(
|
||||
userId: user!.id,
|
||||
token: 'install:$installId',
|
||||
platform: defaultTargetPlatform == TargetPlatform.android ? 'android' : 'other',
|
||||
token: resolved.token,
|
||||
platform: resolved.platform,
|
||||
);
|
||||
debugPrint(
|
||||
resolved.isFcm
|
||||
? 'device token registered (FCM)'
|
||||
: 'device token registered (install placeholder)',
|
||||
);
|
||||
} catch (e) {
|
||||
debugPrint('device token register skipped: $e');
|
||||
|
|
|
|||
|
|
@ -9,6 +9,14 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "88.0.0"
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: "460e9e684edb461d85498fc166ff8416f303f22216838d302d80676b348c6a4c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.75"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -93,10 +101,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
version: "1.4.0"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -209,6 +217,54 @@ packages:
|
|||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
firebase_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "6f22d1c62e0c20976f02cd842c7b7cd3c0f561cc2052586411871045c08860c9"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.1"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: f74d1d6fabccf7743b0144c2ed363d81049e258e22428ebb6fb0faec2fa7d938
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.0.0"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: ddab99d709b8c27dd47576eb05a1e719e07a2aa45c009a49ae92ac4d2a8ca555
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.9.1"
|
||||
firebase_messaging:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_messaging
|
||||
sha256: "30ad2d59bcd86117dc49d278c8998a0fb390c5a3202f6e43e4bd215d3f1d0556"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "16.4.3"
|
||||
firebase_messaging_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_messaging_platform_interface
|
||||
sha256: "4d144cb42b9a5a42855596be2d7682d32c50169f42e7df2cb3278ecf935e7d63"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.2"
|
||||
firebase_messaging_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_messaging_web
|
||||
sha256: fcd25d0b9da55766ef4d28ae05a7460f5189635d6b42607adcd9f08818fd35f0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.2.3"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -356,26 +412,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
sha256: "6bb818ecbdffe216e81182c2f0714a2e62b593f4a4f13098713ff1685dfb6ab0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
version: "10.0.9"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
sha256: f8b613e7e6a13ec79cfdc0e97638fddb3ab848452eff057653abd3edba760573
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
version: "3.0.9"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
version: "3.0.1"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -396,26 +452,26 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.19"
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.0"
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.18.0"
|
||||
version: "1.16.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -713,10 +769,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.11"
|
||||
version: "0.7.4"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -729,10 +785,10 @@ packages:
|
|||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
sha256: "80b3257d1492ce4d091729e3a67a60407d227c27241d6927be0130c98e741803"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
version: "2.1.4"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
|
@ -798,5 +854,5 @@ packages:
|
|||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.0-0 <4.0.0"
|
||||
dart: ">=3.8.1 <4.0.0"
|
||||
flutter: ">=3.29.0"
|
||||
|
|
|
|||
|
|
@ -45,6 +45,8 @@ dependencies:
|
|||
flutter_secure_storage: ^10.3.1
|
||||
sqlcipher_flutter_libs: ^0.6.8
|
||||
google_fonts: ^6.3.2
|
||||
firebase_core: ^4.12.1
|
||||
firebase_messaging: ^16.4.3
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
|
|||
Loading…
Reference in New Issue