[ui] UI theme should match repertory blue #61
All checks were successful
BlockStorage/repertory/pipeline/head This commit looks good

This commit is contained in:
2025-09-04 10:59:36 -05:00
parent 58451858fd
commit d1183c00af
9 changed files with 750 additions and 944 deletions

View File

@@ -1,11 +1,16 @@
// helpers.dart // helpers.dart
import 'dart:ui';
import 'package:convert/convert.dart'; import 'package:convert/convert.dart';
import 'package:collection/collection.dart'; import 'package:collection/collection.dart';
import 'package:flutter/foundation.dart'; import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:repertory/constants.dart' as constants; import 'package:repertory/constants.dart' as constants;
import 'package:repertory/models/auth.dart'; import 'package:repertory/models/auth.dart';
import 'package:repertory/models/settings.dart';
import 'package:repertory/widgets/aurora_sweep.dart';
import 'package:sodium_libs/sodium_libs.dart' show SecureKey, StringX; import 'package:sodium_libs/sodium_libs.dart' show SecureKey, StringX;
Future doShowDialog(BuildContext context, Widget child) => showDialog( Future doShowDialog(BuildContext context, Widget child) => showDialog(
@@ -426,3 +431,56 @@ Future<String?> editMountLocation(
), ),
); );
} }
Scaffold createCommonScaffold(Widget child, {Widget? floatingActionButton}) =>
Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: constants.gradientColors,
),
),
),
Consumer<Settings>(
builder: (_, settings, _) =>
AuroraSweep(enabled: settings.enableAnimations),
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(color: Colors.black.withValues(alpha: 0.06)),
),
),
child,
],
),
),
floatingActionButton: floatingActionButton,
);
InputDecoration createCommonDecoration(
ColorScheme colorScheme,
String label,
IconData icon,
) => InputDecoration(
labelText: label,
prefixIcon: Icon(icon),
filled: true,
fillColor: colorScheme.primary.withValues(alpha: constants.primaryAlpha),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
borderSide: BorderSide(color: colorScheme.primary, width: 2),
),
contentPadding: const EdgeInsets.all(constants.paddingSmall),
);

View File

@@ -9,9 +9,7 @@ import 'package:repertory/helpers.dart';
import 'package:repertory/models/auth.dart'; import 'package:repertory/models/auth.dart';
import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount.dart';
import 'package:repertory/models/mount_list.dart'; import 'package:repertory/models/mount_list.dart';
import 'package:repertory/models/settings.dart';
import 'package:repertory/types/mount_config.dart'; import 'package:repertory/types/mount_config.dart';
import 'package:repertory/widgets/aurora_sweep.dart';
import 'package:repertory/widgets/mount_settings.dart'; import 'package:repertory/widgets/mount_settings.dart';
class AddMountScreen extends StatefulWidget { class AddMountScreen extends StatefulWidget {
@@ -46,15 +44,149 @@ class _AddMountScreenState extends State<AddMountScreen> {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
Widget glassTile({required Widget child, EdgeInsets? padding}) { return createCommonScaffold(
return Container( Padding(
decoration: BoxDecoration( padding: const EdgeInsets.all(constants.padding),
color: scheme.primary.withValues(alpha: constants.primaryAlpha), child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Header
Row(
children: [
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(constants.borderRadius), borderRadius: BorderRadius.circular(constants.borderRadius),
onTap: () => Navigator.of(context).pop(),
child: Ink(
width: 40,
height: 40,
decoration: BoxDecoration(
color: scheme.surface.withValues(alpha: 0.40),
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues(alpha: 0.08), color: scheme.outlineVariant.withValues(alpha: 0.08),
width: 1, width: 1,
), ),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.22),
blurRadius: constants.borderRadius,
offset: Offset(0, constants.borderRadius),
),
],
),
child: const Icon(Icons.arrow_back),
),
),
),
const SizedBox(width: constants.padding),
Expanded(
child: Text(
widget.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
letterSpacing: 0.2,
color: scheme.onSurface.withValues(alpha: 0.96),
),
),
),
const SizedBox(width: constants.padding),
ClipRRect(
borderRadius: BorderRadius.circular(constants.borderRadius),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: constants.borderRadius,
sigmaY: constants.borderRadius,
),
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
color: scheme.surface.withValues(alpha: 0.40),
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
border: Border.all(
color: scheme.outlineVariant.withValues(alpha: 0.08),
width: 1,
),
),
child: Consumer<Auth>(
builder: (context, auth, _) => IconButton(
tooltip: 'Log out',
icon: const Icon(Icons.logout),
onPressed: auth.logoff,
),
),
),
),
),
],
),
const SizedBox(height: constants.padding),
DropdownButtonFormField<String>(
initialValue: _mountType.isEmpty ? null : _mountType,
decoration: createCommonDecoration(
scheme,
'Provider Type',
Icons.cloud_outlined,
),
isExpanded: true,
items: constants.providerTypeList
.map<DropdownMenuItem<String>>(
(item) => DropdownMenuItem<String>(
value: item,
child: Text(item),
),
)
.toList(),
onChanged: (mountType) => _handleChange(
Provider.of<Auth>(context, listen: false),
mountType ?? '',
),
),
// Configuration name (only when not Remote)
if (_mountType.isNotEmpty && _mountType != 'Remote') ...[
const SizedBox(height: constants.padding),
TextField(
autofocus: true,
controller: _mountNameController,
keyboardType: TextInputType.text,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r'\s')),
],
onChanged: (_) => _handleChange(
Provider.of<Auth>(context, listen: false),
_mountType,
),
decoration: createCommonDecoration(
scheme,
'Configuration Name',
Icons.drive_file_rename_outline,
).copyWith(hintText: 'Enter a unique name'),
),
],
// Settings + Actions
if (_mount != null) ...[
const SizedBox(height: constants.padding),
Expanded(
child: Container(
decoration: BoxDecoration(
color: scheme.primary.withValues(
alpha: constants.primaryAlpha,
),
borderRadius: BorderRadius.circular(constants.borderRadius),
border: Border.all(
color: scheme.outlineVariant.withValues(alpha: 0.06),
width: 1,
),
gradient: const LinearGradient( gradient: const LinearGradient(
begin: Alignment.topCenter, begin: Alignment.topCenter,
end: Alignment.bottomCenter, end: Alignment.bottomCenter,
@@ -70,220 +202,6 @@ class _AddMountScreenState extends State<AddMountScreen> {
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular(constants.borderRadius), borderRadius: BorderRadius.circular(constants.borderRadius),
child: Padding(
padding: padding ?? const EdgeInsets.all(constants.padding),
child: child,
),
),
);
}
return Scaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: constants.gradientColors,
),
),
),
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(color: Colors.black.withValues(alpha: 0.06)),
),
),
Padding(
padding: const EdgeInsets.all(constants.padding),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Row(
children: [
Material(
color: Colors.transparent,
child: InkWell(
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
onTap: () {
Navigator.of(context).pop();
},
child: Ink(
width: 40,
height: 40,
decoration: BoxDecoration(
color: scheme.surface.withValues(alpha: 0.40),
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
border: Border.all(
color: scheme.outlineVariant.withValues(
alpha: 0.08,
),
width: 1,
),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.22),
blurRadius: constants.borderRadius,
offset: Offset(0, constants.borderRadius),
),
],
),
child: const Icon(Icons.arrow_back),
),
),
),
const SizedBox(width: constants.padding),
Expanded(
child: Text(
widget.title,
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: textTheme.headlineSmall?.copyWith(
fontWeight: FontWeight.w700,
letterSpacing: 0.2,
color: scheme.onSurface.withValues(alpha: 0.96),
),
),
),
const SizedBox(width: constants.padding),
ClipRRect(
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
child: BackdropFilter(
filter: ImageFilter.blur(
sigmaX: constants.borderRadius,
sigmaY: constants.borderRadius,
),
child: Container(
height: 40,
padding: const EdgeInsets.symmetric(horizontal: 6),
decoration: BoxDecoration(
color: scheme.surface.withValues(alpha: 0.40),
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
border: Border.all(
color: scheme.outlineVariant.withValues(
alpha: 0.08,
),
width: 1,
),
),
child: Consumer<Auth>(
builder: (context, auth, _) {
return IconButton(
tooltip: 'Log out',
icon: const Icon(Icons.logout),
onPressed: () {
auth.logoff();
},
);
},
),
),
),
),
],
),
const SizedBox(height: constants.padding),
glassTile(
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Text(
'Provider Type',
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(width: constants.padding),
DropdownButton<String>(
value: _mountType.isEmpty ? null : _mountType,
autofocus: true,
underline: const SizedBox.shrink(),
onChanged: (mountType) {
_handleChange(
Provider.of<Auth>(context, listen: false),
mountType ?? '',
);
},
items: constants.providerTypeList
.map<DropdownMenuItem<String>>((item) {
return DropdownMenuItem<String>(
value: item,
child: Text(item),
);
})
.toList(),
),
],
),
),
if (_mountType.isNotEmpty && _mountType != 'Remote') ...[
const SizedBox(height: constants.padding),
glassTile(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Configuration Name',
style: textTheme.titleMedium?.copyWith(
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 8),
TextField(
autofocus: true,
controller: _mountNameController,
keyboardType: TextInputType.text,
inputFormatters: [
FilteringTextInputFormatter.deny(RegExp(r'\s')),
],
onChanged: (_) => _handleChange(
Provider.of<Auth>(context, listen: false),
_mountType,
),
decoration: InputDecoration(
hintText: 'Enter a unique name',
filled: true,
fillColor: scheme.surface.withValues(alpha: 0.30),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(
constants.borderRadius,
),
borderSide: BorderSide.none,
),
contentPadding: const EdgeInsets.symmetric(
horizontal: 12,
vertical: 12,
),
),
),
],
),
),
],
if (_mount != null) ...[
const SizedBox(height: constants.padding),
Expanded(
child: glassTile(
padding: EdgeInsets.zero,
child: Padding( child: Padding(
padding: const EdgeInsets.all(constants.padding), padding: const EdgeInsets.all(constants.padding),
child: MountSettingsWidget( child: MountSettingsWidget(
@@ -295,6 +213,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
), ),
), ),
), ),
),
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
Row( Row(
children: [ children: [
@@ -303,9 +222,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
label: const Text('Test'), label: const Text('Test'),
icon: const Icon(Icons.check), icon: const Icon(Icons.check),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: scheme.primary.withValues( backgroundColor: scheme.primary.withValues(alpha: 0.18),
alpha: 0.18,
),
foregroundColor: scheme.primary, foregroundColor: scheme.primary,
elevation: 0, elevation: 0,
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
@@ -332,9 +249,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
backgroundColor: scheme.primary, backgroundColor: scheme.primary,
foregroundColor: scheme.onPrimary, foregroundColor: scheme.onPrimary,
elevation: 8, elevation: 8,
shadowColor: scheme.primary.withValues( shadowColor: scheme.primary.withValues(alpha: 0.45),
alpha: 0.45,
),
shape: RoundedRectangleBorder( shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(
constants.borderRadius, constants.borderRadius,
@@ -348,10 +263,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
); );
List<String> failed = []; List<String> failed = [];
if (!validateSettings( if (!validateSettings(_settings[_mountType]!, failed)) {
_settings[_mountType]!,
failed,
)) {
for (var key in failed) { for (var key in failed) {
displayErrorMessage( displayErrorMessage(
context, context,
@@ -374,10 +286,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
final bucket = final bucket =
_settings[_mountType]!["${_mountType}Config"]["Bucket"] _settings[_mountType]!["${_mountType}Config"]["Bucket"]
as String; as String;
if (mountList.hasBucketName( if (mountList.hasBucketName(_mountType, bucket)) {
_mountType,
bucket,
)) {
return displayErrorMessage( return displayErrorMessage(
context, context,
"Bucket '$bucket' already exists", "Bucket '$bucket' already exists",
@@ -393,9 +302,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
_settings[_mountType]!, _settings[_mountType]!,
); );
if (!success || !context.mounted) { if (!success || !context.mounted) return;
return;
}
Navigator.pop(context); Navigator.pop(context);
}, },
@@ -407,9 +314,6 @@ class _AddMountScreenState extends State<AddMountScreen> {
], ],
), ),
), ),
],
),
),
); );
} }
@@ -440,14 +344,10 @@ class _AddMountScreenState extends State<AddMountScreen> {
} }
Future<void> _handleProviderTest() async { Future<void> _handleProviderTest() async {
if (_mount == null) { if (_mount == null) return;
return;
}
final success = await _mount!.test(); final success = await _mount!.test();
if (!mounted) { if (!mounted) return;
return;
}
displayErrorMessage( displayErrorMessage(
context, context,
@@ -457,9 +357,7 @@ class _AddMountScreenState extends State<AddMountScreen> {
@override @override
void setState(VoidCallback fn) { void setState(VoidCallback fn) {
if (!mounted) { if (!mounted) return;
return;
}
super.setState(fn); super.setState(fn);
} }
} }

View File

@@ -4,6 +4,7 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:repertory/constants.dart' as constants; import 'package:repertory/constants.dart' as constants;
import 'package:repertory/helpers.dart';
import 'package:repertory/models/auth.dart'; import 'package:repertory/models/auth.dart';
import 'package:repertory/models/settings.dart'; import 'package:repertory/models/settings.dart';
import 'package:repertory/widgets/aurora_sweep.dart'; import 'package:repertory/widgets/aurora_sweep.dart';
@@ -35,22 +36,6 @@ class _AuthScreenState extends State<AuthScreen> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
InputDecoration decoration(String label, IconData icon) => InputDecoration(
labelText: label,
prefixIcon: Icon(icon),
filled: true,
fillColor: scheme.primary.withValues(alpha: constants.primaryAlpha),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
borderSide: BorderSide.none,
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
borderSide: BorderSide(color: scheme.primary, width: 2),
),
contentPadding: const EdgeInsets.all(constants.paddingSmall),
);
Future<void> doLogin(Auth auth) async { Future<void> doLogin(Auth auth) async {
if (!_enabled) { if (!_enabled) {
return; return;
@@ -99,9 +84,8 @@ class _AuthScreenState extends State<AuthScreen> {
), ),
), ),
Consumer<Settings>( Consumer<Settings>(
builder: (_, settings, _) { builder: (_, settings, _) =>
return AuroraSweep(enabled: settings.enableAnimations); AuroraSweep(enabled: settings.enableAnimations),
},
), ),
Positioned.fill( Positioned.fill(
child: BackdropFilter( child: BackdropFilter(
@@ -246,7 +230,8 @@ class _AuthScreenState extends State<AuthScreen> {
autofocus: true, autofocus: true,
controller: _userController, controller: _userController,
textInputAction: TextInputAction.next, textInputAction: TextInputAction.next,
decoration: decoration( decoration: createCommonDecoration(
scheme,
'Username', 'Username',
Icons.person, Icons.person,
), ),
@@ -267,7 +252,8 @@ class _AuthScreenState extends State<AuthScreen> {
obscureText: _obscure, obscureText: _obscure,
textInputAction: TextInputAction.go, textInputAction: TextInputAction.go,
decoration: decoration:
decoration( createCommonDecoration(
scheme,
'Password', 'Password',
Icons.lock, Icons.lock,
).copyWith( ).copyWith(

View File

@@ -1,15 +1,13 @@
// edit_mount_screen.dart // edit_mount_screen.dart
import 'dart:convert'; import 'dart:convert';
import 'dart:ui'; import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:repertory/constants.dart' as constants; import 'package:repertory/constants.dart' as constants;
import 'package:repertory/helpers.dart';
import 'package:repertory/models/auth.dart'; import 'package:repertory/models/auth.dart';
import 'package:repertory/models/mount.dart'; import 'package:repertory/models/mount.dart';
import 'package:repertory/models/settings.dart';
import 'package:repertory/widgets/aurora_sweep.dart';
import 'package:repertory/widgets/mount_settings.dart'; import 'package:repertory/widgets/mount_settings.dart';
class EditMountScreen extends StatefulWidget { class EditMountScreen extends StatefulWidget {
@@ -29,51 +27,20 @@ class _EditMountScreenState extends State<EditMountScreen> {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
return Scaffold( return createCommonScaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: constants.gradientColors,
),
),
),
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(color: Colors.black.withValues(alpha: 0.06)),
),
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: constants.padding),
horizontal: constants.padding,
),
child: Row( child: Row(
children: [ children: [
Material( Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius, onTap: () => Navigator.of(context).pop(),
),
onTap: () {
Navigator.of(context).pop();
},
child: Ink( child: Ink(
width: 40, width: 40,
height: 40, height: 40,
@@ -83,9 +50,7 @@ class _EditMountScreenState extends State<EditMountScreen> {
constants.borderRadius, constants.borderRadius,
), ),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.08),
alpha: 0.08,
),
width: 1, width: 1,
), ),
boxShadow: [ boxShadow: [
@@ -115,9 +80,7 @@ class _EditMountScreenState extends State<EditMountScreen> {
), ),
const SizedBox(width: constants.padding), const SizedBox(width: constants.padding),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
child: BackdropFilter( child: BackdropFilter(
filter: ImageFilter.blur( filter: ImageFilter.blur(
sigmaX: constants.borderRadius, sigmaX: constants.borderRadius,
@@ -132,9 +95,7 @@ class _EditMountScreenState extends State<EditMountScreen> {
constants.borderRadius, constants.borderRadius,
), ),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.08),
alpha: 0.08,
),
width: 1, width: 1,
), ),
), ),
@@ -144,9 +105,7 @@ class _EditMountScreenState extends State<EditMountScreen> {
Text( Text(
"Advanced", "Advanced",
style: textTheme.labelLarge?.copyWith( style: textTheme.labelLarge?.copyWith(
color: scheme.onSurface.withValues( color: scheme.onSurface.withValues(alpha: 0.90),
alpha: 0.90,
),
), ),
), ),
const SizedBox(width: 6), const SizedBox(width: 6),
@@ -161,14 +120,9 @@ class _EditMountScreenState extends State<EditMountScreen> {
), ),
color: _showAdvanced color: _showAdvanced
? scheme.primary ? scheme.primary
: scheme.onSurface.withValues( : scheme.onSurface.withValues(alpha: 0.70),
alpha: 0.70, onPressed: () =>
), setState(() => _showAdvanced = !_showAdvanced),
onPressed: () {
setState(() {
_showAdvanced = !_showAdvanced;
});
},
), ),
], ],
), ),
@@ -177,9 +131,7 @@ class _EditMountScreenState extends State<EditMountScreen> {
), ),
const SizedBox(width: constants.padding), const SizedBox(width: constants.padding),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
child: BackdropFilter( child: BackdropFilter(
filter: ImageFilter.blur( filter: ImageFilter.blur(
sigmaX: constants.borderRadius, sigmaX: constants.borderRadius,
@@ -194,22 +146,16 @@ class _EditMountScreenState extends State<EditMountScreen> {
constants.borderRadius, constants.borderRadius,
), ),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.08),
alpha: 0.08,
),
width: 1, width: 1,
), ),
), ),
child: Consumer<Auth>( child: Consumer<Auth>(
builder: (context, auth, _) { builder: (context, auth, _) => IconButton(
return IconButton(
tooltip: 'Log out', tooltip: 'Log out',
icon: const Icon(Icons.logout), icon: const Icon(Icons.logout),
onPressed: () { onPressed: auth.logoff,
auth.logoff(); ),
},
);
},
), ),
), ),
), ),
@@ -227,14 +173,12 @@ class _EditMountScreenState extends State<EditMountScreen> {
color: Colors.transparent, color: Colors.transparent,
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: scheme.surface.withValues(alpha: 0.40), color: scheme.primary.withValues(
borderRadius: BorderRadius.circular( alpha: constants.primaryAlpha,
constants.borderRadius,
), ),
borderRadius: BorderRadius.circular(constants.borderRadius),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.06),
alpha: 0.06,
),
width: 1, width: 1,
), ),
gradient: const LinearGradient( gradient: const LinearGradient(
@@ -251,9 +195,9 @@ class _EditMountScreenState extends State<EditMountScreen> {
], ],
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius, child: Padding(
), padding: const EdgeInsets.all(constants.padding),
child: MountSettingsWidget( child: MountSettingsWidget(
mount: widget.mount, mount: widget.mount,
settings: jsonDecode( settings: jsonDecode(
@@ -266,21 +210,17 @@ class _EditMountScreenState extends State<EditMountScreen> {
), ),
), ),
), ),
),
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
], ],
), ),
],
),
),
); );
} }
@override @override
void setState(VoidCallback fn) { void setState(VoidCallback fn) {
if (!mounted) { if (!mounted) return;
return;
}
super.setState(fn); super.setState(fn);
} }
} }

View File

@@ -9,8 +9,6 @@ import 'package:provider/provider.dart';
import 'package:repertory/constants.dart' as constants; import 'package:repertory/constants.dart' as constants;
import 'package:repertory/helpers.dart'; import 'package:repertory/helpers.dart';
import 'package:repertory/models/auth.dart'; import 'package:repertory/models/auth.dart';
import 'package:repertory/models/settings.dart';
import 'package:repertory/widgets/aurora_sweep.dart';
import 'package:repertory/widgets/ui_settings.dart'; import 'package:repertory/widgets/ui_settings.dart';
class EditSettingsScreen extends StatefulWidget { class EditSettingsScreen extends StatefulWidget {
@@ -27,48 +25,19 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
return Scaffold( return createCommonScaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: constants.gradientColors,
),
),
),
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(color: Colors.black.withValues(alpha: 0.06)),
),
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: constants.padding),
horizontal: constants.padding,
),
child: Row( child: Row(
children: [ children: [
Material( Material(
color: Colors.transparent, color: Colors.transparent,
child: InkWell( child: InkWell(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
onTap: () { onTap: () {
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
@@ -81,9 +50,7 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
constants.borderRadius, constants.borderRadius,
), ),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.08),
alpha: 0.08,
),
width: 1, width: 1,
), ),
boxShadow: [ boxShadow: [
@@ -113,9 +80,7 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
), ),
const SizedBox(width: constants.padding), const SizedBox(width: constants.padding),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
child: BackdropFilter( child: BackdropFilter(
filter: ImageFilter.blur( filter: ImageFilter.blur(
sigmaX: constants.borderRadius, sigmaX: constants.borderRadius,
@@ -130,9 +95,7 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
constants.borderRadius, constants.borderRadius,
), ),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.08),
alpha: 0.08,
),
width: 1, width: 1,
), ),
), ),
@@ -164,13 +127,9 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
child: Container( child: Container(
decoration: BoxDecoration( decoration: BoxDecoration(
color: scheme.surface.withValues(alpha: 0.40), color: scheme.surface.withValues(alpha: 0.40),
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.06),
alpha: 0.06,
),
width: 1, width: 1,
), ),
gradient: const LinearGradient( gradient: const LinearGradient(
@@ -187,9 +146,7 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
], ],
), ),
child: ClipRRect( child: ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
child: FutureBuilder<Map<String, dynamic>>( child: FutureBuilder<Map<String, dynamic>>(
future: _grabSettings(), future: _grabSettings(),
initialData: const <String, dynamic>{}, initialData: const <String, dynamic>{},
@@ -217,9 +174,6 @@ class _EditSettingsScreenState extends State<EditSettingsScreen> {
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
], ],
), ),
],
),
),
); );
} }

View File

@@ -4,10 +4,10 @@ import 'dart:ui';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:provider/provider.dart'; import 'package:provider/provider.dart';
import 'package:repertory/constants.dart' as constants; import 'package:repertory/constants.dart' as constants;
import 'package:repertory/helpers.dart';
import 'package:repertory/models/auth.dart'; import 'package:repertory/models/auth.dart';
import 'package:repertory/models/settings.dart'; import 'package:repertory/models/settings.dart';
import 'package:repertory/widgets/mount_list_widget.dart'; import 'package:repertory/widgets/mount_list_widget.dart';
import 'package:repertory/widgets/aurora_sweep.dart';
class HomeScreen extends StatefulWidget { class HomeScreen extends StatefulWidget {
final String title; final String title;
@@ -23,45 +23,18 @@ class _HomeScreeState extends State<HomeScreen> {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
final textTheme = Theme.of(context).textTheme; final textTheme = Theme.of(context).textTheme;
return Scaffold( return createCommonScaffold(
body: SafeArea(
child: Stack(
children: [
Container(
width: double.infinity,
height: double.infinity,
decoration: const BoxDecoration(
gradient: LinearGradient(
begin: Alignment.topLeft,
end: Alignment.bottomRight,
colors: constants.gradientColors,
),
),
),
Consumer<Settings>(
builder: (_, settings, _) {
return AuroraSweep(enabled: settings.enableAnimations);
},
),
Positioned.fill(
child: BackdropFilter(
filter: ImageFilter.blur(sigmaX: 6, sigmaY: 6),
child: Container(color: Colors.black.withValues(alpha: 0.06)),
),
),
Column( Column(
crossAxisAlignment: CrossAxisAlignment.stretch, crossAxisAlignment: CrossAxisAlignment.stretch,
children: [ children: [
const SizedBox(height: constants.padding), const SizedBox(height: constants.padding),
Padding( Padding(
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(horizontal: constants.padding),
horizontal: constants.padding,
),
child: Row( child: Row(
children: [ children: [
SizedBox( SizedBox(
width: 48, width: 40,
height: 48, height: 40,
child: Image.asset( child: Image.asset(
'assets/images/repertory.png', 'assets/images/repertory.png',
fit: BoxFit.contain, fit: BoxFit.contain,
@@ -69,7 +42,7 @@ class _HomeScreeState extends State<HomeScreen> {
return Icon( return Icon(
Icons.folder, Icons.folder,
color: scheme.primary, color: scheme.primary,
size: 40, size: 32,
); );
}, },
), ),
@@ -89,9 +62,7 @@ class _HomeScreeState extends State<HomeScreen> {
), ),
const SizedBox(width: constants.padding), const SizedBox(width: constants.padding),
ClipRRect( ClipRRect(
borderRadius: BorderRadius.circular( borderRadius: BorderRadius.circular(constants.borderRadius),
constants.borderRadius,
),
child: BackdropFilter( child: BackdropFilter(
filter: ImageFilter.blur( filter: ImageFilter.blur(
sigmaX: constants.borderRadius, sigmaX: constants.borderRadius,
@@ -106,9 +77,7 @@ class _HomeScreeState extends State<HomeScreen> {
constants.borderRadius, constants.borderRadius,
), ),
border: Border.all( border: Border.all(
color: scheme.outlineVariant.withValues( color: scheme.outlineVariant.withValues(alpha: 0.08),
alpha: 0.08,
),
width: 1, width: 1,
), ),
), ),
@@ -125,12 +94,9 @@ class _HomeScreeState extends State<HomeScreen> {
), ),
color: settings.autoStart color: settings.autoStart
? scheme.primary ? scheme.primary
: scheme.onSurface.withValues( : scheme.onSurface.withValues(alpha: 0.70),
alpha: 0.70, onPressed: () =>
), settings.setAutoStart(!settings.autoStart),
onPressed: () => settings.setAutoStart(
!settings.autoStart,
),
); );
}, },
), ),
@@ -171,9 +137,6 @@ class _HomeScreeState extends State<HomeScreen> {
), ),
], ],
), ),
],
),
),
floatingActionButton: Padding( floatingActionButton: Padding(
padding: const EdgeInsets.all(constants.padding), padding: const EdgeInsets.all(constants.padding),
child: Hero( child: Hero(

View File

@@ -39,10 +39,11 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
// Theme SettingsList to your glass aesthetic (consistent with UISettingsWidget)
final theme = SettingsThemeData( final theme = SettingsThemeData(
settingsListBackground: Colors.transparent, settingsListBackground: Colors.transparent,
settingsSectionBackground: scheme.surface.withValues(alpha: 0.30), settingsSectionBackground: scheme.primary.withValues(
alpha: constants.primaryAlpha,
),
titleTextColor: scheme.onSurface.withValues(alpha: 0.96), titleTextColor: scheme.onSurface.withValues(alpha: 0.96),
trailingTextColor: scheme.onSurface.withValues(alpha: 0.80), trailingTextColor: scheme.onSurface.withValues(alpha: 0.80),
tileDescriptionTextColor: scheme.onSurface.withValues(alpha: 0.68), tileDescriptionTextColor: scheme.onSurface.withValues(alpha: 0.68),
@@ -394,6 +395,10 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
} }
}); });
const sectionMargin = EdgeInsetsDirectional.symmetric(
vertical: constants.padding / 2,
);
return SettingsList( return SettingsList(
shrinkWrap: false, shrinkWrap: false,
platform: DevicePlatform.device, platform: DevicePlatform.device,
@@ -402,38 +407,48 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
sections: [ sections: [
if (encryptConfigSettings.isNotEmpty) if (encryptConfigSettings.isNotEmpty)
SettingsSection( SettingsSection(
margin: sectionMargin,
title: const Text('Encrypt Config'), title: const Text('Encrypt Config'),
tiles: encryptConfigSettings, tiles: encryptConfigSettings,
), ),
if (hostConfigSettings.isNotEmpty) if (hostConfigSettings.isNotEmpty)
SettingsSection( SettingsSection(
margin: sectionMargin,
title: const Text('Host Config'), title: const Text('Host Config'),
tiles: hostConfigSettings, tiles: hostConfigSettings,
), ),
if (remoteConfigSettings.isNotEmpty) if (remoteConfigSettings.isNotEmpty)
SettingsSection( SettingsSection(
margin: sectionMargin,
title: const Text('Remote Config'), title: const Text('Remote Config'),
tiles: remoteConfigSettings, tiles: remoteConfigSettings,
), ),
if (s3ConfigSettings.isNotEmpty) if (s3ConfigSettings.isNotEmpty)
SettingsSection( SettingsSection(
margin: sectionMargin,
title: const Text('S3 Config'), title: const Text('S3 Config'),
tiles: s3ConfigSettings, tiles: s3ConfigSettings,
), ),
if (siaConfigSettings.isNotEmpty) if (siaConfigSettings.isNotEmpty)
SettingsSection( SettingsSection(
margin: sectionMargin,
title: const Text('Sia Config'), title: const Text('Sia Config'),
tiles: siaConfigSettings, tiles: siaConfigSettings,
), ),
if (remoteMountSettings.isNotEmpty) if (remoteMountSettings.isNotEmpty)
SettingsSection( SettingsSection(
margin: sectionMargin,
title: const Text('Remote Mount'), title: const Text('Remote Mount'),
tiles: (widget.settings['RemoteMount']['Enable'] as bool) tiles: (widget.settings['RemoteMount']['Enable'] as bool)
? remoteMountSettings ? remoteMountSettings
: [remoteMountSettings[0]], : [remoteMountSettings[0]],
), ),
if (commonSettings.isNotEmpty) if (commonSettings.isNotEmpty)
SettingsSection(title: const Text('Settings'), tiles: commonSettings), SettingsSection(
margin: sectionMargin,
title: const Text('Settings'),
tiles: commonSettings,
),
], ],
); );
} }
@@ -1017,24 +1032,18 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
}); });
return; return;
} }
mount.setValue(key, value); mount.setValue(key, value);
}); });
}); });
} }
} }
super.dispose(); super.dispose();
} }
@override @override
void setState(VoidCallback fn) { void setState(VoidCallback fn) {
if (!mounted) { if (!mounted) return;
return; if (widget.onChanged != null) widget.onChanged!();
}
if (widget.onChanged != null) {
widget.onChanged!();
}
super.setState(fn); super.setState(fn);
} }
} }

View File

@@ -272,7 +272,6 @@ class _MountWidgetState extends State<MountWidget> {
return location; return location;
} }
// ignore: use_build_context_synchronously
return editMountLocation(context, await mount.getAvailableLocations()); return editMountLocation(context, await mount.getAvailableLocations());
} }

View File

@@ -39,18 +39,17 @@ class _UISettingsWidgetState extends State<UISettingsWidget> {
Widget build(BuildContext context) { Widget build(BuildContext context) {
final scheme = Theme.of(context).colorScheme; final scheme = Theme.of(context).colorScheme;
// Theme the SettingsList to align with your glass look
final theme = SettingsThemeData( final theme = SettingsThemeData(
settingsListBackground: Colors.transparent, settingsListBackground: Colors.transparent,
tileDescriptionTextColor: scheme.onSurface.withValues(alpha: 0.68), settingsSectionBackground: scheme.primary.withValues(
settingsSectionBackground: scheme.surface.withValues( alpha: constants.primaryAlpha,
alpha: 0.30, ),
), // slight glass base
tileHighlightColor: scheme.primary.withValues(alpha: 0.08),
titleTextColor: scheme.onSurface.withValues(alpha: 0.96), titleTextColor: scheme.onSurface.withValues(alpha: 0.96),
trailingTextColor: scheme.onSurface.withValues(alpha: 0.80), trailingTextColor: scheme.onSurface.withValues(alpha: 0.80),
tileDescriptionTextColor: scheme.onSurface.withValues(alpha: 0.68),
leadingIconsColor: scheme.onSurface.withValues(alpha: 0.90), leadingIconsColor: scheme.onSurface.withValues(alpha: 0.90),
dividerColor: scheme.outlineVariant.withValues(alpha: 0.10), dividerColor: scheme.outlineVariant.withValues(alpha: 0.10),
tileHighlightColor: scheme.primary.withValues(alpha: 0.08),
); );
List<SettingsTile> commonSettings = []; List<SettingsTile> commonSettings = [];