Compare commits
7 Commits
1b261c08bb
...
temp2
| Author | SHA1 | Date | |
|---|---|---|---|
| 6318e60351 | |||
| 906abad2ba | |||
| f809375a31 | |||
| 6957075f46 | |||
| b835603a7c | |||
| e1684285a2 | |||
| 51cc737ad8 |
@ -2,7 +2,7 @@
|
||||
<application
|
||||
android:label="rtime"
|
||||
android:name="${applicationName}"
|
||||
android:icon="@mipmap/ic_launcher">
|
||||
android:icon="@mipmap/launcher_icon">
|
||||
<activity
|
||||
android:name="com.yalantis.ucrop.UCropActivity"
|
||||
android:screenOrientation="portrait"
|
||||
|
||||
BIN
android/app/src/main/res/mipmap-hdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 4.5 KiB |
BIN
android/app/src/main/res/mipmap-mdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 2.7 KiB |
BIN
android/app/src/main/res/mipmap-xhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 6.3 KiB |
BIN
android/app/src/main/res/mipmap-xxhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 9.9 KiB |
BIN
android/app/src/main/res/mipmap-xxxhdpi/launcher_icon.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
7
assets/images/drone.svg
Normal file
@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Svg Vector Icons : http://www.onlinewebfonts.com/icon -->
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
|
||||
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 256 256" enable-background="new 0 0 256 256" xml:space="preserve">
|
||||
<metadata> Svg Vector Icons : http://www.onlinewebfonts.com/icon </metadata>
|
||||
<g><g><g><path fill="#000000" d="M46.4,10.5c-9.4,1.8-16.9,5.8-23.7,12.7c-6,6-9.8,12.8-11.9,21.1c-1.1,4.4-1.1,15.4,0,19.8c2.2,8.6,5.9,15.2,12.3,21.5c4.7,4.6,9.2,7.6,14.8,9.9c5.7,2.2,8.9,2.8,16.1,2.8c8.4,0,10.3-0.4,20.6-4.7c0.6-0.2,8.7,10.7,10,13.7c1,2,1,2.8,1,20.8c0,18,0,18.8-1,20.8c-1.3,3-9.4,13.9-10,13.7c-10.3-4.2-12.2-4.7-20.6-4.7c-9.4,0-15.9,1.8-23.6,6.6c-4.4,2.7-11.2,9.5-13.9,13.9c-4.8,7.7-6.6,14.1-6.6,23.6c0,7.4,0.6,10.5,3,16.6c4.3,10.9,13.9,20.4,24.9,24.7c5.7,2.2,8.9,2.8,16.1,2.8c9.4,0,15.9-1.8,23.6-6.6c4.4-2.7,11.2-9.5,13.9-13.9c4.8-7.7,6.6-14.1,6.6-23.6c0-8.4-0.4-10.3-4.7-20.6c-0.2-0.6,10.6-8.6,13.4-9.9c2.8-1.2,6.8-1.8,10.9-1.3c4.4,0.5,15.9,0.5,20.3,0c4.1-0.4,8.1,0.1,10.9,1.3c1.1,0.5,4.7,2.8,7.8,5.1c4.5,3.3,5.7,4.4,5.4,5c-3.7,8.5-5,15-4.7,22.4c0.6,11.7,4.9,21.2,13.4,29.6c6,5.9,12.7,9.6,20.9,11.6c4.4,1.1,15.4,1.1,19.8,0c8.4-2.1,15.1-5.9,21.3-12.1c6.1-6.2,9.9-12.9,12.1-21.3c1.1-4.4,1.1-15.4,0-19.8c-2.1-8.2-5.8-14.9-11.6-20.9c-8.4-8.5-17.8-12.8-29.6-13.4c-7.5-0.4-14,1-22.4,4.7c-0.6,0.3-1.7-1-5-5.4c-2.3-3.1-4.6-6.7-5.1-7.8c-1.2-2.8-1.7-6.8-1.3-11.2c0.5-4.8,0.5-15.1,0-19.9c-0.4-4.4,0.1-8.4,1.3-11.2c1.2-2.8,9.2-13.6,9.9-13.4c10.3,4.2,12.2,4.7,20.6,4.7c9.4,0,15.9-1.8,23.6-6.6c4.4-2.7,11.2-9.5,13.9-13.9c4.8-7.7,6.6-14.1,6.6-23.6c0-9.3-1.5-14.8-6.1-22.9c-2.7-4.7-9.6-11.7-14.4-14.6c-7.7-4.8-14.1-6.6-23.6-6.6s-15.9,1.8-23.6,6.6c-4.4,2.7-11.2,9.5-13.9,13.9c-4.8,7.7-6.6,14.1-6.6,23.6c0,8.4,0.4,10.3,4.7,20.6c0.2,0.6-10.7,8.7-13.7,10c-2,0.9-2.9,1-20.8,1s-18.8,0-20.8-1c-3-1.3-13.9-9.4-13.7-10c4.2-10.3,4.7-12.2,4.7-20.6c0-7.2-0.6-10.4-2.8-16.1c-2.2-5.5-5.3-10.2-9.7-14.6c-6.2-6.3-12.6-10.1-21-12.2C60.2,9.9,51,9.6,46.4,10.5z M62.9,18.3c4.6,1.3,7.8,2.6,11.2,4.9C88.7,32.8,94.6,50.5,88.7,67c-0.9,2.5-1.6,3.7-1.9,3.5c-0.3-0.2-5.3-3.7-11-7.9c-9.2-6.7-10.5-7.8-10.5-8.8c0-0.7-0.2-1.9-0.5-2.7c-0.5-1.4-0.4-1.5,2.9-5c4.1-4.4,7.2-8.5,8.4-11.1c0.8-1.8,0.8-2.1,0.2-2.7c-0.6-0.6-0.9-0.6-2.7,0.2c-2.7,1.2-6.7,4.2-11.1,8.4c-3.5,3.3-3.6,3.4-5,2.9c-3.7-1.3-7.6-0.2-10.8,3c-3.2,3.2-4.2,7.1-3,10.8c0.5,1.4,0.4,1.5-2.9,5c-4.1,4.4-7.2,8.5-8.4,11.1c-0.8,1.8-0.8,2.1-0.2,2.7c0.6,0.6,0.9,0.6,2.7-0.2c2.7-1.2,6.7-4.2,11.1-8.4c3.5-3.3,3.6-3.4,5-2.9c0.8,0.3,2,0.5,2.7,0.5c1.1,0,2.2,1.3,8.8,10.5c4.2,5.8,7.8,10.8,7.9,11.1c0.2,0.3-1,1-3.5,1.9c-16.5,6-34.2,0.1-43.8-14.6c-8.8-13.4-7.6-31.4,2.8-43.7c5.5-6.5,12.6-10.7,21.2-12.5C50.9,17.1,59.3,17.3,62.9,18.3z M210.7,18.3c7,1.9,12.5,5,17.3,9.8c14.4,14.4,14.4,37.6,0,52.1c-8.6,8.6-21,12.5-32.6,10.3c-4.5-0.9-10.2-3-9.7-3.7c0.1-0.3,3.7-5.2,7.9-11c6.7-9.1,7.8-10.5,8.8-10.5c0.7,0,1.9-0.2,2.7-0.5c1.4-0.5,1.5-0.4,5,2.9c4.4,4.1,8.5,7.2,11.1,8.4c1.8,0.8,2.1,0.8,2.7,0.2s0.6-0.9-0.2-2.7c-1.2-2.7-4.2-6.7-8.4-11.1c-3.3-3.5-3.4-3.6-2.9-5c1.3-3.7,0.2-7.6-3-10.8c-3.2-3.2-7.1-4.2-10.8-3c-1.4,0.5-1.5,0.4-5-2.9c-4.4-4.1-8.5-7.2-11.1-8.4c-1.8-0.8-2.1-0.8-2.7-0.2s-0.6,0.9,0.2,2.7c1.2,2.7,4.2,6.7,8.4,11.1c3.3,3.5,3.4,3.6,2.9,5c-0.3,0.8-0.5,2-0.5,2.7c0,1.1-1.3,2.2-10.4,8.8c-5.8,4.2-10.7,7.7-11,7.9c-0.7,0.5-2.8-4.9-3.7-9.7c-2-10.6,1-21.9,8.1-30.3c5.5-6.5,12.6-10.7,21.2-12.5C198.7,17.1,207.1,17.3,210.7,18.3z M60.7,165.6c4.8,0.9,10.2,3,9.7,3.7c-0.2,0.3-3.7,5.3-7.9,11c-6.7,9.1-7.8,10.4-8.8,10.4c-0.7,0-1.9,0.2-2.7,0.5c-1.4,0.5-1.5,0.4-5-2.9c-4.4-4.1-8.5-7.2-11.1-8.4c-1.8-0.8-2.1-0.8-2.7-0.2s-0.6,0.9,0.2,2.7c1.2,2.7,4.2,6.7,8.4,11.1c3.3,3.5,3.4,3.6,2.9,5c-2.8,7.9,5.9,16.5,13.8,13.8c1.4-0.5,1.5-0.4,5,2.9c4.4,4.1,8.5,7.2,11.1,8.4c1.8,0.8,2.1,0.8,2.7,0.2c0.6-0.6,0.6-0.9-0.2-2.7c-1.2-2.7-4.2-6.7-8.4-11.1c-3.3-3.5-3.4-3.6-2.9-5c0.3-0.8,0.5-2,0.5-2.7c0-1.1,1.3-2.2,10.5-8.8c5.8-4.2,10.7-7.8,11-7.9c0.7-0.4,2.8,5.2,3.7,9.7c2.2,11.6-1.7,24-10.3,32.6c-12.3,12.3-31.6,14.4-46,4.9c-14.5-9.6-20.5-27.2-14.7-43.6C25.4,172.6,43.3,162.3,60.7,165.6z M210.7,166.1c7,1.9,12.5,5,17.3,9.8c12.1,12.1,14.3,30.8,5.4,45.3c-10.8,17.6-34.2,23-51.4,11.8c-14.7-9.6-20.6-27.3-14.6-43.8c0.9-2.5,1.6-3.7,1.9-3.5c0.3,0.2,5.3,3.7,11.1,7.9c9.2,6.7,10.5,7.8,10.5,8.8c0,0.7,0.2,1.9,0.5,2.7c0.5,1.4,0.4,1.5-3,5.2c-4,4.2-7.3,8.7-8.4,11.3c-0.6,1.5-0.6,1.8,0,2.4c0.6,0.6,0.9,0.6,2.7-0.2c2.7-1.2,6.7-4.2,11.1-8.4c3.5-3.3,3.6-3.4,5-2.9c3.7,1.3,7.6,0.2,10.8-3c3.2-3.2,4.2-7.1,3-10.8c-0.5-1.4-0.4-1.5,2.9-5c4.1-4.4,7.2-8.5,8.4-11.1c0.8-1.8,0.8-2.1,0.2-2.7c-0.6-0.6-0.9-0.6-2.4,0c-2.5,1.1-7,4.4-11.3,8.4c-3.6,3.4-3.8,3.5-5.2,3c-0.8-0.3-2-0.5-2.7-0.5c-1.1,0-2.2-1.3-8.8-10.5c-4.2-5.8-7.8-10.7-7.9-11c-0.4-0.6,5-2.7,9.4-3.7C198.7,164.9,207.1,165.1,210.7,166.1z"/></g></g></g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
BIN
assets/images/rtimelogo.png
Normal file
|
After Width: | Height: | Size: 42 KiB |
110
assets/images/rtimelogo.svg
Normal file
|
After Width: | Height: | Size: 11 KiB |
@ -427,7 +427,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
@ -484,7 +484,7 @@
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
|
||||
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = AppIcon;
|
||||
CLANG_ANALYZER_NONNULL = YES;
|
||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
|
||||
CLANG_CXX_LIBRARY = "libc++";
|
||||
|
||||
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 112 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 828 B |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 3.3 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 5.4 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 7.9 KiB |
|
After Width: | Height: | Size: 2.7 KiB |
|
After Width: | Height: | Size: 6.4 KiB |
|
After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 7.5 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 7.9 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 13 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 4.5 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 12 KiB |
@ -206,5 +206,10 @@
|
||||
}
|
||||
},
|
||||
"flightDeletedSuccessfully": "Flight deleted successfully!",
|
||||
"failedToDeleteFlight": "Failed to delete flight"
|
||||
"failedToDeleteFlight": "Failed to delete flight",
|
||||
"customColorSetting": "Button Color",
|
||||
"selectButtonColor": "Select Button Primary Color",
|
||||
"pickColor": "Pick a color",
|
||||
"select": "Select",
|
||||
"resetColor": "Reset Color"
|
||||
}
|
||||
|
||||
@ -206,5 +206,10 @@
|
||||
}
|
||||
},
|
||||
"flightDeletedSuccessfully": "Vol supprimé avec succès !",
|
||||
"failedToDeleteFlight": "Échec de la suppression du vol"
|
||||
"failedToDeleteFlight": "Échec de la suppression du vol",
|
||||
"customColorSetting": "Couleur des Boutons",
|
||||
"selectButtonColor": "Sélectionner la couleur principale des boutons",
|
||||
"pickColor": "Choisir une couleur",
|
||||
"select": "Sélectionner",
|
||||
"resetColor": "Réinitialiser la couleur"
|
||||
}
|
||||
|
||||
@ -712,6 +712,36 @@ abstract class AppLocalizations {
|
||||
/// In en, this message translates to:
|
||||
/// **'Failed to delete flight'**
|
||||
String get failedToDeleteFlight;
|
||||
|
||||
/// No description provided for @customColorSetting.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Button Color'**
|
||||
String get customColorSetting;
|
||||
|
||||
/// No description provided for @selectButtonColor.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select Button Primary Color'**
|
||||
String get selectButtonColor;
|
||||
|
||||
/// No description provided for @pickColor.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Pick a color'**
|
||||
String get pickColor;
|
||||
|
||||
/// No description provided for @select.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Select'**
|
||||
String get select;
|
||||
|
||||
/// No description provided for @resetColor.
|
||||
///
|
||||
/// In en, this message translates to:
|
||||
/// **'Reset Color'**
|
||||
String get resetColor;
|
||||
}
|
||||
|
||||
class _AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
|
||||
|
||||
@ -342,4 +342,19 @@ class AppLocalizationsEn extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get failedToDeleteFlight => 'Failed to delete flight';
|
||||
|
||||
@override
|
||||
String get customColorSetting => 'Button Color';
|
||||
|
||||
@override
|
||||
String get selectButtonColor => 'Select Button Primary Color';
|
||||
|
||||
@override
|
||||
String get pickColor => 'Pick a color';
|
||||
|
||||
@override
|
||||
String get select => 'Select';
|
||||
|
||||
@override
|
||||
String get resetColor => 'Reset Color';
|
||||
}
|
||||
|
||||
@ -342,4 +342,19 @@ class AppLocalizationsFr extends AppLocalizations {
|
||||
|
||||
@override
|
||||
String get failedToDeleteFlight => 'Échec de la suppression du vol';
|
||||
|
||||
@override
|
||||
String get customColorSetting => 'Couleur des Boutons';
|
||||
|
||||
@override
|
||||
String get selectButtonColor => 'Sélectionner la couleur principale des boutons';
|
||||
|
||||
@override
|
||||
String get pickColor => 'Choisir une couleur';
|
||||
|
||||
@override
|
||||
String get select => 'Sélectionner';
|
||||
|
||||
@override
|
||||
String get resetColor => 'Réinitialiser la couleur';
|
||||
}
|
||||
|
||||
@ -5,6 +5,7 @@ import 'package:rtime/l10n/app_localizations.dart';
|
||||
import 'package:rtime/pages/home_page.dart';
|
||||
import 'package:rtime/providers/local_provider.dart';
|
||||
import 'package:rtime/providers/theme_provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
|
||||
void main() {
|
||||
runApp(
|
||||
@ -16,6 +17,9 @@ void main() {
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => ThemeProvider(),
|
||||
),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => ColorProvider(),
|
||||
),
|
||||
],
|
||||
child: const MyApp(),
|
||||
),
|
||||
@ -32,8 +36,10 @@ class MyApp extends StatefulWidget {
|
||||
class _MyAppState extends State<MyApp> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Consumer2<LocaleProvider, ThemeProvider>(
|
||||
builder: (context, localeProvider, themeProvider, child) {
|
||||
return Consumer3<LocaleProvider, ThemeProvider, ColorProvider>(
|
||||
builder: (context, localeProvider, themeProvider, colorProvider, child) {
|
||||
final customAccentColor = colorProvider.accentColor;
|
||||
|
||||
return MaterialApp(
|
||||
title: 'Rtime',
|
||||
locale: localeProvider.locale,
|
||||
@ -47,35 +53,33 @@ class _MyAppState extends State<MyApp> {
|
||||
Locale('en', ''),
|
||||
Locale('fr', ''),
|
||||
],
|
||||
|
||||
theme: ThemeData(
|
||||
primaryColor: Colors.white,
|
||||
primaryColor: Colors.yellow[700],
|
||||
colorScheme: ColorScheme.light(
|
||||
primary: Colors.blue.shade600,
|
||||
primary: Colors.yellow[700]!,
|
||||
secondary: Colors.teal.shade400,
|
||||
surface: Colors.white,
|
||||
background: Colors.white,
|
||||
error: Colors.red.shade700,
|
||||
onPrimary: Colors.white,
|
||||
onPrimary: Colors.black87,
|
||||
onSecondary: Colors.white,
|
||||
onSurface: Colors.black87,
|
||||
onBackground: Colors.black87,
|
||||
),
|
||||
scaffoldBackgroundColor: Colors.white,
|
||||
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: Colors.white,
|
||||
backgroundColor: Colors.yellow[700],
|
||||
foregroundColor: Colors.black87,
|
||||
elevation: 0.5,
|
||||
iconTheme: const IconThemeData(color: Colors.black87),
|
||||
titleTextStyle: const TextStyle(
|
||||
color: Colors.black87,
|
||||
color: Colors.black,
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
||||
backgroundColor: Colors.blue.shade600,
|
||||
backgroundColor: customAccentColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
@ -99,7 +103,7 @@ class _MyAppState extends State<MyApp> {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
selectedTileColor: Colors.blue.shade50,
|
||||
selectedTileColor: customAccentColor.withOpacity(0.1),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
headlineSmall: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87),
|
||||
@ -110,11 +114,27 @@ class _MyAppState extends State<MyApp> {
|
||||
bodySmall: TextStyle(fontSize: 12, color: Colors.black45),
|
||||
),
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
buttonTheme: ButtonThemeData(
|
||||
buttonColor: Colors.blue.shade600,
|
||||
textTheme: ButtonTextTheme.primary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: customAccentColor,
|
||||
foregroundColor: Colors.white,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: customAccentColor,
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: customAccentColor),
|
||||
foregroundColor: customAccentColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
@ -124,7 +144,7 @@ class _MyAppState extends State<MyApp> {
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.blue.shade600, width: 2),
|
||||
borderSide: BorderSide(color: customAccentColor, width: 2),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
@ -134,12 +154,10 @@ class _MyAppState extends State<MyApp> {
|
||||
hintStyle: TextStyle(color: Colors.grey.shade500),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
darkTheme: ThemeData.dark().copyWith(
|
||||
primaryColor: Colors.blueGrey[900],
|
||||
colorScheme: ColorScheme.dark(
|
||||
primary: Colors.lightBlueAccent,
|
||||
primary: Colors.blueGrey[600]!,
|
||||
secondary: Colors.tealAccent,
|
||||
surface: Colors.blueGrey.shade800,
|
||||
background: Colors.blueGrey.shade900,
|
||||
@ -148,9 +166,6 @@ class _MyAppState extends State<MyApp> {
|
||||
onSurface: Colors.white,
|
||||
onBackground: Colors.white,
|
||||
),
|
||||
|
||||
|
||||
|
||||
appBarTheme: AppBarTheme(
|
||||
backgroundColor: Colors.blueGrey[900],
|
||||
foregroundColor: Colors.white,
|
||||
@ -163,7 +178,7 @@ class _MyAppState extends State<MyApp> {
|
||||
),
|
||||
),
|
||||
floatingActionButtonTheme: FloatingActionButtonThemeData(
|
||||
backgroundColor: Colors.lightBlueAccent,
|
||||
backgroundColor: customAccentColor,
|
||||
foregroundColor: Colors.black,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30.0),
|
||||
@ -185,7 +200,7 @@ class _MyAppState extends State<MyApp> {
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 20, vertical: 10),
|
||||
tileColor: Colors.blueGrey[800],
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)),
|
||||
selectedTileColor: Colors.blueGrey[700],
|
||||
selectedTileColor: customAccentColor.withOpacity(0.3),
|
||||
),
|
||||
textTheme: const TextTheme(
|
||||
headlineSmall: TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
@ -196,11 +211,27 @@ class _MyAppState extends State<MyApp> {
|
||||
bodySmall: TextStyle(fontSize: 12, color: Colors.white70),
|
||||
),
|
||||
visualDensity: VisualDensity.adaptivePlatformDensity,
|
||||
buttonTheme: ButtonThemeData(
|
||||
buttonColor: Colors.lightBlueAccent,
|
||||
textTheme: ButtonTextTheme.primary,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: customAccentColor,
|
||||
foregroundColor: Colors.black,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
textButtonTheme: TextButtonThemeData(
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: customAccentColor,
|
||||
),
|
||||
),
|
||||
outlinedButtonTheme: OutlinedButtonThemeData(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: customAccentColor),
|
||||
foregroundColor: customAccentColor,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
@ -210,7 +241,7 @@ class _MyAppState extends State<MyApp> {
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
borderSide: BorderSide(color: Colors.lightBlueAccent, width: 2),
|
||||
borderSide: BorderSide(color: customAccentColor, width: 2),
|
||||
),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
|
||||
@ -59,29 +59,21 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _loadAssociatedFlights() async {
|
||||
setState(() {
|
||||
_isLoadingFlights = true;
|
||||
});
|
||||
try {
|
||||
final allFlights = await DbHelper.instance.getBatteryFlights(widget.battery.id!);
|
||||
|
||||
_associatedFlights = allFlights
|
||||
.where((flight) => flight.batteryId == widget.battery.id)
|
||||
.toList();
|
||||
|
||||
_associatedFlights.sort((a, b) => b.startTimestamp.compareTo(a.startTimestamp));
|
||||
|
||||
setState(() {
|
||||
_isLoadingFlights = false;
|
||||
});
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
|
||||
setState(() {
|
||||
_isLoadingFlights = false;
|
||||
});
|
||||
@ -91,9 +83,7 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
|
||||
Future<void> _pickImage(ImageSource source) async {
|
||||
final uuid = await ImagesManager.instance.createImage(source);
|
||||
|
||||
if (uuid != null) {
|
||||
|
||||
if (_currentImageUuid != null && _currentImageUuid != uuid) {
|
||||
await ImagesManager.instance.deleteImage(_currentImageUuid!);
|
||||
}
|
||||
@ -124,20 +114,13 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
voltage: double.parse(_voltageController.text.trim()),
|
||||
imageUuid: _currentImageUuid,
|
||||
);
|
||||
|
||||
try {
|
||||
await DbHelper.instance.updateBattery(updatedBattery);
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,25 +145,17 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
if (confirm == true) {
|
||||
try {
|
||||
|
||||
if (_currentImageUuid != null && _currentImageUuid!.isNotEmpty) {
|
||||
await ImagesManager.instance.deleteImage(_currentImageUuid!);
|
||||
}
|
||||
await DbHelper.instance.deleteBattery(widget.battery.id!);
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -195,8 +170,8 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
appBar: AppBar(
|
||||
title: Text(_isEditing ? l10n.editBattery : l10n.batteryDetails),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
foregroundColor: Theme.of(context).appBarTheme.foregroundColor,
|
||||
iconTheme: Theme.of(context).appBarTheme.iconTheme,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(_isEditing ? Icons.save : Icons.edit),
|
||||
@ -227,9 +202,10 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
width: 180,
|
||||
height: 180,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.tealAccent, width: 2),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary, width: 2),
|
||||
image: _displayImage != null
|
||||
? DecorationImage(
|
||||
image: _displayImage!.image,
|
||||
@ -241,15 +217,17 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
? Icon(
|
||||
Icons.camera_alt,
|
||||
size: 80,
|
||||
color: Colors.blueGrey[400],
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.4),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
if (_isEditing) ...[
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 10.0,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.camera),
|
||||
@ -260,7 +238,6 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.gallery),
|
||||
icon: const Icon(Icons.photo_library),
|
||||
@ -285,15 +262,13 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
readOnly: !_isEditing,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.batteryName,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: _isEditing ? Colors.blueGrey[600]! : Colors.white30),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.tealAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.pleaseEnterBatteryName;
|
||||
@ -307,15 +282,13 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
readOnly: !_isEditing,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.batteryType,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: _isEditing ? Colors.blueGrey[600]! : Colors.white30),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.tealAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.pleaseEnterBatteryType;
|
||||
@ -330,15 +303,13 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
readOnly: !_isEditing,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.batteryVoltage,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: _isEditing ? Colors.blueGrey[600]! : Colors.white30),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.tealAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.pleaseEnterBatteryVoltage;
|
||||
@ -354,25 +325,24 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveBattery,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 15)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
)),
|
||||
foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
child: Text(
|
||||
l10n.saveChanges,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
l10n.latestFlights,
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(color: Colors.white),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
_isLoadingFlights
|
||||
@ -381,7 +351,7 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
? Center(
|
||||
child: Text(
|
||||
l10n.noFlightsYet,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(color: Colors.white70),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
@ -403,8 +373,8 @@ class _BatteryDetailPageState extends State<BatteryDetailPage> {
|
||||
'${flight.name} - ${flightDate.toLocal().toString().split(' ')[0]}',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios,
|
||||
size: 20, color: Colors.white54),
|
||||
trailing: Icon(Icons.arrow_forward_ios,
|
||||
size: 20, color: Theme.of(context).colorScheme.onSurface),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
|
||||
@ -53,7 +53,6 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Future<void> _loadAssociatedFlights() async {
|
||||
setState(() {
|
||||
_isLoadingFlights = true;
|
||||
@ -72,10 +71,6 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
});
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
|
||||
setState(() {
|
||||
_isLoadingFlights = false;
|
||||
});
|
||||
@ -119,16 +114,10 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
try {
|
||||
await DbHelper.instance.updateDrone(updatedDrone);
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,16 +150,10 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
}
|
||||
await DbHelper.instance.deleteDrone(widget.drone.id!);
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -185,8 +168,8 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
appBar: AppBar(
|
||||
title: Text(_isEditing ? l10n.editDrone : l10n.droneDetails),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
foregroundColor: Theme.of(context).appBarTheme.foregroundColor,
|
||||
iconTheme: Theme.of(context).appBarTheme.iconTheme,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: Icon(_isEditing ? Icons.save : Icons.edit),
|
||||
@ -217,9 +200,10 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
width: 180,
|
||||
height: 180,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
color: Theme.of(context).colorScheme.surface,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.lightBlueAccent, width: 2),
|
||||
border: Border.all(
|
||||
color: Theme.of(context).colorScheme.primary, width: 2),
|
||||
image: _displayImage != null
|
||||
? DecorationImage(
|
||||
image: _displayImage!.image,
|
||||
@ -231,15 +215,17 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
? Icon(
|
||||
Icons.camera_alt,
|
||||
size: 80,
|
||||
color: Colors.blueGrey[400],
|
||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.4),
|
||||
)
|
||||
: null,
|
||||
),
|
||||
),
|
||||
if (_isEditing) ...[
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 10.0,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.camera),
|
||||
@ -250,7 +236,6 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.gallery),
|
||||
icon: const Icon(Icons.photo_library),
|
||||
@ -275,15 +260,13 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
readOnly: !_isEditing,
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.droneName,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: _isEditing ? Colors.blueGrey[600]! : Colors.white30),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.lightBlueAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
validator: (value) {
|
||||
if (value == null || value.isEmpty) {
|
||||
return l10n.pleaseEnterDroneName;
|
||||
@ -296,25 +279,24 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveDrone,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.black,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 15)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
)),
|
||||
foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
child: Text(
|
||||
l10n.saveChanges,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
l10n.latestFlights,
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(color: Colors.white),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
_isLoadingFlights
|
||||
@ -323,7 +305,7 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
? Center(
|
||||
child: Text(
|
||||
l10n.noFlightsYet,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(color: Colors.white70),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
)
|
||||
: ListView.builder(
|
||||
@ -345,8 +327,8 @@ class _DroneDetailPageState extends State<DroneDetailPage> {
|
||||
'${flight.name} - ${flightDate.toLocal().toString().split(' ')[0]}',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios,
|
||||
size: 20, color: Colors.white54),
|
||||
trailing: Icon(Icons.arrow_forward_ios,
|
||||
size: 20, color: Theme.of(context).colorScheme.onSurface),
|
||||
onTap: () {
|
||||
Navigator.of(context).push(
|
||||
MaterialPageRoute(
|
||||
|
||||
@ -7,6 +7,9 @@ import 'package:rtime/db/db_helper.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:rtime/images_manager.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
|
||||
|
||||
class FlightDetailPage extends StatefulWidget {
|
||||
final Flight flight;
|
||||
@ -48,15 +51,11 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
final droneList = await DbHelper.instance.getDrones();
|
||||
final batteryList = await DbHelper.instance.getBatteries();
|
||||
|
||||
final drone = droneList.firstWhere(
|
||||
(d) => d.id == widget.flight.droneId,
|
||||
final drone = droneList.firstWhere((d) => d.id == widget.flight.droneId,
|
||||
orElse: () => Drone(name: 'Unknown Drone'));
|
||||
|
||||
final battery = batteryList.firstWhere(
|
||||
(b) => b.id == widget.flight.batteryId,
|
||||
orElse: () => Battery(
|
||||
name: 'Unknown Battery', type: '', voltage: 0.0));
|
||||
|
||||
final battery = batteryList.firstWhere((b) => b.id == widget.flight.batteryId,
|
||||
orElse: () => Battery(name: 'Unknown Battery', type: '', voltage: 0.0));
|
||||
|
||||
await _loadImage(drone.imageUuid, (image) {
|
||||
setState(() {
|
||||
@ -119,9 +118,7 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
Navigator.of(context).pop(true);
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -130,6 +127,8 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final flight = widget.flight;
|
||||
final colorProvider = Provider.of<ColorProvider>(context);
|
||||
final accentColor = colorProvider.accentColor;
|
||||
|
||||
final DateTime startDate =
|
||||
DateTime.fromMillisecondsSinceEpoch(flight.startTimestamp * 1000);
|
||||
@ -140,13 +139,12 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
appBar: AppBar(
|
||||
title: Text(l10n.flightDetails),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
foregroundColor: Colors.white,
|
||||
iconTheme: const IconThemeData(color: Colors.white),
|
||||
elevation: 0,
|
||||
backgroundColor: Theme.of(context).appBarTheme.backgroundColor,
|
||||
foregroundColor: Theme.of(context).appBarTheme.foregroundColor,
|
||||
iconTheme: Theme.of(context).iconTheme,
|
||||
actions: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.delete_forever, color: Colors.redAccent),
|
||||
icon: Icon(Icons.delete_forever, color: Theme.of(context).colorScheme.error),
|
||||
onPressed: _deleteFlight,
|
||||
),
|
||||
],
|
||||
@ -161,8 +159,7 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
Card(
|
||||
elevation: 4,
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(15)),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(20.0),
|
||||
child: Column(
|
||||
@ -170,63 +167,61 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
children: [
|
||||
Text(
|
||||
flight.name,
|
||||
style: Theme.of(context).textTheme.headlineMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(
|
||||
color: Theme.of(context).textTheme.headlineSmall!.color,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Divider(color: Colors.grey[300]),
|
||||
|
||||
Divider(color: Theme.of(context).dividerColor),
|
||||
_buildImageAndDetailRow(
|
||||
context,
|
||||
l10n.drone,
|
||||
_associatedDrone?.name ?? l10n.unknown,
|
||||
_droneDisplayImage,
|
||||
Icons.flight_takeoff,
|
||||
accentColor,
|
||||
),
|
||||
|
||||
_buildImageAndDetailRow(
|
||||
context,
|
||||
l10n.battery,
|
||||
_associatedBattery?.name ?? l10n.unknown,
|
||||
_batteryDisplayImage,
|
||||
Icons.battery_std,
|
||||
accentColor,
|
||||
),
|
||||
_buildDetailRow(
|
||||
l10n.startTime,
|
||||
'${startDate.toLocal().toString().split(' ')[0]} ${startDate.toLocal().toString().split(' ')[1].substring(0, 5)}'),
|
||||
'${startDate.toLocal().toString().split(' ')[0]} ${startDate.toLocal().toString().split(' ')[1].substring(0, 5)}',
|
||||
accentColor),
|
||||
_buildDetailRow(
|
||||
l10n.endTime,
|
||||
'${endDate.toLocal().toString().split(' ')[0]} ${endDate.toLocal().toString().split(' ')[1].substring(0, 5)}'),
|
||||
_buildDetailRow(
|
||||
l10n.duration,
|
||||
_formatDuration(
|
||||
flight.startTimestamp, flight.endTimestamp)),
|
||||
'${endDate.toLocal().toString().split(' ')[0]} ${endDate.toLocal().toString().split(' ')[1].substring(0, 5)}',
|
||||
accentColor),
|
||||
_buildDetailRow(l10n.duration,
|
||||
_formatDuration(flight.startTimestamp, flight.endTimestamp),
|
||||
accentColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
if (flight.locationLat != null && flight.locationLong != null) ...[
|
||||
Text(
|
||||
l10n.flightLocation,
|
||||
style: Theme.of(context).textTheme.headlineSmall!.copyWith(color: Colors.white),
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 15),
|
||||
Container(
|
||||
height: 300,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[300],
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: FlutterMap(
|
||||
options: MapOptions(
|
||||
initialCenter: LatLng(
|
||||
flight.locationLat!, flight.locationLong!),
|
||||
initialCenter: LatLng(flight.locationLat!, flight.locationLong!),
|
||||
initialZoom: 15.0,
|
||||
interactionOptions: const InteractionOptions(
|
||||
flags: InteractiveFlag.all & ~InteractiveFlag.rotate,
|
||||
@ -234,20 +229,18 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate:
|
||||
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
urlTemplate: "https://tile.openstreetmap.org/{z}/{x}/{y}.png",
|
||||
userAgentPackageName: 'com.example.rtime',
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: [
|
||||
Marker(
|
||||
point: LatLng(
|
||||
flight.locationLat!, flight.locationLong!),
|
||||
point: LatLng(flight.locationLat!, flight.locationLong!),
|
||||
width: 80,
|
||||
height: 80,
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.red,
|
||||
color: accentColor,
|
||||
size: 40.0,
|
||||
),
|
||||
),
|
||||
@ -262,10 +255,7 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
Center(
|
||||
child: Text(
|
||||
l10n.noLocationData,
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(color: Colors.white70),
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
),
|
||||
],
|
||||
@ -276,8 +266,7 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildDetailRow(String label, String value) {
|
||||
Widget _buildDetailRow(String label, String value, Color labelColor) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
@ -288,7 +277,7 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
child: Text(
|
||||
'$label:',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
color: labelColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
@ -298,7 +287,7 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
child: Text(
|
||||
value,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
color: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
@ -307,62 +296,58 @@ class _FlightDetailPageState extends State<FlightDetailPage> {
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Widget _buildImageAndDetailRow(
|
||||
BuildContext context, String label, String value, Image? displayImage, IconData defaultIcon) {
|
||||
BuildContext context, String label, String value, Image? displayImage, IconData defaultIcon, Color labelColor) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 8.0),
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
margin: const EdgeInsets.only(right: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Colors.blueGrey[600]!, width: 1),
|
||||
image: displayImage != null
|
||||
? DecorationImage(
|
||||
image: displayImage.image,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: displayImage == null
|
||||
? Icon(
|
||||
defaultIcon,
|
||||
size: 24,
|
||||
color: Colors.blueGrey[400],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'$label:',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
color: labelColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 2,
|
||||
child: Text(
|
||||
'$label:',
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(
|
||||
color: Theme.of(context).colorScheme.secondary,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Expanded(
|
||||
flex: 3,
|
||||
child: Text(
|
||||
value,
|
||||
style: Theme.of(context).textTheme.titleMedium!.copyWith(
|
||||
color: Theme.of(context).colorScheme.onSurface,
|
||||
color: Theme.of(context).textTheme.bodyLarge!.color,
|
||||
),
|
||||
),
|
||||
),
|
||||
Container(
|
||||
width: 40,
|
||||
height: 40,
|
||||
margin: const EdgeInsets.only(left: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).cardColor,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: Theme.of(context).dividerColor, width: 1),
|
||||
image: displayImage != null
|
||||
? DecorationImage(
|
||||
image: displayImage.image,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: displayImage == null
|
||||
? Icon(
|
||||
defaultIcon,
|
||||
size: 24,
|
||||
color: Theme.of(context).iconTheme.color,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@ -1,4 +1,3 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rtime/models/drone.dart';
|
||||
import 'package:rtime/models/battery.dart';
|
||||
@ -14,9 +13,10 @@ import 'package:rtime/pages/drone_detail_page.dart';
|
||||
import 'package:rtime/pages/battery_detail_page.dart';
|
||||
import 'package:rtime/db/db_helper.dart';
|
||||
import 'package:rtime/pages/flight_detail_page.dart';
|
||||
|
||||
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
import 'package:rtime/widgets/page_transition_animations.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
|
||||
class HomePage extends StatefulWidget {
|
||||
const HomePage({super.key});
|
||||
@ -47,6 +47,7 @@ class _HomePageState extends State<HomePage> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final customAccentColor = Provider.of<ColorProvider>(context).accentColor;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
@ -64,20 +65,27 @@ class _HomePageState extends State<HomePage> {
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SvgPicture.asset(
|
||||
'assets/images/rtimelogo.svg',
|
||||
height: 48,
|
||||
width: 48,
|
||||
colorFilter: ColorFilter.mode(customAccentColor, BlendMode.srcIn),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
l10n.appTitle,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 48,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.lightBlueAccent,
|
||||
color: customAccentColor,
|
||||
fontFamily: 'Montserrat',
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
IconButton(
|
||||
icon:
|
||||
const Icon(Icons.settings, size: 30, color: Colors.white70),
|
||||
onPressed: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideRightPageRoute(
|
||||
page: const SettingsPage(),
|
||||
@ -89,7 +97,6 @@ class _HomePageState extends State<HomePage> {
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
|
||||
|
||||
Text(
|
||||
l10n.yourDrones,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
@ -111,7 +118,6 @@ class _HomePageState extends State<HomePage> {
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideUpPageRoute(
|
||||
page: const NewDronePage(),
|
||||
@ -129,7 +135,7 @@ class _HomePageState extends State<HomePage> {
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.addDrone,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -146,7 +152,6 @@ class _HomePageState extends State<HomePage> {
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideUpPageRoute(
|
||||
page: const NewDronePage(),
|
||||
@ -164,7 +169,7 @@ class _HomePageState extends State<HomePage> {
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.addDrone,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -174,13 +179,11 @@ class _HomePageState extends State<HomePage> {
|
||||
return DroneCard(
|
||||
drone: drones[index],
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideRightPageRoute(
|
||||
page: DroneDetailPage(drone: drones[index]),
|
||||
),
|
||||
).then((result) {
|
||||
|
||||
if (result == true) {
|
||||
_loadData();
|
||||
}
|
||||
@ -196,7 +199,6 @@ class _HomePageState extends State<HomePage> {
|
||||
const Divider(
|
||||
height: 50, thickness: 2, indent: 0, endIndent: 0, color: Colors.white10),
|
||||
|
||||
|
||||
Text(
|
||||
l10n.yourBatteries,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
@ -218,7 +220,6 @@ class _HomePageState extends State<HomePage> {
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideUpPageRoute(
|
||||
page: const NewBatteryPage(),
|
||||
@ -236,12 +237,12 @@ class _HomePageState extends State<HomePage> {
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.addBattery,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
),
|
||||
);
|
||||
} else {
|
||||
final batteries = snapshot.data!;
|
||||
return ListView.builder(
|
||||
@ -253,7 +254,6 @@ class _HomePageState extends State<HomePage> {
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
child: InkWell(
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideUpPageRoute(
|
||||
page: const NewBatteryPage(),
|
||||
@ -271,7 +271,7 @@ class _HomePageState extends State<HomePage> {
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.addBattery,
|
||||
textAlign: TextAlign.center,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
style: Theme.of(context).textTheme.titleSmall!.copyWith(color: Colors.white)),
|
||||
],
|
||||
),
|
||||
),
|
||||
@ -281,13 +281,11 @@ class _HomePageState extends State<HomePage> {
|
||||
return BatteryCard(
|
||||
battery: batteries[index],
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideRightPageRoute(
|
||||
page: BatteryDetailPage(battery: batteries[index]),
|
||||
),
|
||||
).then((result) {
|
||||
|
||||
if (result == true) {
|
||||
_loadData();
|
||||
}
|
||||
@ -303,7 +301,6 @@ class _HomePageState extends State<HomePage> {
|
||||
const Divider(
|
||||
height: 50, thickness: 2, indent: 0, endIndent: 0, color: Colors.white10),
|
||||
|
||||
|
||||
Text(
|
||||
l10n.latestFlights,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
@ -325,7 +322,7 @@ class _HomePageState extends State<HomePage> {
|
||||
style: Theme.of(context)
|
||||
.textTheme
|
||||
.titleMedium!
|
||||
.copyWith(color: Colors.white70),
|
||||
.copyWith(color: Theme.of(context).colorScheme.onBackground),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
@ -341,16 +338,16 @@ class _HomePageState extends State<HomePage> {
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 20, vertical: 12),
|
||||
leading: const Icon(Icons.flight_takeoff,
|
||||
color: Colors.orangeAccent, size: 32),
|
||||
leading: Icon(Icons.flight_takeoff,
|
||||
color: customAccentColor, // Use the user's accent color here
|
||||
size: 32),
|
||||
title: Text(
|
||||
'${flight.name} - ${DateTime.fromMillisecondsSinceEpoch(flight.startTimestamp * 1000).toLocal().toString().split(' ')[0]}',
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
trailing: const Icon(Icons.arrow_forward_ios,
|
||||
size: 20, color: Colors.white54),
|
||||
trailing: Icon(Icons.arrow_forward_ios,
|
||||
size: 20, color: Theme.of(context).colorScheme.onSurface),
|
||||
onTap: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideRightPageRoute(
|
||||
page: FlightDetailPage(flight: flight),
|
||||
@ -370,7 +367,6 @@ class _HomePageState extends State<HomePage> {
|
||||
),
|
||||
floatingActionButton: FloatingActionButton.extended(
|
||||
onPressed: () {
|
||||
|
||||
Navigator.of(context).push(
|
||||
SlideUpPageRoute(
|
||||
page: const NewFlightPage(),
|
||||
@ -379,9 +375,9 @@ class _HomePageState extends State<HomePage> {
|
||||
},
|
||||
label: Text(
|
||||
l10n.newFlight,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
icon: const Icon(Icons.add_to_photos, size: 28),
|
||||
icon: const Icon(Icons.add_to_photos, size: 28, color: Colors.white),
|
||||
),
|
||||
floatingActionButtonLocation: FloatingActionButtonLocation.centerFloat,
|
||||
);
|
||||
|
||||
@ -28,9 +28,7 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
Future<void> _pickImage(ImageSource source) async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final uuid = await ImagesManager.instance.createImage(source);
|
||||
if (uuid != null) {
|
||||
final loadedImage = await ImagesManager.instance.loadImage(uuid);
|
||||
@ -38,23 +36,11 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
_selectedImageUuid = uuid;
|
||||
_displayImage = loadedImage;
|
||||
});
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _saveBattery() async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final newBattery = Battery(
|
||||
name: _nameController.text.trim(),
|
||||
type: _typeController.text.trim(),
|
||||
@ -65,18 +51,9 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
try {
|
||||
await DbHelper.instance.insertBattery(newBattery);
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -98,40 +75,42 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 150,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.lightBlueAccent, width: 2),
|
||||
|
||||
image: _displayImage != null
|
||||
? DecorationImage(
|
||||
image: _displayImage!.image,
|
||||
fit: BoxFit.cover,
|
||||
GestureDetector(
|
||||
onTap: () => _pickImage(ImageSource.camera),
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.blueGrey[600]!, width: 2),
|
||||
image: _displayImage != null
|
||||
? DecorationImage(
|
||||
image: _displayImage!.image,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: _displayImage == null
|
||||
? Icon(
|
||||
Icons.camera_alt,
|
||||
size: 60,
|
||||
color: Colors.blueGrey[400],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
||||
child: _displayImage == null
|
||||
? Icon(
|
||||
Icons.camera_alt,
|
||||
size: 60,
|
||||
color: Colors.blueGrey[400],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.batteryImage,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 10.0,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.camera),
|
||||
@ -142,7 +121,6 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.gallery),
|
||||
icon: const Icon(Icons.photo_library),
|
||||
@ -163,11 +141,11 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.batteryName,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.lightBlueAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
@ -184,11 +162,11 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.batteryType,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.lightBlueAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
@ -206,11 +184,11 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.batteryVoltage,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.lightBlueAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
@ -228,15 +206,14 @@ class _NewBatteryPageState extends State<NewBatteryPage> {
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveBattery,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.black,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 15)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
)),
|
||||
foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
child: Text(
|
||||
l10n.saveBattery,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
|
||||
@ -24,9 +24,7 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
Future<void> _pickImage(ImageSource source) async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final uuid = await ImagesManager.instance.createImage(source);
|
||||
if (uuid != null) {
|
||||
final loadedImage = await ImagesManager.instance.loadImage(uuid);
|
||||
@ -34,23 +32,11 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
_selectedImageUuid = uuid;
|
||||
_displayImage = loadedImage;
|
||||
});
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
} else {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void _saveDrone() async {
|
||||
if (_formKey.currentState!.validate()) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final newDrone = Drone(
|
||||
name: _nameController.text.trim(),
|
||||
imageUuid: _selectedImageUuid,
|
||||
@ -59,18 +45,9 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
try {
|
||||
await DbHelper.instance.insertDrone(newDrone);
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
Navigator.of(context).pop();
|
||||
}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
} catch (e) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,40 +69,42 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
key: _formKey,
|
||||
child: ListView(
|
||||
children: [
|
||||
|
||||
Center(
|
||||
child: Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 150,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.lightBlueAccent, width: 2),
|
||||
|
||||
image: _displayImage != null
|
||||
? DecorationImage(
|
||||
image: _displayImage!.image,
|
||||
fit: BoxFit.cover,
|
||||
GestureDetector(
|
||||
onTap: () => _pickImage(ImageSource.camera),
|
||||
child: Container(
|
||||
width: 150,
|
||||
height: 150,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.blueGrey[800],
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
border: Border.all(color: Colors.blueGrey[600]!, width: 2),
|
||||
image: _displayImage != null
|
||||
? DecorationImage(
|
||||
image: _displayImage!.image,
|
||||
fit: BoxFit.cover,
|
||||
)
|
||||
: null,
|
||||
),
|
||||
child: _displayImage == null
|
||||
? Icon(
|
||||
Icons.camera_alt,
|
||||
size: 60,
|
||||
color: Colors.blueGrey[400],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
|
||||
child: _displayImage == null
|
||||
? Icon(
|
||||
Icons.camera_alt,
|
||||
size: 60,
|
||||
color: Colors.blueGrey[400],
|
||||
)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.droneImage,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
const SizedBox(height: 15),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
Wrap(
|
||||
alignment: WrapAlignment.center,
|
||||
spacing: 10.0,
|
||||
runSpacing: 10.0,
|
||||
children: [
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.camera),
|
||||
@ -136,7 +115,6 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 10),
|
||||
ElevatedButton.icon(
|
||||
onPressed: () => _pickImage(ImageSource.gallery),
|
||||
icon: const Icon(Icons.photo_library),
|
||||
@ -157,11 +135,11 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
decoration: InputDecoration(
|
||||
labelText: l10n.droneName,
|
||||
labelStyle: const TextStyle(color: Colors.white70),
|
||||
enabledBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.white30),
|
||||
enabledBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
focusedBorder: const OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.lightBlueAccent),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderSide: BorderSide(color: Colors.blueGrey[600]!),
|
||||
),
|
||||
),
|
||||
style: const TextStyle(color: Colors.white),
|
||||
@ -176,15 +154,14 @@ class _NewDronePageState extends State<NewDronePage> {
|
||||
Center(
|
||||
child: ElevatedButton(
|
||||
onPressed: _saveDrone,
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Theme.of(context).colorScheme.secondary,
|
||||
foregroundColor: Colors.black,
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 15),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
),
|
||||
style: Theme.of(context).elevatedButtonTheme.style?.copyWith(
|
||||
padding: MaterialStateProperty.all(
|
||||
const EdgeInsets.symmetric(horizontal: 40, vertical: 15)),
|
||||
shape: MaterialStateProperty.all(RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
)),
|
||||
foregroundColor: MaterialStateProperty.all(Colors.white),
|
||||
),
|
||||
child: Text(
|
||||
l10n.saveDrone,
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
|
||||
|
||||
@ -8,6 +8,8 @@ import 'dart:async';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
|
||||
class NewFlightPage extends StatefulWidget {
|
||||
const NewFlightPage({super.key});
|
||||
@ -17,28 +19,23 @@ class NewFlightPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _NewFlightPageState extends State<NewFlightPage> {
|
||||
|
||||
late Future<List<Drone>> _dronesFuture;
|
||||
late Future<List<Battery>> _batteriesFuture;
|
||||
|
||||
|
||||
Drone? selectedDrone;
|
||||
Battery? selectedBattery;
|
||||
bool isFlightActive = false;
|
||||
Stopwatch stopwatch = Stopwatch();
|
||||
Timer? timer;
|
||||
String formattedTime = '00:00:00';
|
||||
String formattedTime = '00:00';
|
||||
int? flightStartTime;
|
||||
|
||||
|
||||
bool useGpsLocation = false;
|
||||
Position? currentPosition;
|
||||
bool isGettingLocation = false;
|
||||
|
||||
|
||||
final MapController _mapController = MapController();
|
||||
|
||||
|
||||
bool _initialMapCentered = false;
|
||||
|
||||
@override
|
||||
@ -47,7 +44,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
_loadAvailableItems();
|
||||
}
|
||||
|
||||
|
||||
void _loadAvailableItems() {
|
||||
_dronesFuture = DbHelper.instance.getDrones();
|
||||
_batteriesFuture = DbHelper.instance.getBatteries();
|
||||
@ -59,8 +55,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
|
||||
|
||||
Future<void> _getCurrentLocation() async {
|
||||
if (!mounted) return;
|
||||
|
||||
@ -75,9 +69,7 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
|
||||
serviceEnabled = await Geolocator.isLocationServiceEnabled();
|
||||
if (!serviceEnabled) {
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
useGpsLocation = false;
|
||||
@ -95,9 +87,7 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
if (permission == LocationPermission.denied) {
|
||||
permission = await Geolocator.requestPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
useGpsLocation = false;
|
||||
@ -113,9 +103,7 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
}
|
||||
|
||||
if (permission == LocationPermission.deniedForever) {
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
useGpsLocation = false;
|
||||
@ -138,7 +126,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
setState(() {
|
||||
currentPosition = position;
|
||||
|
||||
|
||||
if (_initialMapCentered && _mapController.camera.center != LatLng(position.latitude, position.longitude)) {
|
||||
_mapController.move(
|
||||
LatLng(currentPosition!.latitude, currentPosition!.longitude),
|
||||
@ -147,13 +134,9 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
}
|
||||
});
|
||||
}
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
useGpsLocation = false;
|
||||
@ -169,12 +152,10 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _startFlight() {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
if (selectedDrone == null || selectedBattery == null) {
|
||||
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(content: Text(l10n.selectDroneBattery)),
|
||||
);
|
||||
@ -187,10 +168,7 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
});
|
||||
|
||||
_getCurrentLocation().then((_) {
|
||||
|
||||
if (useGpsLocation && currentPosition == null) {
|
||||
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -201,7 +179,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void _proceedToStartFlight() {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
@ -217,26 +194,21 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
Future<void> _stopFlight() async {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
|
||||
|
||||
stopwatch.stop();
|
||||
timer?.cancel();
|
||||
|
||||
|
||||
final int flightEndTime = DateTime.now().millisecondsSinceEpoch ~/ 1000;
|
||||
|
||||
|
||||
final Duration flightDuration = stopwatch.elapsed;
|
||||
|
||||
|
||||
final Flight newFlight = Flight(
|
||||
name: '${selectedDrone!.name} - ${DateTime.fromMillisecondsSinceEpoch(flightStartTime! * 1000).toLocal().toString().split(' ')[0]}',
|
||||
name:
|
||||
'${selectedDrone!.name} - ${DateTime.fromMillisecondsSinceEpoch(flightStartTime! * 1000).toLocal().toString().split(' ')[0]}',
|
||||
startTimestamp: flightStartTime!,
|
||||
endTimestamp: flightEndTime,
|
||||
droneId: selectedDrone!.id!,
|
||||
@ -246,21 +218,15 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
);
|
||||
|
||||
try {
|
||||
|
||||
await DbHelper.instance.insertFlight(newFlight);
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
} catch (e) {
|
||||
if (mounted) {
|
||||
|
||||
}
|
||||
if (mounted) {}
|
||||
} finally {
|
||||
|
||||
setState(() {
|
||||
isFlightActive = false;
|
||||
stopwatch.reset();
|
||||
formattedTime = '00:00:00';
|
||||
formattedTime = '00:00';
|
||||
selectedDrone = null;
|
||||
selectedBattery = null;
|
||||
currentPosition = null;
|
||||
@ -275,18 +241,24 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
String _formatDuration(Duration duration) {
|
||||
String twoDigits(int n) => n.toString().padLeft(2, '0');
|
||||
String hours = twoDigits(duration.inHours);
|
||||
String minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
String seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return '$hours:$minutes:$seconds';
|
||||
if (duration.inHours > 0) {
|
||||
String hours = twoDigits(duration.inHours);
|
||||
String minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
String seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return '$hours:$minutes:$seconds';
|
||||
} else {
|
||||
String minutes = twoDigits(duration.inMinutes.remainder(60));
|
||||
String seconds = twoDigits(duration.inSeconds.remainder(60));
|
||||
return '$minutes:$seconds';
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final colorProvider = Provider.of<ColorProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
@ -300,7 +272,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
|
||||
Text(
|
||||
l10n.chooseDrone,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
@ -347,7 +318,7 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
items: drones.map<DropdownMenuItem<Drone>>((Drone drone) {
|
||||
return DropdownMenuItem<Drone>(
|
||||
value: drone,
|
||||
child: Text(drone.name),
|
||||
child: Text(drone.name, style: Theme.of(context).textTheme.titleMedium!.copyWith(color: Colors.white)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
@ -357,8 +328,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
Text(
|
||||
l10n.chooseBattery,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
@ -405,7 +374,7 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
items: batteries.map<DropdownMenuItem<Battery>>((Battery battery) {
|
||||
return DropdownMenuItem<Battery>(
|
||||
value: battery,
|
||||
child: Text('${battery.name} (${battery.voltage}V)'),
|
||||
child: Text('${battery.name} (${battery.voltage}V)', style: Theme.of(context).textTheme.titleMedium!.copyWith(color: Colors.white)),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
@ -415,8 +384,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
Card(
|
||||
margin: const EdgeInsets.only(bottom: 30),
|
||||
child: ListTile(
|
||||
@ -440,11 +407,9 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
},
|
||||
activeColor: Theme.of(context).colorScheme.primary,
|
||||
),
|
||||
leading: Icon(Icons.location_on, color: Theme.of(context).colorScheme.secondary),
|
||||
leading: Icon(Icons.location_on, color: colorProvider.accentColor),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
if (currentPosition != null && useGpsLocation)
|
||||
Container(
|
||||
margin: const EdgeInsets.only(bottom: 20),
|
||||
@ -461,7 +426,6 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
],
|
||||
),
|
||||
child: ClipRRect(
|
||||
|
||||
borderRadius: BorderRadius.circular(15),
|
||||
child: FlutterMap(
|
||||
mapController: _mapController,
|
||||
@ -496,9 +460,9 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
point: LatLng(currentPosition!.latitude, currentPosition!.longitude),
|
||||
width: 80,
|
||||
height: 80,
|
||||
child: const Icon(
|
||||
child: Icon(
|
||||
Icons.location_on,
|
||||
color: Colors.red,
|
||||
color: colorProvider.accentColor,
|
||||
size: 40,
|
||||
),
|
||||
),
|
||||
@ -508,30 +472,26 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
|
||||
Center(
|
||||
child: Text(
|
||||
formattedTime,
|
||||
style: const TextStyle(
|
||||
style: TextStyle(
|
||||
fontSize: 72,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.lightBlueAccent,
|
||||
color: colorProvider.accentColor,
|
||||
fontFamily: 'RobotoMono',
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 40),
|
||||
|
||||
|
||||
Center(
|
||||
child: isFlightActive
|
||||
? ElevatedButton.icon(
|
||||
onPressed: _stopFlight,
|
||||
icon: const Icon(Icons.stop, size: 30),
|
||||
icon: const Icon(Icons.stop, size: 30, color: Colors.white),
|
||||
label: Text(
|
||||
l10n.stopFlight,
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.redAccent,
|
||||
@ -546,21 +506,22 @@ class _NewFlightPageState extends State<NewFlightPage> {
|
||||
: (isGettingLocation
|
||||
? Column(
|
||||
children: [
|
||||
const CircularProgressIndicator(color: Colors.tealAccent),
|
||||
CircularProgressIndicator(color: colorProvider.accentColor),
|
||||
const SizedBox(height: 10),
|
||||
Text(l10n.obtainingLocation, style: Theme.of(context).textTheme.titleSmall),
|
||||
Text(l10n.obtainingLocation,
|
||||
style: Theme.of(context).textTheme.titleSmall),
|
||||
],
|
||||
)
|
||||
: ElevatedButton.icon(
|
||||
onPressed: _startFlight,
|
||||
icon: const Icon(Icons.flight_takeoff, size: 30),
|
||||
icon: const Icon(Icons.flight_takeoff, size: 30, color: Colors.white),
|
||||
label: Text(
|
||||
l10n.startFlight,
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold),
|
||||
style: const TextStyle(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.white),
|
||||
),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.tealAccent,
|
||||
foregroundColor: Colors.black,
|
||||
backgroundColor: colorProvider.accentColor,
|
||||
foregroundColor: Theme.of(context).colorScheme.onPrimary,
|
||||
padding: const EdgeInsets.symmetric(horizontal: 40, vertical: 20),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(30),
|
||||
|
||||
@ -3,6 +3,8 @@ import 'package:rtime/l10n/app_localizations.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rtime/providers/local_provider.dart';
|
||||
import 'package:rtime/providers/theme_provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
import 'package:flutter_colorpicker/flutter_colorpicker.dart';
|
||||
|
||||
class SettingsPage extends StatefulWidget {
|
||||
const SettingsPage({super.key});
|
||||
@ -12,11 +14,24 @@ class SettingsPage extends StatefulWidget {
|
||||
}
|
||||
|
||||
class _SettingsPageState extends State<SettingsPage> {
|
||||
late Color _pickerColor;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_pickerColor = Provider.of<ColorProvider>(context, listen: false).accentColor;
|
||||
}
|
||||
|
||||
void _onColorChanged(Color color) {
|
||||
setState(() => _pickerColor = color);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final l10n = AppLocalizations.of(context)!;
|
||||
final currentLocale = Localizations.localeOf(context);
|
||||
final themeProvider = Provider.of<ThemeProvider>(context);
|
||||
final colorProvider = Provider.of<ColorProvider>(context);
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Theme.of(context).colorScheme.background,
|
||||
@ -28,97 +43,160 @@ class _SettingsPageState extends State<SettingsPage> {
|
||||
),
|
||||
body: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.languageSetting,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(l10n.english, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: currentLocale.languageCode == 'en'
|
||||
? Icon(Icons.check_circle, color: Theme.of(context).colorScheme.secondary)
|
||||
: null,
|
||||
onTap: () {
|
||||
final localeProvider = Provider.of<LocaleProvider>(context, listen: false);
|
||||
localeProvider.setLocale(const Locale('en', ''));
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.french, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: currentLocale.languageCode == 'fr'
|
||||
? Icon(Icons.check_circle, color: Theme.of(context).colorScheme.secondary)
|
||||
: null,
|
||||
onTap: () {
|
||||
final localeProvider = Provider.of<LocaleProvider>(context, listen: false);
|
||||
localeProvider.setLocale(const Locale('fr', ''));
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
],
|
||||
child: SingleChildScrollView(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
l10n.languageSetting,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
l10n.themeSetting,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(l10n.themeSystem, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: themeProvider.themeMode == ThemeMode.system
|
||||
? Icon(Icons.check_circle, color: Theme.of(context).colorScheme.secondary)
|
||||
: null,
|
||||
onTap: () {
|
||||
themeProvider.setThemeMode(ThemeMode.system);
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.themeLight, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: themeProvider.themeMode == ThemeMode.light
|
||||
? Icon(Icons.check_circle, color: Theme.of(context).colorScheme.secondary)
|
||||
: null,
|
||||
onTap: () {
|
||||
themeProvider.setThemeMode(ThemeMode.light);
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.themeDark, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: themeProvider.themeMode == ThemeMode.dark
|
||||
? Icon(Icons.check_circle, color: Theme.of(context).colorScheme.secondary)
|
||||
: null,
|
||||
onTap: () {
|
||||
themeProvider.setThemeMode(ThemeMode.dark);
|
||||
|
||||
|
||||
|
||||
},
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text('English', style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: currentLocale.languageCode == 'en'
|
||||
? Icon(Icons.check_circle, color: colorProvider.accentColor)
|
||||
: null,
|
||||
onTap: () {
|
||||
final localeProvider = Provider.of<LocaleProvider>(context, listen: false);
|
||||
localeProvider.setLocale(const Locale('en', ''));
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text('Français', style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: currentLocale.languageCode == 'fr'
|
||||
? Icon(Icons.check_circle, color: colorProvider.accentColor)
|
||||
: null,
|
||||
onTap: () {
|
||||
final localeProvider = Provider.of<LocaleProvider>(context, listen: false);
|
||||
localeProvider.setLocale(const Locale('fr', ''));
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
l10n.themeSetting,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(l10n.themeSystem, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: themeProvider.themeMode == ThemeMode.system
|
||||
? Icon(Icons.check_circle, color: colorProvider.accentColor)
|
||||
: null,
|
||||
onTap: () {
|
||||
themeProvider.setThemeMode(ThemeMode.system);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.themeLight, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: themeProvider.themeMode == ThemeMode.light
|
||||
? Icon(Icons.check_circle, color: colorProvider.accentColor)
|
||||
: null,
|
||||
onTap: () {
|
||||
themeProvider.setThemeMode(ThemeMode.light);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.themeDark, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: themeProvider.themeMode == ThemeMode.dark
|
||||
? Icon(Icons.check_circle, color: colorProvider.accentColor)
|
||||
: null,
|
||||
onTap: () {
|
||||
themeProvider.setThemeMode(ThemeMode.dark);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 30),
|
||||
Text(
|
||||
l10n.customColorSetting,
|
||||
style: Theme.of(context).textTheme.headlineSmall,
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Card(
|
||||
color: Theme.of(context).cardTheme.color,
|
||||
child: Column(
|
||||
children: [
|
||||
ListTile(
|
||||
title: Text(
|
||||
l10n.selectButtonColor,
|
||||
style: Theme.of(context).textTheme.titleMedium,
|
||||
),
|
||||
trailing: Container(
|
||||
width: 30,
|
||||
height: 30,
|
||||
decoration: BoxDecoration(
|
||||
color: colorProvider.accentColor,
|
||||
shape: BoxShape.circle,
|
||||
border: Border.all(color: Theme.of(context).dividerColor),
|
||||
),
|
||||
),
|
||||
onTap: () {
|
||||
setState(() {
|
||||
_pickerColor = colorProvider.accentColor;
|
||||
});
|
||||
showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: Text(l10n.pickColor),
|
||||
content: SingleChildScrollView(
|
||||
child: ColorPicker(
|
||||
pickerColor: _pickerColor,
|
||||
onColorChanged: _onColorChanged,
|
||||
pickerAreaHeightPercent: 0.8,
|
||||
enableAlpha: false,
|
||||
displayThumbColor: true,
|
||||
paletteType: PaletteType.hsv,
|
||||
),
|
||||
),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
child: Text(l10n.cancel),
|
||||
onPressed: () {
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
TextButton(
|
||||
child: Text(l10n.select),
|
||||
onPressed: () {
|
||||
colorProvider.setAccentColor(_pickerColor);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
ListTile(
|
||||
title: Text(l10n.resetColor, style: Theme.of(context).textTheme.titleMedium),
|
||||
trailing: const Icon(Icons.restore),
|
||||
onTap: () {
|
||||
colorProvider.setAccentColor(Colors.yellow[700]!);
|
||||
setState(() {
|
||||
_pickerColor = Colors.yellow[700]!;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
31
lib/providers/color_provider.dart
Normal file
@ -0,0 +1,31 @@
|
||||
// providers/color_provider.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
class ColorProvider extends ChangeNotifier {
|
||||
Color _accentColor = Colors.blue.shade600;
|
||||
|
||||
Color get accentColor => _accentColor;
|
||||
|
||||
ColorProvider() {
|
||||
_loadAccentColor();
|
||||
}
|
||||
|
||||
void _loadAccentColor() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final int? colorValue = prefs.getInt('customAccentColor');
|
||||
if (colorValue != null) {
|
||||
_accentColor = Color(colorValue);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void setAccentColor(Color color) async {
|
||||
if (_accentColor != color) {
|
||||
_accentColor = color;
|
||||
notifyListeners();
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setInt('customAccentColor', color.value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
// providers/local_provider.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
@ -6,12 +7,10 @@ class LocaleProvider extends ChangeNotifier {
|
||||
|
||||
Locale? get locale => _locale;
|
||||
|
||||
|
||||
LocaleProvider() {
|
||||
_loadLocale();
|
||||
}
|
||||
|
||||
|
||||
void _loadLocale() async {
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
final languageCode = prefs.getString('languageCode');
|
||||
@ -25,14 +24,12 @@ class LocaleProvider extends ChangeNotifier {
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
|
||||
void setLocale(Locale newLocale) async {
|
||||
if (_locale == newLocale) return;
|
||||
|
||||
_locale = newLocale;
|
||||
notifyListeners();
|
||||
|
||||
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
await prefs.setString('languageCode', newLocale.languageCode);
|
||||
if (newLocale.countryCode != null) {
|
||||
@ -42,7 +39,6 @@ class LocaleProvider extends ChangeNotifier {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void clearLocale() async {
|
||||
_locale = null;
|
||||
notifyListeners();
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
// providers/theme_provider.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rtime/models/battery.dart';
|
||||
import 'package:rtime/images_manager.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
|
||||
class BatteryCard extends StatelessWidget {
|
||||
final Battery battery;
|
||||
@ -14,10 +17,13 @@ class BatteryCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final BorderRadius cardBorderRadius =
|
||||
Theme.of(context).cardTheme.shape is RoundedRectangleBorder
|
||||
? (Theme.of(context).cardTheme.shape as RoundedRectangleBorder).borderRadius as BorderRadius
|
||||
: BorderRadius.circular(12);
|
||||
final colorProvider = Provider.of<ColorProvider>(context);
|
||||
final accentColor = colorProvider.accentColor;
|
||||
|
||||
final ShapeBorder? cardShape = Theme.of(context).cardTheme.shape;
|
||||
final BorderRadius cardBorderRadius = (cardShape is RoundedRectangleBorder)
|
||||
? (cardShape).borderRadius as BorderRadius
|
||||
: BorderRadius.circular(12);
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
@ -34,23 +40,21 @@ class BatteryCard extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
SizedBox(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.blueGrey[700]
|
||||
: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: battery.imageUuid != null && battery.imageUuid!.isNotEmpty
|
||||
? FutureBuilder<Image?>(
|
||||
future: ImagesManager.instance.loadImage(battery.imageUuid!),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.tealAccent));
|
||||
return Center(child: CircularProgressIndicator(color: accentColor));
|
||||
} else if (snapshot.hasError || !snapshot.hasData || snapshot.data == null) {
|
||||
return Icon(Icons.broken_image, size: 50, color: Colors.blueGrey[400]);
|
||||
return Icon(
|
||||
Icons.battery_charging_full,
|
||||
size: 80,
|
||||
color: accentColor,
|
||||
);
|
||||
} else {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@ -59,7 +63,11 @@ class BatteryCard extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
)
|
||||
: Icon(Icons.battery_charging_full, size: 50, color: Theme.of(context).brightness == Brightness.dark ? Colors.tealAccent : Colors.green.shade400),
|
||||
: Icon(
|
||||
Icons.battery_charging_full,
|
||||
size: 80,
|
||||
color: accentColor,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:rtime/models/drone.dart';
|
||||
import 'package:rtime/images_manager.dart';
|
||||
import 'package:flutter_svg/flutter_svg.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:rtime/providers/color_provider.dart';
|
||||
|
||||
class DroneCard extends StatelessWidget {
|
||||
final Drone drone;
|
||||
@ -14,6 +17,8 @@ class DroneCard extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final colorProvider = Provider.of<ColorProvider>(context);
|
||||
final accentColor = colorProvider.accentColor;
|
||||
|
||||
final BorderRadius cardBorderRadius =
|
||||
Theme.of(context).cardTheme.shape is RoundedRectangleBorder
|
||||
@ -22,10 +27,8 @@ class DroneCard extends StatelessWidget {
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(right: 16),
|
||||
|
||||
child: InkWell(
|
||||
onTap: onTap,
|
||||
|
||||
borderRadius: cardBorderRadius,
|
||||
child: ClipRRect(
|
||||
borderRadius: cardBorderRadius,
|
||||
@ -36,23 +39,22 @@ class DroneCard extends StatelessWidget {
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
SizedBox(
|
||||
width: 80,
|
||||
height: 80,
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.of(context).brightness == Brightness.dark
|
||||
? Colors.blueGrey[700]
|
||||
: Colors.grey[200],
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: drone.imageUuid != null && drone.imageUuid!.isNotEmpty
|
||||
? FutureBuilder<Image?>(
|
||||
future: ImagesManager.instance.loadImage(drone.imageUuid!),
|
||||
builder: (context, snapshot) {
|
||||
if (snapshot.connectionState == ConnectionState.waiting) {
|
||||
return const Center(child: CircularProgressIndicator(color: Colors.lightBlueAccent));
|
||||
return Center(child: CircularProgressIndicator(color: accentColor));
|
||||
} else if (snapshot.hasError || !snapshot.hasData || snapshot.data == null) {
|
||||
return Icon(Icons.broken_image, size: 50, color: Colors.blueGrey[400]);
|
||||
return SvgPicture.asset(
|
||||
'assets/images/drone.svg',
|
||||
width: 50,
|
||||
height: 50,
|
||||
colorFilter: ColorFilter.mode(accentColor, BlendMode.srcIn),
|
||||
);
|
||||
} else {
|
||||
return ClipRRect(
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
@ -61,7 +63,12 @@ class DroneCard extends StatelessWidget {
|
||||
}
|
||||
},
|
||||
)
|
||||
: Icon(Icons.airplanemode_active, size: 50, color: Theme.of(context).brightness == Brightness.dark ? Colors.lightBlueAccent : Colors.blue.shade400),
|
||||
: SvgPicture.asset(
|
||||
'assets/images/drone.svg',
|
||||
width: 50,
|
||||
height: 50,
|
||||
colorFilter: ColorFilter.mode(accentColor, BlendMode.srcIn),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
|
||||
64
pubspec.lock
@ -129,6 +129,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.4"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
@ -270,6 +278,22 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_colorpicker:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_colorpicker
|
||||
sha256: "969de5f6f9e2a570ac660fb7b501551451ea2a1ab9e2097e89475f60e07816ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
flutter_launcher_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_launcher_icons
|
||||
sha256: "526faf84284b86a4cb36d20a5e45147747b7563d921373d4ee0559c54fcdbcea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.13.1"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
@ -299,6 +323,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.28"
|
||||
flutter_svg:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_svg
|
||||
sha256: cd57f7969b4679317c17af6fd16ee233c1e60a82ed209d8a475c54fd6fd6f845
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
@ -669,6 +701,14 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_parsing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_parsing
|
||||
sha256: "883402936929eac138ee0a45da5b0f2c80f89913e6dc3bf77eb65b84b409c6ca"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
path_provider:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
@ -1058,6 +1098,30 @@ packages:
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.1"
|
||||
vector_graphics:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics
|
||||
sha256: a4f059dc26fc8295b5921376600a194c4ec7d55e72f2fe4c7d2831e103d461e6
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.19"
|
||||
vector_graphics_codec:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics_codec
|
||||
sha256: "99fd9fbd34d9f9a32efd7b6a6aae14125d8237b10403b422a6a6dfeac2806146"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.13"
|
||||
vector_graphics_compiler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_graphics_compiler
|
||||
sha256: "557a315b7d2a6dbb0aaaff84d857967ce6bdc96a63dc6ee2a57ce5a6ee5d3331"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.17"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
20
pubspec.yaml
@ -30,7 +30,8 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
|
||||
flutter_colorpicker: ^1.0.0
|
||||
flutter_launcher_icons: ^0.13.1
|
||||
# The following adds the Cupertino Icons font to your application.
|
||||
# Use with the CupertinoIcons class for iOS style icons.
|
||||
cupertino_icons: ^1.0.8
|
||||
@ -46,13 +47,14 @@ dependencies:
|
||||
json_serializable: ^6.9.5
|
||||
image_cropper: ^9.1.0
|
||||
intl: ^0.20.2
|
||||
provider: ^6.1.2
|
||||
provider: ^6.1.2
|
||||
geolocator: ^12.0.0
|
||||
flutter_map: ^8.0.0
|
||||
latlong2: ^0.9.1
|
||||
flutter_localizations:
|
||||
sdk: flutter
|
||||
shared_preferences: ^2.2.0
|
||||
flutter_svg: ^2.0.10+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
@ -70,7 +72,6 @@ dev_dependencies:
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# The following line ensures that the Material Icons font is
|
||||
# included with your application, so that you can use the icons in
|
||||
# the material Icons class.
|
||||
@ -78,9 +79,8 @@ flutter:
|
||||
generate: true
|
||||
|
||||
# To add assets to your application, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
assets:
|
||||
- assets/images/ # This is the correct and only place to declare your assets folder.
|
||||
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
@ -107,3 +107,11 @@ flutter:
|
||||
#
|
||||
# For details regarding fonts from package dependencies,
|
||||
# see https://flutter.dev/to/font-from-package
|
||||
|
||||
# This section needs to be at the root level, not under "flutter:"
|
||||
flutter_launcher_icons:
|
||||
android: "launcher_icon"
|
||||
ios: true
|
||||
image_path: "assets/images/rtimelogo.png"
|
||||
min_sdk_android: 21
|
||||
remove_alpha_ios: true
|
||||
|
||||