theme changes
This commit is contained in:
1 parent
539ad84cf3
commit
a8715c8ec4
21 files changed
+487
-248
No files matched your search
@@ -11,6 +11,8 @@
|
||||
### Changes from v2.0.7-release
|
||||
|
||||
* Fixed Windows setup icon location
|
||||
* Upgraded `cpp-build-system` to v2.0.1
|
||||
* Updated theme colors and layout
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,49 +1,157 @@
|
||||
// constants.dart
|
||||
|
||||
import 'package:flutter/material.dart' show GlobalKey, NavigatorState, Color;
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:sodium_libs/sodium_libs.dart';
|
||||
|
||||
const accentBlue = Color(0xFF1050A0);
|
||||
const addMountTitle = 'Add New Mount';
|
||||
const appLogonTitle = 'Repertory Portal Login';
|
||||
const appSettingsTitle = 'Portal Settings';
|
||||
const appTitle = 'Repertory Management Portal';
|
||||
const borderRadius = 16.0;
|
||||
const borderRadiusSmall = borderRadius / 2.0;
|
||||
const borderRadiusTiny = borderRadiusSmall / 2.0;
|
||||
const boxShadowAlpha = 0.20;
|
||||
const databaseTypeList = ['rocksdb', 'sqlite'];
|
||||
const dialogAlpha = 0.95;
|
||||
const downloadTypeList = ['default', 'direct', 'ring_buffer'];
|
||||
const dropDownAlpha = 99.0;
|
||||
const eventLevelList = ['critical', 'error', 'warn', 'info', 'debug', 'trace'];
|
||||
const gradientColors = [Color(0xFF0A0F1F), Color(0xFF1B1C1F)];
|
||||
const gradientColors2 = [Color(0x07FFFFFF), Color(0x00000000)];
|
||||
const highlightAlpha = 0.10;
|
||||
const largeIconSize = 32.0;
|
||||
const loginIconSize = 54.0;
|
||||
const logonWidth = 420.0;
|
||||
const outlineAlpha = 0.15;
|
||||
const padding = 16.0;
|
||||
const paddingLarge = padding * 2.0;
|
||||
const paddingMedium = 12.0;
|
||||
const paddingSmall = padding / 2.0;
|
||||
const primaryAlpha = 0.12;
|
||||
const primarySurfaceAlpha = 92.0;
|
||||
const protocolTypeList = ['http', 'https'];
|
||||
const providerTypeList = ['Encrypt', 'Remote', 'S3', 'Sia'];
|
||||
const ringBufferSizeList = ['128', '256', '512', '1024', '2048'];
|
||||
const secondaryAlpha = 0.45;
|
||||
const secondarySurfaceAlpha = 70.0;
|
||||
const smallIconSize = 18.0;
|
||||
const surfaceContainerLowDark = Color(0xFF292A2D);
|
||||
const surfaceDark = Color(0xFF202124);
|
||||
// ---------------------------------------------------------------------------
|
||||
// App strings
|
||||
// Window/screen titles shown in the UI.
|
||||
// ---------------------------------------------------------------------------
|
||||
const addMountTitle = 'Add New Mount'; // Title for the add-mount dialog.
|
||||
const appLogonTitle = 'Repertory Portal Login'; // Title for the login screen.
|
||||
const appSettingsTitle = 'Portal Settings'; // Title for the settings dialog.
|
||||
const appTitle = 'Repertory Management Portal'; // App/home screen title.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Palette
|
||||
// Seed/accent blue, muted dark-theme primary, dark grey surfaces, error red.
|
||||
// ---------------------------------------------------------------------------
|
||||
const accentBlue = Color(0xFF1050A0); // Seed color the ColorScheme is built from.
|
||||
const primaryBlue = Color(0xFF6E95C9); // Muted primary accent used in dark mode.
|
||||
const surfaceDark = Color(0xFF1E2127); // Scaffold/base surface (blue-tinted grey).
|
||||
const surfaceContainerLowDark = Color(0xFF272B33); // Elevated surface (cards/dialogs).
|
||||
const errorRed = Color(0xFFD98A86); // Muted error red for validation/error text.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Gradients
|
||||
// Full-screen background and the subtle FAB sheen overlay.
|
||||
// ---------------------------------------------------------------------------
|
||||
const gradientColors = [Color(0xFF0A0F1F), Color(0xFF1B1C1F)]; // App background gradient.
|
||||
const gradientColors2 = [Color(0x07FFFFFF), Color(0x00000000)]; // Faint top-down FAB sheen.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Alpha tints
|
||||
// Translucency applied over primary/surface/outline colors.
|
||||
// ---------------------------------------------------------------------------
|
||||
const boxShadowAlpha = 0.20; // Opacity of drop shadows.
|
||||
const dialogTintAlpha = 0.06; // Accent tint blended onto dialog/dropdown surfaces.
|
||||
const highlightAlpha = 0.10; // Subtle highlight/border tint.
|
||||
const outlineAlpha = 0.15; // Outline/border and scrim tint.
|
||||
const primaryAlpha = 0.12; // Primary fill tint for cards/fields.
|
||||
const primarySurfaceAlpha = 92.0; // Near-opaque primary surface text/icon tint.
|
||||
const secondaryAlpha = 0.45; // Medium primary tint for buttons/toggles.
|
||||
const secondarySurfaceAlpha = 70.0; // Dimmer surface text tint (descriptions).
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Corner radii
|
||||
// Shared rounding scale for cards, buttons, and dialogs.
|
||||
// ---------------------------------------------------------------------------
|
||||
const borderRadius = 16.0; // Base corner radius.
|
||||
const borderRadiusSmall = borderRadius / 2.0; // Half radius for cards/fields (8).
|
||||
const borderRadiusTiny = borderRadiusSmall / 2.0; // Quarter radius for small chips (4).
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Border widths
|
||||
// Default hairline and the thicker focused-input border.
|
||||
// ---------------------------------------------------------------------------
|
||||
const borderWidth = 1.0; // Standard hairline border for bevels.
|
||||
const focusBorderWidth = 2.0; // Emphasized border for focused inputs.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Elevations
|
||||
// Card drop shadow and the stronger floating-element shadow.
|
||||
// ---------------------------------------------------------------------------
|
||||
const cardElevation = 4.0; // Subtle shadow for content cards.
|
||||
const fabElevation = 12.0; // Stronger shadow for the floating action button.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Spacing
|
||||
// Shared padding scale used across layouts.
|
||||
// ---------------------------------------------------------------------------
|
||||
const padding = 16.0; // Base padding/gap unit.
|
||||
const paddingLarge = padding * 2.0; // Double padding for larger gaps (32).
|
||||
const paddingMedium = 12.0; // Medium padding between base and small.
|
||||
const paddingSmall = padding / 2.0; // Half padding for tight gaps (8).
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Icon sizes
|
||||
// Shared scale for iconography.
|
||||
// ---------------------------------------------------------------------------
|
||||
const largeIconSize = 32.0; // Large icons (FAB, prominent actions).
|
||||
const loginIconSize = 54.0; // Oversized icon on the login screen.
|
||||
const smallIconSize = 18.0; // Small inline icons.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Icons
|
||||
// Shared iconography reused across the UI (actions, toggles, fields).
|
||||
// ---------------------------------------------------------------------------
|
||||
const iconAdd = Icons.add; // Add / create actions (FAB, add mount).
|
||||
const iconAutoMount = Icons.auto_mode; // Auto-mount indicator.
|
||||
const iconBack = Icons.arrow_back; // Back navigation.
|
||||
const iconClose = Icons.close; // Close/dismiss (dialogs).
|
||||
const iconDelete = Icons.delete; // Delete/remove actions.
|
||||
const iconEdit = Icons.edit; // Edit actions.
|
||||
const iconFolder = Icons.folder; // Folder/path and app logo fallback.
|
||||
const iconLock = Icons.lock; // Password/lock fields.
|
||||
const iconPassword = Icons.password; // Password-style settings.
|
||||
const iconRename = Icons.drive_file_rename_outline; // Rename/config-name field.
|
||||
const iconRemoteMount = Icons.lan; // Remote mount (networking).
|
||||
const iconSettings = Icons.settings; // Settings/gear actions.
|
||||
const iconTest = Icons.check; // Test/confirm actions.
|
||||
const iconToggleOff = Icons.toggle_off; // Toggle (off state).
|
||||
const iconToggleOn = Icons.toggle_on; // Toggle (on state).
|
||||
const iconUser = Icons.person; // User/username fields.
|
||||
const iconVisibility = Icons.visibility; // Reveal password.
|
||||
const iconVisibilityOff = Icons.visibility_off; // Hide password.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Control sizes
|
||||
// Fixed dimensions for buttons, framed controls, and the FAB.
|
||||
// ---------------------------------------------------------------------------
|
||||
const buttonHeight = 40.0; // Minimum height of primary action buttons.
|
||||
const fabInnerSize = 56.0; // Inner tappable square of the add-mount FAB.
|
||||
const framedControlSize = 46.0; // Bordered icon buttons on mount cards.
|
||||
const headerControlSize = 40.0; // App-bar logo/back box.
|
||||
const mountCardMinHeight = 120.0; // Minimum height of a mount list card.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Layout widths/factors
|
||||
// Fixed widths and viewport-fraction sizing.
|
||||
// ---------------------------------------------------------------------------
|
||||
const logonWidth = 420.0; // Fixed width of the login card.
|
||||
const snackBarWidthFactor = 0.50; // Snackbar width as a fraction of the viewport.
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Dialog sizing
|
||||
// Viewport-fraction width/height with absolute pixel clamps.
|
||||
// ---------------------------------------------------------------------------
|
||||
const dialogMinWidthFactor = 0.40; // Min dialog width as a viewport fraction.
|
||||
const dialogMaxWidthFactor = 0.70; // Max dialog width as a viewport fraction.
|
||||
const dialogMaxHeightFactor = 0.85; // Max dialog height before it scrolls.
|
||||
const dialogMinWidth = 360.0; // Absolute minimum dialog width (px).
|
||||
const dialogMaxWidth = 820.0; // Absolute maximum dialog width (px).
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Option lists
|
||||
// Selectable values for configuration dropdowns.
|
||||
// ---------------------------------------------------------------------------
|
||||
const databaseTypeList = ['rocksdb', 'sqlite']; // Database backend options.
|
||||
const downloadTypeList = ['default', 'direct', 'ring_buffer']; // Download strategy options.
|
||||
const eventLevelList = ['critical', 'error', 'warn', 'info', 'debug', 'trace']; // Log level options.
|
||||
const protocolTypeList = ['http', 'https']; // Connection protocol options.
|
||||
const providerTypeList = ['Encrypt', 'Remote', 'S3', 'Sia']; // Mount provider options.
|
||||
const ringBufferSizeList = ['128', '256', '512', '1024', '2048']; // Ring-buffer size options (MiB).
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Runtime globals
|
||||
// Navigator key and lazily-initialized libsodium instance.
|
||||
// ---------------------------------------------------------------------------
|
||||
// Enables navigation/context access from outside the widget tree.
|
||||
final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();
|
||||
|
||||
Sodium? _sodium;
|
||||
Sodium? _sodium; // Backing store for the libsodium instance.
|
||||
void setSodium(Sodium sodium) {
|
||||
_sodium = sodium;
|
||||
_sodium = sodium; // Set once during app initialization.
|
||||
}
|
||||
|
||||
Sodium get sodium => _sodium!;
|
||||
Sodium get sodium => _sodium!; // Access the initialized libsodium instance.
|
||||
@@ -18,10 +18,14 @@ Future doShowDialog(BuildContext context, Widget child) => showDialog(
|
||||
return Theme(
|
||||
data: theme.copyWith(
|
||||
dialogTheme: DialogThemeData(
|
||||
backgroundColor: darken(
|
||||
scheme.primary,
|
||||
0.95,
|
||||
).withValues(alpha: constants.dropDownAlpha),
|
||||
// Elevated surface: slightly lighter than the scaffold background
|
||||
// (surfaceDark) with a faint accent tint so the popup reads as part
|
||||
// of the dark theme rather than a near-black panel. The backdrop blur
|
||||
// and barrier scrim already provide separation, so keep it opaque.
|
||||
backgroundColor: Color.alphaBlend(
|
||||
scheme.primary.withValues(alpha: constants.dialogTintAlpha),
|
||||
constants.surfaceContainerLowDark,
|
||||
),
|
||||
surfaceTintColor: Colors.transparent,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadius),
|
||||
@@ -29,7 +33,7 @@ Future doShowDialog(BuildContext context, Widget child) => showDialog(
|
||||
color: scheme.outlineVariant.withValues(
|
||||
alpha: constants.outlineAlpha,
|
||||
),
|
||||
width: 1,
|
||||
width: constants.borderWidth,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -90,8 +94,8 @@ final Validator portIsValid = (value) {
|
||||
// ignore: prefer_function_declarations_over_variables
|
||||
final Validator trimNotEmptyValidator = (value) => value.trim().isNotEmpty;
|
||||
|
||||
createUriValidator<Validator>({host, port}) {
|
||||
return (value) =>
|
||||
Validator createUriValidator({Object? host, Object? port}) {
|
||||
return (String value) =>
|
||||
Uri.tryParse('http://${host ?? value}:${port ?? value}/') != null;
|
||||
}
|
||||
|
||||
@@ -448,14 +452,39 @@ InputDecoration createCommonDecoration(
|
||||
filled: filled,
|
||||
fillColor: colorScheme.primary.withValues(alpha: constants.primaryAlpha),
|
||||
hintText: hintText,
|
||||
// Keep label and prefix/suffix icons neutral so the error state does not
|
||||
// flood the whole field with red; the error signal stays on the border and
|
||||
// helper text only.
|
||||
labelStyle: TextStyle(color: colorScheme.onSurface),
|
||||
floatingLabelStyle: TextStyle(color: colorScheme.onSurface),
|
||||
prefixIconColor: colorScheme.onSurface,
|
||||
suffixIconColor: colorScheme.onSurface,
|
||||
border: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
|
||||
borderSide: BorderSide.none,
|
||||
),
|
||||
focusedBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
|
||||
borderSide: BorderSide(color: colorScheme.primary, width: 2),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.primary,
|
||||
width: constants.focusBorderWidth,
|
||||
),
|
||||
),
|
||||
errorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.error,
|
||||
width: constants.borderWidth,
|
||||
),
|
||||
),
|
||||
focusedErrorBorder: OutlineInputBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
|
||||
borderSide: BorderSide(
|
||||
color: colorScheme.error,
|
||||
width: constants.focusBorderWidth,
|
||||
),
|
||||
),
|
||||
errorStyle: TextStyle(color: colorScheme.error),
|
||||
contentPadding: const EdgeInsets.all(constants.paddingSmall),
|
||||
);
|
||||
|
||||
@@ -504,3 +533,4 @@ Color darken(Color color, [double percentage = 0.1]) {
|
||||
);
|
||||
return hslDark.toColor();
|
||||
}
|
||||
|
||||
+33
-41
@@ -3,15 +3,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/models/mount_list.dart';
|
||||
import 'package:repertory/models/settings.dart';
|
||||
import 'package:repertory/screens/add_mount_screen.dart';
|
||||
import 'package:repertory/screens/auth_screen.dart';
|
||||
import 'package:repertory/screens/edit_mount_screen.dart';
|
||||
import 'package:repertory/screens/edit_settings_screen.dart';
|
||||
import 'package:repertory/screens/home_screen.dart';
|
||||
import 'package:repertory/widgets/auth_check.dart';
|
||||
import 'package:sodium_libs/sodium_libs.dart' show SodiumInit;
|
||||
@@ -47,36 +42,57 @@ class _MyAppState extends State<MyApp> {
|
||||
@override
|
||||
Widget build(context) {
|
||||
final snackBarTheme = SnackBarThemeData(
|
||||
width: MediaQuery.of(context).size.width * 0.50,
|
||||
width:
|
||||
MediaQuery.of(context).size.width * constants.snackBarWidthFactor,
|
||||
behavior: SnackBarBehavior.floating,
|
||||
);
|
||||
|
||||
final colorScheme = ColorScheme.fromSeed(
|
||||
brightness: Brightness.dark,
|
||||
seedColor: constants.accentBlue,
|
||||
primary: constants.primaryBlue,
|
||||
error: constants.errorRed,
|
||||
onSurface: Colors.white70,
|
||||
surface: constants.surfaceDark,
|
||||
surfaceContainerLow: constants.surfaceContainerLowDark,
|
||||
);
|
||||
|
||||
return MaterialApp(
|
||||
navigatorKey: constants.navigatorKey,
|
||||
themeMode: ThemeMode.dark,
|
||||
darkTheme: ThemeData(
|
||||
useMaterial3: true,
|
||||
brightness: Brightness.dark,
|
||||
colorScheme: ColorScheme.fromSeed(
|
||||
brightness: Brightness.dark,
|
||||
seedColor: constants.accentBlue,
|
||||
onSurface: Colors.white70,
|
||||
surface: constants.surfaceDark,
|
||||
surfaceContainerLow: constants.surfaceContainerLowDark,
|
||||
),
|
||||
colorScheme: colorScheme,
|
||||
scaffoldBackgroundColor: constants.surfaceDark,
|
||||
snackBarTheme: snackBarTheme,
|
||||
appBarTheme: const AppBarTheme(scrolledUnderElevation: 0),
|
||||
inputDecorationTheme: const InputDecorationTheme(
|
||||
focusedBorder: UnderlineInputBorder(borderSide: BorderSide(width: 2)),
|
||||
cardTheme: CardThemeData(
|
||||
elevation: constants.cardElevation,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
|
||||
side: BorderSide(
|
||||
color: colorScheme.outlineVariant.withValues(
|
||||
alpha: constants.highlightAlpha,
|
||||
),
|
||||
width: constants.borderWidth,
|
||||
),
|
||||
),
|
||||
),
|
||||
inputDecorationTheme: InputDecorationTheme(
|
||||
focusedBorder: UnderlineInputBorder(
|
||||
borderSide: BorderSide(width: constants.focusBorderWidth),
|
||||
),
|
||||
),
|
||||
elevatedButtonTheme: ElevatedButtonThemeData(
|
||||
style: ElevatedButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(40),
|
||||
minimumSize: const Size.fromHeight(constants.buttonHeight),
|
||||
),
|
||||
),
|
||||
filledButtonTheme: FilledButtonThemeData(
|
||||
style: FilledButton.styleFrom(minimumSize: const Size.fromHeight(40)),
|
||||
style: FilledButton.styleFrom(
|
||||
minimumSize: const Size.fromHeight(constants.buttonHeight),
|
||||
),
|
||||
),
|
||||
dividerTheme: const DividerThemeData(thickness: 0.6, space: 0),
|
||||
),
|
||||
@@ -85,31 +101,7 @@ class _MyAppState extends State<MyApp> {
|
||||
routes: {
|
||||
'/': (context) =>
|
||||
const AuthCheck(child: HomeScreen(title: constants.appTitle)),
|
||||
'/add': (context) => const AuthCheck(
|
||||
child: AddMountScreen(title: constants.addMountTitle),
|
||||
),
|
||||
'/auth': (context) => const AuthScreen(title: constants.appTitle),
|
||||
'/settings': (context) => const AuthCheck(
|
||||
child: EditSettingsScreen(title: constants.appSettingsTitle),
|
||||
),
|
||||
},
|
||||
onGenerateRoute: (settings) {
|
||||
if (settings.name != '/edit') {
|
||||
return null;
|
||||
}
|
||||
|
||||
final mount = settings.arguments as Mount;
|
||||
return MaterialPageRoute(
|
||||
builder: (context) {
|
||||
return AuthCheck(
|
||||
child: EditMountScreen(
|
||||
mount: mount,
|
||||
title:
|
||||
'${mount.provider} Settings • ${formatMountName(mount.type, mount.name)}',
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
@@ -125,7 +125,7 @@ class Mount with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
return refresh();
|
||||
return await refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
@@ -154,7 +154,7 @@ class Mount with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
return refresh();
|
||||
return await refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
@@ -330,7 +330,7 @@ class Mount with ChangeNotifier {
|
||||
return;
|
||||
}
|
||||
|
||||
return refresh();
|
||||
return await refresh();
|
||||
} catch (e) {
|
||||
debugPrint('$e');
|
||||
}
|
||||
|
||||
@@ -10,21 +10,31 @@ import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/models/mount_list.dart';
|
||||
import 'package:repertory/types/mount_config.dart';
|
||||
import 'package:repertory/utils/safe_set_state_mixin.dart';
|
||||
import 'package:repertory/widgets/app_dialog.dart';
|
||||
import 'package:repertory/widgets/app_dropdown.dart';
|
||||
import 'package:repertory/widgets/app_outlined_icon_button.dart';
|
||||
import 'package:repertory/widgets/app_scaffold.dart';
|
||||
import 'package:repertory/widgets/mount_settings.dart';
|
||||
|
||||
class AddMountScreen extends StatefulWidget {
|
||||
final String title;
|
||||
const AddMountScreen({super.key, required this.title});
|
||||
|
||||
@override
|
||||
State<AddMountScreen> createState() => _AddMountScreenState();
|
||||
/// Opens the "Add New Mount" modal dialog.
|
||||
Future<void> showAddMountDialog(BuildContext context) {
|
||||
return doShowDialog(
|
||||
context,
|
||||
const AppDialog(
|
||||
title: constants.addMountTitle,
|
||||
child: AddMountContent(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
class _AddMountScreenState extends State<AddMountScreen>
|
||||
with SafeSetState<AddMountScreen> {
|
||||
class AddMountContent extends StatefulWidget {
|
||||
const AddMountContent({super.key});
|
||||
|
||||
@override
|
||||
State<AddMountContent> createState() => _AddMountContentState();
|
||||
}
|
||||
|
||||
class _AddMountContentState extends State<AddMountContent>
|
||||
with SafeSetState<AddMountContent> {
|
||||
Mount? _mount;
|
||||
final _mountNameController = TextEditingController();
|
||||
String _mountType = "";
|
||||
@@ -48,9 +58,9 @@ class _AddMountScreenState extends State<AddMountScreen>
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
|
||||
return AppScaffold(
|
||||
title: widget.title,
|
||||
showBack: true,
|
||||
return Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
AppDropdown<String>(
|
||||
constrainToIntrinsic: true,
|
||||
@@ -83,26 +93,19 @@ class _AddMountScreenState extends State<AddMountScreen>
|
||||
scheme,
|
||||
'Configuration Name',
|
||||
hintText: 'Enter a unique name',
|
||||
icon: Icons.drive_file_rename_outline,
|
||||
icon: constants.iconRename,
|
||||
),
|
||||
),
|
||||
],
|
||||
if (_mount != null) ...[
|
||||
const SizedBox(height: constants.padding),
|
||||
Expanded(
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: constants.padding,
|
||||
),
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadius),
|
||||
child: MountSettingsWidget(
|
||||
isAdd: true,
|
||||
mount: _mount!,
|
||||
settings: _settings[_mountType]!,
|
||||
showAdvanced: false,
|
||||
),
|
||||
),
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadius),
|
||||
child: MountSettingsWidget(
|
||||
isAdd: true,
|
||||
mount: _mount!,
|
||||
settings: _settings[_mountType]!,
|
||||
showAdvanced: false,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: constants.padding),
|
||||
@@ -111,7 +114,7 @@ class _AddMountScreenState extends State<AddMountScreen>
|
||||
IntrinsicWidth(
|
||||
child: AppOutlinedIconButton(
|
||||
enabled: _enabled,
|
||||
icon: Icons.check,
|
||||
icon: constants.iconTest,
|
||||
text: 'Test',
|
||||
onPressed: _handleProviderTest,
|
||||
),
|
||||
@@ -120,7 +123,7 @@ class _AddMountScreenState extends State<AddMountScreen>
|
||||
IntrinsicWidth(
|
||||
child: AppOutlinedIconButton(
|
||||
enabled: _enabled,
|
||||
icon: Icons.add,
|
||||
icon: constants.iconAdd,
|
||||
text: 'Add',
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
|
||||
@@ -198,7 +198,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, _, _) {
|
||||
return Icon(
|
||||
Icons.folder,
|
||||
constants.iconFolder,
|
||||
color: scheme.primary,
|
||||
size: constants.loginIconSize,
|
||||
);
|
||||
@@ -234,7 +234,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
AppTextField(
|
||||
autofocus: true,
|
||||
controller: _userController,
|
||||
icon: Icons.person,
|
||||
icon: constants.iconUser,
|
||||
labelText: 'Username',
|
||||
textInputAction: TextInputAction.next,
|
||||
validator: (v) {
|
||||
@@ -250,7 +250,7 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
const SizedBox(height: constants.padding),
|
||||
AppTextField(
|
||||
controller: _passwordController,
|
||||
icon: Icons.lock,
|
||||
icon: constants.iconLock,
|
||||
labelText: 'Password',
|
||||
obscureText: _obscure,
|
||||
suffixIcon: IconButton(
|
||||
@@ -259,8 +259,8 @@ class _AuthScreenState extends State<AuthScreen> {
|
||||
: 'Hide password',
|
||||
icon: Icon(
|
||||
_obscure
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
? constants.iconVisibility
|
||||
: constants.iconVisibilityOff,
|
||||
),
|
||||
onPressed: () {
|
||||
setState(() {
|
||||
|
||||
@@ -3,32 +3,38 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/utils/safe_set_state_mixin.dart';
|
||||
import 'package:repertory/widgets/app_scaffold.dart';
|
||||
import 'package:repertory/widgets/app_dialog.dart';
|
||||
import 'package:repertory/widgets/mount_settings.dart';
|
||||
|
||||
class EditMountScreen extends StatefulWidget {
|
||||
final Mount mount;
|
||||
final String title;
|
||||
const EditMountScreen({super.key, required this.mount, required this.title});
|
||||
|
||||
@override
|
||||
State<EditMountScreen> createState() => _EditMountScreenState();
|
||||
/// Opens the "Edit Mount" modal dialog for the given [mount].
|
||||
Future<void> showEditMountDialog(BuildContext context, Mount mount) {
|
||||
return doShowDialog(context, EditMountContent(mount: mount));
|
||||
}
|
||||
|
||||
class _EditMountScreenState extends State<EditMountScreen>
|
||||
with SafeSetState<EditMountScreen> {
|
||||
class EditMountContent extends StatefulWidget {
|
||||
final Mount mount;
|
||||
const EditMountContent({super.key, required this.mount});
|
||||
|
||||
@override
|
||||
State<EditMountContent> createState() => _EditMountContentState();
|
||||
}
|
||||
|
||||
class _EditMountContentState extends State<EditMountContent>
|
||||
with SafeSetState<EditMountContent> {
|
||||
bool _showAdvanced = false;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
return AppScaffold(
|
||||
title: widget.title,
|
||||
showBack: true,
|
||||
advancedWidget: Row(
|
||||
|
||||
return AppDialog(
|
||||
title:
|
||||
'${widget.mount.provider} Settings • ${formatMountName(widget.mount.type, widget.mount.name)}',
|
||||
trailing: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Text(
|
||||
@@ -38,22 +44,19 @@ class _EditMountScreenState extends State<EditMountScreen>
|
||||
const SizedBox(width: 6),
|
||||
IconButton(
|
||||
tooltip: _showAdvanced ? 'Hide advanced' : 'Show advanced',
|
||||
icon: Icon(_showAdvanced ? Icons.toggle_on : Icons.toggle_off),
|
||||
icon: Icon(
|
||||
_showAdvanced ? constants.iconToggleOn : constants.iconToggleOff,
|
||||
),
|
||||
color: _showAdvanced ? scheme.primary : scheme.onSurface,
|
||||
onPressed: () => setState(() => _showAdvanced = !_showAdvanced),
|
||||
),
|
||||
],
|
||||
),
|
||||
children: [
|
||||
Expanded(
|
||||
child: MountSettingsWidget(
|
||||
mount: widget.mount,
|
||||
settings: jsonDecode(jsonEncode(widget.mount.mountConfig.settings)),
|
||||
showAdvanced: _showAdvanced,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: constants.padding),
|
||||
],
|
||||
child: MountSettingsWidget(
|
||||
mount: widget.mount,
|
||||
settings: jsonDecode(jsonEncode(widget.mount.mountConfig.settings)),
|
||||
showAdvanced: _showAdvanced,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -9,45 +9,42 @@ import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/utils/safe_set_state_mixin.dart';
|
||||
import 'package:repertory/widgets/app_scaffold.dart';
|
||||
import 'package:repertory/widgets/app_dialog.dart';
|
||||
import 'package:repertory/widgets/ui_settings.dart';
|
||||
|
||||
class EditSettingsScreen extends StatefulWidget {
|
||||
final String title;
|
||||
const EditSettingsScreen({super.key, required this.title});
|
||||
|
||||
@override
|
||||
State<EditSettingsScreen> createState() => _EditSettingsScreenState();
|
||||
/// Opens the "Portal Settings" modal dialog.
|
||||
Future<void> showPortalSettingsDialog(BuildContext context) {
|
||||
return doShowDialog(context, const PortalSettingsContent());
|
||||
}
|
||||
|
||||
class _EditSettingsScreenState extends State<EditSettingsScreen>
|
||||
with SafeSetState<EditSettingsScreen> {
|
||||
class PortalSettingsContent extends StatefulWidget {
|
||||
const PortalSettingsContent({super.key});
|
||||
|
||||
@override
|
||||
State<PortalSettingsContent> createState() => _PortalSettingsContentState();
|
||||
}
|
||||
|
||||
class _PortalSettingsContentState extends State<PortalSettingsContent>
|
||||
with SafeSetState<PortalSettingsContent> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return AppScaffold(
|
||||
title: widget.title,
|
||||
showBack: true,
|
||||
showUISettings: true,
|
||||
children: [
|
||||
Expanded(
|
||||
child: FutureBuilder<Map<String, dynamic>>(
|
||||
future: _grabSettings(),
|
||||
initialData: const <String, dynamic>{},
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
return AppDialog(
|
||||
title: constants.appSettingsTitle,
|
||||
child: FutureBuilder<Map<String, dynamic>>(
|
||||
future: _grabSettings(),
|
||||
initialData: const <String, dynamic>{},
|
||||
builder: (context, snapshot) {
|
||||
if (!snapshot.hasData) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
return UISettingsWidget(
|
||||
origSettings: jsonDecode(jsonEncode(snapshot.requireData)),
|
||||
settings: snapshot.requireData,
|
||||
showAdvanced: false,
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(height: constants.padding),
|
||||
],
|
||||
return UISettingsWidget(
|
||||
origSettings: jsonDecode(jsonEncode(snapshot.requireData)),
|
||||
settings: snapshot.requireData,
|
||||
showAdvanced: false,
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/screens/add_mount_screen.dart';
|
||||
import 'package:repertory/widgets/app_scaffold.dart';
|
||||
import 'package:repertory/widgets/mount_list_widget.dart';
|
||||
|
||||
@@ -26,7 +27,7 @@ class _HomeScreeState extends State<HomeScreen> {
|
||||
tag: 'add_mount_fab',
|
||||
child: Material(
|
||||
color: scheme.primary.withValues(alpha: constants.secondaryAlpha),
|
||||
elevation: 12,
|
||||
elevation: constants.fabElevation,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadius),
|
||||
),
|
||||
@@ -37,7 +38,7 @@ class _HomeScreeState extends State<HomeScreen> {
|
||||
color: scheme.outlineVariant.withValues(
|
||||
alpha: constants.outlineAlpha,
|
||||
),
|
||||
width: 1,
|
||||
width: constants.borderWidth,
|
||||
),
|
||||
gradient: const LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
@@ -57,13 +58,13 @@ class _HomeScreeState extends State<HomeScreen> {
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadius),
|
||||
onTap: () {
|
||||
Navigator.pushNamed(context, '/add');
|
||||
showAddMountDialog(context);
|
||||
},
|
||||
child: const SizedBox(
|
||||
width: 56,
|
||||
height: 56,
|
||||
width: constants.fabInnerSize,
|
||||
height: constants.fabInnerSize,
|
||||
child: Center(
|
||||
child: Icon(Icons.add, size: constants.largeIconSize),
|
||||
child: Icon(constants.iconAdd, size: constants.largeIconSize),
|
||||
),
|
||||
),
|
||||
),
|
||||
|
||||
@@ -20,7 +20,7 @@ void createBooleanSetting(
|
||||
widget,
|
||||
Function setState, {
|
||||
String? description,
|
||||
IconData icon = Icons.quiz,
|
||||
IconData icon = constants.iconToggleOn,
|
||||
}) {
|
||||
if (!isAdvanced || showAdvanced) {
|
||||
list.add(
|
||||
@@ -149,7 +149,7 @@ void createPasswordSetting(
|
||||
widget,
|
||||
Function setState, {
|
||||
String? description,
|
||||
IconData icon = Icons.password,
|
||||
IconData icon = constants.iconPassword,
|
||||
List<Validator> validators = const [],
|
||||
}) {
|
||||
if (!isAdvanced || widget.showAdvanced) {
|
||||
@@ -222,8 +222,8 @@ void createPasswordSetting(
|
||||
),
|
||||
icon: Icon(
|
||||
hidePassword1
|
||||
? Icons.visibility
|
||||
: Icons.visibility_off,
|
||||
? constants.iconVisibility
|
||||
: constants.iconVisibilityOff,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
// app_dialog.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
|
||||
/// A themed modal dialog shell that sizes to its content.
|
||||
///
|
||||
/// The width is sized between [constants.dialogMinWidthFactor] and
|
||||
/// [constants.dialogMaxWidthFactor] of the viewport (clamped to the absolute
|
||||
/// pixel bounds in [constants]). The dialog hugs its content vertically and
|
||||
/// only begins scrolling once the content would exceed
|
||||
/// [constants.dialogMaxHeightFactor] of the viewport. For this to work, the
|
||||
/// content ([child]) must size itself to its intrinsic height (e.g. settings
|
||||
/// lists built with `shrinkWrap: true`) rather than expanding to fill.
|
||||
///
|
||||
/// A fixed header row (title, optional [trailing] widget, close button) stays
|
||||
/// pinned above the scrollable content.
|
||||
class AppDialog extends StatelessWidget {
|
||||
const AppDialog({
|
||||
super.key,
|
||||
required this.title,
|
||||
required this.child,
|
||||
this.trailing,
|
||||
});
|
||||
|
||||
final String title;
|
||||
final Widget child;
|
||||
final Widget? trailing;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final scheme = Theme.of(context).colorScheme;
|
||||
final textTheme = Theme.of(context).textTheme;
|
||||
final media = MediaQuery.of(context).size;
|
||||
|
||||
final maxWidth = (media.width * constants.dialogMaxWidthFactor).clamp(
|
||||
constants.dialogMinWidth,
|
||||
constants.dialogMaxWidth,
|
||||
);
|
||||
final minWidth = (media.width * constants.dialogMinWidthFactor).clamp(
|
||||
constants.dialogMinWidth,
|
||||
maxWidth,
|
||||
);
|
||||
final maxHeight = media.height * constants.dialogMaxHeightFactor;
|
||||
|
||||
final header = Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Text(
|
||||
title,
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
style: textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: scheme.primary,
|
||||
letterSpacing: 0.2,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (trailing != null) ...[
|
||||
const SizedBox(width: constants.padding),
|
||||
trailing!,
|
||||
],
|
||||
const SizedBox(width: constants.paddingSmall),
|
||||
IconButton(
|
||||
tooltip: 'Close',
|
||||
icon: const Icon(constants.iconClose),
|
||||
onPressed: () => Navigator.of(context).pop(),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
return Dialog(
|
||||
insetPadding: const EdgeInsets.all(constants.padding),
|
||||
child: ConstrainedBox(
|
||||
constraints: BoxConstraints(
|
||||
minWidth: minWidth,
|
||||
maxWidth: maxWidth,
|
||||
maxHeight: maxHeight,
|
||||
),
|
||||
child: Padding(
|
||||
// Tighter top/bottom than left/right so the dialog does not look
|
||||
// vertically empty around the settings list.
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: constants.padding,
|
||||
vertical: constants.paddingSmall,
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.stretch,
|
||||
children: [
|
||||
header,
|
||||
const SizedBox(height: constants.paddingSmall),
|
||||
// Hug the content height; scroll only once it would exceed the
|
||||
// dialog's max height.
|
||||
Flexible(
|
||||
child: SingleChildScrollView(child: child),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,10 @@ class AppDropdown<T> extends StatelessWidget {
|
||||
|
||||
final effectiveFill =
|
||||
fillColor ??
|
||||
darken(scheme.primary, 0.95).withValues(alpha: constants.dropDownAlpha);
|
||||
Color.alphaBlend(
|
||||
scheme.primary.withValues(alpha: constants.dialogTintAlpha),
|
||||
constants.surfaceContainerLowDark,
|
||||
);
|
||||
|
||||
final effectiveTextStyle =
|
||||
textStyle ??
|
||||
|
||||
@@ -26,8 +26,8 @@ class AppIconButtonFramed extends StatelessWidget {
|
||||
onTap: onPressed,
|
||||
borderRadius: radius,
|
||||
child: Ink(
|
||||
width: 46,
|
||||
height: 46,
|
||||
width: constants.framedControlSize,
|
||||
height: constants.framedControlSize,
|
||||
decoration: BoxDecoration(
|
||||
color: scheme.primary.withValues(alpha: constants.outlineAlpha),
|
||||
borderRadius: radius,
|
||||
@@ -35,7 +35,7 @@ class AppIconButtonFramed extends StatelessWidget {
|
||||
color: scheme.outlineVariant.withValues(
|
||||
alpha: constants.outlineAlpha,
|
||||
),
|
||||
width: 1,
|
||||
width: constants.borderWidth,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/models/auth.dart';
|
||||
import 'package:repertory/models/settings.dart';
|
||||
import 'package:repertory/screens/edit_settings_screen.dart';
|
||||
import 'package:repertory/widgets/aurora_sweep.dart';
|
||||
|
||||
class AppScaffold extends StatelessWidget {
|
||||
@@ -67,14 +68,14 @@ class AppScaffold extends StatelessWidget {
|
||||
children: [
|
||||
if (!showBack) ...[
|
||||
SizedBox(
|
||||
width: 40,
|
||||
height: 40,
|
||||
width: constants.headerControlSize,
|
||||
height: constants.headerControlSize,
|
||||
child: Image.asset(
|
||||
'assets/images/repertory.png',
|
||||
fit: BoxFit.contain,
|
||||
errorBuilder: (_, _, _) {
|
||||
return Icon(
|
||||
Icons.folder,
|
||||
constants.iconFolder,
|
||||
color: scheme.primary,
|
||||
size: constants.largeIconSize,
|
||||
);
|
||||
@@ -92,8 +93,8 @@ class AppScaffold extends StatelessWidget {
|
||||
),
|
||||
onTap: () => Navigator.of(context).pop(),
|
||||
child: Ink(
|
||||
width: 40,
|
||||
height: 40,
|
||||
width: constants.headerControlSize,
|
||||
height: constants.headerControlSize,
|
||||
decoration: BoxDecoration(
|
||||
color: scheme.surface.withValues(
|
||||
alpha: constants.secondaryAlpha,
|
||||
@@ -105,7 +106,7 @@ class AppScaffold extends StatelessWidget {
|
||||
color: scheme.outlineVariant.withValues(
|
||||
alpha: constants.highlightAlpha,
|
||||
),
|
||||
width: 1,
|
||||
width: constants.borderWidth,
|
||||
),
|
||||
boxShadow: [
|
||||
BoxShadow(
|
||||
@@ -117,7 +118,7 @@ class AppScaffold extends StatelessWidget {
|
||||
),
|
||||
],
|
||||
),
|
||||
child: const Icon(Icons.arrow_back),
|
||||
child: Icon(constants.iconBack),
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -141,8 +142,8 @@ class AppScaffold extends StatelessWidget {
|
||||
builder: (context, settings, _) => IconButton(
|
||||
icon: Icon(
|
||||
settings.enableAnimations
|
||||
? Icons.toggle_on
|
||||
: Icons.toggle_off,
|
||||
? constants.iconToggleOn
|
||||
: constants.iconToggleOff,
|
||||
),
|
||||
color: settings.enableAnimations
|
||||
? scheme.primary
|
||||
@@ -157,8 +158,8 @@ class AppScaffold extends StatelessWidget {
|
||||
builder: (context, settings, _) => IconButton(
|
||||
icon: Icon(
|
||||
settings.autoStart
|
||||
? Icons.toggle_on
|
||||
: Icons.toggle_off,
|
||||
? constants.iconToggleOn
|
||||
: constants.iconToggleOff,
|
||||
),
|
||||
color: settings.autoStart
|
||||
? scheme.primary
|
||||
@@ -175,7 +176,7 @@ class AppScaffold extends StatelessWidget {
|
||||
tooltip: 'Settings',
|
||||
icon: const Icon(Icons.settings),
|
||||
onPressed: () {
|
||||
Navigator.pushNamed(context, '/settings');
|
||||
showPortalSettingsDialog(context);
|
||||
},
|
||||
),
|
||||
const SizedBox(width: constants.padding),
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
// app_toggle_button_framed.dart
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/widgets/app_icon_button_framed.dart';
|
||||
|
||||
class AppToggleButtonFramed extends StatelessWidget {
|
||||
@@ -22,7 +23,7 @@ class AppToggleButtonFramed extends StatelessWidget {
|
||||
Color iconColor = scheme.onSurface;
|
||||
|
||||
if (mounted != null) {
|
||||
icon = isOn ? Icons.toggle_on : Icons.toggle_off;
|
||||
icon = isOn ? constants.iconToggleOn : constants.iconToggleOff;
|
||||
iconColor = isOn ? scheme.primary : scheme.onSurface;
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
||||
widget.settings,
|
||||
key,
|
||||
value,
|
||||
Icons.person,
|
||||
constants.iconUser,
|
||||
true,
|
||||
widget.showAdvanced,
|
||||
widget,
|
||||
@@ -389,11 +389,11 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
||||
|
||||
final titleStyle = theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: scheme.onSurface,
|
||||
color: scheme.primary,
|
||||
);
|
||||
|
||||
return SettingsList(
|
||||
shrinkWrap: false,
|
||||
shrinkWrap: true,
|
||||
platform: DevicePlatform.web,
|
||||
lightTheme: settingsTheme,
|
||||
darkTheme: settingsTheme,
|
||||
@@ -840,6 +840,7 @@ class _MountSettingsWidgetState extends State<MountSettingsWidget> {
|
||||
widget,
|
||||
setState,
|
||||
description: getSettingDescription('$key.$subKey'),
|
||||
icon: constants.iconRemoteMount,
|
||||
);
|
||||
remoteMountSettings.insertAll(0, tempSettings);
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:provider/provider.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
import 'package:repertory/helpers.dart';
|
||||
import 'package:repertory/models/mount.dart';
|
||||
import 'package:repertory/screens/edit_mount_screen.dart';
|
||||
import 'package:repertory/utils/safe_set_state_mixin.dart';
|
||||
import 'package:repertory/widgets/app_outlined_icon_button.dart';
|
||||
import 'package:repertory/widgets/app_icon_button_framed.dart';
|
||||
@@ -40,20 +41,10 @@ class _MountWidgetState extends State<MountWidget>
|
||||
);
|
||||
|
||||
return ConstrainedBox(
|
||||
constraints: const BoxConstraints(minHeight: 120),
|
||||
constraints: const BoxConstraints(minHeight: constants.mountCardMinHeight),
|
||||
child: Card(
|
||||
margin: const EdgeInsets.all(0.0),
|
||||
elevation: 12,
|
||||
color: scheme.primary.withValues(alpha: constants.primaryAlpha),
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(constants.borderRadiusSmall),
|
||||
side: BorderSide(
|
||||
color: scheme.outlineVariant.withValues(
|
||||
alpha: constants.outlineAlpha,
|
||||
),
|
||||
width: 1,
|
||||
),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(constants.padding),
|
||||
child: Consumer<Mount>(
|
||||
@@ -66,12 +57,8 @@ class _MountWidgetState extends State<MountWidget>
|
||||
crossAxisAlignment: CrossAxisAlignment.center,
|
||||
children: [
|
||||
AppIconButtonFramed(
|
||||
icon: Icons.settings,
|
||||
onPressed: () => Navigator.pushNamed(
|
||||
context,
|
||||
'/edit',
|
||||
arguments: mount,
|
||||
),
|
||||
icon: constants.iconSettings,
|
||||
onPressed: () => showEditMountDialog(context, mount),
|
||||
),
|
||||
const SizedBox(width: constants.padding),
|
||||
Expanded(
|
||||
@@ -88,13 +75,13 @@ class _MountWidgetState extends State<MountWidget>
|
||||
),
|
||||
if (mount.mounted == false) ...[
|
||||
AppIconButtonFramed(
|
||||
icon: Icons.delete,
|
||||
icon: constants.iconDelete,
|
||||
onPressed: _enabled
|
||||
? () async {
|
||||
? () {
|
||||
setState(() {
|
||||
_enabled = false;
|
||||
});
|
||||
return doShowDialog(
|
||||
doShowDialog(
|
||||
context,
|
||||
AlertDialog(
|
||||
actions: [
|
||||
@@ -102,6 +89,9 @@ class _MountWidgetState extends State<MountWidget>
|
||||
child: const Text('Yes'),
|
||||
onPressed: () async {
|
||||
await mount.remove();
|
||||
if (!context.mounted) {
|
||||
return;
|
||||
}
|
||||
setState(() {
|
||||
_enabled = true;
|
||||
});
|
||||
@@ -140,7 +130,7 @@ class _MountWidgetState extends State<MountWidget>
|
||||
children: [
|
||||
AppOutlinedIconButton(
|
||||
text: 'Edit path',
|
||||
icon: Icons.edit,
|
||||
icon: constants.iconEdit,
|
||||
enabled: _enabled && mount.mounted == false,
|
||||
onPressed: () async {
|
||||
setState(() {
|
||||
@@ -211,7 +201,7 @@ class _MountWidgetState extends State<MountWidget>
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: const [
|
||||
Icon(
|
||||
Icons.auto_mode,
|
||||
constants.iconAutoMount,
|
||||
size: constants.smallIconSize,
|
||||
),
|
||||
SizedBox(width: constants.paddingSmall),
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_settings_ui/src/sections/abstract_settings_section.dart';
|
||||
import 'package:flutter_settings_ui/src/sections/platforms/android_settings_section.dart';
|
||||
import 'package:flutter_settings_ui/src/sections/platforms/ios_settings_section.dart';
|
||||
import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
||||
import 'package:repertory/widgets/settings/web_settings_section.dart';
|
||||
import 'package:flutter_settings_ui/src/tiles/abstract_settings_tile.dart';
|
||||
import 'package:flutter_settings_ui/src/utils/platform_utils.dart';
|
||||
import 'package:flutter_settings_ui/src/utils/settings_theme.dart';
|
||||
// These platform section widgets are not re-exported by the public barrel, so
|
||||
// they must be imported directly. They are only reached on non-web platforms;
|
||||
// this app targets web, but the switch below handles them for completeness.
|
||||
// ignore: implementation_imports
|
||||
import 'package:flutter_settings_ui/src/sections/platforms/android_settings_section.dart';
|
||||
// ignore: implementation_imports
|
||||
import 'package:flutter_settings_ui/src/sections/platforms/ios_settings_section.dart';
|
||||
|
||||
class SettingsSection extends AbstractSettingsSection {
|
||||
const SettingsSection({
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_settings_ui/flutter_settings_ui.dart';
|
||||
import 'package:repertory/constants.dart' as constants;
|
||||
|
||||
// Container(
|
||||
// height: 65 * scaleFactor,
|
||||
@@ -30,7 +31,6 @@ class WebSettingsSection extends StatelessWidget {
|
||||
|
||||
Widget buildSectionBody(BuildContext context) {
|
||||
final theme = SettingsTheme.of(context);
|
||||
final scaleFactor = MediaQuery.textScalerOf(context).scale(1);
|
||||
|
||||
return Padding(
|
||||
padding: margin ?? EdgeInsets.zero,
|
||||
@@ -39,10 +39,10 @@ class WebSettingsSection extends StatelessWidget {
|
||||
children: [
|
||||
if (title != null)
|
||||
Padding(
|
||||
padding: EdgeInsetsDirectional.only(
|
||||
bottom: 5 * scaleFactor,
|
||||
padding: const EdgeInsetsDirectional.only(
|
||||
bottom: constants.paddingSmall,
|
||||
start: 6,
|
||||
top: 20 * scaleFactor,
|
||||
top: constants.paddingSmall,
|
||||
),
|
||||
child: title!,
|
||||
),
|
||||
@@ -50,7 +50,7 @@ class WebSettingsSection extends StatelessWidget {
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
),
|
||||
elevation: 4,
|
||||
elevation: constants.cardElevation,
|
||||
color: theme.themeData.settingsSectionBackground,
|
||||
child: buildTileList(),
|
||||
),
|
||||
|
||||
@@ -94,7 +94,7 @@ class _UISettingsWidgetState extends State<UISettingsWidget>
|
||||
widget.settings,
|
||||
key,
|
||||
value,
|
||||
Icons.person,
|
||||
constants.iconUser,
|
||||
false,
|
||||
widget.showAdvanced,
|
||||
widget,
|
||||
@@ -109,11 +109,11 @@ class _UISettingsWidgetState extends State<UISettingsWidget>
|
||||
|
||||
final titleStyle = theme.textTheme.titleLarge?.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
color: scheme.onSurface,
|
||||
color: scheme.primary,
|
||||
);
|
||||
|
||||
return SettingsList(
|
||||
shrinkWrap: false,
|
||||
shrinkWrap: true,
|
||||
platform: DevicePlatform.web,
|
||||
lightTheme: settingsTheme,
|
||||
darkTheme: settingsTheme,
|
||||
|
||||
Reference in new issue
Block a user