save token and settings also in sqlite db

This commit is contained in:
lukas-heiligenbrunner 2022-12-01 00:50:55 +01:00
parent 96b8d172ff
commit 7f039396aa
17 changed files with 142 additions and 290 deletions

View File

@ -2,6 +2,7 @@ import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import 'token.dart'; import 'token.dart';
class TokenException implements Exception { class TokenException implements Exception {
@ -11,7 +12,7 @@ class TokenException implements Exception {
class API { class API {
static Future<String> query( static Future<String> query(
String apinode, String action, Object payload) async { String apinode, String action, Object payload) async {
final t = await Token.getInstance().getToken(); final t = await getToken();
if (t != null) { if (t != null) {
final resp = await http.post( final resp = await http.post(
Uri.parse(t.domain + '/api/$apinode/$action'), Uri.parse(t.domain + '/api/$apinode/$action'),

View File

@ -1,60 +1,19 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/widgets.dart'; import 'package:openmediacentermobile/db/settings_db.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import '../log/log.dart';
class TokenT { class TokenT {
String token; String token;
String domain; String domain;
String videoPath;
TokenT(this.token, this.domain, this.videoPath); TokenT(this.token, this.domain);
} }
class Token { Future<TokenT?> getToken() async {
static final Token _token = Token._(); final settings = await SettingsDB.getInstance().getSettings();
final _storage = const FlutterSecureStorage(); if (settings.token == "" || settings.domain == "") {
String _tokenval = "";
String _domain = "";
String _vPath = "";
static Token getInstance() {
return _token;
}
Future<TokenT?> getToken() async {
if (_tokenval == "" || _domain == "" || _vPath == "") {
Log.d("reading token store");
WidgetsFlutterBinding.ensureInitialized();
final token = await _storage.read(key: 'jwt');
final domain = await _storage.read(key: 'domain');
final vPath = await _storage.read(key: 'videoPath');
// check if value is defined in phone store
if (token != null && domain != null && vPath != null) {
_tokenval = token;
_domain = domain;
return TokenT(token, domain, vPath);
} else {
Log.d("no token defined");
return null; return null;
}
} else { } else {
return TokenT(_tokenval, _domain, _vPath); return TokenT(settings.token, settings.domain);
} }
}
void setToken(String token, String domain, String videoPath) {
_tokenval = token;
_domain = domain;
_vPath = videoPath;
_storage.write(key: 'jwt', value: token);
_storage.write(key: 'domain', value: domain);
_storage.write(key: 'videoPath', value: videoPath);
}
Token._();
} }

View File

@ -8,7 +8,7 @@ import '../log/log.dart';
class Db { class Db {
late Database _db; late Database _db;
void init() async { Future<void> init() async {
if (kIsWeb) { if (kIsWeb) {
Log.i("Database on web is not supported"); Log.i("Database on web is not supported");
return; return;
@ -27,13 +27,20 @@ class Db {
_db = await openDatabase( _db = await openDatabase(
dbpath, dbpath,
onCreate: (db, version) { onCreate: (db, version) {
return db.execute( final batch = db.batch();
'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB)', batch.execute(
'CREATE TABLE previews(id INTEGER PRIMARY KEY, thumbnail BLOB);',
); );
batch.execute(
'CREATE TABLE settings(domain TEXT, token TEXT, videopath TEXT, tilewidth INTEGER);',
);
batch.insert("settings",
{"domain": "", "token": "", "videopath": "", "tilewidth": 0});
return batch.commit();
}, },
// Set the version. This executes the onCreate function and provides a // Set the version. This executes the onCreate function and provides a
// path to perform database upgrades and downgrades. // path to perform database upgrades and downgrades.
version: 1, version: 2,
); );
} }
@ -46,10 +53,16 @@ class Db {
/// get db size in bytes /// get db size in bytes
Future<int> getDbSize() async { Future<int> getDbSize() async {
final int cnt = (await Db().db().rawQuery("pragma page_count;"))[0] final batch = _db.batch();
["page_count"] as int; batch.rawQuery("pragma page_count;");
batch.rawQuery("pragma page_size;");
final result = (await batch.commit(noResult: false));
print(result);
final int cnt =
((result[0] as List<Map<String, dynamic>>)[0]["page_count"] as int);
final int pagesize = final int pagesize =
(await Db().db().rawQuery("pragma page_size;"))[0]["page_size"] as int; (result[1] as List<Map<String, dynamic>>)[0]["page_size"] as int;
return cnt * pagesize; return cnt * pagesize;
} }

51
lib/db/settings_db.dart Normal file
View File

@ -0,0 +1,51 @@
import 'database.dart';
class SettingsT {
String domain;
String token;
String videopath;
int tilewidth;
SettingsT(this.domain, this.token, this.videopath, this.tilewidth);
}
class SettingsDB {
static final SettingsDB _instance = SettingsDB._();
SettingsT _settings = SettingsT("", "", "", 0);
bool _initialized = false;
Future<SettingsT> getSettings() async {
if (!_initialized) {
final result = (await Db().db().query("settings",
where: "1",
columns: ["domain", "token", "videopath", "tilewidth"]))
.first;
_settings = SettingsT(
result["domain"] as String,
result["token"] as String,
result["videopath"] as String,
result["tilewidth"] as int);
}
return _settings;
}
Future<void> setSettings(SettingsT settings) async {
await Db().db().update(
"settings",
{
"domain": settings.domain,
"token": settings.token,
"videopath": settings.videopath,
"tilewidth": settings.tilewidth
},
where: "1");
}
static SettingsDB getInstance() {
return _instance;
}
SettingsDB._();
}

View File

@ -32,7 +32,8 @@ class _AddActorDialogState extends State<AddActorDialog> {
return Text("Error"); return Text("Error");
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
final data = snapshot.data! as List<Actor>; final data = snapshot.data! as List<Actor>;
data.sort((a, b) => a.name.compareTo(b.name)); data.sort((a, b) =>
a.name.toLowerCase().compareTo(b.name.toLowerCase()));
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
children: data children: data

View File

@ -33,7 +33,8 @@ class _AddTagDialogState extends State<AddTagDialog> {
} else if (snapshot.hasData) { } else if (snapshot.hasData) {
final data = snapshot.data! as List<Tag>; final data = snapshot.data! as List<Tag>;
data.sort( data.sort(
(a, b) => a.tagName.compareTo(b.tagName), (a, b) =>
a.tagName.toLowerCase().compareTo(b.tagName.toLowerCase()),
); );
return Column( return Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,

View File

@ -23,7 +23,7 @@ class _LoginContainerState extends State<LoginContainer> {
} }
void _init() async { void _init() async {
final token = await Token.getInstance().getToken(); final token = await getToken();
Log.i("The token value is $token"); Log.i("The token value is $token");
if (token != null) { if (token != null) {
setState(() { setState(() {

View File

@ -3,8 +3,9 @@ import 'dart:convert';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:http/http.dart' as http; import 'package:http/http.dart' as http;
import '../api/settings_api.dart'; import '../api/settings_api.dart';
import '../api/token.dart'; import '../db/settings_db.dart';
import '../log/log.dart'; import '../log/log.dart';
import 'login_context.dart'; import 'login_context.dart';
@ -44,11 +45,13 @@ class _LoginScreenState extends State<LoginScreen> {
final json = jsonDecode(resp.body); final json = jsonDecode(resp.body);
final token = json["Token"]; final token = json["Token"];
// todo bit hacky SettingsT settings = await SettingsDB.getInstance().getSettings();
Token.getInstance().setToken(token, domain, "temp"); settings.domain = domain;
// we need to call this twice because we need for the loadInitialData an api token settings.token = token;
Token.getInstance() SettingsDB.getInstance().setSettings(settings);
.setToken(token, domain, (await loadInitialData()).videoPath); // we need two steps here because we need an authenticated api call for the videopath
settings.videopath = (await loadInitialData()).videoPath;
SettingsDB.getInstance().setSettings(settings);
LoginContext.of(context).onLoggin(true); LoginContext.of(context).onLoggin(true);
return ""; return "";

View File

@ -15,7 +15,7 @@ void main() async {
} else { } else {
await loadDeviceInfo(); await loadDeviceInfo();
} }
Db().init(); await Db().init();
runApp(Shortcuts(shortcuts: <LogicalKeySet, Intent>{ runApp(Shortcuts(shortcuts: <LogicalKeySet, Intent>{
LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(), LogicalKeySet(LogicalKeyboardKey.select): ActivateIntent(),

View File

@ -1,10 +1,11 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../utils/file_formatter.dart'; import 'package:openmediacentermobile/db/settings_db.dart';
import '../api/token.dart'; import '../api/token.dart';
import '../db/database.dart'; import '../db/database.dart';
import '../drawer/my_drawer.dart'; import '../drawer/my_drawer.dart';
import '../login/login_context.dart'; import '../login/login_context.dart';
import '../utils/file_formatter.dart';
class SettingsScreen extends StatefulWidget { class SettingsScreen extends StatefulWidget {
const SettingsScreen({Key? key}) : super(key: key); const SettingsScreen({Key? key}) : super(key: key);
@ -24,7 +25,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
dbsize = v; dbsize = v;
})); }));
Token.getInstance().getToken().then((value) => setState(() { getToken().then((value) => setState(() {
serverUrl = value?.domain ?? "unknown"; serverUrl = value?.domain ?? "unknown";
})); }));
} }
@ -53,7 +54,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
ElevatedButton( ElevatedButton(
onPressed: () { onPressed: () {
loginCtx.onLoggin(false); loginCtx.onLoggin(false);
Token.getInstance().setToken("", "", ""); SettingsDB.getInstance()
.setSettings(SettingsT("", "", "", 0));
Db().clear(); Db().clear();
}, },
child: Text("Logout")) child: Text("Logout"))

View File

@ -3,9 +3,9 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart'; import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import '../utils/platform.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/video.dart'; import '../types/video.dart';
import '../utils/platform.dart';
import 'preview_tile.dart'; import 'preview_tile.dart';
class PreviewGrid extends StatefulWidget { class PreviewGrid extends StatefulWidget {
@ -78,13 +78,13 @@ class _PreviewGridState extends State<PreviewGrid> {
Widget _mainGrid(List<VideoT> data, double width) { Widget _mainGrid(List<VideoT> data, double width) {
return Stack( return Stack(
children: [ children: [
Column( Container(
color: Color(0xff999999),
child: Column(
children: [ children: [
if (widget.headerBuilder != null) widget.headerBuilder!(this), if (widget.headerBuilder != null) widget.headerBuilder!(this),
data.length > 0 data.length > 0
? Expanded( ? Expanded(
child: Container(
color: Color(0xff999999),
child: MasonryGridView.count( child: MasonryGridView.count(
// every tile should be at max 330 pixels long... // every tile should be at max 330 pixels long...
crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275, crossAxisCount: isTV() ? width ~/ 200 : width ~/ 275,
@ -109,7 +109,6 @@ class _PreviewGridState extends State<PreviewGrid> {
); );
}, },
), ),
),
) )
: Center( : Center(
child: Column( child: Column(
@ -126,6 +125,7 @@ class _PreviewGridState extends State<PreviewGrid> {
if (widget.footerBuilder != null) widget.footerBuilder!(this), if (widget.footerBuilder != null) widget.footerBuilder!(this),
], ],
), ),
),
if (_previewImage != null) ..._buildPreviewImage(), if (_previewImage != null) ..._buildPreviewImage(),
], ],
); );

View File

@ -5,8 +5,8 @@ import 'package:sqflite/sqflite.dart';
import '../api/video_api.dart'; import '../api/video_api.dart';
import '../db/database.dart'; import '../db/database.dart';
import '../log/log.dart'; import '../log/log.dart';
import '../utils/platform.dart';
import '../types/video.dart'; import '../types/video.dart';
import '../utils/platform.dart';
import '../video_screen/videoscreen.dart'; import '../video_screen/videoscreen.dart';
class PreviewTile extends StatefulWidget { class PreviewTile extends StatefulWidget {

View File

@ -1,14 +1,14 @@
import 'dart:async'; import 'dart:async';
import 'package:flutter/material.dart';
import '../api/token.dart'; import 'package:flutter/material.dart';
import 'package:openmediacentermobile/db/settings_db.dart';
import '../api/video_api.dart'; import '../api/video_api.dart';
import '../utils/platform.dart';
import '../screen_loading.dart'; import '../screen_loading.dart';
import '../types/video.dart'; import '../types/video.dart';
import '../types/video_data.dart'; import '../types/video_data.dart';
import '../utils/platform.dart';
import 'info_view.dart'; import 'info_view.dart';
import 'videoscreen_desktop.dart' import 'videoscreen_desktop.dart'
if (dart.library.html) 'videoscreen_mobile.dart'; if (dart.library.html) 'videoscreen_mobile.dart';
import 'videoscreen_mobile.dart'; import 'videoscreen_mobile.dart';
@ -34,10 +34,8 @@ class _VideoScreenState extends State<VideoScreen> {
void initPlayer() async { void initPlayer() async {
final videodata = await _videoData; final videodata = await _videoData;
final token = await Token.getInstance().getToken(); final settings = await SettingsDB.getInstance().getSettings();
if (token == null) return; final path = settings.domain + settings.videopath + videodata.movieUrl;
final path = token.domain + token.videoPath + videodata.movieUrl;
url = path; url = path;
} }

View File

@ -7,21 +7,9 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <dart_vlc/dart_vlc_plugin.h> #include <dart_vlc/dart_vlc_plugin.h>
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
#include <screen_retriever/screen_retriever_plugin.h>
#include <window_manager/window_manager_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) dart_vlc_registrar = g_autoptr(FlPluginRegistrar) dart_vlc_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin"); fl_plugin_registry_get_registrar_for_plugin(registry, "DartVlcPlugin");
dart_vlc_plugin_register_with_registrar(dart_vlc_registrar); dart_vlc_plugin_register_with_registrar(dart_vlc_registrar);
g_autoptr(FlPluginRegistrar) flutter_secure_storage_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterSecureStorageLinuxPlugin");
flutter_secure_storage_linux_plugin_register_with_registrar(flutter_secure_storage_linux_registrar);
g_autoptr(FlPluginRegistrar) screen_retriever_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "ScreenRetrieverPlugin");
screen_retriever_plugin_register_with_registrar(screen_retriever_registrar);
g_autoptr(FlPluginRegistrar) window_manager_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "WindowManagerPlugin");
window_manager_plugin_register_with_registrar(window_manager_registrar);
} }

View File

@ -4,9 +4,6 @@
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
dart_vlc dart_vlc
flutter_secure_storage_linux
screen_retriever
window_manager
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -35,7 +35,7 @@ packages:
name: chewie name: chewie
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.3.5" version: "1.3.6"
clock: clock:
dependency: transitive dependency: transitive
description: description:
@ -70,56 +70,28 @@ packages:
name: dart_vlc name: dart_vlc
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.3.0" version: "0.4.0"
dart_vlc_ffi: dart_vlc_ffi:
dependency: transitive dependency: transitive
description: description:
name: dart_vlc_ffi name: dart_vlc_ffi
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.1.8" version: "0.2.0+1"
device_info_plus: device_info_plus:
dependency: "direct main" dependency: "direct main"
description: description:
name: device_info_plus name: device_info_plus
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "4.0.0" version: "8.0.0"
device_info_plus_linux:
dependency: transitive
description:
name: device_info_plus_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
device_info_plus_macos:
dependency: transitive
description:
name: device_info_plus_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.2.3"
device_info_plus_platform_interface: device_info_plus_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: device_info_plus_platform_interface name: device_info_plus_platform_interface
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.6.1" version: "7.0.0"
device_info_plus_web:
dependency: transitive
description:
name: device_info_plus_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.0"
device_info_plus_windows:
dependency: transitive
description:
name: device_info_plus_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.1"
fake_async: fake_async:
dependency: transitive dependency: transitive
description: description:
@ -133,7 +105,7 @@ packages:
name: ffi name: ffi
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.1" version: "2.0.1"
file: file:
dependency: transitive dependency: transitive
description: description:
@ -153,55 +125,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
flutter_native_view:
dependency: transitive
description:
name: flutter_native_view
url: "https://pub.dartlang.org"
source: hosted
version: "0.0.2"
flutter_secure_storage:
dependency: "direct main"
description:
name: flutter_secure_storage
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.0"
flutter_secure_storage_linux:
dependency: transitive
description:
name: flutter_secure_storage_linux
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
flutter_secure_storage_macos:
dependency: transitive
description:
name: flutter_secure_storage_macos
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.1"
flutter_secure_storage_platform_interface:
dependency: transitive
description:
name: flutter_secure_storage_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.0"
flutter_secure_storage_web:
dependency: transitive
description:
name: flutter_secure_storage_web
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.2"
flutter_secure_storage_windows:
dependency: transitive
description:
name: flutter_secure_storage_windows
url: "https://pub.dartlang.org"
source: hosted
version: "1.1.2"
flutter_staggered_grid_view: flutter_staggered_grid_view:
dependency: "direct main" dependency: "direct main"
description: description:
@ -296,62 +219,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.8.2" version: "1.8.2"
path_provider:
dependency: transitive
description:
name: path_provider
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
path_provider_android:
dependency: transitive
description:
name: path_provider_android
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.21"
path_provider_ios:
dependency: transitive
description:
name: path_provider_ios
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.11"
path_provider_linux:
dependency: transitive
description:
name: path_provider_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.1.7"
path_provider_macos:
dependency: transitive
description:
name: path_provider_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.6"
path_provider_platform_interface:
dependency: transitive
description:
name: path_provider_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.5"
path_provider_windows:
dependency: transitive
description:
name: path_provider_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.7"
platform:
dependency: transitive
description:
name: platform
url: "https://pub.dartlang.org"
source: hosted
version: "3.1.0"
plugin_platform_interface: plugin_platform_interface:
dependency: transitive dependency: transitive
description: description:
@ -359,13 +226,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.3" version: "2.1.3"
process:
dependency: transitive
description:
name: process
url: "https://pub.dartlang.org"
source: hosted
version: "4.2.4"
provider: provider:
dependency: transitive dependency: transitive
description: description:
@ -373,13 +233,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "6.0.4" version: "6.0.4"
screen_retriever:
dependency: transitive
description:
name: screen_retriever
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.4"
sky_engine: sky_engine:
dependency: transitive dependency: transitive
description: flutter description: flutter
@ -482,7 +335,7 @@ packages:
name: video_player name: video_player
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.4.7" version: "2.4.8"
video_player_android: video_player_android:
dependency: transitive dependency: transitive
description: description:
@ -545,28 +398,14 @@ packages:
name: wakelock_windows name: wakelock_windows
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "0.2.0" version: "0.2.1"
win32: win32:
dependency: transitive dependency: transitive
description: description:
name: win32 name: win32
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.6.1" version: "3.1.2"
window_manager:
dependency: transitive
description:
name: window_manager
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.7"
xdg_directories:
dependency: transitive
description:
name: xdg_directories
url: "https://pub.dartlang.org"
source: hosted
version: "0.2.0+2"
sdks: sdks:
dart: ">=2.18.0 <3.0.0" dart: ">=2.18.0 <3.0.0"
flutter: ">=3.3.0" flutter: ">=3.3.0"

View File

@ -34,12 +34,11 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
flutter_secure_storage: ^6.0.0
logger: ^1.1.0 logger: ^1.1.0
http: ^0.13.4 http: ^0.13.4
flutter_staggered_grid_view: ^0.6.1 flutter_staggered_grid_view: ^0.6.1
dart_vlc: ^0.3.0 dart_vlc: ^0.4.0
device_info_plus: ^4.0.0 device_info_plus: ^8.0.0
video_player: ^2.3.0 video_player: ^2.3.0
chewie: ^1.3.2 chewie: ^1.3.2
sqflite: ^2.0.3+1 sqflite: ^2.0.3+1