Commit 38200824 authored by Mahmoud Aglan's avatar Mahmoud Aglan

Pre-reset backup: full Flutter app + .claude config + all assets

parents
{
"permissions": {
"allow": [
"Bash(*)",
"Read(*)",
"Edit(*)",
"Write(*)",
"Agent(*)",
"Workflow(*)",
"WebFetch(*)",
"NotebookEdit(*)"
],
"defaultMode": "bypassPermissions"
}
}
# Miscellaneous
*.class
*.log
*.pyc
*.swp
.DS_Store
.atom/
.build/
.buildlog/
.history
.svn/
.swiftpm/
migrate_working_dir/
# IntelliJ related
*.iml
*.ipr
*.iws
.idea/
# The .vscode folder contains launch configuration and tasks you configure in
# VS Code which you may wish to be included in version control, so this line
# is commented out by default.
#.vscode/
# Flutter/Dart/Pub related
**/doc/api/
**/ios/Flutter/.last_build_id
.dart_tool/
.flutter-plugins-dependencies
.pub-cache/
.pub/
/build/
/coverage/
# Symbolication related
app.*.symbols
# Obfuscation related
app.*.map.json
# Android Studio will place build artifacts here
/android/app/debug
/android/app/profile
/android/app/release
# Signing
android/key.properties
android/app/upload-keystore.jks
*.jks
*.keystore
# This file tracks properties of this Flutter project.
# Used by Flutter tool to assess capabilities and perform upgrades etc.
#
# This file should be version controlled and should not be manually edited.
version:
revision: "c9a6c484230f8b5e408ec57be1ef71dee1e77020"
channel: "stable"
project_type: app
# Tracks metadata for the flutter migrate command
migration:
platforms:
- platform: root
create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
- platform: android
create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
- platform: ios
create_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
base_revision: c9a6c484230f8b5e408ec57be1ef71dee1e77020
# User provided section
# List of Local paths (relative to this file) that should be
# ignored by the migrate tool.
#
# Files that are not part of the templates will be ignored by default.
unmanaged_files:
- 'lib/main.dart'
- 'ios/Runner.xcodeproj/project.pbxproj'
# EL3AB Mobile — Architecture Document
## Overview
EL3AB Mobile is a **Flutter-based native shell** wrapping the existing web player at `https://el3ab-player.caprover.al-arcade.com/`. The app provides genuine native functionality (push notifications, haptics, biometric auth, native navigation, offline support) while keeping all game logic, UI, and content updates inside the WebView — meaning updates to the web player propagate instantly to mobile users without app store review.
**Package:** `com.alarcade.el3ab`
**Platforms:** iOS 14+ / Android API 24+ (Android 7.0+)
**Framework:** Flutter 3.x
**Web Player URL:** `https://el3ab-player.caprover.al-arcade.com/`
---
## Why This Won't Get Rejected
Apple (Guideline 4.2) and Google (Minimum Functionality) reject "thin web wrappers." Our app passes because:
| Requirement | How We Satisfy It |
|-------------|-------------------|
| Uses device capabilities | Haptics, biometrics, camera (avatar), contacts, share sheet |
| Native navigation | Flutter bottom tab bar with native transitions |
| Push notifications | FCM/APNs with rich notification content |
| Works offline (partially) | Cached profile, match history, puzzle of the day |
| Native onboarding | Animated splash + first-run tutorial (not a loading spinner) |
| Deep linking | Universal Links (iOS) / App Links (Android) open directly to matches |
| Platform integration | iOS Widgets, App Shortcuts, Spotlight search |
---
## Architecture Diagram
```
┌─────────────────────────────────────────────────────────────┐
│ FLUTTER APP SHELL │
│ │
│ ┌────────────┐ ┌────────────┐ ┌────────────────────────┐ │
│ │ Native │ │ Native │ │ Native Services │ │
│ │ Splash & │ │ Bottom │ │ • Push (FCM/APNs) │ │
│ │ Onboard │ │ Tab Bar │ │ • Haptics │ │
│ │ │ │ (5 tabs) │ │ • Biometric Auth │ │
│ └────────────┘ └─────┬──────┘ │ • Share Sheet │ │
│ │ │ • Deep Links │ │
│ ┌──────────────────────┴───────┐ │ • Offline Cache │ │
│ │ │ │ • Native Audio │ │
│ │ InAppWebView │ │ • App Shortcuts │ │
│ │ (flutter_inappwebview) │ │ • Contacts Access │ │
│ │ │ │ • Camera (avatar) │ │
│ │ Loads: el3ab-player web app │ └────────────────────────┘ │
│ │ │ │
│ │ ◄──── JS Bridge ────► │ │
│ │ │ │
│ └──────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
## Project Structure
```
lib/
├── main.dart # App entry, DI setup
├── app.dart # MaterialApp, theme, routing
├── config/
│ ├── constants.dart # URLs, keys, feature flags
│ └── theme.dart # EL3AB dark theme (matches web)
├── core/
│ ├── bridge/
│ │ ├── js_bridge.dart # JS ↔ Flutter message handler
│ │ ├── bridge_commands.dart # Command enum + serialization
│ │ └── web_controller.dart # WebView controller wrapper
│ ├── services/
│ │ ├── push_service.dart # FCM/APNs registration + handling
│ │ ├── haptics_service.dart # Haptic feedback patterns
│ │ ├── audio_service.dart # Native audio playback
│ │ ├── auth_service.dart # Biometric + token storage
│ │ ├── deep_link_service.dart # Universal/App links
│ │ ├── share_service.dart # Native share sheet
│ │ ├── offline_service.dart # Cache manager
│ │ └── contacts_service.dart# Find friends
│ └── storage/
│ ├── secure_storage.dart # Flutter Secure Storage (tokens)
│ └── cache_manager.dart # Offline data cache (Hive)
├── features/
│ ├── splash/
│ │ └── splash_screen.dart # Animated native splash
│ ├── onboarding/
│ │ └── onboarding_screen.dart # First-run tutorial
│ ├── shell/
│ │ ├── app_shell.dart # Main scaffold with bottom nav
│ │ ├── bottom_nav.dart # Native bottom tab bar
│ │ └── webview_container.dart # WebView with bridge
│ ├── notifications/
│ │ └── notification_handler.dart
│ └── settings/
│ └── native_settings.dart # Push prefs, biometric toggle
├── models/
│ ├── player.dart # Cached player model
│ ├── notification.dart # Push notification model
│ └── match_summary.dart # Offline match history
└── utils/
├── platform_utils.dart # iOS/Android helpers
└── connectivity.dart # Network state monitoring
```
---
## JS Bridge Protocol
The bridge enables bidirectional communication between the web player and Flutter native layer.
### Web → Flutter (JavaScript Channel)
The web player calls `window.flutter_bridge.postMessage(JSON.stringify(payload))`:
```javascript
// Haptic feedback
{ "cmd": "haptic", "type": "light" | "medium" | "heavy" | "success" | "error" | "selection" }
// Share content
{ "cmd": "share", "title": "...", "text": "...", "url": "..." }
// Play native audio
{ "cmd": "audio", "sound": "click" | "move" | "capture" | "win" | "lose" | "dice" | "coin" }
// Sync navigation state (web tells Flutter which tab is active)
{ "cmd": "nav_sync", "world": "play" | "rank" | "social" | "tournaments" | "profile" }
// Auth state changed
{ "cmd": "auth_ready", "token": "jwt...", "userId": "uuid" }
{ "cmd": "auth_logout" }
// Badge update
{ "cmd": "badge", "count": 3 }
// Game state (for offline caching)
{ "cmd": "cache_profile", "data": { ... } }
{ "cmd": "cache_match", "data": { ... } }
// Request native feature
{ "cmd": "request_contacts" }
{ "cmd": "request_camera" } // avatar upload
```
### Flutter → Web (evaluateJavascript)
Flutter injects calls into the WebView:
```javascript
// Handle deep link navigation
window.el3ab_native.onDeepLink({ route: "match", id: "abc123" })
// Handle push notification tap
window.el3ab_native.onPushTap({ type: "match_invite", matchId: "abc123" })
// Network state changed
window.el3ab_native.onConnectivityChanged({ online: true })
// Token refreshed (biometric re-auth)
window.el3ab_native.onTokenRefreshed({ token: "new_jwt..." })
// Native nav tab tapped
window.el3ab_native.onNavTap({ world: "play" })
```
### Web Player Integration (Minimal Changes)
Add to the web player's `engine.js` boot sequence:
```javascript
// Detect native app environment
window.IS_NATIVE_APP = !!window.flutter_bridge;
// Register native bridge listener
if (window.IS_NATIVE_APP) {
window.el3ab_native = {
onDeepLink: (data) => bus.emit('native:deeplink', data),
onPushTap: (data) => bus.emit('native:push_tap', data),
onConnectivityChanged: (data) => bus.emit('native:connectivity', data),
onTokenRefreshed: (data) => store.set('auth.token', data.token),
onNavTap: (data) => scene.switchWorld(data.world)
};
// Hide web bottom nav (Flutter renders it natively)
document.documentElement.classList.add('native-app');
// Override haptic calls to use native
const originalHaptic = input.haptic;
input.haptic = (type) => {
window.flutter_bridge.postMessage(JSON.stringify({ cmd: 'haptic', type }));
};
}
```
And in CSS:
```css
.native-app #hud .bottom-nav { display: none; }
.native-app #game { padding-bottom: 0; }
```
---
## Native Bottom Navigation Bar
Flutter renders a native bottom tab bar that mirrors the web player's 5 worlds:
| Index | Label | Icon | Color (active) |
|-------|-------|------|----------------|
| 0 | الترتيب (Rank) | trophy | #F5B731 (gold) |
| 1 | اجتماعي (Social) | people | #00D4FF (cyan) |
| 2 | العب (Play) | gamepad | #F5B731 (gold) — center, larger |
| 3 | البطولات (Tournaments) | medal | #8B5CF6 (purple) |
| 4 | حسابي (Profile) | person | #94A3B8 (slate) |
- Tab bar background: `#050810` with top border `rgba(255,255,255,0.08)`
- Active indicator: game-color glow beneath icon
- Tab taps → Flutter sends `onNavTap` to WebView → web switches world
- Web nav changes → web sends `nav_sync` to Flutter → bottom bar updates
---
## Push Notifications
### Registration Flow
1. App boot → request notification permission
2. Get FCM token (Android) / APNs device token (iOS)
3. Send token to backend: `POST /api/push.php { action: "register", token, platform, userId }`
4. Backend stores in `push_tokens` table
### Notification Types
| Type | Trigger | Behavior on Tap |
|------|---------|-----------------|
| `match_invite` | Friend invites to play | Open match lobby |
| `your_turn` | Turn-based game, opponent moved | Open active match |
| `match_result` | Game ended while app backgrounded | Open results screen |
| `tournament_start` | Tournament about to begin | Open tournament lobby |
| `friend_online` | Close friend came online | Open social tab |
| `daily_reward` | Daily streak available | Open play tab |
| `chat_message` | Org/friend chat message | Open chat scene |
### Payload Structure
```json
{
"notification": {
"title": "دورك! ♟️",
"body": "أحمد لعب e4 — ردّ عليه"
},
"data": {
"type": "your_turn",
"match_id": "abc123",
"game_type": "chess",
"route": "/match/abc123"
}
}
```
---
## Haptic Feedback Patterns
| Game Event | Haptic Type | iOS | Android |
|------------|------------|-----|---------|
| Button press | Light | UIImpactFeedbackGenerator(.light) | HapticFeedbackConstants.lightImpact |
| Piece move | Medium | UIImpactFeedbackGenerator(.medium) | HapticFeedbackConstants.mediumImpact |
| Capture/eat | Heavy | UIImpactFeedbackGenerator(.heavy) | VibrationEffect (50ms) |
| Win | Success | UINotificationFeedbackGenerator(.success) | Pattern: [0, 50, 30, 50, 30, 100] |
| Lose | Error | UINotificationFeedbackGenerator(.error) | Pattern: [0, 100, 50, 100] |
| Dice roll | Selection (repeated) | UISelectionFeedbackGenerator × 3 | Pattern: [0, 20, 20, 20, 20, 20] |
| Coin earned | Light × 3 rapid | Triple light impact | Pattern: [0, 10, 30, 10, 30, 10] |
| Tab switch | Selection | UISelectionFeedbackGenerator | HapticFeedbackConstants.selectionClick |
---
## Offline Support
### What Works Offline
- View cached player profile (name, avatar, stats, rating)
- Browse match history (last 50 matches cached)
- Daily puzzle (pre-fetched each day, playable offline)
- Settings and preferences
- View friends list (last known state)
### What Shows "Offline" State
- Play tab: grayed-out matchmaking, shows "Connect to play"
- Social: shows cached friends with "last seen" timestamps
- Tournaments: "Connect to view live tournaments"
- Shop: "Connect to browse shop"
### Cache Strategy
- **Hive** (local DB) for structured data (profile, match history, friends)
- **flutter_cache_manager** for images (avatars, assets)
- **SharedPreferences** for simple flags (onboarding complete, push prefs)
- **Flutter Secure Storage** for auth tokens (encrypted, biometric-gated)
### Sync on Reconnect
When connectivity returns:
1. Re-validate auth token
2. Fetch fresh profile data
3. Check for pending notifications
4. Resume WebView connection
---
## Deep Links / Universal Links
### URL Scheme
```
el3ab://match/{matchId} → Open active match
el3ab://invite/{code} → Accept friend/org invite
el3ab://tournament/{id} → Open tournament detail
el3ab://profile/{userId} → View player profile
el3ab://play/{gameType} → Jump to game lobby
```
### Universal Links (HTTPS)
```
https://el3ab-player.caprover.al-arcade.com/match/{id}
https://el3ab-player.caprover.al-arcade.com/invite/{code}
https://el3ab-player.caprover.al-arcade.com/tournament/{id}
```
### iOS Configuration
- `apple-app-site-association` file on web server
- Associated Domains entitlement in Xcode
### Android Configuration
- `assetlinks.json` on web server at `/.well-known/`
- `<intent-filter>` in AndroidManifest.xml with `autoVerify="true"`
---
## Biometric Authentication
### Flow
1. First login: user enters credentials in WebView (normal auth flow)
2. On auth success, web sends `{ cmd: "auth_ready", token, userId }` to Flutter
3. Flutter stores token in Secure Storage (encrypted)
4. Flutter prompts: "Enable Face ID / fingerprint login?"
5. If yes: next app launch → biometric prompt → retrieve token → inject into WebView
### Re-auth
- Token expired while app backgrounded → biometric re-auth → refresh token
- Avoids forcing users to re-enter password after every background
---
## Native Audio (Optional Enhancement)
If web audio has issues on certain devices (common on older Android WebViews):
- Web sends `{ cmd: "audio", sound: "move" }` to Flutter
- Flutter plays from pre-bundled audio assets using `audioplayers` package
- Fallback only: web audio still works, native is for reliability + lower latency
---
## App Shortcuts (Quick Actions)
### iOS (UIApplicationShortcutItem)
- Long-press app icon reveals:
- "Quick Play Chess ♟️" → opens Play tab with chess pre-selected
- "Quick Play Ludo 🎲" → opens Play tab with ludo pre-selected
- "Daily Puzzle 🧩" → opens puzzle of the day
### Android (ShortcutInfo)
- Same 3 shortcuts in long-press menu
- Also supports pinned shortcuts for favorite game
---
## Dependencies (pubspec.yaml)
```yaml
dependencies:
flutter:
sdk: flutter
# WebView
flutter_inappwebview: ^6.0.0 # Full-featured WebView with JS bridge
# Push Notifications
firebase_core: ^3.0.0
firebase_messaging: ^15.0.0
flutter_local_notifications: ^17.0.0
# Storage
flutter_secure_storage: ^9.0.0 # Encrypted token storage
hive_flutter: ^1.1.0 # Fast local DB for offline cache
shared_preferences: ^2.2.0 # Simple key-value
# Auth
local_auth: ^2.2.0 # Biometric auth (Face ID / fingerprint)
# Native Features
share_plus: ^9.0.0 # Native share sheet
url_launcher: ^6.2.0 # External links
connectivity_plus: ^6.0.0 # Network state monitoring
permission_handler: ^11.0.0 # Runtime permissions
contacts_service: ^0.6.3 # Find friends via contacts
image_picker: ^1.0.0 # Camera for avatar upload
app_links: ^6.0.0 # Deep links / Universal links
quick_actions_ios: ^1.0.0 # Home screen shortcuts (iOS)
quick_actions_android: ^1.0.0 # Home screen shortcuts (Android)
# UI
flutter_svg: ^2.0.0 # SVG icons
cached_network_image: ^3.3.0 # Image caching
shimmer: ^3.0.0 # Loading states
lottie: ^3.0.0 # Animated splash/onboarding
# Utils
path_provider: ^2.1.0
package_info_plus: ^8.0.0
```
---
## App Lifecycle
```
┌──────────────────────────────────────────────────┐
│ APP LAUNCH │
├──────────────────────────────────────────────────┤
│ │
│ 1. Native Splash (Lottie animation, 1.5s) │
│ └─ Initialize services (push, connectivity) │
│ │
│ 2. Check auth state │
│ ├─ Has biometric + stored token? │
│ │ └─ Biometric prompt → inject token │
│ ├─ Has stored token (no biometric)? │
│ │ └─ Load WebView with token │
│ └─ No token? │
│ └─ Load WebView (shows auth-splash) │
│ │
│ 3. Load App Shell │
│ ├─ Native bottom tab bar (5 tabs) │
│ ├─ WebView (loads player URL) │
│ └─ Register JS bridge handlers │
│ │
│ 4. WebView Ready │
│ ├─ Web sends auth_ready → register push │
│ ├─ Web sends nav_sync → sync bottom bar │
│ └─ App is fully interactive │
│ │
├──────────────────────────────────────────────────┤
│ APP BACKGROUNDED │
├──────────────────────────────────────────────────┤
│ • Keep WebSocket alive (5 min grace) │
│ • Cache current state │
│ • Push notifications remain active │
│ │
├──────────────────────────────────────────────────┤
│ APP FOREGROUNDED │
├──────────────────────────────────────────────────┤
│ • Check token validity │
│ • Reconnect WebSocket if dropped │
│ • Sync missed notifications │
│ • Resume WebView (no reload if < 5 min) │
└──────────────────────────────────────────────────┘
```
---
## Web Player Changes Required
Minimal changes to the existing web player — a single file addition:
### New file: `public/js/core/native-bridge.js`
Handles all native app communication. Loaded conditionally only when running inside the Flutter app (detected via `window.flutter_bridge` existence).
### Changes to existing files:
1. **`engine.js`** — Add one import + 3 lines at boot:
```javascript
import * as nativeBridge from './core/native-bridge.js';
// In boot():
if (window.flutter_bridge) nativeBridge.init();
```
2. **`core/input.js`** — Override haptic function when native:
```javascript
// Already handled by native-bridge.js patching
```
3. **`public/css/core.css`** — Add native-app overrides:
```css
.native-app .bottom-nav { display: none !important; }
.native-app .scene-container { padding-bottom: 0 !important; }
.native-app body { padding-bottom: env(safe-area-inset-bottom); }
```
**Total web changes: ~50 lines across 3 files.**
---
## Build & Release Pipeline
### iOS
- Xcode 15+, iOS deployment target 14.0
- Signing: Apple Development identity (aglantech@gmail.com)
- Capabilities: Push Notifications, Associated Domains, Background Modes
- TestFlight for beta → App Store submission
### Android
- Gradle 8.x, Android SDK 34, minSdk 24
- Signing: upload keystore (generate before first release)
- Google Play Console: Internal testing → Production
### CI/CD (future)
- GitHub Actions or Codemagic
- On push to `release/*`: build iOS + Android → upload to stores
---
## Testing Strategy
| Layer | Tool | What |
|-------|------|------|
| Unit | flutter_test | Bridge command parsing, cache logic |
| Widget | flutter_test | Bottom nav, splash, settings screens |
| Integration | integration_test | Full app boot → WebView load → bridge communication |
| Manual | Real devices | Push notifications, haptics, biometrics, deep links |
---
## Security Considerations
- Auth tokens stored in Flutter Secure Storage (Keychain on iOS, EncryptedSharedPreferences on Android)
- WebView: disable file access, restrict navigation to `al-arcade.com` domains
- Certificate pinning for API calls (optional, adds protection against MITM)
- No sensitive data in WebView cache (tokens passed via bridge, not URL params)
- Biometric gate on app resume (optional user preference)
---
## Estimated Implementation Timeline
| Phase | Duration | Deliverable |
|-------|----------|-------------|
| 1. Core shell + WebView + Bridge | 2-3 days | App loads web player, bridge works |
| 2. Native bottom nav + sync | 1 day | Tabs work bidirectionally |
| 3. Push notifications (FCM) | 1-2 days | Registration + handling |
| 4. Haptics + audio | 0.5 day | All patterns wired |
| 5. Biometric auth | 0.5 day | Face ID / fingerprint |
| 6. Deep links | 1 day | Universal links + scheme |
| 7. Offline support | 1-2 days | Cache layer + offline UI |
| 8. Splash + onboarding | 0.5 day | Animated native intro |
| 9. Share + contacts + shortcuts | 0.5 day | Native integrations |
| 10. Web player bridge code | 0.5 day | ~50 lines on web side |
| 11. Testing + polish | 2-3 days | Real device testing |
| **Total** | **~10-14 days** | Store-ready build |
---
## Store Submission Checklist
### App Store (iOS)
- [ ] App icon (1024×1024)
- [ ] Screenshots (6.7", 6.5", 5.5" — Arabic, RTL)
- [ ] App Preview video (optional but recommended)
- [ ] Privacy policy URL
- [ ] App description (Arabic + English)
- [ ] Age rating: 12+ (competitive gaming, mild language in chat)
- [ ] In-App Purchases: None (for now)
- [ ] Review notes: explain native features (haptics, push, biometric, offline)
### Google Play
- [ ] App icon (512×512)
- [ ] Feature graphic (1024×500)
- [ ] Screenshots (phone + tablet)
- [ ] Privacy policy URL
- [ ] Content rating questionnaire
- [ ] Target audience declaration
- [ ] Data safety form
---
## Key Decision: Why Flutter (not React Native / Kotlin+Swift)
| Factor | Flutter | React Native | Native (Kotlin/Swift) |
|--------|---------|-------------|----------------------|
| Single codebase | ✅ | ✅ | ❌ (2 codebases) |
| WebView quality | ✅ (flutter_inappwebview is excellent) | ⚠️ (react-native-webview has quirks) | ✅ |
| Native feel | ✅ (Cupertino + Material) | ✅ | ✅✅ |
| Team knowledge | ✅ (Dart is simple) | ⚠️ (JS ecosystem complexity) | ❌ (need both Kotlin + Swift) |
| Build speed | ✅ | ⚠️ | ⚠️ |
| Store approval | ✅ (used by major apps) | ✅ | ✅✅ |
Flutter with `flutter_inappwebview` gives us the best balance: one codebase, excellent WebView bridge, truly native bottom nav and services, and fast iteration.
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEA9j4m//cEMsGH9fWDxlS8KxNrKO42Yn+ZdBEPC0JcCXnNkt5v
slGreclTJzbUZ3PIDo8xTY3S41PdkXJuqblvlOVBfcGup/26MPsVtX2RuQXJxpSq
EiM69yM0wYD3JH/Rsv19YUDhOq799T571IluU5JBCLPxI7rjHeCFJRb6b741+aRo
d/O/XVU1mp8SBbPOufbKfFE6HaDh34XBaleDgaEtvLTNMxJeJ0tMFBrXymTjn0Et
Xv184A+puYQ0JTaEFxYrhemvZJxE7JX2IH2DeNdfOoZs1AyS634LK9CsK8bdu+bK
MKeMPFweyP5NZDwG0Fau+UK902a1V3Ts0nHytwIDAQABAoIBAH14ugKfaYI7Xv4T
NiwvLRp98JKQnyGdhyG/N30zORaS/A8kTKH+P3P1A+vtYlm8g2n3yNWNuLGhIDtm
pcDR/mtOTgq4UDjFbsRajHtIGqK+skv/oJFRZlBbnhwjjMsKXl3oEKUnnBx84ma+
ifaIeLEYyw6WMf9U0Tl+GBoIFWAt+g/j0sRzQlP/YiBWpJVXLaBZUnZvkvNAQH/r
AGcxfJWMFMZ4Gr/goGcyyKm5I43jmZH3pQFe3LtQq8GpMDPCegl3tuMpz66L62u1
XhDpZrc6JMhmGHrAhL6SOb21CXnpD4uXFZThcmRpsBAVjAVch4OnOTcVd1onANCo
7LL7IDECgYEA+3yUxAQbpYF1f9aVLsE0jAgZQi9JVdXzYAdeGFXxzouEQ+jc2ZWs
3oQ+hkD1Tbo80uGSZ25YN/0XuQby2LIzkqdLNeaD+mkD6OrQiIlq600Cd7fpMzTN
F6Vc3zzPznwMKyIJrQmHvI1bgeDXbORNiuI8/d/uMOzZEp1TSewoyI0CgYEA+ql6
o9o7TxzqMjveTo/Le/qnpjNJt5SiuwwO0exCOujz8Yz4zIiQoChadifEGBORFD1v
jTAq30eJS6z230CEiBMwWQ9U4ZTEDl43uwfIb5kBQpA46nDMpeOJaYIAZZ0Q5hyk
RNE17Pv79rF+9YNYrszyW7o8QtGfSnKMPswJ4VMCgYEA+o7Z7R2mqdP8zDxv7+BA
yU98Uuun5iQ+0aslfZuLSlzIj5xfBU3RqPUbEkl/3UwzM2vYoMJYlJfN3ePlai7J
oVCaZ35eecvNQG9LCu3Wn87fKYYLiqANwoRXrnb5F+GRghvshgEbwqkXxaPNHkms
VDYpzEsiFo6zi49Bn0HyNDUCgYBZmyRgVq9FBM0j0AsUgor3cw9jdoovuk7CFvll
SdQTQxuRiheW5mrtFf/gpS6QfM4sMhoimsl4sBYAm4BVJrc5/cIW1Eg/q/K6QCwk
DBA29LdimDQAevptgv2oXfTOXmugFzUR9MgWQ6467hC4q4+UTWeGKvlH2a/b6T8B
Y+dUSQKBgGP6zuQZxvFLDXiLFbjALnYkGW3y4AcbksfGS81s8+QnEKzDFaxcu/6M
d4Ww5X1WVNbTNBA3ea49zuV1snp+fC158EvSexHzGVQdPylH0VqPte4OOK7deyDj
XFVhIkhrrdhyfmaOfioXvHIInFt/hTlujFVkkpl5WX7PHmBLKdej
-----END RSA PRIVATE KEY-----
\ No newline at end of file
# el3ab
A new Flutter project.
## Getting Started
This project is a starting point for a Flutter application.
A few resources to get you started if this is your first Flutter project:
- [Learn Flutter](https://docs.flutter.dev/get-started/learn-flutter)
- [Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
- [Flutter learning resources](https://docs.flutter.dev/reference/learning-resources)
For help getting started with Flutter development, view the
[online documentation](https://docs.flutter.dev/), which offers tutorials,
samples, guidance on mobile development, and a full API reference.
# This file configures the analyzer, which statically analyzes Dart code to
# check for errors, warnings, and lints.
#
# The issues identified by the analyzer are surfaced in the UI of Dart-enabled
# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be
# invoked from the command line by running `flutter analyze`.
# The following line activates a set of recommended lints for Flutter apps,
# packages, and plugins designed to encourage good coding practices.
include: package:flutter_lints/flutter.yaml
linter:
# The lint rules applied to this project can be customized in the
# section below to disable rules from the `package:flutter_lints/flutter.yaml`
# included above or to enable additional rules. A list of all available lints
# and their documentation is published at https://dart.dev/lints.
#
# Instead of disabling a lint rule for the entire project in the
# section below, it can also be suppressed for a single line of code
# or a specific dart file by using the `// ignore: name_of_lint` and
# `// ignore_for_file: name_of_lint` syntax on the line or in the file
# producing the lint.
rules:
# avoid_print: false # Uncomment to disable the `avoid_print` rule
# prefer_single_quotes: true # Uncomment to enable the `prefer_single_quotes` rule
# Additional information about this file can be found at
# https://dart.dev/guides/language/analysis-options
gradle-wrapper.jar
/.gradle
/captures/
/gradlew
/gradlew.bat
/local.properties
GeneratedPluginRegistrant.java
.cxx/
# Remember to never publicly share your keystore.
# See https://flutter.dev/to/reference-keystore
key.properties
**/*.keystore
**/*.jks
import java.util.Properties
import java.io.FileInputStream
plugins {
id("com.android.application")
id("dev.flutter.flutter-gradle-plugin")
}
val keystoreProperties = Properties()
val keystorePropertiesFile = rootProject.file("key.properties")
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(FileInputStream(keystorePropertiesFile))
}
android {
namespace = "com.alarcade.el3ab"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
compileOptions {
isCoreLibraryDesugaringEnabled = true
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
defaultConfig {
applicationId = "com.alarcade.el3ab"
minSdk = 24
targetSdk = flutter.targetSdkVersion
versionCode = flutter.versionCode
versionName = flutter.versionName
multiDexEnabled = true
}
signingConfigs {
create("release") {
keyAlias = keystoreProperties["keyAlias"] as String
keyPassword = keystoreProperties["keyPassword"] as String
storeFile = file(keystoreProperties["storeFile"] as String)
storePassword = keystoreProperties["storePassword"] as String
}
}
buildTypes {
release {
signingConfig = signingConfigs.getByName("release")
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
}
kotlin {
compilerOptions {
jvmTarget = org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
}
}
dependencies {
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
flutter {
source = "../.."
}
-keep class io.flutter.app.** { *; }
-keep class io.flutter.plugin.** { *; }
-keep class io.flutter.util.** { *; }
-keep class io.flutter.view.** { *; }
-keep class io.flutter.** { *; }
-keep class io.flutter.plugins.** { *; }
-dontwarn io.flutter.embedding.**
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Permissions -->
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.VIBRATE"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.USE_BIOMETRIC"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<application
android:label="EL3AB"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"
android:usesCleartextTraffic="false"
android:hardwareAccelerated="true">
<activity
android:name=".MainActivity"
android:exported="true"
android:launchMode="singleTop"
android:taskAffinity=""
android:theme="@style/LaunchTheme"
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="io.flutter.embedding.android.NormalTheme"
android:resource="@style/NormalTheme"
/>
<!-- Main launcher -->
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<!-- Deep Links (custom scheme) -->
<intent-filter>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data android:scheme="el3ab"/>
</intent-filter>
<!-- App Links (HTTPS universal links) -->
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.DEFAULT"/>
<category android:name="android.intent.category.BROWSABLE"/>
<data
android:scheme="https"
android:host="el3ab-player.caprover.al-arcade.com"/>
</intent-filter>
</activity>
<!-- Notification channel default -->
<meta-data
android:name="default_notification_channel_id"
android:value="el3ab_game"/>
<meta-data
android:name="flutterEmbedding"
android:value="2" />
</application>
<queries>
<intent>
<action android:name="android.intent.action.PROCESS_TEXT"/>
<data android:mimeType="text/plain"/>
</intent>
<intent>
<action android:name="android.intent.action.VIEW"/>
<data android:scheme="https"/>
</intent>
</queries>
</manifest>
package com.alarcade.el3ab
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()
<?xml version="1.0" encoding="utf-8"?>
<!-- Modify this file to customize your launch splash screen -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?android:colorBackground" />
<!-- You can insert your own image assets here -->
<!-- <item>
<bitmap
android:gravity="center"
android:src="@mipmap/launch_image" />
</item> -->
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/launch_bg" />
</layer-list>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">#FF0A1020</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">#FF050810</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">#FF0A1020</item>
</style>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="notification_color">#FFE4AC38</color>
<color name="launch_bg">#FF050810</color>
</resources>
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">@drawable/launch_background</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">#FF0A1020</item>
</style>
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
<item name="android:windowBackground">#FF050810</item>
<item name="android:statusBarColor">@android:color/transparent</item>
<item name="android:navigationBarColor">#FF0A1020</item>
</style>
</resources>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- The INTERNET permission is required for development. Specifically,
the Flutter tool needs it to communicate with the running application
to allow setting breakpoints, to provide hot reload, etc.
-->
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>
allprojects {
repositories {
google()
mavenCentral()
}
}
val newBuildDir: Directory =
rootProject.layout.buildDirectory
.dir("../../build")
.get()
rootProject.layout.buildDirectory.value(newBuildDir)
subprojects {
val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)
project.layout.buildDirectory.value(newSubprojectBuildDir)
}
subprojects {
project.evaluationDependsOn(":app")
}
tasks.register<Delete>("clean") {
delete(rootProject.layout.buildDirectory)
}
This source diff could not be displayed because it is too large. You can view the blob instead.
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
# This newDsl flag was added by the Flutter template
android.newDsl=false
# This builtInKotlin flag was added by the Flutter template
android.builtInKotlin=false
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.1.0-all.zip
pluginManagement {
val flutterSdkPath =
run {
val properties = java.util.Properties()
file("local.properties").inputStream().use { properties.load(it) }
val flutterSdkPath = properties.getProperty("flutter.sdk")
require(flutterSdkPath != null) { "flutter.sdk not set in local.properties" }
flutterSdkPath
}
includeBuild("$flutterSdkPath/packages/flutter_tools/gradle")
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "8.11.1" apply false
id("org.jetbrains.kotlin.android") version "2.2.20" apply false
}
include(":app")
description: This file stores settings for Dart & Flutter DevTools.
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
extensions:
**/dgph
*.mode1v3
*.mode2v3
*.moved-aside
*.pbxuser
*.perspectivev3
**/*sync/
.sconsign.dblite
.tags*
**/.vagrant/
**/DerivedData/
Icon?
**/Pods/
**/.symlinks/
profile
xcuserdata
**/.generated/
Flutter/App.framework
Flutter/Flutter.framework
Flutter/Flutter.podspec
Flutter/Generated.xcconfig
Flutter/ephemeral/
Flutter/app.flx
Flutter/app.zip
Flutter/flutter_assets/
Flutter/flutter_export_environment.sh
ServiceDefinitions.json
Runner/GeneratedPluginRegistrant.*
# Exceptions to above rules.
!default.mode1v3
!default.mode2v3
!default.pbxuser
!default.perspectivev3
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>App</string>
<key>CFBundleIdentifier</key>
<string>io.flutter.flutter.app</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>App</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>1.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>1.0</string>
</dict>
</plist>
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
platform :ios, '15.0'
# CocoaPods analytics sends network stats synchronously affecting flutter build latency.
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
project 'Runner', {
'Debug' => :debug,
'Profile' => :release,
'Release' => :release,
}
def flutter_root
generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)
unless File.exist?(generated_xcode_build_settings_path)
raise "#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first"
end
File.foreach(generated_xcode_build_settings_path) do |line|
matches = line.match(/FLUTTER_ROOT\=(.*)/)
return matches[1].strip if matches
end
raise "FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get"
end
require File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)
flutter_ios_podfile_setup
target 'Runner' do
use_frameworks!
flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))
target 'RunnerTests' do
inherit! :search_paths
end
end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
end
end
PODS:
- app_links (6.4.1):
- Flutter
- connectivity_plus (0.0.1):
- Flutter
- Flutter (1.0.0)
- flutter_inappwebview_ios (0.0.1):
- Flutter
- flutter_inappwebview_ios/Core (= 0.0.1)
- OrderedSet (~> 6.0.3)
- flutter_inappwebview_ios/Core (0.0.1):
- Flutter
- OrderedSet (~> 6.0.3)
- flutter_local_notifications (0.0.1):
- Flutter
- flutter_secure_storage (6.0.0):
- Flutter
- local_auth_darwin (0.0.1):
- Flutter
- FlutterMacOS
- OrderedSet (6.0.3)
- package_info_plus (0.4.5):
- Flutter
- permission_handler_apple (9.4.8):
- Flutter
- share_plus (0.0.1):
- Flutter
- shared_preferences_foundation (0.0.1):
- Flutter
- FlutterMacOS
- sqflite_darwin (0.0.4):
- Flutter
- FlutterMacOS
- url_launcher_ios (0.0.1):
- Flutter
DEPENDENCIES:
- app_links (from `.symlinks/plugins/app_links/ios`)
- connectivity_plus (from `.symlinks/plugins/connectivity_plus/ios`)
- Flutter (from `Flutter`)
- flutter_inappwebview_ios (from `.symlinks/plugins/flutter_inappwebview_ios/ios`)
- flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
- flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`)
- local_auth_darwin (from `.symlinks/plugins/local_auth_darwin/darwin`)
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
- permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`)
- share_plus (from `.symlinks/plugins/share_plus/ios`)
- shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
- sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
SPEC REPOS:
trunk:
- OrderedSet
EXTERNAL SOURCES:
app_links:
:path: ".symlinks/plugins/app_links/ios"
connectivity_plus:
:path: ".symlinks/plugins/connectivity_plus/ios"
Flutter:
:path: Flutter
flutter_inappwebview_ios:
:path: ".symlinks/plugins/flutter_inappwebview_ios/ios"
flutter_local_notifications:
:path: ".symlinks/plugins/flutter_local_notifications/ios"
flutter_secure_storage:
:path: ".symlinks/plugins/flutter_secure_storage/ios"
local_auth_darwin:
:path: ".symlinks/plugins/local_auth_darwin/darwin"
package_info_plus:
:path: ".symlinks/plugins/package_info_plus/ios"
permission_handler_apple:
:path: ".symlinks/plugins/permission_handler_apple/ios"
share_plus:
:path: ".symlinks/plugins/share_plus/ios"
shared_preferences_foundation:
:path: ".symlinks/plugins/shared_preferences_foundation/darwin"
sqflite_darwin:
:path: ".symlinks/plugins/sqflite_darwin/darwin"
url_launcher_ios:
:path: ".symlinks/plugins/url_launcher_ios/ios"
SPEC CHECKSUMS:
app_links: 3dbc685f76b1693c66a6d9dd1e9ab6f73d97dc0a
connectivity_plus: cb623214f4e1f6ef8fe7403d580fdad517d2f7dd
Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
flutter_inappwebview_ios: b89ba3482b96fb25e00c967aae065701b66e9b99
flutter_local_notifications: 395056b3175ba4f08480a7c5de30cd36d69827e4
flutter_secure_storage: 1ed9476fba7e7a782b22888f956cce43e2c62f13
local_auth_darwin: c3ee6cce0a8d56be34c8ccb66ba31f7f180aaebb
OrderedSet: e539b66b644ff081c73a262d24ad552a69be3a94
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
permission_handler_apple: 92d754bbaa7361d436db2d6c3c1c2a0fdcec462e
share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
PODFILE CHECKSUM: ce2a4dd764e1c7aeed6a7cdc5e61d092b6dc6d32
COCOAPODS: 1.16.2
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 54;
objects = {
/* Begin PBXBuildFile section */
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
4BC1AF9D7DF769E34E8186BA /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AA0F00DA005396A558A2E5E7 /* Pods_RunnerTests.framework */; };
658C5F7EB3EB12D4C792852D /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B83896D1A9AE1F485217EF59 /* Pods_Runner.framework */; };
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */; };
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
proxyType = 1;
remoteGlobalIDString = 97C146ED1CF9000F007C117D;
remoteInfo = Runner;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
isa = PBXCopyFilesBuildPhase;
buildActionMask = 2147483647;
dstPath = "";
dstSubfolderSpec = 10;
files = (
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
0910F98544B303977072BE90 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; };
0964B8B6C7773142CDEAB912 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; };
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; };
18DC239F65CB2BF08E144956 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = "<group>"; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; };
9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; };
9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; };
97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };
97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
A9430C818E9B9CBC5FEA80A4 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; };
AA0F00DA005396A558A2E5E7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B83896D1A9AE1F485217EF59 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
BD10B9AB07245FDF2CB72D89 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; };
FDFE1B3CF17B71CDC3EF6A93 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
91A538430ADFE5AE47C05E9B /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4BC1AF9D7DF769E34E8186BA /* Pods_RunnerTests.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EB1CF9000F007C117D /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
658C5F7EB3EB12D4C792852D /* Pods_Runner.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
331C8082294A63A400263BE5 /* RunnerTests */ = {
isa = PBXGroup;
children = (
331C807B294A618700263BE5 /* RunnerTests.swift */,
);
path = RunnerTests;
sourceTree = "<group>";
};
425C2C0FBC9E57C57E874B58 /* Pods */ = {
isa = PBXGroup;
children = (
18DC239F65CB2BF08E144956 /* Pods-Runner.debug.xcconfig */,
A9430C818E9B9CBC5FEA80A4 /* Pods-Runner.release.xcconfig */,
BD10B9AB07245FDF2CB72D89 /* Pods-Runner.profile.xcconfig */,
0910F98544B303977072BE90 /* Pods-RunnerTests.debug.xcconfig */,
0964B8B6C7773142CDEAB912 /* Pods-RunnerTests.release.xcconfig */,
FDFE1B3CF17B71CDC3EF6A93 /* Pods-RunnerTests.profile.xcconfig */,
);
path = Pods;
sourceTree = "<group>";
};
9740EEB11CF90186004384FC /* Flutter */ = {
isa = PBXGroup;
children = (
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,
9740EEB21CF90195004384FC /* Debug.xcconfig */,
7AFA3C8E1D35360C0083082E /* Release.xcconfig */,
9740EEB31CF90195004384FC /* Generated.xcconfig */,
);
name = Flutter;
sourceTree = "<group>";
};
97C146E51CF9000F007C117D = {
isa = PBXGroup;
children = (
9740EEB11CF90186004384FC /* Flutter */,
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
425C2C0FBC9E57C57E874B58 /* Pods */,
B1C79FDC80F0D2F2FAE3F60D /* Frameworks */,
);
sourceTree = "<group>";
};
97C146EF1CF9000F007C117D /* Products */ = {
isa = PBXGroup;
children = (
97C146EE1CF9000F007C117D /* Runner.app */,
331C8081294A63A400263BE5 /* RunnerTests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
97C146F01CF9000F007C117D /* Runner */ = {
isa = PBXGroup;
children = (
97C146FA1CF9000F007C117D /* Main.storyboard */,
97C146FD1CF9000F007C117D /* Assets.xcassets */,
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,
97C147021CF9000F007C117D /* Info.plist */,
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,
74858FAE1ED2DC5600515810 /* AppDelegate.swift */,
7884E8672EC3CC0400C636F2 /* SceneDelegate.swift */,
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
);
path = Runner;
sourceTree = "<group>";
};
B1C79FDC80F0D2F2FAE3F60D /* Frameworks */ = {
isa = PBXGroup;
children = (
B83896D1A9AE1F485217EF59 /* Pods_Runner.framework */,
AA0F00DA005396A558A2E5E7 /* Pods_RunnerTests.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
331C8080294A63A400263BE5 /* RunnerTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
buildPhases = (
ED6DF58CC200E9DD913E58CC /* [CP] Check Pods Manifest.lock */,
331C807D294A63A400263BE5 /* Sources */,
331C807F294A63A400263BE5 /* Resources */,
91A538430ADFE5AE47C05E9B /* Frameworks */,
);
buildRules = (
);
dependencies = (
331C8086294A63A400263BE5 /* PBXTargetDependency */,
);
name = RunnerTests;
productName = RunnerTests;
productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
97C146ED1CF9000F007C117D /* Runner */ = {
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
165A333941EE463ED05D3B62 /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
EA84C29E65B905086F0CD112 /* [CP] Embed Pods Frameworks */,
8FB9D98AE4C8BFCB6B2BADDA /* [CP] Copy Pods Resources */,
);
buildRules = (
);
dependencies = (
);
name = Runner;
productName = Runner;
productReference = 97C146EE1CF9000F007C117D /* Runner.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
97C146E61CF9000F007C117D /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = YES;
LastUpgradeCheck = 1510;
ORGANIZATIONNAME = "";
TargetAttributes = {
331C8080294A63A400263BE5 = {
CreatedOnToolsVersion = 14.0;
TestTargetID = 97C146ED1CF9000F007C117D;
};
97C146ED1CF9000F007C117D = {
CreatedOnToolsVersion = 7.3.1;
LastSwiftMigration = 1100;
};
};
};
buildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */;
compatibilityVersion = "Xcode 9.3";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 97C146E51CF9000F007C117D;
productRefGroup = 97C146EF1CF9000F007C117D /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
97C146ED1CF9000F007C117D /* Runner */,
331C8080294A63A400263BE5 /* RunnerTests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
331C807F294A63A400263BE5 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EC1CF9000F007C117D /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXShellScriptBuildPhase section */
165A333941EE463ED05D3B62 /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}",
);
name = "Thin Binary";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin";
};
8FB9D98AE4C8BFCB6B2BADDA /* [CP] Copy Pods Resources */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Copy Pods Resources";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
showEnvVarsInLog = 0;
};
9740EEB61CF901F6004384FC /* Run Script */ = {
isa = PBXShellScriptBuildPhase;
alwaysOutOfDate = 1;
buildActionMask = 2147483647;
files = (
);
inputPaths = (
);
name = "Run Script";
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
EA84C29E65B905086F0CD112 /* [CP] Embed Pods Frameworks */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist",
);
name = "[CP] Embed Pods Frameworks";
outputFileListPaths = (
"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n";
showEnvVarsInLog = 0;
};
ED6DF58CC200E9DD913E58CC /* [CP] Check Pods Manifest.lock */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
"${PODS_ROOT}/Manifest.lock",
);
name = "[CP] Check Pods Manifest.lock";
outputFileListPaths = (
);
outputPaths = (
"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt",
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
331C807D294A63A400263BE5 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
97C146EA1CF9000F007C117D /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,
7884E8682EC3CC0700C636F2 /* SceneDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
331C8086294A63A400263BE5 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 97C146ED1CF9000F007C117D /* Runner */;
targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
97C146FA1CF9000F007C117D /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C146FB1CF9000F007C117D /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
97C147001CF9000F007C117D /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
249021D3217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Profile;
};
249021D4217E4FDB00AE95B9 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = HXAUF476A7;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "El3ab - العب\nEl3ab - العب\nEl3ab - العب\nEl3ab - العب\n";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.board-games";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.al-arcade.el3ab";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = el3abBoardGames;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Profile;
};
331C8088294A63A400263BE5 /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0910F98544B303977072BE90 /* Pods-RunnerTests.debug.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.al-arcade.el3ab.RunnerTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Debug;
};
331C8089294A63A400263BE5 /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 0964B8B6C7773142CDEAB912 /* Pods-RunnerTests.release.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.al-arcade.el3ab.RunnerTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Release;
};
331C808A294A63A400263BE5 /* Profile */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = FDFE1B3CF17B71CDC3EF6A93 /* Pods-RunnerTests.profile.xcconfig */;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.al-arcade.el3ab.RunnerTests";
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
};
name = Profile;
};
97C147031CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
97C147041CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
CLANG_CXX_LIBRARY = "libc++";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
97C147061CF9000F007C117D /* Debug */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = HXAUF476A7;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "El3ab - العب\nEl3ab - العب\nEl3ab - العب\nEl3ab - العب\n";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.board-games";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.al-arcade.el3ab";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = el3abBoardGames;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Debug;
};
97C147071CF9000F007C117D /* Release */ = {
isa = XCBuildConfiguration;
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "Apple Development";
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Distribution";
CODE_SIGN_STYLE = Manual;
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
DEVELOPMENT_TEAM = "";
"DEVELOPMENT_TEAM[sdk=iphoneos*]" = HXAUF476A7;
ENABLE_BITCODE = NO;
INFOPLIST_FILE = Runner/Info.plist;
INFOPLIST_KEY_CFBundleDisplayName = "El3ab - العب\nEl3ab - العب\nEl3ab - العب\nEl3ab - العب\n";
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.board-games";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 3.0.0;
PRODUCT_BUNDLE_IDENTIFIER = "com.al-arcade.el3ab";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
"PROVISIONING_PROFILE_SPECIFIER[sdk=iphoneos*]" = el3abBoardGames;
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SUPPORTS_XR_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = 1;
VERSIONING_SYSTEM = "apple-generic";
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
331C8088294A63A400263BE5 /* Debug */,
331C8089294A63A400263BE5 /* Release */,
331C808A294A63A400263BE5 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147031CF9000F007C117D /* Debug */,
97C147041CF9000F007C117D /* Release */,
249021D3217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */ = {
isa = XCConfigurationList;
buildConfigurations = (
97C147061CF9000F007C117D /* Debug */,
97C147071CF9000F007C117D /* Release */,
249021D4217E4FDB00AE95B9 /* Profile */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 97C146E61CF9000F007C117D /* Project object */;
}
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1510"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES">
<PreActions>
<ExecutionAction
ActionType = "Xcode.IDEStandardExecutionActionsCore.ExecutionActionType.ShellScriptAction">
<ActionContent
title = "Run Prepare Flutter Framework Script"
scriptText = "/bin/sh &quot;$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh&quot; prepare&#10;">
<EnvironmentBuildable>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</EnvironmentBuildable>
</ActionContent>
</ExecutionAction>
</PreActions>
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
shouldUseLaunchSchemeArgsEnv = "YES">
<MacroExpansion>
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</MacroExpansion>
<Testables>
<TestableReference
skipped = "NO"
parallelizable = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "331C8080294A63A400263BE5"
BuildableName = "RunnerTests.xctest"
BlueprintName = "RunnerTests"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
customLLDBInitFile = "$(SRCROOT)/Flutter/ephemeral/flutter_lldbinit"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
enableGPUValidationMode = "1"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Profile"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "97C146ED1CF9000F007C117D"
BuildableName = "Runner.app"
BlueprintName = "Runner"
ReferencedContainer = "container:Runner.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "group:Runner.xcodeproj">
</FileRef>
<FileRef
location = "group:Pods/Pods.xcodeproj">
</FileRef>
</Workspace>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>IDEDidComputeMac32BitWarning</key>
<true/>
</dict>
</plist>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>
import Flutter
import UIKit
@main
@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
// Register for local notifications
UNUserNotificationCenter.current().delegate = self
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
}
}
{
"images" : [
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "20x20",
"idiom" : "iphone",
"filename" : "Icon-App-20x20@3x.png",
"scale" : "3x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "iphone",
"filename" : "Icon-App-29x29@3x.png",
"scale" : "3x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "iphone",
"filename" : "Icon-App-40x40@3x.png",
"scale" : "3x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@2x.png",
"scale" : "2x"
},
{
"size" : "60x60",
"idiom" : "iphone",
"filename" : "Icon-App-60x60@3x.png",
"scale" : "3x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@1x.png",
"scale" : "1x"
},
{
"size" : "20x20",
"idiom" : "ipad",
"filename" : "Icon-App-20x20@2x.png",
"scale" : "2x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@1x.png",
"scale" : "1x"
},
{
"size" : "29x29",
"idiom" : "ipad",
"filename" : "Icon-App-29x29@2x.png",
"scale" : "2x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@1x.png",
"scale" : "1x"
},
{
"size" : "40x40",
"idiom" : "ipad",
"filename" : "Icon-App-40x40@2x.png",
"scale" : "2x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@1x.png",
"scale" : "1x"
},
{
"size" : "76x76",
"idiom" : "ipad",
"filename" : "Icon-App-76x76@2x.png",
"scale" : "2x"
},
{
"size" : "83.5x83.5",
"idiom" : "ipad",
"filename" : "Icon-App-83.5x83.5@2x.png",
"scale" : "2x"
},
{
"size" : "1024x1024",
"idiom" : "ios-marketing",
"filename" : "Icon-App-1024x1024@1x.png",
"scale" : "1x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
{
"images" : [
{
"idiom" : "universal",
"filename" : "LaunchImage.png",
"scale" : "1x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@2x.png",
"scale" : "2x"
},
{
"idiom" : "universal",
"filename" : "LaunchImage@3x.png",
"scale" : "3x"
}
],
"info" : {
"version" : 1,
"author" : "xcode"
}
}
# Launch Screen Assets
You can customize the launch screen with your own desired assets by replacing the image files in this directory.
You can also do it by opening your Flutter project's Xcode project with `open ios/Runner.xcworkspace`, selecting `Runner/Assets.xcassets` in the Project Navigator and dropping in the desired images.
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="21701" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="21678"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="393" height="852"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="scaleAspectFit" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
<constraints>
<constraint firstAttribute="width" constant="150" id="w-constraint"/>
<constraint firstAttribute="height" constant="150" id="h-constraint"/>
</constraints>
</imageView>
</subviews>
<color key="backgroundColor" red="0.019607843137254902" green="0.031372549019607843" blue="0.062745098039215685" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<constraints>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="1a2-6s-vTC"/>
<constraint firstItem="YRO-k0-Ey4" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="4X2-HB-R7a"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>
<image name="LaunchImage" width="168" height="185"/>
</resources>
</document>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="10117" systemVersion="15F34" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
</dependencies>
<scenes>
<!--Flutter View Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="FlutterViewController" sceneMemberID="viewController">
<layoutGuides>
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
</layoutGuides>
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="calibratedWhite"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
</document>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CADisableMinimumFrameDurationOnPhone</key>
<true/>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>El3ab - العب</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundleName</key>
<string>El3ab</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$(FLUTTER_BUILD_NAME)</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>$(FLUTTER_BUILD_NUMBER)</string>
<key>LSRequiresIPhoneOS</key>
<true/>
<!-- Deep Links -->
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>CFBundleURLName</key>
<string>com.al-arcade.el3ab</string>
<key>CFBundleURLSchemes</key>
<array>
<string>el3ab</string>
</array>
</dict>
</array>
<!-- Universal Links -->
<key>FlutterDeepLinkingEnabled</key>
<true/>
<!-- Camera usage -->
<key>NSCameraUsageDescription</key>
<string>نحتاج للكاميرا لتحديث صورة بروفايلك</string>
<!-- Photo library -->
<key>NSPhotoLibraryUsageDescription</key>
<string>نحتاج للصور لاختيار صورة بروفايلك</string>
<!-- Contacts -->
<key>NSContactsUsageDescription</key>
<string>نحتاج لجهات الاتصال لمساعدتك بإيجاد أصدقائك على EL3AB</string>
<!-- Biometric -->
<key>NSFaceIDUsageDescription</key>
<string>استخدم Face ID لتسجيل دخول سريع</string>
<!-- Microphone (for potential future voice chat) -->
<key>NSMicrophoneUsageDescription</key>
<string>نحتاج المايكروفون للمحادثات الصوتية</string>
<!-- Background modes for push -->
<key>UIBackgroundModes</key>
<array>
<string>fetch</string>
<string>remote-notification</string>
</array>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneClassName</key>
<string>UIWindowScene</string>
<key>UISceneConfigurationName</key>
<string>flutter</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>UILaunchStoryboardName</key>
<string>LaunchScreen</string>
<key>UIMainStoryboardFile</key>
<string>Main</string>
<key>UIStatusBarStyle</key>
<string>UIStatusBarStyleLightContent</string>
<key>UIViewControllerBasedStatusBarAppearance</key>
<false/>
<key>UISupportedInterfaceOrientations</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
</array>
<key>UISupportedInterfaceOrientations~ipad</key>
<array>
<string>UIInterfaceOrientationPortrait</string>
<string>UIInterfaceOrientationPortraitUpsideDown</string>
</array>
</dict>
</plist>
#import "GeneratedPluginRegistrant.h"
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.associated-domains</key>
<array>
<string>applinks:el3ab-player.caprover.al-arcade.com</string>
</array>
</dict>
</plist>
import Flutter
import UIKit
class SceneDelegate: FlutterSceneDelegate {
}
import Flutter
import UIKit
import XCTest
class RunnerTests: XCTestCase {
func testExample() {
// If you add code to the Runner application, consider adding tests here.
// See https://developer.apple.com/documentation/xctest for more information about using XCTest.
}
}
import 'package:flutter/material.dart';
import 'config/theme.dart';
import 'core/services/auth_service.dart';
import 'core/services/connectivity_service.dart';
import 'core/services/deep_link_service.dart';
import 'core/services/push_service.dart';
import 'features/shell/app_shell.dart';
import 'features/splash/splash_screen.dart';
class El3abApp extends StatefulWidget {
final AuthService authService;
final PushService pushService;
final DeepLinkService deepLinkService;
final ConnectivityService connectivityService;
const El3abApp({
super.key,
required this.authService,
required this.pushService,
required this.deepLinkService,
required this.connectivityService,
});
@override
State<El3abApp> createState() => _El3abAppState();
}
class _El3abAppState extends State<El3abApp> {
bool _showSplash = true;
String? _biometricToken;
void _onSplashComplete() {
setState(() => _showSplash = false);
}
void _onBiometricSuccess() async {
final token = await widget.authService.getToken();
setState(() {
_biometricToken = token;
_showSplash = false;
});
}
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'EL3AB',
debugShowCheckedModeBanner: false,
theme: El3abTheme.dark,
locale: const Locale('ar'),
builder: (context, child) {
return Directionality(
textDirection: TextDirection.rtl,
child: child!,
);
},
home: _showSplash
? SplashScreen(
authService: widget.authService,
onComplete: _onSplashComplete,
onBiometricSuccess: _onBiometricSuccess,
)
: AppShell(
authService: widget.authService,
pushService: widget.pushService,
deepLinkService: widget.deepLinkService,
connectivityService: widget.connectivityService,
initialToken: _biometricToken,
),
);
}
}
class AppConstants {
AppConstants._();
static const String appName = 'EL3AB';
static const String packageName = 'com.al-arcade.el3ab';
// Supabase
static const String supabaseUrl = 'https://safe-supabase-kong.caprover.al-arcade.com';
static const String supabaseAnonKey =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYW5vbiIsImlzcyI6InN1cGFiYXNlIiwiaWF0IjoxNzM1Njg5NjAwLCJleHAiOjE4OTM0NTYwMDB9.31PF6PvP-pSrvRuQwLFptQoejR0W1A7o53lZhEbnz84';
// Web player URL
static const String webPlayerUrl = 'https://el3ab-player.caprover.al-arcade.com/';
static const String webPlayerHost = 'el3ab-player.caprover.al-arcade.com';
// Allowed domains for WebView navigation
static const List<String> allowedDomains = [
// App & backend
'el3ab-player.caprover.al-arcade.com',
'al-arcade.com',
'safe-supabase-kong.caprover.al-arcade.com',
'stockfishapi.caprover.al-arcade.com',
'swissapi.caprover.al-arcade.com',
// Google OAuth
'accounts.google.com',
'google.com',
'googleapis.com',
'gstatic.com',
'googleusercontent.com',
// Apple Sign-In (future)
'appleid.apple.com',
'apple.com',
// Facebook OAuth (future)
'facebook.com',
'fbcdn.net',
// CDN & assets
'cloudflare.com',
'cdn.jsdelivr.net',
'fonts.googleapis.com',
'fonts.gstatic.com',
// Payment providers (future)
'stripe.com',
'js.stripe.com',
];
// Deep link schemes
static const String deepLinkScheme = 'el3ab';
static const String universalLinkHost = 'el3ab-player.caprover.al-arcade.com';
// Storage keys
static const String authTokenKey = 'auth_token';
static const String refreshTokenKey = 'refresh_token';
static const String userIdKey = 'user_id';
static const String biometricEnabledKey = 'biometric_enabled';
static const String onboardingCompleteKey = 'onboarding_complete';
static const String pushTokenKey = 'push_token';
// Timeouts
static const Duration webViewTimeout = Duration(seconds: 30);
static const Duration backgroundGracePeriod = Duration(minutes: 5);
}
import 'package:flutter/material.dart';
class El3abColors {
El3abColors._();
// Backgrounds
static const Color bgDeep = Color(0xFF050810);
static const Color bgBase = Color(0xFF0A1020);
static const Color bgCard = Color(0xFF121A2E);
static const Color bgElevated = Color(0xFF1A2440);
// Brand
static const Color gold = Color(0xFFE4AC38);
static const Color goldSoft = Color(0xFFFFCC66);
static const Color cyan = Color(0xFF00FFFF);
static const Color blue = Color(0xFF2082F0);
// Game colors
static const Color chessPrimary = Color(0xFF2563EB);
static const Color ludoPrimary = Color(0xFF8B5CF6);
static const Color dominoPrimary = Color(0xFF10B981);
static const Color backgammonPrimary = Color(0xFFF59E0B);
// Text
static const Color textPrimary = Color(0xFFF8FAFC);
static const Color textSecondary = Color(0xFF94A3B8);
static const Color textMuted = Color(0xFF475569);
// Status
static const Color success = Color(0xFF34D399);
static const Color error = Color(0xFFEF4444);
static const Color warning = Color(0xFFF59E0B);
// Border
static const Color border = Color(0x0FFFFFFF);
}
class El3abTheme {
El3abTheme._();
static ThemeData get dark => ThemeData(
brightness: Brightness.dark,
scaffoldBackgroundColor: El3abColors.bgDeep,
colorScheme: const ColorScheme.dark(
primary: El3abColors.gold,
secondary: El3abColors.cyan,
surface: El3abColors.bgBase,
error: El3abColors.error,
),
fontFamily: 'IBM Plex Sans Arabic',
appBarTheme: const AppBarTheme(
backgroundColor: El3abColors.bgBase,
elevation: 0,
centerTitle: true,
),
navigationBarTheme: NavigationBarThemeData(
backgroundColor: El3abColors.bgBase,
indicatorColor: El3abColors.gold.withValues(alpha: 0.15),
labelTextStyle: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w600,
color: El3abColors.gold,
fontFamily: 'IBM Plex Sans Arabic',
);
}
return const TextStyle(
fontSize: 11,
fontWeight: FontWeight.w500,
color: El3abColors.textSecondary,
fontFamily: 'IBM Plex Sans Arabic',
);
}),
iconTheme: WidgetStateProperty.resolveWith((states) {
if (states.contains(WidgetState.selected)) {
return const IconThemeData(color: El3abColors.gold, size: 26);
}
return const IconThemeData(color: El3abColors.textSecondary, size: 24);
}),
),
);
}
enum BridgeCommand {
haptic,
share,
audio,
navSync,
authReady,
authLogout,
badge,
cacheProfile,
cacheMatch,
requestContacts,
requestCamera,
}
class BridgeMessage {
final BridgeCommand command;
final Map<String, dynamic> data;
BridgeMessage({required this.command, this.data = const {}});
factory BridgeMessage.fromJson(Map<String, dynamic> json) {
final cmd = _parseCommand(json['cmd'] as String? ?? '');
final data = Map<String, dynamic>.from(json)..remove('cmd');
return BridgeMessage(command: cmd, data: data);
}
static BridgeCommand _parseCommand(String cmd) {
switch (cmd) {
case 'haptic':
return BridgeCommand.haptic;
case 'share':
return BridgeCommand.share;
case 'audio':
return BridgeCommand.audio;
case 'nav_sync':
return BridgeCommand.navSync;
case 'auth_ready':
return BridgeCommand.authReady;
case 'auth_logout':
return BridgeCommand.authLogout;
case 'badge':
return BridgeCommand.badge;
case 'cache_profile':
return BridgeCommand.cacheProfile;
case 'cache_match':
return BridgeCommand.cacheMatch;
case 'request_contacts':
return BridgeCommand.requestContacts;
case 'request_camera':
return BridgeCommand.requestCamera;
default:
return BridgeCommand.haptic;
}
}
}
import 'dart:convert';
import 'package:flutter/foundation.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import 'bridge_commands.dart';
typedef BridgeHandler = void Function(BridgeMessage message);
class JsBridge {
InAppWebViewController? _controller;
final Map<BridgeCommand, List<BridgeHandler>> _handlers = {};
void attach(InAppWebViewController controller) {
_controller = controller;
}
void detach() {
_controller = null;
}
void on(BridgeCommand command, BridgeHandler handler) {
_handlers.putIfAbsent(command, () => []).add(handler);
}
void off(BridgeCommand command, BridgeHandler handler) {
_handlers[command]?.remove(handler);
}
void handleMessage(String rawMessage) {
try {
final json = jsonDecode(rawMessage) as Map<String, dynamic>;
final message = BridgeMessage.fromJson(json);
final handlers = _handlers[message.command];
if (handlers != null) {
for (final handler in handlers) {
handler(message);
}
}
} catch (e) {
debugPrint('[JsBridge] Error parsing message: $e');
}
}
Future<void> sendToWeb(String method, Map<String, dynamic> data) async {
if (_controller == null) return;
final jsonData = jsonEncode(data);
final js = 'if(window.el3ab_native&&window.el3ab_native.$method){window.el3ab_native.$method($jsonData)}';
try {
await _controller!.evaluateJavascript(source: js);
} catch (e) {
debugPrint('[JsBridge] Error sending to web: $e');
}
}
Future<void> onDeepLink(String route, String id) async {
await sendToWeb('onDeepLink', {'route': route, 'id': id});
}
Future<void> onPushTap(Map<String, dynamic> payload) async {
await sendToWeb('onPushTap', payload);
}
Future<void> onConnectivityChanged(bool online) async {
await sendToWeb('onConnectivityChanged', {'online': online});
}
Future<void> onTokenRefreshed(String token) async {
await sendToWeb('onTokenRefreshed', {'token': token});
}
Future<void> onNavTap(String world) async {
await sendToWeb('onNavTap', {'world': world});
}
}
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:local_auth/local_auth.dart';
import 'package:shared_preferences/shared_preferences.dart';
import '../../config/constants.dart';
class AuthService {
final FlutterSecureStorage _secureStorage = const FlutterSecureStorage();
final LocalAuthentication _localAuth = LocalAuthentication();
Future<bool> get isBiometricAvailable async {
try {
final canCheck = await _localAuth.canCheckBiometrics;
final isSupported = await _localAuth.isDeviceSupported();
return canCheck && isSupported;
} catch (_) {
return false;
}
}
Future<bool> get isBiometricEnabled async {
final prefs = await SharedPreferences.getInstance();
return prefs.getBool(AppConstants.biometricEnabledKey) ?? false;
}
Future<void> setBiometricEnabled(bool enabled) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setBool(AppConstants.biometricEnabledKey, enabled);
}
Future<bool> authenticateWithBiometric() async {
try {
return await _localAuth.authenticate(
localizedReason: 'سجّل دخولك باستخدام البصمة',
options: const AuthenticationOptions(
stickyAuth: true,
biometricOnly: true,
),
);
} catch (_) {
return false;
}
}
Future<void> saveToken(String token) async {
await _secureStorage.write(key: AppConstants.authTokenKey, value: token);
}
Future<void> saveRefreshToken(String token) async {
await _secureStorage.write(key: AppConstants.refreshTokenKey, value: token);
}
Future<void> saveUserId(String userId) async {
await _secureStorage.write(key: AppConstants.userIdKey, value: userId);
}
Future<String?> getToken() async {
return _secureStorage.read(key: AppConstants.authTokenKey);
}
Future<String?> getRefreshToken() async {
return _secureStorage.read(key: AppConstants.refreshTokenKey);
}
Future<String?> getUserId() async {
return _secureStorage.read(key: AppConstants.userIdKey);
}
Future<bool> hasStoredCredentials() async {
final token = await getToken();
return token != null && token.isNotEmpty;
}
Future<void> clearCredentials() async {
await _secureStorage.delete(key: AppConstants.authTokenKey);
await _secureStorage.delete(key: AppConstants.refreshTokenKey);
await _secureStorage.delete(key: AppConstants.userIdKey);
await setBiometricEnabled(false);
}
}
import 'dart:async';
import 'package:connectivity_plus/connectivity_plus.dart';
class ConnectivityService {
final Connectivity _connectivity = Connectivity();
final StreamController<bool> _controller = StreamController<bool>.broadcast();
bool _isOnline = true;
Stream<bool> get onConnectivityChanged => _controller.stream;
bool get isOnline => _isOnline;
Future<void> init() async {
final results = await _connectivity.checkConnectivity();
_isOnline = !results.contains(ConnectivityResult.none);
_connectivity.onConnectivityChanged.listen((results) {
final online = !results.contains(ConnectivityResult.none);
if (online != _isOnline) {
_isOnline = online;
_controller.add(online);
}
});
}
void dispose() {
_controller.close();
}
}
import 'dart:async';
import 'package:app_links/app_links.dart';
import 'package:flutter/foundation.dart';
class DeepLinkData {
final String route;
final String? id;
DeepLinkData({required this.route, this.id});
}
class DeepLinkService {
final AppLinks _appLinks = AppLinks();
final StreamController<DeepLinkData> _linkController =
StreamController<DeepLinkData>.broadcast();
Stream<DeepLinkData> get links => _linkController.stream;
Future<void> init() async {
// Handle initial link (app opened via link)
try {
final initialUri = await _appLinks.getInitialLink();
if (initialUri != null) {
_handleUri(initialUri);
}
} catch (e) {
debugPrint('[DeepLink] No initial link: $e');
}
// Handle subsequent links (app already running)
_appLinks.uriLinkStream.listen(
_handleUri,
onError: (e) => debugPrint('[DeepLink] Stream error: $e'),
);
}
void _handleUri(Uri uri) {
// Handle el3ab://match/abc123 or https://el3ab-player.../match/abc123
final segments = uri.pathSegments;
if (segments.isEmpty) return;
final route = segments.first;
final id = segments.length > 1 ? segments[1] : null;
_linkController.add(DeepLinkData(route: route, id: id));
}
void dispose() {
_linkController.close();
}
}
import 'package:flutter/services.dart';
class HapticsService {
void trigger(String type) {
switch (type) {
case 'light':
HapticFeedback.lightImpact();
break;
case 'medium':
HapticFeedback.mediumImpact();
break;
case 'heavy':
HapticFeedback.heavyImpact();
break;
case 'success':
HapticFeedback.mediumImpact();
Future.delayed(const Duration(milliseconds: 100), () {
HapticFeedback.lightImpact();
});
break;
case 'error':
HapticFeedback.heavyImpact();
Future.delayed(const Duration(milliseconds: 80), () {
HapticFeedback.heavyImpact();
});
break;
case 'selection':
HapticFeedback.selectionClick();
break;
case 'dice':
_dicePattern();
break;
case 'coin':
_coinPattern();
break;
default:
HapticFeedback.lightImpact();
}
}
void _dicePattern() async {
for (int i = 0; i < 3; i++) {
HapticFeedback.selectionClick();
await Future.delayed(const Duration(milliseconds: 60));
}
}
void _coinPattern() async {
for (int i = 0; i < 3; i++) {
HapticFeedback.lightImpact();
await Future.delayed(const Duration(milliseconds: 40));
}
}
}
import 'dart:convert';
import 'dart:io';
import 'package:flutter/foundation.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
class PushService {
final FlutterLocalNotificationsPlugin _localNotifications =
FlutterLocalNotificationsPlugin();
RealtimeChannel? _notificationChannel;
Function(Map<String, dynamic>)? onNotificationTap;
Future<void> init() async {
const androidSettings =
AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings(
requestAlertPermission: true,
requestBadgePermission: true,
requestSoundPermission: true,
);
await _localNotifications.initialize(
const InitializationSettings(
android: androidSettings,
iOS: iosSettings,
),
onDidReceiveNotificationResponse: _onNotificationTap,
);
if (Platform.isAndroid) {
await _localNotifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(const AndroidNotificationChannel(
'el3ab_game',
'Game Notifications',
description: 'Match invites, turn alerts, tournament updates',
importance: Importance.high,
));
}
}
void subscribeToNotifications(String userId) {
final supabase = Supabase.instance.client;
_notificationChannel?.unsubscribe();
_notificationChannel = supabase
.channel('notifications:$userId')
.onPostgresChanges(
event: PostgresChangeEvent.insert,
schema: 'public',
table: 'notifications',
filter: PostgresChangeFilter(
type: PostgresChangeFilterType.eq,
column: 'user_id',
value: userId,
),
callback: (payload) {
final record = payload.newRecord;
_showLocalNotification(record);
},
)
.subscribe();
}
void unsubscribe() {
_notificationChannel?.unsubscribe();
_notificationChannel = null;
}
Future<void> registerToken(String token, String platform) async {
final supabase = Supabase.instance.client;
final userId = supabase.auth.currentUser?.id;
if (userId == null) return;
await supabase.from('push_tokens').upsert({
'user_id': userId,
'token': token,
'platform': platform,
'updated_at': DateTime.now().toIso8601String(),
}, onConflict: 'user_id,platform');
}
void _showLocalNotification(Map<String, dynamic> record) {
final title = record['title_ar'] ?? record['title'] ?? 'EL3AB';
final body = record['body_ar'] ?? record['body'] ?? '';
final data = record['data'] as Map<String, dynamic>? ?? {};
_localNotifications.show(
DateTime.now().millisecondsSinceEpoch ~/ 1000,
title,
body,
NotificationDetails(
android: const AndroidNotificationDetails(
'el3ab_game',
'Game Notifications',
importance: Importance.high,
priority: Priority.high,
icon: '@mipmap/ic_launcher',
),
iOS: const DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
),
),
payload: jsonEncode(data),
);
}
void _onNotificationTap(NotificationResponse response) {
if (response.payload == null) return;
try {
final data = jsonDecode(response.payload!) as Map<String, dynamic>;
onNotificationTap?.call(data);
} catch (e) {
debugPrint('[Push] Error parsing notification payload: $e');
}
}
}
import 'package:share_plus/share_plus.dart';
class ShareService {
Future<void> share({
required String text,
String? subject,
}) async {
await Share.share(text, subject: subject);
}
Future<void> shareMatchInvite(String matchId, String gameType) async {
final url = 'https://el3ab-player.caprover.al-arcade.com/match/$matchId';
await share(
text: 'تعال نلعب $gameType على EL3AB! 🎮\n$url',
subject: 'دعوة لعب على EL3AB',
);
}
Future<void> shareProfile(String userId, String playerName) async {
final url = 'https://el3ab-player.caprover.al-arcade.com/profile/$userId';
await share(
text: 'شوف بروفايل $playerName على EL3AB 👑\n$url',
subject: 'بروفايل $playerName',
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import '../../config/theme.dart';
import '../../core/bridge/bridge_commands.dart';
import '../../core/bridge/js_bridge.dart';
import '../../core/services/auth_service.dart';
import '../../core/services/connectivity_service.dart';
import '../../core/services/deep_link_service.dart';
import '../../core/services/haptics_service.dart';
import '../../core/services/push_service.dart';
import '../../core/services/share_service.dart';
import 'webview_container.dart';
class AppShell extends StatefulWidget {
final AuthService authService;
final PushService pushService;
final DeepLinkService deepLinkService;
final ConnectivityService connectivityService;
final String? initialToken;
const AppShell({
super.key,
required this.authService,
required this.pushService,
required this.deepLinkService,
required this.connectivityService,
this.initialToken,
});
@override
State<AppShell> createState() => _AppShellState();
}
class _AppShellState extends State<AppShell> with WidgetsBindingObserver {
final JsBridge _bridge = JsBridge();
final HapticsService _haptics = HapticsService();
final ShareService _share = ShareService();
bool _isGameMode = false;
bool _isOffline = false;
InAppWebViewController? _webController;
DateTime? _backgroundedAt;
@override
void initState() {
super.initState();
WidgetsBinding.instance.addObserver(this);
_setupBridge();
_setupPushHandler();
_setupDeepLinks();
_setupConnectivity();
}
void _setupBridge() {
_bridge.on(BridgeCommand.haptic, (msg) {
_haptics.trigger(msg.data['type'] as String? ?? 'light');
});
_bridge.on(BridgeCommand.share, (msg) {
_share.share(
text: msg.data['text'] as String? ?? '',
subject: msg.data['title'] as String?,
);
});
_bridge.on(BridgeCommand.navSync, (msg) {
final gameMode = msg.data['gameMode'] as bool? ?? false;
if (gameMode != _isGameMode) {
setState(() => _isGameMode = gameMode);
SystemChrome.setEnabledSystemUIMode(
gameMode ? SystemUiMode.immersiveSticky : SystemUiMode.edgeToEdge,
);
}
});
_bridge.on(BridgeCommand.authReady, (msg) async {
final token = msg.data['token'] as String?;
final userId = msg.data['userId'] as String?;
if (token != null) {
await widget.authService.saveToken(token);
if (userId != null) {
await widget.authService.saveUserId(userId);
}
// Register push token with backend
_registerPushToken(userId);
// Offer biometric setup if available and not already enabled
_offerBiometric();
}
});
_bridge.on(BridgeCommand.authLogout, (msg) async {
await widget.authService.clearCredentials();
});
_bridge.on(BridgeCommand.badge, (msg) {});
}
void _setupPushHandler() {
widget.pushService.onNotificationTap = (data) {
_bridge.onPushTap(data);
};
}
void _setupDeepLinks() {
widget.deepLinkService.links.listen((link) {
_bridge.onDeepLink(link.route, link.id ?? '');
});
}
void _setupConnectivity() {
_isOffline = !widget.connectivityService.isOnline;
widget.connectivityService.onConnectivityChanged.listen((online) {
setState(() => _isOffline = !online);
_bridge.onConnectivityChanged(online);
});
}
Future<void> _registerPushToken(String? userId) async {
if (userId == null) return;
widget.pushService.subscribeToNotifications(userId);
}
Future<void> _offerBiometric() async {
final available = await widget.authService.isBiometricAvailable;
final alreadyEnabled = await widget.authService.isBiometricEnabled;
if (available && !alreadyEnabled && mounted) {
// Show biometric opt-in after a short delay
Future.delayed(const Duration(seconds: 3), () {
if (!mounted) return;
_showBiometricDialog();
});
}
}
void _showBiometricDialog() {
showDialog(
context: context,
builder: (ctx) => AlertDialog(
backgroundColor: El3abColors.bgCard,
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
title: const Text(
'تسجيل دخول سريع',
style: TextStyle(color: El3abColors.textPrimary),
textAlign: TextAlign.center,
),
content: const Text(
'فعّل البصمة لتسجيل الدخول بسرعة في المرة الجاية',
style: TextStyle(color: El3abColors.textSecondary),
textAlign: TextAlign.center,
),
actions: [
TextButton(
onPressed: () => Navigator.pop(ctx),
child: const Text('لاحقاً', style: TextStyle(color: El3abColors.textSecondary)),
),
FilledButton(
onPressed: () async {
await widget.authService.setBiometricEnabled(true);
if (ctx.mounted) Navigator.pop(ctx);
},
style: FilledButton.styleFrom(
backgroundColor: El3abColors.gold,
foregroundColor: Colors.black,
),
child: const Text('فعّل'),
),
],
),
);
}
@override
void didChangeAppLifecycleState(AppLifecycleState state) {
if (state == AppLifecycleState.paused) {
_backgroundedAt = DateTime.now();
} else if (state == AppLifecycleState.resumed) {
if (_backgroundedAt != null) {
final elapsed = DateTime.now().difference(_backgroundedAt!);
if (elapsed > const Duration(minutes: 5)) {
// Reload WebView after long background
_webController?.reload();
}
}
_backgroundedAt = null;
}
}
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
_bridge.detach();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: El3abColors.bgDeep,
body: SafeArea(
bottom: false,
child: WebViewContainer(
bridge: _bridge,
initialToken: widget.initialToken,
isOffline: _isOffline,
onControllerReady: (controller) {
_webController = controller;
},
),
),
);
}
}
import 'package:flutter/material.dart';
import '../../config/theme.dart';
class El3abBottomNav extends StatelessWidget {
final int currentIndex;
final ValueChanged<int> onTap;
final int badgeCount;
final bool isGameMode;
const El3abBottomNav({
super.key,
required this.currentIndex,
required this.onTap,
this.badgeCount = 0,
this.isGameMode = false,
});
static const List<_NavItem> _items = [
_NavItem(icon: Icons.emoji_events_rounded, label: 'الترتيب', world: 'rank'),
_NavItem(icon: Icons.people_rounded, label: 'اجتماعي', world: 'social'),
_NavItem(icon: Icons.sports_esports_rounded, label: 'العب', world: 'play'),
_NavItem(icon: Icons.military_tech_rounded, label: 'البطولات', world: 'tournaments'),
_NavItem(icon: Icons.person_rounded, label: 'حسابي', world: 'profile'),
];
static String worldForIndex(int index) => _items[index].world;
static int indexForWorld(String world) {
final idx = _items.indexWhere((item) => item.world == world);
return idx >= 0 ? idx : 2;
}
@override
Widget build(BuildContext context) {
if (isGameMode) return const SizedBox.shrink();
return Container(
decoration: const BoxDecoration(
color: El3abColors.bgBase,
border: Border(
top: BorderSide(color: El3abColors.border, width: 1),
),
),
child: SafeArea(
top: false,
child: SizedBox(
height: 60,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: List.generate(_items.length, (index) {
final item = _items[index];
final isActive = index == currentIndex;
final isCenter = index == 2;
return Expanded(
child: GestureDetector(
behavior: HitTestBehavior.opaque,
onTap: () => onTap(index),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Stack(
clipBehavior: Clip.none,
children: [
AnimatedContainer(
duration: const Duration(milliseconds: 200),
padding: const EdgeInsets.all(6),
decoration: isActive
? BoxDecoration(
borderRadius: BorderRadius.circular(12),
color: El3abColors.gold.withValues(alpha: 0.12),
)
: null,
child: Icon(
item.icon,
size: isCenter ? 28 : 24,
color: isActive
? El3abColors.gold
: El3abColors.textSecondary,
),
),
if (index == 1 && badgeCount > 0)
Positioned(
right: -2,
top: -2,
child: Container(
padding: const EdgeInsets.all(4),
decoration: const BoxDecoration(
color: El3abColors.error,
shape: BoxShape.circle,
),
constraints: const BoxConstraints(
minWidth: 16,
minHeight: 16,
),
child: Text(
badgeCount > 9 ? '9+' : '$badgeCount',
style: const TextStyle(
fontSize: 9,
fontWeight: FontWeight.w700,
color: Colors.white,
),
textAlign: TextAlign.center,
),
),
),
],
),
const SizedBox(height: 2),
Text(
item.label,
style: TextStyle(
fontSize: 10,
fontWeight: isActive ? FontWeight.w600 : FontWeight.w500,
color: isActive
? El3abColors.gold
: El3abColors.textSecondary,
),
),
],
),
),
);
}),
),
),
),
);
}
}
class _NavItem {
final IconData icon;
final String label;
final String world;
const _NavItem({
required this.icon,
required this.label,
required this.world,
});
}
import 'package:flutter/material.dart';
import '../../config/theme.dart';
class OfflineScreen extends StatefulWidget {
final VoidCallback onRetry;
const OfflineScreen({super.key, required this.onRetry});
@override
State<OfflineScreen> createState() => _OfflineScreenState();
}
class _OfflineScreenState extends State<OfflineScreen>
with SingleTickerProviderStateMixin {
late AnimationController _pulseController;
late Animation<double> _pulseAnimation;
bool _retrying = false;
@override
void initState() {
super.initState();
_pulseController = AnimationController(
duration: const Duration(milliseconds: 2000),
vsync: this,
)..repeat(reverse: true);
_pulseAnimation = Tween<double>(begin: 0.6, end: 1.0).animate(
CurvedAnimation(parent: _pulseController, curve: Curves.easeInOut),
);
}
@override
void dispose() {
_pulseController.dispose();
super.dispose();
}
void _handleRetry() {
setState(() => _retrying = true);
widget.onRetry();
Future.delayed(const Duration(seconds: 3), () {
if (mounted) setState(() => _retrying = false);
});
}
@override
Widget build(BuildContext context) {
return Container(
color: El3abColors.bgDeep,
child: SafeArea(
child: Center(
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 32),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Animated wifi-off icon
AnimatedBuilder(
animation: _pulseAnimation,
builder: (context, child) {
return Opacity(
opacity: _pulseAnimation.value,
child: Container(
width: 100,
height: 100,
decoration: BoxDecoration(
shape: BoxShape.circle,
color: El3abColors.bgCard,
border: Border.all(
color: El3abColors.gold.withValues(alpha: 0.3),
width: 2,
),
),
child: const Icon(
Icons.wifi_off_rounded,
size: 48,
color: El3abColors.gold,
),
),
);
},
),
const SizedBox(height: 32),
const Text(
'لا يوجد اتصال',
style: TextStyle(
fontSize: 22,
fontWeight: FontWeight.w700,
color: El3abColors.textPrimary,
),
),
const SizedBox(height: 12),
const Text(
'تأكد من اتصالك بالإنترنت وحاول مرة ثانية',
textAlign: TextAlign.center,
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w400,
color: El3abColors.textSecondary,
height: 1.5,
),
),
const SizedBox(height: 40),
// Retry button
SizedBox(
width: double.infinity,
height: 52,
child: FilledButton(
onPressed: _retrying ? null : _handleRetry,
style: FilledButton.styleFrom(
backgroundColor: El3abColors.gold,
foregroundColor: Colors.black,
disabledBackgroundColor: El3abColors.bgElevated,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(14),
),
),
child: _retrying
? const SizedBox(
width: 22,
height: 22,
child: CircularProgressIndicator(
strokeWidth: 2.5,
valueColor: AlwaysStoppedAnimation<Color>(
El3abColors.textSecondary),
),
)
: const Text(
'إعادة المحاولة',
style: TextStyle(
fontSize: 16,
fontWeight: FontWeight.w700,
),
),
),
),
],
),
),
),
),
);
}
}
import 'dart:collection';
import 'package:flutter/material.dart';
import 'package:flutter_inappwebview/flutter_inappwebview.dart';
import '../../config/constants.dart';
import '../../config/theme.dart';
import '../../core/bridge/js_bridge.dart';
import 'offline_screen.dart';
class WebViewContainer extends StatefulWidget {
final JsBridge bridge;
final String? initialToken;
final Function(InAppWebViewController) onControllerReady;
final VoidCallback? onPageLoaded;
final bool isOffline;
const WebViewContainer({
super.key,
required this.bridge,
required this.onControllerReady,
this.initialToken,
this.onPageLoaded,
this.isOffline = false,
});
@override
State<WebViewContainer> createState() => WebViewContainerState();
}
class WebViewContainerState extends State<WebViewContainer> {
InAppWebViewController? _controller;
bool _isLoading = true;
bool _hasLoadError = false;
double _progress = 0;
bool _pageLoadedOnce = false;
@override
void didUpdateWidget(WebViewContainer oldWidget) {
super.didUpdateWidget(oldWidget);
// When connectivity comes back, auto-retry if we had an error
if (oldWidget.isOffline && !widget.isOffline && _hasLoadError) {
_retry();
}
}
void _retry() {
setState(() {
_hasLoadError = false;
_isLoading = true;
});
if (_pageLoadedOnce) {
_controller?.reload();
} else {
_controller?.loadUrl(
urlRequest: URLRequest(url: WebUri(AppConstants.webPlayerUrl)),
);
}
}
bool get _shouldShowOffline => _hasLoadError || (widget.isOffline && !_pageLoadedOnce);
@override
Widget build(BuildContext context) {
return Stack(
children: [
// WebView — always mounted so it keeps state, but hidden behind offline screen
Opacity(
opacity: _shouldShowOffline ? 0 : 1,
child: IgnorePointer(
ignoring: _shouldShowOffline,
child: InAppWebView(
initialUrlRequest: URLRequest(
url: WebUri(AppConstants.webPlayerUrl),
),
initialUserScripts: UnmodifiableListView([
UserScript(
source: '''
window.IS_NATIVE_APP = true;
document.documentElement.classList.add('native-app');
''',
injectionTime: UserScriptInjectionTime.AT_DOCUMENT_START,
),
]),
initialSettings: InAppWebViewSettings(
javaScriptEnabled: true,
domStorageEnabled: true,
databaseEnabled: true,
mediaPlaybackRequiresUserGesture: false,
allowsInlineMediaPlayback: true,
transparentBackground: true,
userAgent:
'El3abApp/1.0 (Flutter; ${Theme.of(context).platform == TargetPlatform.iOS ? "iOS" : "Android"})',
allowFileAccessFromFileURLs: false,
allowUniversalAccessFromFileURLs: false,
useShouldOverrideUrlLoading: true,
supportZoom: false,
disableHorizontalScroll: false,
disableVerticalScroll: false,
javaScriptCanOpenWindowsAutomatically: true,
supportMultipleWindows: true,
),
onWebViewCreated: (controller) {
_controller = controller;
widget.bridge.attach(controller);
widget.onControllerReady(controller);
controller.addJavaScriptHandler(
handlerName: 'flutter_bridge',
callback: (args) {
if (args.isNotEmpty) {
widget.bridge.handleMessage(args[0].toString());
}
return null;
},
);
},
onLoadStart: (controller, url) {
setState(() {
_isLoading = true;
_hasLoadError = false;
});
},
onLoadStop: (controller, url) async {
setState(() {
_isLoading = false;
_pageLoadedOnce = true;
});
await _injectBridgeScript();
if (widget.initialToken != null) {
await _injectToken(widget.initialToken!);
}
widget.onPageLoaded?.call();
},
onProgressChanged: (controller, progress) {
setState(() => _progress = progress / 100.0);
},
onReceivedError: (controller, request, error) {
// Catch all WebView errors (no internet, DNS failure, timeout, etc.)
final isMainFrame =
request.url.toString() == AppConstants.webPlayerUrl ||
request.isForMainFrame == true;
if (isMainFrame) {
setState(() {
_hasLoadError = true;
_isLoading = false;
});
}
},
onReceivedHttpError: (controller, request, response) {
// Catch server errors (500, 502, 503, etc.)
if (response.statusCode != null && response.statusCode! >= 500) {
final isMainFrame =
request.url.toString() == AppConstants.webPlayerUrl ||
request.isForMainFrame == true;
if (isMainFrame) {
setState(() {
_hasLoadError = true;
_isLoading = false;
});
}
}
},
onCreateWindow: (controller, createWindowAction) async {
// Handle OAuth popups (Google Sign-In etc.) — load in same WebView
final url = createWindowAction.request.url;
if (url != null) {
controller.loadUrl(urlRequest: URLRequest(url: url));
}
return true;
},
shouldOverrideUrlLoading: (controller, navigationAction) async {
final uri = navigationAction.request.url;
if (uri == null) return NavigationActionPolicy.CANCEL;
final host = uri.host;
final isAllowed = AppConstants.allowedDomains.any(
(domain) => host == domain || host.endsWith('.$domain'),
);
if (isAllowed) {
return NavigationActionPolicy.ALLOW;
}
return NavigationActionPolicy.CANCEL;
},
onConsoleMessage: (controller, consoleMessage) {
debugPrint('[WebView] ${consoleMessage.message}');
},
),
),
),
// Native offline screen — covers WebView when there's an error
if (_shouldShowOffline)
Positioned.fill(
child: OfflineScreen(onRetry: _retry),
),
// Loading bar (only shows when actively loading, NOT when offline)
if (_isLoading && !_shouldShowOffline)
Positioned(
top: 0,
left: 0,
right: 0,
child: LinearProgressIndicator(
value: _progress > 0 ? _progress : null,
backgroundColor: Colors.transparent,
valueColor: const AlwaysStoppedAnimation<Color>(El3abColors.gold),
minHeight: 2,
),
),
],
);
}
Future<void> _injectBridgeScript() async {
if (_controller == null) return;
const bridgeScript = '''
(function() {
if (window._el3ab_bridge_injected) return;
window._el3ab_bridge_injected = true;
window.flutter_bridge = {
postMessage: function(message) {
window.flutter_inappwebview.callHandler('flutter_bridge', message);
}
};
window.el3ab_native = window.el3ab_native || {};
window.IS_NATIVE_APP = true;
document.documentElement.classList.add('native-app');
console.log('[El3ab Native] Bridge injected');
})();
''';
await _controller!.evaluateJavascript(source: bridgeScript);
}
Future<void> _injectToken(String token) async {
if (_controller == null) return;
await _controller!.evaluateJavascript(
source: '''
(function() {
var state = JSON.parse(localStorage.getItem('el3ab_state') || '{}');
if (state.auth) {
state.auth.token = '$token';
}
localStorage.setItem('el3ab_state', JSON.stringify(state));
if (window.el3ab_native && window.el3ab_native.onTokenRefreshed) {
window.el3ab_native.onTokenRefreshed({token: '$token'});
}
})();
''',
);
}
Future<void> reload() async {
_retry();
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import '../../config/theme.dart';
import '../../core/services/auth_service.dart';
class SplashScreen extends StatefulWidget {
final AuthService authService;
final VoidCallback onComplete;
final VoidCallback onBiometricSuccess;
const SplashScreen({
super.key,
required this.authService,
required this.onComplete,
required this.onBiometricSuccess,
});
@override
State<SplashScreen> createState() => _SplashScreenState();
}
class _SplashScreenState extends State<SplashScreen>
with SingleTickerProviderStateMixin {
late AnimationController _controller;
late Animation<double> _scaleAnimation;
late Animation<double> _opacityAnimation;
late Animation<double> _glowAnimation;
@override
void initState() {
super.initState();
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
));
_controller = AnimationController(
duration: const Duration(milliseconds: 1800),
vsync: this,
);
_scaleAnimation = Tween<double>(begin: 0.6, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.6, curve: Curves.elasticOut),
),
);
_opacityAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.0, 0.4, curve: Curves.easeOut),
),
);
_glowAnimation = Tween<double>(begin: 0.0, end: 1.0).animate(
CurvedAnimation(
parent: _controller,
curve: const Interval(0.4, 1.0, curve: Curves.easeInOut),
),
);
_controller.forward();
_initializeApp();
}
Future<void> _initializeApp() async {
await Future.delayed(const Duration(milliseconds: 1500));
final hasCredentials = await widget.authService.hasStoredCredentials();
final biometricEnabled = await widget.authService.isBiometricEnabled;
if (hasCredentials && biometricEnabled) {
final authenticated = await widget.authService.authenticateWithBiometric();
if (authenticated) {
widget.onBiometricSuccess();
return;
}
}
widget.onComplete();
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
backgroundColor: El3abColors.bgDeep,
body: AnimatedBuilder(
animation: _controller,
builder: (context, child) {
return Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
// Logo with glow effect
Transform.scale(
scale: _scaleAnimation.value,
child: Opacity(
opacity: _opacityAnimation.value,
child: Container(
width: 120,
height: 120,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(24),
boxShadow: [
BoxShadow(
color: El3abColors.gold
.withValues(alpha: 0.4 * _glowAnimation.value),
blurRadius: 40 * _glowAnimation.value,
spreadRadius: 5 * _glowAnimation.value,
),
],
),
child: ClipRRect(
borderRadius: BorderRadius.circular(24),
child: Image.asset(
'assets/images/logo.png',
fit: BoxFit.cover,
),
),
),
),
),
const SizedBox(height: 24),
// Brand name
Opacity(
opacity: _opacityAnimation.value,
child: const Text(
'EL3AB',
style: TextStyle(
fontSize: 32,
fontWeight: FontWeight.w800,
color: El3abColors.gold,
letterSpacing: 4,
),
),
),
const SizedBox(height: 8),
Opacity(
opacity: _glowAnimation.value,
child: const Text(
'العب • نافس • فوز',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: El3abColors.textSecondary,
),
),
),
const SizedBox(height: 48),
// Loading indicator
Opacity(
opacity: _glowAnimation.value,
child: SizedBox(
width: 24,
height: 24,
child: CircularProgressIndicator(
strokeWidth: 2,
valueColor: AlwaysStoppedAnimation<Color>(
El3abColors.gold.withValues(alpha: 0.6),
),
),
),
),
],
),
);
},
),
);
}
}
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'app.dart';
import 'config/constants.dart';
import 'core/services/auth_service.dart';
import 'core/services/connectivity_service.dart';
import 'core/services/deep_link_service.dart';
import 'core/services/push_service.dart';
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await SystemChrome.setPreferredOrientations([
DeviceOrientation.portraitUp,
DeviceOrientation.portraitDown,
]);
SystemChrome.setSystemUIOverlayStyle(const SystemUiOverlayStyle(
statusBarColor: Colors.transparent,
statusBarIconBrightness: Brightness.light,
systemNavigationBarColor: Color(0xFF0A1020),
systemNavigationBarIconBrightness: Brightness.light,
));
await Supabase.initialize(
url: AppConstants.supabaseUrl,
publishableKey: AppConstants.supabaseAnonKey,
);
final authService = AuthService();
final pushService = PushService();
final deepLinkService = DeepLinkService();
final connectivityService = ConnectivityService();
await Future.wait([
pushService.init(),
deepLinkService.init(),
connectivityService.init(),
]);
runApp(El3abApp(
authService: authService,
pushService: pushService,
deepLinkService: deepLinkService,
connectivityService: connectivityService,
));
}
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
app_links:
dependency: "direct main"
description:
name: app_links
sha256: "5f88447519add627fe1cbcab4fd1da3d4fed15b9baf29f28b22535c95ecee3e8"
url: "https://pub.dev"
source: hosted
version: "6.4.1"
app_links_linux:
dependency: transitive
description:
name: app_links_linux
sha256: f5f7173a78609f3dfd4c2ff2c95bd559ab43c80a87dc6a095921d96c05688c81
url: "https://pub.dev"
source: hosted
version: "1.0.3"
app_links_platform_interface:
dependency: transitive
description:
name: app_links_platform_interface
sha256: "78a18580eecac98108d1eef52a7db668bc317714f5205e616973363326efe333"
url: "https://pub.dev"
source: hosted
version: "2.0.3"
app_links_web:
dependency: transitive
description:
name: app_links_web
sha256: af060ed76183f9e2b87510a9480e56a5352b6c249778d07bd2c95fc35632a555
url: "https://pub.dev"
source: hosted
version: "1.0.4"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
name: async
sha256: e2eb0491ba5ddb6177742d2da23904574082139b07c1e33b8503b9f46f3e1a37
url: "https://pub.dev"
source: hosted
version: "2.13.1"
boolean_selector:
dependency: transitive
description:
name: boolean_selector
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
cached_network_image:
dependency: "direct main"
description:
name: cached_network_image
sha256: "7c1183e361e5c8b0a0f21a28401eecdbde252441106a9816400dd4c2b2424916"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
cached_network_image_platform_interface:
dependency: transitive
description:
name: cached_network_image_platform_interface
sha256: "35814b016e37fbdc91f7ae18c8caf49ba5c88501813f73ce8a07027a395e2829"
url: "https://pub.dev"
source: hosted
version: "4.1.1"
cached_network_image_web:
dependency: transitive
description:
name: cached_network_image_web
sha256: "980842f4e8e2535b8dbd3d5ca0b1f0ba66bf61d14cc3a17a9b4788a3685ba062"
url: "https://pub.dev"
source: hosted
version: "1.3.1"
characters:
dependency: transitive
description:
name: characters
sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
version: "1.4.1"
clock:
dependency: transitive
description:
name: clock
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
url: "https://pub.dev"
source: hosted
version: "1.1.2"
code_assets:
dependency: transitive
description:
name: code_assets
sha256: bf394f466ba9205f1812a0433b392d6af280f155f56651eda7c18cc32ed493b8
url: "https://pub.dev"
source: hosted
version: "1.2.1"
collection:
dependency: transitive
description:
name: collection
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
url: "https://pub.dev"
source: hosted
version: "1.19.1"
connectivity_plus:
dependency: "direct main"
description:
name: connectivity_plus
sha256: b5e72753cf63becce2c61fd04dfe0f1c430cc5278b53a1342dc5ad839eab29ec
url: "https://pub.dev"
source: hosted
version: "6.1.5"
connectivity_plus_platform_interface:
dependency: transitive
description:
name: connectivity_plus_platform_interface
sha256: "3c09627c536d22fd24691a905cdd8b14520de69da52c7a97499c8be5284a32ed"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "28bb3ae56f117b5aec029d702a90f57d285cd975c3c5c281eaca38dbc47c5937"
url: "https://pub.dev"
source: hosted
version: "0.3.5+2"
crypto:
dependency: transitive
description:
name: crypto
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
url: "https://pub.dev"
source: hosted
version: "3.0.7"
cupertino_icons:
dependency: "direct main"
description:
name: cupertino_icons
sha256: "41e005c33bd814be4d3096aff55b1908d419fde52ca656c8c47719ec745873cd"
url: "https://pub.dev"
source: hosted
version: "1.0.9"
dart_jsonwebtoken:
dependency: transitive
description:
name: dart_jsonwebtoken
sha256: ad84e60181696513d04d5f2078e0bbc20365b911f46f647797317414bdc88fbe
url: "https://pub.dev"
source: hosted
version: "3.4.1"
dbus:
dependency: transitive
description:
name: dbus
sha256: "0ce9b0a839e6dee59a37a623d2fc26a35bbbe6404213e419b0d6411023d62645"
url: "https://pub.dev"
source: hosted
version: "0.7.14"
fake_async:
dependency: transitive
description:
name: fake_async
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
url: "https://pub.dev"
source: hosted
version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
flutter_cache_manager:
dependency: transitive
description:
name: flutter_cache_manager
sha256: "400b6592f16a4409a7f2bb929a9a7e38c72cceb8ffb99ee57bbf2cb2cecf8386"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
flutter_inappwebview:
dependency: "direct main"
description:
name: flutter_inappwebview
sha256: "80092d13d3e29b6227e25b67973c67c7210bd5e35c4b747ca908e31eb71a46d5"
url: "https://pub.dev"
source: hosted
version: "6.1.5"
flutter_inappwebview_android:
dependency: transitive
description:
name: flutter_inappwebview_android
sha256: "62557c15a5c2db5d195cb3892aab74fcaec266d7b86d59a6f0027abd672cddba"
url: "https://pub.dev"
source: hosted
version: "1.1.3"
flutter_inappwebview_internal_annotations:
dependency: transitive
description:
name: flutter_inappwebview_internal_annotations
sha256: e30fba942e3debea7b7e6cdd4f0f59ce89dd403a9865193e3221293b6d1544c6
url: "https://pub.dev"
source: hosted
version: "1.3.0"
flutter_inappwebview_ios:
dependency: transitive
description:
name: flutter_inappwebview_ios
sha256: "5818cf9b26cf0cbb0f62ff50772217d41ea8d3d9cc00279c45f8aabaa1b4025d"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_macos:
dependency: transitive
description:
name: flutter_inappwebview_macos
sha256: c1fbb86af1a3738e3541364d7d1866315ffb0468a1a77e34198c9be571287da1
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_platform_interface:
dependency: transitive
description:
name: flutter_inappwebview_platform_interface
sha256: cf5323e194096b6ede7a1ca808c3e0a078e4b33cc3f6338977d75b4024ba2500
url: "https://pub.dev"
source: hosted
version: "1.3.0+1"
flutter_inappwebview_web:
dependency: transitive
description:
name: flutter_inappwebview_web
sha256: "55f89c83b0a0d3b7893306b3bb545ba4770a4df018204917148ebb42dc14a598"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_inappwebview_windows:
dependency: transitive
description:
name: flutter_inappwebview_windows
sha256: "8b4d3a46078a2cdc636c4a3d10d10f2a16882f6be607962dbfff8874d1642055"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
flutter_lints:
dependency: "direct dev"
description:
name: flutter_lints
sha256: "3105dc8492f6183fb076ccf1f351ac3d60564bff92e20bfc4af9cc1651f4e7e1"
url: "https://pub.dev"
source: hosted
version: "6.0.0"
flutter_local_notifications:
dependency: "direct main"
description:
name: flutter_local_notifications
sha256: ef41ae901e7529e52934feba19ed82827b11baa67336829564aeab3129460610
url: "https://pub.dev"
source: hosted
version: "18.0.1"
flutter_local_notifications_linux:
dependency: transitive
description:
name: flutter_local_notifications_linux
sha256: "8f685642876742c941b29c32030f6f4f6dacd0e4eaecb3efbb187d6a3812ca01"
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_local_notifications_platform_interface:
dependency: transitive
description:
name: flutter_local_notifications_platform_interface
sha256: "6c5b83c86bf819cdb177a9247a3722067dd8cc6313827ce7c77a4b238a26fd52"
url: "https://pub.dev"
source: hosted
version: "8.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: "3854fe5e3bff0b113c658f260b90c95dea17c92db0f2addeac2e343dd9969785"
url: "https://pub.dev"
source: hosted
version: "2.0.35"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
sha256: "9cad52d75ebc511adfae3d447d5d13da15a55a92c9410e50f67335b6d21d16ea"
url: "https://pub.dev"
source: hosted
version: "9.2.4"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
sha256: be76c1d24a97d0b98f8b54bce6b481a380a6590df992d0098f868ad54dc8f688
url: "https://pub.dev"
source: hosted
version: "1.2.3"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
sha256: "6c0a2795a2d1de26ae202a0d78527d163f4acbb11cde4c75c670f3a0fc064247"
url: "https://pub.dev"
source: hosted
version: "3.1.3"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
sha256: cf91ad32ce5adef6fba4d736a542baca9daf3beac4db2d04be350b87f69ac4a8
url: "https://pub.dev"
source: hosted
version: "1.1.2"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
sha256: f4ebff989b4f07b2656fb16b47852c0aab9fed9b4ec1c70103368337bc1886a9
url: "https://pub.dev"
source: hosted
version: "1.2.1"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
sha256: b20b07cb5ed4ed74fc567b78a72936203f587eba460af1df11281c9326cd3709
url: "https://pub.dev"
source: hosted
version: "3.1.2"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
functions_client:
dependency: transitive
description:
name: functions_client
sha256: "94bde5b8062b1c498795ce509cbe7e83417e171eb55c2c611da64c15034b7851"
url: "https://pub.dev"
source: hosted
version: "2.6.0"
gotrue:
dependency: transitive
description:
name: gotrue
sha256: "22f0a6ea86c8024de6b164a49a213cfa60a23f19df26e5bca8d6eb7e087fdf17"
url: "https://pub.dev"
source: hosted
version: "2.21.0"
gtk:
dependency: transitive
description:
name: gtk
sha256: "4ff85b2a16724029dd9e5bbb5a94b6918f9973f74ba571c949d2002801879cf5"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
hive:
dependency: transitive
description:
name: hive
sha256: "8dcf6db979d7933da8217edcec84e9df1bdb4e4edc7fc77dbd5aa74356d6d941"
url: "https://pub.dev"
source: hosted
version: "2.2.3"
hive_flutter:
dependency: "direct main"
description:
name: hive_flutter
sha256: dca1da446b1d808a51689fb5d0c6c9510c0a2ba01e22805d492c73b68e33eecc
url: "https://pub.dev"
source: hosted
version: "1.1.0"
hooks:
dependency: transitive
description:
name: hooks
sha256: "9a62a50b50b769a737bc0a8ff381f333529df3ab746b2f6b02e83760231455ba"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
http:
dependency: transitive
description:
name: http
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
url: "https://pub.dev"
source: hosted
version: "1.6.0"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
intl:
dependency: transitive
description:
name: intl
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
url: "https://pub.dev"
source: hosted
version: "0.20.2"
jni:
dependency: transitive
description:
name: jni
sha256: c2230682d5bc2362c1c9e8d3c7f406d9cbba23ab3f2e203a025dd47e0fb2e68f
url: "https://pub.dev"
source: hosted
version: "1.0.0"
jni_flutter:
dependency: transitive
description:
name: jni_flutter
sha256: "8b59e590786050b1cd866677dddaf76b1ade5e7bc751abe04b86e84d379d3ba6"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
js:
dependency: transitive
description:
name: js
sha256: f2c445dce49627136094980615a031419f7f3eb393237e4ecd97ac15dea343f3
url: "https://pub.dev"
source: hosted
version: "0.6.7"
jwt_decode:
dependency: transitive
description:
name: jwt_decode
sha256: d2e9f68c052b2225130977429d30f187aa1981d789c76ad104a32243cfdebfbb
url: "https://pub.dev"
source: hosted
version: "0.3.1"
leak_tracker:
dependency: transitive
description:
name: leak_tracker
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
url: "https://pub.dev"
source: hosted
version: "11.0.2"
leak_tracker_flutter_testing:
dependency: transitive
description:
name: leak_tracker_flutter_testing
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
url: "https://pub.dev"
source: hosted
version: "3.0.10"
leak_tracker_testing:
dependency: transitive
description:
name: leak_tracker_testing
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
url: "https://pub.dev"
source: hosted
version: "3.0.2"
lints:
dependency: transitive
description:
name: lints
sha256: "12f842a479589fea194fe5c5a3095abc7be0c1f2ddfa9a0e76aed1dbd26a87df"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
local_auth:
dependency: "direct main"
description:
name: local_auth
sha256: "434d854cf478f17f12ab29a76a02b3067f86a63a6d6c4eb8fbfdcfe4879c1b7b"
url: "https://pub.dev"
source: hosted
version: "2.3.0"
local_auth_android:
dependency: transitive
description:
name: local_auth_android
sha256: a0bdfcc0607050a26ef5b31d6b4b254581c3d3ce3c1816ab4d4f4a9173e84467
url: "https://pub.dev"
source: hosted
version: "1.0.56"
local_auth_darwin:
dependency: transitive
description:
name: local_auth_darwin
sha256: "699873970067a40ef2f2c09b4c72eb1cfef64224ef041b3df9fdc5c4c1f91f49"
url: "https://pub.dev"
source: hosted
version: "1.6.1"
local_auth_platform_interface:
dependency: transitive
description:
name: local_auth_platform_interface
sha256: f98b8e388588583d3f781f6806e4f4c9f9e189d898d27f0c249b93a1973dd122
url: "https://pub.dev"
source: hosted
version: "1.1.0"
local_auth_windows:
dependency: transitive
description:
name: local_auth_windows
sha256: bc4e66a29b0fdf751aafbec923b5bed7ad6ed3614875d8151afe2578520b2ab5
url: "https://pub.dev"
source: hosted
version: "1.0.11"
logging:
dependency: transitive
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
name: matcher
sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
version: "0.13.0"
meta:
dependency: transitive
description:
name: meta
sha256: "1741988757a65eb6b36abe716829688cf01910bbf91c34354ff7ec1c3de2b349"
url: "https://pub.dev"
source: hosted
version: "1.18.0"
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
nm:
dependency: transitive
description:
name: nm
sha256: "2c9aae4127bdc8993206464fcc063611e0e36e72018696cd9631023a31b24254"
url: "https://pub.dev"
source: hosted
version: "0.5.0"
objective_c:
dependency: transitive
description:
name: objective_c
sha256: "6cb691c686fa2838c6deb34980d426145c2a5d537491cb83d463c33cdbc726ed"
url: "https://pub.dev"
source: hosted
version: "9.4.1"
octo_image:
dependency: transitive
description:
name: octo_image
sha256: "34faa6639a78c7e3cbe79be6f9f96535867e879748ade7d17c9b1ae7536293bd"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.2.0"
package_info_plus:
dependency: "direct main"
description:
name: package_info_plus
sha256: "16eee997588c60225bda0488b6dcfac69280a6b7a3cf02c741895dd370a02968"
url: "https://pub.dev"
source: hosted
version: "8.3.1"
package_info_plus_platform_interface:
dependency: transitive
description:
name: package_info_plus_platform_interface
sha256: "202a487f08836a592a6bd4f901ac69b3a8f146af552bbd14407b6b41e1c3f086"
url: "https://pub.dev"
source: hosted
version: "3.2.1"
path:
dependency: transitive
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: "69cbd515a62b94d32a7944f086b2f82b4ac40a1d45bebfc00813a430ab2dabcd"
url: "https://pub.dev"
source: hosted
version: "2.3.1"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
url: "https://pub.dev"
source: hosted
version: "2.6.0"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
sha256: "59adad729136f01ea9e35a48f5d1395e25cba6cea552249ddbe9cf950f5d7849"
url: "https://pub.dev"
source: hosted
version: "11.4.0"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
sha256: d3971dcdd76182a0c198c096b5db2f0884b0d4196723d21a866fc4cdea057ebc
url: "https://pub.dev"
source: hosted
version: "12.1.0"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
sha256: "79dfa1df734798aa3cfdad166d3a3698c206d8813de13516ea1071b5d7e2f420"
url: "https://pub.dev"
source: hosted
version: "9.4.10"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24"
url: "https://pub.dev"
source: hosted
version: "0.1.3+5"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878
url: "https://pub.dev"
source: hosted
version: "4.3.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e"
url: "https://pub.dev"
source: hosted
version: "0.2.1"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675"
url: "https://pub.dev"
source: hosted
version: "7.0.2"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pointycastle:
dependency: transitive
description:
name: pointycastle
sha256: "92aa3841d083cc4b0f4709b5c74fd6409a3e6ba833ffc7dc6a8fee096366acf5"
url: "https://pub.dev"
source: hosted
version: "4.0.0"
postgrest:
dependency: transitive
description:
name: postgrest
sha256: dbe357f9eacac45a98cd70194006e59c44c8ce6de0fafbdfef1f0c397c20f5de
url: "https://pub.dev"
source: hosted
version: "2.7.1"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
realtime_client:
dependency: transitive
description:
name: realtime_client
sha256: ae16ad5bbd9f77a025ae1042b0310bc9440902e51d62ffd761a6691788af9ae9
url: "https://pub.dev"
source: hosted
version: "2.7.4"
record_use:
dependency: transitive
description:
name: record_use
sha256: "2551bd8eecfe95d14ae75f6021ad0248be5c27f138c2ec12fcb52b500b3ba1ed"
url: "https://pub.dev"
source: hosted
version: "0.6.0"
retry:
dependency: transitive
description:
name: retry
sha256: "822e118d5b3aafed083109c72d5f484c6dc66707885e07c0fbcb8b986bba7efc"
url: "https://pub.dev"
source: hosted
version: "3.1.2"
rxdart:
dependency: transitive
description:
name: rxdart
sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962"
url: "https://pub.dev"
source: hosted
version: "0.28.0"
share_plus:
dependency: "direct main"
description:
name: share_plus
sha256: fce43200aa03ea87b91ce4c3ac79f0cecd52e2a7a56c7a4185023c271fbfa6da
url: "https://pub.dev"
source: hosted
version: "10.1.4"
share_plus_platform_interface:
dependency: transitive
description:
name: share_plus_platform_interface
sha256: cc012a23fc2d479854e6c80150696c4a5f5bb62cb89af4de1c505cf78d0a5d0b
url: "https://pub.dev"
source: hosted
version: "5.0.2"
shared_preferences:
dependency: "direct main"
description:
name: shared_preferences
sha256: c3025c5534b01739267eb7d76959bbc25a6d10f6988e1c2a3036940133dd10bf
url: "https://pub.dev"
source: hosted
version: "2.5.5"
shared_preferences_android:
dependency: transitive
description:
name: shared_preferences_android
sha256: "93ae5884a9df5d3bb696825bceb3a17590754548b5d740eba51500afc8d088f5"
url: "https://pub.dev"
source: hosted
version: "2.4.26"
shared_preferences_foundation:
dependency: transitive
description:
name: shared_preferences_foundation
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
shared_preferences_linux:
dependency: transitive
description:
name: shared_preferences_linux
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shared_preferences_platform_interface:
dependency: transitive
description:
name: shared_preferences_platform_interface
sha256: "649dc798a33931919ea356c4305c2d1f81619ea6e92244070b520187b5140ef9"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
shared_preferences_web:
dependency: transitive
description:
name: shared_preferences_web
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
url: "https://pub.dev"
source: hosted
version: "2.4.3"
shared_preferences_windows:
dependency: transitive
description:
name: shared_preferences_windows
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
shimmer:
dependency: "direct main"
description:
name: shimmer
sha256: "5f88c883a22e9f9f299e5ba0e4f7e6054857224976a5d9f839d4ebdc94a14ac9"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_span:
dependency: transitive
description:
name: source_span
sha256: "56a02f1f4cd1a2d96303c0144c93bd6d909eea6bee6bf5a0e0b685edbd4c47ab"
url: "https://pub.dev"
source: hosted
version: "1.10.2"
sqflite:
dependency: transitive
description:
name: sqflite
sha256: "58a799e6ac17dd32fbab93813d39ed835a75ccc0f8f85b8955fe318c6712b082"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: d0548f9d7422a2dae99ec6f8b0a3074463b132d216fa5ba0d230eeefc901983b
url: "https://pub.dev"
source: hosted
version: "2.4.3"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "5bf6a55c166e73bf651ba7ec3ed486e577620e3dc8f3a9c6a258a8031b624590"
url: "https://pub.dev"
source: hosted
version: "2.5.11"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "164a5d73ab87a134566057219988bafde837029a64264e61f1f04376ef3cfcd2"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: f84939f84350d92d04416f8bc4dc52d3896aec7716cc9e80cf0146342139dc50
url: "https://pub.dev"
source: hosted
version: "2.4.1"
stack_trace:
dependency: transitive
description:
name: stack_trace
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
url: "https://pub.dev"
source: hosted
version: "1.12.1"
storage_client:
dependency: transitive
description:
name: storage_client
sha256: "05b04c3a1578b5469b8e1f51670feb4e183e8b1be20d9b47e7882bf7048777b5"
url: "https://pub.dev"
source: hosted
version: "2.5.6"
stream_channel:
dependency: transitive
description:
name: stream_channel
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
string_scanner:
dependency: transitive
description:
name: string_scanner
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
url: "https://pub.dev"
source: hosted
version: "1.4.1"
supabase:
dependency: transitive
description:
name: supabase
sha256: ebae283b56df8a97491b8484aa34b60115c60bc612de24bd83b4918c85a14f31
url: "https://pub.dev"
source: hosted
version: "2.12.2"
supabase_flutter:
dependency: "direct main"
description:
name: supabase_flutter
sha256: "205f732f0e75163b197a7c4da8ee6654c792ace1efb15643e82d5b9bced5e94d"
url: "https://pub.dev"
source: hosted
version: "2.14.2"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: "93b153dcb6a26dcddee6ca087dd634b53e38c10b5aa163e8e49501a776456153"
url: "https://pub.dev"
source: hosted
version: "3.4.1"
term_glyph:
dependency: transitive
description:
name: term_glyph
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
url: "https://pub.dev"
source: hosted
version: "1.2.2"
test_api:
dependency: transitive
description:
name: test_api
sha256: "949a932224383300f01be9221c39180316445ecb8e7547f70a41a35bf421fb9e"
url: "https://pub.dev"
source: hosted
version: "0.7.11"
timezone:
dependency: transitive
description:
name: timezone
sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1
url: "https://pub.dev"
source: hosted
version: "0.10.1"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
url_launcher:
dependency: "direct main"
description:
name: url_launcher
sha256: f6a7e5c4835bb4e3026a04793a4199ca2d14c739ec378fdfe23fc8075d0439f8
url: "https://pub.dev"
source: hosted
version: "6.3.2"
url_launcher_android:
dependency: transitive
description:
name: url_launcher_android
sha256: b413d49b73867ac08dd2f9890efd3cc11f2a0e577618d50843440a1fb3776c32
url: "https://pub.dev"
source: hosted
version: "6.3.32"
url_launcher_ios:
dependency: transitive
description:
name: url_launcher_ios
sha256: "580fe5dfb51671ae38191d316e027f6b76272b026370708c2d898799750a02b0"
url: "https://pub.dev"
source: hosted
version: "6.4.1"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
sha256: d5e14138b3bc193a0f63c10a53c94b91d399df0512b1f29b94a043db7482384a
url: "https://pub.dev"
source: hosted
version: "3.2.2"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
sha256: "368adf46f71ad3c21b8f06614adb38346f193f3a59ba8fe9a2fd74133070ba18"
url: "https://pub.dev"
source: hosted
version: "3.2.5"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
sha256: "552f8a1e663569be95a8190206a38187b531910283c3e982193e4f2733f01029"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
sha256: "85c81589622fbc87c1c683aaea164d3604a7777495a79d91e39ffcdec39ddb34"
url: "https://pub.dev"
source: hosted
version: "2.4.3"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
sha256: "712c70ab1b99744ff066053cbe3e80c73332b38d46e5e945c98689b2e66fc15f"
url: "https://pub.dev"
source: hosted
version: "3.1.5"
uuid:
dependency: transitive
description:
name: uuid
sha256: "1fef9e8e11e2991bb773070d4656b7bd5d850967a2456cfc83cf47925ba79489"
url: "https://pub.dev"
source: hosted
version: "4.5.3"
vector_math:
dependency: transitive
description:
name: vector_math
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
url: "https://pub.dev"
source: hosted
version: "2.2.0"
vm_service:
dependency: transitive
description:
name: vm_service
sha256: "0016aef94fc66495ac78af5859181e3f3bf2026bd8eecc72b9565601e19ab360"
url: "https://pub.dev"
source: hosted
version: "15.2.0"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
win32:
dependency: transitive
description:
name: win32
sha256: d7cb55e04cd34096cd3a79b3330245f54cb96a370a1c27adb3c84b917de8b08e
url: "https://pub.dev"
source: hosted
version: "5.15.0"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025"
url: "https://pub.dev"
source: hosted
version: "6.6.1"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
yet_another_json_isolate:
dependency: transitive
description:
name: yet_another_json_isolate
sha256: fe45897501fa156ccefbfb9359c9462ce5dec092f05e8a56109db30be864f01e
url: "https://pub.dev"
source: hosted
version: "2.1.0"
sdks:
dart: ">=3.12.2 <4.0.0"
flutter: ">=3.44.0"
name: el3ab
description: "EL3AB Social Gaming Hub for Arabic Speakers"
publish_to: 'none'
version: 3.0.1+10
environment:
sdk: ^3.12.2
dependencies:
flutter:
sdk: flutter
cupertino_icons: ^1.0.8
# WebView
flutter_inappwebview: ^6.1.5
# Supabase
supabase_flutter: ^2.8.1
# Local Notifications
flutter_local_notifications: ^18.0.1
# Storage
flutter_secure_storage: ^9.2.4
hive_flutter: ^1.1.0
shared_preferences: ^2.3.4
# Auth
local_auth: ^2.3.0
# Native Features
share_plus: ^10.1.4
url_launcher: ^6.3.1
connectivity_plus: ^6.1.2
permission_handler: ^11.3.1
app_links: ^6.3.3
# UI
cached_network_image: ^3.4.1
shimmer: ^3.0.0
# Utils
path_provider: ^2.1.5
package_info_plus: ^8.1.3
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^6.0.0
flutter:
uses-material-design: true
assets:
- assets/images/
- assets/icons/
import 'package:flutter_test/flutter_test.dart';
void main() {
testWidgets('App smoke test', (WidgetTester tester) async {
expect(1 + 1, 2);
});
}
-----BEGIN CERTIFICATE-----
MIIDajCCAlKgAwIBAgIJANjtP5dlL6hBMA0GCSqGSIb3DQEBDAUAMGIxCzAJBgNV
BAYTAkVHMQ4wDAYDVQQIEwVDYWlybzEOMAwGA1UEBxMFQ2Fpcm8xEjAQBgNVBAoT
CUFsIEFyY2FkZTEPMA0GA1UECxMGTW9iaWxlMQ4wDAYDVQQDEwVFTDNBQjAgFw0y
NjA2MTYwODA0NTRaGA8yMDUzMTEwMTA4MDQ1NFowYjELMAkGA1UEBhMCRUcxDjAM
BgNVBAgTBUNhaXJvMQ4wDAYDVQQHEwVDYWlybzESMBAGA1UEChMJQWwgQXJjYWRl
MQ8wDQYDVQQLEwZNb2JpbGUxDjAMBgNVBAMTBUVMM0FCMIIBIjANBgkqhkiG9w0B
AQEFAAOCAQ8AMIIBCgKCAQEA0DY7uMMoOZwo8LqlUrr98z4nSUjb74f1k7w7+tqu
ACiIm1Ad4alBV6YcFdxqx8jIhVEUjrdvz8DVsOUE+PpRp5hTl5Q74GvpYg08nK4N
RdtR1R3GTVyJGE9Ue3jcGgMopvaf2LzuAS7HbSq9qtikcOP8yyj2l5LD3LttzoOY
CfJ4bZkR1QiWXMq91hnoLHoGtaGNuKrE6o4+Uh3lxe+7Tggd1i6QwovCBUD4jHt8
F382riPKkB93uVarAg08ZaLgILWaW/SwpB/M03q25Evhi10N7pitSkc9HSf5eIHr
YROIqdFhxETz3m+EveIIlbqkkRCusiE65GgC8haDJU9ujQIDAQABoyEwHzAdBgNV
HQ4EFgQUTrOJvvHUQjj5R6SNUP/xOc/fUB4wDQYJKoZIhvcNAQEMBQADggEBAKVm
mZzzXMLPd/sxUKzFubHx8cM2/OP6ZXW75vM8Jo/6ahla/6jtXHL1GaUma3IPlWoQ
vS4YDUUzH2ViFRgnm2AsntNvfNdxAMokWsnHxhcBjY6UY2xM6sKaiQt05fNnt5Dw
iVbn+iVBS5NsUI4mwZLjpzAskMcVsnRRVVORholkv3xJtaJ/M6VmC+iMJcYr1/qu
R6/LTAL3aMkmVVxDfloxPQ7C8xWtMnMaXbRZnCMGHbqymG8nKNCAK8RqGQDMI92W
ObOrtvDPw5FIV1TPkw54XIhkelbY/fhRi3X0flGd6nh6jwl1fNfH4pesxfxlGa9N
VvUNOXtlTLq+Q+pqU7w=
-----END CERTIFICATE-----
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment