This commit is contained in:
2026-04-09 15:47:59 +01:00
parent 2033ad79e2
commit 558a28d12b
22 changed files with 1275 additions and 271 deletions
+252
View File
@@ -0,0 +1,252 @@
import 'package:flutter/material.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:intl/intl.dart';
import 'package:nextcloud/webdav.dart';
import 'package:nextcloudapp/components/file_size.dart';
import 'package:nextcloudapp/models/files.dart';
import 'package:nextcloudapp/provider/nextcloud_provider.dart';
import 'package:nextcloudapp/service/snackbar_service.dart';
import 'package:nextcloudapp/components/mime_type.dart';
import 'package:file_icon/file_icon.dart';
class HomeSceen extends StatefulWidget {
const HomeSceen({super.key});
@override
State<HomeSceen> createState() => _HomeSceenState();
}
class _HomeSceenState extends State<HomeSceen> {
//
var _deviceHeight;
var _deviceWidth;
var _percentage;
var _usedMemory;
var _totalMemory;
var _cwd;
List<String> _temp = [];
List<Files>? _data = [];
final MimetypeIcon _mimetypeIcon = MimetypeIcon.instance;
final FileSize _fileSize = FileSize.instance;
final NextcloudProvider _provider = NextcloudProvider.instance;
final ScrollController _scrollController = ScrollController();
_HomeSceenState() {
_cwd = "";
}
@override
Widget build(BuildContext context) {
_deviceHeight = MediaQuery.of(context).size.height;
_deviceWidth = MediaQuery.of(context).size.width;
SnackBarService.instance.buildContext = context;
return Scaffold(
appBar: AppBar(
title: Row(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Container(
height: _deviceHeight * 0.10,
width: _deviceWidth * 0.20,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(10),
image: DecorationImage(
image: AssetImage('assets/images/nextcloud_logo.png'),
),
),
),
Text("NextCloud"),
],
),
),
drawer: _appDrawerUI(),
body: _appBodyUI(),
floatingActionButton: _floatingButton());
}
Widget _floatingButton() {
return SpeedDial(
closeManually: true,
animatedIcon: AnimatedIcons.menu_arrow,
children: [
SpeedDialChild(
label: "File Upload",
child: Icon(Icons.upload),
),
SpeedDialChild(
label: "Folder Upload",
child: Icon(Icons.upload),
),
],
);
}
Widget _appDrawerUI() {
_totalMemory = _provider.userDetails!.quota.total;
_usedMemory = _provider.userDetails!.quota.used;
_percentage = (_usedMemory / _totalMemory);
return NavigationDrawer(
backgroundColor: Color.fromARGB(255, 72, 160, 232),
children: [
Stack(
clipBehavior: Clip.none,
children: [
SizedBox(
height: _deviceHeight * 0.05,
child: Row(
crossAxisAlignment: CrossAxisAlignment.start,
mainAxisAlignment: MainAxisAlignment.spaceBetween,
mainAxisSize: MainAxisSize.max,
children: [
Text(_provider.userDetails!.displayName),
IconButton(
onPressed: () {
_provider.logout();
},
icon: Icon(Icons.logout_sharp),
),
],
),
),
SizedBox(
height: _deviceHeight * 0.20,
child: Column(
crossAxisAlignment: CrossAxisAlignment.center,
mainAxisAlignment: MainAxisAlignment.start,
mainAxisSize: MainAxisSize.max,
children: [
Text(
'Memory Usage',
style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
SizedBox(height: 10),
LinearProgressIndicator(
value: _percentage,
backgroundColor: Colors.grey[300],
valueColor: AlwaysStoppedAnimation<Color>(
_percentage > 0.80 ? Colors.red : Colors.green,
),
),
SizedBox(height: 10),
Text(
'${(_percentage * 100).toStringAsFixed(2)}% used',
style: TextStyle(fontSize: 14),
),
Text(
'${_fileSize.getFileSizeString(
bytes: _usedMemory,
decimals: 1,
)} of ${_fileSize.getFileSizeString(
bytes: _totalMemory,
decimals: 1,
)}',
style: TextStyle(fontSize: 14),
),
],
),
)
],
),
],
);
}
Widget _appBodyUI() {
return SizedBox(
height: _deviceHeight * 0.80,
child: FutureBuilder(
future: _provider.listDirectory(_cwd),
builder: (context, _snapshot) {
_data = _snapshot.data;
if (_data != null) {
// WidgetsBinding.instance.addPostFrameCallback(
// (_) {
// if (_scrollController.hasClients) {
// _scrollController
// .jumpTo(_scrollController.position.minScrollExtent);
// }
// },
// );
return Scrollbar(
controller: _scrollController,
child: ListView.builder(
controller: _scrollController,
itemCount: _data!.length,
itemBuilder: (BuildContext _context, int _index) {
return ListTile(
onLongPress: () {},
onTap: () {
setState(() {
if (_data![_index].resourceType! == "folder") {
if (_index == 0) {
_temp.removeAt(_temp.length - 2);
_cwd = _temp
.join("/")
.replaceAll(webdavBase.toString(), '');
} else {
_cwd = _data![_index]
.path!
.replaceAll(webdavBase.toString(), '');
_temp = _data![_index].path!.split("/");
}
}
});
},
leading: _data![_index].resourceType! == 'folder'
? Icon(_mimetypeIcon
.getIconForMimeType(_data![_index].contentType!))
: FileIcon(
_data![_index].name!,
size: 32.0,
),
title: Text(
_data![_index].name!,
overflow: TextOverflow.ellipsis,
style: TextStyle(fontSize: 15),
),
subtitle: Text(
_index == 0
? ''
: DateFormat('dd MMM, yyyy hh:mm a')
.format(_data![_index].modifiedDate!),
style: TextStyle(
fontSize: 12,
),
),
trailing: Text(
_index != 0
? _fileSize.getFileSizeString(
bytes: _data![_index].size!,
decimals: 2,
)
: '',
),
);
},
),
);
} else if (_snapshot.hasError) {
return Align(
child: Text("No Data"),
);
} else if (_snapshot.connectionState == ConnectionState.waiting) {
return Align(
child: CircularProgressIndicator(),
);
} else {
return Align(
child: Text("Undecided"),
);
;
}
},
),
);
}
}
+101 -77
View File
@@ -1,8 +1,7 @@
import 'package:flutter/material.dart';
import 'package:nextcloudapp/api/login_api.dart';
import 'package:nextcloudapp/components/default_values.dart';
import 'package:nextcloudapp/screens/welcome_screen.dart';
import 'package:nextcloudapp/provider/nextcloud_provider.dart';
import 'package:nextcloudapp/service/snackbar_service.dart';
import 'package:provider/provider.dart';
class LoginScreen extends StatefulWidget {
const LoginScreen({super.key});
@@ -14,44 +13,55 @@ class LoginScreen extends StatefulWidget {
class _LoginScreenState extends State<LoginScreen> {
var _deviceHeight;
var _deviceWidth;
var urlController;
var usernameController;
var passwordController;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
var checked = false;
var _url;
var _username;
var _password;
var enableInput = true;
var savePassword = false;
var obscureText = true;
NextcloudProvider _provider = NextcloudProvider.instance;
final GlobalKey<FormState> _formKey = GlobalKey<FormState>();
@override
Widget build(BuildContext context) {
SnackBarService.instance.buildContext = context;
_deviceHeight = MediaQuery.of(context).size.height;
_deviceWidth = MediaQuery.of(context).size.width;
return Scaffold(
body: Align(
child: Container(
height: _deviceHeight * 0.60,
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.05),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_inputField(),
_passwordSaveField(),
_signInField(),
],
),
child: ChangeNotifierProvider<NextcloudProvider>.value(
value: _provider,
child: _loginPage(),
),
),
);
}
Widget _inputField() {
Widget _loginPage() {
return Builder(builder: (BuildContext _context) {
SnackBarService.instance.buildContext = _context;
_provider = Provider.of<NextcloudProvider>(_context);
return Container(
height: _deviceHeight * 0.80,
padding: EdgeInsets.symmetric(horizontal: _deviceWidth * 0.05),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
mainAxisSize: MainAxisSize.max,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
_inputForm(),
_passwordSaveField(),
_signInButton(_context),
],
),
);
});
}
Widget _inputForm() {
return SizedBox(
height: _deviceHeight * 0.25,
height: _deviceHeight * 0.27,
child: Form(
key: _formKey,
onChanged: () {
@@ -63,64 +73,84 @@ class _LoginScreenState extends State<LoginScreen> {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
TextFormField(
initialValue: "https://storage.theonasanyas.com",
enabled: enableInput,
keyboardType: TextInputType.url,
textInputAction: TextInputAction.next,
cursorColor: Colors.white,
cursorColor: Colors.black,
style: TextStyle(
color: Colors.black,
),
onSaved: (_input) {
setState(() {
urlController = _input!;
_url = _input!;
});
},
validator: (_input) {
return null;
return _input!.isNotEmpty ? null : "nnnns";
},
decoration: const InputDecoration(
decoration: InputDecoration(
hintText: "hosting URL",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white)
),
borderSide: BorderSide(color: Colors.white)),
prefixIcon: Padding(
padding: EdgeInsets.all(defaultPadding),
padding: EdgeInsets.all(15.0),
child: Icon(Icons.computer_outlined),
),
),
),
TextFormField(
initialValue: "fluttertest",
enabled: enableInput,
keyboardType: TextInputType.emailAddress,
textInputAction: TextInputAction.next,
cursorColor: Colors.black,
style: TextStyle(
color: Colors.black,
),
onSaved: (_input) {
if (_input == null) {}
setState(() {
usernameController = _input!;
_username = _input!;
});
},
decoration: const InputDecoration(
validator: (_input) {
return _input!.isNotEmpty ? null : "nnnns";
},
decoration: InputDecoration(
hintText: "username",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white)),
prefixIcon: Padding(
padding: EdgeInsets.all(defaultPadding),
padding: EdgeInsets.all(15.0),
child: Icon(Icons.person),
),
),
),
TextFormField(
initialValue: "9tWx8XAv545+ijnBxOrh",
enabled: enableInput,
keyboardType: TextInputType.visiblePassword,
textInputAction: TextInputAction.done,
cursorColor: Colors.black,
style: TextStyle(
color: Colors.black,
),
obscureText: obscureText,
onSaved: (_input) {
setState(() {
passwordController = _input!;
_password = _input!;
});
},
cursorColor: primaryColor,
validator: (_input) {
return _input!.isNotEmpty ? null : "nnnns";
},
decoration: InputDecoration(
hintText: "Password",
hintText: "password",
focusedBorder: UnderlineInputBorder(
borderSide: BorderSide(color: Colors.white)),
prefixIcon: const Padding(
padding: EdgeInsets.all(defaultPadding),
padding: EdgeInsets.all(15.0),
child: Icon(Icons.lock),
),
suffixIcon: IconButton(
@@ -146,51 +176,45 @@ class _LoginScreenState extends State<LoginScreen> {
mainAxisAlignment: MainAxisAlignment.start,
children: [
Checkbox(
value: checked,
value: savePassword,
onChanged: (value) {
setState(() {
checked = value!;
savePassword = value!;
});
}),
const Text('Save Password'),
const Text('save password'),
],
);
}
Widget _signInField() {
return ElevatedButton(
onPressed: () {
login(urlController.value.text, usernameController.value.text,
passwordController.value.text)
.then((
status,
) {
if (status.startsWith('successful')) {
Navigator.push(
context,
MaterialPageRoute(
builder: (context) {
return const WelcomeScreen();
},
Widget _signInButton(BuildContext _context) {
return _provider.status == AuthStatus.authenticating
? Align(
alignment: Alignment.center,
child: CircularProgressIndicator(),
)
: SizedBox(
height: _deviceHeight * 0.06,
width: _deviceWidth * 0.30,
child: MaterialButton(
onPressed: () async {
enableInput = !enableInput;
if (_formKey.currentState!.validate()) {
//Login User
await _provider.login(
_url, _username, _password, savePassword);
}
enableInput = !enableInput;
},
color: Colors.grey,
child: Text(
'login',
style: TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
),
),
);
SnackBarService.instance.showSnackBarSuccess(status);
} else {
SnackBarService.instance.showSnackBarError(status);
}
});
},
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
"sign in".toUpperCase(),
),
const SizedBox(width: 10),
const Icon(Icons.login)
],
),
);
),
);
}
}
-27
View File
@@ -1,27 +0,0 @@
import 'package:flutter/material.dart';
class WelcomeScreen extends StatefulWidget {
const WelcomeScreen({super.key});
@override
State<WelcomeScreen> createState() => _WelcomeScreenState();
}
class _WelcomeScreenState extends State<WelcomeScreen> {
@override
void initState() {
super.initState();
}
@override
Widget build(BuildContext context) {
return const Scaffold(
body: Column(
children: [
],
),
);
}
}