Compare commits
8 Commits
5f5d5995c5
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 069b829294 | |||
| 704d1e7378 | |||
| 6e8bedd9b4 | |||
| 1ac755bcbd | |||
| bba5733783 | |||
| d6132fabe1 | |||
| 2534d321cf | |||
| 2673ef658d |
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"permissions": {
|
||||
"allow": [
|
||||
"Bash(./mvnw clean compile -q)",
|
||||
"mcp__ide__getDiagnostics",
|
||||
"Bash(find:*)",
|
||||
"Bash(./mvnw:*)",
|
||||
"Bash(rm:*)",
|
||||
"Bash(lsof:*)",
|
||||
"Bash(xargs kill:*)",
|
||||
"Bash(cat:*)",
|
||||
"Bash(mongosh:*)",
|
||||
"Bash(mongo:*)",
|
||||
"Bash(kill:*)"
|
||||
],
|
||||
"deny": [],
|
||||
"ask": []
|
||||
}
|
||||
}
|
||||
32
.gitignore
vendored
@@ -1,4 +1,3 @@
|
||||
/target/
|
||||
.idea/
|
||||
.vscode/
|
||||
.settings
|
||||
@@ -8,14 +7,27 @@
|
||||
*.iml
|
||||
.DS_Store
|
||||
|
||||
# The following files are generated/updated by vaadin-maven-plugin
|
||||
node_modules/
|
||||
src/main/frontend/generated/
|
||||
vite.generated.ts
|
||||
# Backend (Spring Boot / Vaadin)
|
||||
/backend/target/
|
||||
/backend/node_modules/
|
||||
/backend/src/main/frontend/generated/
|
||||
/backend/vite.generated.ts
|
||||
/backend/logs/
|
||||
/backend/.env
|
||||
|
||||
# Log files
|
||||
logs/
|
||||
# Flutter app
|
||||
/app/.dart_tool/
|
||||
/app/build/
|
||||
/app/coverage/
|
||||
/app/.flutter-plugins
|
||||
/app/.flutter-plugins-dependencies
|
||||
/app/android/.gradle/
|
||||
/app/android/.kotlin/
|
||||
/app/android/local.properties
|
||||
/app/ios/Pods/
|
||||
/app/ios/.symlinks/
|
||||
/app/macos/Pods/
|
||||
|
||||
# Root build artifacts
|
||||
/target/
|
||||
*.log
|
||||
|
||||
# Environment variables
|
||||
.env
|
||||
|
||||
22
AGENTS.md
@@ -1,22 +0,0 @@
|
||||
# Repository Guidelines
|
||||
|
||||
## Project Structure & Module Organization
|
||||
Backend Java sits in `src/main/java/de/assecutor/votianlt`; domain models stay in `model`, persistence logic in `repository`, services in `service`, MQTT integration under `mqtt`, and access control in `security`. Vaadin views and UI helpers live in `pages/view` and `pages/base`. TypeScript, styles, and theming live in `src/main/frontend` (leave `generated/` untouched). Shared configs and templates live in `src/main/resources`, while Vaadin bundle descriptors reside in `src/main/bundles`. Maven output lands in `target/`.
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
Use `./mvnw` for the Spring Boot dev server with frontend hot reload. Build production bits with `./mvnw -Pproduction package`. Run `./mvnw test` for unit checks and `./mvnw -Pintegration-test verify` when integration coverage is needed. After dependency changes, refresh Vaadin assets with `./mvnw vaadin:prepare-frontend`. Apply formatting via `./mvnw spotless:apply`.
|
||||
|
||||
## Coding Style & Naming Conventions
|
||||
Spotless enforces Java 21 formatting using `eclipse-formatter.xml`; keep imports ordered and rely on Lombok already present. Classes remain PascalCase, Spring stereotypes end with `Service`, `Repository`, or `Config`, and Vaadin views retain the `*View` naming within `pages/view`. Frontend code follows the repo’s Prettier rules (`.prettierrc.json`); keep TypeScript modules co-located with their views, prefer camelCase for variables, and avoid checking in generated `.class` files.
|
||||
|
||||
## Testing Guidelines
|
||||
Create tests under `src/test/java` mirroring the production package path. Name unit classes `*Test` and integration suites `*IT` so the failsafe profile picks them up. Lean on Spring Boot’s testing annotations for wiring, Mockito for isolates, and add Testcontainers when MongoDB or MQTT brokers are involved. Run `./mvnw test` before any push; trigger `./mvnw -Pintegration-test verify` for messaging, persistence, or security changes.
|
||||
|
||||
## Commit & Pull Request Guidelines
|
||||
History currently uses brief German titles; shift to imperative, scoped summaries such as `feat: add PDF mailer` or `fix: guard MQTT reconnects`. Keep unrelated updates out of the same commit and exclude artifacts like `node_modules/` or `target/`. Pull requests should explain the motivation, link issues, note config or data-seed impacts, and attach screenshots or screencasts when Vaadin views change. List manual verification steps and flag any migrations or bundle adjustments for reviewers.
|
||||
|
||||
## Security & Configuration Tips
|
||||
External service credentials for MongoDB, SMTP, and MQTT belong in environment variables or a developer-specific `application-local.properties` kept out of version control. Document default ports and topics when touching `MqttConfig` so ops can replicate environments. For two-factor flows, keep shared secrets in secure storage and avoid logging codes during development.
|
||||
|
||||
# Misc
|
||||
Never start the application; leave that to the user.
|
||||
103
CLAUDE.md
@@ -1,103 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Development Commands
|
||||
|
||||
- **Start development server**: `./mvnw` (runs Spring Boot with Vaadin dev mode)
|
||||
- **Build for production**: `./mvnw -Pproduction package`
|
||||
- **Clean build**: `./mvnw clean compile`
|
||||
- **Format code**: `./mvnw spotless:apply` (applies Eclipse formatter for Java, Prettier for TypeScript)
|
||||
- **Check formatting**: `./mvnw spotless:check`
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
This is a **Vaadin Spring Boot** application for job/task management with real-time mobile app communication via a pluggable messaging transport layer. The system manages logistics jobs with tasks that mobile app users complete.
|
||||
|
||||
### Core Architecture Layers
|
||||
|
||||
**Frontend**: Vaadin Flow views (server-side rendered)
|
||||
- `src/main/java/de/assecutor/votianlt/pages/view/` - Main UI views
|
||||
- `src/main/java/de/assecutor/votianlt/pages/base/ui/` - Shared UI components
|
||||
|
||||
**Backend Services**:
|
||||
- `src/main/java/de/assecutor/votianlt/service/` - Business logic
|
||||
- `src/main/java/de/assecutor/votianlt/controller/` - Message handling (routes inbound messages to processors)
|
||||
- `src/main/java/de/assecutor/votianlt/repository/` - MongoDB data access
|
||||
|
||||
**Messaging Layer** (`src/main/java/de/assecutor/votianlt/messaging/`):
|
||||
- `plugin/` - Transport plugin interface and implementations (MQTT, extensible for WebSocket, gRPC)
|
||||
- `delivery/` - Reliable message delivery with acknowledgment tracking, retries, and expiry
|
||||
- `model/` - Message envelopes, delivery status, pending deliveries
|
||||
- `config/` - Messaging configuration and wiring
|
||||
|
||||
**Models**:
|
||||
- `src/main/java/de/assecutor/votianlt/model/` - Domain entities
|
||||
- Task hierarchy: `BaseTask` with subtypes (`PhotoTask`, `BarcodeTask`, `SignatureTask`, etc.)
|
||||
|
||||
### Key Architectural Patterns
|
||||
|
||||
**Job-Task Relationship**: Jobs contain multiple ordered tasks. Tasks have completion states and can store completion data (photos, barcodes, signatures).
|
||||
|
||||
**User Hierarchy**:
|
||||
- `User` - Web interface users (job managers)
|
||||
- `AppUser` - Mobile app users (task executors)
|
||||
- `AppUser.owner` field links to `User` for notifications
|
||||
|
||||
**Messaging Plugin Architecture**:
|
||||
- `MessagingPlugin` interface abstracts transport protocols (currently MQTT via HiveMQ)
|
||||
- `MessageDeliveryService` provides guaranteed delivery with acknowledgment tracking
|
||||
- `AcknowledgmentHandler` processes ACKs and updates delivery status
|
||||
- Plugins are responsible for topic/channel structure; delivery layer uses `clientId` and `messageType`
|
||||
|
||||
**Client Connection Monitoring**:
|
||||
- `ClientConnectionService` tracks connected mobile clients via ping/pong mechanism
|
||||
- Server sends ping to `/client/{clientId}/ping`, client responds on `/server/{clientId}/pong`
|
||||
|
||||
**History Tracking**: `JobHistoryService` logs all job/task changes with detailed audit trail displayed in `JobHistoryView`.
|
||||
|
||||
**Email Notifications**: `EmailService` sends notifications for job creation, task completion, and job completion using Spring Mail with SMTP.
|
||||
|
||||
## Data Storage
|
||||
|
||||
**MongoDB Collections**:
|
||||
- `jobs` - Main job entities with status tracking
|
||||
- `tasks` - Polymorphic task storage (discriminated by `taskType`)
|
||||
- `job_history` - Audit trail for all job changes
|
||||
- `pending_deliveries` - Message delivery tracking for retries
|
||||
- `photos`, `barcodes`, `signatures` - Task completion data
|
||||
- `users` - Web interface users
|
||||
- `app_user` - Mobile app users
|
||||
- `cargo_item` - Job cargo/delivery items
|
||||
|
||||
## Configuration
|
||||
|
||||
**Database**: MongoDB (configurable via `spring.data.mongodb.uri`)
|
||||
**Messaging**: Plugin-based, currently MQTT via HiveMQ (`app.messaging.plugin.*` properties)
|
||||
**Email**: SMTP via Spring Boot mail auto-configuration
|
||||
|
||||
## Development Environment
|
||||
|
||||
**Java 21** with **Spring Boot 3.4.3** and **Vaadin 24.7.0**
|
||||
**Security**: Spring Security with role-based access (`USER` role required)
|
||||
**Formatting**: Spotless Maven plugin with Eclipse formatter (Java) and Prettier (TypeScript)
|
||||
**Profiles**: `production` profile for optimized builds, `integration-test` profile for failsafe plugin
|
||||
|
||||
## Key Integration Points
|
||||
|
||||
When adding new task types:
|
||||
1. Extend `BaseTask` and add to `@JsonSubTypes`
|
||||
2. Add completion logic in `MessageController.handleTaskCompleted()`
|
||||
3. Update `JobHistoryView` for task-specific previews if needed
|
||||
|
||||
When modifying job status flow:
|
||||
1. Update `JobStatus` enum
|
||||
2. Modify `EmailService.updateJobStatusToCompleted()` logic
|
||||
3. Consider email notification templates
|
||||
|
||||
When adding new messaging transports:
|
||||
1. Implement `MessagingPlugin` interface
|
||||
2. Register in `PluginMessagingConfig`
|
||||
3. Add configuration properties under `app.messaging.plugin.<type>.*`
|
||||
|
||||
Message routing follows pattern: `MessageController` receives messages via `MessageDeliveryService`, extracts `taskType`/`messageType` from payload, routes to appropriate processor method.
|
||||
42
README.md
@@ -1,6 +1,40 @@
|
||||
docker buildx build --platform linux/amd64 -t appcreationgmbh/votianlt:0.8.0 --push .
|
||||
# VotianLT Monorepo
|
||||
|
||||
docker buildx build --platform linux/amd64 -t registry.assecutor.de/votianlt:0.9.10 --push .
|
||||
## Struktur
|
||||
|
||||
adsg
|
||||
G8m0T3vz
|
||||
- `backend/`: Spring Boot / Vaadin Backend
|
||||
- `app/`: Flutter App
|
||||
- `.vscode/`: gemeinsame Workspace-Launches für Backend und Flutter
|
||||
|
||||
## Backend
|
||||
|
||||
```bash
|
||||
cd backend
|
||||
./mvnw
|
||||
```
|
||||
|
||||
Wichtige Befehle:
|
||||
|
||||
```bash
|
||||
cd backend && ./mvnw test
|
||||
cd backend && ./mvnw -Pproduction package
|
||||
cd backend && ./mvnw spotless:apply
|
||||
```
|
||||
|
||||
## Flutter App
|
||||
|
||||
```bash
|
||||
cd app
|
||||
flutter pub get
|
||||
flutter run
|
||||
```
|
||||
|
||||
## Release Image
|
||||
|
||||
Das Release-Script liegt im Repo-Root und baut/pusht das Backend-Image:
|
||||
|
||||
```bash
|
||||
docker login registry.assecutor.org
|
||||
./docker_push.sh
|
||||
./docker_push.sh 0.9.13
|
||||
```
|
||||
|
||||
2
app/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
.dart_tool/
|
||||
build/
|
||||
429
app/AGENTS.md
Normal file
@@ -0,0 +1,429 @@
|
||||
# Votian LT App - Agent Guidelines
|
||||
|
||||
## Project Overview
|
||||
|
||||
**Votian LT** is a Flutter-based mobile application for logistics and transport management. The app enables drivers to manage transport jobs, complete tasks (photos, signatures, barcodes), communicate via real-time chat, and navigate to pickup/delivery locations.
|
||||
|
||||
### Key Features
|
||||
- **Job Management**: View and manage assigned transport jobs with cargo items
|
||||
- **Task System**: Complete various task types (confirmation, photo capture, signature, barcode scanning, todo lists, comments)
|
||||
- **Real-time Chat**: Job-specific and general chat via WebSocket
|
||||
- **Offline Support**: Full offline functionality with local ObjectBox database
|
||||
- **Navigation**: Integration with external maps and embedded WebView routing
|
||||
- **Push Notifications**: Local notifications for new jobs and messages
|
||||
- **Implementation**: When making changes, be careful not to damage existing functionalities.
|
||||
|
||||
---
|
||||
|
||||
## Technology Stack
|
||||
|
||||
| Layer | Technology |
|
||||
|-------|------------|
|
||||
| Framework | Flutter 3.7+ with Dart |
|
||||
| State Management | Singleton pattern + Custom DartMQ pub/sub |
|
||||
| Local Database | ObjectBox (NoSQL) |
|
||||
| Real-time Communication | WebSocket (STOMP-style protocol) |
|
||||
| Backend Integration | WebSocket to `ws://localhost:8082/ws/messaging` |
|
||||
| UI Design | Material Design 3 |
|
||||
|
||||
### Key Dependencies (pubspec.yaml)
|
||||
- `web_socket_channel: ^3.0.0` - WebSocket client
|
||||
- `objectbox: ^4.0.3` + `objectbox_flutter_libs` - Local database
|
||||
- `camera: ^0.10.5+9` - Photo capture
|
||||
- `mobile_scanner: ^5.0.0` - Barcode scanning
|
||||
- `signature: ^5.5.0` - Signature capture canvas
|
||||
- `flutter_local_notifications: ^18.0.0` - Local notifications
|
||||
- `url_launcher: ^6.3.0` - External navigation
|
||||
- `webview_flutter: ^4.8.0` - Embedded maps
|
||||
- `image: ^4.2.0` - Image processing
|
||||
- `package_info_plus: ^8.0.0` - App version info
|
||||
|
||||
---
|
||||
|
||||
## Project Structure
|
||||
|
||||
```
|
||||
lib/
|
||||
├── main.dart # App entry point, MaterialApp setup
|
||||
├── app_state.dart # Global app state singleton (jobs, login)
|
||||
├── navigation_observer.dart # Route observer for analytics
|
||||
├── routing_view.dart # Embedded navigation WebView
|
||||
│
|
||||
├── models/ # Domain models (JSON serializable)
|
||||
│ ├── job.dart # Transport job with cargo items and tasks
|
||||
│ ├── cargo_item.dart # Cargo/load items
|
||||
│ ├── task.dart # Abstract task base class
|
||||
│ ├── tasks/ # Concrete task implementations
|
||||
│ │ ├── confirmation_task.dart
|
||||
│ │ ├── photo_task.dart
|
||||
│ │ ├── signature_task.dart
|
||||
│ │ ├── barcode_task.dart
|
||||
│ │ ├── todolist_task.dart
|
||||
│ │ ├── comment_task.dart
|
||||
│ │ └── generic_task.dart
|
||||
│ ├── chat.dart # Chat conversation
|
||||
│ ├── chat_message.dart # Individual message
|
||||
│ ├── message_envelope.dart # WebSocket message wrapper
|
||||
│ ├── acknowledgment_message.dart
|
||||
│ └── queued_message.dart # Offline message queue
|
||||
│
|
||||
├── entities/ # ObjectBox database entities
|
||||
│ ├── job_entity.dart
|
||||
│ ├── task_status_entity.dart
|
||||
│ ├── user_data_entity.dart
|
||||
│ ├── photo_entity.dart
|
||||
│ ├── queued_message_entity.dart
|
||||
│ └── chat_message_entity.dart
|
||||
│
|
||||
├── services/ # Business logic services (singletons)
|
||||
│ ├── websocket_service.dart # WebSocket connection & messaging
|
||||
│ ├── database_service.dart # ObjectBox database operations
|
||||
│ ├── chat_service.dart # Chat management
|
||||
│ ├── dart_mq.dart # In-app pub/sub message bus
|
||||
│ ├── notification_service.dart # Local notifications
|
||||
│ ├── message_handler.dart # Incoming message processing
|
||||
│ ├── ack_tracker.dart # Message acknowledgment tracking
|
||||
│ └── developer.dart # Developer utilities/logging
|
||||
│
|
||||
├── views/ # Main UI screens
|
||||
│ ├── login_view.dart # Authentication screen
|
||||
│ ├── jobs_view.dart # Job list screen
|
||||
│ ├── cargo_items_view.dart # Cargo details for a job
|
||||
│ ├── chats_view.dart # Chat list screen
|
||||
│ ├── chat_details_view.dart # Individual chat conversation
|
||||
│ └── task_view.dart # Task completion screen
|
||||
│
|
||||
├── tasks/ # Task-specific UI screens
|
||||
│ ├── photo_capture_screen.dart
|
||||
│ ├── signature_capture_screen.dart
|
||||
│ └── barcode_capture_screen.dart
|
||||
│
|
||||
└── widgets/ # Reusable UI components
|
||||
├── chat_photo_dialog.dart
|
||||
└── offline_banner.dart
|
||||
|
||||
test/ # Unit and widget tests
|
||||
├── models/
|
||||
│ ├── job_parsing_test.dart
|
||||
│ ├── message_envelope_test.dart
|
||||
│ └── acknowledgment_message_test.dart
|
||||
└── services/
|
||||
├── ack_tracker_test.dart
|
||||
├── message_handler_test.dart
|
||||
└── mqtt_integration_test.dart
|
||||
|
||||
android/, ios/, macos/, linux/, windows/ # Platform-specific code
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Build, Test, and Development Commands
|
||||
|
||||
### Setup
|
||||
```bash
|
||||
# Install dependencies
|
||||
flutter pub get
|
||||
|
||||
# Generate ObjectBox code (after entity changes)
|
||||
flutter pub run build_runner build
|
||||
```
|
||||
|
||||
### Development
|
||||
```bash
|
||||
# Run static analysis
|
||||
flutter analyze
|
||||
|
||||
# Format code (CI expects formatted code)
|
||||
dart format lib/ test/
|
||||
|
||||
# Run the app
|
||||
flutter run
|
||||
|
||||
# Run on specific device
|
||||
flutter run -d <device_id>
|
||||
```
|
||||
|
||||
### Testing
|
||||
```bash
|
||||
# Run all tests
|
||||
flutter test
|
||||
|
||||
# Run with coverage
|
||||
flutter test --coverage
|
||||
|
||||
# Run specific test file
|
||||
flutter test test/models/job_parsing_test.dart
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Architecture Patterns
|
||||
|
||||
### Singleton Services
|
||||
All major services use the singleton pattern for app-wide state:
|
||||
|
||||
```dart
|
||||
// Accessing services
|
||||
final appState = AppState();
|
||||
final chatService = ChatService();
|
||||
final wsService = WebSocketService();
|
||||
final dbService = DatabaseService();
|
||||
```
|
||||
|
||||
### DartMQ Pub/Sub
|
||||
Custom lightweight message bus for decoupled communication:
|
||||
|
||||
```dart
|
||||
// Subscribe to topics
|
||||
final sub = DartMQ().subscribe<Map<String, dynamic>>(
|
||||
MQTopics.authResponse,
|
||||
(data) => handleAuth(data),
|
||||
);
|
||||
|
||||
// Publish messages
|
||||
DartMQ().publish<bool>(MQTopics.connectionStatus, true);
|
||||
|
||||
// Cleanup
|
||||
sub.cancel();
|
||||
```
|
||||
|
||||
**Common Topics** (`lib/services/dart_mq.dart`):
|
||||
- `connection/status` - WebSocket connection state (bool)
|
||||
- `auth/response` - Authentication responses (Map)
|
||||
- `jobs/response` - Job list updates (List)
|
||||
- `jobsUpdated` - Job data changed notification (void)
|
||||
- `job/deleted` - Job deletion event (Map)
|
||||
- `job/created` - New job created (Map)
|
||||
- `chat/incoming` - New chat message (ChatMessage)
|
||||
|
||||
### Task System
|
||||
Tasks are polymorphic based on `taskType` field:
|
||||
|
||||
| Task Type | Description |
|
||||
|-----------|-------------|
|
||||
| `CONFIRMATION` | Button tap confirmation |
|
||||
| `PHOTO` | Capture photos (min/max count) |
|
||||
| `SIGNATURE` | Capture signature as SVG |
|
||||
| `BARCODE` | Scan barcodes/QR codes |
|
||||
| `TODOLIST` | Checklist of items |
|
||||
| `COMMENT` | Text input field |
|
||||
| `GENERIC` | Fallback type |
|
||||
|
||||
---
|
||||
|
||||
## Coding Style Guidelines
|
||||
|
||||
### Dart/Flutter Conventions
|
||||
- **Indentation**: 2 spaces
|
||||
- **Trailing Commas**: Use trailing commas to encourage proper auto-formatting
|
||||
- **Naming**:
|
||||
- Classes: `UpperCamelCase`
|
||||
- Methods/variables: `lowerCamelCase`
|
||||
- Private members: `_leadingUnderscore`
|
||||
- Constants: `camelCase` or `UPPER_SNAKE_CASE` for static const
|
||||
|
||||
### Code Style Examples
|
||||
```dart
|
||||
// Good: trailing commas for multi-line
|
||||
final job = Job(
|
||||
id: '123',
|
||||
jobNumber: 'JOB-001',
|
||||
status: 'ASSIGNED',
|
||||
// ...
|
||||
);
|
||||
|
||||
// Good: private helper methods
|
||||
String _formatAddress(String street, String city) {
|
||||
return '$street, $city';
|
||||
}
|
||||
|
||||
// Good: type annotations for public APIs
|
||||
Future<List<Job>> loadJobs() async {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Imports
|
||||
- Order: Dart SDK → Flutter → Third-party → Project (alphabetical within groups)
|
||||
- Use `package:votianlt_app/` prefix for project imports
|
||||
|
||||
---
|
||||
|
||||
## Testing Guidelines
|
||||
|
||||
### Test Structure
|
||||
```dart
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:votianlt_app/models/job.dart';
|
||||
|
||||
void main() {
|
||||
group('Job Parsing', () {
|
||||
test('parses basic fields correctly', () {
|
||||
// Arrange
|
||||
final json = {'job': {'id': '123', ...}};
|
||||
|
||||
// Act
|
||||
final job = Job.fromJson(json);
|
||||
|
||||
// Assert
|
||||
expect(job.id, '123');
|
||||
});
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Testing Patterns
|
||||
- Mirror the `lib/` directory structure in `test/`
|
||||
- Name tests after the unit: `job_parsing_test.dart`
|
||||
- Use `group()` for related assertions
|
||||
- Test both success and edge cases
|
||||
- Test round-trip serialization (`fromJson` → `toJson` → `fromJson`)
|
||||
|
||||
### Mocking
|
||||
Use `mocktail` for mocking dependencies in service tests.
|
||||
|
||||
---
|
||||
|
||||
## Database (ObjectBox)
|
||||
|
||||
### Entity Definition Example
|
||||
```dart
|
||||
@Entity()
|
||||
class JobEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String jobId;
|
||||
|
||||
String jobData; // JSON-encoded
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime updatedAt;
|
||||
|
||||
JobEntity({...});
|
||||
}
|
||||
```
|
||||
|
||||
### Regenerating Code
|
||||
After modifying entities in `lib/entities/`:
|
||||
```bash
|
||||
flutter pub run build_runner build
|
||||
```
|
||||
|
||||
This generates `lib/objectbox.g.dart`.
|
||||
|
||||
---
|
||||
|
||||
## WebSocket Protocol
|
||||
|
||||
### Connection
|
||||
- URL: `ws://localhost:8082/ws/messaging` (desktop)
|
||||
- Android Emulator: `ws://10.0.2.2:8082/ws/messaging`
|
||||
|
||||
### Message Format
|
||||
```json
|
||||
{
|
||||
"topic": "/client/auth",
|
||||
"payload": { ... }
|
||||
}
|
||||
```
|
||||
|
||||
### Client → Server Topics
|
||||
- `/server/login` - Authentication
|
||||
- `/server/jobs/assigned` - Request job list
|
||||
- `/server/message` - Send chat message
|
||||
- `/server/task_completed` - Mark task complete
|
||||
|
||||
### Server → Client Topics
|
||||
- `/client/{userId}/auth` - Auth response
|
||||
- `/client/{userId}/jobs` - Job list
|
||||
- `/client/{userId}/message` - Incoming chat
|
||||
- `/client/{userId}/job_deleted` - Job deleted
|
||||
- `/client/{userId}/job_created` - New job
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations
|
||||
|
||||
### Credentials
|
||||
- Email/password stored in ObjectBox (encrypted at rest by OS)
|
||||
- Credentials cleared on logout
|
||||
- Auto-login with saved credentials
|
||||
|
||||
### WebSocket
|
||||
- Reconnection with 15-second interval
|
||||
- Message buffering when offline
|
||||
- Unique App ID per installation for client identification
|
||||
|
||||
### Secrets
|
||||
- Do not commit API keys or credentials
|
||||
- Server endpoint configurable in `WebSocketService._buildWebSocketUrl()`
|
||||
|
||||
---
|
||||
|
||||
## Platform-Specific Notes
|
||||
|
||||
### Android
|
||||
- Minimum SDK: Defined in `android/app/build.gradle`
|
||||
- Permissions in `AndroidManifest.xml`:
|
||||
- `INTERNET` - WebSocket communication
|
||||
- `CAMERA` - Photo/barcode capture
|
||||
- `WRITE_EXTERNAL_STORAGE` - Photo storage
|
||||
- `POST_NOTIFICATIONS` - Push notifications
|
||||
- `VIBRATE` - Notification vibration
|
||||
|
||||
### iOS
|
||||
- Camera and photo permissions in `ios/Runner/Info.plist`
|
||||
- Notification permissions configured
|
||||
|
||||
---
|
||||
|
||||
## Common Development Tasks
|
||||
|
||||
### Adding a New Task Type
|
||||
1. Create model in `lib/models/tasks/new_task_type.dart`
|
||||
2. Add to `Task.fromJson()` factory in `lib/models/task.dart`
|
||||
3. Add UI screen in `lib/tasks/` if needed
|
||||
4. Update `task_view.dart` to handle the new type
|
||||
5. Add tests in `test/models/`
|
||||
|
||||
### Adding a New Database Entity
|
||||
1. Create entity class in `lib/entities/`
|
||||
2. Run `flutter pub run build_runner build`
|
||||
3. Add CRUD operations in `DatabaseService`
|
||||
|
||||
### Modifying WebSocket Messages
|
||||
1. Update message handler in `WebSocketService._handleMessage()`
|
||||
2. Add topic constant to `MQTopics` if needed
|
||||
3. Update `MessageHandler` for processing logic
|
||||
|
||||
---
|
||||
|
||||
## Localization
|
||||
|
||||
The app uses German for UI text:
|
||||
- Job status: "Erstellt", "Zugewiesen", "In Bearbeitung", "Abgeschlossen"
|
||||
- Notifications: "Neue Jobs", "Neue Nachricht"
|
||||
- Chat: "Allgemeine Nachrichten"
|
||||
|
||||
---
|
||||
|
||||
## Debugging
|
||||
|
||||
### Logging
|
||||
Use the developer log utility:
|
||||
```dart
|
||||
import 'package:votianlt_app/services/developer.dart' as developer;
|
||||
|
||||
developer.log('Debug message', name: 'ComponentName');
|
||||
```
|
||||
|
||||
### Common Issues
|
||||
1. **WebSocket not connecting**: Check server is running on port 8082
|
||||
2. **Database errors**: Run `flutter clean` and `flutter pub get`
|
||||
3. **ObjectBox issues**: Regenerate with `build_runner`
|
||||
4. **Camera not working**: Check platform permissions
|
||||
71
app/CLAUDE.md
Normal file
@@ -0,0 +1,71 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Build & Development Commands
|
||||
|
||||
```bash
|
||||
flutter pub get # Install dependencies
|
||||
flutter analyze # Run static analysis (run after every task)
|
||||
dart format <paths> # Format code
|
||||
flutter test # Run tests
|
||||
flutter run -d <device> # Run app on device
|
||||
dart run build_runner build # Generate ObjectBox code after entity changes
|
||||
```
|
||||
|
||||
**Important:** Run `flutter analyze` after every task and fix any reported issues before committing.
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
This is a Flutter app for job/task management with MQTT-based backend communication. The app is written in German for end users.
|
||||
|
||||
### Core Components
|
||||
|
||||
**AppState** (`lib/app_state.dart`)
|
||||
- Singleton managing global state: `appUserId`, in-memory jobs list
|
||||
- Handles persistence via DatabaseService with coalesced writes
|
||||
- Emits `jobsUpdated` events via both StreamController and DartMQ
|
||||
|
||||
**DartMQ** (`lib/services/dart_mq.dart`)
|
||||
- Lightweight in-app pub/sub message bus for decoupled communication
|
||||
- Key topics defined in `MQTopics`: `connectionStatus`, `authResponse`, `jobsResponse`, `taskEvents`, `jobsUpdated`, `chatIncoming`, `chatOutgoing`
|
||||
- UI and services subscribe/publish without direct dependencies
|
||||
|
||||
**MqttService** (`lib/services/mqtt_service.dart`, aliased as `StompService`)
|
||||
- MQTT client connecting to Mosquitto broker at `mqtt-2.assecutor.de:42099`
|
||||
- Handles authentication, job loading, chat messages, task completion
|
||||
- Message envelope pattern with ACK/retry system for reliable delivery
|
||||
- Offline message queuing via DatabaseService
|
||||
- Publishes all server events through DartMQ topics
|
||||
|
||||
**DatabaseService** (`lib/services/database_service.dart`)
|
||||
- ObjectBox-based local persistence
|
||||
- Stores jobs, task status, user data, chat messages, queued MQTT messages
|
||||
- Entities in `lib/entities/` require `dart run build_runner build` after changes
|
||||
|
||||
### Data Flow
|
||||
|
||||
1. `LoginView` initiates MQTT connection, sends credentials to `/server/login`
|
||||
2. Server responds on `/client/{appId}/auth` → MqttService publishes `MQTopics.authResponse`
|
||||
3. On success, `AppState` stores `appUserId`, `JobsView` requests jobs via `/server/{userId}/jobs/assigned`
|
||||
4. Jobs arrive on `/client/{userId}/jobs` → published to `MQTopics.jobsResponse` → persisted → UI refresh via `MQTopics.jobsUpdated`
|
||||
5. Task updates flow through `/client/{userId}/notifications` → `MQTopics.taskEvents`
|
||||
|
||||
### Models
|
||||
|
||||
- **Job** (`lib/models/job.dart`): Contains pickup/delivery addresses, cargo items, and tasks
|
||||
- **Task** (`lib/models/task.dart`): Abstract base with subtypes: `ConfirmationTask`, `PhotoTask`, `TodoListTask`, `SignatureTask`, `BarcodeTask`, `CommentTask`, `GenericTask`
|
||||
- **ChatMessage** (`lib/models/chat_message.dart`): Chat with direction, content type, job linking
|
||||
|
||||
### Views
|
||||
|
||||
- `LoginView` → `JobsView` → `CargoItemsView` → Task screens
|
||||
- `ChatsView` → `ChatDetailsView`
|
||||
- Task capture screens in `lib/tasks/`: photo, signature, barcode
|
||||
|
||||
## Key Patterns
|
||||
|
||||
- All services are singletons (factory constructors returning `_instance`)
|
||||
- MQTT messages wrapped in `MessageEnvelope` for reliable delivery with ACK
|
||||
- UI subscribes to DartMQ topics rather than holding service references
|
||||
- Jobs are normalized before persistence to ensure consistent data
|
||||
16
app/README.md
Normal file
@@ -0,0 +1,16 @@
|
||||
# votianlt_app
|
||||
|
||||
votian LT
|
||||
|
||||
## 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:
|
||||
|
||||
- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab)
|
||||
- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook)
|
||||
|
||||
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.
|
||||
28
app/analysis_options.yaml
Normal file
@@ -0,0 +1,28 @@
|
||||
# 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
|
||||
14
app/android/.gitignore
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
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
|
||||
49
app/android/app/build.gradle.kts
Normal file
@@ -0,0 +1,49 @@
|
||||
plugins {
|
||||
id("com.android.application")
|
||||
id("kotlin-android")
|
||||
// The Flutter Gradle Plugin must be applied after the Android and Kotlin Gradle plugins.
|
||||
id("dev.flutter.flutter-gradle-plugin")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "de.assecutor.votianlt_app"
|
||||
compileSdk = 36
|
||||
ndkVersion = "27.0.12077973"
|
||||
|
||||
compileOptions {
|
||||
isCoreLibraryDesugaringEnabled = true
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
|
||||
kotlinOptions {
|
||||
jvmTarget = JavaVersion.VERSION_11.toString()
|
||||
}
|
||||
|
||||
defaultConfig {
|
||||
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
|
||||
applicationId = "de.assecutor.votianlt_app"
|
||||
// You can update the following values to match your application needs.
|
||||
// For more information, see: https://flutter.dev/to/review-gradle-config.
|
||||
minSdk = flutter.minSdkVersion
|
||||
targetSdk = flutter.targetSdkVersion
|
||||
versionCode = flutter.versionCode
|
||||
versionName = flutter.versionName
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
// TODO: Add your own signing config for the release build.
|
||||
// Signing with the debug keys for now, so `flutter run --release` works.
|
||||
signingConfig = signingConfigs.getByName("debug")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
|
||||
}
|
||||
|
||||
flutter {
|
||||
source = "../.."
|
||||
}
|
||||
7
app/android/app/src/debug/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<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>
|
||||
56
app/android/app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,56 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.CAMERA"/>
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
|
||||
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||
<!-- GPS Location permissions -->
|
||||
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION"/>
|
||||
<application
|
||||
android:label="votianlt_app"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:networkSecurityConfig="@xml/network_security_config">
|
||||
<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">
|
||||
<!-- Specifies an Android theme to apply to this Activity as soon as
|
||||
the Android process has started. This theme is visible to the user
|
||||
while the Flutter UI initializes. After that, this theme continues
|
||||
to determine the Window background behind the Flutter UI. -->
|
||||
<meta-data
|
||||
android:name="io.flutter.embedding.android.NormalTheme"
|
||||
android:resource="@style/NormalTheme"
|
||||
/>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN"/>
|
||||
<category android:name="android.intent.category.LAUNCHER"/>
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<!-- Don't delete the meta-data below.
|
||||
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
|
||||
<meta-data
|
||||
android:name="flutterEmbedding"
|
||||
android:value="2" />
|
||||
</application>
|
||||
<!-- Required to query activities that can process text, see:
|
||||
https://developer.android.com/training/package-visibility and
|
||||
https://developer.android.com/reference/android/content/Intent#ACTION_PROCESS_TEXT.
|
||||
|
||||
In particular, this is used by the Flutter engine in io.flutter.plugin.text.ProcessTextPlugin. -->
|
||||
<queries>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.PROCESS_TEXT"/>
|
||||
<data android:mimeType="text/plain"/>
|
||||
</intent>
|
||||
</queries>
|
||||
</manifest>
|
||||
@@ -0,0 +1,5 @@
|
||||
package de.assecutor.votianlt_app
|
||||
|
||||
import io.flutter.embedding.android.FlutterActivity
|
||||
|
||||
class MainActivity : FlutterActivity()
|
||||
@@ -0,0 +1,12 @@
|
||||
<?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>
|
||||
12
app/android/app/src/main/res/drawable/launch_background.xml
Normal file
@@ -0,0 +1,12 @@
|
||||
<?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:color/white" />
|
||||
|
||||
<!-- You can insert your own image assets here -->
|
||||
<!-- <item>
|
||||
<bitmap
|
||||
android:gravity="center"
|
||||
android:src="@mipmap/launch_image" />
|
||||
</item> -->
|
||||
</layer-list>
|
||||
BIN
app/android/app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 544 B |
BIN
app/android/app/src/main/res/mipmap-mdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 442 B |
BIN
app/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 721 B |
BIN
app/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
18
app/android/app/src/main/res/values-night/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Black.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
18
app/android/app/src/main/res/values/styles.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->
|
||||
<style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<!-- Show a splash screen on the activity. Automatically removed when
|
||||
the Flutter engine draws its first frame -->
|
||||
<item name="android:windowBackground">@drawable/launch_background</item>
|
||||
</style>
|
||||
<!-- Theme applied to the Android Window as soon as the process has started.
|
||||
This theme determines the color of the Android Window while your
|
||||
Flutter UI initializes, as well as behind your Flutter UI while its
|
||||
running.
|
||||
|
||||
This Theme is only used starting with V2 of Flutter's Android embedding. -->
|
||||
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
|
||||
<item name="android:windowBackground">?android:colorBackground</item>
|
||||
</style>
|
||||
</resources>
|
||||
13
app/android/app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<network-security-config>
|
||||
<domain-config cleartextTrafficPermitted="true">
|
||||
<!-- Allow cleartext traffic for local network addresses -->
|
||||
<domain includeSubdomains="true">192.168.180.10</domain>
|
||||
<domain includeSubdomains="true">192.168.0.0/16</domain>
|
||||
<domain includeSubdomains="true">10.0.0.0/8</domain>
|
||||
<domain includeSubdomains="true">10.0.2.2</domain>
|
||||
<domain includeSubdomains="true">172.16.0.0/12</domain>
|
||||
<domain includeSubdomains="true">localhost</domain>
|
||||
<domain includeSubdomains="true">127.0.0.1</domain>
|
||||
</domain-config>
|
||||
</network-security-config>
|
||||
7
app/android/app/src/profile/AndroidManifest.xml
Normal file
@@ -0,0 +1,7 @@
|
||||
<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>
|
||||
21
app/android/build.gradle.kts
Normal file
@@ -0,0 +1,21 @@
|
||||
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)
|
||||
}
|
||||
3
app/android/gradle.properties
Normal file
@@ -0,0 +1,3 @@
|
||||
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
|
||||
android.useAndroidX=true
|
||||
android.enableJetifier=true
|
||||
5
app/android/gradle/wrapper/gradle-wrapper.properties
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-all.zip
|
||||
25
app/android/settings.gradle.kts
Normal file
@@ -0,0 +1,25 @@
|
||||
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.7.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
||||
}
|
||||
|
||||
include(":app")
|
||||
3
app/devtools_options.yaml
Normal file
@@ -0,0 +1,3 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
34
app/ios/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
||||
**/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
|
||||
26
app/ios/Flutter/AppFrameworkInfo.plist
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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>
|
||||
<key>MinimumOSVersion</key>
|
||||
<string>12.0</string>
|
||||
</dict>
|
||||
</plist>
|
||||
2
app/ios/Flutter/Debug.xcconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
2
app/ios/Flutter/Release.xcconfig
Normal file
@@ -0,0 +1,2 @@
|
||||
#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
|
||||
#include "Generated.xcconfig"
|
||||
43
app/ios/Podfile
Normal file
@@ -0,0 +1,43 @@
|
||||
# Uncomment this line to define a global platform for your project
|
||||
# platform :ios, '12.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
|
||||
198
app/ios/Podfile.lock
Normal file
@@ -0,0 +1,198 @@
|
||||
PODS:
|
||||
- camera_avfoundation (0.0.1):
|
||||
- Flutter
|
||||
- DKImagePickerController/Core (4.3.9):
|
||||
- DKImagePickerController/ImageDataManager
|
||||
- DKImagePickerController/Resource
|
||||
- DKImagePickerController/ImageDataManager (4.3.9)
|
||||
- DKImagePickerController/PhotoGallery (4.3.9):
|
||||
- DKImagePickerController/Core
|
||||
- DKPhotoGallery
|
||||
- DKImagePickerController/Resource (4.3.9)
|
||||
- DKPhotoGallery (0.0.19):
|
||||
- DKPhotoGallery/Core (= 0.0.19)
|
||||
- DKPhotoGallery/Model (= 0.0.19)
|
||||
- DKPhotoGallery/Preview (= 0.0.19)
|
||||
- DKPhotoGallery/Resource (= 0.0.19)
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Core (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Preview
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Model (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Preview (0.0.19):
|
||||
- DKPhotoGallery/Model
|
||||
- DKPhotoGallery/Resource
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- DKPhotoGallery/Resource (0.0.19):
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
- file_picker (0.0.1):
|
||||
- DKImagePickerController/PhotoGallery
|
||||
- Flutter
|
||||
- file_selector_ios (0.0.1):
|
||||
- Flutter
|
||||
- Flutter (1.0.0)
|
||||
- GoogleDataTransport (9.4.1):
|
||||
- GoogleUtilities/Environment (~> 7.7)
|
||||
- nanopb (< 2.30911.0, >= 2.30908.0)
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- GoogleMLKit/BarcodeScanning (6.0.0):
|
||||
- GoogleMLKit/MLKitCore
|
||||
- MLKitBarcodeScanning (~> 5.0.0)
|
||||
- GoogleMLKit/MLKitCore (6.0.0):
|
||||
- MLKitCommon (~> 11.0.0)
|
||||
- GoogleToolboxForMac/Defines (4.2.1)
|
||||
- GoogleToolboxForMac/Logger (4.2.1):
|
||||
- GoogleToolboxForMac/Defines (= 4.2.1)
|
||||
- "GoogleToolboxForMac/NSData+zlib (4.2.1)":
|
||||
- GoogleToolboxForMac/Defines (= 4.2.1)
|
||||
- GoogleUtilities/Environment (7.13.3):
|
||||
- GoogleUtilities/Privacy
|
||||
- PromisesObjC (< 3.0, >= 1.2)
|
||||
- GoogleUtilities/Logger (7.13.3):
|
||||
- GoogleUtilities/Environment
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilities/Privacy (7.13.3)
|
||||
- GoogleUtilities/UserDefaults (7.13.3):
|
||||
- GoogleUtilities/Logger
|
||||
- GoogleUtilities/Privacy
|
||||
- GoogleUtilitiesComponents (1.1.0):
|
||||
- GoogleUtilities/Logger
|
||||
- GTMSessionFetcher/Core (3.5.0)
|
||||
- MLImage (1.0.0-beta5)
|
||||
- MLKitBarcodeScanning (5.0.0):
|
||||
- MLKitCommon (~> 11.0)
|
||||
- MLKitVision (~> 7.0)
|
||||
- MLKitCommon (11.0.0):
|
||||
- GoogleDataTransport (< 10.0, >= 9.4.1)
|
||||
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
||||
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
||||
- GoogleUtilities/UserDefaults (< 8.0, >= 7.13.0)
|
||||
- GoogleUtilitiesComponents (~> 1.0)
|
||||
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
||||
- MLKitVision (7.0.0):
|
||||
- GoogleToolboxForMac/Logger (< 5.0, >= 4.2.1)
|
||||
- "GoogleToolboxForMac/NSData+zlib (< 5.0, >= 4.2.1)"
|
||||
- GTMSessionFetcher/Core (< 4.0, >= 3.3.2)
|
||||
- MLImage (= 1.0.0-beta5)
|
||||
- MLKitCommon (~> 11.0)
|
||||
- mobile_scanner (5.2.3):
|
||||
- Flutter
|
||||
- GoogleMLKit/BarcodeScanning (~> 6.0.0)
|
||||
- nanopb (2.30910.0):
|
||||
- nanopb/decode (= 2.30910.0)
|
||||
- nanopb/encode (= 2.30910.0)
|
||||
- nanopb/decode (2.30910.0)
|
||||
- nanopb/encode (2.30910.0)
|
||||
- ObjectBox (4.4.1)
|
||||
- objectbox_flutter_libs (0.0.1):
|
||||
- Flutter
|
||||
- ObjectBox (= 4.4.1)
|
||||
- package_info_plus (0.4.5):
|
||||
- Flutter
|
||||
- path_provider_foundation (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
- PromisesObjC (2.4.0)
|
||||
- SDWebImage (5.21.5):
|
||||
- SDWebImage/Core (= 5.21.5)
|
||||
- SDWebImage/Core (5.21.5)
|
||||
- SwiftyGif (5.4.5)
|
||||
- url_launcher_ios (0.0.1):
|
||||
- Flutter
|
||||
- webview_flutter_wkwebview (0.0.1):
|
||||
- Flutter
|
||||
- FlutterMacOS
|
||||
|
||||
DEPENDENCIES:
|
||||
- camera_avfoundation (from `.symlinks/plugins/camera_avfoundation/ios`)
|
||||
- file_picker (from `.symlinks/plugins/file_picker/ios`)
|
||||
- file_selector_ios (from `.symlinks/plugins/file_selector_ios/ios`)
|
||||
- Flutter (from `Flutter`)
|
||||
- mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`)
|
||||
- objectbox_flutter_libs (from `.symlinks/plugins/objectbox_flutter_libs/ios`)
|
||||
- package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
|
||||
- path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`)
|
||||
- url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
|
||||
- webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/darwin`)
|
||||
|
||||
SPEC REPOS:
|
||||
trunk:
|
||||
- DKImagePickerController
|
||||
- DKPhotoGallery
|
||||
- GoogleDataTransport
|
||||
- GoogleMLKit
|
||||
- GoogleToolboxForMac
|
||||
- GoogleUtilities
|
||||
- GoogleUtilitiesComponents
|
||||
- GTMSessionFetcher
|
||||
- MLImage
|
||||
- MLKitBarcodeScanning
|
||||
- MLKitCommon
|
||||
- MLKitVision
|
||||
- nanopb
|
||||
- ObjectBox
|
||||
- PromisesObjC
|
||||
- SDWebImage
|
||||
- SwiftyGif
|
||||
|
||||
EXTERNAL SOURCES:
|
||||
camera_avfoundation:
|
||||
:path: ".symlinks/plugins/camera_avfoundation/ios"
|
||||
file_picker:
|
||||
:path: ".symlinks/plugins/file_picker/ios"
|
||||
file_selector_ios:
|
||||
:path: ".symlinks/plugins/file_selector_ios/ios"
|
||||
Flutter:
|
||||
:path: Flutter
|
||||
mobile_scanner:
|
||||
:path: ".symlinks/plugins/mobile_scanner/ios"
|
||||
objectbox_flutter_libs:
|
||||
:path: ".symlinks/plugins/objectbox_flutter_libs/ios"
|
||||
package_info_plus:
|
||||
:path: ".symlinks/plugins/package_info_plus/ios"
|
||||
path_provider_foundation:
|
||||
:path: ".symlinks/plugins/path_provider_foundation/darwin"
|
||||
url_launcher_ios:
|
||||
:path: ".symlinks/plugins/url_launcher_ios/ios"
|
||||
webview_flutter_wkwebview:
|
||||
:path: ".symlinks/plugins/webview_flutter_wkwebview/darwin"
|
||||
|
||||
SPEC CHECKSUMS:
|
||||
camera_avfoundation: be3be85408cd4126f250386828e9b1dfa40ab436
|
||||
DKImagePickerController: 946cec48c7873164274ecc4624d19e3da4c1ef3c
|
||||
DKPhotoGallery: b3834fecb755ee09a593d7c9e389d8b5d6deed60
|
||||
file_picker: a0560bc09d61de87f12d246fc47d2119e6ef37be
|
||||
file_selector_ios: f92e583d43608aebc2e4a18daac30b8902845502
|
||||
Flutter: e0871f40cf51350855a761d2e70bf5af5b9b5de7
|
||||
GoogleDataTransport: 6c09b596d841063d76d4288cc2d2f42cc36e1e2a
|
||||
GoogleMLKit: 97ac7af399057e99182ee8edfa8249e3226a4065
|
||||
GoogleToolboxForMac: d1a2cbf009c453f4d6ded37c105e2f67a32206d8
|
||||
GoogleUtilities: ea963c370a38a8069cc5f7ba4ca849a60b6d7d15
|
||||
GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe
|
||||
GTMSessionFetcher: 5aea5ba6bd522a239e236100971f10cb71b96ab6
|
||||
MLImage: 1824212150da33ef225fbd3dc49f184cf611046c
|
||||
MLKitBarcodeScanning: 10ca0845a6d15f2f6e911f682a1998b68b973e8b
|
||||
MLKitCommon: afec63980417d29ffbb4790529a1b0a2291699e1
|
||||
MLKitVision: e858c5f125ecc288e4a31127928301eaba9ae0c1
|
||||
mobile_scanner: 92e8812bf22a8f84131e2a7f9d0f44dad1a4742b
|
||||
nanopb: 438bc412db1928dac798aa6fd75726007be04262
|
||||
ObjectBox: 7da4aceb5013d041bfafdbc6d744a26918b09757
|
||||
objectbox_flutter_libs: 09b1dec1b4cd27bf1a5f9bae7ccaa7e43588bf31
|
||||
package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
|
||||
path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564
|
||||
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
|
||||
SDWebImage: e9c98383c7572d713c1a0d7dd2783b10599b9838
|
||||
SwiftyGif: 706c60cf65fa2bc5ee0313beece843c8eb8194d4
|
||||
url_launcher_ios: 694010445543906933d732453a59da0a173ae33d
|
||||
webview_flutter_wkwebview: 1821ceac936eba6f7984d89a9f3bcb4dea99ebb2
|
||||
|
||||
PODFILE CHECKSUM: 4305caec6b40dde0ae97be1573c53de1882a07e5
|
||||
|
||||
COCOAPODS: 1.16.2
|
||||
746
app/ios/Runner.xcodeproj/project.pbxproj
Normal file
@@ -0,0 +1,746 @@
|
||||
// !$*UTF8*$!
|
||||
{
|
||||
archiveVersion = 1;
|
||||
classes = {
|
||||
};
|
||||
objectVersion = 54;
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };
|
||||
2510A7C53652D94A0BD1AC0E /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B1894F93A282ED106CA1628A /* Pods_RunnerTests.framework */; };
|
||||
331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; };
|
||||
3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
7750814138033D7B5089EA79 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6A466C5081D4AFB2E83EE48C /* Pods_Runner.framework */; };
|
||||
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 */
|
||||
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>"; };
|
||||
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; };
|
||||
3A0BB33DF424113620931648 /* 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>"; };
|
||||
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; };
|
||||
48563AD38CD52F6EB5981C45 /* 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>"; };
|
||||
6352F6ED7E5B9DC92334C090 /* 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>"; };
|
||||
6A466C5081D4AFB2E83EE48C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
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>"; };
|
||||
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>"; };
|
||||
97E626E31BDD11C098C2470E /* 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>"; };
|
||||
A042B5F29AFF592248DD7D50 /* 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>"; };
|
||||
AB23306D1B7EEAFBF39838CE /* 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>"; };
|
||||
B1894F93A282ED106CA1628A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
/* End PBXFileReference section */
|
||||
|
||||
/* Begin PBXFrameworksBuildPhase section */
|
||||
353CB3693FF9EBD22F03DEB9 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
2510A7C53652D94A0BD1AC0E /* Pods_RunnerTests.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
97C146EB1CF9000F007C117D /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
7750814138033D7B5089EA79 /* Pods_Runner.framework in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
2AC4A5BBFC8A63F231B5394E /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
A042B5F29AFF592248DD7D50 /* Pods-Runner.debug.xcconfig */,
|
||||
6352F6ED7E5B9DC92334C090 /* Pods-Runner.release.xcconfig */,
|
||||
48563AD38CD52F6EB5981C45 /* Pods-Runner.profile.xcconfig */,
|
||||
AB23306D1B7EEAFBF39838CE /* Pods-RunnerTests.debug.xcconfig */,
|
||||
97E626E31BDD11C098C2470E /* Pods-RunnerTests.release.xcconfig */,
|
||||
3A0BB33DF424113620931648 /* Pods-RunnerTests.profile.xcconfig */,
|
||||
);
|
||||
name = Pods;
|
||||
path = Pods;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
331C8082294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
331C807B294A618700263BE5 /* RunnerTests.swift */,
|
||||
);
|
||||
path = RunnerTests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
511A3FF81F794991B7581FB8 /* Frameworks */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
6A466C5081D4AFB2E83EE48C /* Pods_Runner.framework */,
|
||||
B1894F93A282ED106CA1628A /* Pods_RunnerTests.framework */,
|
||||
);
|
||||
name = Frameworks;
|
||||
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 */,
|
||||
2AC4A5BBFC8A63F231B5394E /* Pods */,
|
||||
511A3FF81F794991B7581FB8 /* 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 */,
|
||||
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,
|
||||
);
|
||||
path = Runner;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXGroup section */
|
||||
|
||||
/* Begin PBXNativeTarget section */
|
||||
331C8080294A63A400263BE5 /* RunnerTests */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */;
|
||||
buildPhases = (
|
||||
34F233ACE4BA3F5B820F795F /* [CP] Check Pods Manifest.lock */,
|
||||
331C807D294A63A400263BE5 /* Sources */,
|
||||
331C807F294A63A400263BE5 /* Resources */,
|
||||
353CB3693FF9EBD22F03DEB9 /* 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 = (
|
||||
B808696A3E30F0EC4B8DF6F5 /* [CP] Check Pods Manifest.lock */,
|
||||
9740EEB61CF901F6004384FC /* Run Script */,
|
||||
97C146EA1CF9000F007C117D /* Sources */,
|
||||
97C146EB1CF9000F007C117D /* Frameworks */,
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
70582023162C4E4C0B4CC063 /* [CP] Embed Pods Frameworks */,
|
||||
DE505B8273E1DC801F9E27AD /* [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 */
|
||||
34F233ACE4BA3F5B820F795F /* [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;
|
||||
};
|
||||
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";
|
||||
};
|
||||
70582023162C4E4C0B4CC063 /* [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;
|
||||
};
|
||||
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";
|
||||
};
|
||||
B808696A3E30F0EC4B8DF6F5 /* [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;
|
||||
};
|
||||
DE505B8273E1DC801F9E27AD /* [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;
|
||||
};
|
||||
/* 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 */,
|
||||
);
|
||||
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 = 12.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;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.assecutor.votianltApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Profile;
|
||||
};
|
||||
331C8088294A63A400263BE5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = AB23306D1B7EEAFBF39838CE /* 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 = de.assecutor.votianltApp.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 = 97E626E31BDD11C098C2470E /* 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 = de.assecutor.votianltApp.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 = 3A0BB33DF424113620931648 /* 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 = de.assecutor.votianltApp.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 = 12.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 = 12.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;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.assecutor.votianltApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
VERSIONING_SYSTEM = "apple-generic";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
97C147071CF9000F007C117D /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CURRENT_PROJECT_VERSION = "$(FLUTTER_BUILD_NUMBER)";
|
||||
ENABLE_BITCODE = NO;
|
||||
INFOPLIST_FILE = Runner/Info.plist;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
PRODUCT_BUNDLE_IDENTIFIER = de.assecutor.votianltApp;
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
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 */;
|
||||
}
|
||||
7
app/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Workspace
|
||||
version = "1.0">
|
||||
<FileRef
|
||||
location = "self:">
|
||||
</FileRef>
|
||||
</Workspace>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,99 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1510"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<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"
|
||||
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"
|
||||
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>
|
||||
10
app/ios/Runner.xcworkspace/contents.xcworkspacedata
generated
Normal file
@@ -0,0 +1,10 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
@@ -0,0 +1,8 @@
|
||||
<?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>
|
||||
13
app/ios/Runner/AppDelegate.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
import Flutter
|
||||
import UIKit
|
||||
|
||||
@main
|
||||
@objc class AppDelegate: FlutterAppDelegate {
|
||||
override func application(
|
||||
_ application: UIApplication,
|
||||
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
|
||||
) -> Bool {
|
||||
GeneratedPluginRegistrant.register(with: self)
|
||||
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
||||
}
|
||||
}
|
||||
122
app/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
@@ -0,0 +1,122 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 11 KiB |
|
After Width: | Height: | Size: 295 B |
|
After Width: | Height: | Size: 406 B |
|
After Width: | Height: | Size: 450 B |
|
After Width: | Height: | Size: 282 B |
|
After Width: | Height: | Size: 462 B |
|
After Width: | Height: | Size: 704 B |
|
After Width: | Height: | Size: 406 B |
|
After Width: | Height: | Size: 586 B |
|
After Width: | Height: | Size: 862 B |
|
After Width: | Height: | Size: 862 B |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 762 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.4 KiB |
23
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
BIN
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage.png
vendored
Normal file
|
After Width: | Height: | Size: 68 B |
BIN
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@2x.png
vendored
Normal file
|
After Width: | Height: | Size: 68 B |
BIN
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/LaunchImage@3x.png
vendored
Normal file
|
After Width: | Height: | Size: 68 B |
5
app/ios/Runner/Assets.xcassets/LaunchImage.imageset/README.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
# 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.
|
||||
37
app/ios/Runner/Base.lproj/LaunchScreen.storyboard
Normal file
@@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="12121" systemVersion="16G29" 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="12089"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<scene sceneID="EHf-IW-A2E">
|
||||
<objects>
|
||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="Ydg-fD-yQy"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="xbc-2k-c8Z"/>
|
||||
</layoutGuides>
|
||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<imageView opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" image="LaunchImage" translatesAutoresizingMaskIntoConstraints="NO" id="YRO-k0-Ey4">
|
||||
</imageView>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" 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>
|
||||
26
app/ios/Runner/Base.lproj/Main.storyboard
Normal file
@@ -0,0 +1,26 @@
|
||||
<?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>
|
||||
68
app/ios/Runner/Info.plist
Normal file
@@ -0,0 +1,68 @@
|
||||
<?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>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Votianlt App</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>votianlt_app</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>${FLUTTER_BUILD_NAME:1.0.0}</string>
|
||||
<key>CFBundleSignature</key>
|
||||
<string>????</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>${FLUTTER_BUILD_NUMBER:1}</string>
|
||||
<key>LSRequiresIPhoneOS</key>
|
||||
<true/>
|
||||
<key>UILaunchStoryboardName</key>
|
||||
<string>LaunchScreen</string>
|
||||
<key>UIMainStoryboardFile</key>
|
||||
<string>Main</string>
|
||||
<key>UISupportedInterfaceOrientations</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||
<array>
|
||||
<string>UIInterfaceOrientationPortrait</string>
|
||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||
</array>
|
||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||
<true/>
|
||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||
<true/>
|
||||
<!-- Network configuration for STOMP WebSocket connections -->
|
||||
<key>NSAppTransportSecurity</key>
|
||||
<dict>
|
||||
<key>NSAllowsArbitraryLoads</key>
|
||||
<true/>
|
||||
<key>NSAllowsLocalNetworking</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<key>NSLocalNetworkUsageDescription</key>
|
||||
<string>This app needs to connect to local network services for STOMP messaging.</string>
|
||||
<key>NSCameraUsageDescription</key>
|
||||
<string>This app needs access to camera to take photos for task completion.</string>
|
||||
<key>NSPhotoLibraryUsageDescription</key>
|
||||
<string>This app needs access to photo library to save and manage task photos.</string>
|
||||
<!-- GPS Location permissions -->
|
||||
<key>NSLocationWhenInUseUsageDescription</key>
|
||||
<string>This app needs access to your location to track delivery routes.</string>
|
||||
<key>NSLocationAlwaysUsageDescription</key>
|
||||
<string>This app needs access to your location to track delivery routes even when in the background.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
1
app/ios/Runner/Runner-Bridging-Header.h
Normal file
@@ -0,0 +1 @@
|
||||
#import "GeneratedPluginRegistrant.h"
|
||||
12
app/ios/RunnerTests/RunnerTests.swift
Normal file
@@ -0,0 +1,12 @@
|
||||
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.
|
||||
}
|
||||
|
||||
}
|
||||
173
app/lib/app_state.dart
Normal file
@@ -0,0 +1,173 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'models/job.dart';
|
||||
import 'services/database_service.dart';
|
||||
import 'services/dart_mq.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
|
||||
/// Global notifier for language changes
|
||||
final ValueNotifier<Locale> localeNotifier = ValueNotifier<Locale>(const Locale('de'));
|
||||
|
||||
class AppState {
|
||||
static final AppState _instance = AppState._internal();
|
||||
factory AppState() => _instance;
|
||||
AppState._internal();
|
||||
|
||||
String? _loggedInEmail;
|
||||
List<Job> _assignedJobs = [];
|
||||
final DatabaseService _databaseService = DatabaseService();
|
||||
|
||||
// Language settings
|
||||
String _languageCode = 'de';
|
||||
String get languageCode => _languageCode;
|
||||
|
||||
/// Get current locale
|
||||
Locale get currentLocale => Locale(_languageCode);
|
||||
|
||||
/// Set language and update the global notifier
|
||||
Future<void> setLanguage(String languageCode) async {
|
||||
if (supportedLanguageCodes.contains(languageCode)) {
|
||||
_languageCode = languageCode;
|
||||
await _databaseService.saveLanguagePreference(languageCode);
|
||||
localeNotifier.value = Locale(languageCode);
|
||||
}
|
||||
}
|
||||
|
||||
/// Load language preference from database
|
||||
Future<void> loadLanguagePreference() async {
|
||||
final savedLanguage = await _databaseService.loadLanguagePreference();
|
||||
if (savedLanguage != null && supportedLanguageCodes.contains(savedLanguage)) {
|
||||
_languageCode = savedLanguage;
|
||||
localeNotifier.value = Locale(savedLanguage);
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize persistence to avoid overlapping DB save/load cycles
|
||||
bool _isPersistingJobs = false;
|
||||
List<Job>? _pendingJobs; // holds the latest jobs to persist if calls overlap
|
||||
|
||||
// Jobs update notification (emitted once after DB was updated)
|
||||
final StreamController<void> _jobsUpdatedController = StreamController<void>.broadcast();
|
||||
Stream<void> get jobsUpdated => _jobsUpdatedController.stream;
|
||||
|
||||
/// The logged-in user's email (used as local identifier for chats)
|
||||
String? get loggedInEmail => _loggedInEmail;
|
||||
|
||||
List<Job> get assignedJobs => List.unmodifiable(_assignedJobs);
|
||||
|
||||
void setLoggedInEmail(String email) {
|
||||
_loggedInEmail = email;
|
||||
}
|
||||
|
||||
Future<void> clearLogin() async {
|
||||
_loggedInEmail = null;
|
||||
_assignedJobs.clear();
|
||||
// Clear database
|
||||
await _databaseService.clearAllData();
|
||||
// Notify listeners/UI that jobs were cleared
|
||||
_jobsUpdatedController.add(null);
|
||||
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
|
||||
}
|
||||
|
||||
bool get isLoggedIn => _loggedInEmail != null;
|
||||
|
||||
Future<void> setAssignedJobs(List<Job> jobs) async {
|
||||
// Coalesce overlapping calls: if a persist is already running, remember only the latest
|
||||
if (_isPersistingJobs) {
|
||||
_pendingJobs = jobs;
|
||||
return;
|
||||
}
|
||||
|
||||
_isPersistingJobs = true;
|
||||
try {
|
||||
// Start with the initial batch to persist
|
||||
var toPersist = jobs;
|
||||
while (true) {
|
||||
// Normalize first
|
||||
final normalized = toPersist.map((j) => j.normalized()).toList();
|
||||
|
||||
// Persist normalized list to DB only (no UI notifications here)
|
||||
await _databaseService.saveJobs(normalized);
|
||||
|
||||
// If another request came in during persistence, handle only the latest once
|
||||
if (_pendingJobs != null) {
|
||||
toPersist = _pendingJobs!;
|
||||
_pendingJobs = null;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
// After DB is updated with the latest data, notify listeners once to refresh UI
|
||||
_jobsUpdatedController.add(null);
|
||||
// Also publish via dart_mq for app-wide decoupled messaging
|
||||
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
|
||||
} finally {
|
||||
_isPersistingJobs = false;
|
||||
}
|
||||
}
|
||||
|
||||
void addJob(Job job) {
|
||||
if (!_assignedJobs.contains(job)) {
|
||||
_assignedJobs.add(job);
|
||||
}
|
||||
}
|
||||
|
||||
void removeJob(String jobId) {
|
||||
_assignedJobs.removeWhere((job) => job.id == jobId);
|
||||
// Update database
|
||||
_databaseService.saveJobs(_assignedJobs);
|
||||
}
|
||||
|
||||
/// Delete a job by ID (called when server sends job_deleted event)
|
||||
Future<void> deleteJob(String jobId) async {
|
||||
_assignedJobs.removeWhere((job) => job.id == jobId);
|
||||
// Delete from database
|
||||
await _databaseService.deleteJob(jobId);
|
||||
// Notify listeners
|
||||
_jobsUpdatedController.add(null);
|
||||
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
|
||||
}
|
||||
|
||||
/// Add a new job (called when server sends job_created event)
|
||||
Future<void> addNewJob(Job job) async {
|
||||
// Check if job already exists
|
||||
if (_assignedJobs.any((j) => j.id == job.id)) {
|
||||
return;
|
||||
}
|
||||
// Add to memory
|
||||
_assignedJobs.insert(0, job);
|
||||
// Persist to database
|
||||
await _databaseService.saveOrUpdateJob(job);
|
||||
// Notify listeners
|
||||
_jobsUpdatedController.add(null);
|
||||
DartMQ().publish<void>(MQTopics.jobsUpdated, null);
|
||||
}
|
||||
|
||||
/// Load login state from saved credentials on app start
|
||||
Future<void> loadLoginFromDatabase() async {
|
||||
final credentials = await _databaseService.loadCredentials();
|
||||
if (credentials != null) {
|
||||
_loggedInEmail = credentials.email;
|
||||
}
|
||||
}
|
||||
|
||||
void updateJob(Job updatedJob) {
|
||||
final index = _assignedJobs.indexWhere((job) => job.id == updatedJob.id);
|
||||
if (index != -1) {
|
||||
_assignedJobs[index] = updatedJob;
|
||||
}
|
||||
}
|
||||
|
||||
/// Refresh in-memory jobs from the database without emitting other notifications
|
||||
Future<void> refreshJobsFromDatabase() async {
|
||||
final jobs = await _databaseService.loadJobs();
|
||||
_assignedJobs = jobs;
|
||||
}
|
||||
|
||||
/// Persistently upsert a single job and refresh in-memory list
|
||||
Future<void> upsertJob(Job job) async {
|
||||
await _databaseService.saveOrUpdateJob(job);
|
||||
final persisted = await _databaseService.loadJobs();
|
||||
_assignedJobs = persisted.isNotEmpty ? persisted : _assignedJobs;
|
||||
}
|
||||
}
|
||||
175
app/lib/app_theme.dart
Normal file
@@ -0,0 +1,175 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
class AppColors {
|
||||
static const Color primary = Color(0xFF2563EB);
|
||||
static const Color primaryStrong = Color(0xFF1D4ED8);
|
||||
static const Color primarySoft = Color(0xFFE8F0FF);
|
||||
static const Color secondary = Color(0xFF0F4C5C);
|
||||
static const Color secondarySoft = Color(0xFFDDEEF2);
|
||||
static const Color success = Color(0xFF059669);
|
||||
static const Color successSoft = Color(0xFFE7F6F1);
|
||||
static const Color warning = Color(0xFFD97706);
|
||||
static const Color warningSoft = Color(0xFFFFF4E5);
|
||||
static const Color danger = Color(0xFFDC2626);
|
||||
static const Color dangerSoft = Color(0xFFFDECEC);
|
||||
static const Color surface = Color(0xFFFFFFFF);
|
||||
static const Color surfaceMuted = Color(0xFFF7FAFF);
|
||||
static const Color scaffold = Color(0xFFF5F7FB);
|
||||
static const Color scaffoldAccent = Color(0xFFEEF4FF);
|
||||
static const Color border = Color(0xFFD6DDE7);
|
||||
static const Color borderStrong = Color(0xFFC6D0DD);
|
||||
static const Color text = Color(0xFF1E293B);
|
||||
static const Color textStrong = Color(0xFF0F172A);
|
||||
static const Color textMuted = Color(0xFF64748B);
|
||||
}
|
||||
|
||||
class AppGradients {
|
||||
static const LinearGradient shellBackground = LinearGradient(
|
||||
begin: Alignment.topLeft,
|
||||
end: Alignment.bottomRight,
|
||||
colors: [
|
||||
AppColors.scaffoldAccent,
|
||||
AppColors.surfaceMuted,
|
||||
AppColors.scaffold,
|
||||
],
|
||||
stops: [0, 0.45, 1],
|
||||
);
|
||||
}
|
||||
|
||||
ThemeData buildAppTheme() {
|
||||
final colorScheme = ColorScheme.fromSeed(
|
||||
seedColor: AppColors.primary,
|
||||
brightness: Brightness.light,
|
||||
).copyWith(
|
||||
primary: AppColors.primary,
|
||||
onPrimary: Colors.white,
|
||||
primaryContainer: AppColors.primarySoft,
|
||||
onPrimaryContainer: AppColors.primaryStrong,
|
||||
secondary: AppColors.secondary,
|
||||
onSecondary: Colors.white,
|
||||
secondaryContainer: AppColors.secondarySoft,
|
||||
onSecondaryContainer: AppColors.secondary,
|
||||
tertiary: AppColors.success,
|
||||
onTertiary: Colors.white,
|
||||
tertiaryContainer: AppColors.successSoft,
|
||||
onTertiaryContainer: AppColors.success,
|
||||
surface: AppColors.surface,
|
||||
onSurface: AppColors.textStrong,
|
||||
onSurfaceVariant: AppColors.textMuted,
|
||||
outline: AppColors.border,
|
||||
surfaceTint: Colors.transparent,
|
||||
error: AppColors.danger,
|
||||
onError: Colors.white,
|
||||
);
|
||||
final baseTheme = ThemeData(useMaterial3: true, colorScheme: colorScheme);
|
||||
const radius = Radius.circular(14);
|
||||
final border = OutlineInputBorder(
|
||||
borderRadius: const BorderRadius.all(radius),
|
||||
borderSide: const BorderSide(color: AppColors.border),
|
||||
);
|
||||
|
||||
return baseTheme.copyWith(
|
||||
scaffoldBackgroundColor: AppColors.scaffold,
|
||||
canvasColor: AppColors.scaffold,
|
||||
textTheme: baseTheme.textTheme
|
||||
.apply(bodyColor: AppColors.text, displayColor: AppColors.textStrong)
|
||||
.copyWith(
|
||||
headlineMedium: baseTheme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textStrong,
|
||||
),
|
||||
titleLarge: baseTheme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: AppColors.textStrong,
|
||||
),
|
||||
bodyLarge: baseTheme.textTheme.bodyLarge?.copyWith(
|
||||
color: AppColors.text,
|
||||
),
|
||||
bodyMedium: baseTheme.textTheme.bodyMedium?.copyWith(
|
||||
color: AppColors.text,
|
||||
),
|
||||
),
|
||||
appBarTheme: const AppBarTheme(
|
||||
backgroundColor: AppColors.primarySoft,
|
||||
foregroundColor: AppColors.primaryStrong,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
),
|
||||
cardTheme: const CardThemeData(
|
||||
color: AppColors.surface,
|
||||
surfaceTintColor: Colors.transparent,
|
||||
elevation: 0,
|
||||
margin: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.all(Radius.circular(18)),
|
||||
side: BorderSide(color: AppColors.border),
|
||||
),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(
|
||||
color: AppColors.border,
|
||||
space: 1,
|
||||
thickness: 1,
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
filled: true,
|
||||
fillColor: AppColors.surface,
|
||||
labelStyle: const TextStyle(color: AppColors.textMuted),
|
||||
hintStyle: const TextStyle(color: AppColors.textMuted),
|
||||
prefixIconColor: AppColors.textMuted,
|
||||
suffixIconColor: AppColors.textMuted,
|
||||
border: border,
|
||||
enabledBorder: border,
|
||||
focusedBorder: border.copyWith(
|
||||
borderSide: const BorderSide(color: AppColors.primary, width: 1.5),
|
||||
),
|
||||
errorBorder: border.copyWith(
|
||||
borderSide: const BorderSide(color: AppColors.danger),
|
||||
),
|
||||
focusedErrorBorder: border.copyWith(
|
||||
borderSide: const BorderSide(color: AppColors.danger, width: 1.5),
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
disabledBackgroundColor: AppColors.border,
|
||||
disabledForegroundColor: AppColors.textMuted,
|
||||
elevation: 0,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
foregroundColor: AppColors.primaryStrong,
|
||||
side: const BorderSide(color: AppColors.border),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 14),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: AppColors.primaryStrong,
|
||||
textStyle: const TextStyle(fontWeight: FontWeight.w600),
|
||||
),
|
||||
),
|
||||
snackBarTheme: SnackBarThemeData(
|
||||
behavior: SnackBarBehavior.floating,
|
||||
backgroundColor: AppColors.textStrong,
|
||||
contentTextStyle: baseTheme.textTheme.bodyMedium?.copyWith(
|
||||
color: Colors.white,
|
||||
),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
badgeTheme: const BadgeThemeData(
|
||||
backgroundColor: AppColors.primary,
|
||||
textColor: Colors.white,
|
||||
),
|
||||
progressIndicatorTheme: const ProgressIndicatorThemeData(
|
||||
color: AppColors.primary,
|
||||
),
|
||||
listTileTheme: const ListTileThemeData(iconColor: AppColors.textMuted),
|
||||
);
|
||||
}
|
||||
443
app/lib/cargo_items_view.dart
Normal file
@@ -0,0 +1,443 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'app_theme.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
import 'l10n/localization_helpers.dart';
|
||||
import 'models/delivery_station.dart';
|
||||
import 'models/job.dart';
|
||||
import 'services/database_service.dart';
|
||||
import 'task_view.dart';
|
||||
import 'widgets/offline_banner.dart';
|
||||
|
||||
@visibleForTesting
|
||||
Color? deliveryStationCardBackgroundColor(
|
||||
DeliveryStation station,
|
||||
Map<String, bool> taskStatuses,
|
||||
) {
|
||||
if (station.tasks.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final isCompleted = station.tasks.every(
|
||||
(task) => taskStatuses[task.id] ?? task.completed,
|
||||
);
|
||||
return isCompleted ? AppColors.successSoft : null;
|
||||
}
|
||||
|
||||
class CargoItemsView extends StatefulWidget {
|
||||
final Job job;
|
||||
|
||||
const CargoItemsView({super.key, required this.job});
|
||||
|
||||
@override
|
||||
State<CargoItemsView> createState() => _CargoItemsViewState();
|
||||
}
|
||||
|
||||
class _CargoItemsViewState extends State<CargoItemsView> {
|
||||
final DatabaseService _databaseService = DatabaseService();
|
||||
Map<String, bool> _taskStatuses = const {};
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_loadLocalTaskStatuses();
|
||||
}
|
||||
|
||||
Future<void> _loadLocalTaskStatuses() async {
|
||||
final map = await _databaseService.loadAllTaskStatuses();
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_taskStatuses = map;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Text(widget.job.jobNumber),
|
||||
leading: IconButton(
|
||||
icon: const Icon(Icons.arrow_back),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.chat),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pushNamed('/chats');
|
||||
},
|
||||
tooltip: AppLocalizations.of(context).openChat,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
OfflineBanner(),
|
||||
// Main content area
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Job summary card
|
||||
Card(
|
||||
margin: const EdgeInsets.only(bottom: 16),
|
||||
elevation: 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
widget.job.jobNumber.isNotEmpty
|
||||
? widget.job.jobNumber
|
||||
: localizeKnownText(context, widget.job.title),
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (widget.job.customerSelection.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
widget.job.customerSelection,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
color: Colors.grey[700],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 8),
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.arrow_upward,
|
||||
size: 16,
|
||||
color: Colors.green[600],
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
widget.job.pickupCity,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(
|
||||
Icons.arrow_forward,
|
||||
size: 16,
|
||||
color: Colors.grey[600],
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Icon(
|
||||
Icons.arrow_downward,
|
||||
size: 16,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
widget.job.deliveryCitiesDisplay.isNotEmpty
|
||||
? widget.job.deliveryCitiesDisplay
|
||||
: widget.job.deliveryCity,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Delivery stations section header
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.local_shipping_outlined,
|
||||
size: 24,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
l10n.deliveryStationsCount(_deliveryStations.length),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(child: _buildDeliveryStationsList()),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
List<DeliveryStation> get _deliveryStations {
|
||||
if (widget.job.deliveryStations.isNotEmpty) {
|
||||
return widget.job.deliveryStations;
|
||||
}
|
||||
|
||||
return [
|
||||
DeliveryStation(
|
||||
stationOrder: 0,
|
||||
company: widget.job.deliveryCompany,
|
||||
salutation: widget.job.deliverySalutation,
|
||||
firstName: widget.job.deliveryFirstName,
|
||||
lastName: widget.job.deliveryLastName,
|
||||
phone: widget.job.deliveryPhone,
|
||||
street: widget.job.deliveryStreet,
|
||||
houseNumber: widget.job.deliveryHouseNumber,
|
||||
addressAddition: widget.job.deliveryAddressAddition,
|
||||
zip: widget.job.deliveryZip,
|
||||
city: widget.job.deliveryCity,
|
||||
deliveryDate: widget.job.deliveryDate,
|
||||
deliveryTime: widget.job.deliveryTime,
|
||||
tasks: widget.job.tasks,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
Widget _buildDeliveryStationsList() {
|
||||
if (_deliveryStations.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.local_shipping_outlined,
|
||||
size: 64,
|
||||
color: Colors.grey[400],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context).noDeliveryStations,
|
||||
style: TextStyle(fontSize: 16, color: Colors.grey[600]),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
AppLocalizations.of(context).noDeliveryStationsMessage,
|
||||
style: TextStyle(fontSize: 14, color: Colors.grey[500]),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: _deliveryStations.length,
|
||||
itemBuilder: (context, index) {
|
||||
final station = _deliveryStations[index];
|
||||
return _buildDeliveryStationCard(station);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDeliveryStationCard(DeliveryStation station) {
|
||||
final backgroundColor = deliveryStationCardBackgroundColor(
|
||||
station,
|
||||
_taskStatuses,
|
||||
);
|
||||
final title =
|
||||
station.displayName.isNotEmpty ? station.displayName : station.company;
|
||||
final subtitle =
|
||||
station.company.isNotEmpty && station.company != title
|
||||
? station.company
|
||||
: null;
|
||||
final l10n = AppLocalizations.of(context);
|
||||
final addressLines =
|
||||
<String>[
|
||||
[
|
||||
station.street,
|
||||
station.houseNumber,
|
||||
].where((part) => part.trim().isNotEmpty).join(' '),
|
||||
if (station.addressAddition.trim().isNotEmpty)
|
||||
station.addressAddition,
|
||||
[
|
||||
station.zip,
|
||||
station.city,
|
||||
].where((part) => part.trim().isNotEmpty).join(' '),
|
||||
].where((line) => line.trim().isNotEmpty).toList();
|
||||
|
||||
return Card(
|
||||
color: backgroundColor,
|
||||
margin: const EdgeInsets.symmetric(horizontal: 0, vertical: 8),
|
||||
elevation: 2,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: BorderSide(color: Colors.grey[300]!, width: 1),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: () async {
|
||||
await Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
builder:
|
||||
(context) => TaskView(
|
||||
job: widget.job,
|
||||
stationOrder: station.stationOrder,
|
||||
stationTitle:
|
||||
station.displayName.isNotEmpty
|
||||
? station.displayName
|
||||
: l10n.stationNumber(station.stationOrder + 1),
|
||||
),
|
||||
),
|
||||
);
|
||||
await _loadLocalTaskStatuses();
|
||||
},
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 8,
|
||||
vertical: 4,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primarySoft,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
l10n.stationNumber(station.stationOrder + 1),
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: AppColors.primaryStrong,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
title.isNotEmpty
|
||||
? localizeKnownText(context, title)
|
||||
: l10n.unnamedStation,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
if (subtitle != null) ...[
|
||||
const SizedBox(height: 2),
|
||||
Text(
|
||||
subtitle,
|
||||
style: TextStyle(
|
||||
fontSize: 13,
|
||||
color: Colors.grey[700],
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailItem(
|
||||
Icons.location_on_outlined,
|
||||
AppLocalizations.of(context).location,
|
||||
addressLines.join('\n'),
|
||||
AppColors.primary,
|
||||
),
|
||||
if (station.phone.trim().isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailItem(
|
||||
Icons.phone_outlined,
|
||||
l10n.phone,
|
||||
station.phone,
|
||||
AppColors.success,
|
||||
),
|
||||
],
|
||||
if (station.deliveryDate.trim().isNotEmpty ||
|
||||
station.deliveryTime.trim().isNotEmpty) ...[
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailItem(
|
||||
Icons.schedule,
|
||||
AppLocalizations.of(context).delivery,
|
||||
[
|
||||
station.deliveryDate,
|
||||
station.deliveryTime,
|
||||
].where((part) => part.trim().isNotEmpty).join(' '),
|
||||
AppColors.warning,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 12),
|
||||
_buildDetailItem(
|
||||
Icons.task_alt,
|
||||
AppLocalizations.of(context).tasks,
|
||||
'${station.tasks.length}',
|
||||
AppColors.primaryStrong,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDetailItem(
|
||||
IconData icon,
|
||||
String label,
|
||||
String value,
|
||||
Color color,
|
||||
) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withValues(alpha: 0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: color.withValues(alpha: 0.3)),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(icon, size: 16, color: color.withValues(alpha: 0.8)),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
label,
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[700],
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
value,
|
||||
style: TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey[800],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
699
app/lib/chat_details_view.dart
Normal file
@@ -0,0 +1,699 @@
|
||||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import 'app_theme.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
import 'l10n/localization_helpers.dart';
|
||||
import 'app_state.dart';
|
||||
import 'models/chat.dart';
|
||||
import 'models/chat_message.dart';
|
||||
import 'services/chat_service.dart';
|
||||
import 'services/websocket_service.dart';
|
||||
import 'services/notification_service.dart';
|
||||
import 'widgets/chat_photo_dialog.dart';
|
||||
import 'widgets/offline_banner.dart';
|
||||
|
||||
class ChatDetailsView extends StatefulWidget {
|
||||
final Chat chat;
|
||||
|
||||
const ChatDetailsView({super.key, required this.chat});
|
||||
|
||||
@override
|
||||
State<ChatDetailsView> createState() => _ChatDetailsViewState();
|
||||
}
|
||||
|
||||
class _PreparedImage {
|
||||
const _PreparedImage({required this.base64DataUri, required this.bytes});
|
||||
|
||||
final String base64DataUri;
|
||||
final Uint8List bytes;
|
||||
}
|
||||
|
||||
class _ChatDetailsViewState extends State<ChatDetailsView> {
|
||||
final TextEditingController _messageController = TextEditingController();
|
||||
final ScrollController _scrollController = ScrollController();
|
||||
late List<ChatMessage> _messages;
|
||||
final WebSocketService _webSocketService = WebSocketService();
|
||||
StreamSubscription<List<Chat>>? _chatsStreamSubscription;
|
||||
String? _currentUserId;
|
||||
late final String _conversationKey;
|
||||
final ChatService _chatService = ChatService();
|
||||
final Map<String, Uint8List> _imageCache = <String, Uint8List>{};
|
||||
late Chat _activeChat;
|
||||
static const int _maxDisplayMessages = 30;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_activeChat = widget.chat;
|
||||
_conversationKey = _activeChat.id;
|
||||
NotificationService().activeConversationKey = _conversationKey;
|
||||
_messages = _lastMessages(_activeChat.messages);
|
||||
_currentUserId = AppState().loggedInEmail;
|
||||
|
||||
_chatsStreamSubscription = _chatService.chatsStream.listen(
|
||||
_handleChatsUpdate,
|
||||
);
|
||||
|
||||
_chatService.initialize().then((_) async {
|
||||
_syncActiveChatFromService(replaceMessages: _messages.isEmpty);
|
||||
final history = await _chatService.loadMessagesForChat(_conversationKey);
|
||||
if (!mounted) return;
|
||||
if (history.isNotEmpty) {
|
||||
setState(() {
|
||||
_imageCache.clear();
|
||||
_messages = _lastMessages(history);
|
||||
});
|
||||
_scrollToBottom(immediate: true);
|
||||
}
|
||||
_syncActiveChatFromService();
|
||||
await _chatService.markConversationRead(_conversationKey);
|
||||
});
|
||||
|
||||
// Scroll to bottom after initial build is complete
|
||||
_scrollToBottom(immediate: true);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
if (NotificationService().activeConversationKey == _conversationKey) {
|
||||
NotificationService().activeConversationKey = null;
|
||||
}
|
||||
_chatsStreamSubscription?.cancel();
|
||||
_messageController.dispose();
|
||||
_scrollController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
void _scrollToBottom({bool immediate = false}) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!_scrollController.hasClients) {
|
||||
return;
|
||||
}
|
||||
final target = _scrollController.position.maxScrollExtent;
|
||||
if (immediate) {
|
||||
_scrollController.jumpTo(target);
|
||||
} else {
|
||||
_scrollController.animateTo(
|
||||
target,
|
||||
duration: const Duration(milliseconds: 250),
|
||||
curve: Curves.easeOut,
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void _handleChatsUpdate(List<Chat> chats) {
|
||||
if (!mounted) {
|
||||
return;
|
||||
}
|
||||
final updated = _findChatById(chats);
|
||||
if (updated == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final shouldReplace = _shouldReplaceMessages(updated);
|
||||
|
||||
setState(() {
|
||||
_activeChat = updated;
|
||||
if (shouldReplace) {
|
||||
_imageCache.clear();
|
||||
_messages = _lastMessages(updated.messages);
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldReplace) {
|
||||
_scrollToBottom();
|
||||
unawaited(_chatService.markConversationRead(_conversationKey));
|
||||
}
|
||||
}
|
||||
|
||||
bool _shouldReplaceMessages(Chat chat) {
|
||||
if (chat.messages.length != _messages.length) {
|
||||
return true;
|
||||
}
|
||||
if (_messages.isEmpty) {
|
||||
return false;
|
||||
}
|
||||
final currentLast = _messages.last;
|
||||
final updatedLast = chat.messages.last;
|
||||
return currentLast.id != updatedLast.id ||
|
||||
currentLast.content != updatedLast.content ||
|
||||
currentLast.contentType != updatedLast.contentType;
|
||||
}
|
||||
|
||||
List<ChatMessage> _lastMessages(List<ChatMessage> messages) {
|
||||
final sorted = List<ChatMessage>.from(messages)
|
||||
..sort((a, b) => a.createdAt.compareTo(b.createdAt));
|
||||
if (sorted.length > _maxDisplayMessages) {
|
||||
return sorted.sublist(sorted.length - _maxDisplayMessages);
|
||||
}
|
||||
return sorted;
|
||||
}
|
||||
|
||||
Chat? _findChatById(List<Chat> chats) {
|
||||
for (final chat in chats) {
|
||||
if (chat.id == _conversationKey) {
|
||||
return chat;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
void _syncActiveChatFromService({bool replaceMessages = false}) {
|
||||
final updated = _findChatById(_chatService.currentChats);
|
||||
if (updated == null || !mounted) {
|
||||
return;
|
||||
}
|
||||
|
||||
final shouldReplace = replaceMessages || _shouldReplaceMessages(updated);
|
||||
|
||||
setState(() {
|
||||
_activeChat = updated;
|
||||
if (shouldReplace) {
|
||||
_imageCache.clear();
|
||||
_messages = _lastMessages(updated.messages);
|
||||
}
|
||||
});
|
||||
|
||||
if (shouldReplace) {
|
||||
_scrollToBottom();
|
||||
unawaited(_chatService.markConversationRead(_conversationKey));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _sendMessage() async {
|
||||
final text = _messageController.text.trim();
|
||||
if (text.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
final sender = _currentUserId;
|
||||
final receiver = _activeChat.receiver;
|
||||
|
||||
if (sender == null || sender.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(AppLocalizations.of(context).noSenderMessage)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiver == null || receiver.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context).noRecipientMessage),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await _webSocketService.sendChatMessage(
|
||||
sender: sender,
|
||||
receiver: receiver,
|
||||
content: text,
|
||||
jobId: _activeChat.jobId,
|
||||
jobNumber: _activeChat.jobNumber,
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context).messageSendError),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_syncActiveChatFromService();
|
||||
|
||||
_messageController.clear();
|
||||
|
||||
_scrollToBottom();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final isJobChat = _activeChat.type == ChatType.jobSpecific;
|
||||
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
localizedChatTitle(context, _activeChat),
|
||||
style: const TextStyle(fontSize: 16),
|
||||
),
|
||||
if (isJobChat && _activeChat.jobNumber != null)
|
||||
Text(
|
||||
'Job-Nr: ${_activeChat.jobNumber}',
|
||||
style: TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.textMuted,
|
||||
fontWeight: FontWeight.normal,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(isJobChat ? Icons.work : Icons.support_agent),
|
||||
onPressed: () {
|
||||
// Show chat info
|
||||
_showChatInfo();
|
||||
},
|
||||
tooltip: AppLocalizations.of(context).chatInfo,
|
||||
),
|
||||
],
|
||||
),
|
||||
body: Column(
|
||||
children: [
|
||||
const OfflineBanner(),
|
||||
// Messages list
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: const BoxDecoration(color: AppColors.surfaceMuted),
|
||||
child: ListView.builder(
|
||||
controller: _scrollController,
|
||||
padding: const EdgeInsets.fromLTRB(8, 8, 8, 96),
|
||||
itemCount: _messages.length,
|
||||
itemBuilder: (context, index) {
|
||||
final message = _messages[index];
|
||||
return _buildMessageBubble(message);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
// Message input
|
||||
_buildMessageInput(),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageBubble(ChatMessage message) {
|
||||
final isOwn = message.isOwn;
|
||||
final isImage = message.contentType == ChatContentType.image;
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 2),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
isOwn ? MainAxisAlignment.end : MainAxisAlignment.start,
|
||||
children: [
|
||||
if (!isOwn) const SizedBox(width: 8),
|
||||
Flexible(
|
||||
child: Container(
|
||||
constraints: BoxConstraints(
|
||||
maxWidth: MediaQuery.of(context).size.width * 0.7,
|
||||
),
|
||||
margin: EdgeInsets.only(
|
||||
left: isOwn ? 40 : 0,
|
||||
right: isOwn ? 0 : 40,
|
||||
),
|
||||
padding: EdgeInsets.symmetric(
|
||||
horizontal: isImage ? 6 : 12,
|
||||
vertical: isImage ? 6 : 8,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: isOwn ? AppColors.primarySoft : AppColors.surface,
|
||||
borderRadius: BorderRadius.only(
|
||||
topLeft: const Radius.circular(12),
|
||||
topRight: const Radius.circular(12),
|
||||
bottomLeft: Radius.circular(isOwn ? 12 : 4),
|
||||
bottomRight: Radius.circular(isOwn ? 4 : 12),
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.1),
|
||||
blurRadius: 2,
|
||||
offset: const Offset(0, 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
_buildMessageContent(message, isImage: isImage),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
mainAxisAlignment: MainAxisAlignment.end,
|
||||
children: [
|
||||
Text(
|
||||
_formatMessageTime(message.createdAt),
|
||||
style: const TextStyle(
|
||||
fontSize: 11,
|
||||
color: AppColors.textMuted,
|
||||
),
|
||||
),
|
||||
if (isOwn) ...[
|
||||
const SizedBox(width: 4),
|
||||
Icon(
|
||||
message.pendingSync
|
||||
? Icons.schedule
|
||||
: (message.read ? Icons.done_all : Icons.done),
|
||||
size: 14,
|
||||
color:
|
||||
message.pendingSync
|
||||
? AppColors.warning
|
||||
: (message.read
|
||||
? AppColors.primary
|
||||
: AppColors.textMuted),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
if (isOwn) const SizedBox(width: 8),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageContent(ChatMessage message, {required bool isImage}) {
|
||||
if (!isImage) {
|
||||
return Text(
|
||||
message.content,
|
||||
style: const TextStyle(fontSize: 15, color: AppColors.textStrong),
|
||||
);
|
||||
}
|
||||
|
||||
final imageBytes = _imageCache[message.id] ?? _decodeImageBytes(message);
|
||||
|
||||
if (imageBytes == null) {
|
||||
return const Text(
|
||||
'Bild konnte nicht geladen werden.',
|
||||
style: TextStyle(fontSize: 15),
|
||||
);
|
||||
}
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () => _showImagePreview(imageBytes),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
child: Container(
|
||||
color: Colors.black.withValues(alpha: 0.05),
|
||||
constraints: const BoxConstraints(maxWidth: 260, minWidth: 140),
|
||||
child: AspectRatio(
|
||||
aspectRatio: 4 / 3,
|
||||
child: Image.memory(imageBytes, fit: BoxFit.cover),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Uint8List? _decodeImageBytes(ChatMessage message) {
|
||||
final rawContent = message.content.trim();
|
||||
if (rawContent.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final base64Payload =
|
||||
rawContent.startsWith('data:')
|
||||
? rawContent.substring(rawContent.indexOf(',') + 1)
|
||||
: rawContent;
|
||||
|
||||
final normalized = base64Payload.replaceAll(RegExp(r'\s'), '');
|
||||
|
||||
try {
|
||||
final bytes = base64Decode(normalized);
|
||||
_imageCache[message.id] = bytes;
|
||||
return bytes;
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _showImagePreview(Uint8List imageBytes) async {
|
||||
if (!mounted) return;
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (context) {
|
||||
return Dialog(
|
||||
insetPadding: const EdgeInsets.all(16),
|
||||
backgroundColor: Colors.black,
|
||||
child: InteractiveViewer(
|
||||
child: Image.memory(imageBytes, fit: BoxFit.contain),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildMessageInput() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surface,
|
||||
border: const Border(top: BorderSide(color: AppColors.border)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Row(
|
||||
children: [
|
||||
GestureDetector(
|
||||
onTap: _handleAttachmentTap,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceMuted,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.attach_file,
|
||||
color: AppColors.text,
|
||||
size: 20,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.surfaceMuted,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _messageController,
|
||||
decoration: InputDecoration(
|
||||
hintText: AppLocalizations.of(context).typeMessage,
|
||||
contentPadding: EdgeInsets.symmetric(
|
||||
horizontal: 16,
|
||||
vertical: 8,
|
||||
),
|
||||
border: InputBorder.none,
|
||||
),
|
||||
maxLines: null,
|
||||
keyboardType: TextInputType.multiline,
|
||||
textInputAction: TextInputAction.send,
|
||||
onSubmitted: (_) => _sendMessage(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
GestureDetector(
|
||||
onTap: () {
|
||||
_sendMessage();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColors.primary,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
),
|
||||
child: const Icon(Icons.send, color: Colors.white, size: 20),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleAttachmentTap() async {
|
||||
if (!mounted) return;
|
||||
final Uint8List? photoBytes = await showDialog<Uint8List>(
|
||||
context: context,
|
||||
builder: (context) => const ChatPhotoDialog(),
|
||||
);
|
||||
|
||||
if (photoBytes == null || photoBytes.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
await _sendImageMessage(photoBytes);
|
||||
}
|
||||
|
||||
Future<void> _sendImageMessage(Uint8List imageBytes) async {
|
||||
final sender = _currentUserId;
|
||||
final receiver = _activeChat.receiver;
|
||||
|
||||
if (sender == null || sender.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(AppLocalizations.of(context).noSenderMessage)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (receiver == null || receiver.isEmpty) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context).noRecipientMessage),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final prepared = await _prepareImagePayload(imageBytes);
|
||||
if (prepared == null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context).photoProcessError),
|
||||
),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final result = await _webSocketService.sendChatMessage(
|
||||
sender: sender,
|
||||
receiver: receiver,
|
||||
content: prepared.base64DataUri,
|
||||
contentType: ChatContentType.image,
|
||||
jobId: _activeChat.jobId,
|
||||
jobNumber: _activeChat.jobNumber,
|
||||
);
|
||||
|
||||
if (result == null) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(AppLocalizations.of(context).imageSendError)),
|
||||
);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
_syncActiveChatFromService();
|
||||
|
||||
if (prepared.bytes.isNotEmpty) {
|
||||
_imageCache[result.id] = prepared.bytes;
|
||||
}
|
||||
}
|
||||
|
||||
Future<_PreparedImage?> _prepareImagePayload(Uint8List originalBytes) async {
|
||||
try {
|
||||
final decoded = img.decodeImage(originalBytes);
|
||||
if (decoded == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final baked = img.bakeOrientation(decoded);
|
||||
const maxDimension = 1280;
|
||||
img.Image processed = baked;
|
||||
|
||||
if (baked.width > maxDimension || baked.height > maxDimension) {
|
||||
final scale =
|
||||
baked.width > baked.height
|
||||
? maxDimension / baked.width
|
||||
: maxDimension / baked.height;
|
||||
final targetWidth = (baked.width * scale).round();
|
||||
final targetHeight = (baked.height * scale).round();
|
||||
processed = img.copyResize(
|
||||
baked,
|
||||
width: targetWidth,
|
||||
height: targetHeight,
|
||||
interpolation: img.Interpolation.average,
|
||||
);
|
||||
}
|
||||
|
||||
final encodedBytes = Uint8List.fromList(
|
||||
img.encodeJpg(processed, quality: 85),
|
||||
);
|
||||
final base64Payload = base64Encode(encodedBytes);
|
||||
final dataUri = 'data:image/jpeg;base64,$base64Payload';
|
||||
|
||||
return _PreparedImage(base64DataUri: dataUri, bytes: encodedBytes);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
String _formatMessageTime(DateTime dateTime) {
|
||||
final now = DateTime.now();
|
||||
final today = DateTime(now.year, now.month, now.day);
|
||||
final messageDate = DateTime(dateTime.year, dateTime.month, dateTime.day);
|
||||
|
||||
if (messageDate == today) {
|
||||
// Today - show only time
|
||||
return '${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||
} else if (messageDate == today.subtract(const Duration(days: 1))) {
|
||||
// Yesterday
|
||||
return '${AppLocalizations.of(context).yesterday} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||
} else {
|
||||
// Older - show date and time
|
||||
return '${dateTime.day.toString().padLeft(2, '0')}.${dateTime.month.toString().padLeft(2, '0')} ${dateTime.hour.toString().padLeft(2, '0')}:${dateTime.minute.toString().padLeft(2, '0')}';
|
||||
}
|
||||
}
|
||||
|
||||
void _showChatInfo() {
|
||||
final isJobChat = _activeChat.type == ChatType.jobSpecific;
|
||||
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(localizedChatTitle(context, _activeChat)),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'${AppLocalizations.of(context).status}: ${isJobChat ? AppLocalizations.of(context).chatTypeJob : AppLocalizations.of(context).chatTypeGeneral}',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
if (isJobChat && _activeChat.jobNumber != null) ...[
|
||||
Text(
|
||||
'${AppLocalizations.of(context).jobNumber}: ${_activeChat.jobNumber}',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
],
|
||||
Text(
|
||||
'${AppLocalizations.of(context).messages}: ${_messages.length}',
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
'${AppLocalizations.of(context).created}: ${_formatMessageTime(_messages.isNotEmpty ? _messages.first.createdAt : DateTime.now())}',
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
child: Text(AppLocalizations.of(context).close),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
189
app/lib/chats_view.dart
Normal file
@@ -0,0 +1,189 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'app_theme.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
import 'l10n/localization_helpers.dart';
|
||||
import 'models/chat.dart';
|
||||
import 'services/chat_service.dart';
|
||||
import 'widgets/offline_banner.dart';
|
||||
|
||||
class ChatsView extends StatefulWidget {
|
||||
const ChatsView({super.key});
|
||||
|
||||
@override
|
||||
State<ChatsView> createState() => _ChatsViewState();
|
||||
}
|
||||
|
||||
class _ChatsViewState extends State<ChatsView> {
|
||||
final ChatService _chatService = ChatService();
|
||||
List<Chat> _chats = const [];
|
||||
StreamSubscription<List<Chat>>? _chatSubscription;
|
||||
bool _isInitializing = true;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_initializeChats();
|
||||
}
|
||||
|
||||
Future<void> _initializeChats() async {
|
||||
await _chatService.initialize();
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {
|
||||
_chats = _chatService.currentChats;
|
||||
_isInitializing = false;
|
||||
});
|
||||
|
||||
_chatSubscription = _chatService.chatsStream.listen((chats) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_chats = chats;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_chatSubscription?.cancel();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(AppLocalizations.of(context).chats)),
|
||||
body: Column(
|
||||
children: [const OfflineBanner(), Expanded(child: _buildBody())],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBody() {
|
||||
if (_isInitializing) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (_chats.isEmpty) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(
|
||||
Icons.chat_outlined,
|
||||
size: 64,
|
||||
color: AppColors.textMuted,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
AppLocalizations.of(context).noChatsAvailable,
|
||||
style: const TextStyle(fontSize: 16, color: AppColors.textMuted),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
return ListView.builder(
|
||||
itemCount: _chats.length,
|
||||
itemBuilder: (context, index) {
|
||||
final chat = _chats[index];
|
||||
return _buildChatTile(chat);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildChatTile(Chat chat) {
|
||||
final isJobChat = chat.type == ChatType.jobSpecific;
|
||||
final hasMessages = chat.messages.isNotEmpty;
|
||||
final previewText =
|
||||
hasMessages
|
||||
? chat.lastMessagePreview
|
||||
: AppLocalizations.of(context).noMessagesYet;
|
||||
final timeLabel = hasMessages ? _formatTime(chat.lastMessageTime) : '--';
|
||||
final jobId = chat.jobId?.trim();
|
||||
final jobNumber = chat.jobNumber?.trim();
|
||||
final showJobId = isJobChat && jobId != null && jobId.isNotEmpty;
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
child: ListTile(
|
||||
leading: CircleAvatar(
|
||||
backgroundColor:
|
||||
isJobChat ? AppColors.primarySoft : AppColors.secondarySoft,
|
||||
child: Icon(
|
||||
isJobChat ? Icons.work : Icons.support_agent,
|
||||
color: isJobChat ? AppColors.primaryStrong : AppColors.secondary,
|
||||
),
|
||||
),
|
||||
title: Text(() {
|
||||
if (isJobChat) {
|
||||
if (jobNumber != null && jobNumber.isNotEmpty) {
|
||||
return 'Job $jobNumber';
|
||||
}
|
||||
if (showJobId) {
|
||||
return 'Job $jobId';
|
||||
}
|
||||
}
|
||||
return localizedChatTitle(context, chat);
|
||||
}(), style: const TextStyle(fontWeight: FontWeight.w600, fontSize: 16)),
|
||||
subtitle: Text(
|
||||
previewText,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: const TextStyle(fontSize: 14, color: AppColors.textMuted),
|
||||
),
|
||||
trailing: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.end,
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(
|
||||
timeLabel,
|
||||
style: const TextStyle(fontSize: 12, color: AppColors.textMuted),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2),
|
||||
decoration: BoxDecoration(
|
||||
color:
|
||||
isJobChat ? AppColors.primarySoft : AppColors.secondarySoft,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(
|
||||
color: isJobChat ? AppColors.primary : AppColors.secondary,
|
||||
),
|
||||
),
|
||||
child: Text(
|
||||
isJobChat ? 'JOB' : 'ALLG',
|
||||
style: TextStyle(
|
||||
fontSize: 10,
|
||||
fontWeight: FontWeight.w600,
|
||||
color:
|
||||
isJobChat ? AppColors.primaryStrong : AppColors.secondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
onTap: () {
|
||||
Navigator.of(context).pushNamed('/chat_details', arguments: chat);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
String _formatTime(DateTime dateTime) {
|
||||
final now = DateTime.now();
|
||||
final difference = now.difference(dateTime);
|
||||
|
||||
if (difference.inDays > 0) {
|
||||
return '${difference.inDays}T';
|
||||
} else if (difference.inHours > 0) {
|
||||
return '${difference.inHours}h';
|
||||
} else if (difference.inMinutes > 0) {
|
||||
return '${difference.inMinutes}m';
|
||||
} else {
|
||||
return 'jetzt';
|
||||
}
|
||||
}
|
||||
}
|
||||
32
app/lib/config/translation_config.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
enum TranslationBackend { lmStudio, moonshot }
|
||||
|
||||
class TranslationConfig {
|
||||
TranslationConfig._();
|
||||
|
||||
/// Das aktive Übersetzungs-Backend.
|
||||
/// Hier umschalten zwischen LM Studio (lokal) und Moonshot AI (Cloud).
|
||||
static const TranslationBackend activeBackend = TranslationBackend.moonshot;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// LM Studio (lokales Modell)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Basis-URL des LM Studio REST-Servers (lokales Netzwerk)
|
||||
static const String lmStudioBaseUrl = 'http://lmstudio.appcreation.de';
|
||||
|
||||
/// Modellname – LM Studio ignoriert diesen Wert normalerweise
|
||||
static const String lmStudioModel = 'local-model';
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Moonshot AI (Kimi Cloud API)
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
/// Basis-URL der Moonshot AI API
|
||||
static const String moonshotBaseUrl = 'https://api.moonshot.ai/v1';
|
||||
|
||||
/// API-Key für die Moonshot AI Authentifizierung
|
||||
static const String moonshotApiKey = 'sk-EfHJfwCsxiZbOoBJ21OLWb9RUJQXSXAFIFGKnOedKke5JYZp';
|
||||
|
||||
/// Moonshot-Modell: moonshot-v1-8k (kurze Texte), moonshot-v1-32k, moonshot-v1-128k
|
||||
static const String moonshotModel = 'moonshot-v1-8k';
|
||||
}
|
||||
48
app/lib/entities/chat_message_entity.dart
Normal file
@@ -0,0 +1,48 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class ChatMessageEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String messageId;
|
||||
|
||||
@Index()
|
||||
String conversationKey;
|
||||
|
||||
String content;
|
||||
|
||||
String contentType; // 'TEXT' or 'IMAGE'
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
@Index()
|
||||
DateTime createdAt;
|
||||
|
||||
String origin; // 'INCOMING' or 'OUTGOING'
|
||||
|
||||
String messageType; // 'NORMAL', 'JOB_ASSIGNMENT', etc.
|
||||
|
||||
String? jobId;
|
||||
|
||||
String? jobNumber;
|
||||
|
||||
bool read;
|
||||
|
||||
bool pendingSync;
|
||||
|
||||
ChatMessageEntity({
|
||||
required this.messageId,
|
||||
required this.conversationKey,
|
||||
required this.content,
|
||||
this.contentType = 'TEXT',
|
||||
required this.createdAt,
|
||||
required this.origin,
|
||||
required this.messageType,
|
||||
this.jobId,
|
||||
this.jobNumber,
|
||||
this.read = false,
|
||||
this.pendingSync = false,
|
||||
});
|
||||
}
|
||||
|
||||
26
app/lib/entities/job_entity.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class JobEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String jobId; // The original job ID from the Job model
|
||||
|
||||
String jobData; // JSON-encoded job data
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime updatedAt;
|
||||
|
||||
JobEntity({
|
||||
required this.jobId,
|
||||
required this.jobData,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
23
app/lib/entities/photo_entity.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class PhotoEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
String taskId;
|
||||
|
||||
int photoIndex;
|
||||
|
||||
String data; // Base64-encoded photo data
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
PhotoEntity({
|
||||
required this.taskId,
|
||||
required this.photoIndex,
|
||||
required this.data,
|
||||
required this.createdAt,
|
||||
});
|
||||
}
|
||||
27
app/lib/entities/queued_message_entity.dart
Normal file
@@ -0,0 +1,27 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class QueuedMessageEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String messageId;
|
||||
|
||||
String topic;
|
||||
|
||||
String payload; // JSON-encoded payload
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
int retryCount;
|
||||
|
||||
QueuedMessageEntity({
|
||||
required this.messageId,
|
||||
required this.topic,
|
||||
required this.payload,
|
||||
required this.createdAt,
|
||||
this.retryCount = 0,
|
||||
});
|
||||
}
|
||||
30
app/lib/entities/task_status_entity.dart
Normal file
@@ -0,0 +1,30 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class TaskStatusEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String taskId;
|
||||
|
||||
bool completed;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime? completedAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime updatedAt;
|
||||
|
||||
TaskStatusEntity({
|
||||
required this.taskId,
|
||||
required this.completed,
|
||||
this.completedAt,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
26
app/lib/entities/user_data_entity.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
import 'package:objectbox/objectbox.dart';
|
||||
|
||||
@Entity()
|
||||
class UserDataEntity {
|
||||
@Id()
|
||||
int id = 0;
|
||||
|
||||
@Unique()
|
||||
String key;
|
||||
|
||||
String value;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime createdAt;
|
||||
|
||||
@Property(type: PropertyType.date)
|
||||
DateTime updatedAt;
|
||||
|
||||
UserDataEntity({
|
||||
required this.key,
|
||||
required this.value,
|
||||
required this.createdAt,
|
||||
required this.updatedAt,
|
||||
});
|
||||
}
|
||||
|
||||
23
app/lib/jobs_route_mixin.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'navigation_observer.dart';
|
||||
|
||||
mixin RouteAwareState<T extends StatefulWidget> on State<T> implements RouteAware {
|
||||
@override
|
||||
void didPopNext() {
|
||||
// When returning to this route, subclasses can override to refresh state.
|
||||
}
|
||||
|
||||
void subscribeRouteAware() {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
final route = ModalRoute.of(context);
|
||||
if (route != null) {
|
||||
routeObserver.subscribe(this, route);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void unsubscribeRouteAware() {
|
||||
routeObserver.unsubscribe(this);
|
||||
}
|
||||
}
|
||||
|
||||
1912
app/lib/jobs_view.dart
Normal file
304
app/lib/l10n/app_localizations.dart
Normal file
@@ -0,0 +1,304 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'app_localizations_de.dart';
|
||||
import 'app_localizations_en.dart';
|
||||
import 'app_localizations_es.dart';
|
||||
import 'app_localizations_fr.dart';
|
||||
import 'app_localizations_pl.dart';
|
||||
import 'app_localizations_ru.dart';
|
||||
import 'app_localizations_tr.dart';
|
||||
import 'app_localizations_et.dart';
|
||||
import 'app_localizations_lv.dart';
|
||||
import 'app_localizations_lt.dart';
|
||||
|
||||
/// Supported language codes
|
||||
const List<String> supportedLanguageCodes = [
|
||||
'de',
|
||||
'en',
|
||||
'es',
|
||||
'fr',
|
||||
'pl',
|
||||
'ru',
|
||||
'tr',
|
||||
'et',
|
||||
'lv',
|
||||
'lt',
|
||||
];
|
||||
|
||||
/// AppLocalizations provides localized strings for the app
|
||||
abstract class AppLocalizations {
|
||||
static AppLocalizations of(BuildContext context) {
|
||||
return Localizations.of<AppLocalizations>(context, AppLocalizations) ??
|
||||
AppLocalizationsDe();
|
||||
}
|
||||
|
||||
static const LocalizationsDelegate<AppLocalizations> delegate =
|
||||
_AppLocalizationsDelegate();
|
||||
|
||||
/// Language name
|
||||
String get languageName;
|
||||
|
||||
/// Flag emoji
|
||||
String get flagEmoji;
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
String get appTitle;
|
||||
String get ok;
|
||||
String get cancel;
|
||||
String get save;
|
||||
String get delete;
|
||||
String get close;
|
||||
String get confirm;
|
||||
String get error;
|
||||
String get success;
|
||||
String get loading;
|
||||
String get refresh;
|
||||
String get version;
|
||||
String get unknown;
|
||||
String get yesterday;
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
String get jobs;
|
||||
String get availableJobs;
|
||||
String get chats;
|
||||
String get settings;
|
||||
String get logout;
|
||||
String get logoutConfirm;
|
||||
String get logoutConfirmMessage;
|
||||
String get openChat;
|
||||
String get chatInfo;
|
||||
String get routePlan;
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
String get welcomeBack;
|
||||
String get loginSubtitle;
|
||||
String get email;
|
||||
String get emailAddress;
|
||||
String get emailAddressHint;
|
||||
String get emailAddressRequired;
|
||||
String get emailAddressInvalid;
|
||||
String get password;
|
||||
String get passwordHint;
|
||||
String get passwordRequired;
|
||||
String get passwordMinLength;
|
||||
String get login;
|
||||
String get loggingIn;
|
||||
String get forgotPassword;
|
||||
String get forgotPasswordMessage;
|
||||
String get loginSuccess;
|
||||
String get loginFailed;
|
||||
String get connectionFailed;
|
||||
String get connectionTimeout;
|
||||
String get connecting;
|
||||
String get connectionError;
|
||||
String get loginError;
|
||||
|
||||
// ==================== JOBS ====================
|
||||
String get noJobsAssigned;
|
||||
String get noJobsMessage;
|
||||
String get pullToRefresh;
|
||||
String get newLabel;
|
||||
String get tasksToComplete;
|
||||
String get pickup;
|
||||
String get delivery;
|
||||
String get created;
|
||||
String get status;
|
||||
String get priority;
|
||||
String get dueDate;
|
||||
String get location;
|
||||
String get description;
|
||||
String get cargo;
|
||||
String get quantity;
|
||||
String get weight;
|
||||
String get dimensions;
|
||||
String get jobDeleted;
|
||||
String get jobDeleteError;
|
||||
String get jobCompleted;
|
||||
String get from;
|
||||
String get to;
|
||||
String get jobsUpdated;
|
||||
String get connectionRestored;
|
||||
String get connectionLost;
|
||||
String get offline;
|
||||
String get deleteJob;
|
||||
String get jobRemoved;
|
||||
String get newJobReceived;
|
||||
String get jobDetails;
|
||||
String get jobTasks;
|
||||
String get deliveryStations;
|
||||
String deliveryStationsCount(int count);
|
||||
String get noDeliveryStations;
|
||||
String get noDeliveryStationsMessage;
|
||||
String get phone;
|
||||
String get unnamedStation;
|
||||
String stationNumber(int number);
|
||||
|
||||
// ==================== TASKS ====================
|
||||
String get tasks;
|
||||
String get noTasks;
|
||||
String get noTasksMessage;
|
||||
String get taskOrder;
|
||||
String get confirmationRequired;
|
||||
String get confirmationDescription;
|
||||
String get checklist;
|
||||
String get checklistDescription;
|
||||
String get completeTask;
|
||||
String get completeTaskConfirm;
|
||||
String get completeTaskNote;
|
||||
String get taskCompleted;
|
||||
String get comment;
|
||||
String get commentRequired;
|
||||
String get enterComment;
|
||||
String get commentDescription;
|
||||
String get finish;
|
||||
String get signature;
|
||||
String get signatureCapture;
|
||||
String get signatureRequired;
|
||||
String get clear;
|
||||
String get signatureError;
|
||||
String get signatureInstruction;
|
||||
String get photoCapture;
|
||||
String get requiredPhotos;
|
||||
String get photosTaken;
|
||||
String get photos;
|
||||
String get takePhoto;
|
||||
String get selectFromLibrary;
|
||||
String get retakePhoto;
|
||||
String get photoRequired;
|
||||
String get minPhotos;
|
||||
String get maxPhotos;
|
||||
String get photoError;
|
||||
String get deletePhoto;
|
||||
String get deletePhotoConfirm;
|
||||
String get barcode;
|
||||
String get barcodeScan;
|
||||
String get scanBarcode;
|
||||
String get barcodeRequired;
|
||||
String get minBarcodes;
|
||||
String get maxBarcodes;
|
||||
String get scanned;
|
||||
String get scannedBarcodes;
|
||||
String get barcodesRequired;
|
||||
String get enterBarcode;
|
||||
String get barcodeEnterDescription;
|
||||
String barcodeNumberRequired(int number);
|
||||
String barcodeNumberOptional(int number);
|
||||
String get barcodeError;
|
||||
String get cameraError;
|
||||
String get cameraNotReady;
|
||||
String get cameraNotAvailable;
|
||||
String get cameraNotSupportedMessage;
|
||||
String get cameraNotSupportedOnPlatform;
|
||||
String get maxPhotosReached;
|
||||
String get cameraReadyNoPreview;
|
||||
String get cameraLoading;
|
||||
String get cameraInitializing;
|
||||
String get cameraLoadingMessage;
|
||||
String get addPhotos;
|
||||
String get addPhotosInstruction;
|
||||
String get photoOf;
|
||||
|
||||
// ==================== CHAT ====================
|
||||
String get typeMessage;
|
||||
String get send;
|
||||
String get noSender;
|
||||
String get noSenderMessage;
|
||||
String get noRecipient;
|
||||
String get noRecipientMessage;
|
||||
String get messageSendError;
|
||||
String get photoSendError;
|
||||
String get photoProcessError;
|
||||
String get imageSendError;
|
||||
String get chatTypeJob;
|
||||
String get chatTypeGeneral;
|
||||
String get jobNumber;
|
||||
String get messages;
|
||||
String get generalMessages;
|
||||
String get noMessagesYet;
|
||||
String get noChatsAvailable;
|
||||
String get selectPhoto;
|
||||
String get unreadMessages;
|
||||
|
||||
// ==================== CARGO ====================
|
||||
String get cargoDetails;
|
||||
String get itemName;
|
||||
String get itemNumber;
|
||||
String get item;
|
||||
String get weightUnit;
|
||||
String get dimensionUnit;
|
||||
String get noCargoItems;
|
||||
String get noCargoItemsMessage;
|
||||
String get article;
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
String get takePhotos;
|
||||
String get photosCount;
|
||||
String get checklistPoints;
|
||||
String get signatureRequiredText;
|
||||
String get scanBarcodes;
|
||||
String get barcodeCount;
|
||||
String get commentOptional;
|
||||
String get genericTask;
|
||||
String get complete;
|
||||
String get abort;
|
||||
String get optional;
|
||||
String get skipTask;
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
String get language;
|
||||
String get languageChanged;
|
||||
String get appInfo;
|
||||
|
||||
// ==================== STATUS ====================
|
||||
String get statusCreated;
|
||||
String get statusPending;
|
||||
String get statusAssigned;
|
||||
String get statusInProgress;
|
||||
String get statusCompleted;
|
||||
String get statusCancelled;
|
||||
String get statusFailed;
|
||||
String get priorityLow;
|
||||
String get priorityMedium;
|
||||
String get priorityHigh;
|
||||
String get priorityUrgent;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate
|
||||
extends LocalizationsDelegate<AppLocalizations> {
|
||||
const _AppLocalizationsDelegate();
|
||||
|
||||
@override
|
||||
bool isSupported(Locale locale) {
|
||||
return supportedLanguageCodes.contains(locale.languageCode);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<AppLocalizations> load(Locale locale) async {
|
||||
switch (locale.languageCode) {
|
||||
case 'de':
|
||||
return AppLocalizationsDe();
|
||||
case 'en':
|
||||
return AppLocalizationsEn();
|
||||
case 'es':
|
||||
return AppLocalizationsEs();
|
||||
case 'fr':
|
||||
return AppLocalizationsFr();
|
||||
case 'pl':
|
||||
return AppLocalizationsPl();
|
||||
case 'ru':
|
||||
return AppLocalizationsRu();
|
||||
case 'tr':
|
||||
return AppLocalizationsTr();
|
||||
case 'et':
|
||||
return AppLocalizationsEt();
|
||||
case 'lv':
|
||||
return AppLocalizationsLv();
|
||||
case 'lt':
|
||||
return AppLocalizationsLt();
|
||||
default:
|
||||
return AppLocalizationsDe();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
|
||||
}
|
||||
636
app/lib/l10n/app_localizations_de.dart
Normal file
@@ -0,0 +1,636 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsDe extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Deutsch';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇩🇪';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
|
||||
@override
|
||||
String get cancel => 'Abbrechen';
|
||||
|
||||
@override
|
||||
String get save => 'Speichern';
|
||||
|
||||
@override
|
||||
String get delete => 'Löschen';
|
||||
|
||||
@override
|
||||
String get close => 'Schließen';
|
||||
|
||||
@override
|
||||
String get confirm => 'Bestätigen';
|
||||
|
||||
@override
|
||||
String get error => 'Fehler';
|
||||
|
||||
@override
|
||||
String get success => 'Erfolg';
|
||||
|
||||
@override
|
||||
String get loading => 'Laden...';
|
||||
|
||||
@override
|
||||
String get refresh => 'Aktualisieren';
|
||||
|
||||
@override
|
||||
String get version => 'Version';
|
||||
|
||||
@override
|
||||
String get unknown => 'Unbekannt';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Gestern';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Jobs';
|
||||
|
||||
@override
|
||||
String get availableJobs => 'Auftragsliste';
|
||||
|
||||
@override
|
||||
String get chats => 'Chats';
|
||||
|
||||
@override
|
||||
String get settings => 'Einstellungen';
|
||||
|
||||
@override
|
||||
String get logout => 'Abmelden';
|
||||
|
||||
@override
|
||||
String get logoutConfirm => 'Abmelden';
|
||||
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Möchten Sie sich wirklich abmelden?';
|
||||
|
||||
@override
|
||||
String get openChat => 'Chat öffnen';
|
||||
|
||||
@override
|
||||
String get chatInfo => 'Chat-Info';
|
||||
|
||||
@override
|
||||
String get routePlan => 'Route planen';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Willkommen zurück';
|
||||
|
||||
@override
|
||||
String get loginSubtitle => 'Melden Sie sich in Ihrem Konto an';
|
||||
|
||||
@override
|
||||
String get email => 'E-Mail';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'E-Mail-Adresse';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Geben Sie Ihre E-Mail-Adresse ein';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Bitte geben Sie Ihre E-Mail-Adresse ein';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid =>
|
||||
'Bitte geben Sie eine gültige E-Mail-Adresse ein';
|
||||
|
||||
@override
|
||||
String get password => 'Passwort';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Geben Sie Ihr Passwort ein';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Bitte geben Sie Ihr Passwort ein';
|
||||
|
||||
@override
|
||||
String get passwordMinLength =>
|
||||
'Das Passwort muss mindestens 6 Zeichen lang sein';
|
||||
|
||||
@override
|
||||
String get login => 'Anmelden';
|
||||
|
||||
@override
|
||||
String get loggingIn => 'Verbinden…';
|
||||
|
||||
@override
|
||||
String get forgotPassword => 'Passwort vergessen?';
|
||||
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Passwort vergessen Funktion noch nicht implementiert';
|
||||
|
||||
@override
|
||||
String get loginSuccess => 'Erfolgreich abgemeldet';
|
||||
|
||||
@override
|
||||
String get loginFailed => 'Anmeldung fehlgeschlagen';
|
||||
|
||||
@override
|
||||
String get connectionFailed =>
|
||||
'Verbindung zum Server fehlgeschlagen (Timeout).';
|
||||
|
||||
@override
|
||||
String get connectionTimeout =>
|
||||
'Verbindung zum Server fehlgeschlagen (Timeout).';
|
||||
|
||||
@override
|
||||
String get connecting => 'Verbindung zum Server wird hergestellt...';
|
||||
|
||||
@override
|
||||
String get connectionError => 'Verbindungsfehler';
|
||||
|
||||
@override
|
||||
String get loginError => 'Fehler bei der Anmeldung';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Keine Jobs zugewiesen';
|
||||
|
||||
@override
|
||||
String get noJobsMessage => 'Ihre zugewiesenen Jobs werden hier angezeigt.';
|
||||
|
||||
@override
|
||||
String get pullToRefresh => 'Nach unten ziehen zum Aktualisieren';
|
||||
|
||||
@override
|
||||
String get newLabel => 'NEU';
|
||||
|
||||
@override
|
||||
String get tasksToComplete => 'Zu erledigende Aufgaben';
|
||||
|
||||
@override
|
||||
String get pickup => 'Abholung';
|
||||
|
||||
@override
|
||||
String get delivery => 'Zustellung';
|
||||
|
||||
@override
|
||||
String get created => 'Erstellt';
|
||||
|
||||
@override
|
||||
String get status => 'Status';
|
||||
|
||||
@override
|
||||
String get priority => 'Priorität';
|
||||
|
||||
@override
|
||||
String get dueDate => 'Fälligkeitsdatum';
|
||||
|
||||
@override
|
||||
String get location => 'Ort';
|
||||
|
||||
@override
|
||||
String get description => 'Beschreibung';
|
||||
|
||||
@override
|
||||
String get cargo => 'Fracht';
|
||||
|
||||
@override
|
||||
String get quantity => 'Anzahl';
|
||||
|
||||
@override
|
||||
String get weight => 'Gewicht';
|
||||
|
||||
@override
|
||||
String get dimensions => 'Abmessungen';
|
||||
|
||||
@override
|
||||
String get jobDeleted => 'Job gelöscht';
|
||||
|
||||
@override
|
||||
String get jobDeleteError => 'Fehler beim Löschen des Jobs';
|
||||
|
||||
@override
|
||||
String get jobCompleted => 'Job abgeschlossen';
|
||||
|
||||
@override
|
||||
String get from => 'Von';
|
||||
|
||||
@override
|
||||
String get to => 'nach';
|
||||
|
||||
@override
|
||||
String get jobsUpdated => 'Jobs aktualisiert';
|
||||
|
||||
@override
|
||||
String get connectionRestored => 'Verbindung wiederhergestellt. Lade Jobs...';
|
||||
|
||||
@override
|
||||
String get connectionLost => 'Verbindung verloren. Offline.';
|
||||
|
||||
@override
|
||||
String get offline => 'Offline';
|
||||
|
||||
@override
|
||||
String get deleteJob => 'Job löschen';
|
||||
|
||||
@override
|
||||
String get jobRemoved => 'wurde entfernt';
|
||||
|
||||
@override
|
||||
String get newJobReceived => 'Neuer Job erhalten';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Auftragsdetails';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Aufgaben eines Auftrags';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Lieferstationen';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Lieferstationen ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Keine Lieferstationen';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Dieser Job enthält aktuell keine Lieferstationen.';
|
||||
|
||||
@override
|
||||
String get phone => 'Telefon';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Unbenannte Station';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Station $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Aufgaben';
|
||||
|
||||
@override
|
||||
String get noTasks => 'Keine Aufgaben';
|
||||
|
||||
@override
|
||||
String get noTasksMessage => 'Für diesen Job sind keine Aufgaben definiert.';
|
||||
|
||||
@override
|
||||
String get taskOrder => 'Reihenfolge';
|
||||
|
||||
@override
|
||||
String get confirmationRequired => 'Bestätigung erforderlich';
|
||||
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Klicken Sie auf den Button um die Aufgabe zu erledigen.';
|
||||
|
||||
@override
|
||||
String get checklist => 'Checkliste';
|
||||
|
||||
@override
|
||||
String get checklistDescription => 'Bitte alle Punkte abhaken:';
|
||||
|
||||
@override
|
||||
String get completeTask => 'Aufgabe abschließen';
|
||||
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Möchten Sie diese Aufgabe als erledigt markieren?';
|
||||
|
||||
@override
|
||||
String get completeTaskNote => 'Notiz (optional)';
|
||||
|
||||
@override
|
||||
String get taskCompleted => 'Aufgabe erledigt';
|
||||
|
||||
@override
|
||||
String get comment => 'Kommentar';
|
||||
|
||||
@override
|
||||
String get commentRequired => 'Kommentar (erforderlich)';
|
||||
|
||||
@override
|
||||
String get enterComment => 'Kommentar eingeben';
|
||||
|
||||
@override
|
||||
String get commentDescription => 'Bitte geben Sie einen Kommentar ein:';
|
||||
|
||||
@override
|
||||
String get finish => 'Fertig';
|
||||
|
||||
@override
|
||||
String get signature => 'Unterschrift';
|
||||
|
||||
@override
|
||||
String get signatureCapture => 'Unterschrift erfassen';
|
||||
|
||||
@override
|
||||
String get signatureRequired => 'Bitte eine Unterschrift erfassen.';
|
||||
|
||||
@override
|
||||
String get clear => 'Leeren';
|
||||
|
||||
@override
|
||||
String get signatureError => 'Fehler beim Speichern der Unterschrift';
|
||||
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Bitte unterschreiben Sie im Feld unten (Maus oder Finger).';
|
||||
|
||||
@override
|
||||
String get photoCapture => 'Fotos aufnehmen';
|
||||
@override
|
||||
String get requiredPhotos => 'Benötigte Fotos';
|
||||
@override
|
||||
String get photosTaken => 'Aufgenommen';
|
||||
|
||||
@override
|
||||
String get photos => 'Fotos';
|
||||
|
||||
@override
|
||||
String get takePhoto => 'Foto aufnehmen';
|
||||
|
||||
@override
|
||||
String get selectFromLibrary => 'Aus Bibliothek wählen';
|
||||
|
||||
@override
|
||||
String get retakePhoto => 'Neu aufnehmen';
|
||||
|
||||
@override
|
||||
String get photoRequired => 'Foto erforderlich';
|
||||
|
||||
@override
|
||||
String get minPhotos => 'Mindestens';
|
||||
|
||||
@override
|
||||
String get maxPhotos => 'Maximal';
|
||||
|
||||
@override
|
||||
String get photoError => 'Fehler beim Aufnehmen des Fotos';
|
||||
|
||||
@override
|
||||
String get deletePhoto => 'Foto löschen';
|
||||
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Möchten Sie dieses Foto wirklich löschen?';
|
||||
|
||||
@override
|
||||
String get barcode => 'Barcode';
|
||||
|
||||
@override
|
||||
String get barcodeScan => 'Barcode scannen';
|
||||
|
||||
@override
|
||||
String get scanBarcode => 'Barcode scannen';
|
||||
|
||||
@override
|
||||
String get barcodeRequired => 'Barcode erforderlich';
|
||||
|
||||
@override
|
||||
String get minBarcodes => 'Mindestens';
|
||||
|
||||
@override
|
||||
String get maxBarcodes => 'Maximal';
|
||||
|
||||
@override
|
||||
String get scanned => 'Gescannt';
|
||||
|
||||
@override
|
||||
String get scannedBarcodes => 'Gescannte Barcodes';
|
||||
|
||||
@override
|
||||
String get barcodesRequired => 'Barcodes erforderlich';
|
||||
|
||||
@override
|
||||
String get enterBarcode => 'Barcode eingeben';
|
||||
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Bitte geben Sie die Barcodes ein:';
|
||||
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Barcode $number (erforderlich)';
|
||||
|
||||
@override
|
||||
String barcodeNumberOptional(int number) => 'Barcode $number (optional)';
|
||||
|
||||
@override
|
||||
String get barcodeError => 'Fehler beim Scannen des Barcodes';
|
||||
|
||||
@override
|
||||
String get cameraError => 'Fehler beim Initialisieren der Kamera';
|
||||
|
||||
@override
|
||||
String get cameraNotReady => 'Kamera ist nicht bereit oder nicht verfügbar';
|
||||
|
||||
@override
|
||||
String get cameraNotAvailable => 'Kamera nicht verfügbar';
|
||||
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Auf dieser Plattform wird die Kamera nicht unterstützt.';
|
||||
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform =>
|
||||
'Nicht unterstützt auf dieser Plattform';
|
||||
|
||||
@override
|
||||
String get maxPhotosReached => 'Maximum erreicht';
|
||||
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Kamera bereit (ohne Vorschau)';
|
||||
|
||||
@override
|
||||
String get cameraLoading => 'Kamera lädt...';
|
||||
|
||||
@override
|
||||
String get cameraInitializing => 'Kamera wird initialisiert...';
|
||||
|
||||
@override
|
||||
String get cameraLoadingMessage =>
|
||||
'Bitte warten Sie, während die Kamera geladen wird';
|
||||
|
||||
@override
|
||||
String get addPhotos => 'Fotos hinzufügen';
|
||||
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Verwenden Sie den Button „Foto auswählen", um Bilder von Ihrer Kamera oder Festplatte hinzuzufügen.';
|
||||
|
||||
@override
|
||||
String get photoOf => 'von';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Nachricht eingeben...';
|
||||
|
||||
@override
|
||||
String get send => 'Senden';
|
||||
|
||||
@override
|
||||
String get noSender => 'Kein Absender verfügbar';
|
||||
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Kein Absender verfügbar. Bitte erneut anmelden.';
|
||||
|
||||
@override
|
||||
String get noRecipient => 'Kein Empfänger konfiguriert';
|
||||
|
||||
@override
|
||||
String get noRecipientMessage =>
|
||||
'Kein Empfänger für diesen Chat konfiguriert.';
|
||||
|
||||
@override
|
||||
String get messageSendError => 'Nachricht konnte nicht gesendet werden.';
|
||||
|
||||
@override
|
||||
String get photoSendError => 'Foto konnte nicht gesendet werden.';
|
||||
|
||||
@override
|
||||
String get photoProcessError => 'Foto konnte nicht verarbeitet werden.';
|
||||
|
||||
@override
|
||||
String get imageSendError => 'Bild konnte nicht gesendet werden.';
|
||||
|
||||
@override
|
||||
String get chatTypeJob => 'Job-spezifisch';
|
||||
|
||||
@override
|
||||
String get chatTypeGeneral => 'Allgemein';
|
||||
|
||||
@override
|
||||
String get jobNumber => 'Job-Nummer';
|
||||
|
||||
@override
|
||||
String get messages => 'Nachrichten';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Allgemeine Nachrichten';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Noch keine Nachrichten';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Keine Chats verfügbar';
|
||||
|
||||
@override
|
||||
String get selectPhoto => 'Foto auswählen';
|
||||
|
||||
@override
|
||||
String get unreadMessages => 'Ungelesene Nachrichten';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Sprache';
|
||||
|
||||
@override
|
||||
String get languageChanged => 'Sprache geändert zu';
|
||||
|
||||
@override
|
||||
String get appInfo => 'APP-INFO';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Frachtdetails';
|
||||
|
||||
@override
|
||||
String get itemName => 'Bezeichnung';
|
||||
|
||||
@override
|
||||
String get itemNumber => 'Positions-Nr.';
|
||||
|
||||
@override
|
||||
String get item => 'Position';
|
||||
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
|
||||
@override
|
||||
String get noCargoItems => 'Keine Frachtgüter';
|
||||
|
||||
@override
|
||||
String get noCargoItemsMessage =>
|
||||
'Für diesen Job sind keine Frachtgüter definiert.';
|
||||
|
||||
@override
|
||||
String get article => 'Artikel';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Fotos aufnehmen';
|
||||
|
||||
@override
|
||||
String get photosCount => 'Fotos';
|
||||
|
||||
@override
|
||||
String get checklistPoints => 'Punkte';
|
||||
|
||||
@override
|
||||
String get signatureRequiredText => 'Unterschrift erforderlich';
|
||||
|
||||
@override
|
||||
String get scanBarcodes => 'Barcode scannen';
|
||||
|
||||
@override
|
||||
String get barcodeCount => 'Codes';
|
||||
|
||||
@override
|
||||
String get commentOptional => 'Kommentar';
|
||||
|
||||
@override
|
||||
String get genericTask => 'Allgemeine Aufgabe';
|
||||
|
||||
@override
|
||||
String get complete => 'Abschließen';
|
||||
|
||||
@override
|
||||
String get abort => 'Abbrechen';
|
||||
|
||||
@override
|
||||
String get optional => 'Optional';
|
||||
|
||||
@override
|
||||
String get skipTask => 'Überspringen';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Erstellt';
|
||||
|
||||
@override
|
||||
String get statusPending => 'Wartend';
|
||||
|
||||
@override
|
||||
String get statusAssigned => 'Zugewiesen';
|
||||
|
||||
@override
|
||||
String get statusInProgress => 'In Bearbeitung';
|
||||
|
||||
@override
|
||||
String get statusCompleted => 'Abgeschlossen';
|
||||
|
||||
@override
|
||||
String get statusCancelled => 'Abgebrochen';
|
||||
|
||||
@override
|
||||
String get statusFailed => 'Fehlgeschlagen';
|
||||
|
||||
@override
|
||||
String get priorityLow => 'Niedrig';
|
||||
|
||||
@override
|
||||
String get priorityMedium => 'Mittel';
|
||||
|
||||
@override
|
||||
String get priorityHigh => 'Hoch';
|
||||
|
||||
@override
|
||||
String get priorityUrgent => 'Dringend';
|
||||
}
|
||||
627
app/lib/l10n/app_localizations_en.dart
Normal file
@@ -0,0 +1,627 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsEn extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'English';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇬🇧';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
|
||||
@override
|
||||
String get cancel => 'Cancel';
|
||||
|
||||
@override
|
||||
String get save => 'Save';
|
||||
|
||||
@override
|
||||
String get delete => 'Delete';
|
||||
|
||||
@override
|
||||
String get close => 'Close';
|
||||
|
||||
@override
|
||||
String get confirm => 'Confirm';
|
||||
|
||||
@override
|
||||
String get error => 'Error';
|
||||
|
||||
@override
|
||||
String get success => 'Success';
|
||||
|
||||
@override
|
||||
String get loading => 'Loading...';
|
||||
|
||||
@override
|
||||
String get refresh => 'Refresh';
|
||||
|
||||
@override
|
||||
String get version => 'Version';
|
||||
|
||||
@override
|
||||
String get unknown => 'Unknown';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Yesterday';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Jobs';
|
||||
|
||||
@override
|
||||
String get availableJobs => 'Order List';
|
||||
|
||||
@override
|
||||
String get chats => 'Chats';
|
||||
|
||||
@override
|
||||
String get settings => 'Settings';
|
||||
|
||||
@override
|
||||
String get logout => 'Logout';
|
||||
|
||||
@override
|
||||
String get logoutConfirm => 'Logout';
|
||||
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Do you really want to logout?';
|
||||
|
||||
@override
|
||||
String get openChat => 'Open Chat';
|
||||
|
||||
@override
|
||||
String get chatInfo => 'Chat Info';
|
||||
|
||||
@override
|
||||
String get routePlan => 'Plan Route';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Welcome Back';
|
||||
|
||||
@override
|
||||
String get loginSubtitle => 'Sign in to your account';
|
||||
|
||||
@override
|
||||
String get email => 'Email';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'Email Address';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Enter your email address';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Please enter your email address';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid => 'Please enter a valid email address';
|
||||
|
||||
@override
|
||||
String get password => 'Password';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Enter your password';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Please enter your password';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Password must be at least 6 characters long';
|
||||
|
||||
@override
|
||||
String get login => 'Login';
|
||||
|
||||
@override
|
||||
String get loggingIn => 'Connecting...';
|
||||
|
||||
@override
|
||||
String get forgotPassword => 'Forgot Password?';
|
||||
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Forgot password feature not yet implemented';
|
||||
|
||||
@override
|
||||
String get loginSuccess => 'Successfully logged out';
|
||||
|
||||
@override
|
||||
String get loginFailed => 'Login failed';
|
||||
|
||||
@override
|
||||
String get connectionFailed => 'Connection to server failed (Timeout).';
|
||||
|
||||
@override
|
||||
String get connectionTimeout => 'Connection to server failed (Timeout).';
|
||||
|
||||
@override
|
||||
String get connecting => 'Connecting to server...';
|
||||
|
||||
@override
|
||||
String get connectionError => 'Connection error';
|
||||
|
||||
@override
|
||||
String get loginError => 'Error during login';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'No Jobs Assigned';
|
||||
|
||||
@override
|
||||
String get noJobsMessage => 'Your assigned jobs will be displayed here.';
|
||||
|
||||
@override
|
||||
String get pullToRefresh => 'Pull down to refresh';
|
||||
|
||||
@override
|
||||
String get newLabel => 'NEW';
|
||||
|
||||
@override
|
||||
String get tasksToComplete => 'Tasks to Complete';
|
||||
|
||||
@override
|
||||
String get pickup => 'Pickup';
|
||||
|
||||
@override
|
||||
String get delivery => 'Delivery';
|
||||
|
||||
@override
|
||||
String get created => 'Created';
|
||||
|
||||
@override
|
||||
String get status => 'Status';
|
||||
|
||||
@override
|
||||
String get priority => 'Priority';
|
||||
|
||||
@override
|
||||
String get dueDate => 'Due Date';
|
||||
|
||||
@override
|
||||
String get location => 'Location';
|
||||
|
||||
@override
|
||||
String get description => 'Description';
|
||||
|
||||
@override
|
||||
String get cargo => 'Cargo';
|
||||
|
||||
@override
|
||||
String get quantity => 'Quantity';
|
||||
|
||||
@override
|
||||
String get weight => 'Weight';
|
||||
|
||||
@override
|
||||
String get dimensions => 'Dimensions';
|
||||
|
||||
@override
|
||||
String get jobDeleted => 'Job deleted';
|
||||
|
||||
@override
|
||||
String get jobDeleteError => 'Error deleting job';
|
||||
|
||||
@override
|
||||
String get jobCompleted => 'Job completed';
|
||||
|
||||
@override
|
||||
String get from => 'From';
|
||||
|
||||
@override
|
||||
String get to => 'to';
|
||||
|
||||
@override
|
||||
String get jobsUpdated => 'Jobs updated';
|
||||
|
||||
@override
|
||||
String get connectionRestored => 'Connection restored. Loading jobs...';
|
||||
|
||||
@override
|
||||
String get connectionLost => 'Connection lost. Offline.';
|
||||
|
||||
@override
|
||||
String get offline => 'Offline';
|
||||
|
||||
@override
|
||||
String get deleteJob => 'Delete Job';
|
||||
|
||||
@override
|
||||
String get jobRemoved => 'was removed';
|
||||
|
||||
@override
|
||||
String get newJobReceived => 'New job received';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Job Details';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Job Tasks';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Delivery Stations';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Delivery Stations ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'No Delivery Stations';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'This job currently contains no delivery stations.';
|
||||
|
||||
@override
|
||||
String get phone => 'Phone';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Unnamed Station';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Station $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Tasks';
|
||||
|
||||
@override
|
||||
String get noTasks => 'No Tasks';
|
||||
|
||||
@override
|
||||
String get noTasksMessage => 'No tasks defined for this job.';
|
||||
|
||||
@override
|
||||
String get taskOrder => 'Order';
|
||||
|
||||
@override
|
||||
String get confirmationRequired => 'Confirmation Required';
|
||||
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Click the button to complete the task.';
|
||||
|
||||
@override
|
||||
String get checklist => 'Checklist';
|
||||
|
||||
@override
|
||||
String get checklistDescription => 'Please check all items:';
|
||||
|
||||
@override
|
||||
String get completeTask => 'Complete Task';
|
||||
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Do you want to mark this task as completed?';
|
||||
|
||||
@override
|
||||
String get completeTaskNote => 'Note (optional)';
|
||||
|
||||
@override
|
||||
String get taskCompleted => 'Task completed';
|
||||
|
||||
@override
|
||||
String get comment => 'Comment';
|
||||
|
||||
@override
|
||||
String get commentRequired => 'Comment (required)';
|
||||
|
||||
@override
|
||||
String get enterComment => 'Enter Comment';
|
||||
|
||||
@override
|
||||
String get commentDescription => 'Please enter a comment:';
|
||||
|
||||
@override
|
||||
String get finish => 'Finish';
|
||||
|
||||
@override
|
||||
String get signature => 'Signature';
|
||||
|
||||
@override
|
||||
String get signatureCapture => 'Capture Signature';
|
||||
|
||||
@override
|
||||
String get signatureRequired => 'Please capture a signature.';
|
||||
|
||||
@override
|
||||
String get clear => 'Clear';
|
||||
|
||||
@override
|
||||
String get signatureError => 'Error saving signature';
|
||||
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Please sign in the field below (mouse or finger).';
|
||||
|
||||
@override
|
||||
String get photoCapture => 'Take Photos';
|
||||
@override
|
||||
String get requiredPhotos => 'Required Photos';
|
||||
@override
|
||||
String get photosTaken => 'Taken';
|
||||
|
||||
@override
|
||||
String get photos => 'Photos';
|
||||
|
||||
@override
|
||||
String get takePhoto => 'Take Photo';
|
||||
|
||||
@override
|
||||
String get selectFromLibrary => 'Select from Library';
|
||||
|
||||
@override
|
||||
String get retakePhoto => 'Retake';
|
||||
|
||||
@override
|
||||
String get photoRequired => 'Photo required';
|
||||
|
||||
@override
|
||||
String get minPhotos => 'At least';
|
||||
|
||||
@override
|
||||
String get maxPhotos => 'Maximum';
|
||||
|
||||
@override
|
||||
String get photoError => 'Error taking photo';
|
||||
|
||||
@override
|
||||
String get deletePhoto => 'Delete Photo';
|
||||
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Do you really want to delete this photo?';
|
||||
|
||||
@override
|
||||
String get barcode => 'Barcode';
|
||||
|
||||
@override
|
||||
String get barcodeScan => 'Scan Barcode';
|
||||
|
||||
@override
|
||||
String get scanBarcode => 'Scan Barcode';
|
||||
|
||||
@override
|
||||
String get barcodeRequired => 'Barcode required';
|
||||
|
||||
@override
|
||||
String get minBarcodes => 'At least';
|
||||
|
||||
@override
|
||||
String get maxBarcodes => 'Maximum';
|
||||
|
||||
@override
|
||||
String get scanned => 'Scanned';
|
||||
|
||||
@override
|
||||
String get scannedBarcodes => 'Scanned Barcodes';
|
||||
|
||||
@override
|
||||
String get barcodesRequired => 'Barcodes Required';
|
||||
|
||||
@override
|
||||
String get enterBarcode => 'Enter Barcode';
|
||||
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Please enter the barcodes:';
|
||||
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Barcode $number (required)';
|
||||
|
||||
@override
|
||||
String barcodeNumberOptional(int number) => 'Barcode $number (optional)';
|
||||
|
||||
@override
|
||||
String get barcodeError => 'Error scanning barcode';
|
||||
|
||||
@override
|
||||
String get cameraError => 'Error initializing camera';
|
||||
|
||||
@override
|
||||
String get cameraNotReady => 'Camera is not ready or not available';
|
||||
|
||||
@override
|
||||
String get cameraNotAvailable => 'Camera not available';
|
||||
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'The camera is not supported on this platform.';
|
||||
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'Not supported on this platform';
|
||||
|
||||
@override
|
||||
String get maxPhotosReached => 'Maximum reached';
|
||||
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Camera ready (no preview)';
|
||||
|
||||
@override
|
||||
String get cameraLoading => 'Camera loading...';
|
||||
|
||||
@override
|
||||
String get cameraInitializing => 'Initializing camera...';
|
||||
|
||||
@override
|
||||
String get cameraLoadingMessage => 'Please wait while the camera is loading';
|
||||
|
||||
@override
|
||||
String get addPhotos => 'Add photos';
|
||||
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Use the "Select photo" button to add images from your camera or hard drive.';
|
||||
|
||||
@override
|
||||
String get photoOf => 'of';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Type a message...';
|
||||
|
||||
@override
|
||||
String get send => 'Send';
|
||||
|
||||
@override
|
||||
String get noSender => 'No sender available';
|
||||
|
||||
@override
|
||||
String get noSenderMessage => 'No sender available. Please login again.';
|
||||
|
||||
@override
|
||||
String get noRecipient => 'No recipient configured';
|
||||
|
||||
@override
|
||||
String get noRecipientMessage => 'No recipient configured for this chat.';
|
||||
|
||||
@override
|
||||
String get messageSendError => 'Message could not be sent.';
|
||||
|
||||
@override
|
||||
String get photoSendError => 'Photo could not be sent.';
|
||||
|
||||
@override
|
||||
String get photoProcessError => 'Photo could not be processed.';
|
||||
|
||||
@override
|
||||
String get imageSendError => 'Image could not be sent.';
|
||||
|
||||
@override
|
||||
String get chatTypeJob => 'Job-specific';
|
||||
|
||||
@override
|
||||
String get chatTypeGeneral => 'General';
|
||||
|
||||
@override
|
||||
String get jobNumber => 'Job Number';
|
||||
|
||||
@override
|
||||
String get messages => 'Messages';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'General Messages';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'No messages yet';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'No chats available';
|
||||
|
||||
@override
|
||||
String get selectPhoto => 'Select Photo';
|
||||
|
||||
@override
|
||||
String get unreadMessages => 'Unread Messages';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Cargo Details';
|
||||
|
||||
@override
|
||||
String get itemName => 'Description';
|
||||
|
||||
@override
|
||||
String get itemNumber => 'Item Number';
|
||||
|
||||
@override
|
||||
String get item => 'Item';
|
||||
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
|
||||
@override
|
||||
String get noCargoItems => 'No Cargo Items';
|
||||
|
||||
@override
|
||||
String get noCargoItemsMessage => 'No cargo items defined for this job.';
|
||||
|
||||
@override
|
||||
String get article => 'Article';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Take Photos';
|
||||
|
||||
@override
|
||||
String get photosCount => 'Photos';
|
||||
|
||||
@override
|
||||
String get checklistPoints => 'Points';
|
||||
|
||||
@override
|
||||
String get signatureRequiredText => 'Signature Required';
|
||||
|
||||
@override
|
||||
String get scanBarcodes => 'Scan Barcodes';
|
||||
|
||||
@override
|
||||
String get barcodeCount => 'Codes';
|
||||
|
||||
@override
|
||||
String get commentOptional => 'Comment';
|
||||
|
||||
@override
|
||||
String get genericTask => 'Generic Task';
|
||||
|
||||
@override
|
||||
String get complete => 'Complete';
|
||||
|
||||
@override
|
||||
String get abort => 'Cancel';
|
||||
|
||||
@override
|
||||
String get optional => 'Optional';
|
||||
|
||||
@override
|
||||
String get skipTask => 'Skip';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Language';
|
||||
|
||||
@override
|
||||
String get languageChanged => 'Language changed to';
|
||||
|
||||
@override
|
||||
String get appInfo => 'APP INFO';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Created';
|
||||
|
||||
@override
|
||||
String get statusPending => 'Pending';
|
||||
|
||||
@override
|
||||
String get statusAssigned => 'Assigned';
|
||||
|
||||
@override
|
||||
String get statusInProgress => 'In Progress';
|
||||
|
||||
@override
|
||||
String get statusCompleted => 'Completed';
|
||||
|
||||
@override
|
||||
String get statusCancelled => 'Cancelled';
|
||||
|
||||
@override
|
||||
String get statusFailed => 'Failed';
|
||||
|
||||
@override
|
||||
String get priorityLow => 'Low';
|
||||
|
||||
@override
|
||||
String get priorityMedium => 'Medium';
|
||||
|
||||
@override
|
||||
String get priorityHigh => 'High';
|
||||
|
||||
@override
|
||||
String get priorityUrgent => 'Urgent';
|
||||
}
|
||||
470
app/lib/l10n/app_localizations_es.dart
Normal file
@@ -0,0 +1,470 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsEs extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Español';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇪🇸';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@override
|
||||
String get cancel => 'Cancelar';
|
||||
@override
|
||||
String get save => 'Guardar';
|
||||
@override
|
||||
String get delete => 'Eliminar';
|
||||
@override
|
||||
String get close => 'Cerrar';
|
||||
@override
|
||||
String get confirm => 'Confirmar';
|
||||
@override
|
||||
String get error => 'Error';
|
||||
@override
|
||||
String get success => 'Éxito';
|
||||
@override
|
||||
String get loading => 'Cargando...';
|
||||
@override
|
||||
String get refresh => 'Actualizar';
|
||||
@override
|
||||
String get version => 'Versión';
|
||||
@override
|
||||
String get unknown => 'Desconocido';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Ayer';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Trabajos';
|
||||
@override
|
||||
String get availableJobs => 'Lista de pedidos';
|
||||
@override
|
||||
String get chats => 'Chats';
|
||||
@override
|
||||
String get settings => 'Ajustes';
|
||||
@override
|
||||
String get logout => 'Cerrar sesión';
|
||||
@override
|
||||
String get logoutConfirm => 'Cerrar sesión';
|
||||
@override
|
||||
String get logoutConfirmMessage => '¿Realmente desea cerrar sesión?';
|
||||
@override
|
||||
String get openChat => 'Abrir chat';
|
||||
@override
|
||||
String get chatInfo => 'Info del chat';
|
||||
@override
|
||||
String get routePlan => 'Planificar ruta';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Bienvenido de nuevo';
|
||||
@override
|
||||
String get loginSubtitle => 'Inicie sesión en su cuenta';
|
||||
@override
|
||||
String get email => 'Correo electrónico';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'Dirección de correo electrónico';
|
||||
|
||||
@override
|
||||
String get emailAddressHint =>
|
||||
'Introduzca su dirección de correo electrónico';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired =>
|
||||
'Por favor, introduzca su dirección de correo electrónico';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid =>
|
||||
'Por favor, introduzca una dirección de correo electrónico válida';
|
||||
@override
|
||||
String get password => 'Contraseña';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Introduzca su contraseña';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Por favor, introduzca su contraseña';
|
||||
|
||||
@override
|
||||
String get passwordMinLength =>
|
||||
'La contraseña debe tener al menos 6 caracteres';
|
||||
@override
|
||||
String get login => 'Iniciar sesión';
|
||||
@override
|
||||
String get loggingIn => 'Conectando...';
|
||||
@override
|
||||
String get forgotPassword => '¿Olvidó su contraseña?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Función de contraseña olvidada aún no implementada';
|
||||
@override
|
||||
String get loginSuccess => 'Sesión cerrada correctamente';
|
||||
@override
|
||||
String get loginFailed => 'Error al iniciar sesión';
|
||||
@override
|
||||
String get connectionFailed =>
|
||||
'Error de conexión al servidor (Tiempo agotado).';
|
||||
@override
|
||||
String get connectionTimeout =>
|
||||
'Error de conexión al servidor (Tiempo agotado).';
|
||||
@override
|
||||
String get connecting => 'Conectando al servidor...';
|
||||
@override
|
||||
String get connectionError => 'Error de conexión';
|
||||
@override
|
||||
String get loginError => 'Error durante el inicio de sesión';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'No hay trabajos asignados';
|
||||
@override
|
||||
String get noJobsMessage => 'Sus trabajos asignados se mostrarán aquí.';
|
||||
@override
|
||||
String get pullToRefresh => 'Deslice hacia abajo para actualizar';
|
||||
@override
|
||||
String get newLabel => 'NUEVO';
|
||||
@override
|
||||
String get tasksToComplete => 'Tareas por completar';
|
||||
@override
|
||||
String get pickup => 'Recogida';
|
||||
@override
|
||||
String get delivery => 'Entrega';
|
||||
@override
|
||||
String get created => 'Creado';
|
||||
@override
|
||||
String get status => 'Estado';
|
||||
@override
|
||||
String get priority => 'Prioridad';
|
||||
@override
|
||||
String get dueDate => 'Fecha de vencimiento';
|
||||
@override
|
||||
String get location => 'Ubicación';
|
||||
@override
|
||||
String get description => 'Descripción';
|
||||
@override
|
||||
String get cargo => 'Carga';
|
||||
@override
|
||||
String get quantity => 'Cantidad';
|
||||
@override
|
||||
String get weight => 'Peso';
|
||||
@override
|
||||
String get dimensions => 'Dimensiones';
|
||||
@override
|
||||
String get jobDeleted => 'Trabajo eliminado';
|
||||
@override
|
||||
String get jobDeleteError => 'Error al eliminar el trabajo';
|
||||
@override
|
||||
String get jobCompleted => 'Trabajo completado';
|
||||
@override
|
||||
String get from => 'De';
|
||||
@override
|
||||
String get to => 'a';
|
||||
@override
|
||||
String get jobsUpdated => 'Trabajos actualizados';
|
||||
@override
|
||||
String get connectionRestored => 'Conexión restaurada. Cargando trabajos...';
|
||||
@override
|
||||
String get connectionLost => 'Conexión perdida. Sin conexión.';
|
||||
@override
|
||||
String get offline => 'Sin conexión';
|
||||
@override
|
||||
String get deleteJob => 'Eliminar trabajo';
|
||||
@override
|
||||
String get jobRemoved => 'fue eliminado';
|
||||
@override
|
||||
String get newJobReceived => 'Nuevo trabajo recibido';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Detalles del pedido';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Tareas del pedido';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Estaciones de entrega';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Estaciones de entrega ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'No hay estaciones de entrega';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Este trabajo no contiene estaciones de entrega actualmente.';
|
||||
|
||||
@override
|
||||
String get phone => 'Teléfono';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Estación sin nombre';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Estación $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Tareas';
|
||||
@override
|
||||
String get noTasks => 'Sin tareas';
|
||||
@override
|
||||
String get noTasksMessage => 'No hay tareas definidas para este trabajo.';
|
||||
@override
|
||||
String get taskOrder => 'Orden';
|
||||
@override
|
||||
String get confirmationRequired => 'Confirmación requerida';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Haga clic en el botón para completar la tarea.';
|
||||
@override
|
||||
String get checklist => 'Lista de verificación';
|
||||
@override
|
||||
String get checklistDescription => 'Por favor marque todos los elementos:';
|
||||
@override
|
||||
String get completeTask => 'Completar tarea';
|
||||
@override
|
||||
String get completeTaskConfirm => '¿Desea marcar esta tarea como completada?';
|
||||
@override
|
||||
String get completeTaskNote => 'Nota (opcional)';
|
||||
@override
|
||||
String get taskCompleted => 'Tarea completada';
|
||||
@override
|
||||
String get comment => 'Comentario';
|
||||
@override
|
||||
String get commentRequired => 'Comentario (requerido)';
|
||||
@override
|
||||
String get enterComment => 'Ingrese comentario';
|
||||
@override
|
||||
String get commentDescription => 'Por favor ingrese un comentario:';
|
||||
@override
|
||||
String get finish => 'Finalizar';
|
||||
@override
|
||||
String get signature => 'Firma';
|
||||
@override
|
||||
String get signatureCapture => 'Capturar firma';
|
||||
@override
|
||||
String get signatureRequired => 'Por favor capture una firma.';
|
||||
@override
|
||||
String get clear => 'Limpiar';
|
||||
@override
|
||||
String get signatureError => 'Error al guardar la firma';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Por favor, firme en el campo de abajo (ratón o dedo).';
|
||||
@override
|
||||
String get photoCapture => 'Tomar fotos';
|
||||
@override
|
||||
String get requiredPhotos => 'Fotos requeridas';
|
||||
@override
|
||||
String get photosTaken => 'Tomadas';
|
||||
@override
|
||||
String get photos => 'Fotos';
|
||||
@override
|
||||
String get takePhoto => 'Tomar foto';
|
||||
@override
|
||||
String get selectFromLibrary => 'Seleccionar de la biblioteca';
|
||||
@override
|
||||
String get retakePhoto => 'Volver a tomar';
|
||||
@override
|
||||
String get photoRequired => 'Foto requerida';
|
||||
@override
|
||||
String get minPhotos => 'Al menos';
|
||||
@override
|
||||
String get maxPhotos => 'Máximo';
|
||||
@override
|
||||
String get photoError => 'Error al tomar la foto';
|
||||
@override
|
||||
String get deletePhoto => 'Eliminar foto';
|
||||
@override
|
||||
String get deletePhotoConfirm => '¿Realmente desea eliminar esta foto?';
|
||||
@override
|
||||
String get barcode => 'Código de barras';
|
||||
@override
|
||||
String get barcodeScan => 'Escanear código de barras';
|
||||
@override
|
||||
String get scanBarcode => 'Escanear código de barras';
|
||||
@override
|
||||
String get barcodeRequired => 'Código de barras requerido';
|
||||
@override
|
||||
String get minBarcodes => 'Al menos';
|
||||
@override
|
||||
String get maxBarcodes => 'Máximo';
|
||||
@override
|
||||
String get scanned => 'Escaneado';
|
||||
@override
|
||||
String get scannedBarcodes => 'Códigos de barras escaneados';
|
||||
@override
|
||||
String get barcodesRequired => 'Códigos de barras requeridos';
|
||||
@override
|
||||
String get enterBarcode => 'Ingresar código de barras';
|
||||
@override
|
||||
String get barcodeEnterDescription =>
|
||||
'Por favor ingrese los códigos de barras:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) =>
|
||||
'Código de barras $number (requerido)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) =>
|
||||
'Código de barras $number (opcional)';
|
||||
@override
|
||||
String get barcodeError => 'Error al escanear el código de barras';
|
||||
@override
|
||||
String get cameraError => 'Error al inicializar la cámara';
|
||||
@override
|
||||
String get cameraNotReady => 'La cámara no está lista o no disponible';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Cámara no disponible';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'La cámara no es compatible con esta plataforma.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'No soportado en esta plataforma';
|
||||
@override
|
||||
String get maxPhotosReached => 'Máximo alcanzado';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Cámara lista (sin vista previa)';
|
||||
@override
|
||||
String get cameraLoading => 'Cargando cámara...';
|
||||
@override
|
||||
String get cameraInitializing => 'Inicializando cámara...';
|
||||
@override
|
||||
String get cameraLoadingMessage =>
|
||||
'Por favor espere mientras se carga la cámara';
|
||||
@override
|
||||
String get addPhotos => 'Añadir fotos';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Use el botón "Seleccionar foto" para añadir imágenes de su cámara o disco duro.';
|
||||
@override
|
||||
String get photoOf => 'de';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Escriba un mensaje...';
|
||||
@override
|
||||
String get send => 'Enviar';
|
||||
@override
|
||||
String get noSender => 'No hay remitente disponible';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'No hay remitente disponible. Por favor inicie sesión de nuevo.';
|
||||
@override
|
||||
String get noRecipient => 'No hay destinatario configurado';
|
||||
@override
|
||||
String get noRecipientMessage =>
|
||||
'No hay destinatario configurado para este chat.';
|
||||
@override
|
||||
String get messageSendError => 'El mensaje no pudo ser enviado.';
|
||||
@override
|
||||
String get photoSendError => 'La foto no pudo ser enviada.';
|
||||
@override
|
||||
String get photoProcessError => 'La foto no pudo ser procesada.';
|
||||
@override
|
||||
String get imageSendError => 'La imagen no pudo ser enviada.';
|
||||
@override
|
||||
String get chatTypeJob => 'Específico del trabajo';
|
||||
@override
|
||||
String get chatTypeGeneral => 'General';
|
||||
@override
|
||||
String get jobNumber => 'Número de trabajo';
|
||||
@override
|
||||
String get messages => 'Mensajes';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Mensajes generales';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Todavía no hay mensajes';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'No hay chats disponibles';
|
||||
@override
|
||||
String get selectPhoto => 'Seleccionar foto';
|
||||
@override
|
||||
String get unreadMessages => 'Mensajes no leídos';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Detalles de carga';
|
||||
@override
|
||||
String get itemName => 'Descripción';
|
||||
@override
|
||||
String get itemNumber => 'Nº de posición';
|
||||
@override
|
||||
String get item => 'Posición';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Sin artículos de carga';
|
||||
@override
|
||||
String get noCargoItemsMessage =>
|
||||
'No hay artículos de carga definidos para este trabajo.';
|
||||
@override
|
||||
String get article => 'Artículo';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Tomar fotos';
|
||||
@override
|
||||
String get photosCount => 'Fotos';
|
||||
@override
|
||||
String get checklistPoints => 'Puntos';
|
||||
@override
|
||||
String get signatureRequiredText => 'Firma requerida';
|
||||
@override
|
||||
String get scanBarcodes => 'Escanear códigos';
|
||||
@override
|
||||
String get barcodeCount => 'Códigos';
|
||||
@override
|
||||
String get commentOptional => 'Comentario';
|
||||
@override
|
||||
String get genericTask => 'Tarea genérica';
|
||||
@override
|
||||
String get complete => 'Completar';
|
||||
@override
|
||||
String get abort => 'Cancelar';
|
||||
@override
|
||||
String get optional => 'Opcional';
|
||||
@override
|
||||
String get skipTask => 'Omitir';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Idioma';
|
||||
@override
|
||||
String get languageChanged => 'Idioma cambiado a';
|
||||
@override
|
||||
String get appInfo => 'INFO DE LA APP';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Creado';
|
||||
@override
|
||||
String get statusPending => 'Pendiente';
|
||||
@override
|
||||
String get statusAssigned => 'Asignado';
|
||||
@override
|
||||
String get statusInProgress => 'En progreso';
|
||||
@override
|
||||
String get statusCompleted => 'Completado';
|
||||
@override
|
||||
String get statusCancelled => 'Cancelado';
|
||||
@override
|
||||
String get statusFailed => 'Fallido';
|
||||
@override
|
||||
String get priorityLow => 'Baja';
|
||||
@override
|
||||
String get priorityMedium => 'Media';
|
||||
@override
|
||||
String get priorityHigh => 'Alta';
|
||||
@override
|
||||
String get priorityUrgent => 'Urgente';
|
||||
}
|
||||
462
app/lib/l10n/app_localizations_et.dart
Normal file
@@ -0,0 +1,462 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsEt extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Eesti';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇪🇪';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@override
|
||||
String get cancel => 'Tühista';
|
||||
@override
|
||||
String get save => 'Salvesta';
|
||||
@override
|
||||
String get delete => 'Kustuta';
|
||||
@override
|
||||
String get close => 'Sulge';
|
||||
@override
|
||||
String get confirm => 'Kinnita';
|
||||
@override
|
||||
String get error => 'Viga';
|
||||
@override
|
||||
String get success => 'Edu';
|
||||
@override
|
||||
String get loading => 'Laadimine...';
|
||||
@override
|
||||
String get refresh => 'Värskenda';
|
||||
@override
|
||||
String get version => 'Versioon';
|
||||
@override
|
||||
String get unknown => 'Tundmatu';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Eile';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Tööd';
|
||||
@override
|
||||
String get availableJobs => 'Tellimuste loend';
|
||||
@override
|
||||
String get chats => 'Vestlused';
|
||||
@override
|
||||
String get settings => 'Seaded';
|
||||
@override
|
||||
String get logout => 'Logi välja';
|
||||
@override
|
||||
String get logoutConfirm => 'Logi välja';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Kas soovite tõesti välja logida?';
|
||||
@override
|
||||
String get openChat => 'Ava vestlus';
|
||||
@override
|
||||
String get chatInfo => 'Vestluse info';
|
||||
@override
|
||||
String get routePlan => 'Kavanda marsruut';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Tere tulemast tagasi';
|
||||
@override
|
||||
String get loginSubtitle => 'Logige oma kontosse sisse';
|
||||
@override
|
||||
String get email => 'E-post';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'E-posti aadress';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Sisestage oma e-posti aadress';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Palun sisestage oma e-posti aadress';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid => 'Palun sisestage kehtiv e-posti aadress';
|
||||
@override
|
||||
String get password => 'Parool';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Sisestage oma parool';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Palun sisestage oma parool';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Parool peab olema vähemalt 6 tähemärki pikk';
|
||||
@override
|
||||
String get login => 'Logi sisse';
|
||||
@override
|
||||
String get loggingIn => 'Ühendamine...';
|
||||
@override
|
||||
String get forgotPassword => 'Unustasid parooli?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Unustatud parooli funktsioon pole veel rakendatud';
|
||||
@override
|
||||
String get loginSuccess => 'Edukalt välja logitud';
|
||||
@override
|
||||
String get loginFailed => 'Sisselogimine ebaõnnestus';
|
||||
@override
|
||||
String get connectionFailed =>
|
||||
'Serveriga ühenduse loomine ebaõnnestus (Aegunud).';
|
||||
@override
|
||||
String get connectionTimeout =>
|
||||
'Serveriga ühenduse loomine ebaõnnestus (Aegunud).';
|
||||
@override
|
||||
String get connecting => 'Serveriga ühendamine...';
|
||||
@override
|
||||
String get connectionError => 'Ühenduse viga';
|
||||
@override
|
||||
String get loginError => 'Viga sisselogimisel';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Ülesandeid pole määratud';
|
||||
@override
|
||||
String get noJobsMessage => 'Teie määratud tööd kuvatakse siin.';
|
||||
@override
|
||||
String get pullToRefresh => 'Värskendamiseks tõmmake alla';
|
||||
@override
|
||||
String get newLabel => 'UUS';
|
||||
@override
|
||||
String get tasksToComplete => 'Täitmiseks ülesanded';
|
||||
@override
|
||||
String get pickup => 'Pealevõtt';
|
||||
@override
|
||||
String get delivery => 'Kohaletoimetamine';
|
||||
@override
|
||||
String get created => 'Loodud';
|
||||
@override
|
||||
String get status => 'Olek';
|
||||
@override
|
||||
String get priority => 'Prioriteet';
|
||||
@override
|
||||
String get dueDate => 'Tähtaeg';
|
||||
@override
|
||||
String get location => 'Asukoht';
|
||||
@override
|
||||
String get description => 'Kirjeldus';
|
||||
@override
|
||||
String get cargo => 'Kaup';
|
||||
@override
|
||||
String get quantity => 'Kogus';
|
||||
@override
|
||||
String get weight => 'Kaal';
|
||||
@override
|
||||
String get dimensions => 'Mõõtmed';
|
||||
@override
|
||||
String get jobDeleted => 'Töö kustutatud';
|
||||
@override
|
||||
String get jobDeleteError => 'Viga töö kustutamisel';
|
||||
@override
|
||||
String get jobCompleted => 'Töö lõpetatud';
|
||||
@override
|
||||
String get from => 'Kust';
|
||||
@override
|
||||
String get to => 'kus';
|
||||
@override
|
||||
String get jobsUpdated => 'Tööd värskendatud';
|
||||
@override
|
||||
String get connectionRestored => 'Ühendus taastatud. Tööde laadimine...';
|
||||
@override
|
||||
String get connectionLost => 'Ühendus kaotatud. Võrguühenduseta.';
|
||||
@override
|
||||
String get offline => 'Võrguühenduseta';
|
||||
@override
|
||||
String get deleteJob => 'Kustuta töö';
|
||||
@override
|
||||
String get jobRemoved => 'eemaldati';
|
||||
@override
|
||||
String get newJobReceived => 'Uus töö saadud';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Töö üksikasjad';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Töö ülesanded';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Tarnejaamad';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Tarnejaamad ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Tarnejaamu pole';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Sellel tööl ei ole praegu tarnejaamu.';
|
||||
|
||||
@override
|
||||
String get phone => 'Telefon';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Nimetu jaam';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Jaam $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Ülesanded';
|
||||
@override
|
||||
String get noTasks => 'Ülesandeid pole';
|
||||
@override
|
||||
String get noTasksMessage => 'Selle töö jaoks pole ülesandeid määratud.';
|
||||
@override
|
||||
String get taskOrder => 'Järjekord';
|
||||
@override
|
||||
String get confirmationRequired => 'Vajalik kinnitus';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Ülesande lõpuleviimiseks klõpsake nuppu.';
|
||||
@override
|
||||
String get checklist => 'Kontrollnimekiri';
|
||||
@override
|
||||
String get checklistDescription => 'Palun märkige kõik punktid:';
|
||||
@override
|
||||
String get completeTask => 'Lõpeta ülesanne';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Kas soovite selle ülesande lõpetatuks märgistada?';
|
||||
@override
|
||||
String get completeTaskNote => 'Märkus (valikuline)';
|
||||
@override
|
||||
String get taskCompleted => 'Ülesanne lõpetatud';
|
||||
@override
|
||||
String get comment => 'Kommentaar';
|
||||
@override
|
||||
String get commentRequired => 'Kommentaar (nõutav)';
|
||||
@override
|
||||
String get enterComment => 'Sisesta kommentaar';
|
||||
@override
|
||||
String get commentDescription => 'Palun sisestage kommentaar:';
|
||||
@override
|
||||
String get finish => 'Lõpeta';
|
||||
@override
|
||||
String get signature => 'Allkiri';
|
||||
@override
|
||||
String get signatureCapture => 'Salvesta allkiri';
|
||||
@override
|
||||
String get signatureRequired => 'Palun salvestage allkiri.';
|
||||
@override
|
||||
String get clear => 'Tühjenda';
|
||||
@override
|
||||
String get signatureError => 'Viga allkirja salvestamisel';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Palun allkirjastage allolevas väljas (hiir või sõrm).';
|
||||
@override
|
||||
String get photoCapture => 'Tee pilte';
|
||||
@override
|
||||
String get requiredPhotos => 'Vajalikud fotod';
|
||||
@override
|
||||
String get photosTaken => 'Tehtud';
|
||||
@override
|
||||
String get photos => 'Fotod';
|
||||
@override
|
||||
String get takePhoto => 'Tee foto';
|
||||
@override
|
||||
String get selectFromLibrary => 'Vali galeriist';
|
||||
@override
|
||||
String get retakePhoto => 'Pildista uuesti';
|
||||
@override
|
||||
String get photoRequired => 'Foto nõutav';
|
||||
@override
|
||||
String get minPhotos => 'Vähemalt';
|
||||
@override
|
||||
String get maxPhotos => 'Maksimum';
|
||||
@override
|
||||
String get photoError => 'Viga foto tegemisel';
|
||||
@override
|
||||
String get deletePhoto => 'Kustuta foto';
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Kas soovite tõesti selle foto kustutada?';
|
||||
@override
|
||||
String get barcode => 'Vöötkood';
|
||||
@override
|
||||
String get barcodeScan => 'Skaneeri vöötkood';
|
||||
@override
|
||||
String get scanBarcode => 'Skaneeri vöötkood';
|
||||
@override
|
||||
String get barcodeRequired => 'Vöötkood nõutav';
|
||||
@override
|
||||
String get minBarcodes => 'Vähemalt';
|
||||
@override
|
||||
String get maxBarcodes => 'Maksimum';
|
||||
@override
|
||||
String get scanned => 'Skaneeritud';
|
||||
@override
|
||||
String get scannedBarcodes => 'Skaneeritud vöötkoodid';
|
||||
@override
|
||||
String get barcodesRequired => 'Vöötkoodid nõutavad';
|
||||
@override
|
||||
String get enterBarcode => 'Sisesta vöötkood';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Palun sisestage vöötkoodid:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Vöötkood $number (nõutav)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) => 'Vöötkood $number (valikuline)';
|
||||
@override
|
||||
String get barcodeError => 'Viga vöötkoodi skaneerimisel';
|
||||
@override
|
||||
String get cameraError => 'Viga kaamera käivitamisel';
|
||||
@override
|
||||
String get cameraNotReady => 'Kaamera pole valmis või pole saadaval';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Kaamera pole saadaval';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Kaamerat ei toetata sellel platvormil.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'Sellel platvormil ei toetata';
|
||||
@override
|
||||
String get maxPhotosReached => 'Maksimaalne arv saavutatud';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Kaamera valmis (eelvaade puudub)';
|
||||
@override
|
||||
String get cameraLoading => 'Kaamera laadib...';
|
||||
@override
|
||||
String get cameraInitializing => 'Kaamera initsialiseerimine...';
|
||||
@override
|
||||
String get cameraLoadingMessage => 'Palun oodake, kuni kaamera laadib';
|
||||
@override
|
||||
String get addPhotos => 'Lisa fotod';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Kasutage nuppu "Vali foto", et lisada pilte kaamerast või kõvakettalt.';
|
||||
@override
|
||||
String get photoOf => '/';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Sisesta sõnum...';
|
||||
@override
|
||||
String get send => 'Saada';
|
||||
@override
|
||||
String get noSender => 'Saatja pole saadaval';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Saatja pole saadaval. Palun logige uuesti sisse.';
|
||||
@override
|
||||
String get noRecipient => 'Vastuvõtjat pole konfigureeritud';
|
||||
@override
|
||||
String get noRecipientMessage =>
|
||||
'Selle vestluse jaoks pole vastuvõtjat konfigureeritud.';
|
||||
@override
|
||||
String get messageSendError => 'Sõnumi saatmine ebaõnnestus.';
|
||||
@override
|
||||
String get photoSendError => 'Foto saatmine ebaõnnestus.';
|
||||
@override
|
||||
String get photoProcessError => 'Foto töötlemine ebaõnnestus.';
|
||||
@override
|
||||
String get imageSendError => 'Pildi saatmine ebaõnnestus.';
|
||||
@override
|
||||
String get chatTypeJob => 'Töö-spetsiifiline';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Üldine';
|
||||
@override
|
||||
String get jobNumber => 'Töö number';
|
||||
@override
|
||||
String get messages => 'Sõnumid';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Üldised sõnumid';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Sõnumeid veel pole';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Vestlusi pole saadaval';
|
||||
@override
|
||||
String get selectPhoto => 'Vali foto';
|
||||
@override
|
||||
String get unreadMessages => 'Lugemata sõnumid';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Kauba detailid';
|
||||
@override
|
||||
String get itemName => 'Kirjeldus';
|
||||
@override
|
||||
String get itemNumber => 'Positsiooni nr';
|
||||
@override
|
||||
String get item => 'Positsioon';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Kaubaosi puuduvad';
|
||||
@override
|
||||
String get noCargoItemsMessage => 'Selle töö jaoks pole kaubaosi määratud.';
|
||||
@override
|
||||
String get article => 'Artikkel';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Tee pilte';
|
||||
@override
|
||||
String get photosCount => 'Fotod';
|
||||
@override
|
||||
String get checklistPoints => 'Punktid';
|
||||
@override
|
||||
String get signatureRequiredText => 'Allkiri nõutav';
|
||||
@override
|
||||
String get scanBarcodes => 'Skaneeri vöötkoode';
|
||||
@override
|
||||
String get barcodeCount => 'Koodid';
|
||||
@override
|
||||
String get commentOptional => 'Kommentaar';
|
||||
@override
|
||||
String get genericTask => 'Üldine ülesanne';
|
||||
@override
|
||||
String get complete => 'Lõpeta';
|
||||
@override
|
||||
String get abort => 'Tühista';
|
||||
@override
|
||||
String get optional => 'Valikuline';
|
||||
@override
|
||||
String get skipTask => 'Vahele jätta';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Keel';
|
||||
@override
|
||||
String get languageChanged => 'Keel muudetud:';
|
||||
@override
|
||||
String get appInfo => 'RAKENDUSE INFO';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Loodud';
|
||||
@override
|
||||
String get statusPending => 'Ootel';
|
||||
@override
|
||||
String get statusAssigned => 'Määratud';
|
||||
@override
|
||||
String get statusInProgress => 'Töös';
|
||||
@override
|
||||
String get statusCompleted => 'Lõpetatud';
|
||||
@override
|
||||
String get statusCancelled => 'Tühistatud';
|
||||
@override
|
||||
String get statusFailed => 'Ebaõnnestunud';
|
||||
@override
|
||||
String get priorityLow => 'Madal';
|
||||
@override
|
||||
String get priorityMedium => 'Keskmine';
|
||||
@override
|
||||
String get priorityHigh => 'Kõrge';
|
||||
@override
|
||||
String get priorityUrgent => 'Kiire';
|
||||
}
|
||||
469
app/lib/l10n/app_localizations_fr.dart
Normal file
@@ -0,0 +1,469 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsFr extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Français';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇫🇷';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@override
|
||||
String get cancel => 'Annuler';
|
||||
@override
|
||||
String get save => 'Enregistrer';
|
||||
@override
|
||||
String get delete => 'Supprimer';
|
||||
@override
|
||||
String get close => 'Fermer';
|
||||
@override
|
||||
String get confirm => 'Confirmer';
|
||||
@override
|
||||
String get error => 'Erreur';
|
||||
@override
|
||||
String get success => 'Succès';
|
||||
@override
|
||||
String get loading => 'Chargement...';
|
||||
@override
|
||||
String get refresh => 'Actualiser';
|
||||
@override
|
||||
String get version => 'Version';
|
||||
@override
|
||||
String get unknown => 'Inconnu';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Hier';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Emplois';
|
||||
@override
|
||||
String get availableJobs => 'Liste des commandes';
|
||||
@override
|
||||
String get chats => 'Discussions';
|
||||
@override
|
||||
String get settings => 'Paramètres';
|
||||
@override
|
||||
String get logout => 'Déconnexion';
|
||||
@override
|
||||
String get logoutConfirm => 'Déconnexion';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Voulez-vous vraiment vous déconnecter?';
|
||||
@override
|
||||
String get openChat => 'Ouvrir la discussion';
|
||||
@override
|
||||
String get chatInfo => 'Info discussion';
|
||||
@override
|
||||
String get routePlan => 'Planifier l\'itinéraire';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Bon retour';
|
||||
@override
|
||||
String get loginSubtitle => 'Connectez-vous à votre compte';
|
||||
@override
|
||||
String get email => 'E-mail';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'Adresse e-mail';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Saisissez votre adresse e-mail';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Veuillez saisir votre adresse e-mail';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid => 'Veuillez saisir une adresse e-mail valide';
|
||||
@override
|
||||
String get password => 'Mot de passe';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Saisissez votre mot de passe';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Veuillez saisir votre mot de passe';
|
||||
|
||||
@override
|
||||
String get passwordMinLength =>
|
||||
'Le mot de passe doit contenir au moins 6 caractères';
|
||||
@override
|
||||
String get login => 'Connexion';
|
||||
@override
|
||||
String get loggingIn => 'Connexion...';
|
||||
@override
|
||||
String get forgotPassword => 'Mot de passe oublié?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Fonction mot de passe oublié pas encore implémentée';
|
||||
@override
|
||||
String get loginSuccess => 'Déconnexion réussie';
|
||||
@override
|
||||
String get loginFailed => 'Échec de la connexion';
|
||||
@override
|
||||
String get connectionFailed =>
|
||||
'Échec de la connexion au serveur (Délai dépassé).';
|
||||
@override
|
||||
String get connectionTimeout =>
|
||||
'Échec de la connexion au serveur (Délai dépassé).';
|
||||
@override
|
||||
String get connecting => 'Connexion au serveur...';
|
||||
@override
|
||||
String get connectionError => 'Erreur de connexion';
|
||||
@override
|
||||
String get loginError => 'Erreur lors de la connexion';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Aucun emploi assigné';
|
||||
@override
|
||||
String get noJobsMessage => 'Vos emplois assignés seront affichés ici.';
|
||||
@override
|
||||
String get pullToRefresh => 'Tirez vers le bas pour actualiser';
|
||||
@override
|
||||
String get newLabel => 'NOUVEAU';
|
||||
@override
|
||||
String get tasksToComplete => 'Tâches à accomplir';
|
||||
@override
|
||||
String get pickup => 'Ramassage';
|
||||
@override
|
||||
String get delivery => 'Livraison';
|
||||
@override
|
||||
String get created => 'Créé';
|
||||
@override
|
||||
String get status => 'Statut';
|
||||
@override
|
||||
String get priority => 'Priorité';
|
||||
@override
|
||||
String get dueDate => 'Date d\'échéance';
|
||||
@override
|
||||
String get location => 'Lieu';
|
||||
@override
|
||||
String get description => 'Description';
|
||||
@override
|
||||
String get cargo => 'Cargaison';
|
||||
@override
|
||||
String get quantity => 'Quantité';
|
||||
@override
|
||||
String get weight => 'Poids';
|
||||
@override
|
||||
String get dimensions => 'Dimensions';
|
||||
@override
|
||||
String get jobDeleted => 'Emploi supprimé';
|
||||
@override
|
||||
String get jobDeleteError => 'Erreur lors de la suppression de l\'emploi';
|
||||
@override
|
||||
String get jobCompleted => 'Emploi terminé';
|
||||
@override
|
||||
String get from => 'De';
|
||||
@override
|
||||
String get to => 'à';
|
||||
@override
|
||||
String get jobsUpdated => 'Emplois actualisés';
|
||||
@override
|
||||
String get connectionRestored =>
|
||||
'Connexion restaurée. Chargement des emplois...';
|
||||
@override
|
||||
String get connectionLost => 'Connexion perdue. Hors ligne.';
|
||||
@override
|
||||
String get offline => 'Hors ligne';
|
||||
@override
|
||||
String get deleteJob => 'Supprimer l\'emploi';
|
||||
@override
|
||||
String get jobRemoved => 'a été supprimé';
|
||||
@override
|
||||
String get newJobReceived => 'Nouvel emploi reçu';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Détails de la commande';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Tâches de la commande';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Stations de livraison';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Stations de livraison ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Aucune station de livraison';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Cette mission ne contient actuellement aucune station de livraison.';
|
||||
|
||||
@override
|
||||
String get phone => 'Téléphone';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Station sans nom';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Station $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Tâches';
|
||||
@override
|
||||
String get noTasks => 'Aucune tâche';
|
||||
@override
|
||||
String get noTasksMessage => 'Aucune tâche définie pour cet emploi.';
|
||||
@override
|
||||
String get taskOrder => 'Ordre';
|
||||
@override
|
||||
String get confirmationRequired => 'Confirmation requise';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Cliquez sur le bouton pour terminer la tâche.';
|
||||
@override
|
||||
String get checklist => 'Liste de contrôle';
|
||||
@override
|
||||
String get checklistDescription => 'Veuillez cocher tous les éléments:';
|
||||
@override
|
||||
String get completeTask => 'Terminer la tâche';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Voulez-vous marquer cette tâche comme terminée?';
|
||||
@override
|
||||
String get completeTaskNote => 'Note (optionnelle)';
|
||||
@override
|
||||
String get taskCompleted => 'Tâche terminée';
|
||||
@override
|
||||
String get comment => 'Commentaire';
|
||||
@override
|
||||
String get commentRequired => 'Commentaire (requis)';
|
||||
@override
|
||||
String get enterComment => 'Saisir un commentaire';
|
||||
@override
|
||||
String get commentDescription => 'Veuillez saisir un commentaire:';
|
||||
@override
|
||||
String get finish => 'Terminer';
|
||||
@override
|
||||
String get signature => 'Signature';
|
||||
@override
|
||||
String get signatureCapture => 'Capturer la signature';
|
||||
@override
|
||||
String get signatureRequired => 'Veuillez capturer une signature.';
|
||||
@override
|
||||
String get clear => 'Effacer';
|
||||
@override
|
||||
String get signatureError =>
|
||||
'Erreur lors de l\'enregistrement de la signature';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Veuillez signer dans le champ ci-dessous (souris ou doigt).';
|
||||
@override
|
||||
String get photoCapture => 'Prendre des photos';
|
||||
@override
|
||||
String get requiredPhotos => 'Photos requises';
|
||||
@override
|
||||
String get photosTaken => 'Prises';
|
||||
@override
|
||||
String get photos => 'Photos';
|
||||
@override
|
||||
String get takePhoto => 'Prendre une photo';
|
||||
@override
|
||||
String get selectFromLibrary => 'Sélectionner depuis la bibliothèque';
|
||||
@override
|
||||
String get retakePhoto => 'Reprendre';
|
||||
@override
|
||||
String get photoRequired => 'Photo requise';
|
||||
@override
|
||||
String get minPhotos => 'Au moins';
|
||||
@override
|
||||
String get maxPhotos => 'Maximum';
|
||||
@override
|
||||
String get photoError => 'Erreur lors de la prise de photo';
|
||||
@override
|
||||
String get deletePhoto => 'Supprimer la photo';
|
||||
@override
|
||||
String get deletePhotoConfirm =>
|
||||
'Voulez-vous vraiment supprimer cette photo?';
|
||||
@override
|
||||
String get barcode => 'Code-barres';
|
||||
@override
|
||||
String get barcodeScan => 'Scanner le code-barres';
|
||||
@override
|
||||
String get scanBarcode => 'Scanner le code-barres';
|
||||
@override
|
||||
String get barcodeRequired => 'Code-barres requis';
|
||||
@override
|
||||
String get minBarcodes => 'Au moins';
|
||||
@override
|
||||
String get maxBarcodes => 'Maximum';
|
||||
@override
|
||||
String get scanned => 'Scanné';
|
||||
@override
|
||||
String get scannedBarcodes => 'Codes-barres scannés';
|
||||
@override
|
||||
String get barcodesRequired => 'Codes-barres requis';
|
||||
@override
|
||||
String get enterBarcode => 'Entrer le code-barres';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Veuillez entrer les codes-barres:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Code-barres $number (requis)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) => 'Code-barres $number (optionnel)';
|
||||
@override
|
||||
String get barcodeError => 'Erreur lors du scan du code-barres';
|
||||
@override
|
||||
String get cameraError => 'Erreur lors de l\'initialisation de la caméra';
|
||||
@override
|
||||
String get cameraNotReady => 'La caméra n\'est pas prête ou non disponible';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Caméra non disponible';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'La caméra n\'est pas prise en charge sur cette plateforme.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform =>
|
||||
'Non supporté sur cette plateforme';
|
||||
@override
|
||||
String get maxPhotosReached => 'Maximum atteint';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Caméra prête (sans aperçu)';
|
||||
@override
|
||||
String get cameraLoading => 'Chargement de la caméra...';
|
||||
@override
|
||||
String get cameraInitializing => 'Initialisation de la caméra...';
|
||||
@override
|
||||
String get cameraLoadingMessage =>
|
||||
'Veuillez patienter pendant le chargement de la caméra';
|
||||
@override
|
||||
String get addPhotos => 'Ajouter des photos';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Utilisez le bouton "Sélectionner une photo" pour ajouter des images depuis votre appareil photo ou disque dur.';
|
||||
@override
|
||||
String get photoOf => 'sur';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Tapez un message...';
|
||||
@override
|
||||
String get send => 'Envoyer';
|
||||
@override
|
||||
String get noSender => 'Aucun expéditeur disponible';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Aucun expéditeur disponible. Veuillez vous reconnecter.';
|
||||
@override
|
||||
String get noRecipient => 'Aucun destinataire configuré';
|
||||
@override
|
||||
String get noRecipientMessage =>
|
||||
'Aucun destinataire configuré pour cette discussion.';
|
||||
@override
|
||||
String get messageSendError => 'Le message n\'a pas pu être envoyé.';
|
||||
@override
|
||||
String get photoSendError => 'La photo n\'a pas pu être envoyée.';
|
||||
@override
|
||||
String get photoProcessError => 'La photo n\'a pas pu être traitée.';
|
||||
@override
|
||||
String get imageSendError => 'L\'image n\'a pas pu être envoyée.';
|
||||
@override
|
||||
String get chatTypeJob => 'Spécifique à l\'emploi';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Général';
|
||||
@override
|
||||
String get jobNumber => 'Numéro d\'emploi';
|
||||
@override
|
||||
String get messages => 'Messages';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Messages généraux';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Pas encore de messages';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Aucune discussion disponible';
|
||||
@override
|
||||
String get selectPhoto => 'Sélectionner une photo';
|
||||
@override
|
||||
String get unreadMessages => 'Messages non lus';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Détails de cargaison';
|
||||
@override
|
||||
String get itemName => 'Description';
|
||||
@override
|
||||
String get itemNumber => 'N° de position';
|
||||
@override
|
||||
String get item => 'Position';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Aucun article de cargaison';
|
||||
@override
|
||||
String get noCargoItemsMessage =>
|
||||
'Aucun article de cargaison défini pour cet emploi.';
|
||||
@override
|
||||
String get article => 'Article';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Prendre des photos';
|
||||
@override
|
||||
String get photosCount => 'Photos';
|
||||
@override
|
||||
String get checklistPoints => 'Points';
|
||||
@override
|
||||
String get signatureRequiredText => 'Signature requise';
|
||||
@override
|
||||
String get scanBarcodes => 'Scanner les codes-barres';
|
||||
@override
|
||||
String get barcodeCount => 'Codes';
|
||||
@override
|
||||
String get commentOptional => 'Commentaire';
|
||||
@override
|
||||
String get genericTask => 'Tâche générique';
|
||||
@override
|
||||
String get complete => 'Terminer';
|
||||
@override
|
||||
String get abort => 'Annuler';
|
||||
@override
|
||||
String get optional => 'Facultatif';
|
||||
@override
|
||||
String get skipTask => 'Ignorer';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Langue';
|
||||
@override
|
||||
String get languageChanged => 'Langue changée en';
|
||||
@override
|
||||
String get appInfo => 'INFO APP';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Créé';
|
||||
@override
|
||||
String get statusPending => 'En attente';
|
||||
@override
|
||||
String get statusAssigned => 'Assigné';
|
||||
@override
|
||||
String get statusInProgress => 'En cours';
|
||||
@override
|
||||
String get statusCompleted => 'Terminé';
|
||||
@override
|
||||
String get statusCancelled => 'Annulé';
|
||||
@override
|
||||
String get statusFailed => 'Échoué';
|
||||
@override
|
||||
String get priorityLow => 'Basse';
|
||||
@override
|
||||
String get priorityMedium => 'Moyenne';
|
||||
@override
|
||||
String get priorityHigh => 'Haute';
|
||||
@override
|
||||
String get priorityUrgent => 'Urgente';
|
||||
}
|
||||
465
app/lib/l10n/app_localizations_lt.dart
Normal file
@@ -0,0 +1,465 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsLt extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Lietuvių';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇱🇹';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'Gerai';
|
||||
@override
|
||||
String get cancel => 'Atšaukti';
|
||||
@override
|
||||
String get save => 'Išsaugoti';
|
||||
@override
|
||||
String get delete => 'Ištrinti';
|
||||
@override
|
||||
String get close => 'Uždaryti';
|
||||
@override
|
||||
String get confirm => 'Patvirtinti';
|
||||
@override
|
||||
String get error => 'Klaida';
|
||||
@override
|
||||
String get success => 'Sėkmė';
|
||||
@override
|
||||
String get loading => 'Kraunama...';
|
||||
@override
|
||||
String get refresh => 'Atnaujinti';
|
||||
@override
|
||||
String get version => 'Versija';
|
||||
@override
|
||||
String get unknown => 'Nežinoma';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Vakar';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Darbai';
|
||||
@override
|
||||
String get availableJobs => 'Užsakymų sąrašas';
|
||||
@override
|
||||
String get chats => 'Pokalbiai';
|
||||
@override
|
||||
String get settings => 'Nustatymai';
|
||||
@override
|
||||
String get logout => 'Atsijungti';
|
||||
@override
|
||||
String get logoutConfirm => 'Atsijungti';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Ar tikrai norite atsijungti?';
|
||||
@override
|
||||
String get openChat => 'Atidaryti pokalbį';
|
||||
@override
|
||||
String get chatInfo => 'Pokalbio info';
|
||||
@override
|
||||
String get routePlan => 'Planuoti maršrutą';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Sveiki sugrįžę';
|
||||
@override
|
||||
String get loginSubtitle => 'Prisijunkite prie savo paskyros';
|
||||
@override
|
||||
String get email => 'El. paštas';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'El. pašto adresas';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Įveskite savo el. pašto adresą';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Prašome įvesti savo el. pašto adresą';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid =>
|
||||
'Prašome įvesti galiojantį el. pašto adresą';
|
||||
@override
|
||||
String get password => 'Slaptažodis';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Įveskite savo slaptažodį';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Prašome įvesti savo slaptažodį';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Slaptažodis turi būti bent 6 simbolių ilgio';
|
||||
@override
|
||||
String get login => 'Prisijungti';
|
||||
@override
|
||||
String get loggingIn => 'Jungiamasi...';
|
||||
@override
|
||||
String get forgotPassword => 'Pamiršote slaptažodį?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Pamiršto slaptažodžio funkcija dar neįdiegta';
|
||||
@override
|
||||
String get loginSuccess => 'Sėkmingai atsijungta';
|
||||
@override
|
||||
String get loginFailed => 'Prisijungimas nepavyko';
|
||||
@override
|
||||
String get connectionFailed =>
|
||||
'Nepavyko prisijungti prie serverio (Laikas baigėsi).';
|
||||
@override
|
||||
String get connectionTimeout =>
|
||||
'Nepavyko prisijungti prie serverio (Laikas baigėsi).';
|
||||
@override
|
||||
String get connecting => 'Jungiamasi prie serverio...';
|
||||
@override
|
||||
String get connectionError => 'Ryšio klaida';
|
||||
@override
|
||||
String get loginError => 'Klaida prisijungiant';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Nėra priskirtų darbų';
|
||||
@override
|
||||
String get noJobsMessage => 'Jūsų priskirti darbai bus rodomi čia.';
|
||||
@override
|
||||
String get pullToRefresh => 'Patraukite žemyn, kad atnaujintumėte';
|
||||
@override
|
||||
String get newLabel => 'NAUJAS';
|
||||
@override
|
||||
String get tasksToComplete => 'Užduotys, kurias reikia atlikti';
|
||||
@override
|
||||
String get pickup => 'Paėmimas';
|
||||
@override
|
||||
String get delivery => 'Pristatymas';
|
||||
@override
|
||||
String get created => 'Sukurta';
|
||||
@override
|
||||
String get status => 'Būsena';
|
||||
@override
|
||||
String get priority => 'Prioritetas';
|
||||
@override
|
||||
String get dueDate => 'Terminas';
|
||||
@override
|
||||
String get location => 'Vieta';
|
||||
@override
|
||||
String get description => 'Aprašymas';
|
||||
@override
|
||||
String get cargo => 'Krovinys';
|
||||
@override
|
||||
String get quantity => 'Kiekis';
|
||||
@override
|
||||
String get weight => 'Svoris';
|
||||
@override
|
||||
String get dimensions => 'Matmenys';
|
||||
@override
|
||||
String get jobDeleted => 'Darbas ištrintas';
|
||||
@override
|
||||
String get jobDeleteError => 'Klaida ištrinant darbą';
|
||||
@override
|
||||
String get jobCompleted => 'Darbas baigtas';
|
||||
@override
|
||||
String get from => 'Iš';
|
||||
@override
|
||||
String get to => 'į';
|
||||
@override
|
||||
String get jobsUpdated => 'Darbai atnaujinti';
|
||||
@override
|
||||
String get connectionRestored => 'Ryšys atkurtas. Kraunami darbai...';
|
||||
@override
|
||||
String get connectionLost => 'Ryšys prarastas. Neprisijungta.';
|
||||
@override
|
||||
String get offline => 'Neprisijungta';
|
||||
@override
|
||||
String get deleteJob => 'Ištrinti darbą';
|
||||
@override
|
||||
String get jobRemoved => 'buvo pašalintas';
|
||||
@override
|
||||
String get newJobReceived => 'Gautas naujas darbas';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Užsakymo detalės';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Užsakymo užduotys';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Pristatymo stotelės';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Pristatymo stotelės ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Nėra pristatymo stotelių';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Ši užduotis šiuo metu neturi pristatymo stotelių.';
|
||||
|
||||
@override
|
||||
String get phone => 'Telefonas';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Neįvardyta stotelė';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Stotelė $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Užduotys';
|
||||
@override
|
||||
String get noTasks => 'Nėra užduočių';
|
||||
@override
|
||||
String get noTasksMessage => 'Šiam darbui nėra apibrėžtų užduočių.';
|
||||
@override
|
||||
String get taskOrder => 'Eilės tvarka';
|
||||
@override
|
||||
String get confirmationRequired => 'Reikalingas patvirtinimas';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Spustelėkite mygtuką, kad atliktumėte užduotį.';
|
||||
@override
|
||||
String get checklist => 'Patikros sąrašas';
|
||||
@override
|
||||
String get checklistDescription => 'Prašome pažymėti visus punktus:';
|
||||
@override
|
||||
String get completeTask => 'Baigti užduotį';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Ar norite pažymėti šią užduotį kaip baigtą?';
|
||||
@override
|
||||
String get completeTaskNote => 'Pastaba (neprivaloma)';
|
||||
@override
|
||||
String get taskCompleted => 'Užduotis baigta';
|
||||
@override
|
||||
String get comment => 'Komentaras';
|
||||
@override
|
||||
String get commentRequired => 'Komentaras (būtinas)';
|
||||
@override
|
||||
String get enterComment => 'Įveskite komentarą';
|
||||
@override
|
||||
String get commentDescription => 'Prašome įvesti komentarą:';
|
||||
@override
|
||||
String get finish => 'Baigti';
|
||||
@override
|
||||
String get signature => 'Parašas';
|
||||
@override
|
||||
String get signatureCapture => 'Įrašyti parašą';
|
||||
@override
|
||||
String get signatureRequired => 'Prašome įrašyti parašą.';
|
||||
@override
|
||||
String get clear => 'Išvalyti';
|
||||
@override
|
||||
String get signatureError => 'Klaida išsaugant parašą';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Prašome pasirašyti laukelyje žemiau (pele arba pirštu).';
|
||||
@override
|
||||
String get photoCapture => 'Daryti nuotraukas';
|
||||
@override
|
||||
String get requiredPhotos => 'Reikalingos nuotraukos';
|
||||
@override
|
||||
String get photosTaken => 'Padaryta';
|
||||
@override
|
||||
String get photos => 'Nuotraukos';
|
||||
@override
|
||||
String get takePhoto => 'Daryti nuotrauką';
|
||||
@override
|
||||
String get selectFromLibrary => 'Pasirinkti iš bibliotekos';
|
||||
@override
|
||||
String get retakePhoto => 'Perdaryti';
|
||||
@override
|
||||
String get photoRequired => 'Reikalinga nuotrauka';
|
||||
@override
|
||||
String get minPhotos => 'Mažiausiai';
|
||||
@override
|
||||
String get maxPhotos => 'Daugiausia';
|
||||
@override
|
||||
String get photoError => 'Klaida darant nuotrauką';
|
||||
@override
|
||||
String get deletePhoto => 'Ištrinti nuotrauką';
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Ar tikrai norite ištrinti šią nuotrauką?';
|
||||
@override
|
||||
String get barcode => 'Brūkšninis kodas';
|
||||
@override
|
||||
String get barcodeScan => 'Skaityti brūkšninį kodą';
|
||||
@override
|
||||
String get scanBarcode => 'Skaityti brūkšninį kodą';
|
||||
@override
|
||||
String get barcodeRequired => 'Reikalingas brūkšninis kodas';
|
||||
@override
|
||||
String get minBarcodes => 'Mažiausiai';
|
||||
@override
|
||||
String get maxBarcodes => 'Daugiausia';
|
||||
@override
|
||||
String get scanned => 'Nuskaityta';
|
||||
@override
|
||||
String get scannedBarcodes => 'Nuskaityti brūkšniniai kodai';
|
||||
@override
|
||||
String get barcodesRequired => 'Reikalingi brūkšniniai kodai';
|
||||
@override
|
||||
String get enterBarcode => 'Įveskite brūkšninį kodą';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Prašome įvesti brūkšninius kodus:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) =>
|
||||
'Brūkšninis kodas $number (būtinas)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) =>
|
||||
'Brūkšninis kodas $number (neprivalomas)';
|
||||
@override
|
||||
String get barcodeError => 'Klaida skaitant brūkšninį kodą';
|
||||
@override
|
||||
String get cameraError => 'Klaida inicializuojant kamerą';
|
||||
@override
|
||||
String get cameraNotReady => 'Kamera nėra pasiruošusi arba nepasiekiama';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Kamera nepasiekiama';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Šioje platformoje kamera nepalaikoma.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'Nepalaikoma šioje platformoje';
|
||||
@override
|
||||
String get maxPhotosReached => 'Pasiektas maksimumas';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Kamera paruošta (be peržiūros)';
|
||||
@override
|
||||
String get cameraLoading => 'Kamera kraunama...';
|
||||
@override
|
||||
String get cameraInitializing => 'Kamera inicializuojama...';
|
||||
@override
|
||||
String get cameraLoadingMessage => 'Palaukite, kol kamera įkraunama';
|
||||
@override
|
||||
String get addPhotos => 'Pridėti nuotraukas';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Naudokite mygtuką "Pasirinkti nuotrauką", norėdami pridėti vaizdų iš fotoaparato ar standžiojo disko.';
|
||||
@override
|
||||
String get photoOf => 'iš';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Įveskite žinutę...';
|
||||
@override
|
||||
String get send => 'Siųsti';
|
||||
@override
|
||||
String get noSender => 'Siuntėjas nepasiekiamas';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Siuntėjas nepasiekiamas. Prašome prisijungti dar kartą.';
|
||||
@override
|
||||
String get noRecipient => 'Gavėjas nesukonfigūruotas';
|
||||
@override
|
||||
String get noRecipientMessage => 'Šiam pokalbiui nesukonfigūruotas gavėjas.';
|
||||
@override
|
||||
String get messageSendError => 'Žinutės išsiųsti nepavyko.';
|
||||
@override
|
||||
String get photoSendError => 'Nuotraukos išsiųsti nepavyko.';
|
||||
@override
|
||||
String get photoProcessError => 'Nuotraukos apdoroti nepavyko.';
|
||||
@override
|
||||
String get imageSendError => 'Vaizdo išsiųsti nepavyko.';
|
||||
@override
|
||||
String get chatTypeJob => 'Specifinis darbui';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Bendras';
|
||||
@override
|
||||
String get jobNumber => 'Darbo numeris';
|
||||
@override
|
||||
String get messages => 'Žinutės';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Bendri pranešimai';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Pranešimų dar nėra';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Nėra galimų pokalbių';
|
||||
@override
|
||||
String get selectPhoto => 'Pasirinkti nuotrauką';
|
||||
@override
|
||||
String get unreadMessages => 'Neskaitytos žinutės';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Krovinio detalės';
|
||||
@override
|
||||
String get itemName => 'Aprašymas';
|
||||
@override
|
||||
String get itemNumber => 'Pozicijos Nr.';
|
||||
@override
|
||||
String get item => 'Pozicija';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Nėra krovinių pozicijų';
|
||||
@override
|
||||
String get noCargoItemsMessage =>
|
||||
'Šiam darbui nėra apibrėžtų krovinių pozicijų.';
|
||||
@override
|
||||
String get article => 'Pozicija';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Daryti nuotraukas';
|
||||
@override
|
||||
String get photosCount => 'Nuotraukos';
|
||||
@override
|
||||
String get checklistPoints => 'Taškai';
|
||||
@override
|
||||
String get signatureRequiredText => 'Parašas būtinas';
|
||||
@override
|
||||
String get scanBarcodes => 'Skaityti brūkšninius kodus';
|
||||
@override
|
||||
String get barcodeCount => 'Kodai';
|
||||
@override
|
||||
String get commentOptional => 'Komentaras';
|
||||
@override
|
||||
String get genericTask => 'Bendra užduotis';
|
||||
@override
|
||||
String get complete => 'Baigti';
|
||||
@override
|
||||
String get abort => 'Atšaukti';
|
||||
@override
|
||||
String get optional => 'Neprivaloma';
|
||||
@override
|
||||
String get skipTask => 'Praleisti';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Kalba';
|
||||
@override
|
||||
String get languageChanged => 'Kalba pakeista į';
|
||||
@override
|
||||
String get appInfo => 'PROGRAMĖLĖS INFO';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Sukurta';
|
||||
@override
|
||||
String get statusPending => 'Laukiama';
|
||||
@override
|
||||
String get statusAssigned => 'Priskirta';
|
||||
@override
|
||||
String get statusInProgress => 'Vykdoma';
|
||||
@override
|
||||
String get statusCompleted => 'Baigta';
|
||||
@override
|
||||
String get statusCancelled => 'Atšaukta';
|
||||
@override
|
||||
String get statusFailed => 'Nepavyko';
|
||||
@override
|
||||
String get priorityLow => 'Žemas';
|
||||
@override
|
||||
String get priorityMedium => 'Vidutinis';
|
||||
@override
|
||||
String get priorityHigh => 'Aukštas';
|
||||
@override
|
||||
String get priorityUrgent => 'Skubus';
|
||||
}
|
||||
460
app/lib/l10n/app_localizations_lv.dart
Normal file
@@ -0,0 +1,460 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsLv extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Latviešu';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇱🇻';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'Labi';
|
||||
@override
|
||||
String get cancel => 'Atcelt';
|
||||
@override
|
||||
String get save => 'Saglabāt';
|
||||
@override
|
||||
String get delete => 'Dzēst';
|
||||
@override
|
||||
String get close => 'Aizvērt';
|
||||
@override
|
||||
String get confirm => 'Apstiprināt';
|
||||
@override
|
||||
String get error => 'Kļūda';
|
||||
@override
|
||||
String get success => 'Veiksmīgi';
|
||||
@override
|
||||
String get loading => 'Ielādē...';
|
||||
@override
|
||||
String get refresh => 'Atsvaidzināt';
|
||||
@override
|
||||
String get version => 'Versija';
|
||||
@override
|
||||
String get unknown => 'Nezināms';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Vakar';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Darbi';
|
||||
@override
|
||||
String get availableJobs => 'Pasūtījumu saraksts';
|
||||
@override
|
||||
String get chats => 'Tērzēšanas';
|
||||
@override
|
||||
String get settings => 'Iestatījumi';
|
||||
@override
|
||||
String get logout => 'Iziet';
|
||||
@override
|
||||
String get logoutConfirm => 'Iziet';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Vai tiešām vēlaties iziet?';
|
||||
@override
|
||||
String get openChat => 'Atvērt tērzēšanu';
|
||||
@override
|
||||
String get chatInfo => 'Tērzēšanas info';
|
||||
@override
|
||||
String get routePlan => 'Plānot maršrutu';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Laipni lūgti atpakaļ';
|
||||
@override
|
||||
String get loginSubtitle => 'Pierakstieties savā kontā';
|
||||
@override
|
||||
String get email => 'E-pasts';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'E-pasta adrese';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Ievadiet savu e-pasta adresi';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Lūdzu, ievadiet savu e-pasta adresi';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid => 'Lūdzu, ievadiet derīgu e-pasta adresi';
|
||||
@override
|
||||
String get password => 'Parole';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Ievadiet savu paroli';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Lūdzu, ievadiet savu paroli';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Parolei jābūt vismaz 6 rakstzīmes garai';
|
||||
@override
|
||||
String get login => 'Pierakstīties';
|
||||
@override
|
||||
String get loggingIn => 'Savienojas...';
|
||||
@override
|
||||
String get forgotPassword => 'Aizmirsāt paroli?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Aizmirstās paroles funkcija vēl nav ieviesta';
|
||||
@override
|
||||
String get loginSuccess => 'Veiksmīgi izrakstījās';
|
||||
@override
|
||||
String get loginFailed => 'Pierakstīšanās neizdevās';
|
||||
@override
|
||||
String get connectionFailed => 'Savienojuma kļūda ar serveri (Noildze).';
|
||||
@override
|
||||
String get connectionTimeout => 'Savienojuma kļūda ar serveri (Noildze).';
|
||||
@override
|
||||
String get connecting => 'Savienojas ar serveri...';
|
||||
@override
|
||||
String get connectionError => 'Savienojuma kļūda';
|
||||
@override
|
||||
String get loginError => 'Kļūda pierakstīšanās laikā';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Nav piešķirtu darbu';
|
||||
@override
|
||||
String get noJobsMessage => 'Jūsu piešķirtie darbi tiks parādīti šeit.';
|
||||
@override
|
||||
String get pullToRefresh => 'Velciet uz leju, lai atsvaidzinātu';
|
||||
@override
|
||||
String get newLabel => 'JAUNS';
|
||||
@override
|
||||
String get tasksToComplete => 'Uzdevumi, kas jāveic';
|
||||
@override
|
||||
String get pickup => 'Saņemšana';
|
||||
@override
|
||||
String get delivery => 'Piegāde';
|
||||
@override
|
||||
String get created => 'Izveidots';
|
||||
@override
|
||||
String get status => 'Statuss';
|
||||
@override
|
||||
String get priority => 'Prioritāte';
|
||||
@override
|
||||
String get dueDate => 'Izpildes termiņš';
|
||||
@override
|
||||
String get location => 'Atrašanās vieta';
|
||||
@override
|
||||
String get description => 'Apraksts';
|
||||
@override
|
||||
String get cargo => 'Krava';
|
||||
@override
|
||||
String get quantity => 'Daudzums';
|
||||
@override
|
||||
String get weight => 'Svars';
|
||||
@override
|
||||
String get dimensions => 'Izmēri';
|
||||
@override
|
||||
String get jobDeleted => 'Darbs izdzēsts';
|
||||
@override
|
||||
String get jobDeleteError => 'Kļūda dzēšot darbu';
|
||||
@override
|
||||
String get jobCompleted => 'Darbs pabeigts';
|
||||
@override
|
||||
String get from => 'No';
|
||||
@override
|
||||
String get to => 'uz';
|
||||
@override
|
||||
String get jobsUpdated => 'Darbi atsvaidzināti';
|
||||
@override
|
||||
String get connectionRestored => 'Savienojums atjaunots. Ielādē darbus...';
|
||||
@override
|
||||
String get connectionLost => 'Savienojums pazaudēts. Bezsaistē.';
|
||||
@override
|
||||
String get offline => 'Bezsaistē';
|
||||
@override
|
||||
String get deleteJob => 'Dzēst darbu';
|
||||
@override
|
||||
String get jobRemoved => 'tika noņemts';
|
||||
@override
|
||||
String get newJobReceived => 'Saņemts jauns darbs';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Darba detaļas';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Darba uzdevumi';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Piegādes stacijas';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Piegādes stacijas ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Nav piegādes staciju';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Šajā darbā pašlaik nav piegādes staciju.';
|
||||
|
||||
@override
|
||||
String get phone => 'Tālrunis';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Nenosaukta stacija';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Stacija $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Uzdevumi';
|
||||
@override
|
||||
String get noTasks => 'Nav uzdevumu';
|
||||
@override
|
||||
String get noTasksMessage => 'Šim darbam nav definētu uzdevumu.';
|
||||
@override
|
||||
String get taskOrder => 'Secība';
|
||||
@override
|
||||
String get confirmationRequired => 'Nepieciešams apstiprinājums';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Noklikšķiniet uz pogas, lai pabeigtu uzdevumu.';
|
||||
@override
|
||||
String get checklist => 'Pārbaudes saraksts';
|
||||
@override
|
||||
String get checklistDescription => 'Lūdzu, atzīmējiet visus punktus:';
|
||||
@override
|
||||
String get completeTask => 'Pabeigt uzdevumu';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Vai vēlaties atzīmēt šo uzdevumu kā pabeigtu?';
|
||||
@override
|
||||
String get completeTaskNote => 'Piezīme (neobligāta)';
|
||||
@override
|
||||
String get taskCompleted => 'Uzdevums pabeigts';
|
||||
@override
|
||||
String get comment => 'Komentārs';
|
||||
@override
|
||||
String get commentRequired => 'Komentārs (obligāts)';
|
||||
@override
|
||||
String get enterComment => 'Ievadiet komentāru';
|
||||
@override
|
||||
String get commentDescription => 'Lūdzu, ievadiet komentāru:';
|
||||
@override
|
||||
String get finish => 'Pabeigt';
|
||||
@override
|
||||
String get signature => 'Paraksts';
|
||||
@override
|
||||
String get signatureCapture => 'Uzņemt parakstu';
|
||||
@override
|
||||
String get signatureRequired => 'Lūdzu, uzņemiet parakstu.';
|
||||
@override
|
||||
String get clear => 'Notīrīt';
|
||||
@override
|
||||
String get signatureError => 'Kļūda saglabājot parakstu';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Lūdzu parakstieties zemāk esošajā laukā (pele vai pirksts).';
|
||||
@override
|
||||
String get photoCapture => 'Uzņemt fotogrāfijas';
|
||||
@override
|
||||
String get requiredPhotos => 'Nepieciešamās fotogrāfijas';
|
||||
@override
|
||||
String get photosTaken => 'Uzņemtas';
|
||||
@override
|
||||
String get photos => 'Fotogrāfijas';
|
||||
@override
|
||||
String get takePhoto => 'Uzņemt fotogrāfiju';
|
||||
@override
|
||||
String get selectFromLibrary => 'Izvēlēties no bibliotēkas';
|
||||
@override
|
||||
String get retakePhoto => 'Uzņemt vēlreiz';
|
||||
@override
|
||||
String get photoRequired => 'Nepieciešama fotogrāfija';
|
||||
@override
|
||||
String get minPhotos => 'Vismaz';
|
||||
@override
|
||||
String get maxPhotos => 'Maksimums';
|
||||
@override
|
||||
String get photoError => 'Kļūda uzņemot fotogrāfiju';
|
||||
@override
|
||||
String get deletePhoto => 'Dzēst fotogrāfiju';
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Vai tiešām vēlaties dzēst šo fotogrāfiju?';
|
||||
@override
|
||||
String get barcode => 'Svītrkods';
|
||||
@override
|
||||
String get barcodeScan => 'Skenēt svītrkodu';
|
||||
@override
|
||||
String get scanBarcode => 'Skenēt svītrkodu';
|
||||
@override
|
||||
String get barcodeRequired => 'Nepieciešams svītrkods';
|
||||
@override
|
||||
String get minBarcodes => 'Vismaz';
|
||||
@override
|
||||
String get maxBarcodes => 'Maksimums';
|
||||
@override
|
||||
String get scanned => 'Skenēts';
|
||||
@override
|
||||
String get scannedBarcodes => 'Skenēti svītrkodi';
|
||||
@override
|
||||
String get barcodesRequired => 'Nepieciešami svītrkodi';
|
||||
@override
|
||||
String get enterBarcode => 'Ievadiet svītrkodu';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Lūdzu, ievadiet svītrkodus:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Svītrkods $number (obligāts)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) => 'Svītrkods $number (neobligāts)';
|
||||
@override
|
||||
String get barcodeError => 'Kļūda skenējot svītrkodu';
|
||||
@override
|
||||
String get cameraError => 'Kļūda inicializējot kameru';
|
||||
@override
|
||||
String get cameraNotReady => 'Kamera nav gatava vai nav pieejama';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Kamera nav pieejama';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Šajā platformā kamera netiek atbalstīta.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'Šajā platformā netiek atbalstīts';
|
||||
@override
|
||||
String get maxPhotosReached => 'Maksimums sasniegts';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Kamera gatava (bez priekšskatījuma)';
|
||||
@override
|
||||
String get cameraLoading => 'Kamera ielādē...';
|
||||
@override
|
||||
String get cameraInitializing => 'Kamera tiek inicializēta...';
|
||||
@override
|
||||
String get cameraLoadingMessage =>
|
||||
'Lūdzu, uzgaidiet, kamēr kamera tiek ielādēta';
|
||||
@override
|
||||
String get addPhotos => 'Pievienot fotogrāfijas';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Izmantojiet pogu "Izvēlēties fotogrāfiju", lai pievienotu attēlus no kameras vai cietā diska.';
|
||||
@override
|
||||
String get photoOf => 'no';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Ierakstiet ziņojumu...';
|
||||
@override
|
||||
String get send => 'Sūtīt';
|
||||
@override
|
||||
String get noSender => 'Sūtītājs nav pieejams';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Sūtītājs nav pieejams. Lūdzu, piesakieties vēlreiz.';
|
||||
@override
|
||||
String get noRecipient => 'Saņēmējs nav konfigurēts';
|
||||
@override
|
||||
String get noRecipientMessage => 'Šai tērzēšanai nav konfigurēts saņēmējs.';
|
||||
@override
|
||||
String get messageSendError => 'Ziņojumu neizdevās nosūtīt.';
|
||||
@override
|
||||
String get photoSendError => 'Fotogrāfiju neizdevās nosūtīt.';
|
||||
@override
|
||||
String get photoProcessError => 'Fotogrāfiju neizdevās apstrādāt.';
|
||||
@override
|
||||
String get imageSendError => 'Attēlu neizdevās nosūtīt.';
|
||||
@override
|
||||
String get chatTypeJob => 'Darba specifisks';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Vispārējs';
|
||||
@override
|
||||
String get jobNumber => 'Darba numurs';
|
||||
@override
|
||||
String get messages => 'Ziņojumi';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Vispārīgi ziņojumi';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Ziņojumu vēl nav';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Nav pieejamu tērzēšanu';
|
||||
@override
|
||||
String get selectPhoto => 'Izvēlēties fotogrāfiju';
|
||||
@override
|
||||
String get unreadMessages => 'Nelasīti ziņojumi';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Kravas detaļas';
|
||||
@override
|
||||
String get itemName => 'Apraksts';
|
||||
@override
|
||||
String get itemNumber => 'Pozīcijas Nr.';
|
||||
@override
|
||||
String get item => 'Pozīcija';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Nav kravas pozīciju';
|
||||
@override
|
||||
String get noCargoItemsMessage => 'Šim darbam nav definētu kravas pozīciju.';
|
||||
@override
|
||||
String get article => 'Pozīcija';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Uzņemt fotogrāfijas';
|
||||
@override
|
||||
String get photosCount => 'Fotogrāfijas';
|
||||
@override
|
||||
String get checklistPoints => 'Punkti';
|
||||
@override
|
||||
String get signatureRequiredText => 'Paraksts nepieciešams';
|
||||
@override
|
||||
String get scanBarcodes => 'Skenēt svītrkodus';
|
||||
@override
|
||||
String get barcodeCount => 'Kodi';
|
||||
@override
|
||||
String get commentOptional => 'Komentārs';
|
||||
@override
|
||||
String get genericTask => 'Vispārējs uzdevums';
|
||||
@override
|
||||
String get complete => 'Pabeigt';
|
||||
@override
|
||||
String get abort => 'Atcelt';
|
||||
@override
|
||||
String get optional => 'Neobligāts';
|
||||
@override
|
||||
String get skipTask => 'Izlaist';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Valoda';
|
||||
@override
|
||||
String get languageChanged => 'Valoda mainīta uz';
|
||||
@override
|
||||
String get appInfo => 'LIETOTNES INFO';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Izveidots';
|
||||
@override
|
||||
String get statusPending => 'Gaida';
|
||||
@override
|
||||
String get statusAssigned => 'Piešķirts';
|
||||
@override
|
||||
String get statusInProgress => 'Procesā';
|
||||
@override
|
||||
String get statusCompleted => 'Pabeigts';
|
||||
@override
|
||||
String get statusCancelled => 'Atcelts';
|
||||
@override
|
||||
String get statusFailed => 'Neizdevās';
|
||||
@override
|
||||
String get priorityLow => 'Zema';
|
||||
@override
|
||||
String get priorityMedium => 'Vidēja';
|
||||
@override
|
||||
String get priorityHigh => 'Augsta';
|
||||
@override
|
||||
String get priorityUrgent => 'Steidzama';
|
||||
}
|
||||
463
app/lib/l10n/app_localizations_pl.dart
Normal file
@@ -0,0 +1,463 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsPl extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Polski';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇵🇱';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@override
|
||||
String get cancel => 'Anuluj';
|
||||
@override
|
||||
String get save => 'Zapisz';
|
||||
@override
|
||||
String get delete => 'Usuń';
|
||||
@override
|
||||
String get close => 'Zamknij';
|
||||
@override
|
||||
String get confirm => 'Potwierdź';
|
||||
@override
|
||||
String get error => 'Błąd';
|
||||
@override
|
||||
String get success => 'Sukces';
|
||||
@override
|
||||
String get loading => 'Ładowanie...';
|
||||
@override
|
||||
String get refresh => 'Odśwież';
|
||||
@override
|
||||
String get version => 'Wersja';
|
||||
@override
|
||||
String get unknown => 'Nieznany';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Wczoraj';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Zadania';
|
||||
@override
|
||||
String get availableJobs => 'Lista zleceń';
|
||||
@override
|
||||
String get chats => 'Czaty';
|
||||
@override
|
||||
String get settings => 'Ustawienia';
|
||||
@override
|
||||
String get logout => 'Wyloguj';
|
||||
@override
|
||||
String get logoutConfirm => 'Wyloguj';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Czy na pewno chcesz się wylogować?';
|
||||
@override
|
||||
String get openChat => 'Otwórz czat';
|
||||
@override
|
||||
String get chatInfo => 'Info o czacie';
|
||||
@override
|
||||
String get routePlan => 'Planuj trasę';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Witaj ponownie';
|
||||
@override
|
||||
String get loginSubtitle => 'Zaloguj się do swojego konta';
|
||||
@override
|
||||
String get email => 'E-mail';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'Adres e-mail';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Wpisz adres e-mail';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Proszę wpisać adres e-mail';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid => 'Proszę wpisać prawidłowy adres e-mail';
|
||||
@override
|
||||
String get password => 'Hasło';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Wpisz hasło';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Proszę wpisać hasło';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Hasło musi mieć co najmniej 6 znaków';
|
||||
@override
|
||||
String get login => 'Zaloguj';
|
||||
@override
|
||||
String get loggingIn => 'Łączenie...';
|
||||
@override
|
||||
String get forgotPassword => 'Zapomniałeś hasła?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Funkcja zapomnianego hasła jeszcze nie zaimplementowana';
|
||||
@override
|
||||
String get loginSuccess => 'Pomyślnie wylogowano';
|
||||
@override
|
||||
String get loginFailed => 'Logowanie nie powiodło się';
|
||||
@override
|
||||
String get connectionFailed => 'Błąd połączenia z serwerem (Upłynął czas).';
|
||||
@override
|
||||
String get connectionTimeout => 'Błąd połączenia z serwerem (Upłynął czas).';
|
||||
@override
|
||||
String get connecting => 'Łączenie z serwerem...';
|
||||
@override
|
||||
String get connectionError => 'Błąd połączenia';
|
||||
@override
|
||||
String get loginError => 'Błąd podczas logowania';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Brak przypisanych zadań';
|
||||
@override
|
||||
String get noJobsMessage =>
|
||||
'Twoje przypisane zadania będą wyświetlane tutaj.';
|
||||
@override
|
||||
String get pullToRefresh => 'Przeciągnij w dół, aby odświeżyć';
|
||||
@override
|
||||
String get newLabel => 'NOWE';
|
||||
@override
|
||||
String get tasksToComplete => 'Zadania do wykonania';
|
||||
@override
|
||||
String get pickup => 'Odbiór';
|
||||
@override
|
||||
String get delivery => 'Dostawa';
|
||||
@override
|
||||
String get created => 'Utworzono';
|
||||
@override
|
||||
String get status => 'Status';
|
||||
@override
|
||||
String get priority => 'Priorytet';
|
||||
@override
|
||||
String get dueDate => 'Termin';
|
||||
@override
|
||||
String get location => 'Lokalizacja';
|
||||
@override
|
||||
String get description => 'Opis';
|
||||
@override
|
||||
String get cargo => 'Ładunek';
|
||||
@override
|
||||
String get quantity => 'Ilość';
|
||||
@override
|
||||
String get weight => 'Waga';
|
||||
@override
|
||||
String get dimensions => 'Wymiary';
|
||||
@override
|
||||
String get jobDeleted => 'Zadanie usunięte';
|
||||
@override
|
||||
String get jobDeleteError => 'Błąd podczas usuwania zadania';
|
||||
@override
|
||||
String get jobCompleted => 'Zadanie ukończone';
|
||||
@override
|
||||
String get from => 'Z';
|
||||
@override
|
||||
String get to => 'do';
|
||||
@override
|
||||
String get jobsUpdated => 'Zadania zaktualizowane';
|
||||
@override
|
||||
String get connectionRestored => 'Połączenie przywrócone. Ładowanie zadań...';
|
||||
@override
|
||||
String get connectionLost => 'Utracono połączenie. Offline.';
|
||||
@override
|
||||
String get offline => 'Offline';
|
||||
@override
|
||||
String get deleteJob => 'Usuń zadanie';
|
||||
@override
|
||||
String get jobRemoved => 'zostało usunięte';
|
||||
@override
|
||||
String get newJobReceived => 'Otrzymano nowe zadanie';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Szczegóły zlecenia';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Zadania zlecenia';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Stacje dostawy';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Stacje dostawy ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Brak stacji dostawy';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'To zlecenie nie zawiera obecnie żadnych stacji dostawy.';
|
||||
|
||||
@override
|
||||
String get phone => 'Telefon';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Nienazwana stacja';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Stacja $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Zadania';
|
||||
@override
|
||||
String get noTasks => 'Brak zadań';
|
||||
@override
|
||||
String get noTasksMessage => 'Brak zdefiniowanych zadań dla tego zadania.';
|
||||
@override
|
||||
String get taskOrder => 'Kolejność';
|
||||
@override
|
||||
String get confirmationRequired => 'Wymagane potwierdzenie';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Kliknij przycisk, aby ukończyć zadanie.';
|
||||
@override
|
||||
String get checklist => 'Lista kontrolna';
|
||||
@override
|
||||
String get checklistDescription => 'Proszę zaznaczyć wszystkie punkty:';
|
||||
@override
|
||||
String get completeTask => 'Ukończ zadanie';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Czy chcesz oznaczyć to zadanie jako ukończone?';
|
||||
@override
|
||||
String get completeTaskNote => 'Notatka (opcjonalnie)';
|
||||
@override
|
||||
String get taskCompleted => 'Zadanie ukończone';
|
||||
@override
|
||||
String get comment => 'Komentarz';
|
||||
@override
|
||||
String get commentRequired => 'Komentarz (wymagany)';
|
||||
@override
|
||||
String get enterComment => 'Wprowadź komentarz';
|
||||
@override
|
||||
String get commentDescription => 'Proszę wprowadzić komentarz:';
|
||||
@override
|
||||
String get finish => 'Zakończ';
|
||||
@override
|
||||
String get signature => 'Podpis';
|
||||
@override
|
||||
String get signatureCapture => 'Przechwyć podpis';
|
||||
@override
|
||||
String get signatureRequired => 'Proszę przechwycić podpis.';
|
||||
@override
|
||||
String get clear => 'Wyczyść';
|
||||
@override
|
||||
String get signatureError => 'Błąd podczas zapisywania podpisu';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Proszę podpisać się w polu poniżej (mysz lub palec).';
|
||||
@override
|
||||
String get photoCapture => 'Zrób zdjęcia';
|
||||
@override
|
||||
String get requiredPhotos => 'Wymagane zdjęcia';
|
||||
@override
|
||||
String get photosTaken => 'Wykonane';
|
||||
@override
|
||||
String get photos => 'Zdjęcia';
|
||||
@override
|
||||
String get takePhoto => 'Zrób zdjęcie';
|
||||
@override
|
||||
String get selectFromLibrary => 'Wybierz z biblioteki';
|
||||
@override
|
||||
String get retakePhoto => 'Ponów';
|
||||
@override
|
||||
String get photoRequired => 'Zdjęcie wymagane';
|
||||
@override
|
||||
String get minPhotos => 'Co najmniej';
|
||||
@override
|
||||
String get maxPhotos => 'Maksimum';
|
||||
@override
|
||||
String get photoError => 'Błąd podczas robienia zdjęcia';
|
||||
@override
|
||||
String get deletePhoto => 'Usuń zdjęcie';
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Czy na pewno chcesz usunąć to zdjęcie?';
|
||||
@override
|
||||
String get barcode => 'Kod kreskowy';
|
||||
@override
|
||||
String get barcodeScan => 'Skanuj kod kreskowy';
|
||||
@override
|
||||
String get scanBarcode => 'Skanuj kod kreskowy';
|
||||
@override
|
||||
String get barcodeRequired => 'Kod kreskowy wymagany';
|
||||
@override
|
||||
String get minBarcodes => 'Co najmniej';
|
||||
@override
|
||||
String get maxBarcodes => 'Maksimum';
|
||||
@override
|
||||
String get scanned => 'Zeskanowano';
|
||||
@override
|
||||
String get scannedBarcodes => 'Zeskanowane kody kreskowe';
|
||||
@override
|
||||
String get barcodesRequired => 'Wymagane kody kreskowe';
|
||||
@override
|
||||
String get enterBarcode => 'Wprowadź kod kreskowy';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Proszę wprowadzić kody kreskowe:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Kod kreskowy $number (wymagany)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) =>
|
||||
'Kod kreskowy $number (opcjonalny)';
|
||||
@override
|
||||
String get barcodeError => 'Błąd podczas skanowania kodu kreskowego';
|
||||
@override
|
||||
String get cameraError => 'Błąd podczas inicjalizacji kamery';
|
||||
@override
|
||||
String get cameraNotReady => 'Kamera nie jest gotowa lub niedostępna';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Kamera niedostępna';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Kamera nie jest obsługiwana na tej platformie.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'Nieobsługiwane na tej platformie';
|
||||
@override
|
||||
String get maxPhotosReached => 'Maksimum osiągnięte';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Kamera gotowa (bez podglądu)';
|
||||
@override
|
||||
String get cameraLoading => 'Kamera ładuje się...';
|
||||
@override
|
||||
String get cameraInitializing => 'Inicjalizacja kamery...';
|
||||
@override
|
||||
String get cameraLoadingMessage => 'Proszę czekać, trwa ładowanie kamery';
|
||||
@override
|
||||
String get addPhotos => 'Dodaj zdjęcia';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Użyj przycisku "Wybierz zdjęcie", aby dodać obrazy z kamery lub dysku twardego.';
|
||||
@override
|
||||
String get photoOf => 'z';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Wpisz wiadomość...';
|
||||
@override
|
||||
String get send => 'Wyślij';
|
||||
@override
|
||||
String get noSender => 'Brak dostępnego nadawcy';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Brak dostępnego nadawcy. Proszę zalogować się ponownie.';
|
||||
@override
|
||||
String get noRecipient => 'Brak skonfigurowanego odbiorcy';
|
||||
@override
|
||||
String get noRecipientMessage =>
|
||||
'Brak skonfigurowanego odbiorcy dla tego czatu.';
|
||||
@override
|
||||
String get messageSendError => 'Wiadomość nie mogła zostać wysłana.';
|
||||
@override
|
||||
String get photoSendError => 'Zdjęcie nie mogło zostać wysłane.';
|
||||
@override
|
||||
String get photoProcessError => 'Zdjęcie nie mogło zostać przetworzone.';
|
||||
@override
|
||||
String get imageSendError => 'Obraz nie mógł zostać wysłany.';
|
||||
@override
|
||||
String get chatTypeJob => 'Specyficzne dla zadania';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Ogólny';
|
||||
@override
|
||||
String get jobNumber => 'Numer zadania';
|
||||
@override
|
||||
String get messages => 'Wiadomości';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Wiadomości ogólne';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Brak wiadomości';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Brak dostępnych czatów';
|
||||
@override
|
||||
String get selectPhoto => 'Wybierz zdjęcie';
|
||||
@override
|
||||
String get unreadMessages => 'Nieprzeczytane wiadomości';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Szczegóły ładunku';
|
||||
@override
|
||||
String get itemName => 'Opis';
|
||||
@override
|
||||
String get itemNumber => 'Nr pozycji';
|
||||
@override
|
||||
String get item => 'Pozycja';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Brak pozycji ładunku';
|
||||
@override
|
||||
String get noCargoItemsMessage =>
|
||||
'Brak pozycji ładunku zdefiniowanych dla tego zadania.';
|
||||
@override
|
||||
String get article => 'Pozycja';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Zrób zdjęcia';
|
||||
@override
|
||||
String get photosCount => 'Zdjęcia';
|
||||
@override
|
||||
String get checklistPoints => 'Punkty';
|
||||
@override
|
||||
String get signatureRequiredText => 'Wymagany podpis';
|
||||
@override
|
||||
String get scanBarcodes => 'Skanuj kody kreskowe';
|
||||
@override
|
||||
String get barcodeCount => 'Kody';
|
||||
@override
|
||||
String get commentOptional => 'Komentarz';
|
||||
@override
|
||||
String get genericTask => 'Zadanie ogólne';
|
||||
@override
|
||||
String get complete => 'Zakończ';
|
||||
@override
|
||||
String get abort => 'Anuluj';
|
||||
@override
|
||||
String get optional => 'Opcjonalny';
|
||||
@override
|
||||
String get skipTask => 'Pomiń';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Język';
|
||||
@override
|
||||
String get languageChanged => 'Język zmieniony na';
|
||||
@override
|
||||
String get appInfo => 'INFO O APLIKACJI';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Utworzono';
|
||||
@override
|
||||
String get statusPending => 'Oczekujące';
|
||||
@override
|
||||
String get statusAssigned => 'Przypisano';
|
||||
@override
|
||||
String get statusInProgress => 'W trakcie';
|
||||
@override
|
||||
String get statusCompleted => 'Ukończono';
|
||||
@override
|
||||
String get statusCancelled => 'Anulowano';
|
||||
@override
|
||||
String get statusFailed => 'Nieudane';
|
||||
@override
|
||||
String get priorityLow => 'Niski';
|
||||
@override
|
||||
String get priorityMedium => 'Średni';
|
||||
@override
|
||||
String get priorityHigh => 'Wysoki';
|
||||
@override
|
||||
String get priorityUrgent => 'Pilny';
|
||||
}
|
||||
466
app/lib/l10n/app_localizations_ru.dart
Normal file
@@ -0,0 +1,466 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsRu extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Русский';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇷🇺';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'OK';
|
||||
@override
|
||||
String get cancel => 'Отмена';
|
||||
@override
|
||||
String get save => 'Сохранить';
|
||||
@override
|
||||
String get delete => 'Удалить';
|
||||
@override
|
||||
String get close => 'Закрыть';
|
||||
@override
|
||||
String get confirm => 'Подтвердить';
|
||||
@override
|
||||
String get error => 'Ошибка';
|
||||
@override
|
||||
String get success => 'Успех';
|
||||
@override
|
||||
String get loading => 'Загрузка...';
|
||||
@override
|
||||
String get refresh => 'Обновить';
|
||||
@override
|
||||
String get version => 'Версия';
|
||||
@override
|
||||
String get unknown => 'Неизвестно';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Вчера';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'Задания';
|
||||
@override
|
||||
String get availableJobs => 'Список заказов';
|
||||
@override
|
||||
String get chats => 'Чаты';
|
||||
@override
|
||||
String get settings => 'Настройки';
|
||||
@override
|
||||
String get logout => 'Выход';
|
||||
@override
|
||||
String get logoutConfirm => 'Выход';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Вы действительно хотите выйти?';
|
||||
@override
|
||||
String get openChat => 'Открыть чат';
|
||||
@override
|
||||
String get chatInfo => 'Информация о чате';
|
||||
@override
|
||||
String get routePlan => 'Планировать маршрут';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'С возвращением';
|
||||
@override
|
||||
String get loginSubtitle => 'Войдите в свою учетную запись';
|
||||
@override
|
||||
String get email => 'Эл. почта';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'Адрес эл. почты';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'Введите адрес эл. почты';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Пожалуйста, введите адрес эл. почты';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid =>
|
||||
'Пожалуйста, введите корректный адрес эл. почты';
|
||||
@override
|
||||
String get password => 'Пароль';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Введите пароль';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Пожалуйста, введите пароль';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Пароль должен содержать не менее 6 символов';
|
||||
@override
|
||||
String get login => 'Войти';
|
||||
@override
|
||||
String get loggingIn => 'Подключение...';
|
||||
@override
|
||||
String get forgotPassword => 'Забыли пароль?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Функция восстановления пароля еще не реализована';
|
||||
@override
|
||||
String get loginSuccess => 'Успешный выход из системы';
|
||||
@override
|
||||
String get loginFailed => 'Ошибка входа';
|
||||
@override
|
||||
String get connectionFailed => 'Ошибка подключения к серверу (Таймаут).';
|
||||
@override
|
||||
String get connectionTimeout => 'Ошибка подключения к серверу (Таймаут).';
|
||||
@override
|
||||
String get connecting => 'Подключение к серверу...';
|
||||
@override
|
||||
String get connectionError => 'Ошибка подключения';
|
||||
@override
|
||||
String get loginError => 'Ошибка при входе';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Нет назначенных заданий';
|
||||
@override
|
||||
String get noJobsMessage =>
|
||||
'Ваши назначенные задания будут отображаться здесь.';
|
||||
@override
|
||||
String get pullToRefresh => 'Потяните вниз, чтобы обновить';
|
||||
@override
|
||||
String get newLabel => 'НОВОЕ';
|
||||
@override
|
||||
String get tasksToComplete => 'Задачи для выполнения';
|
||||
@override
|
||||
String get pickup => 'Забор';
|
||||
@override
|
||||
String get delivery => 'Доставка';
|
||||
@override
|
||||
String get created => 'Создано';
|
||||
@override
|
||||
String get status => 'Статус';
|
||||
@override
|
||||
String get priority => 'Приоритет';
|
||||
@override
|
||||
String get dueDate => 'Срок выполнения';
|
||||
@override
|
||||
String get location => 'Местоположение';
|
||||
@override
|
||||
String get description => 'Описание';
|
||||
@override
|
||||
String get cargo => 'Груз';
|
||||
@override
|
||||
String get quantity => 'Количество';
|
||||
@override
|
||||
String get weight => 'Вес';
|
||||
@override
|
||||
String get dimensions => 'Размеры';
|
||||
@override
|
||||
String get jobDeleted => 'Задание удалено';
|
||||
@override
|
||||
String get jobDeleteError => 'Ошибка при удалении задания';
|
||||
@override
|
||||
String get jobCompleted => 'Задание завершено';
|
||||
@override
|
||||
String get from => 'Из';
|
||||
@override
|
||||
String get to => 'в';
|
||||
@override
|
||||
String get jobsUpdated => 'Задания обновлены';
|
||||
@override
|
||||
String get connectionRestored =>
|
||||
'Соединение восстановлено. Загрузка заданий...';
|
||||
@override
|
||||
String get connectionLost => 'Соединение потеряно. Офлайн.';
|
||||
@override
|
||||
String get offline => 'Офлайн';
|
||||
@override
|
||||
String get deleteJob => 'Удалить задание';
|
||||
@override
|
||||
String get jobRemoved => 'было удалено';
|
||||
@override
|
||||
String get newJobReceived => 'Получено новое задание';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'Детали заказа';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'Задачи заказа';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Точки доставки';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Точки доставки ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Нет точек доставки';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Для этого заказа сейчас нет точек доставки.';
|
||||
|
||||
@override
|
||||
String get phone => 'Телефон';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Станция без названия';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Станция $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Задачи';
|
||||
@override
|
||||
String get noTasks => 'Нет задач';
|
||||
@override
|
||||
String get noTasksMessage => 'Для этого задания не определены задачи.';
|
||||
@override
|
||||
String get taskOrder => 'Порядок';
|
||||
@override
|
||||
String get confirmationRequired => 'Требуется подтверждение';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Нажмите кнопку, чтобы выполнить задачу.';
|
||||
@override
|
||||
String get checklist => 'Контрольный список';
|
||||
@override
|
||||
String get checklistDescription => 'Пожалуйста, отметьте все пункты:';
|
||||
@override
|
||||
String get completeTask => 'Завершить задачу';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Хотите отметить эту задачу как выполненную?';
|
||||
@override
|
||||
String get completeTaskNote => 'Примечание (необязательно)';
|
||||
@override
|
||||
String get taskCompleted => 'Задача выполнена';
|
||||
@override
|
||||
String get comment => 'Комментарий';
|
||||
@override
|
||||
String get commentRequired => 'Комментарий (обязательно)';
|
||||
@override
|
||||
String get enterComment => 'Введите комментарий';
|
||||
@override
|
||||
String get commentDescription => 'Пожалуйста, введите комментарий:';
|
||||
@override
|
||||
String get finish => 'Готово';
|
||||
@override
|
||||
String get signature => 'Подпись';
|
||||
@override
|
||||
String get signatureCapture => 'Захватить подпись';
|
||||
@override
|
||||
String get signatureRequired => 'Пожалуйста, сделайте подпись.';
|
||||
@override
|
||||
String get clear => 'Очистить';
|
||||
@override
|
||||
String get signatureError => 'Ошибка при сохранении подписи';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Пожалуйста, подпишитесь в поле ниже (мышь или палец).';
|
||||
@override
|
||||
String get photoCapture => 'Сделать фото';
|
||||
@override
|
||||
String get requiredPhotos => 'Необходимые фото';
|
||||
@override
|
||||
String get photosTaken => 'Сделано';
|
||||
@override
|
||||
String get photos => 'Фото';
|
||||
@override
|
||||
String get takePhoto => 'Сделать фото';
|
||||
@override
|
||||
String get selectFromLibrary => 'Выбрать из библиотеки';
|
||||
@override
|
||||
String get retakePhoto => 'Переснять';
|
||||
@override
|
||||
String get photoRequired => 'Требуется фото';
|
||||
@override
|
||||
String get minPhotos => 'Минимум';
|
||||
@override
|
||||
String get maxPhotos => 'Максимум';
|
||||
@override
|
||||
String get photoError => 'Ошибка при съемке фото';
|
||||
@override
|
||||
String get deletePhoto => 'Удалить фото';
|
||||
@override
|
||||
String get deletePhotoConfirm => 'Вы действительно хотите удалить это фото?';
|
||||
@override
|
||||
String get barcode => 'Штрих-код';
|
||||
@override
|
||||
String get barcodeScan => 'Сканировать штрих-код';
|
||||
@override
|
||||
String get scanBarcode => 'Сканировать штрих-код';
|
||||
@override
|
||||
String get barcodeRequired => 'Требуется штрих-код';
|
||||
@override
|
||||
String get minBarcodes => 'Минимум';
|
||||
@override
|
||||
String get maxBarcodes => 'Максимум';
|
||||
@override
|
||||
String get scanned => 'Отсканировано';
|
||||
@override
|
||||
String get scannedBarcodes => 'Отсканированные штрих-коды';
|
||||
@override
|
||||
String get barcodesRequired => 'Требуются штрих-коды';
|
||||
@override
|
||||
String get enterBarcode => 'Введите штрих-код';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Пожалуйста, введите штрих-коды:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Штрих-код $number (обязательно)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) =>
|
||||
'Штрих-код $number (необязательно)';
|
||||
@override
|
||||
String get barcodeError => 'Ошибка при сканировании штрих-кода';
|
||||
@override
|
||||
String get cameraError => 'Ошибка инициализации камеры';
|
||||
@override
|
||||
String get cameraNotReady => 'Камера не готова или недоступна';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Камера недоступна';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Камера не поддерживается на этой платформе.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform =>
|
||||
'Не поддерживается на этой платформе';
|
||||
@override
|
||||
String get maxPhotosReached => 'Максимум достигнут';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Камера готова (без предпросмотра)';
|
||||
@override
|
||||
String get cameraLoading => 'Камера загружается...';
|
||||
@override
|
||||
String get cameraInitializing => 'Инициализация камеры...';
|
||||
@override
|
||||
String get cameraLoadingMessage =>
|
||||
'Пожалуйста, подождите, пока загружается камера';
|
||||
@override
|
||||
String get addPhotos => 'Добавить фото';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Используйте кнопку "Выбрать фото", чтобы добавить изображения с камеры или жёсткого диска.';
|
||||
@override
|
||||
String get photoOf => 'из';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Введите сообщение...';
|
||||
@override
|
||||
String get send => 'Отправить';
|
||||
@override
|
||||
String get noSender => 'Отправитель недоступен';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Отправитель недоступен. Пожалуйста, войдите снова.';
|
||||
@override
|
||||
String get noRecipient => 'Получатель не настроен';
|
||||
@override
|
||||
String get noRecipientMessage => 'Получатель не настроен для этого чата.';
|
||||
@override
|
||||
String get messageSendError => 'Сообщение не удалось отправить.';
|
||||
@override
|
||||
String get photoSendError => 'Фото не удалось отправить.';
|
||||
@override
|
||||
String get photoProcessError => 'Фото не удалось обработать.';
|
||||
@override
|
||||
String get imageSendError => 'Изображение не удалось отправить.';
|
||||
@override
|
||||
String get chatTypeJob => 'Специфичный для задания';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Общий';
|
||||
@override
|
||||
String get jobNumber => 'Номер задания';
|
||||
@override
|
||||
String get messages => 'Сообщения';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Общие сообщения';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Сообщений пока нет';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Нет доступных чатов';
|
||||
@override
|
||||
String get selectPhoto => 'Выбрать фото';
|
||||
@override
|
||||
String get unreadMessages => 'Непрочитанные сообщения';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Детали груза';
|
||||
@override
|
||||
String get itemName => 'Описание';
|
||||
@override
|
||||
String get itemNumber => 'Номер позиции';
|
||||
@override
|
||||
String get item => 'Позиция';
|
||||
@override
|
||||
String get weightUnit => 'кг';
|
||||
@override
|
||||
String get dimensionUnit => 'см';
|
||||
@override
|
||||
String get noCargoItems => 'Нет позиций груза';
|
||||
@override
|
||||
String get noCargoItemsMessage =>
|
||||
'Для этого задания не определены позиции груза.';
|
||||
@override
|
||||
String get article => 'Позиция';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Сделать фото';
|
||||
@override
|
||||
String get photosCount => 'Фото';
|
||||
@override
|
||||
String get checklistPoints => 'Пункты';
|
||||
@override
|
||||
String get signatureRequiredText => 'Требуется подпись';
|
||||
@override
|
||||
String get scanBarcodes => 'Сканировать штрих-коды';
|
||||
@override
|
||||
String get barcodeCount => 'Коды';
|
||||
@override
|
||||
String get commentOptional => 'Комментарий';
|
||||
@override
|
||||
String get genericTask => 'Общая задача';
|
||||
@override
|
||||
String get complete => 'Завершить';
|
||||
@override
|
||||
String get abort => 'Отмена';
|
||||
@override
|
||||
String get optional => 'Необязательно';
|
||||
@override
|
||||
String get skipTask => 'Пропустить';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Язык';
|
||||
@override
|
||||
String get languageChanged => 'Язык изменен на';
|
||||
@override
|
||||
String get appInfo => 'ИНФОРМАЦИЯ О ПРИЛОЖЕНИИ';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Создано';
|
||||
@override
|
||||
String get statusPending => 'В ожидании';
|
||||
@override
|
||||
String get statusAssigned => 'Назначено';
|
||||
@override
|
||||
String get statusInProgress => 'В процессе';
|
||||
@override
|
||||
String get statusCompleted => 'Завершено';
|
||||
@override
|
||||
String get statusCancelled => 'Отменено';
|
||||
@override
|
||||
String get statusFailed => 'Не удалось';
|
||||
@override
|
||||
String get priorityLow => 'Низкий';
|
||||
@override
|
||||
String get priorityMedium => 'Средний';
|
||||
@override
|
||||
String get priorityHigh => 'Высокий';
|
||||
@override
|
||||
String get priorityUrgent => 'Срочный';
|
||||
}
|
||||
461
app/lib/l10n/app_localizations_tr.dart
Normal file
@@ -0,0 +1,461 @@
|
||||
import 'app_localizations.dart';
|
||||
|
||||
class AppLocalizationsTr extends AppLocalizations {
|
||||
@override
|
||||
String get languageName => 'Türkçe';
|
||||
|
||||
@override
|
||||
String get flagEmoji => '🇹🇷';
|
||||
|
||||
// ==================== GENERAL ====================
|
||||
@override
|
||||
String get appTitle => 'VotianLT App';
|
||||
@override
|
||||
String get ok => 'Tamam';
|
||||
@override
|
||||
String get cancel => 'İptal';
|
||||
@override
|
||||
String get save => 'Kaydet';
|
||||
@override
|
||||
String get delete => 'Sil';
|
||||
@override
|
||||
String get close => 'Kapat';
|
||||
@override
|
||||
String get confirm => 'Onayla';
|
||||
@override
|
||||
String get error => 'Hata';
|
||||
@override
|
||||
String get success => 'Başarılı';
|
||||
@override
|
||||
String get loading => 'Yükleniyor...';
|
||||
@override
|
||||
String get refresh => 'Yenile';
|
||||
@override
|
||||
String get version => 'Versiyon';
|
||||
@override
|
||||
String get unknown => 'Bilinmiyor';
|
||||
|
||||
@override
|
||||
String get yesterday => 'Dün';
|
||||
|
||||
// ==================== NAVIGATION ====================
|
||||
@override
|
||||
String get jobs => 'İşler';
|
||||
@override
|
||||
String get availableJobs => 'Sipariş Listesi';
|
||||
@override
|
||||
String get chats => 'Sohbetler';
|
||||
@override
|
||||
String get settings => 'Ayarlar';
|
||||
@override
|
||||
String get logout => 'Çıkış';
|
||||
@override
|
||||
String get logoutConfirm => 'Çıkış';
|
||||
@override
|
||||
String get logoutConfirmMessage => 'Gerçekten çıkış yapmak istiyor musunuz?';
|
||||
@override
|
||||
String get openChat => 'Sohbeti aç';
|
||||
@override
|
||||
String get chatInfo => 'Sohbet bilgisi';
|
||||
@override
|
||||
String get routePlan => 'Rota planla';
|
||||
|
||||
// ==================== LOGIN ====================
|
||||
@override
|
||||
String get welcomeBack => 'Tekrar hoş geldiniz';
|
||||
@override
|
||||
String get loginSubtitle => 'Hesabınıza giriş yapın';
|
||||
@override
|
||||
String get email => 'E-posta';
|
||||
|
||||
@override
|
||||
String get emailAddress => 'E-posta adresi';
|
||||
|
||||
@override
|
||||
String get emailAddressHint => 'E-posta adresinizi girin';
|
||||
|
||||
@override
|
||||
String get emailAddressRequired => 'Lütfen e-posta adresinizi girin';
|
||||
|
||||
@override
|
||||
String get emailAddressInvalid => 'Lütfen geçerli bir e-posta adresi girin';
|
||||
@override
|
||||
String get password => 'Şifre';
|
||||
|
||||
@override
|
||||
String get passwordHint => 'Şifrenizi girin';
|
||||
|
||||
@override
|
||||
String get passwordRequired => 'Lütfen şifrenizi girin';
|
||||
|
||||
@override
|
||||
String get passwordMinLength => 'Şifre en az 6 karakter olmalıdır';
|
||||
@override
|
||||
String get login => 'Giriş';
|
||||
@override
|
||||
String get loggingIn => 'Bağlanıyor...';
|
||||
@override
|
||||
String get forgotPassword => 'Şifrenizi mi unuttunuz?';
|
||||
@override
|
||||
String get forgotPasswordMessage =>
|
||||
'Şifremi unuttum özelliği henüz uygulanmadı';
|
||||
@override
|
||||
String get loginSuccess => 'Başarıyla çıkış yapıldı';
|
||||
@override
|
||||
String get loginFailed => 'Giriş başarısız';
|
||||
@override
|
||||
String get connectionFailed => 'Sunucu bağlantısı başarısız (Zaman aşımı).';
|
||||
@override
|
||||
String get connectionTimeout => 'Sunucu bağlantısı başarısız (Zaman aşımı).';
|
||||
@override
|
||||
String get connecting => 'Sunucuya bağlanılıyor...';
|
||||
@override
|
||||
String get connectionError => 'Bağlantı hatası';
|
||||
@override
|
||||
String get loginError => 'Giriş sırasında hata';
|
||||
|
||||
// ==================== JOBS ====================
|
||||
@override
|
||||
String get noJobsAssigned => 'Atanmış iş yok';
|
||||
@override
|
||||
String get noJobsMessage => 'Atanmış işleriniz burada görüntülenecek.';
|
||||
@override
|
||||
String get pullToRefresh => 'Yenilemek için aşağı çekin';
|
||||
@override
|
||||
String get newLabel => 'YENİ';
|
||||
@override
|
||||
String get tasksToComplete => 'Tamamlanacak görevler';
|
||||
@override
|
||||
String get pickup => 'Alım';
|
||||
@override
|
||||
String get delivery => 'Teslimat';
|
||||
@override
|
||||
String get created => 'Oluşturuldu';
|
||||
@override
|
||||
String get status => 'Durum';
|
||||
@override
|
||||
String get priority => 'Öncelik';
|
||||
@override
|
||||
String get dueDate => 'Bitiş tarihi';
|
||||
@override
|
||||
String get location => 'Konum';
|
||||
@override
|
||||
String get description => 'Açıklama';
|
||||
@override
|
||||
String get cargo => 'Yük';
|
||||
@override
|
||||
String get quantity => 'Miktar';
|
||||
@override
|
||||
String get weight => 'Ağırlık';
|
||||
@override
|
||||
String get dimensions => 'Boyutlar';
|
||||
@override
|
||||
String get jobDeleted => 'İş silindi';
|
||||
@override
|
||||
String get jobDeleteError => 'İş silinirken hata oluştu';
|
||||
@override
|
||||
String get jobCompleted => 'İş tamamlandı';
|
||||
@override
|
||||
String get from => 'Kimden';
|
||||
@override
|
||||
String get to => 'den';
|
||||
@override
|
||||
String get jobsUpdated => 'İşler güncellendi';
|
||||
@override
|
||||
String get connectionRestored =>
|
||||
'Bağlantı geri yüklendi. İşler yükleniyor...';
|
||||
@override
|
||||
String get connectionLost => 'Bağlantı kesildi. Çevrimdışı.';
|
||||
@override
|
||||
String get offline => 'Çevrimdışı';
|
||||
@override
|
||||
String get deleteJob => 'İşi sil';
|
||||
@override
|
||||
String get jobRemoved => 'kaldırıldı';
|
||||
@override
|
||||
String get newJobReceived => 'Yeni iş alındı';
|
||||
|
||||
@override
|
||||
String get jobDetails => 'İş detayları';
|
||||
|
||||
@override
|
||||
String get jobTasks => 'İş görevleri';
|
||||
|
||||
@override
|
||||
String get deliveryStations => 'Teslimat durakları';
|
||||
|
||||
@override
|
||||
String deliveryStationsCount(int count) => 'Teslimat durakları ($count)';
|
||||
|
||||
@override
|
||||
String get noDeliveryStations => 'Teslimat durağı yok';
|
||||
|
||||
@override
|
||||
String get noDeliveryStationsMessage =>
|
||||
'Bu iş şu anda hiçbir teslimat durağı içermiyor.';
|
||||
|
||||
@override
|
||||
String get phone => 'Telefon';
|
||||
|
||||
@override
|
||||
String get unnamedStation => 'Adsız durak';
|
||||
|
||||
@override
|
||||
String stationNumber(int number) => 'Durak $number';
|
||||
|
||||
// ==================== TASKS ====================
|
||||
@override
|
||||
String get tasks => 'Görevler';
|
||||
@override
|
||||
String get noTasks => 'Görev yok';
|
||||
@override
|
||||
String get noTasksMessage => 'Bu iş için tanımlanmış görev yok.';
|
||||
@override
|
||||
String get taskOrder => 'Sıra';
|
||||
@override
|
||||
String get confirmationRequired => 'Onay gerekli';
|
||||
@override
|
||||
String get confirmationDescription =>
|
||||
'Görevi tamamlamak için butona tıklayın.';
|
||||
@override
|
||||
String get checklist => 'Kontrol listesi';
|
||||
@override
|
||||
String get checklistDescription => 'Lütfen tüm maddeleri işaretleyin:';
|
||||
@override
|
||||
String get completeTask => 'Görevi tamamla';
|
||||
@override
|
||||
String get completeTaskConfirm =>
|
||||
'Bu görevi tamamlandı olarak işaretlemek istiyor musunuz?';
|
||||
@override
|
||||
String get completeTaskNote => 'Not (isteğe bağlı)';
|
||||
@override
|
||||
String get taskCompleted => 'Görev tamamlandı';
|
||||
@override
|
||||
String get comment => 'Yorum';
|
||||
@override
|
||||
String get commentRequired => 'Yorum (gerekli)';
|
||||
@override
|
||||
String get enterComment => 'Yorum gir';
|
||||
@override
|
||||
String get commentDescription => 'Lütfen bir yorum girin:';
|
||||
@override
|
||||
String get finish => 'Bitir';
|
||||
@override
|
||||
String get signature => 'İmza';
|
||||
@override
|
||||
String get signatureCapture => 'İmza yakalama';
|
||||
@override
|
||||
String get signatureRequired => 'Lütfen bir imza yakalayın.';
|
||||
@override
|
||||
String get clear => 'Temizle';
|
||||
@override
|
||||
String get signatureError => 'İmza kaydedilirken hata oluştu';
|
||||
@override
|
||||
String get signatureInstruction =>
|
||||
'Lütfen aşağıdaki alana imzanızı atın (fare veya parmak).';
|
||||
@override
|
||||
String get photoCapture => 'Fotoğraf çek';
|
||||
@override
|
||||
String get requiredPhotos => 'Gerekli fotoğraflar';
|
||||
@override
|
||||
String get photosTaken => 'Çekilen';
|
||||
@override
|
||||
String get photos => 'Fotoğraflar';
|
||||
@override
|
||||
String get takePhoto => 'Fotoğraf çek';
|
||||
@override
|
||||
String get selectFromLibrary => 'Kütüphaneden seç';
|
||||
@override
|
||||
String get retakePhoto => 'Tekrar çek';
|
||||
@override
|
||||
String get photoRequired => 'Fotoğraf gerekli';
|
||||
@override
|
||||
String get minPhotos => 'En az';
|
||||
@override
|
||||
String get maxPhotos => 'En fazla';
|
||||
@override
|
||||
String get photoError => 'Fotoğraf çekilirken hata oluştu';
|
||||
@override
|
||||
String get deletePhoto => 'Fotoğrafı sil';
|
||||
@override
|
||||
String get deletePhotoConfirm =>
|
||||
'Bu fotoğrafı gerçekten silmek istiyor musunuz?';
|
||||
@override
|
||||
String get barcode => 'Barkod';
|
||||
@override
|
||||
String get barcodeScan => 'Barkod tara';
|
||||
@override
|
||||
String get scanBarcode => 'Barkod tara';
|
||||
@override
|
||||
String get barcodeRequired => 'Barkod gerekli';
|
||||
@override
|
||||
String get minBarcodes => 'En az';
|
||||
@override
|
||||
String get maxBarcodes => 'En fazla';
|
||||
@override
|
||||
String get scanned => 'Tarandı';
|
||||
@override
|
||||
String get scannedBarcodes => 'Taranan barkodlar';
|
||||
@override
|
||||
String get barcodesRequired => 'Barkodlar gerekli';
|
||||
@override
|
||||
String get enterBarcode => 'Barkod gir';
|
||||
@override
|
||||
String get barcodeEnterDescription => 'Lütfen barkodları girin:';
|
||||
@override
|
||||
String barcodeNumberRequired(int number) => 'Barkod $number (gerekli)';
|
||||
@override
|
||||
String barcodeNumberOptional(int number) => 'Barkod $number (isteğe bağlı)';
|
||||
@override
|
||||
String get barcodeError => 'Barkod taranırken hata oluştu';
|
||||
@override
|
||||
String get cameraError => 'Kamera başlatılırken hata oluştu';
|
||||
@override
|
||||
String get cameraNotReady => 'Kamera hazır değil veya kullanılamıyor';
|
||||
@override
|
||||
String get cameraNotAvailable => 'Kamera kullanılamıyor';
|
||||
@override
|
||||
String get cameraNotSupportedMessage =>
|
||||
'Bu platformda kamera desteklenmiyor.';
|
||||
@override
|
||||
String get cameraNotSupportedOnPlatform => 'Bu platformda desteklenmiyor';
|
||||
@override
|
||||
String get maxPhotosReached => 'Maksimum ulaşıldı';
|
||||
@override
|
||||
String get cameraReadyNoPreview => 'Kamera hazır (önizleme yok)';
|
||||
@override
|
||||
String get cameraLoading => 'Kamera yükleniyor...';
|
||||
@override
|
||||
String get cameraInitializing => 'Kamera başlatılıyor...';
|
||||
@override
|
||||
String get cameraLoadingMessage => 'Kamera yüklenirken lütfen bekleyin';
|
||||
@override
|
||||
String get addPhotos => 'Fotoğraf ekle';
|
||||
@override
|
||||
String get addPhotosInstruction =>
|
||||
'Kamera veya sabit diskten görüntü eklemek için "Fotoğraf seç" düğmesini kullanın.';
|
||||
@override
|
||||
String get photoOf => '/';
|
||||
|
||||
// ==================== CHAT ====================
|
||||
@override
|
||||
String get typeMessage => 'Mesaj yazın...';
|
||||
@override
|
||||
String get send => 'Gönder';
|
||||
@override
|
||||
String get noSender => 'Gönderen mevcut değil';
|
||||
@override
|
||||
String get noSenderMessage =>
|
||||
'Gönderen mevcut değil. Lütfen tekrar giriş yapın.';
|
||||
@override
|
||||
String get noRecipient => 'Alıcı yapılandırılmamış';
|
||||
@override
|
||||
String get noRecipientMessage => 'Bu sohbet için alıcı yapılandırılmamış.';
|
||||
@override
|
||||
String get messageSendError => 'Mesaj gönderilemedi.';
|
||||
@override
|
||||
String get photoSendError => 'Fotoğraf gönderilemedi.';
|
||||
@override
|
||||
String get photoProcessError => 'Fotoğraf işlenemedi.';
|
||||
@override
|
||||
String get imageSendError => 'Görüntü gönderilemedi.';
|
||||
@override
|
||||
String get chatTypeJob => 'İşe özel';
|
||||
@override
|
||||
String get chatTypeGeneral => 'Genel';
|
||||
@override
|
||||
String get jobNumber => 'İş numarası';
|
||||
@override
|
||||
String get messages => 'Mesajlar';
|
||||
|
||||
@override
|
||||
String get generalMessages => 'Genel mesajlar';
|
||||
|
||||
@override
|
||||
String get noMessagesYet => 'Henüz mesaj yok';
|
||||
|
||||
@override
|
||||
String get noChatsAvailable => 'Kullanılabilir sohbet yok';
|
||||
@override
|
||||
String get selectPhoto => 'Fotoğraf seç';
|
||||
@override
|
||||
String get unreadMessages => 'Okunmamış mesajlar';
|
||||
|
||||
// ==================== CARGO ====================
|
||||
@override
|
||||
String get cargoDetails => 'Yük Detayları';
|
||||
@override
|
||||
String get itemName => 'Açıklama';
|
||||
@override
|
||||
String get itemNumber => 'Pozisyon No';
|
||||
@override
|
||||
String get item => 'Pozisyon';
|
||||
@override
|
||||
String get weightUnit => 'kg';
|
||||
@override
|
||||
String get dimensionUnit => 'cm';
|
||||
@override
|
||||
String get noCargoItems => 'Yük kalemi yok';
|
||||
@override
|
||||
String get noCargoItemsMessage => 'Bu iş için tanımlanmış yük kalemi yok.';
|
||||
@override
|
||||
String get article => 'Kalem';
|
||||
|
||||
// ==================== TASK TYPES ====================
|
||||
@override
|
||||
String get takePhotos => 'Fotoğraf çek';
|
||||
@override
|
||||
String get photosCount => 'Fotoğraflar';
|
||||
@override
|
||||
String get checklistPoints => 'Noktalar';
|
||||
@override
|
||||
String get signatureRequiredText => 'İmza gerekli';
|
||||
@override
|
||||
String get scanBarcodes => 'Barkodları tara';
|
||||
@override
|
||||
String get barcodeCount => 'Kodlar';
|
||||
@override
|
||||
String get commentOptional => 'Yorum';
|
||||
@override
|
||||
String get genericTask => 'Genel görev';
|
||||
@override
|
||||
String get complete => 'Tamamla';
|
||||
@override
|
||||
String get abort => 'İptal';
|
||||
@override
|
||||
String get optional => 'İsteğe bağlı';
|
||||
@override
|
||||
String get skipTask => 'Atla';
|
||||
|
||||
// ==================== SETTINGS ====================
|
||||
@override
|
||||
String get language => 'Dil';
|
||||
@override
|
||||
String get languageChanged => 'Dil değiştirildi:';
|
||||
@override
|
||||
String get appInfo => 'UYGULAMA BİLGİSİ';
|
||||
|
||||
// ==================== STATUS ====================
|
||||
@override
|
||||
String get statusCreated => 'Oluşturuldu';
|
||||
@override
|
||||
String get statusPending => 'Beklemede';
|
||||
@override
|
||||
String get statusAssigned => 'Atandı';
|
||||
@override
|
||||
String get statusInProgress => 'Devam ediyor';
|
||||
@override
|
||||
String get statusCompleted => 'Tamamlandı';
|
||||
@override
|
||||
String get statusCancelled => 'İptal edildi';
|
||||
@override
|
||||
String get statusFailed => 'Başarısız';
|
||||
@override
|
||||
String get priorityLow => 'Düşük';
|
||||
@override
|
||||
String get priorityMedium => 'Orta';
|
||||
@override
|
||||
String get priorityHigh => 'Yüksek';
|
||||
@override
|
||||
String get priorityUrgent => 'Acil';
|
||||
}
|
||||
50
app/lib/l10n/localization_helpers.dart
Normal file
@@ -0,0 +1,50 @@
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
import '../models/chat.dart';
|
||||
import 'app_localizations.dart';
|
||||
|
||||
String localizeKnownText(BuildContext context, String text) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
switch (text.trim()) {
|
||||
case 'Auftragsdetails':
|
||||
return l10n.jobDetails;
|
||||
case 'Aufgaben eines Auftrags':
|
||||
return l10n.jobTasks;
|
||||
case 'Unterschrift':
|
||||
return l10n.signature;
|
||||
case 'Allgemeine Nachrichten':
|
||||
return l10n.generalMessages;
|
||||
case 'Telefon':
|
||||
return l10n.phone;
|
||||
case 'Erstellt':
|
||||
return l10n.created;
|
||||
case 'E-Mail-Adresse':
|
||||
return l10n.emailAddress;
|
||||
case 'Passwort':
|
||||
return l10n.password;
|
||||
case 'Anmelden':
|
||||
return l10n.login;
|
||||
default:
|
||||
return text;
|
||||
}
|
||||
}
|
||||
|
||||
String localizedStationLabel(
|
||||
BuildContext context,
|
||||
int number, {
|
||||
String? suffix,
|
||||
}) {
|
||||
final base = AppLocalizations.of(context).stationNumber(number);
|
||||
final trimmedSuffix = suffix?.trim() ?? '';
|
||||
if (trimmedSuffix.isEmpty) {
|
||||
return base;
|
||||
}
|
||||
return '$base: ${localizeKnownText(context, trimmedSuffix)}';
|
||||
}
|
||||
|
||||
String localizedChatTitle(BuildContext context, Chat chat) {
|
||||
if (chat.type == ChatType.general) {
|
||||
return AppLocalizations.of(context).generalMessages;
|
||||
}
|
||||
return localizeKnownText(context, chat.title);
|
||||
}
|
||||
648
app/lib/login_view.dart
Normal file
@@ -0,0 +1,648 @@
|
||||
import 'dart:async';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:votianlt_app/services/developer.dart' as developer;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:package_info_plus/package_info_plus.dart';
|
||||
import 'services/websocket_service.dart';
|
||||
import 'services/dart_mq.dart';
|
||||
import 'services/database_service.dart';
|
||||
import 'app_state.dart';
|
||||
import 'app_theme.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
|
||||
class LoginView extends StatefulWidget {
|
||||
const LoginView({super.key, this.suppressConnectionSnack = false});
|
||||
|
||||
// If true, suppress connection-related SnackBars until the user attempts login
|
||||
final bool suppressConnectionSnack;
|
||||
|
||||
@override
|
||||
State<LoginView> createState() => _LoginViewState();
|
||||
}
|
||||
|
||||
class _LoginViewState extends State<LoginView> {
|
||||
final _formKey = GlobalKey<FormState>();
|
||||
final _emailController = TextEditingController();
|
||||
final _passwordController = TextEditingController();
|
||||
bool _isPasswordVisible = false;
|
||||
bool _isLoggingIn = false;
|
||||
final StompService _stompService = StompService();
|
||||
final AppState _appState = AppState();
|
||||
|
||||
// DartMQ subscriptions for proper cleanup
|
||||
DartMQSubscription? _connectionStatusSubscription;
|
||||
DartMQSubscription? _authResponseSubscription;
|
||||
bool _logoutNoticeShown = false;
|
||||
bool _hasNavigatedToJobs = false;
|
||||
String _appVersion = '';
|
||||
String? _pendingLoginEmail;
|
||||
String? _pendingLoginPassword;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Pre-populate with test data
|
||||
if (kDebugMode) {
|
||||
_emailController.text = 'mail@svencarstensen.de';
|
||||
_passwordController.text = 'test123';
|
||||
}
|
||||
|
||||
_loadAppVersion();
|
||||
_initializeStompService();
|
||||
|
||||
// If we came here due to logout, show only a success message and suppress other connection snacks
|
||||
if (widget.suppressConnectionSnack && !_logoutNoticeShown) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
_logoutNoticeShown = true;
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(AppLocalizations.of(context).loginSuccess),
|
||||
backgroundColor: AppColors.success,
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
// Cancel all stream subscriptions to prevent setState() after dispose
|
||||
_connectionStatusSubscription?.cancel();
|
||||
_authResponseSubscription?.cancel();
|
||||
_emailController.dispose();
|
||||
_passwordController.dispose();
|
||||
|
||||
// Don't dispose the singleton StompService as it may be used elsewhere
|
||||
// _stompService.dispose();
|
||||
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
Future<void> _loadAppVersion() async {
|
||||
try {
|
||||
final PackageInfo packageInfo = await PackageInfo.fromPlatform();
|
||||
setState(() {
|
||||
_appVersion = packageInfo.version;
|
||||
});
|
||||
} catch (e) {
|
||||
developer.log('Error loading app version: $e', name: 'LoginView');
|
||||
}
|
||||
}
|
||||
|
||||
void _initializeStompService() {
|
||||
// Listen to connection status changes via dart_mq
|
||||
// Note: Don't reset _isLoggingIn here - the login flow in _handleLogin
|
||||
// manages button state through its own error/success handling.
|
||||
_connectionStatusSubscription = DartMQ().subscribe<bool>(
|
||||
MQTopics.connectionStatus,
|
||||
(isConnected) {
|
||||
if (mounted) {
|
||||
setState(() {});
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
// Listen to authentication responses via dart_mq
|
||||
_authResponseSubscription = DartMQ().subscribe<Map<String, dynamic>>(
|
||||
MQTopics.authResponse,
|
||||
(response) {
|
||||
final responseTime = DateTime.now();
|
||||
developer.log(
|
||||
'=== AUTHENTICATION RESPONSE RECEIVED ===',
|
||||
name: 'LoginView',
|
||||
);
|
||||
developer.log(
|
||||
'Timestamp: ${responseTime.toIso8601String()}',
|
||||
name: 'LoginView',
|
||||
);
|
||||
developer.log('Response data: $response', name: 'LoginView');
|
||||
|
||||
if (mounted) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_handleAuthResponse(response);
|
||||
});
|
||||
} else {
|
||||
developer.log(
|
||||
'Widget not mounted - skipping UI updates for auth response',
|
||||
name: 'LoginView',
|
||||
);
|
||||
}
|
||||
|
||||
developer.log(
|
||||
'Authentication response processing completed',
|
||||
name: 'LoginView',
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _clearPendingLoginCredentials() {
|
||||
_pendingLoginEmail = null;
|
||||
_pendingLoginPassword = null;
|
||||
}
|
||||
|
||||
Future<void> _handleAuthResponse(Map<String, dynamic> response) async {
|
||||
if (!mounted) return;
|
||||
|
||||
final pendingEmail = _pendingLoginEmail?.trim();
|
||||
final pendingPassword = _pendingLoginPassword;
|
||||
final hadPendingLogin =
|
||||
pendingEmail != null &&
|
||||
pendingEmail.isNotEmpty &&
|
||||
pendingPassword != null &&
|
||||
pendingPassword.isNotEmpty;
|
||||
_clearPendingLoginCredentials();
|
||||
|
||||
setState(() {
|
||||
_isLoggingIn = false;
|
||||
});
|
||||
|
||||
if (response['success'] == true) {
|
||||
// Prevent duplicate navigation from multiple auth responses
|
||||
if (_hasNavigatedToJobs) {
|
||||
developer.log(
|
||||
'Already navigated to jobs view - ignoring duplicate auth response',
|
||||
name: 'LoginView',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
final message = response['message'] ?? 'Anmeldung erfolgreich';
|
||||
final savedCredentials = await DatabaseService().loadCredentials();
|
||||
final effectiveEmail =
|
||||
(pendingEmail != null && pendingEmail.isNotEmpty)
|
||||
? pendingEmail
|
||||
: (savedCredentials?.email ??
|
||||
_appState.loggedInEmail ??
|
||||
_emailController.text.trim());
|
||||
final effectivePassword =
|
||||
(pendingPassword != null && pendingPassword.isNotEmpty)
|
||||
? pendingPassword
|
||||
: (savedCredentials?.password ?? _passwordController.text);
|
||||
|
||||
developer.log('=== LOGIN SUCCESS ===', name: 'LoginView');
|
||||
developer.log('Email: $effectiveEmail', name: 'LoginView');
|
||||
developer.log('Message: $message', name: 'LoginView');
|
||||
|
||||
if (effectiveEmail.isNotEmpty) {
|
||||
_appState.setLoggedInEmail(effectiveEmail);
|
||||
}
|
||||
if (effectiveEmail.isNotEmpty && effectivePassword.isNotEmpty) {
|
||||
await DatabaseService().saveCredentials(
|
||||
effectiveEmail,
|
||||
effectivePassword,
|
||||
);
|
||||
}
|
||||
|
||||
if (!mounted) return;
|
||||
_hasNavigatedToJobs = true;
|
||||
|
||||
// Navigate directly to jobs view - jobs will be loaded there
|
||||
developer.log(
|
||||
'Navigating to jobs view - jobs will be loaded there...',
|
||||
name: 'LoginView',
|
||||
);
|
||||
Navigator.of(context).pushReplacementNamed('/jobs');
|
||||
return;
|
||||
}
|
||||
|
||||
final errorMessage = response['message'] ?? 'Unbekannter Fehler';
|
||||
final errorCode = response['code'] ?? 'No code';
|
||||
|
||||
developer.log('=== LOGIN FAILURE ===', name: 'LoginView');
|
||||
developer.log('Error message: $errorMessage', name: 'LoginView');
|
||||
developer.log('Error code: $errorCode', name: 'LoginView');
|
||||
developer.log('Full error response: $response', name: 'LoginView');
|
||||
|
||||
if (!hadPendingLogin || !mounted) {
|
||||
developer.log(
|
||||
'Ignoring auth failure in LoginView because no manual login attempt is pending',
|
||||
name: 'LoginView',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(
|
||||
'${AppLocalizations.of(context).loginFailed}: $errorMessage',
|
||||
),
|
||||
backgroundColor: AppColors.danger,
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _handleLogin() async {
|
||||
final loginStartTime = DateTime.now();
|
||||
final sessionId = loginStartTime.millisecondsSinceEpoch.toString();
|
||||
|
||||
developer.log('=== LOGIN ATTEMPT STARTED ===', name: 'LoginView');
|
||||
developer.log('Session ID: $sessionId', name: 'LoginView');
|
||||
developer.log(
|
||||
'Timestamp: ${loginStartTime.toIso8601String()}',
|
||||
name: 'LoginView',
|
||||
);
|
||||
|
||||
if (!_formKey.currentState!.validate()) {
|
||||
developer.log(
|
||||
'Login validation failed - form is invalid',
|
||||
name: 'LoginView',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if (_isLoggingIn) {
|
||||
developer.log(
|
||||
'Login already in progress - ignoring duplicate request',
|
||||
name: 'LoginView',
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
String email = _emailController.text.trim();
|
||||
String password = _passwordController.text;
|
||||
_pendingLoginEmail = email;
|
||||
_pendingLoginPassword = password;
|
||||
|
||||
developer.log('Login attempt for email: $email', name: 'LoginView');
|
||||
developer.log(
|
||||
'Password length: ${_passwordController.text.length} characters',
|
||||
name: 'LoginView',
|
||||
);
|
||||
|
||||
// Capture ScaffoldMessenger and localizations before any async operations
|
||||
final scaffoldMessenger = ScaffoldMessenger.of(context);
|
||||
final localizations = AppLocalizations.of(context);
|
||||
|
||||
if (!_stompService.isConnected) {
|
||||
developer.log(
|
||||
'Not connected to STOMP server - establishing connection first',
|
||||
name: 'LoginView',
|
||||
);
|
||||
developer.log(
|
||||
'STOMP service connection state: ${_stompService.isConnected}',
|
||||
name: 'LoginView',
|
||||
);
|
||||
|
||||
// Always attempt connection to fixed STOMP endpoint (no discovery gating)
|
||||
// Show connecting message
|
||||
if (!widget.suppressConnectionSnack) {
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(localizations.connecting),
|
||||
backgroundColor: AppColors.primary,
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Set loading state
|
||||
setState(() {
|
||||
_isLoggingIn = true;
|
||||
});
|
||||
|
||||
try {
|
||||
// Start connection to STOMP server
|
||||
await _stompService.connect();
|
||||
|
||||
// Check if already connected after connect returns
|
||||
if (!_stompService.isConnected) {
|
||||
// Wait for connection to be established with a timeout
|
||||
try {
|
||||
final completer = Completer<bool>();
|
||||
final subscription = DartMQ().subscribe<bool>(
|
||||
MQTopics.connectionStatus,
|
||||
(isConnected) {
|
||||
if (isConnected && !completer.isCompleted) {
|
||||
completer.complete(true);
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
await completer.future.timeout(const Duration(seconds: 12));
|
||||
subscription.cancel();
|
||||
developer.log(
|
||||
'STOMP connection established - proceeding with login',
|
||||
name: 'LoginView',
|
||||
);
|
||||
} on TimeoutException {
|
||||
developer.log('STOMP connection timed out', name: 'LoginView');
|
||||
}
|
||||
} else {
|
||||
developer.log(
|
||||
'STOMP already connected after connect - proceeding with login',
|
||||
name: 'LoginView',
|
||||
);
|
||||
}
|
||||
|
||||
// Check if connection was successful
|
||||
if (!_stompService.isConnected) {
|
||||
setState(() {
|
||||
_isLoggingIn = false;
|
||||
});
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(localizations.connectionTimeout),
|
||||
backgroundColor: AppColors.danger,
|
||||
duration: const Duration(seconds: 2),
|
||||
),
|
||||
);
|
||||
_clearPendingLoginCredentials();
|
||||
return;
|
||||
}
|
||||
} catch (e, stackTrace) {
|
||||
setState(() {
|
||||
_isLoggingIn = false;
|
||||
});
|
||||
developer.log(
|
||||
'Error connecting to STOMP server: $e',
|
||||
name: 'LoginView',
|
||||
);
|
||||
developer.log('Stack trace: $stackTrace', name: 'LoginView');
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('${localizations.connectionError}: $e'),
|
||||
backgroundColor: AppColors.danger,
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
_clearPendingLoginCredentials();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
developer.log(
|
||||
'Pre-login checks passed - initiating login request',
|
||||
name: 'LoginView',
|
||||
);
|
||||
developer.log(
|
||||
'Connection status: connected=${_stompService.isConnected}',
|
||||
name: 'LoginView',
|
||||
);
|
||||
|
||||
setState(() {
|
||||
_isLoggingIn = true;
|
||||
});
|
||||
|
||||
developer.log(
|
||||
'Sending login request via STOMP service...',
|
||||
name: 'LoginView',
|
||||
);
|
||||
|
||||
try {
|
||||
// Send login request via STOMP
|
||||
await _stompService.login(email, password);
|
||||
|
||||
final requestSentTime = DateTime.now();
|
||||
final requestDuration =
|
||||
requestSentTime.difference(loginStartTime).inMilliseconds;
|
||||
developer.log(
|
||||
'Login request sent successfully after ${requestDuration}ms',
|
||||
name: 'LoginView',
|
||||
);
|
||||
} catch (e, stackTrace) {
|
||||
final errorTime = DateTime.now();
|
||||
final errorDuration = errorTime.difference(loginStartTime).inMilliseconds;
|
||||
|
||||
developer.log(
|
||||
'LOGIN ERROR: Exception during login request after ${errorDuration}ms',
|
||||
name: 'LoginView',
|
||||
);
|
||||
developer.log('Error: $e', name: 'LoginView');
|
||||
developer.log('Stack trace: $stackTrace', name: 'LoginView');
|
||||
|
||||
setState(() {
|
||||
_isLoggingIn = false;
|
||||
});
|
||||
|
||||
scaffoldMessenger.showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('${localizations.loginError}: $e'),
|
||||
backgroundColor: AppColors.danger,
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
_clearPendingLoginCredentials();
|
||||
}
|
||||
|
||||
// The auth response will be handled by the stream listener
|
||||
// _isLoggingIn will be set to false in the listener
|
||||
developer.log(
|
||||
'Login request phase completed - waiting for auth response',
|
||||
name: 'LoginView',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context);
|
||||
|
||||
return Scaffold(
|
||||
body: DecoratedBox(
|
||||
decoration: const BoxDecoration(gradient: AppGradients.shellBackground),
|
||||
child: Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(24.0),
|
||||
child: Form(
|
||||
key: _formKey,
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
Icon(
|
||||
Icons.account_circle,
|
||||
size: 100,
|
||||
color: AppColors.primary,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
|
||||
Text(
|
||||
l10n.welcomeBack,
|
||||
style: Theme.of(
|
||||
context,
|
||||
).textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppColors.textStrong,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
Text(
|
||||
l10n.loginSubtitle,
|
||||
style: Theme.of(context).textTheme.bodyLarge
|
||||
?.copyWith(color: AppColors.textMuted),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
// E-Mail-Feld
|
||||
TextFormField(
|
||||
controller: _emailController,
|
||||
keyboardType: TextInputType.emailAddress,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.emailAddress,
|
||||
hintText: l10n.emailAddressHint,
|
||||
prefixIcon: const Icon(Icons.email_outlined),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppColors.surface,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.emailAddressRequired;
|
||||
}
|
||||
if (!RegExp(
|
||||
r'^[A-Za-z0-9_.+-]+@[A-Za-z0-9-]+\.[A-Za-z0-9.-]+$',
|
||||
).hasMatch(value)) {
|
||||
return l10n.emailAddressInvalid;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
// Passwort-Feld
|
||||
TextFormField(
|
||||
controller: _passwordController,
|
||||
obscureText: !_isPasswordVisible,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.password,
|
||||
hintText: l10n.passwordHint,
|
||||
prefixIcon: const Icon(Icons.lock_outlined),
|
||||
suffixIcon: IconButton(
|
||||
icon: Icon(
|
||||
_isPasswordVisible
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
_isPasswordVisible = !_isPasswordVisible;
|
||||
});
|
||||
},
|
||||
),
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
filled: true,
|
||||
fillColor: AppColors.surface,
|
||||
),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.passwordRequired;
|
||||
}
|
||||
if (value.length < 6) {
|
||||
return l10n.passwordMinLength;
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Passwort vergessen Link
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: TextButton(
|
||||
onPressed: () {
|
||||
// Hier würde die "Passwort vergessen" Funktionalität implementiert werden
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(l10n.forgotPasswordMessage),
|
||||
duration: const Duration(seconds: 1),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: Text(
|
||||
l10n.forgotPassword,
|
||||
style: const TextStyle(
|
||||
color: AppColors.primaryStrong,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Verbindungsstatus
|
||||
// Anmelden Button
|
||||
ElevatedButton(
|
||||
onPressed: _isLoggingIn ? null : _handleLogin,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColors.primary,
|
||||
foregroundColor: Colors.white,
|
||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
child:
|
||||
_isLoggingIn
|
||||
? Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.center,
|
||||
children: [
|
||||
const SizedBox(
|
||||
width: 18,
|
||||
height: 18,
|
||||
child: CircularProgressIndicator(
|
||||
strokeWidth: 2.5,
|
||||
valueColor:
|
||||
AlwaysStoppedAnimation<Color>(
|
||||
Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Text(
|
||||
l10n.loggingIn,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
: Text(
|
||||
l10n.login,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (_appVersion.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 16.0),
|
||||
child: Text(
|
||||
'Version $_appVersion',
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: AppColors.textMuted,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
199
app/lib/main.dart
Normal file
@@ -0,0 +1,199 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_localizations/flutter_localizations.dart';
|
||||
import 'app_theme.dart';
|
||||
import 'login_view.dart';
|
||||
import 'jobs_view.dart';
|
||||
import 'cargo_items_view.dart';
|
||||
import 'chats_view.dart';
|
||||
import 'chat_details_view.dart';
|
||||
import 'settings_view.dart';
|
||||
import 'models/job.dart';
|
||||
import 'models/chat.dart';
|
||||
import 'services/database_service.dart';
|
||||
import 'services/chat_service.dart';
|
||||
import 'app_state.dart';
|
||||
import 'navigation_observer.dart';
|
||||
import 'services/notification_service.dart';
|
||||
import 'services/websocket_service.dart';
|
||||
import 'l10n/app_localizations.dart';
|
||||
|
||||
void main() async {
|
||||
WidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
// Initialize SQLite database
|
||||
await DatabaseService().initialize();
|
||||
|
||||
// Load data from database
|
||||
await AppState().loadLoginFromDatabase();
|
||||
|
||||
// Load language preference
|
||||
await AppState().loadLanguagePreference();
|
||||
|
||||
// Load jobs from database to trigger message type logging at startup
|
||||
await AppState().refreshJobsFromDatabase();
|
||||
|
||||
// Prepare chat service before WebSocket events start flowing
|
||||
await ChatService().initialize();
|
||||
|
||||
// Initialize notification service for local notifications with sound
|
||||
await NotificationService().initialize();
|
||||
|
||||
// Note: WebSocket connection is initiated from the view that needs it:
|
||||
// - If userId exists: JobsView initiates connection on startup
|
||||
// - If no userId: LoginView initiates connection when login button is clicked
|
||||
|
||||
runApp(const MyApp());
|
||||
}
|
||||
|
||||
class MyApp extends StatefulWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
State<MyApp> createState() => _MyAppState();
|
||||
}
|
||||
|
||||
class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||
static const Duration _resumeReconnectThreshold = Duration(seconds: 30);
|
||||
final AppState _appState = AppState();
|
||||
final WebSocketService _webSocketService = WebSocketService();
|
||||
DateTime? _lastPausedAt;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
WidgetsBinding.instance.addObserver(this);
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
WidgetsBinding.instance.removeObserver(this);
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||
if (state == AppLifecycleState.paused) {
|
||||
_lastPausedAt = DateTime.now();
|
||||
return;
|
||||
}
|
||||
|
||||
if (state == AppLifecycleState.resumed) {
|
||||
final pausedAt = _lastPausedAt;
|
||||
_lastPausedAt = null;
|
||||
if (pausedAt == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final standbyDuration = DateTime.now().difference(pausedAt);
|
||||
if (standbyDuration < _resumeReconnectThreshold ||
|
||||
!_appState.isLoggedIn) {
|
||||
return;
|
||||
}
|
||||
|
||||
_webSocketService.reconnectForAppResume();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// Check if user is already logged in
|
||||
final initialRoute = _appState.isLoggedIn ? '/jobs' : '/login';
|
||||
|
||||
return ValueListenableBuilder<Locale>(
|
||||
valueListenable: localeNotifier,
|
||||
builder: (context, locale, child) {
|
||||
return MaterialApp(
|
||||
title: 'VotianLT App',
|
||||
debugShowCheckedModeBanner: false,
|
||||
theme: buildAppTheme(),
|
||||
// Localization configuration
|
||||
locale: locale,
|
||||
localizationsDelegates: const [
|
||||
AppLocalizations.delegate,
|
||||
GlobalMaterialLocalizations.delegate,
|
||||
GlobalWidgetsLocalizations.delegate,
|
||||
GlobalCupertinoLocalizations.delegate,
|
||||
],
|
||||
supportedLocales:
|
||||
supportedLanguageCodes.map((code) => Locale(code)).toList(),
|
||||
navigatorObservers: [routeObserver],
|
||||
initialRoute: initialRoute,
|
||||
onGenerateRoute: (settings) {
|
||||
switch (settings.name) {
|
||||
case '/login':
|
||||
final arg = settings.arguments;
|
||||
final suppress = (arg is bool) ? arg : false;
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => LoginView(suppressConnectionSnack: suppress),
|
||||
);
|
||||
case '/jobs':
|
||||
return MaterialPageRoute(builder: (_) => const JobsView());
|
||||
case '/cargo_items':
|
||||
final job = settings.arguments as Job;
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => CargoItemsView(job: job),
|
||||
);
|
||||
case '/chats':
|
||||
return MaterialPageRoute(builder: (_) => const ChatsView());
|
||||
case '/chat_details':
|
||||
final chat = settings.arguments as Chat;
|
||||
return MaterialPageRoute(
|
||||
builder: (_) => ChatDetailsView(chat: chat),
|
||||
);
|
||||
case '/settings':
|
||||
return MaterialPageRoute(builder: (_) => const SettingsView());
|
||||
default:
|
||||
return MaterialPageRoute(
|
||||
builder:
|
||||
(_) => const LoginView(suppressConnectionSnack: false),
|
||||
);
|
||||
}
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class MyHomePage extends StatefulWidget {
|
||||
const MyHomePage({super.key, required this.title});
|
||||
|
||||
final String title;
|
||||
|
||||
@override
|
||||
State<MyHomePage> createState() => _MyHomePageState();
|
||||
}
|
||||
|
||||
class _MyHomePageState extends State<MyHomePage> {
|
||||
int _counter = 0;
|
||||
|
||||
void _incrementCounter() {
|
||||
setState(() {
|
||||
_counter++;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Scaffold(
|
||||
appBar: AppBar(title: Text(widget.title)),
|
||||
body: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: <Widget>[
|
||||
const Text('You have pushed the button this many times:'),
|
||||
Text(
|
||||
'$_counter',
|
||||
style: Theme.of(context).textTheme.headlineMedium,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
floatingActionButton: FloatingActionButton(
|
||||
onPressed: _incrementCounter,
|
||||
tooltip: 'Increment',
|
||||
child: const Icon(Icons.add),
|
||||
), // This trailing comma makes auto-formatting nicer for build methods.
|
||||
);
|
||||
}
|
||||
}
|
||||