Compare commits

...

11 Commits

25 changed files with 1928 additions and 119 deletions

View File

@ -6,7 +6,7 @@ plugins {
}
android {
namespace = "com.example.rtime"
namespace = "fr.chaboissier.rtime"
compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion
@ -21,7 +21,7 @@ android {
defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.example.rtime"
applicationId = "fr.chaboissier.rtime"
// You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion

View File

@ -3,6 +3,10 @@
android:label="rtime"
android:name="${applicationName}"
android:icon="@mipmap/ic_launcher">
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Ucrop.CropTheme"/>
<activity
android:name=".MainActivity"
android:exported="true"
@ -42,4 +46,7 @@
<data android:mimeType="text/plain"/>
</intent>
</queries>
<!-- GPS Location permission to be able to locate flights. -->
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
</manifest>

View File

@ -1,4 +1,4 @@
package com.example.rtime
package fr.chaboissier.rtime
import io.flutter.embedding.android.FlutterActivity

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Ucrop.CropTheme" parent="Theme.AppCompat.Light.NoActionBar">
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
</style>
</resources>

View File

@ -15,4 +15,5 @@
<style name="NormalTheme" parent="@android:style/Theme.Light.NoTitleBar">
<item name="android:windowBackground">?android:colorBackground</item>
</style>
<style name="Ucrop.CropTheme" parent="Theme.AppCompat.Light.NoActionBar"/> <!--add this line-->
</resources>

File diff suppressed because one or more lines are too long

View File

@ -1,3 +1,5 @@
org.gradle.jvmargs=-Xmx8G -XX:MaxMetaspaceSize=4G -XX:ReservedCodeCacheSize=512m -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true
android.enableJetifier=true
android.useAndroidX=true
android.enableJetifier=true

View File

@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-all.zip

7
build.yaml Normal file
View File

@ -0,0 +1,7 @@
targets:
$default:
builders:
source_gen|combining_builder:
options:
build_extensions:
'^lib/{{}}.dart': 'lib/generated/{{}}.g.dart'

View File

@ -45,5 +45,7 @@
<true/>
<key>UIApplicationSupportsIndirectInputEvents</key>
<true/>
<key>NSLocationWhenInUseUsageDescription</key>
<string>To locate the flights.</string>
</dict>
</plist>

39
lib/config.dart Normal file
View File

@ -0,0 +1,39 @@
// This file holds a class to represent the configuration/settings of the app
import 'dart:io';
import 'package:json_annotation/json_annotation.dart';
import 'dart:convert';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
part 'generated/config.g.dart';
@JsonSerializable()
class RTimeConfig {
final String language;
RTimeConfig({required this.language});
factory RTimeConfig.fromJson(Map<String, dynamic> json) =>
_$RTimeConfigFromJson(json);
Map<String, dynamic> toJson() => _$RTimeConfigToJson(this);
static Future<RTimeConfig?> readFromConfig() async {
final directory = await getApplicationDocumentsDirectory();
final configName = "rtime_config.json";
final configPath = path.join(directory.path, configName);
final string = jsonDecode(await File(configPath).readAsString());
return RTimeConfig.fromJson(string);
}
void writeConfig() async {
final directory = await getApplicationDocumentsDirectory();
final configName = "rtime_config.json";
final configPath = path.join(directory.path, configName);
await File(configPath).writeAsString(jsonEncode(toJson()));
}
}

197
lib/db/db_helper.dart Normal file
View File

@ -0,0 +1,197 @@
// Singleton to access database
import 'dart:developer';
import 'dart:io';
import 'package:path/path.dart';
import 'package:logging/logging.dart';
import 'package:rtime/models/battery.dart';
import 'package:rtime/models/flight.dart';
import '../models/drone.dart';
import 'package:path_provider/path_provider.dart';
import 'package:sqflite/sqflite.dart';
class DbHelper {
static final DbHelper instance = DbHelper._internal();
static final log = Logger("DbHelper");
static Database? _database;
DbHelper._internal();
Future<Database> get database async {
if (_database != null) return _database!;
await _initDb();
return _database!;
}
Future _initDb() async {
final directory = await getApplicationDocumentsDirectory();
final db_name = "rtime.db";
final path = join(directory.path, db_name);
log.info("Opeing database file at $path");
_database = await openDatabase(path, version: 1, onCreate: _onCreate);
log.info("Database opened successfully !");
log.info("${(await getDrones()).length} drones in DB.");
}
Future _onCreate(Database db, int version) async {
log.info("Database does not exist, creating a new one.");
await db.execute('''
CREATE TABLE drones(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
image_uuid TEXT
)
''');
await db.execute('''
CREATE TABLE batteries(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
type TEXT NOT NULL,
volatege REAL NOT NULL,
image_uuid TEXT
)
''');
await db.execute('''
CREATE TABLE flights(
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL,
start_timestamp INTEGER NOT NULL,
end_timestamp INTEGER NOT NULL,
drone_id INTEGER NOT NULL,
battery_id INTEGER NOT NULL,
location_lat REAL,
location_long REAL,
FOREIGN KEY(drone_id) REFERENCES drones(id),
FOREIGN KEY(battery_id) REFERENCES batteries(id)
)
''');
}
// Drone related helpers
Future<Drone> insertDrone(Drone drone) async {
final db = await database;
drone.id = await db.insert("drones", drone.toMap());
return drone;
}
Future<List<Drone>> getDrones() async {
final db = await database;
final maps = await db.query("drones");
return maps.map((e) => Drone.fromMap(e)).toList();
}
Future<int> deleteDrone(int droneId) async {
// TODO: Delete image
final db = await database;
return await db.delete("drones", where: "id = ?", whereArgs: [droneId]);
}
// Battery related helpers
Future<Battery> insertBattery(Battery battery) async {
final db = await database;
battery.id = await db.insert("batteries", battery.toMap());
return battery;
}
Future<List<Battery>> getBatteries() async {
final db = await database;
final maps = await db.query("batteries");
return maps.map((e) => Battery.fromMap(e)).toList();
}
Future<int> deleteBattery(int batteryId) async {
// TODO: Delete image
final db = await database;
return await db.delete(
"batteries",
where: "id = ?",
whereArgs: [batteryId],
);
}
Future<List<Battery>> getBatteriesByType(String type) async {
final db = await database;
final maps = await db.query(
"batteries",
where: "type = ?",
whereArgs: [type],
);
return maps.map((e) => Battery.fromMap(e)).toList();
}
// Flight related helpers
Future<Flight> insertFlight(Flight flight) async {
final db = await database;
flight.id = await db.insert("flights", flight.toMap());
return flight;
}
Future<List<Flight>> getFlights() async {
final db = await database;
final maps = await db.query("flights");
return maps.map((e) => Flight.fromMap(e)).toList();
}
Future<int> deleteFlight(int flightId) async {
final db = await database;
return await db.delete("flights", where: "id = ?", whereArgs: [flightId]);
}
// Complex requests
Future<List<Flight>> getDroneFlights(int droneId) async {
final db = await database;
final maps = await db.rawQuery('''
SELECT * FROM flights AS F
JOIN drones AS D ON F.drone_id = D.id
''');
return maps.map((e) => Flight.fromMap(e)).toList();
}
Future<List<Flight>> getBatteryFlights(int batteryId) async {
final db = await database;
final maps = await db.rawQuery('''
SELECT * FROM flights AS F
JOIN batteries AS B ON F.battery_id = B.id
''');
return maps.map((e) => Flight.fromMap(e)).toList();
}
Future<List<Flight>> getFlightsBetween(
int startTimestamp,
int endTimestamp,
) async {
final db = await database;
final maps = await db.rawQuery('''
SELECT * FROM flights AS F
WHERE F.start_timestamp >= $startTimestamp AND F.end_timestamp <= $endTimestamp
''');
return maps.map((e) => Flight.fromMap(e)).toList();
}
Future<int?> getTotalFlightTime() async {
final db = await database;
final ft = await db.rawQuery('''
SELECT SUM(F.end_timestamp - F.startTimestamp) FROM flights AS F
''');
final mapping = ft.firstOrNull;
if (mapping == null) {
return null;
}
final val = mapping["SUM(F.end_timestamp - F.startTimestamp)"];
if (val is! int) {
return null;
}
return val;
}
Future closeDb() async {
final db = await database;
log.info("Closing database.");
db.close();
_database = null;
}
}

View File

@ -0,0 +1,13 @@
// GENERATED CODE - DO NOT MODIFY BY HAND
part of '../config.dart';
// **************************************************************************
// JsonSerializableGenerator
// **************************************************************************
RTimeConfig _$RTimeConfigFromJson(Map<String, dynamic> json) =>
RTimeConfig(language: json['language'] as String);
Map<String, dynamic> _$RTimeConfigToJson(RTimeConfig instance) =>
<String, dynamic>{'language': instance.language};

103
lib/images_manager.dart Normal file
View File

@ -0,0 +1,103 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:logging/logging.dart';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart' as path;
import 'package:uuid/uuid.dart';
import 'package:image/image.dart' as img;
import 'package:image_cropper/image_cropper.dart' as img_cropper;
import 'package:uuid/v5.dart';
class ImagesManager {
static final ImagesManager instance = ImagesManager._internal();
ImagesManager._internal();
static final Logger log = Logger("ImagesManager");
static Uri? _imagesDirectory;
Future<Uri> get imageDirectory async {
if (_imagesDirectory != null) return _imagesDirectory!;
await _initImagesDirectory();
return _imagesDirectory!;
}
Future _initImagesDirectory() async {
final directoryLoc = await getApplicationDocumentsDirectory();
final directoryName = "images";
final directoryPath = path.join(directoryLoc.path, directoryName);
final directoryUri = Uri.directory(directoryPath);
final directory = Directory.fromUri(directoryUri);
if (!await directory.exists()) {
log.info("Image directory does not yet extists. Creating it.");
}
directory.create(recursive: false);
log.info("Image directory set up at '$directory'");
_imagesDirectory = directoryUri;
}
Future<String?> createImage(ImageSource source) async {
// Get image from camera or not
final XFile? ximage = await ImagePicker().pickImage(source: source);
if (ximage == null) return null;
// Crop image
final uuid = Uuid().v6();
final imageDir = await imageDirectory;
final finalPath = path.join(
imageDir.path,
uuid + path.extension(ximage.name),
);
final tempPath = path.join(
imageDir.path,
"${uuid}_tocrop${path.extension(ximage.name)}",
);
await ximage.saveTo(tempPath);
img_cropper.CroppedFile? cropped = await img_cropper.ImageCropper()
.cropImage(
sourcePath: tempPath,
aspectRatio: img_cropper.CropAspectRatio(ratioX: 1.0, ratioY: 1.0),
uiSettings: [
img_cropper.AndroidUiSettings(
toolbarTitle: "Crop image",
toolbarColor: Colors.black,
toolbarWidgetColor: Colors.white,
),
img_cropper.IOSUiSettings(title: "Crop image"),
],
);
if (cropped == null) return null;
await File(finalPath).writeAsBytes(await cropped.readAsBytes());
await File(tempPath).delete();
return uuid;
}
Future<Image?> loadImage(String imageUuid) async {
final imageDir = await imageDirectory;
if (!Uuid.isValidUUID(fromString: imageUuid)) {
log.warning(
"Tried to load an image with an invalid UUID : '$imageUuid'.",
);
return null;
}
final imagePath = path.join(imageDir.path, "$imageUuid.jpg");
final file = File(imagePath);
if (!await file.exists()) {
log.warning("Tried to load an image that does not extist: '$imagePath'.");
return null;
}
return Image.file(file);
}
}

View File

@ -1,122 +1,38 @@
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:image_picker/image_picker.dart';
import 'package:logging/logging.dart';
import 'package:rtime/db/db_helper.dart';
import 'package:rtime/images_manager.dart';
import 'package:rtime/models/drone.dart';
import 'package:rtime/pages/home_page.dart';
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import 'package:image_picker/image_picker.dart';
import 'package:sqlite3_flutter_libs/sqlite3_flutter_libs.dart';
void main() {
runApp(const MyApp());
if (Platform.isWindows || Platform.isLinux) {
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
}
Logger.root.level = Level.ALL;
Logger.root.onRecord.listen(
(record) =>
print('${record.level.name}: ${record.time}: ${record.message}'),
);
runApp(const RTimeApp());
//DbHelper.instance.closeDb();
}
class MyApp extends StatelessWidget {
const MyApp({super.key});
class RTimeApp extends StatelessWidget {
const RTimeApp({super.key});
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
// This is the theme of your application.
//
// TRY THIS: Try running your application with "flutter run". You'll see
// the application has a purple toolbar. Then, without quitting the app,
// try changing the seedColor in the colorScheme below to Colors.green
// and then invoke "hot reload" (save your changes or press the "hot
// reload" button in a Flutter-supported IDE, or press "r" if you used
// the command line to start the app).
//
// Notice that the counter didn't reset back to zero; the application
// state is not lost during the reload. To reset the state, use hot
// restart instead.
//
// This works for code too, not just values: Most code changes can be
// tested with just a hot reload.
colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),
),
home: const MyHomePage(title: 'Flutter Demo Home Page'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({super.key, required this.title});
// This widget is the home page of your application. It is stateful, meaning
// that it has a State object (defined below) that contains fields that affect
// how it looks.
// This class is the configuration for the state. It holds the values (in this
// case the title) provided by the parent (in this case the App widget) and
// used by the build method of the State. Fields in a Widget subclass are
// always marked "final".
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
// This call to setState tells the Flutter framework that something has
// changed in this State, which causes it to rerun the build method below
// so that the display can reflect the updated values. If we changed
// _counter without calling setState(), then the build method would not be
// called again, and so nothing would appear to happen.
_counter++;
});
}
@override
Widget build(BuildContext context) {
// This method is rerun every time setState is called, for instance as done
// by the _incrementCounter method above.
//
// The Flutter framework has been optimized to make rerunning build methods
// fast, so that you can just rebuild anything that needs updating rather
// than having to individually change instances of widgets.
return Scaffold(
appBar: AppBar(
// TRY THIS: Try changing the color here to a specific color (to
// Colors.amber, perhaps?) and trigger a hot reload to see the AppBar
// change color while the other colors stay the same.
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(widget.title),
),
body: Center(
// Center is a layout widget. It takes a single child and positions it
// in the middle of the parent.
child: Column(
// Column is also a layout widget. It takes a list of children and
// arranges them vertically. By default, it sizes itself to fit its
// children horizontally, and tries to be as tall as its parent.
//
// Column has various properties to control how it sizes itself and
// how it positions its children. Here we use mainAxisAlignment to
// center the children vertically; the main axis here is the vertical
// axis because Columns are vertical (the cross axis would be
// horizontal).
//
// TRY THIS: Invoke "debug painting" (choose the "Toggle Debug Paint"
// action in the IDE, or press "p" in the console), to see the
// wireframe for each widget.
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Text('You have pushed the button this many times:'),
Text(
'$_counter',
style: Theme.of(context).textTheme.headlineMedium,
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
return Text("RtimeAPP");
}
}

33
lib/models/battery.dart Normal file
View File

@ -0,0 +1,33 @@
class Battery {
int? id;
String name;
String type;
double voltage;
String? imageUuid;
Battery({
this.id,
required this.name,
required this.type,
required this.voltage,
this.imageUuid,
});
factory Battery.fromMap(Map<String, dynamic> map) => Battery(
id: map["id"],
name: map["name"],
type: map["type"],
voltage: map["voltage"],
imageUuid: map["image_uuid"],
);
Map<String, dynamic> toMap() {
return {
"id": id,
"name": name,
"type": type,
"voltage": voltage,
"image_uuid": imageUuid,
};
}
}

14
lib/models/drone.dart Normal file
View File

@ -0,0 +1,14 @@
class Drone {
int? id;
String name;
String? imageUuid;
Drone({this.id, required this.name, this.imageUuid});
factory Drone.fromMap(Map<String, dynamic> map) =>
Drone(id: map["id"], name: map["name"], imageUuid: map["image_uuid"]);
Map<String, dynamic> toMap() {
return {"id": id, "name": name, "image_uuid": imageUuid};
}
}

51
lib/models/flight.dart Normal file
View File

@ -0,0 +1,51 @@
class Flight {
int? id;
String name;
// Timestamp in seconds since epoch
int startTimestamp;
int endTimestamp;
// Foreign keys
int droneId;
int batteryId;
// Location
double? locationLat;
double? locationLong;
Flight({
this.id,
required this.name,
required this.startTimestamp,
required this.endTimestamp,
required this.droneId,
required this.batteryId,
this.locationLat,
this.locationLong,
});
factory Flight.fromMap(Map<String, dynamic> map) => Flight(
id: map["id"],
name: map["name"],
startTimestamp: map["start_timestamp"],
endTimestamp: map["end_timestamp"],
droneId: map["drone_id"],
batteryId: map["batteryId"],
locationLat: map["location_lat"],
locationLong: map["location_long"],
);
Map<String, dynamic> toMap() {
return {
"id": id,
"name": name,
"start_timestamp": startTimestamp,
"end_timestamp": endTimestamp,
"drone_id": droneId,
"battery_id": batteryId,
"location_lat": locationLat,
"location_long": locationLong,
};
}
}

View File

@ -6,6 +6,14 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) sqlite3_flutter_libs_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "Sqlite3FlutterLibsPlugin");
sqlite3_flutter_libs_plugin_register_with_registrar(sqlite3_flutter_libs_registrar);
}

View File

@ -3,6 +3,8 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
sqlite3_flutter_libs
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -5,6 +5,14 @@
import FlutterMacOS
import Foundation
import file_selector_macos
import path_provider_foundation
import sqflite_darwin
import sqlite3_flutter_libs
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin"))
Sqlite3FlutterLibsPlugin.register(with: registry.registrar(forPlugin: "Sqlite3FlutterLibsPlugin"))
}

View File

@ -1,6 +1,38 @@
# Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile
packages:
_fe_analyzer_shared:
dependency: transitive
description:
name: _fe_analyzer_shared
sha256: da0d9209ca76bde579f2da330aeb9df62b6319c834fa7baae052021b0462401f
url: "https://pub.dev"
source: hosted
version: "85.0.0"
analyzer:
dependency: transitive
description:
name: analyzer
sha256: "01949bf52ad33f0e0f74f881fbaac4f348c556531951d92c8d16f1262aa19ff8"
url: "https://pub.dev"
source: hosted
version: "7.5.4"
archive:
dependency: transitive
description:
name: archive
sha256: "2fde1607386ab523f7a36bb3e7edb43bd58e6edaf2ffb29d8a6d578b297fdbbd"
url: "https://pub.dev"
source: hosted
version: "4.0.7"
args:
dependency: transitive
description:
name: args
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
url: "https://pub.dev"
source: hosted
version: "2.7.0"
async:
dependency: transitive
description:
@ -17,6 +49,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
build:
dependency: transitive
description:
name: build
sha256: "51dc711996cbf609b90cbe5b335bbce83143875a9d58e4b5c6d3c4f684d3dda7"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_config:
dependency: transitive
description:
name: build_config
sha256: "4ae2de3e1e67ea270081eaee972e1bd8f027d459f249e0f1186730784c2e7e33"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "8e928697a82be082206edb0b9c99c5a4ad6bc31c9e9b8b2f291ae65cd4a25daa"
url: "https://pub.dev"
source: hosted
version: "4.0.4"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: ee4257b3f20c0c90e72ed2b57ad637f694ccba48839a821e87db762548c22a62
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_runner:
dependency: transitive
description:
name: build_runner
sha256: "382a4d649addbfb7ba71a3631df0ec6a45d5ab9b098638144faf27f02778eb53"
url: "https://pub.dev"
source: hosted
version: "2.5.4"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: "85fbbb1036d576d966332a3f5ce83f2ce66a40bea1a94ad2d5fc29a19a0d3792"
url: "https://pub.dev"
source: hosted
version: "9.1.2"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: "082001b5c3dc495d4a42f1d5789990505df20d8547d42507c29050af6933ee27"
url: "https://pub.dev"
source: hosted
version: "8.10.1"
characters:
dependency: transitive
description:
@ -25,6 +121,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: "959525d3162f249993882720d52b7e0c833978df229be20702b33d48d91de70f"
url: "https://pub.dev"
source: hosted
version: "2.0.4"
clock:
dependency: transitive
description:
@ -33,6 +137,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.1.2"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
url: "https://pub.dev"
source: hosted
version: "4.10.1"
collection:
dependency: transitive
description:
@ -41,6 +153,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.19.1"
convert:
dependency: transitive
description:
name: convert
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
url: "https://pub.dev"
source: hosted
version: "3.1.2"
cross_file:
dependency: transitive
description:
name: cross_file
sha256: "7caf6a750a0c04effbb52a676dce9a4a592e10ad35c34d6d2d0e4811160d5670"
url: "https://pub.dev"
source: hosted
version: "0.3.4+2"
crypto:
dependency: transitive
description:
name: crypto
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
cupertino_icons:
dependency: "direct main"
description:
@ -49,6 +185,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.8"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "5b236382b47ee411741447c1f1e111459c941ea1b3f2b540dde54c210a3662af"
url: "https://pub.dev"
source: hosted
version: "3.1.0"
fake_async:
dependency: transitive
description:
@ -57,6 +201,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.3"
ffi:
dependency: transitive
description:
name: ffi
sha256: "289279317b4b16eb2bb7e271abccd4bf84ec9bdcbe999e278a94b804f5630418"
url: "https://pub.dev"
source: hosted
version: "2.1.4"
file:
dependency: transitive
description:
name: file
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
url: "https://pub.dev"
source: hosted
version: "7.0.1"
file_selector_linux:
dependency: transitive
description:
name: file_selector_linux
sha256: "54cbbd957e1156d29548c7d9b9ec0c0ebb6de0a90452198683a7d23aed617a33"
url: "https://pub.dev"
source: hosted
version: "0.9.3+2"
file_selector_macos:
dependency: transitive
description:
name: file_selector_macos
sha256: "8c9250b2bd2d8d4268e39c82543bacbaca0fda7d29e0728c3c4bbb7c820fd711"
url: "https://pub.dev"
source: hosted
version: "0.9.4+3"
file_selector_platform_interface:
dependency: transitive
description:
name: file_selector_platform_interface
sha256: a3994c26f10378a039faa11de174d7b78eb8f79e4dd0af2a451410c1a5c3f66b
url: "https://pub.dev"
source: hosted
version: "2.6.2"
file_selector_windows:
dependency: transitive
description:
name: file_selector_windows
sha256: "320fcfb6f33caa90f0b58380489fc5ac05d99ee94b61aa96ec2bff0ba81d3c2b"
url: "https://pub.dev"
source: hosted
version: "0.9.3+4"
fixnum:
dependency: transitive
description:
name: fixnum
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
url: "https://pub.dev"
source: hosted
version: "1.1.1"
flutter:
dependency: "direct main"
description: flutter
@ -70,11 +270,200 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.0.0"
flutter_plugin_android_lifecycle:
dependency: transitive
description:
name: flutter_plugin_android_lifecycle
sha256: f948e346c12f8d5480d2825e03de228d0eb8c3a737e4cdaa122267b89c022b5e
url: "https://pub.dev"
source: hosted
version: "2.0.28"
flutter_test:
dependency: "direct dev"
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
frontend_server_client:
dependency: transitive
description:
name: frontend_server_client
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
url: "https://pub.dev"
source: hosted
version: "4.0.0"
glob:
dependency: transitive
description:
name: glob
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
url: "https://pub.dev"
source: hosted
version: "2.1.3"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
http:
dependency: transitive
description:
name: http
sha256: "2c11f3f94c687ee9bad77c171151672986360b2b001d109814ee7140b2cf261b"
url: "https://pub.dev"
source: hosted
version: "1.4.0"
http_multi_server:
dependency: transitive
description:
name: http_multi_server
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
url: "https://pub.dev"
source: hosted
version: "3.2.2"
http_parser:
dependency: transitive
description:
name: http_parser
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
url: "https://pub.dev"
source: hosted
version: "4.1.2"
image:
dependency: "direct main"
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
url: "https://pub.dev"
source: hosted
version: "4.5.4"
image_cropper:
dependency: "direct main"
description:
name: image_cropper
sha256: "4e9c96c029eb5a23798da1b6af39787f964da6ffc78fd8447c140542a9f7c6fc"
url: "https://pub.dev"
source: hosted
version: "9.1.0"
image_cropper_for_web:
dependency: transitive
description:
name: image_cropper_for_web
sha256: fd81ebe36f636576094377aab32673c4e5d1609b32dec16fad98d2b71f1250a9
url: "https://pub.dev"
source: hosted
version: "6.1.0"
image_cropper_platform_interface:
dependency: transitive
description:
name: image_cropper_platform_interface
sha256: "6ca6b81769abff9a4dcc3bbd3d75f5dfa9de6b870ae9613c8cd237333a4283af"
url: "https://pub.dev"
source: hosted
version: "7.1.0"
image_picker:
dependency: "direct main"
description:
name: image_picker
sha256: "021834d9c0c3de46bf0fe40341fa07168407f694d9b2bb18d532dc1261867f7a"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
image_picker_android:
dependency: transitive
description:
name: image_picker_android
sha256: "317a5d961cec5b34e777b9252393f2afbd23084aa6e60fcf601dcf6341b9ebeb"
url: "https://pub.dev"
source: hosted
version: "0.8.12+23"
image_picker_for_web:
dependency: transitive
description:
name: image_picker_for_web
sha256: "717eb042ab08c40767684327be06a5d8dbb341fe791d514e4b92c7bbe1b7bb83"
url: "https://pub.dev"
source: hosted
version: "3.0.6"
image_picker_ios:
dependency: transitive
description:
name: image_picker_ios
sha256: "05da758e67bc7839e886b3959848aa6b44ff123ab4b28f67891008afe8ef9100"
url: "https://pub.dev"
source: hosted
version: "0.8.12+2"
image_picker_linux:
dependency: transitive
description:
name: image_picker_linux
sha256: "34a65f6740df08bbbeb0a1abd8e6d32107941fd4868f67a507b25601651022c9"
url: "https://pub.dev"
source: hosted
version: "0.2.1+2"
image_picker_macos:
dependency: transitive
description:
name: image_picker_macos
sha256: "1b90ebbd9dcf98fb6c1d01427e49a55bd96b5d67b8c67cf955d60a5de74207c1"
url: "https://pub.dev"
source: hosted
version: "0.2.1+2"
image_picker_platform_interface:
dependency: transitive
description:
name: image_picker_platform_interface
sha256: "886d57f0be73c4b140004e78b9f28a8914a09e50c2d816bdd0520051a71236a0"
url: "https://pub.dev"
source: hosted
version: "2.10.1"
image_picker_windows:
dependency: transitive
description:
name: image_picker_windows
sha256: "6ad07afc4eb1bc25f3a01084d28520496c4a3bb0cb13685435838167c9dcedeb"
url: "https://pub.dev"
source: hosted
version: "0.2.1+1"
io:
dependency: transitive
description:
name: io
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
url: "https://pub.dev"
source: hosted
version: "1.0.5"
js:
dependency: transitive
description:
name: js
sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
url: "https://pub.dev"
source: hosted
version: "0.7.2"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
json_serializable:
dependency: "direct main"
description:
name: json_serializable
sha256: c50ef5fc083d5b5e12eef489503ba3bf5ccc899e487d691584699b4bdefeea8c
url: "https://pub.dev"
source: hosted
version: "6.9.5"
leak_tracker:
dependency: transitive
description:
@ -107,6 +496,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "5.1.1"
logging:
dependency: "direct main"
description:
name: logging
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
url: "https://pub.dev"
source: hosted
version: "1.3.0"
matcher:
dependency: transitive
description:
@ -131,19 +528,171 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.16.0"
path:
mime:
dependency: transitive
description:
name: mime
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
package_config:
dependency: transitive
description:
name: package_config
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
url: "https://pub.dev"
source: hosted
version: "2.2.0"
path:
dependency: "direct main"
description:
name: path
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
url: "https://pub.dev"
source: hosted
version: "1.9.1"
path_provider:
dependency: "direct main"
description:
name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
url: "https://pub.dev"
source: hosted
version: "2.1.5"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
sha256: d0d310befe2c8ab9e7f393288ccbb11b60c019c6b5afc21973eeee4dda2b35e9
url: "https://pub.dev"
source: hosted
version: "2.2.17"
path_provider_foundation:
dependency: transitive
description:
name: path_provider_foundation
sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
url: "https://pub.dev"
source: hosted
version: "2.2.1"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
url: "https://pub.dev"
source: hosted
version: "2.1.2"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
url: "https://pub.dev"
source: hosted
version: "2.3.0"
petitparser:
dependency: transitive
description:
name: petitparser
sha256: "07c8f0b1913bcde1ff0d26e57ace2f3012ccbf2b204e070290dad3bb22797646"
url: "https://pub.dev"
source: hosted
version: "6.1.0"
platform:
dependency: transitive
description:
name: platform
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
url: "https://pub.dev"
source: hosted
version: "3.1.6"
plugin_platform_interface:
dependency: transitive
description:
name: plugin_platform_interface
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
url: "https://pub.dev"
source: hosted
version: "2.1.8"
pool:
dependency: transitive
description:
name: pool
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
url: "https://pub.dev"
source: hosted
version: "1.5.1"
posix:
dependency: transitive
description:
name: posix
sha256: "6323a5b0fa688b6a010df4905a56b00181479e6d10534cecfecede2aa55add61"
url: "https://pub.dev"
source: hosted
version: "6.0.3"
pub_semver:
dependency: transitive
description:
name: pub_semver
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
url: "https://pub.dev"
source: hosted
version: "2.2.0"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: "0560ba233314abbed0a48a2956f7f022cce7c3e1e73df540277da7544cad4082"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
shelf:
dependency: transitive
description:
name: shelf
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
url: "https://pub.dev"
source: hosted
version: "1.4.2"
shelf_web_socket:
dependency: transitive
description:
name: shelf_web_socket
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
url: "https://pub.dev"
source: hosted
version: "3.0.0"
sky_engine:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "35c8150ece9e8c8d263337a265153c3329667640850b9304861faea59fc98f6b"
url: "https://pub.dev"
source: hosted
version: "2.0.0"
source_helper:
dependency: transitive
description:
name: source_helper
sha256: "86d247119aedce8e63f4751bd9626fc9613255935558447569ad42f9f5b48b3c"
url: "https://pub.dev"
source: hosted
version: "1.3.5"
source_span:
dependency: transitive
description:
@ -152,6 +701,78 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.10.1"
sprintf:
dependency: transitive
description:
name: sprintf
sha256: "1fc9ffe69d4df602376b52949af107d8f5703b77cda567c4d7d86a0693120f23"
url: "https://pub.dev"
source: hosted
version: "7.0.0"
sqflite:
dependency: "direct main"
description:
name: sqflite
sha256: e2297b1da52f127bc7a3da11439985d9b536f75070f3325e62ada69a5c585d03
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_android:
dependency: transitive
description:
name: sqflite_android
sha256: "2b3070c5fa881839f8b402ee4a39c1b4d561704d4ebbbcfb808a119bc2a1701b"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
sqflite_common:
dependency: transitive
description:
name: sqflite_common
sha256: "84731e8bfd8303a3389903e01fb2141b6e59b5973cacbb0929021df08dddbe8b"
url: "https://pub.dev"
source: hosted
version: "2.5.5"
sqflite_common_ffi:
dependency: "direct main"
description:
name: sqflite_common_ffi
sha256: "9faa2fedc5385ef238ce772589f7718c24cdddd27419b609bb9c6f703ea27988"
url: "https://pub.dev"
source: hosted
version: "2.3.6"
sqflite_darwin:
dependency: transitive
description:
name: sqflite_darwin
sha256: "279832e5cde3fe99e8571879498c9211f3ca6391b0d818df4e17d9fff5c6ccb3"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
sqflite_platform_interface:
dependency: transitive
description:
name: sqflite_platform_interface
sha256: "8dd4515c7bdcae0a785b0062859336de775e8c65db81ae33dd5445f35be61920"
url: "https://pub.dev"
source: hosted
version: "2.4.0"
sqlite3:
dependency: transitive
description:
name: sqlite3
sha256: c0503c69b44d5714e6abbf4c1f51a3c3cc42b75ce785f44404765e4635481d38
url: "https://pub.dev"
source: hosted
version: "2.7.6"
sqlite3_flutter_libs:
dependency: "direct main"
description:
name: sqlite3_flutter_libs
sha256: e07232b998755fe795655c56d1f5426e0190c9c435e1752d39e7b1cd33699c71
url: "https://pub.dev"
source: hosted
version: "0.5.34"
stack_trace:
dependency: transitive
description:
@ -168,6 +789,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: ad47125e588cfd37a9a7f86c7d6356dde8dfe89d071d293f80ca9e9273a33871
url: "https://pub.dev"
source: hosted
version: "2.1.1"
string_scanner:
dependency: transitive
description:
@ -176,6 +805,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.1"
synchronized:
dependency: transitive
description:
name: synchronized
sha256: c254ade258ec8282947a0acbbc90b9575b4f19673533ee46f2f6e9b3aeefd7c0
url: "https://pub.dev"
source: hosted
version: "3.4.0"
term_glyph:
dependency: transitive
description:
@ -192,6 +829,30 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.4"
timing:
dependency: transitive
description:
name: timing
sha256: "62ee18aca144e4a9f29d212f5a4c6a053be252b895ab14b5821996cff4ed90fe"
url: "https://pub.dev"
source: hosted
version: "1.0.2"
typed_data:
dependency: transitive
description:
name: typed_data
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
url: "https://pub.dev"
source: hosted
version: "1.4.0"
uuid:
dependency: "direct main"
description:
name: uuid
sha256: a5be9ef6618a7ac1e964353ef476418026db906c4facdedaa299b7a2e71690ff
url: "https://pub.dev"
source: hosted
version: "4.5.1"
vector_math:
dependency: transitive
description:
@ -208,6 +869,62 @@ packages:
url: "https://pub.dev"
source: hosted
version: "15.0.0"
watcher:
dependency: transitive
description:
name: watcher
sha256: "0b7fd4a0bbc4b92641dbf20adfd7e3fd1398fe17102d94b674234563e110088a"
url: "https://pub.dev"
source: hosted
version: "1.1.2"
web:
dependency: transitive
description:
name: web
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
url: "https://pub.dev"
source: hosted
version: "1.1.1"
web_socket:
dependency: transitive
description:
name: web_socket
sha256: "34d64019aa8e36bf9842ac014bb5d2f5586ca73df5e4d9bf5c936975cae6982c"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
web_socket_channel:
dependency: transitive
description:
name: web_socket_channel
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
url: "https://pub.dev"
source: hosted
version: "3.0.3"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
url: "https://pub.dev"
source: hosted
version: "1.1.0"
xml:
dependency: transitive
description:
name: xml
sha256: b015a8ad1c488f66851d762d3090a21c600e479dc75e68328c52774040cf9226
url: "https://pub.dev"
source: hosted
version: "6.5.0"
yaml:
dependency: transitive
description:
name: yaml
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
url: "https://pub.dev"
source: hosted
version: "3.1.3"
sdks:
dart: ">=3.8.1 <4.0.0"
flutter: ">=3.18.0-18.0.pre.54"
flutter: ">=3.27.0"

View File

@ -1,5 +1,5 @@
name: rtime
description: "A new Flutter project."
description: "An app to track your fpv flight time batteries and drones."
# The following line prevents the package from being accidentally published to
# pub.dev using `flutter pub publish`. This is preferred for private packages.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
@ -34,6 +34,17 @@ dependencies:
# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.8
sqflite: ^2.4.2
path_provider: ^2.1.5
path: ^1.9.1
logging: ^1.3.0
sqlite3_flutter_libs: ^0.5.34
sqflite_common_ffi: ^2.3.6
image: ^4.5.4
image_picker: ^1.1.2
uuid: ^4.5.1
json_serializable: ^6.9.5
image_cropper: ^9.1.0
dev_dependencies:
flutter_test:

View File

@ -6,6 +6,12 @@
#include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_windows.h>
#include <sqlite3_flutter_libs/sqlite3_flutter_libs_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorWindowsRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorWindows"));
Sqlite3FlutterLibsPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("Sqlite3FlutterLibsPlugin"));
}

View File

@ -3,6 +3,8 @@
#
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
sqlite3_flutter_libs
)
list(APPEND FLUTTER_FFI_PLUGIN_LIST