diff --git a/lib/data_provider/data_provider.dart b/lib/data_provider/data_provider.dart index 986bb02..c0e6f76 100644 --- a/lib/data_provider/data_provider.dart +++ b/lib/data_provider/data_provider.dart @@ -14,12 +14,17 @@ class Folder { Uri parent; Folder(this.items, this.self, this.parent); + + @override + String toString() { + return 'Folder{items: $items, self: $self, parent: $parent}'; + } } abstract class DataProvider { final List validSuffix = [".jpg", ".jpeg", ".png"]; - void connect(); + Future connect(); Future listOfFiles({Uri? uri}); ImageProvider getImageProvider(Uri uri); } diff --git a/lib/data_provider/local_data_provider.dart b/lib/data_provider/local_data_provider.dart index 5b1d0cc..961439f 100644 --- a/lib/data_provider/local_data_provider.dart +++ b/lib/data_provider/local_data_provider.dart @@ -9,7 +9,7 @@ class LocalDataProvider extends DataProvider { LocalDataProvider(this.initialPath); @override - void connect() {} + Future connect() async {} @override Future listOfFiles({Uri? uri}) async { diff --git a/lib/data_provider/ssh_data_provider.dart b/lib/data_provider/ssh_data_provider.dart index f8eb9c5..3a30e8b 100644 --- a/lib/data_provider/ssh_data_provider.dart +++ b/lib/data_provider/ssh_data_provider.dart @@ -35,6 +35,7 @@ class SSHDataProvider extends DataProvider { username: username, onPasswordRequest: () => password, ); + await sshClient?.authenticated; sftpClient = await sshClient?.sftp(); } diff --git a/lib/home_page.dart b/lib/home_page.dart index e4a3df0..662541c 100644 --- a/lib/home_page.dart +++ b/lib/home_page.dart @@ -1,9 +1,9 @@ import 'package:flutter/material.dart'; import 'package:gallery/data_provider/data_provider.dart'; -import 'package:gallery/image_tile.dart'; +import 'package:gallery/image_grid.dart'; import 'data_provider/local_data_provider.dart'; -import 'full_screen_image_view.dart'; +import 'data_provider/ssh_data_provider.dart'; class MyHomePage extends StatefulWidget { const MyHomePage({super.key, required this.title}); @@ -14,113 +14,72 @@ class MyHomePage extends StatefulWidget { State createState() => _MyHomePageState(); } +enum Page { local, remote } + class _MyHomePageState extends State { - Future? folder; + Page page = Page.local; - final DataProvider dprovider = LocalDataProvider("/home"); + Widget _buildPage() { + DataProvider provider; - final ScrollController _controller = ScrollController(); - - _MyHomePageState() { - folder = dprovider.listOfFiles(); + switch (page) { + case Page.local: + provider = LocalDataProvider("/home"); + break; + case Page.remote: + // todo do not generate a new provider on each tab switch + provider = SSHDataProvider( + initialPath: "/", host: "", password: "", port: 0, username: ""); + break; + } + return ImageGrid(dProvider: provider); } @override Widget build(BuildContext context) { - final width = MediaQuery.of(context).size.width; - return Scaffold( appBar: AppBar( title: Text(widget.title), ), - body: FutureBuilder( - future: folder, - builder: (context, snapshot) { - if (snapshot.hasData) { - final data = snapshot.data!; - - return GridView.builder( - controller: _controller, - gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( - crossAxisSpacing: 0, - mainAxisSpacing: 0, - crossAxisCount: width ~/ 300, + body: _buildPage(), + drawer: Drawer( + child: ListView( + padding: EdgeInsets.zero, + children: [ + DrawerHeader( + decoration: BoxDecoration( + color: Theme.of(context).primaryColor, ), - itemCount: data.items.length + 1, - itemBuilder: (context, index) { - if (index == 0) { - return ImageTile( - child: const Icon(Icons.arrow_back, size: 142), - onClick: () { - setState(() { - folder = dprovider.listOfFiles(uri: data.parent); - }); - }, - ); - } - - final elem = data.items[index - 1]; - return ImageTile( - dtaProvider: dprovider, - imageUri: elem.isFolder ? null : elem.uri, - onClick: () { - if (elem.isFolder) { - setState(() { - folder = dprovider.listOfFiles(uri: elem.uri); - }); - _controller.jumpTo(.0); - } else { - Navigator.push( - context, - MaterialPageRoute(builder: (context) { - final d = data.items - .where((element) => !element.isFolder) - .toList(growable: false); - // check how many folders are before index and subtract it - final newidx = index - - data.items - .getRange(0, index) - .where((element) => element.isFolder) - .length; - - return FullScreenImageView( - provider: dprovider, - items: d, - idx: newidx, - ); - }), - ); - } - }, - child: elem.isFolder - ? Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - const Icon(Icons.folder_open, size: 142), - Text(elem.name) - ], - ) - : null); + child: const Text('Gallery'), + ), + ListTile( + title: const Text('Local'), + onTap: () { + setState(() { + page = Page.local; + }); }, - ); - } else if (snapshot.hasError) { - return const Text("Error loading files"); - } - - return const CircularProgressIndicator( - strokeWidth: 3, - ); - }, - ), - floatingActionButton: FloatingActionButton( - onPressed: () { - _controller.animateTo(.0, - duration: const Duration(milliseconds: 400), - curve: Curves.easeInOutQuad); - }, - tooltip: 'Increment', - child: const Icon(Icons.arrow_upward), + ), + ListTile( + title: const Text('Remote'), + onTap: () { + setState(() { + page = Page.remote; + }); + }, + ), + ], + ), ), + // floatingActionButton: FloatingActionButton( + // onPressed: () { + // // _controller.animateTo(.0, + // // duration: const Duration(milliseconds: 400), + // // curve: Curves.easeInOutQuad); + // }, + // tooltip: 'Increment', + // child: const Icon(Icons.arrow_upward), + // ), ); } } diff --git a/lib/image_grid.dart b/lib/image_grid.dart new file mode 100644 index 0000000..fcfef2a --- /dev/null +++ b/lib/image_grid.dart @@ -0,0 +1,117 @@ +import 'package:flutter/material.dart'; +import 'data_provider/data_provider.dart'; + +import 'full_screen_image_view.dart'; +import 'image_tile.dart'; + +class ImageGrid extends StatefulWidget { + const ImageGrid({Key? key, required this.dProvider}) : super(key: key); + + final DataProvider dProvider; + + @override + State createState() => _ImageGridState(); +} + +class _ImageGridState extends State { + final ScrollController _controller = ScrollController(); + late Future folder = widget.dProvider.listOfFiles(); + + @override + void didUpdateWidget(ImageGrid oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.dProvider != widget.dProvider) { + folder = widget.dProvider.listOfFiles(); + } + } + + @override + Widget build(BuildContext context) { + final width = MediaQuery.of(context).size.width; + + return FutureBuilder( + future: (() async { + await widget.dProvider.connect(); + return folder; + }).call(), + builder: (context, AsyncSnapshot snapshot) { + if (snapshot.connectionState == ConnectionState.done) { + if (snapshot.hasData && snapshot.data != null) { + final Folder data = snapshot.data!; + + return GridView.builder( + controller: _controller, + gridDelegate: SliverGridDelegateWithFixedCrossAxisCount( + crossAxisSpacing: 0, + mainAxisSpacing: 0, + crossAxisCount: width ~/ 300, + ), + itemCount: data.items.length + 1, + itemBuilder: (context, index) { + if (index == 0) { + return ImageTile( + child: const Icon(Icons.arrow_back, size: 142), + onClick: () { + setState(() { + folder = widget.dProvider.listOfFiles(uri: data.parent); + }); + }, + ); + } + + final elem = data.items[index - 1]; + return ImageTile( + dtaProvider: widget.dProvider, + imageUri: elem.isFolder ? null : elem.uri, + onClick: () { + if (elem.isFolder) { + setState(() { + folder = widget.dProvider.listOfFiles(uri: elem.uri); + }); + _controller.jumpTo(.0); + } else { + Navigator.push( + context, + MaterialPageRoute(builder: (context) { + final d = data.items + .where((element) => !element.isFolder) + .toList(growable: false); + // check how many folders are before index and subtract it + final newidx = index - + data.items + .getRange(0, index) + .where((element) => element.isFolder) + .length; + + return FullScreenImageView( + provider: widget.dProvider, + items: d, + idx: newidx, + ); + }), + ); + } + }, + child: elem.isFolder + ? Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.folder_open, size: 142), + Text(elem.name) + ], + ) + : null); + }, + ); + } else if (snapshot.hasError) { + return const Text("Error loading files"); + } + } + + return const CircularProgressIndicator( + strokeWidth: 3, + ); + }, + ); + } +}