add settings page so set ssh login

request storage permissions on android
This commit is contained in:
lukas-heiligenbrunner 2022-09-28 15:31:24 +02:00
parent 2ba9fb3fbd
commit 369da56258
12 changed files with 233 additions and 24 deletions

View File

@ -26,7 +26,7 @@ apply plugin: 'kotlin-android'
apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle"
android { android {
compileSdkVersion flutter.compileSdkVersion compileSdkVersion 33
ndkVersion flutter.ndkVersion ndkVersion flutter.ndkVersion
compileOptions { compileOptions {

View File

@ -1,5 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="eu.heili.gallery"> package="eu.heili.gallery">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<application <application
android:label="gallery" android:label="gallery"
android:name="${applicationName}" android:name="${applicationName}"

View File

@ -6,6 +6,11 @@ class Item {
String name; String name;
Item(this.isFolder, this.uri, this.name); Item(this.isFolder, this.uri, this.name);
@override
String toString() {
return 'Item{isFolder: $isFolder, uri: $uri, name: $name}';
}
} }
class Folder { class Folder {

View File

@ -10,11 +10,11 @@ import 'dart:ui' as ui show Codec, ImmutableBuffer;
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
class SSHDataProvider extends DataProvider { class SSHDataProvider extends DataProvider {
final String host; final Future<String> host;
final int port; final Future<int> port;
final String username; final Future<String> username;
final String password; final Future<String> password;
final String initialPath; final Future<String> initialPath;
SftpClient? sftpClient; SftpClient? sftpClient;
SSHClient? sshClient; SSHClient? sshClient;
@ -31,8 +31,8 @@ class SSHDataProvider extends DataProvider {
if (sshClient != null && !sshClient!.isClosed) return; if (sshClient != null && !sshClient!.isClosed) return;
sshClient = SSHClient( sshClient = SSHClient(
await SSHSocket.connect(host, port), await SSHSocket.connect(await host, await port),
username: username, username: await username,
onPasswordRequest: () => password, onPasswordRequest: () => password,
); );
await sshClient?.authenticated; await sshClient?.authenticated;
@ -45,7 +45,8 @@ class SSHDataProvider extends DataProvider {
await connect(); await connect();
if (sftpClient == null) throw const FormatException(""); if (sftpClient == null) throw const FormatException("");
final dir = uri != null ? Directory.fromUri(uri) : Directory(initialPath); final dir =
uri != null ? Directory.fromUri(uri) : Directory(await initialPath);
final items = await sftpClient!.listdir(dir.path); final items = await sftpClient!.listdir(dir.path);
List<Item> res = []; List<Item> res = [];

View File

@ -1,8 +1,9 @@
import 'dart:io'; import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:gallery/data_provider/data_provider.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:gallery/image_grid.dart'; import 'package:gallery/image_grid.dart';
import 'package:gallery/settings_page.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'data_provider/local_data_provider.dart'; import 'data_provider/local_data_provider.dart';
@ -17,7 +18,7 @@ class MyHomePage extends StatefulWidget {
State<MyHomePage> createState() => _MyHomePageState(); State<MyHomePage> createState() => _MyHomePageState();
} }
enum Page { local, remote } enum Page { local, remote, settings }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
Page page = Page.local; Page page = Page.local;
@ -34,19 +35,27 @@ class _MyHomePageState extends State<MyHomePage> {
} }
Widget _buildPage() { Widget _buildPage() {
DataProvider provider;
switch (page) { switch (page) {
case Page.local: case Page.local:
provider = LocalDataProvider(_getLocalDir()); return ImageGrid(dProvider: LocalDataProvider(_getLocalDir()));
break;
case Page.remote: case Page.remote:
// todo do not generate a new provider on each tab switch // todo do not generate a new provider on each tab switch
provider = SSHDataProvider( const storage = FlutterSecureStorage();
initialPath: "/", host: "", password: "", port: 0, username: "");
break; return ImageGrid(
dProvider: SSHDataProvider(
initialPath:
Future.value("/media/3TBRaid/3TBRaid/Bilder/2022/"),
host: (() async => (await storage.read(key: "host")) ?? "")
.call(),
password:
(() async => (await storage.read(key: "pwd")) ?? "").call(),
port: Future.value(22),
username: (() async => (await storage.read(key: "user")) ?? "")
.call()));
case Page.settings:
return const SettingsPage();
} }
return ImageGrid(dProvider: provider);
} }
@override @override
@ -84,6 +93,15 @@ class _MyHomePageState extends State<MyHomePage> {
Navigator.pop(context); Navigator.pop(context);
}, },
), ),
ListTile(
title: const Text('Settings'),
onTap: () {
setState(() {
page = Page.settings;
});
Navigator.pop(context);
},
),
], ],
), ),
), ),

View File

@ -1,3 +1,5 @@
import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'data_provider/data_provider.dart'; import 'data_provider/data_provider.dart';
@ -41,13 +43,15 @@ class _ImageGridState extends State<ImageGrid> {
gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(
crossAxisSpacing: 0, crossAxisSpacing: 0,
mainAxisSpacing: 0, mainAxisSpacing: 0,
crossAxisCount: width ~/ 300, crossAxisCount:
Platform.isAndroid ? width ~/ 100 : width ~/ 300,
), ),
itemCount: data.items.length + 1, itemCount: data.items.length + 1,
itemBuilder: (context, index) { itemBuilder: (context, index) {
if (index == 0) { if (index == 0) {
return ImageTile( return ImageTile(
child: const Icon(Icons.arrow_back, size: 142), child: Icon(Icons.arrow_back,
size: Platform.isAndroid ? 64 : 142),
onClick: () { onClick: () {
setState(() { setState(() {
folder = widget.dProvider.listOfFiles(uri: data.parent); folder = widget.dProvider.listOfFiles(uri: data.parent);
@ -93,7 +97,8 @@ class _ImageGridState extends State<ImageGrid> {
? Column( ? Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
const Icon(Icons.folder_open, size: 142), Icon(Icons.folder_open,
size: Platform.isAndroid ? 64 : 142),
Text(elem.name) Text(elem.name)
], ],
) )

View File

@ -1,11 +1,21 @@
import 'dart:io';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:permission_handler/permission_handler.dart';
import 'home_page.dart'; import 'home_page.dart';
void main() { void main() async {
WidgetsFlutterBinding.ensureInitialized();
if (Platform.isAndroid) {
if (await Permission.storage.request().isGranted) {
runApp(const MyApp()); runApp(const MyApp());
}
} else {
runApp(const MyApp());
}
} }
class AppScrollBehavior extends MaterialScrollBehavior { class AppScrollBehavior extends MaterialScrollBehavior {

79
lib/settings_page.dart Normal file
View File

@ -0,0 +1,79 @@
import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
class SettingsPage extends StatefulWidget {
const SettingsPage({Key? key}) : super(key: key);
@override
State<SettingsPage> createState() => _SettingsPageState();
}
class _SettingsPageState extends State<SettingsPage> {
late TextEditingController _controllerHost;
late TextEditingController _controllerUser;
late TextEditingController _controllerPwd;
@override
void initState() {
super.initState();
_controllerHost = TextEditingController();
_controllerUser = TextEditingController();
_controllerPwd = TextEditingController();
const storage = FlutterSecureStorage();
storage
.read(key: "host")
.then((value) => _controllerHost.text = value ?? "");
storage
.read(key: "user")
.then((value) => _controllerUser.text = value ?? "");
storage.read(key: "pwd").then((value) => _controllerPwd.text = value ?? "");
}
@override
Widget build(BuildContext context) {
return Padding(
padding: const EdgeInsets.all(5),
child: Column(
children: [
const SizedBox(
height: 10,
),
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(), labelText: "host"),
controller: _controllerHost,
),
const SizedBox(
height: 5,
),
TextField(
decoration: const InputDecoration(
border: OutlineInputBorder(), labelText: "user"),
controller: _controllerUser,
),
const SizedBox(
height: 5,
),
TextField(
obscureText: true,
decoration: const InputDecoration(
border: OutlineInputBorder(), labelText: "password"),
controller: _controllerPwd,
),
const SizedBox(
height: 5,
),
TextButton(
onPressed: () {
const storage = FlutterSecureStorage();
storage.write(key: "host", value: _controllerHost.value.text);
storage.write(key: "user", value: _controllerUser.value.text);
storage.write(key: "pwd", value: _controllerPwd.value.text);
},
child: const Text("Save"))
],
),
);
}
}

View File

@ -6,6 +6,10 @@
#include "generated_plugin_registrant.h" #include "generated_plugin_registrant.h"
#include <flutter_secure_storage_linux/flutter_secure_storage_linux_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) { void fl_register_plugins(FlPluginRegistry* registry) {
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);
} }

View File

@ -3,6 +3,7 @@
# #
list(APPEND FLUTTER_PLUGIN_LIST list(APPEND FLUTTER_PLUGIN_LIST
flutter_secure_storage_linux
) )
list(APPEND FLUTTER_FFI_PLUGIN_LIST list(APPEND FLUTTER_FFI_PLUGIN_LIST

View File

@ -111,11 +111,58 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.0.1" version: "2.0.1"
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_test: flutter_test:
dependency: "direct dev" dependency: "direct dev"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
intl: intl:
dependency: transitive dependency: transitive
description: description:
@ -214,6 +261,41 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "2.1.3" version: "2.1.3"
permission_handler:
dependency: "direct main"
description:
name: permission_handler
url: "https://pub.dartlang.org"
source: hosted
version: "10.0.2"
permission_handler_android:
dependency: transitive
description:
name: permission_handler_android
url: "https://pub.dartlang.org"
source: hosted
version: "10.0.0"
permission_handler_apple:
dependency: transitive
description:
name: permission_handler_apple
url: "https://pub.dartlang.org"
source: hosted
version: "9.0.4"
permission_handler_platform_interface:
dependency: transitive
description:
name: permission_handler_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "3.8.0"
permission_handler_windows:
dependency: transitive
description:
name: permission_handler_windows
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0"
pinenacl: pinenacl:
dependency: transitive dependency: transitive
description: description:

View File

@ -38,6 +38,8 @@ dependencies:
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.2
path_provider: ^2.0.11 path_provider: ^2.0.11
dartssh2: ^2.7.2+3 dartssh2: ^2.7.2+3
permission_handler: ^10.0.2
flutter_secure_storage: ^6.0.0
dev_dependencies: dev_dependencies:
flutter_test: flutter_test: