1.0.2
This commit is contained in:
@@ -0,0 +1,200 @@
|
||||
# Release Guide - Shooting App
|
||||
|
||||
## Overview
|
||||
This guide explains how to create and publish a new release of the Shooting App. The app uses GitHub releases and semantic versioning for update management.
|
||||
|
||||
## Current Setup
|
||||
- **Repository**: https://github.com/Bl3kiiie/shooting_app
|
||||
- **Update Method**: Manual check via Settings button (no automatic prompts)
|
||||
- **Version File**: `pubspec.yaml`
|
||||
- **Update Detection**: Compares app version with GitHub release tags
|
||||
|
||||
---
|
||||
|
||||
## Step-by-Step Release Process
|
||||
|
||||
### 1. Update Version in pubspec.yaml
|
||||
Open `pubspec.yaml` and update the version number:
|
||||
|
||||
```yaml
|
||||
version: 1.0.2+2
|
||||
```
|
||||
|
||||
Format: `MAJOR.MINOR.PATCH+BUILD_NUMBER`
|
||||
|
||||
Examples:
|
||||
- `1.0.1+1` → `1.0.2+1` (patch release, new build)
|
||||
- `1.0.2+1` → `1.0.2+2` (same version, new build)
|
||||
- `1.0.2+2` → `1.1.0+1` (minor release)
|
||||
|
||||
**Important**: New tag must be HIGHER than previous version for users to see update prompt.
|
||||
|
||||
### 2. Build Release APK
|
||||
|
||||
```bash
|
||||
# Navigate to project directory
|
||||
cd /home/bl3kunja/Documents/Programming/Strelišče/shooting_app
|
||||
|
||||
# Build release APK
|
||||
flutter build apk --release
|
||||
```
|
||||
|
||||
The APK will be generated at: `build/app/outputs/flutter-app/release/app-release.apk`
|
||||
|
||||
### 3. Create GitHub Release (Option A: Web Interface - Recommended)
|
||||
|
||||
1. Go to: https://github.com/Bl3kiiie/shooting_app/releases
|
||||
2. Click **Draft a new release** or **Create a new release**
|
||||
3. Fill in the form:
|
||||
|
||||
**Tag version**: `v1.0.2` (must match your new version number)
|
||||
|
||||
**Release title**: e.g., `Version 1.0.2 - Bug Fixes and Improvements`
|
||||
|
||||
**Description** (example):
|
||||
```
|
||||
## Changes in v1.0.2
|
||||
- Fixed camera calibration issue
|
||||
- Improved bullet detection accuracy
|
||||
- Added German language support
|
||||
- Performance optimizations
|
||||
|
||||
## Installation
|
||||
Download the APK from the assets below and install on your device.
|
||||
```
|
||||
|
||||
4. **Upload APK**:
|
||||
- Click "Attach binaries by dropping them here or selecting them"
|
||||
- Select: `build/app/outputs/flutter-app/release/app-release.apk`
|
||||
|
||||
5. Click **Publish release**
|
||||
|
||||
### 3. Create GitHub Release (Option B: Command Line)
|
||||
|
||||
If using GitHub CLI (`gh` command):
|
||||
|
||||
```bash
|
||||
# Create tag locally
|
||||
git tag v1.0.2
|
||||
|
||||
# Push tag to GitHub
|
||||
git push origin v1.0.2
|
||||
|
||||
# Create release (interactive)
|
||||
gh release create v1.0.2 \
|
||||
--title "Version 1.0.2" \
|
||||
--notes "Your changelog here"
|
||||
|
||||
# Upload APK to release
|
||||
gh release upload v1.0.2 build/app/outputs/flutter-app/release/app-release.apk
|
||||
```
|
||||
|
||||
### 4. Commit Version Update (Optional)
|
||||
If you want to track version changes in git:
|
||||
|
||||
```bash
|
||||
git add pubspec.yaml
|
||||
git commit -m "bump version to 1.0.2+2"
|
||||
git push origin main
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## How Users Update
|
||||
|
||||
1. User opens app → Settings screen
|
||||
2. User taps **"Check for Updates"** button
|
||||
3. App compares:
|
||||
- Current version: `1.0.1+1`
|
||||
- Latest GitHub release tag: `v1.0.2`
|
||||
4. Since `v1.0.2 > v1.0.1`, update is available
|
||||
5. Dialog shows **"Update Available"**
|
||||
6. User clicks **"Update"** → Opens GitHub release page
|
||||
7. User downloads and installs new APK
|
||||
|
||||
---
|
||||
|
||||
## Important Notes
|
||||
|
||||
### Version Numbering
|
||||
- Always increment version number before creating release
|
||||
- Tag format: `vX.Y.Z` (e.g., `v1.0.2`, `v1.1.0`)
|
||||
- App checks: `tag_version > app_version`
|
||||
|
||||
### GitHub Token
|
||||
- Stored in: `lib/services/update_checker.dart`
|
||||
- Token: `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`
|
||||
- Scopes: `repo` (for private repo access)
|
||||
- Consider rotating this token periodically for security
|
||||
|
||||
### Testing Before Release
|
||||
```bash
|
||||
# Test the build
|
||||
flutter build apk --release
|
||||
|
||||
# Verify APK was created
|
||||
ls -lh build/app/outputs/flutter-app/release/app-release.apk
|
||||
|
||||
# Optionally install on connected device
|
||||
flutter install build/app/outputs/flutter-app/release/app-release.apk
|
||||
```
|
||||
|
||||
### Troubleshooting Update Detection
|
||||
|
||||
If users don't see update prompt:
|
||||
|
||||
1. Check GitHub release exists: https://github.com/Bl3kiiie/shooting_app/releases
|
||||
2. Verify tag format is `vX.Y.Z` (must start with 'v')
|
||||
3. Confirm new version > current version
|
||||
4. User must tap "Check for Updates" manually
|
||||
5. Check app logs for errors
|
||||
|
||||
---
|
||||
|
||||
## Checklist for Next Release
|
||||
|
||||
- [ ] Update version in `pubspec.yaml`
|
||||
- [ ] Test build: `flutter build apk --release`
|
||||
- [ ] Create GitHub release with tag `vX.Y.Z`
|
||||
- [ ] Upload APK to release
|
||||
- [ ] Write changelog/description
|
||||
- [ ] Publish release
|
||||
- [ ] (Optional) Test update detection in app
|
||||
|
||||
---
|
||||
|
||||
## Example Release Workflow
|
||||
|
||||
```bash
|
||||
# 1. Update version in pubspec.yaml (manually edit to 1.0.2+1)
|
||||
|
||||
# 2. Build APK
|
||||
flutter build apk --release
|
||||
|
||||
# 3. Create tag and push
|
||||
git tag v1.0.2
|
||||
git push origin v1.0.2
|
||||
|
||||
# 4. Create release via GitHub web:
|
||||
# - Go to https://github.com/Bl3kiiie/shooting_app/releases
|
||||
# - Draft new release
|
||||
# - Tag: v1.0.2
|
||||
# - Upload APK from build/app/outputs/flutter-app/release/app-release.apk
|
||||
# - Publish
|
||||
|
||||
# 5. Users can now "Check for Updates" and see the new version
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Files Referenced
|
||||
- `pubspec.yaml` - Version number
|
||||
- `lib/services/update_checker.dart` - GitHub token and update logic
|
||||
- `lib/screens/settings_screen.dart` - "Check for Updates" button
|
||||
- `lib/utils/translations.dart` - Update dialog translations
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: November 13, 2025
|
||||
**Current Version**: 1.0.1
|
||||
**Next Planned Version**: 1.0.2
|
||||
@@ -1,28 +0,0 @@
|
||||
# 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
|
||||
@@ -1,3 +0,0 @@
|
||||
description: This file stores settings for Dart & Flutter DevTools.
|
||||
documentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states
|
||||
extensions:
|
||||
+11
-1
@@ -36,7 +36,17 @@ class MyApp extends StatelessWidget {
|
||||
colorScheme: ColorScheme.fromSeed(seedColor: Colors.white),
|
||||
useMaterial3: true,
|
||||
),
|
||||
home: const HomeScreen(), // Change LoginScreen to HomeScreen
|
||||
darkTheme: ThemeData(
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
seedColor: Colors.blue,
|
||||
brightness: Brightness.dark,
|
||||
),
|
||||
useMaterial3: true,
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: Colors.grey[900],
|
||||
),
|
||||
),
|
||||
home: const HomeScreen(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ class _CalendarScreenState extends State<CalendarScreen> {
|
||||
String _bulletColor = 'Blue';
|
||||
String _selectedLanguage = 'English';
|
||||
bool _showContacts = false;
|
||||
bool _showDebugPreview = true;
|
||||
bool _showDebugPreview = false;
|
||||
|
||||
// Members contact list
|
||||
final List<Contact> _contacts = [
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:upgrader/upgrader.dart';
|
||||
import '../utils/translations.dart';
|
||||
|
||||
class SettingsScreen extends StatefulWidget {
|
||||
@@ -170,6 +171,224 @@ Future<void> _saveLanguage(String language) async {
|
||||
_loadCameraIPs();
|
||||
}
|
||||
|
||||
Future<void> _checkForUpdatesManual() async {
|
||||
// Check for updates without the 24-hour throttling
|
||||
final upgrader = Upgrader();
|
||||
try {
|
||||
final isUpdateAvailable = await upgrader.isUpdateAvailable();
|
||||
if (isUpdateAvailable && mounted) {
|
||||
_showCustomUpdateDialog(true);
|
||||
} else if (mounted) {
|
||||
// Show "No updates available" dialog
|
||||
_showNoUpdatesDialog();
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(_t('Update check failed'))),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _showCustomUpdateDialog(bool updateAvailable) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Colors.grey[900],
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.update,
|
||||
color: Colors.white,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
_t('Update Available'),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_t('A new version of the app is available'),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[300],
|
||||
height: 1.5,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[800],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_t('Later'),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
Navigator.of(context).pop();
|
||||
// Trigger the update using the upgrader package
|
||||
_triggerUpdate();
|
||||
},
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_t('Update'),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showNoUpdatesDialog() {
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Colors.grey[900],
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.green,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.check_circle,
|
||||
color: Colors.white,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
Text(
|
||||
_t('No updates available'),
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_t('You are using the latest version'),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[300],
|
||||
height: 1.5,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: GestureDetector(
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
_t('OK'),
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _triggerUpdate() {
|
||||
// Use Upgrader to trigger the app store/play store
|
||||
try {
|
||||
Upgrader().sendUserToAppStore();
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(_t('Failed to open app store'))),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _editCameraIP(int index) {
|
||||
TextEditingController controller = TextEditingController(
|
||||
text: _cameraIPs[index],
|
||||
@@ -314,6 +533,8 @@ Future<void> _saveLanguage(String language) async {
|
||||
const SizedBox(height: 8),
|
||||
_buildLanguageSelector(),
|
||||
const SizedBox(height: 16),
|
||||
_buildCheckUpdateButton(),
|
||||
const SizedBox(height: 16),
|
||||
_buildBulletColorSelector(),
|
||||
const SizedBox(height: 16),
|
||||
|
||||
@@ -372,6 +593,46 @@ Future<void> _saveLanguage(String language) async {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCheckUpdateButton() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
_t("Updates"),
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.black87,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
GestureDetector(
|
||||
onTap: _checkForUpdatesManual,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 14),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.system_update, color: Colors.black54),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Text(
|
||||
_t('Check for Updates'),
|
||||
style: const TextStyle(color: Colors.black87, fontSize: 14),
|
||||
),
|
||||
),
|
||||
const Icon(Icons.chevron_right, color: Colors.black54),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBulletColorSelector() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:upgrader/upgrader.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import '../utils/translations.dart';
|
||||
import '../widgets/custom_upgrade_alert.dart';
|
||||
|
||||
// GitHub Configuration
|
||||
const String _githubRepoOwner = 'Bl3kiiie';
|
||||
const String _githubRepoName = 'shooting_app';
|
||||
const String _githubToken =
|
||||
'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'; // Replace with your GitHub Personal Access Token
|
||||
|
||||
class UpdateChecker {
|
||||
static final UpdateChecker _instance = UpdateChecker._internal();
|
||||
|
||||
factory UpdateChecker() {
|
||||
return _instance;
|
||||
}
|
||||
|
||||
UpdateChecker._internal();
|
||||
|
||||
Future<void> checkForUpdate(BuildContext context, String language) async {
|
||||
// Check if we've already checked today
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final lastCheckTime = prefs.getInt('last_update_check_time') ?? 0;
|
||||
final now = DateTime.now().millisecondsSinceEpoch;
|
||||
final oneDayInMilliseconds = 24 * 60 * 60 * 1000;
|
||||
|
||||
// Only check once per day
|
||||
if (now - lastCheckTime < oneDayInMilliseconds) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the last check time
|
||||
await prefs.setInt('last_update_check_time', now);
|
||||
|
||||
final upgrader = Upgrader();
|
||||
|
||||
try {
|
||||
// Check if update is available
|
||||
final isUpdateAvailable = await upgrader.isUpdateAvailable();
|
||||
|
||||
if (isUpdateAvailable && context.mounted) {
|
||||
_showCustomUpdateDialog(context, language);
|
||||
}
|
||||
} catch (e) {
|
||||
// Silently fail if update check fails
|
||||
print('Update check failed: $e');
|
||||
}
|
||||
}
|
||||
|
||||
void _showCustomUpdateDialog(BuildContext context, String language) {
|
||||
showDialog(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (BuildContext context) {
|
||||
return CustomUpdateDialog(
|
||||
title: AppTranslations.translate('Update Available', language),
|
||||
message: AppTranslations.translate(
|
||||
'A new version of the app is available', language),
|
||||
updateButtonText: AppTranslations.translate('Update', language),
|
||||
laterButtonText: AppTranslations.translate('Later', language),
|
||||
onUpdate: () {
|
||||
Upgrader().sendUserToAppStore();
|
||||
},
|
||||
onLater: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -584,6 +584,46 @@ class AppTranslations {
|
||||
'English': 'copied to clipboard',
|
||||
'Slovenian': 'kopirano v odložišče',
|
||||
},
|
||||
|
||||
// App Update translations
|
||||
'Update Available': {
|
||||
'English': 'Update Available',
|
||||
'Slovenian': 'Posodobitev je na voljo',
|
||||
},
|
||||
'A new version of the app is available': {
|
||||
'English': 'A new version of the app is available',
|
||||
'Slovenian': 'Nova različica aplikacije je na voljo',
|
||||
},
|
||||
'Update': {
|
||||
'English': 'Update',
|
||||
'Slovenian': 'Posodobi',
|
||||
},
|
||||
'Later': {
|
||||
'English': 'Later',
|
||||
'Slovenian': 'Kasneje',
|
||||
},
|
||||
|
||||
// App Update Check Button
|
||||
'Check for Updates': {
|
||||
'English': 'Check for Updates',
|
||||
'Slovenian': 'Preveri za posodobitve',
|
||||
},
|
||||
'No updates available': {
|
||||
'English': 'No updates available',
|
||||
'Slovenian': 'Ni dostopnih posodobitev',
|
||||
},
|
||||
'You are using the latest version': {
|
||||
'English': 'You are using the latest version',
|
||||
'Slovenian': 'Uporabljate najnovejšo različico',
|
||||
},
|
||||
'Updates': {
|
||||
'English': 'Updates',
|
||||
'Slovenian': 'Posodobitve',
|
||||
},
|
||||
'OK': {
|
||||
'English': 'OK',
|
||||
'Slovenian': 'V redu',
|
||||
},
|
||||
};
|
||||
|
||||
static String translate(String key, String language) {
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:upgrader/upgrader.dart';
|
||||
import '../utils/translations.dart';
|
||||
|
||||
class CustomUpgradeAlert extends StatefulWidget {
|
||||
final Widget child;
|
||||
final String language;
|
||||
|
||||
const CustomUpgradeAlert({
|
||||
super.key,
|
||||
required this.child,
|
||||
required this.language,
|
||||
});
|
||||
|
||||
@override
|
||||
State<CustomUpgradeAlert> createState() => _CustomUpgradeAlertState();
|
||||
}
|
||||
|
||||
class _CustomUpgradeAlertState extends State<CustomUpgradeAlert> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UpgradeAlert(
|
||||
child: widget.child,
|
||||
upgrader: Upgrader(
|
||||
debugDisplayAlways: true,
|
||||
),
|
||||
dialogStyle: UpgradeDialogStyle.material,
|
||||
shouldPopScope: () => true,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class CustomUpdateDialog extends StatelessWidget {
|
||||
final String title;
|
||||
final String message;
|
||||
final String updateButtonText;
|
||||
final String laterButtonText;
|
||||
final VoidCallback onUpdate;
|
||||
final VoidCallback onLater;
|
||||
|
||||
const CustomUpdateDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.message,
|
||||
required this.updateButtonText,
|
||||
required this.laterButtonText,
|
||||
required this.onUpdate,
|
||||
required this.onLater,
|
||||
});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Dialog(
|
||||
backgroundColor: Colors.grey[900],
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(24),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// App Icon
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.update,
|
||||
color: Colors.white,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
// Title
|
||||
Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.white,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// Message
|
||||
Text(
|
||||
message,
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
color: Colors.grey[300],
|
||||
height: 1.5,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
// Buttons
|
||||
Row(
|
||||
children: [
|
||||
// Later Button
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onLater,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[800],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
laterButtonText,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.grey,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
// Update Button
|
||||
Expanded(
|
||||
child: GestureDetector(
|
||||
onTap: onUpdate,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blue,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Center(
|
||||
child: Text(
|
||||
updateButtonText,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -137,6 +137,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
csslib:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csslib
|
||||
sha256: "09bad715f418841f976c77db72d5398dc1253c21fb9c0c7f0b0b985860b2d58e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.2"
|
||||
cupertino_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -344,6 +352,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
html:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: html
|
||||
sha256: "6d1264f2dffa1b1101c25a91dff0dc2daee4c18e87cd8538729773c073dbf602"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.15.6"
|
||||
http:
|
||||
dependency: "direct overridden"
|
||||
description:
|
||||
@@ -512,6 +528,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
os_detect:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: os_detect
|
||||
sha256: "7d87c0dd98c6faf110d5aa498e9a6df02ffce4bb78cc9cfc8ad02929be9bb71f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
package_info_plus:
|
||||
dependency: transitive
|
||||
description:
|
||||
@@ -797,6 +821,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
upgrader:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: upgrader
|
||||
sha256: "4ba3f840691ffa43c6d71a93a6ad3ae01590c7895c3eb33f9f683513f736a9f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "12.3.0"
|
||||
url_launcher:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@@ -893,6 +925,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
version:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: version
|
||||
sha256: "3d4140128e6ea10d83da32fef2fa4003fccbf6852217bb854845802f04191f94"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
visibility_detector:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
Reference in New Issue
Block a user