mirror of
https://github.com/JGeek00/adguard-home-manager.git
synced 2025-04-20 13:59:12 +00:00
Changed system navigation bar color
This commit is contained in:
parent
83ea589187
commit
b164d520db
47 changed files with 3370 additions and 3207 deletions
|
@ -2,6 +2,7 @@ import 'dart:io';
|
||||||
|
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
import 'package:flutter_dotenv/flutter_dotenv.dart';
|
||||||
import 'package:provider/provider.dart';
|
import 'package:provider/provider.dart';
|
||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
|
@ -35,6 +36,7 @@ import 'package:adguard_home_manager/services/db/database.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
WidgetsFlutterBinding.ensureInitialized();
|
WidgetsFlutterBinding.ensureInitialized();
|
||||||
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
|
|
||||||
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
if (Platform.isWindows || Platform.isLinux || Platform.isMacOS) {
|
||||||
setWindowMinSize(const Size(500, 500));
|
setWindowMinSize(const Size(500, 500));
|
||||||
|
|
|
@ -185,34 +185,36 @@ class _ClientScreenState extends State<ClientScreen> {
|
||||||
),
|
),
|
||||||
actions: actions(),
|
actions: actions(),
|
||||||
),
|
),
|
||||||
body: ClientForm(
|
body: SafeArea(
|
||||||
isFullScreen: true,
|
child: ClientForm(
|
||||||
client: widget.client,
|
isFullScreen: true,
|
||||||
nameController: nameController,
|
client: widget.client,
|
||||||
updateValidValues: (v) => setState(() => validValues = v),
|
nameController: nameController,
|
||||||
identifiersControllers: identifiersControllers,
|
updateValidValues: (v) => setState(() => validValues = v),
|
||||||
selectedTags: selectedTags,
|
identifiersControllers: identifiersControllers,
|
||||||
useGlobalSettingsFiltering: useGlobalSettingsFiltering,
|
selectedTags: selectedTags,
|
||||||
enableFiltering: enableFiltering,
|
useGlobalSettingsFiltering: useGlobalSettingsFiltering,
|
||||||
enableParentalControl: enableParentalControl,
|
enableFiltering: enableFiltering,
|
||||||
enableSafeBrowsing: enableSafeBrowsing,
|
enableParentalControl: enableParentalControl,
|
||||||
enableSafeSearch: enableSafeSearch,
|
enableSafeBrowsing: enableSafeBrowsing,
|
||||||
safeSearch: safeSearch,
|
enableSafeSearch: enableSafeSearch,
|
||||||
blockedServices: blockedServices,
|
safeSearch: safeSearch,
|
||||||
updateBlockedServices: (v) => setState(() => blockedServices = v),
|
blockedServices: blockedServices,
|
||||||
upstreamServers: upstreamServers,
|
updateBlockedServices: (v) => setState(() => blockedServices = v),
|
||||||
updateUpstreamServers: (v) => setState(() => upstreamServers = v),
|
upstreamServers: upstreamServers,
|
||||||
defaultSafeSearch: defaultSafeSearch,
|
updateUpstreamServers: (v) => setState(() => upstreamServers = v),
|
||||||
useGlobalSettingsServices: useGlobalSettingsServices,
|
defaultSafeSearch: defaultSafeSearch,
|
||||||
updateSelectedTags: (v) => setState(() => selectedTags = v),
|
useGlobalSettingsServices: useGlobalSettingsServices,
|
||||||
updateIdentifiersControllers: (v) => setState(() => identifiersControllers = v),
|
updateSelectedTags: (v) => setState(() => selectedTags = v),
|
||||||
enableDisableGlobalSettingsFiltering: enableDisableGlobalSettingsFiltering,
|
updateIdentifiersControllers: (v) => setState(() => identifiersControllers = v),
|
||||||
updateEnableFiltering: (v) => setState(() => enableFiltering = v),
|
enableDisableGlobalSettingsFiltering: enableDisableGlobalSettingsFiltering,
|
||||||
updateEnableParentalControl: (v) => setState(() => enableParentalControl = v),
|
updateEnableFiltering: (v) => setState(() => enableFiltering = v),
|
||||||
updateEnableSafeBrowsing: (v) => setState(() => enableSafeBrowsing = v),
|
updateEnableParentalControl: (v) => setState(() => enableParentalControl = v),
|
||||||
updateEnableSafeSearch: (v) => setState(() => enableSafeSearch = v),
|
updateEnableSafeBrowsing: (v) => setState(() => enableSafeBrowsing = v),
|
||||||
updateSafeSearch: (v) => setState(() => safeSearch = v),
|
updateEnableSafeSearch: (v) => setState(() => enableSafeSearch = v),
|
||||||
updateUseGlobalSettingsServices: (v) => setState(() => useGlobalSettingsServices = v),
|
updateSafeSearch: (v) => setState(() => safeSearch = v),
|
||||||
|
updateUseGlobalSettingsServices: (v) => setState(() => useGlobalSettingsServices = v),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -149,125 +149,127 @@ class _LogsListClientState extends State<LogsListClient> {
|
||||||
]
|
]
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (loadStatus) {
|
builder: (context) {
|
||||||
case 0:
|
switch (loadStatus) {
|
||||||
return SizedBox(
|
case 0:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const CircularProgressIndicator(),
|
children: [
|
||||||
const SizedBox(height: 30),
|
const CircularProgressIndicator(),
|
||||||
Text(
|
const SizedBox(height: 30),
|
||||||
AppLocalizations.of(context)!.loadingLogs,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
AppLocalizations.of(context)!.loadingLogs,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.center,
|
||||||
fontSize: 22,
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
fontSize: 22,
|
||||||
),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
case 1:
|
case 1:
|
||||||
if (logsData!.data.isNotEmpty) {
|
if (logsData!.data.isNotEmpty) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: fetchLogs,
|
onRefresh: fetchLogs,
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
padding: const EdgeInsets.only(top: 0),
|
padding: const EdgeInsets.only(top: 0),
|
||||||
itemCount: isLoadingMore == true
|
itemCount: isLoadingMore == true
|
||||||
? logsData!.data.length+1
|
? logsData!.data.length+1
|
||||||
: logsData!.data.length,
|
: logsData!.data.length,
|
||||||
itemBuilder: (context, index) {
|
itemBuilder: (context, index) {
|
||||||
if (isLoadingMore == true && index == logsData!.data.length) {
|
if (isLoadingMore == true && index == logsData!.data.length) {
|
||||||
return const Padding(
|
return const Padding(
|
||||||
padding: EdgeInsets.symmetric(vertical: 20),
|
padding: EdgeInsets.symmetric(vertical: 20),
|
||||||
child: Center(
|
child: Center(
|
||||||
child: CircularProgressIndicator(),
|
child: CircularProgressIndicator(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return LogTile(
|
return LogTile(
|
||||||
log: logsData!.data[index],
|
log: logsData!.data[index],
|
||||||
index: index,
|
index: index,
|
||||||
length: logsData!.data.length,
|
length: logsData!.data.length,
|
||||||
useAlwaysNormalTile: true,
|
useAlwaysNormalTile: true,
|
||||||
onLogTap: (log) => {
|
onLogTap: (log) => {
|
||||||
if (width > 700) {
|
if (width > 700) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => LogDetailsScreen(
|
|
||||||
log: log,
|
|
||||||
dialog: true
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => LogDetailsScreen(
|
builder: (context) => LogDetailsScreen(
|
||||||
log: log,
|
log: log,
|
||||||
dialog: false
|
dialog: true
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
}
|
||||||
}
|
else {
|
||||||
},
|
Navigator.of(context).push(
|
||||||
twoColumns: widget.splitView,
|
MaterialPageRoute(
|
||||||
);
|
builder: (context) => LogDetailsScreen(
|
||||||
|
log: log,
|
||||||
|
dialog: false
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
twoColumns: widget.splitView,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.noLogsDisplay,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
else {
|
||||||
|
return Center(
|
||||||
case 2:
|
child: Text(
|
||||||
return SizedBox(
|
AppLocalizations.of(context)!.noLogsDisplay,
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.logsNotLoaded,
|
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
);
|
||||||
),
|
}
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
case 2:
|
||||||
return const SizedBox();
|
return SizedBox(
|
||||||
}
|
width: double.maxFinite,
|
||||||
},
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.logsNotLoaded,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
|
||||||
class Connect extends StatefulWidget {
|
class Connect extends StatefulWidget {
|
||||||
const Connect({Key? key}) : super(key: key);
|
const Connect({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Connect> createState() => _ConnectState();
|
State<Connect> createState() => _ConnectState();
|
||||||
|
@ -61,26 +61,28 @@ class _ConnectState extends State<Connect> {
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
title: Text(AppLocalizations.of(context)!.connect),
|
title: Text(AppLocalizations.of(context)!.connect),
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
ServersList(
|
children: [
|
||||||
context: context,
|
ServersList(
|
||||||
controllers: expandableControllerList,
|
context: context,
|
||||||
onChange: expandOrContract,
|
controllers: expandableControllerList,
|
||||||
scrollController: scrollController,
|
onChange: expandOrContract,
|
||||||
breakingWidth: 700,
|
scrollController: scrollController,
|
||||||
),
|
breakingWidth: 700,
|
||||||
AnimatedPositioned(
|
),
|
||||||
duration: const Duration(milliseconds: 100),
|
AnimatedPositioned(
|
||||||
curve: Curves.easeInOut,
|
duration: const Duration(milliseconds: 100),
|
||||||
bottom: isVisible ?
|
curve: Curves.easeInOut,
|
||||||
appConfigProvider.showingSnackbar
|
bottom: isVisible ?
|
||||||
? 70 : 20
|
appConfigProvider.showingSnackbar
|
||||||
: -70,
|
? 90 : 20
|
||||||
right: 20,
|
: -90,
|
||||||
child: const FabConnect()
|
right: 20,
|
||||||
)
|
child: const FabConnect()
|
||||||
],
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/models/filtering.dart';
|
import 'package:adguard_home_manager/models/filtering.dart';
|
||||||
|
|
||||||
class AddListModal extends StatefulWidget {
|
class AddListModal extends StatelessWidget {
|
||||||
final String type;
|
final String type;
|
||||||
final Filter? list;
|
final Filter? list;
|
||||||
final void Function({required String name, required String url, required String type})? onConfirm;
|
final void Function({required String name, required String url, required String type})? onConfirm;
|
||||||
|
@ -13,19 +13,74 @@ class AddListModal extends StatefulWidget {
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const AddListModal({
|
const AddListModal({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.type,
|
required this.type,
|
||||||
this.list,
|
this.list,
|
||||||
this.onConfirm,
|
this.onConfirm,
|
||||||
this.onEdit,
|
this.onEdit,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddListModal> createState() => _AddListModalState();
|
Widget build(BuildContext context) {
|
||||||
|
if (dialog == true) {
|
||||||
|
return Dialog(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400
|
||||||
|
),
|
||||||
|
child: _Content(
|
||||||
|
list: list,
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
onEdit: onEdit,
|
||||||
|
type: type,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Padding(
|
||||||
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(28),
|
||||||
|
topRight: Radius.circular(28)
|
||||||
|
),
|
||||||
|
color: Theme.of(context).dialogBackgroundColor
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
child: _Content(
|
||||||
|
list: list,
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
onEdit: onEdit,
|
||||||
|
type: type,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AddListModalState extends State<AddListModal> {
|
class _Content extends StatefulWidget {
|
||||||
|
final String type;
|
||||||
|
final Filter? list;
|
||||||
|
final void Function({required String name, required String url, required String type})? onConfirm;
|
||||||
|
final void Function({required Filter list, required String type})? onEdit;
|
||||||
|
|
||||||
|
const _Content({
|
||||||
|
required this.type,
|
||||||
|
required this.list,
|
||||||
|
required this.onConfirm,
|
||||||
|
required this.onEdit,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_Content> createState() => _ContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContentState extends State<_Content> {
|
||||||
final TextEditingController nameController = TextEditingController();
|
final TextEditingController nameController = TextEditingController();
|
||||||
final TextEditingController urlController = TextEditingController();
|
final TextEditingController urlController = TextEditingController();
|
||||||
String? urlError;
|
String? urlError;
|
||||||
|
@ -70,161 +125,133 @@ class _AddListModalState extends State<AddListModal> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget content() {
|
return Column(
|
||||||
return Column(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
Flexible(
|
||||||
Flexible(
|
child: SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
child: Wrap(
|
||||||
child: Wrap(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 24),
|
|
||||||
child: Icon(
|
|
||||||
widget.type == 'whitelist'
|
|
||||||
? Icons.verified_user_rounded
|
|
||||||
: Icons.gpp_bad_rounded,
|
|
||||||
size: 24,
|
|
||||||
color: Theme.of(context).listTileTheme.iconColor
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
widget.list != null
|
|
||||||
? widget.type == 'whitelist'
|
|
||||||
? AppLocalizations.of(context)!.editWhitelist
|
|
||||||
: AppLocalizations.of(context)!.editBlacklist
|
|
||||||
: widget.type == 'whitelist'
|
|
||||||
? AppLocalizations.of(context)!.addWhitelist
|
|
||||||
: AppLocalizations.of(context)!.addBlacklist,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: nameController,
|
|
||||||
onChanged: (_) => checkValidValues(),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.badge_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
labelText: AppLocalizations.of(context)!.name,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Container(height: 30),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: urlController,
|
|
||||||
onChanged: validateUrl,
|
|
||||||
enabled: widget.list != null ? false : true,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.link_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
errorText: urlError,
|
|
||||||
labelText: AppLocalizations.of(context)!.urlAbsolutePath,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
Row(
|
||||||
onPressed: () => Navigator.pop(context),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
child: Text(AppLocalizations.of(context)!.cancel)
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 24),
|
||||||
|
child: Icon(
|
||||||
|
widget.type == 'whitelist'
|
||||||
|
? Icons.verified_user_rounded
|
||||||
|
: Icons.gpp_bad_rounded,
|
||||||
|
size: 24,
|
||||||
|
color: Theme.of(context).listTileTheme.iconColor
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
widget.list != null
|
||||||
|
? widget.type == 'whitelist'
|
||||||
|
? AppLocalizations.of(context)!.editWhitelist
|
||||||
|
: AppLocalizations.of(context)!.editBlacklist
|
||||||
|
: widget.type == 'whitelist'
|
||||||
|
? AppLocalizations.of(context)!.addWhitelist
|
||||||
|
: AppLocalizations.of(context)!.addBlacklist,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
Padding(
|
||||||
TextButton(
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
onPressed: () {
|
child: TextFormField(
|
||||||
Navigator.pop(context);
|
controller: nameController,
|
||||||
if (widget.list != null) {
|
onChanged: (_) => checkValidValues(),
|
||||||
final Filter newList = Filter(
|
decoration: InputDecoration(
|
||||||
url: urlController.text,
|
prefixIcon: const Icon(Icons.badge_rounded),
|
||||||
name: nameController.text,
|
border: const OutlineInputBorder(
|
||||||
lastUpdated: widget.list!.lastUpdated,
|
borderRadius: BorderRadius.all(
|
||||||
id: widget.list!.id,
|
Radius.circular(10)
|
||||||
rulesCount: widget.list!.rulesCount,
|
)
|
||||||
enabled: widget.list!.enabled
|
),
|
||||||
);
|
labelText: AppLocalizations.of(context)!.name,
|
||||||
widget.onEdit!(
|
),
|
||||||
list: newList,
|
),
|
||||||
type: widget.type
|
),
|
||||||
);
|
Container(height: 30),
|
||||||
}
|
Padding(
|
||||||
else {
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
widget.onConfirm!(
|
child: TextFormField(
|
||||||
name: nameController.text,
|
controller: urlController,
|
||||||
url: urlController.text,
|
onChanged: validateUrl,
|
||||||
type: widget.type
|
enabled: widget.list != null ? false : true,
|
||||||
);
|
decoration: InputDecoration(
|
||||||
}
|
prefixIcon: const Icon(Icons.link_rounded),
|
||||||
},
|
border: const OutlineInputBorder(
|
||||||
child: Text(
|
borderRadius: BorderRadius.all(
|
||||||
widget.list != null
|
Radius.circular(10)
|
||||||
? AppLocalizations.of(context)!.save
|
)
|
||||||
: AppLocalizations.of(context)!.confirm
|
),
|
||||||
)
|
errorText: urlError,
|
||||||
|
labelText: AppLocalizations.of(context)!.urlAbsolutePath,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (Platform.isIOS) const SizedBox(height: 16)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.dialog == true) {
|
|
||||||
return Dialog(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: 400
|
|
||||||
),
|
|
||||||
child: content()
|
|
||||||
),
|
),
|
||||||
);
|
Padding(
|
||||||
}
|
padding: const EdgeInsets.all(24),
|
||||||
else {
|
child: Row(
|
||||||
return Padding(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
padding: MediaQuery.of(context).viewInsets,
|
children: [
|
||||||
child: Container(
|
TextButton(
|
||||||
decoration: BoxDecoration(
|
onPressed: () => Navigator.pop(context),
|
||||||
borderRadius: const BorderRadius.only(
|
child: Text(AppLocalizations.of(context)!.cancel)
|
||||||
topLeft: Radius.circular(28),
|
),
|
||||||
topRight: Radius.circular(28)
|
const SizedBox(width: 20),
|
||||||
),
|
TextButton(
|
||||||
color: Theme.of(context).dialogBackgroundColor
|
onPressed: () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
if (widget.list != null) {
|
||||||
|
final Filter newList = Filter(
|
||||||
|
url: urlController.text,
|
||||||
|
name: nameController.text,
|
||||||
|
lastUpdated: widget.list!.lastUpdated,
|
||||||
|
id: widget.list!.id,
|
||||||
|
rulesCount: widget.list!.rulesCount,
|
||||||
|
enabled: widget.list!.enabled
|
||||||
|
);
|
||||||
|
widget.onEdit!(
|
||||||
|
list: newList,
|
||||||
|
type: widget.type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
widget.onConfirm!(
|
||||||
|
name: nameController.text,
|
||||||
|
url: urlController.text,
|
||||||
|
type: widget.type
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Text(
|
||||||
|
widget.list != null
|
||||||
|
? AppLocalizations.of(context)!.save
|
||||||
|
: AppLocalizations.of(context)!.confirm
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
child: content()
|
|
||||||
),
|
),
|
||||||
);
|
if (Platform.isIOS) const SizedBox(height: 16)
|
||||||
}
|
],
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -8,19 +8,55 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
import 'package:adguard_home_manager/functions/get_filtered_status.dart';
|
import 'package:adguard_home_manager/functions/get_filtered_status.dart';
|
||||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
|
|
||||||
class CheckHostModal extends StatefulWidget {
|
class CheckHostModal extends StatelessWidget {
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const CheckHostModal({
|
const CheckHostModal({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<CheckHostModal> createState() => _CheckHostModalState();
|
Widget build(BuildContext context) {
|
||||||
|
if (dialog == true) {
|
||||||
|
return Dialog(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400
|
||||||
|
),
|
||||||
|
child: const _Content()
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Padding(
|
||||||
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
|
child: Container(
|
||||||
|
width: double.maxFinite,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(28),
|
||||||
|
topRight: Radius.circular(28),
|
||||||
|
),
|
||||||
|
color: Theme.of(context).dialogBackgroundColor
|
||||||
|
),
|
||||||
|
child: const SafeArea(
|
||||||
|
child: _Content()
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _CheckHostModalState extends State<CheckHostModal> {
|
class _Content extends StatefulWidget {
|
||||||
|
const _Content();
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_Content> createState() => _ContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class _ContentState extends State<_Content> {
|
||||||
final TextEditingController domainController = TextEditingController();
|
final TextEditingController domainController = TextEditingController();
|
||||||
String? domainError;
|
String? domainError;
|
||||||
|
|
||||||
|
@ -59,17 +95,29 @@ class _CheckHostModalState extends State<CheckHostModal> {
|
||||||
setState(() => resultWidget = checking());
|
setState(() => resultWidget = checking());
|
||||||
|
|
||||||
final result = await serversProvider.apiClient2!.checkHostFiltered(host: domainController.text);
|
final result = await serversProvider.apiClient2!.checkHostFiltered(host: domainController.text);
|
||||||
|
if (!mounted) return;
|
||||||
|
|
||||||
if (mounted) {
|
if (result.successful == true) {
|
||||||
if (result.successful == true) {
|
final status = getFilteredStatus(context, appConfigProvider, result.content['reason'], true);
|
||||||
final status = getFilteredStatus(context, appConfigProvider, result.content['reason'], true);
|
if (mounted) {
|
||||||
if (mounted) {
|
setState(() => resultWidget = Row(
|
||||||
setState(() => resultWidget = Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
Icon(
|
||||||
Icon(
|
status['icon'],
|
||||||
status['icon'],
|
size: 18,
|
||||||
size: 18,
|
color: status['filtered'] == true
|
||||||
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
|
? Colors.grey
|
||||||
|
: Colors.red
|
||||||
|
: appConfigProvider.useThemeColorForStatus
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.green,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
status['label'],
|
||||||
|
style: TextStyle(
|
||||||
color: status['filtered'] == true
|
color: status['filtered'] == true
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
? appConfigProvider.useThemeColorForStatus == true
|
||||||
? Colors.grey
|
? Colors.grey
|
||||||
|
@ -77,39 +125,6 @@ class _CheckHostModalState extends State<CheckHostModal> {
|
||||||
: appConfigProvider.useThemeColorForStatus
|
: appConfigProvider.useThemeColorForStatus
|
||||||
? Theme.of(context).colorScheme.primary
|
? Theme.of(context).colorScheme.primary
|
||||||
: Colors.green,
|
: Colors.green,
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
status['label'],
|
|
||||||
style: TextStyle(
|
|
||||||
color: status['filtered'] == true
|
|
||||||
? appConfigProvider.useThemeColorForStatus == true
|
|
||||||
? Colors.grey
|
|
||||||
: Colors.red
|
|
||||||
: appConfigProvider.useThemeColorForStatus
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.green,
|
|
||||||
fontWeight: FontWeight.w500
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(() => resultWidget = Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.cancel,
|
|
||||||
size: 18,
|
|
||||||
color: Colors.red,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 10),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.check,
|
|
||||||
style: const TextStyle(
|
|
||||||
color: Colors.red,
|
|
||||||
fontWeight: FontWeight.w500
|
fontWeight: FontWeight.w500
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -117,143 +132,134 @@ class _CheckHostModalState extends State<CheckHostModal> {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
setState(() => resultWidget = Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.cancel,
|
||||||
|
size: 18,
|
||||||
|
color: Colors.red,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 10),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.check,
|
||||||
|
style: const TextStyle(
|
||||||
|
color: Colors.red,
|
||||||
|
fontWeight: FontWeight.w500
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget content() {
|
return Column(
|
||||||
return Column(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
Flexible(
|
||||||
Flexible(
|
child: SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
child: Wrap(
|
||||||
child: Wrap(
|
children: [
|
||||||
children: [
|
Row(
|
||||||
Row(
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
children: [
|
||||||
children: [
|
Column(
|
||||||
Column(
|
children: [
|
||||||
children: [
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.only(top: 24),
|
||||||
padding: const EdgeInsets.only(top: 24),
|
child: Icon(
|
||||||
child: Icon(
|
Icons.shield_rounded,
|
||||||
Icons.shield_rounded,
|
size: 24,
|
||||||
size: 24,
|
color: Theme.of(context).listTileTheme.iconColor
|
||||||
color: Theme.of(context).listTileTheme.iconColor
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.checkHostFiltered,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: domainController,
|
|
||||||
onChanged: validateDomain,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.link_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
errorText: domainError,
|
const SizedBox(height: 16),
|
||||||
labelText: AppLocalizations.of(context)!.domain,
|
Text(
|
||||||
|
AppLocalizations.of(context)!.checkHostFiltered,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: domainController,
|
||||||
|
onChanged: validateDomain,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.link_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: domainError,
|
||||||
|
labelText: AppLocalizations.of(context)!.domain,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
if (resultWidget != null) Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: resultWidget,
|
||||||
|
),
|
||||||
|
if (resultWidget == null) Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.insertDomain,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
if (resultWidget != null) Padding(
|
),
|
||||||
padding: const EdgeInsets.all(24),
|
],
|
||||||
child: resultWidget,
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
bottom: 24,
|
||||||
|
right: 24
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(AppLocalizations.of(context)!.close),
|
||||||
),
|
),
|
||||||
if (resultWidget == null) Padding(
|
const SizedBox(width: 20),
|
||||||
padding: const EdgeInsets.all(24),
|
TextButton(
|
||||||
child: Center(
|
onPressed: domainController.text != '' && domainError == null
|
||||||
child: Text(
|
? () => checkHost()
|
||||||
AppLocalizations.of(context)!.insertDomain,
|
: null,
|
||||||
textAlign: TextAlign.center,
|
child: Text(
|
||||||
style: const TextStyle(
|
AppLocalizations.of(context)!.check,
|
||||||
fontSize: 16,
|
style: TextStyle(
|
||||||
),
|
color: domainController.text != '' && domainError == null
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.grey
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
),
|
],
|
||||||
Column(
|
)
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
],
|
||||||
children: [
|
);
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
bottom: 24,
|
|
||||||
right: 24
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text(AppLocalizations.of(context)!.close),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
TextButton(
|
|
||||||
onPressed: domainController.text != '' && domainError == null
|
|
||||||
? () => checkHost()
|
|
||||||
: null,
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.check,
|
|
||||||
style: TextStyle(
|
|
||||||
color: domainController.text != '' && domainError == null
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.grey
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.dialog == true) {
|
|
||||||
return Dialog(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: 400
|
|
||||||
),
|
|
||||||
child: content()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Padding(
|
|
||||||
padding: MediaQuery.of(context).viewInsets,
|
|
||||||
child: Container(
|
|
||||||
width: double.maxFinite,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
borderRadius: const BorderRadius.only(
|
|
||||||
topLeft: Radius.circular(28),
|
|
||||||
topRight: Radius.circular(28),
|
|
||||||
),
|
|
||||||
color: Theme.of(context).dialogBackgroundColor
|
|
||||||
),
|
|
||||||
child: content()
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -25,11 +25,11 @@ class ListDetailsScreen extends StatefulWidget {
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const ListDetailsScreen({
|
const ListDetailsScreen({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.listId,
|
required this.listId,
|
||||||
required this.type,
|
required this.type,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ListDetailsScreen> createState() => _ListDetailsScreenState();
|
State<ListDetailsScreen> createState() => _ListDetailsScreenState();
|
||||||
|
@ -367,42 +367,44 @@ class _ListDetailsScreenState extends State<ListDetailsScreen> {
|
||||||
title: Text(AppLocalizations.of(context)!.listDetails),
|
title: Text(AppLocalizations.of(context)!.listDetails),
|
||||||
actions: list != null ? actions() : null,
|
actions: list != null ? actions() : null,
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
if (list != null) ListView(
|
children: [
|
||||||
children: content(),
|
if (list != null) ListView(
|
||||||
),
|
children: content(),
|
||||||
if (list == null) Center(
|
),
|
||||||
child: Text(
|
if (list == null) Center(
|
||||||
AppLocalizations.of(context)!.listNotAvailable,
|
child: Text(
|
||||||
style: const TextStyle(
|
AppLocalizations.of(context)!.listNotAvailable,
|
||||||
fontSize: 24,
|
style: const TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (list != null) AnimatedPositioned(
|
||||||
if (list != null) AnimatedPositioned(
|
duration: const Duration(milliseconds: 100),
|
||||||
duration: const Duration(milliseconds: 100),
|
curve: Curves.easeInOut,
|
||||||
curve: Curves.easeInOut,
|
bottom: fabVisible ?
|
||||||
bottom: fabVisible ?
|
appConfigProvider.showingSnackbar
|
||||||
appConfigProvider.showingSnackbar
|
? 70 : (Platform.isIOS ? 40 : 20)
|
||||||
? 70 : (Platform.isIOS ? 40 : 20)
|
: -70,
|
||||||
: -70,
|
right: 20,
|
||||||
right: 20,
|
child: FloatingActionButton(
|
||||||
child: FloatingActionButton(
|
onPressed: () => updateList(
|
||||||
onPressed: () => updateList(
|
action: list!.enabled == true
|
||||||
action: list!.enabled == true
|
? FilteringListActions.disable
|
||||||
? FilteringListActions.disable
|
: FilteringListActions.enable,
|
||||||
: FilteringListActions.enable,
|
filterList: list
|
||||||
filterList: list
|
),
|
||||||
|
child: Icon(
|
||||||
|
list.enabled == true
|
||||||
|
? Icons.gpp_bad_rounded
|
||||||
|
: Icons.verified_user_rounded,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
child: Icon(
|
)
|
||||||
list.enabled == true
|
],
|
||||||
? Icons.gpp_bad_rounded
|
),
|
||||||
: Icons.verified_user_rounded,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,10 +10,10 @@ class AddCustomRule extends StatefulWidget {
|
||||||
final bool fullScreen;
|
final bool fullScreen;
|
||||||
|
|
||||||
const AddCustomRule({
|
const AddCustomRule({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.onConfirm,
|
required this.onConfirm,
|
||||||
required this.fullScreen
|
required this.fullScreen
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddCustomRule> createState() => _AddCustomRuleState();
|
State<AddCustomRule> createState() => _AddCustomRuleState();
|
||||||
|
@ -349,8 +349,10 @@ class _AddCustomRuleState extends State<AddCustomRule> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
children: content(),
|
child: ListView(
|
||||||
|
children: content(),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -17,9 +17,9 @@ class BlockedServicesScreen extends StatefulWidget {
|
||||||
final bool fullScreen;
|
final bool fullScreen;
|
||||||
|
|
||||||
const BlockedServicesScreen({
|
const BlockedServicesScreen({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.fullScreen
|
required this.fullScreen
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<BlockedServicesScreen> createState() => _BlockedServicesScreenStateWidget();
|
State<BlockedServicesScreen> createState() => _BlockedServicesScreenStateWidget();
|
||||||
|
@ -83,105 +83,6 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Widget body() {
|
|
||||||
switch (filteringProvider.blockedServicesLoadStatus) {
|
|
||||||
case LoadStatus.loading:
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const CircularProgressIndicator(),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.loadingBlockedServicesList,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
|
||||||
return ListView.builder(
|
|
||||||
itemCount: filteringProvider.blockedServices!.services.length,
|
|
||||||
itemBuilder: (context, index) => Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () => updateValues(
|
|
||||||
values.contains(filteringProvider.blockedServices!.services[index].id),
|
|
||||||
filteringProvider.blockedServices!.services[index]
|
|
||||||
),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 6,
|
|
||||||
bottom: 6,
|
|
||||||
right: 12,
|
|
||||||
left: 24
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
filteringProvider.blockedServices!.services[index].name,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Checkbox(
|
|
||||||
value: values.contains(filteringProvider.blockedServices!.services[index].id),
|
|
||||||
onChanged: (value) => updateValues(
|
|
||||||
value!,
|
|
||||||
filteringProvider.blockedServices!.services[index]
|
|
||||||
),
|
|
||||||
shape: RoundedRectangleBorder(
|
|
||||||
borderRadius: BorderRadius.circular(5)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.error:
|
|
||||||
return Container(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.blockedServicesListNotLoaded,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.fullScreen == true) {
|
if (widget.fullScreen == true) {
|
||||||
return Dialog.fullscreen(
|
return Dialog.fullscreen(
|
||||||
child: Scaffold(
|
child: Scaffold(
|
||||||
|
@ -199,18 +100,23 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: RefreshIndicator(
|
body: SafeArea(
|
||||||
onRefresh: () async {
|
child: RefreshIndicator(
|
||||||
final result = await filteringProvider.loadBlockedServices();
|
onRefresh: () async {
|
||||||
if (result == false) {
|
final result = await filteringProvider.loadBlockedServices();
|
||||||
showSnacbkar(
|
if (result == false) {
|
||||||
appConfigProvider: appConfigProvider,
|
showSnacbkar(
|
||||||
label: AppLocalizations.of(context)!.blockedServicesListNotLoaded,
|
appConfigProvider: appConfigProvider,
|
||||||
color: Colors.red
|
label: AppLocalizations.of(context)!.blockedServicesListNotLoaded,
|
||||||
);
|
color: Colors.red
|
||||||
}
|
);
|
||||||
},
|
}
|
||||||
child: body()
|
},
|
||||||
|
child: _Content(
|
||||||
|
values: values,
|
||||||
|
updateValues: updateValues,
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
@ -256,7 +162,10 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: body()
|
child: _Content(
|
||||||
|
values: values,
|
||||||
|
updateValues: updateValues,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
@ -266,6 +175,118 @@ class _BlockedServicesScreenStateWidget extends State<BlockedServicesScreen> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Content extends StatelessWidget {
|
||||||
|
final List<String> values;
|
||||||
|
final void Function(bool value, BlockedService item) updateValues;
|
||||||
|
|
||||||
|
const _Content({
|
||||||
|
required this.values,
|
||||||
|
required this.updateValues,
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
final filteringProvider = Provider.of<FilteringProvider>(context);
|
||||||
|
|
||||||
|
switch (filteringProvider.blockedServicesLoadStatus) {
|
||||||
|
case LoadStatus.loading:
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.loadingBlockedServicesList,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
case LoadStatus.loaded:
|
||||||
|
return ListView.builder(
|
||||||
|
itemCount: filteringProvider.blockedServices!.services.length,
|
||||||
|
itemBuilder: (context, index) => Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () => updateValues(
|
||||||
|
values.contains(filteringProvider.blockedServices!.services[index].id),
|
||||||
|
filteringProvider.blockedServices!.services[index]
|
||||||
|
),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 6,
|
||||||
|
bottom: 6,
|
||||||
|
right: 12,
|
||||||
|
left: 24
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
filteringProvider.blockedServices!.services[index].name,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Checkbox(
|
||||||
|
value: values.contains(filteringProvider.blockedServices!.services[index].id),
|
||||||
|
onChanged: (value) => updateValues(
|
||||||
|
value!,
|
||||||
|
filteringProvider.blockedServices!.services[index]
|
||||||
|
),
|
||||||
|
shape: RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(5)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
case LoadStatus.error:
|
||||||
|
return Container(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.blockedServicesListNotLoaded,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void openBlockedServicesModal({
|
void openBlockedServicesModal({
|
||||||
required BuildContext context,
|
required BuildContext context,
|
||||||
required double width,
|
required double width,
|
||||||
|
|
|
@ -5,9 +5,9 @@ class DeleteListModal extends StatelessWidget {
|
||||||
final void Function() onConfirm;
|
final void Function() onConfirm;
|
||||||
|
|
||||||
const DeleteListModal({
|
const DeleteListModal({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.onConfirm
|
required this.onConfirm
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
|
|
@ -66,10 +66,12 @@ class _UpdateIntervalListsModalState extends State<UpdateIntervalListsModal> {
|
||||||
topRight: Radius.circular(28)
|
topRight: Radius.circular(28)
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
child: _Content(
|
child: SafeArea(
|
||||||
selectedOption: selectedOption,
|
child: _Content(
|
||||||
onUpdateValue: _updateRadioValue,
|
selectedOption: selectedOption,
|
||||||
onConfirm: () => widget.onChange(selectedOption!),
|
onUpdateValue: _updateRadioValue,
|
||||||
|
onConfirm: () => widget.onChange(selectedOption!),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -72,19 +72,18 @@ class SelectionSliverList extends StatelessWidget {
|
||||||
final void Function() unselectAll;
|
final void Function() unselectAll;
|
||||||
|
|
||||||
const SelectionSliverList({
|
const SelectionSliverList({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.lists,
|
required this.lists,
|
||||||
required this.selectedLists,
|
required this.selectedLists,
|
||||||
required this.onSelect,
|
required this.onSelect,
|
||||||
required this.selectAll,
|
required this.selectAll,
|
||||||
required this.unselectAll,
|
required this.unselectAll,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (BuildContext context) {
|
builder: (BuildContext context) {
|
||||||
return CustomScrollView(
|
return CustomScrollView(
|
||||||
|
|
|
@ -19,9 +19,9 @@ class ManagementModal extends StatefulWidget {
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const ManagementModal({
|
const ManagementModal({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ManagementModal> createState() => _ManagementModalState();
|
State<ManagementModal> createState() => _ManagementModalState();
|
||||||
|
@ -141,33 +141,35 @@ class _ManagementModalState extends State<ManagementModal> with SingleTickerProv
|
||||||
topRight: Radius.circular(28)
|
topRight: Radius.circular(28)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
child: Column(
|
child: SafeArea(
|
||||||
mainAxisSize: MainAxisSize.min,
|
child: Column(
|
||||||
children: [
|
mainAxisSize: MainAxisSize.min,
|
||||||
Flexible(
|
children: [
|
||||||
child: SingleChildScrollView(
|
Flexible(
|
||||||
child: _Modal(
|
child: SingleChildScrollView(
|
||||||
expandableController: expandableController,
|
child: _Modal(
|
||||||
updateBlocking: updateBlocking,
|
expandableController: expandableController,
|
||||||
disableWithCountdown: disableWithCountdown,
|
updateBlocking: updateBlocking,
|
||||||
animation: animation,
|
disableWithCountdown: disableWithCountdown,
|
||||||
)
|
animation: animation,
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.all(24),
|
||||||
padding: const EdgeInsets.all(24),
|
child: Row(
|
||||||
child: Row(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
children: [
|
||||||
children: [
|
TextButton(
|
||||||
TextButton(
|
onPressed: () => Navigator.pop(context),
|
||||||
onPressed: () => Navigator.pop(context),
|
child: Text(AppLocalizations.of(context)!.close),
|
||||||
child: Text(AppLocalizations.of(context)!.close),
|
),
|
||||||
),
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
if (Platform.isIOS) const SizedBox(height: 16)
|
||||||
if (Platform.isIOS) const SizedBox(height: 16)
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -201,24 +203,24 @@ class _Modal extends StatelessWidget {
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(top: 24),
|
padding: const EdgeInsets.only(top: 24),
|
||||||
child: Icon(
|
child: Icon(
|
||||||
Icons.shield_rounded,
|
Icons.shield_rounded,
|
||||||
size: 24,
|
size: 24,
|
||||||
color: Theme.of(context).listTileTheme.iconColor
|
color: Theme.of(context).listTileTheme.iconColor
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(vertical: 16),
|
padding: const EdgeInsets.symmetric(vertical: 16),
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context)!.manageServer,
|
AppLocalizations.of(context)!.manageServer,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 24,
|
fontSize: 24,
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
|
|
|
@ -122,14 +122,16 @@ class _TopItemsScreenState extends State<TopItemsScreen> {
|
||||||
const SizedBox(width: 8)
|
const SizedBox(width: 8)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: _Content(
|
body: SafeArea(
|
||||||
buildValue: widget.buildValue,
|
child: _Content(
|
||||||
isClient: widget.isClient,
|
buildValue: widget.buildValue,
|
||||||
onTapEntry: widget.onTapEntry,
|
isClient: widget.isClient,
|
||||||
options: widget.options,
|
onTapEntry: widget.onTapEntry,
|
||||||
screenData: screenData,
|
options: widget.options,
|
||||||
total: total,
|
screenData: screenData,
|
||||||
withProgressBar: widget.withProgressBar,
|
total: total,
|
||||||
|
withProgressBar: widget.withProgressBar,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -148,36 +148,38 @@ class _LogsConfigModalState extends State<LogsConfigModal> {
|
||||||
),
|
),
|
||||||
color: Theme.of(context).dialogBackgroundColor
|
color: Theme.of(context).dialogBackgroundColor
|
||||||
),
|
),
|
||||||
child: Builder(
|
child: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (loadStatus) {
|
||||||
return const ConfigLogsLoading();
|
case LoadStatus.loading:
|
||||||
|
return const ConfigLogsLoading();
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
case LoadStatus.loaded:
|
||||||
return LogsConfigOptions(
|
return LogsConfigOptions(
|
||||||
generalSwitch: generalSwitch,
|
generalSwitch: generalSwitch,
|
||||||
updateGeneralSwitch: (v) => setState(() => generalSwitch = v),
|
updateGeneralSwitch: (v) => setState(() => generalSwitch = v),
|
||||||
anonymizeClientIp: anonymizeClientIp,
|
anonymizeClientIp: anonymizeClientIp,
|
||||||
updateAnonymizeClientIp: (v) => setState(() => anonymizeClientIp = v),
|
updateAnonymizeClientIp: (v) => setState(() => anonymizeClientIp = v),
|
||||||
retentionItems: retentionItems,
|
retentionItems: retentionItems,
|
||||||
retentionTime: retentionTime,
|
retentionTime: retentionTime,
|
||||||
updateRetentionTime: (v) => setState(() => retentionTime = v),
|
updateRetentionTime: (v) => setState(() => retentionTime = v),
|
||||||
onClear: () => widget.onClear(),
|
onClear: () => widget.onClear(),
|
||||||
onConfirm: () => widget.onConfirm({
|
onConfirm: () => widget.onConfirm({
|
||||||
"enabled": generalSwitch,
|
"enabled": generalSwitch,
|
||||||
"interval": retentionTime,
|
"interval": retentionTime,
|
||||||
"anonymize_client_ip": anonymizeClientIp
|
"anonymize_client_ip": anonymizeClientIp
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
case LoadStatus.error:
|
case LoadStatus.error:
|
||||||
return const ConfigLogsError();
|
return const ConfigLogsError();
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,10 +25,10 @@ class LogDetailsScreen extends StatelessWidget {
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const LogDetailsScreen({
|
const LogDetailsScreen({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.log,
|
required this.log,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -322,7 +322,6 @@ class LogDetailsScreen extends StatelessWidget {
|
||||||
],
|
],
|
||||||
body: SafeArea(
|
body: SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (context) => CustomScrollView(
|
builder: (context) => CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
|
|
|
@ -60,9 +60,11 @@ class _ClientsModalState extends State<ClientsModal> {
|
||||||
),
|
),
|
||||||
color: Theme.of(context).dialogBackgroundColor
|
color: Theme.of(context).dialogBackgroundColor
|
||||||
),
|
),
|
||||||
child: _ModalContent(
|
child: SafeArea(
|
||||||
selectedClients: selectedClients,
|
child: _ModalContent(
|
||||||
onClientsSelected: (v) => setState(() => selectedClients = v),
|
selectedClients: selectedClients,
|
||||||
|
onClientsSelected: (v) => setState(() => selectedClients = v),
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -62,10 +62,12 @@ class _FilterStatusModalState extends State<FilterStatusModal> {
|
||||||
),
|
),
|
||||||
color: Theme.of(context).dialogBackgroundColor
|
color: Theme.of(context).dialogBackgroundColor
|
||||||
),
|
),
|
||||||
child: _Content(
|
child: SafeArea(
|
||||||
onApply: apply,
|
child: _Content(
|
||||||
updateSelectedResultStatus: (v) => setState(() => selectedResultStatus = v),
|
onApply: apply,
|
||||||
selectedResultStatus: selectedResultStatus,
|
updateSelectedResultStatus: (v) => setState(() => selectedResultStatus = v),
|
||||||
|
selectedResultStatus: selectedResultStatus,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,9 @@ class LogsFiltersModal extends StatefulWidget {
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const LogsFiltersModal({
|
const LogsFiltersModal({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<LogsFiltersModal> createState() => _LogsFiltersModalState();
|
State<LogsFiltersModal> createState() => _LogsFiltersModalState();
|
||||||
|
@ -65,9 +65,11 @@ class _LogsFiltersModalState extends State<LogsFiltersModal> {
|
||||||
topRight: Radius.circular(28)
|
topRight: Radius.circular(28)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
child: _FiltersList(
|
child: SafeArea(
|
||||||
searchController: searchController,
|
child: _FiltersList(
|
||||||
onClearSearch: () => setState(() => searchController.text = "")
|
searchController: searchController,
|
||||||
|
onClearSearch: () => setState(() => searchController.text = "")
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,9 +16,9 @@ class Servers extends StatefulWidget {
|
||||||
final double? breakingWidth;
|
final double? breakingWidth;
|
||||||
|
|
||||||
const Servers({
|
const Servers({
|
||||||
Key? key,
|
super.key,
|
||||||
this.breakingWidth
|
this.breakingWidth
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<Servers> createState() => _ServersState();
|
State<Servers> createState() => _ServersState();
|
||||||
|
@ -77,29 +77,31 @@ class _ServersState extends State<Servers> {
|
||||||
title: Text(AppLocalizations.of(context)!.servers),
|
title: Text(AppLocalizations.of(context)!.servers),
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
ServersList(
|
children: [
|
||||||
context: context,
|
ServersList(
|
||||||
controllers: expandableControllerList,
|
context: context,
|
||||||
onChange: expandOrContract,
|
controllers: expandableControllerList,
|
||||||
scrollController: scrollController,
|
onChange: expandOrContract,
|
||||||
breakingWidth: widget.breakingWidth ?? 700,
|
scrollController: scrollController,
|
||||||
),
|
breakingWidth: widget.breakingWidth ?? 700,
|
||||||
AnimatedPositioned(
|
|
||||||
duration: const Duration(milliseconds: 100),
|
|
||||||
curve: Curves.easeInOut,
|
|
||||||
bottom: isVisible ?
|
|
||||||
appConfigProvider.showingSnackbar
|
|
||||||
? 70 : (Platform.isIOS ? 40 : 20)
|
|
||||||
: -70,
|
|
||||||
right: 20,
|
|
||||||
child: FloatingActionButton(
|
|
||||||
onPressed: openAddServerModal,
|
|
||||||
child: const Icon(Icons.add),
|
|
||||||
),
|
),
|
||||||
),
|
AnimatedPositioned(
|
||||||
],
|
duration: const Duration(milliseconds: 100),
|
||||||
|
curve: Curves.easeInOut,
|
||||||
|
bottom: isVisible ?
|
||||||
|
appConfigProvider.showingSnackbar
|
||||||
|
? 70 : (Platform.isIOS ? 40 : 20)
|
||||||
|
: -70,
|
||||||
|
right: 20,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: openAddServerModal,
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,11 @@ class AddClientModal extends StatelessWidget {
|
||||||
topRight: Radius.circular(28)
|
topRight: Radius.circular(28)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
child: _Content(
|
child: SafeArea(
|
||||||
type: type,
|
child: _Content(
|
||||||
onConfirm: onConfirm,
|
type: type,
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
|
||||||
class AdvancedSettings extends StatelessWidget {
|
class AdvancedSettings extends StatelessWidget {
|
||||||
const AdvancedSettings({Key? key}) : super(key: key);
|
const AdvancedSettings({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -46,31 +46,33 @@ class AdvancedSettings extends StatelessWidget {
|
||||||
title: Text(AppLocalizations.of(context)!.advancedSettings),
|
title: Text(AppLocalizations.of(context)!.advancedSettings),
|
||||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
children: [
|
child: ListView(
|
||||||
CustomListTile(
|
children: [
|
||||||
icon: Icons.lock,
|
CustomListTile(
|
||||||
title: AppLocalizations.of(context)!.dontCheckCertificate,
|
icon: Icons.lock,
|
||||||
subtitle: AppLocalizations.of(context)!.dontCheckCertificateDescription,
|
title: AppLocalizations.of(context)!.dontCheckCertificate,
|
||||||
trailing: Switch(
|
subtitle: AppLocalizations.of(context)!.dontCheckCertificateDescription,
|
||||||
value: appConfigProvider.overrideSslCheck,
|
trailing: Switch(
|
||||||
onChanged: (value) => updateSettings(
|
value: appConfigProvider.overrideSslCheck,
|
||||||
newStatus: value,
|
onChanged: (value) => updateSettings(
|
||||||
|
newStatus: value,
|
||||||
|
function: appConfigProvider.setOverrideSslCheck
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => updateSettings(
|
||||||
|
newStatus: !appConfigProvider.overrideSslCheck,
|
||||||
function: appConfigProvider.setOverrideSslCheck
|
function: appConfigProvider.setOverrideSslCheck
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 20,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
],
|
||||||
newStatus: !appConfigProvider.overrideSslCheck,
|
),
|
||||||
function: appConfigProvider.setOverrideSslCheck
|
|
||||||
),
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 10,
|
|
||||||
bottom: 10,
|
|
||||||
left: 20,
|
|
||||||
right: 10
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
import 'package:adguard_home_manager/constants/colors.dart';
|
import 'package:adguard_home_manager/constants/colors.dart';
|
||||||
|
|
||||||
class Customization extends StatelessWidget {
|
class Customization extends StatelessWidget {
|
||||||
const Customization({Key? key}) : super(key: key);
|
const Customization({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -65,152 +65,154 @@ class _CustomizationWidgetState extends State<CustomizationWidget> {
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
children: [
|
child: ListView(
|
||||||
SectionLabel(
|
children: [
|
||||||
label: AppLocalizations.of(context)!.theme,
|
SectionLabel(
|
||||||
padding: const EdgeInsets.only(top: 10, left: 16, right: 16, bottom: 5),
|
label: AppLocalizations.of(context)!.theme,
|
||||||
),
|
padding: const EdgeInsets.only(top: 10, left: 16, right: 16, bottom: 5),
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: selectedTheme == 0 ? true : false,
|
|
||||||
onChanged: (value) {
|
|
||||||
selectedTheme = value == true ? 0 : 1;
|
|
||||||
appConfigProvider.setSelectedTheme(value == true ? 0 : 1);
|
|
||||||
},
|
|
||||||
title: AppLocalizations.of(context)!.systemDefined,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
ThemeModeButton(
|
|
||||||
icon: Icons.light_mode,
|
|
||||||
value: 1,
|
|
||||||
selected: selectedTheme,
|
|
||||||
label: AppLocalizations.of(context)!.light,
|
|
||||||
onChanged: (value) {
|
|
||||||
selectedTheme = value;
|
|
||||||
appConfigProvider.setSelectedTheme(value);
|
|
||||||
},
|
|
||||||
disabled: selectedTheme == 0 ? true : false,
|
|
||||||
),
|
|
||||||
ThemeModeButton(
|
|
||||||
icon: Icons.dark_mode,
|
|
||||||
value: 2,
|
|
||||||
selected: selectedTheme,
|
|
||||||
label: AppLocalizations.of(context)!.dark,
|
|
||||||
onChanged: (value) {
|
|
||||||
selectedTheme = value;
|
|
||||||
appConfigProvider.setSelectedTheme(value);
|
|
||||||
},
|
|
||||||
disabled: selectedTheme == 0 ? true : false,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
SectionLabel(
|
|
||||||
label: AppLocalizations.of(context)!.color,
|
|
||||||
padding: const EdgeInsets.only(top: 45, left: 16, right: 16, bottom: 5),
|
|
||||||
),
|
|
||||||
if (appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt >= 31) CustomSwitchListTile(
|
|
||||||
value: dynamicColor,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => dynamicColor = value);
|
|
||||||
appConfigProvider.setUseDynamicColor(value);
|
|
||||||
},
|
|
||||||
title: AppLocalizations.of(context)!.useDynamicTheme,
|
|
||||||
),
|
|
||||||
if (!(appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt >= 31)) const SizedBox(height: 20),
|
|
||||||
if (dynamicColor == false) ...[
|
|
||||||
SizedBox(
|
|
||||||
width: MediaQuery.of(context).size.width,
|
|
||||||
height: 70,
|
|
||||||
child: ListView.builder(
|
|
||||||
scrollDirection: Axis.horizontal,
|
|
||||||
itemCount: colors.length,
|
|
||||||
itemBuilder: (context, index) {
|
|
||||||
if (index == 0) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
const SizedBox(width: 15),
|
|
||||||
ColorItem(
|
|
||||||
color: colors[index],
|
|
||||||
numericValue: index,
|
|
||||||
selectedValue: selectedColor,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => selectedColor = value);
|
|
||||||
appConfigProvider.setStaticColor(value);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
Container(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 10),
|
|
||||||
width: 1,
|
|
||||||
height: 60,
|
|
||||||
decoration: BoxDecoration(
|
|
||||||
color: Colors.grey,
|
|
||||||
borderRadius: BorderRadius.circular(1)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (index == colors.length-1) {
|
|
||||||
return Row(
|
|
||||||
children: [
|
|
||||||
ColorItem(
|
|
||||||
color: colors[index],
|
|
||||||
numericValue: index,
|
|
||||||
selectedValue: selectedColor,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => selectedColor = value);
|
|
||||||
appConfigProvider.setStaticColor(value);
|
|
||||||
}
|
|
||||||
),
|
|
||||||
const SizedBox(width: 15)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return ColorItem(
|
|
||||||
color: colors[index],
|
|
||||||
numericValue: index,
|
|
||||||
selectedValue: selectedColor,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => selectedColor = value);
|
|
||||||
appConfigProvider.setStaticColor(value);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
Padding(
|
Column(
|
||||||
padding: const EdgeInsets.only(
|
children: [
|
||||||
left: 25,
|
CustomSwitchListTile(
|
||||||
top: 10
|
value: selectedTheme == 0 ? true : false,
|
||||||
),
|
onChanged: (value) {
|
||||||
child: Text(
|
selectedTheme = value == true ? 0 : 1;
|
||||||
colorTranslation(context, selectedColor),
|
appConfigProvider.setSelectedTheme(value == true ? 0 : 1);
|
||||||
style: TextStyle(
|
},
|
||||||
color: Theme.of(context).listTileTheme.iconColor,
|
title: AppLocalizations.of(context)!.systemDefined,
|
||||||
fontSize: 16
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
ThemeModeButton(
|
||||||
|
icon: Icons.light_mode,
|
||||||
|
value: 1,
|
||||||
|
selected: selectedTheme,
|
||||||
|
label: AppLocalizations.of(context)!.light,
|
||||||
|
onChanged: (value) {
|
||||||
|
selectedTheme = value;
|
||||||
|
appConfigProvider.setSelectedTheme(value);
|
||||||
|
},
|
||||||
|
disabled: selectedTheme == 0 ? true : false,
|
||||||
|
),
|
||||||
|
ThemeModeButton(
|
||||||
|
icon: Icons.dark_mode,
|
||||||
|
value: 2,
|
||||||
|
selected: selectedTheme,
|
||||||
|
label: AppLocalizations.of(context)!.dark,
|
||||||
|
onChanged: (value) {
|
||||||
|
selectedTheme = value;
|
||||||
|
appConfigProvider.setSelectedTheme(value);
|
||||||
|
},
|
||||||
|
disabled: selectedTheme == 0 ? true : false,
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SectionLabel(
|
||||||
|
label: AppLocalizations.of(context)!.color,
|
||||||
|
padding: const EdgeInsets.only(top: 45, left: 16, right: 16, bottom: 5),
|
||||||
|
),
|
||||||
|
if (appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt >= 31) CustomSwitchListTile(
|
||||||
|
value: dynamicColor,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => dynamicColor = value);
|
||||||
|
appConfigProvider.setUseDynamicColor(value);
|
||||||
|
},
|
||||||
|
title: AppLocalizations.of(context)!.useDynamicTheme,
|
||||||
|
),
|
||||||
|
if (!(appConfigProvider.androidDeviceInfo != null && appConfigProvider.androidDeviceInfo!.version.sdkInt >= 31)) const SizedBox(height: 20),
|
||||||
|
if (dynamicColor == false) ...[
|
||||||
|
SizedBox(
|
||||||
|
width: MediaQuery.of(context).size.width,
|
||||||
|
height: 70,
|
||||||
|
child: ListView.builder(
|
||||||
|
scrollDirection: Axis.horizontal,
|
||||||
|
itemCount: colors.length,
|
||||||
|
itemBuilder: (context, index) {
|
||||||
|
if (index == 0) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
const SizedBox(width: 15),
|
||||||
|
ColorItem(
|
||||||
|
color: colors[index],
|
||||||
|
numericValue: index,
|
||||||
|
selectedValue: selectedColor,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => selectedColor = value);
|
||||||
|
appConfigProvider.setStaticColor(value);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
Container(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 10),
|
||||||
|
width: 1,
|
||||||
|
height: 60,
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Colors.grey,
|
||||||
|
borderRadius: BorderRadius.circular(1)
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else if (index == colors.length-1) {
|
||||||
|
return Row(
|
||||||
|
children: [
|
||||||
|
ColorItem(
|
||||||
|
color: colors[index],
|
||||||
|
numericValue: index,
|
||||||
|
selectedValue: selectedColor,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => selectedColor = value);
|
||||||
|
appConfigProvider.setStaticColor(value);
|
||||||
|
}
|
||||||
|
),
|
||||||
|
const SizedBox(width: 15)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return ColorItem(
|
||||||
|
color: colors[index],
|
||||||
|
numericValue: index,
|
||||||
|
selectedValue: selectedColor,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => selectedColor = value);
|
||||||
|
appConfigProvider.setStaticColor(value);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 25,
|
||||||
|
top: 10
|
||||||
|
),
|
||||||
|
child: Text(
|
||||||
|
colorTranslation(context, selectedColor),
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).listTileTheme.iconColor,
|
||||||
|
fontSize: 16
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
CustomSwitchListTile(
|
||||||
|
value: useThemeColorInsteadGreenRed,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => useThemeColorInsteadGreenRed = value);
|
||||||
|
appConfigProvider.setUseThemeColorForStatus(value);
|
||||||
|
},
|
||||||
|
title: AppLocalizations.of(context)!.useThemeColorStatus,
|
||||||
|
subtitle: AppLocalizations.of(context)!.useThemeColorStatusDescription,
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
CustomSwitchListTile(
|
),
|
||||||
value: useThemeColorInsteadGreenRed,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => useThemeColorInsteadGreenRed = value);
|
|
||||||
appConfigProvider.setUseThemeColorForStatus(value);
|
|
||||||
},
|
|
||||||
title: AppLocalizations.of(context)!.useThemeColorStatus,
|
|
||||||
subtitle: AppLocalizations.of(context)!.useThemeColorStatusDescription,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
import 'package:adguard_home_manager/models/dhcp.dart';
|
import 'package:adguard_home_manager/models/dhcp.dart';
|
||||||
|
|
||||||
class AddStaticLeaseModal extends StatefulWidget {
|
class AddStaticLeaseModal extends StatelessWidget {
|
||||||
final void Function(Lease) onConfirm;
|
final void Function(Lease) onConfirm;
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
|
@ -14,10 +14,49 @@ class AddStaticLeaseModal extends StatefulWidget {
|
||||||
});
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<AddStaticLeaseModal> createState() => _AddStaticLeaseModalState();
|
Widget build(BuildContext context) {
|
||||||
|
if (dialog == true) {
|
||||||
|
return Dialog(
|
||||||
|
child: ConstrainedBox(
|
||||||
|
constraints: const BoxConstraints(
|
||||||
|
maxWidth: 400
|
||||||
|
),
|
||||||
|
child: _Content(onConfirm: onConfirm)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Padding(
|
||||||
|
padding: MediaQuery.of(context).viewInsets,
|
||||||
|
child: Container(
|
||||||
|
decoration: BoxDecoration(
|
||||||
|
color: Theme.of(context).dialogBackgroundColor,
|
||||||
|
borderRadius: const BorderRadius.only(
|
||||||
|
topLeft: Radius.circular(28),
|
||||||
|
topRight: Radius.circular(28)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
child: SafeArea(
|
||||||
|
child: _Content(onConfirm: onConfirm)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
|
class _Content extends StatefulWidget {
|
||||||
|
final void Function(Lease) onConfirm;
|
||||||
|
|
||||||
|
const _Content({
|
||||||
|
required this.onConfirm
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_Content> createState() => __ContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __ContentState extends State<_Content> {
|
||||||
final TextEditingController macController = TextEditingController();
|
final TextEditingController macController = TextEditingController();
|
||||||
String? macError;
|
String? macError;
|
||||||
final TextEditingController ipController = TextEditingController();
|
final TextEditingController ipController = TextEditingController();
|
||||||
|
@ -67,175 +106,147 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget content() {
|
return Column(
|
||||||
return Column(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
Flexible(
|
||||||
Flexible(
|
child: SingleChildScrollView(
|
||||||
child: SingleChildScrollView(
|
child: Wrap(
|
||||||
child: Wrap(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(bottom: 16),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 24),
|
|
||||||
child: Icon(
|
|
||||||
Icons.add,
|
|
||||||
size: 24,
|
|
||||||
color: Theme.of(context).listTileTheme.iconColor
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.addStaticLease,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 24, right: 24, bottom: 12
|
|
||||||
),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: macController,
|
|
||||||
onChanged: validateMac,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.smartphone_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
errorText: macError,
|
|
||||||
labelText: AppLocalizations.of(context)!.macAddress,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: ipController,
|
|
||||||
onChanged: validateIp,
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.link_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
errorText: ipError,
|
|
||||||
labelText: AppLocalizations.of(context)!.ipAddress,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 24, right: 24, top: 12
|
|
||||||
),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: hostNameController,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != '') {
|
|
||||||
setState(() => hostNameError = null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(() => hostNameError = AppLocalizations.of(context)!.hostNameError);
|
|
||||||
}
|
|
||||||
validateData();
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.badge_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
errorText: hostNameError,
|
|
||||||
labelText: AppLocalizations.of(context)!.hostName,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
children: [
|
||||||
TextButton(
|
Padding(
|
||||||
onPressed: () => Navigator.pop(context),
|
padding: const EdgeInsets.only(bottom: 16),
|
||||||
child: Text(AppLocalizations.of(context)!.cancel),
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 24),
|
||||||
|
child: Icon(
|
||||||
|
Icons.add,
|
||||||
|
size: 24,
|
||||||
|
color: Theme.of(context).listTileTheme.iconColor
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.addStaticLease,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(width: 20),
|
Padding(
|
||||||
TextButton(
|
padding: const EdgeInsets.only(
|
||||||
onPressed: validData == true
|
left: 24, right: 24, bottom: 12
|
||||||
? () {
|
),
|
||||||
Navigator.pop(context);
|
child: TextFormField(
|
||||||
widget.onConfirm(
|
controller: macController,
|
||||||
Lease(
|
onChanged: validateMac,
|
||||||
mac: macController.text,
|
decoration: InputDecoration(
|
||||||
hostname: hostNameController.text,
|
prefixIcon: const Icon(Icons.smartphone_rounded),
|
||||||
ip: ipController.text
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
)
|
)
|
||||||
);
|
),
|
||||||
}
|
errorText: macError,
|
||||||
: null,
|
labelText: AppLocalizations.of(context)!.macAddress,
|
||||||
child: Text(
|
),
|
||||||
AppLocalizations.of(context)!.confirm,
|
),
|
||||||
style: TextStyle(
|
),
|
||||||
color: validData == true
|
Padding(
|
||||||
? Theme.of(context).colorScheme.primary
|
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
|
||||||
: Colors.grey
|
child: TextFormField(
|
||||||
|
controller: ipController,
|
||||||
|
onChanged: validateIp,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.link_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: ipError,
|
||||||
|
labelText: AppLocalizations.of(context)!.ipAddress,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 24, right: 24, top: 12
|
||||||
|
),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: hostNameController,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != '') {
|
||||||
|
setState(() => hostNameError = null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => hostNameError = AppLocalizations.of(context)!.hostNameError);
|
||||||
|
}
|
||||||
|
validateData();
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.badge_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: hostNameError,
|
||||||
|
labelText: AppLocalizations.of(context)!.hostName,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.dialog == true) {
|
|
||||||
return Dialog(
|
|
||||||
child: ConstrainedBox(
|
|
||||||
constraints: const BoxConstraints(
|
|
||||||
maxWidth: 400
|
|
||||||
),
|
),
|
||||||
child: content(),
|
|
||||||
),
|
),
|
||||||
);
|
Padding(
|
||||||
}
|
padding: const EdgeInsets.all(24),
|
||||||
else {
|
child: Row(
|
||||||
return Padding(
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
padding: MediaQuery.of(context).viewInsets,
|
children: [
|
||||||
child: Container(
|
TextButton(
|
||||||
decoration: BoxDecoration(
|
onPressed: () => Navigator.pop(context),
|
||||||
color: Theme.of(context).dialogBackgroundColor,
|
child: Text(AppLocalizations.of(context)!.cancel),
|
||||||
borderRadius: const BorderRadius.only(
|
),
|
||||||
topLeft: Radius.circular(28),
|
const SizedBox(width: 20),
|
||||||
topRight: Radius.circular(28)
|
TextButton(
|
||||||
)
|
onPressed: validData == true
|
||||||
|
? () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
widget.onConfirm(
|
||||||
|
Lease(
|
||||||
|
mac: macController.text,
|
||||||
|
hostname: hostNameController.text,
|
||||||
|
ip: ipController.text
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.confirm,
|
||||||
|
style: TextStyle(
|
||||||
|
color: validData == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.grey
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
child: content()
|
)
|
||||||
),
|
],
|
||||||
);
|
);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -411,328 +411,330 @@ class _DhcpScreenState extends State<DhcpScreen> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
] : null,
|
] : null,
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (dhcpProvider.loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (dhcpProvider.loadStatus) {
|
||||||
return SizedBox(
|
case LoadStatus.loading:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const CircularProgressIndicator(),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.loadingDhcp,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
|
||||||
if (selectedInterface != null) {
|
|
||||||
return SingleChildScrollView(
|
|
||||||
child: Wrap(
|
|
||||||
children: [
|
children: [
|
||||||
DhcpMainButton(
|
const CircularProgressIndicator(),
|
||||||
selectedInterface: selectedInterface,
|
const SizedBox(height: 30),
|
||||||
enabled: enabled,
|
Text(
|
||||||
setEnabled: (v) => setState(() => enabled = v)
|
AppLocalizations.of(context)!.loadingDhcp,
|
||||||
),
|
textAlign: TextAlign.center,
|
||||||
if (selectedInterface!.ipv4Addresses.isNotEmpty) ...[
|
style: TextStyle(
|
||||||
SectionLabel(
|
fontSize: 22,
|
||||||
label: AppLocalizations.of(context)!.ipv4settings,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
top: 24, left: 16, right: 16, bottom: 8
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
_DhcpField(
|
)
|
||||||
icon: Icons.skip_previous_rounded,
|
|
||||||
label: AppLocalizations.of(context)!.startOfRange,
|
|
||||||
controller: ipv4StartRangeController,
|
|
||||||
onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid),
|
|
||||||
error: ipv4StartRangeError
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.skip_next_rounded,
|
|
||||||
label: AppLocalizations.of(context)!.endOfRange,
|
|
||||||
controller: ipv4EndRangeController,
|
|
||||||
onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid),
|
|
||||||
error: ipv4EndRangeError
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.hub_rounded,
|
|
||||||
label: AppLocalizations.of(context)!.subnetMask,
|
|
||||||
controller: ipv4SubnetMaskController,
|
|
||||||
onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid),
|
|
||||||
error: ipv4SubnetMaskError
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.router_rounded,
|
|
||||||
label: AppLocalizations.of(context)!.gateway,
|
|
||||||
controller: ipv4GatewayController,
|
|
||||||
onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid),
|
|
||||||
error: ipv4GatewayError
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.timer,
|
|
||||||
label: AppLocalizations.of(context)!.leaseTime,
|
|
||||||
controller: ipv4LeaseTimeController,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (int.tryParse(value).runtimeType == int) {
|
|
||||||
setState(() => ipv4LeaseTimeError = null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(() => ipv4LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: ipv4LeaseTimeError
|
|
||||||
),
|
|
||||||
],
|
|
||||||
if (selectedInterface!.ipv6Addresses.isNotEmpty) ...[
|
|
||||||
SectionLabel(
|
|
||||||
label: AppLocalizations.of(context)!.ipv6settings,
|
|
||||||
padding: const EdgeInsets.all(16)
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.skip_next_rounded,
|
|
||||||
label: AppLocalizations.of(context)!.startOfRange,
|
|
||||||
controller: ipv6StartRangeController,
|
|
||||||
onChanged: (value) => validateIpV6(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid),
|
|
||||||
error: ipv6StartRangeError
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.skip_previous_rounded,
|
|
||||||
label: AppLocalizations.of(context)!.endOfRange,
|
|
||||||
controller: ipv6EndRangeController,
|
|
||||||
onChanged: (value) => validateIpV6(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid),
|
|
||||||
error: ipv6EndRangeError
|
|
||||||
),
|
|
||||||
_DhcpField(
|
|
||||||
icon: Icons.timer,
|
|
||||||
label: AppLocalizations.of(context)!.leaseTime,
|
|
||||||
controller: ipv6LeaseTimeController,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (int.tryParse(value).runtimeType == int) {
|
|
||||||
setState(() => ipv6LeaseTimeError = null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(() => ipv6LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
error: ipv6LeaseTimeError
|
|
||||||
)
|
|
||||||
],
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
SectionLabel(
|
|
||||||
label: AppLocalizations.of(context)!.dhcpLeases,
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
),
|
|
||||||
if (width <= 900) Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => DhcpLeases(
|
|
||||||
items: dhcpProvider.dhcp!.dhcpStatus!.leases,
|
|
||||||
staticLeases: false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.dhcpLeases,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_rounded,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (width <= 900) Material(
|
|
||||||
color: Colors.transparent,
|
|
||||||
child: InkWell(
|
|
||||||
onTap: () {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => DhcpLeases(
|
|
||||||
items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases,
|
|
||||||
staticLeases: true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
child: Container(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.dhcpStatic,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.arrow_forward_rounded,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
if (width > 900) Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
children: [
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (!(Platform.isAndroid || Platform.isIOS)) {
|
|
||||||
SplitView.of(context).push(
|
|
||||||
DhcpLeases(
|
|
||||||
items: dhcpProvider.dhcp!.dhcpStatus!.leases,
|
|
||||||
staticLeases: false,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => DhcpLeases(
|
|
||||||
items: dhcpProvider.dhcp!.dhcpStatus!.leases,
|
|
||||||
staticLeases: false,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(context)!.dhcpLeases),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Icon(Icons.arrow_forward_rounded)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
),
|
|
||||||
ElevatedButton(
|
|
||||||
onPressed: () {
|
|
||||||
if (!(Platform.isAndroid || Platform.isIOS)) {
|
|
||||||
SplitView.of(context).push(
|
|
||||||
DhcpLeases(
|
|
||||||
items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases,
|
|
||||||
staticLeases: true,
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => DhcpLeases(
|
|
||||||
items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases,
|
|
||||||
staticLeases: true,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Text(AppLocalizations.of(context)!.dhcpStatic),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
const Icon(Icons.arrow_forward_rounded)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
else {
|
case LoadStatus.loaded:
|
||||||
return Row(
|
if (selectedInterface != null) {
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
return SingleChildScrollView(
|
||||||
children: [
|
child: Wrap(
|
||||||
Flexible(
|
children: [
|
||||||
child: Column(
|
DhcpMainButton(
|
||||||
mainAxisSize: MainAxisSize.max,
|
selectedInterface: selectedInterface,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
enabled: enabled,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
setEnabled: (v) => setState(() => enabled = v)
|
||||||
children: [
|
),
|
||||||
Padding(
|
if (selectedInterface!.ipv4Addresses.isNotEmpty) ...[
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
SectionLabel(
|
||||||
child: Text(
|
label: AppLocalizations.of(context)!.ipv4settings,
|
||||||
AppLocalizations.of(context)!.neededSelectInterface,
|
padding: const EdgeInsets.only(
|
||||||
textAlign: TextAlign.center,
|
top: 24, left: 16, right: 16, bottom: 8
|
||||||
style: TextStyle(
|
)
|
||||||
fontSize: 22,
|
),
|
||||||
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5)
|
_DhcpField(
|
||||||
|
icon: Icons.skip_previous_rounded,
|
||||||
|
label: AppLocalizations.of(context)!.startOfRange,
|
||||||
|
controller: ipv4StartRangeController,
|
||||||
|
onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid),
|
||||||
|
error: ipv4StartRangeError
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.skip_next_rounded,
|
||||||
|
label: AppLocalizations.of(context)!.endOfRange,
|
||||||
|
controller: ipv4EndRangeController,
|
||||||
|
onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid),
|
||||||
|
error: ipv4EndRangeError
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.hub_rounded,
|
||||||
|
label: AppLocalizations.of(context)!.subnetMask,
|
||||||
|
controller: ipv4SubnetMaskController,
|
||||||
|
onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid),
|
||||||
|
error: ipv4SubnetMaskError
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.router_rounded,
|
||||||
|
label: AppLocalizations.of(context)!.gateway,
|
||||||
|
controller: ipv4GatewayController,
|
||||||
|
onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid),
|
||||||
|
error: ipv4GatewayError
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.timer,
|
||||||
|
label: AppLocalizations.of(context)!.leaseTime,
|
||||||
|
controller: ipv4LeaseTimeController,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (int.tryParse(value).runtimeType == int) {
|
||||||
|
setState(() => ipv4LeaseTimeError = null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => ipv4LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: ipv4LeaseTimeError
|
||||||
|
),
|
||||||
|
],
|
||||||
|
if (selectedInterface!.ipv6Addresses.isNotEmpty) ...[
|
||||||
|
SectionLabel(
|
||||||
|
label: AppLocalizations.of(context)!.ipv6settings,
|
||||||
|
padding: const EdgeInsets.all(16)
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.skip_next_rounded,
|
||||||
|
label: AppLocalizations.of(context)!.startOfRange,
|
||||||
|
controller: ipv6StartRangeController,
|
||||||
|
onChanged: (value) => validateIpV6(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid),
|
||||||
|
error: ipv6StartRangeError
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.skip_previous_rounded,
|
||||||
|
label: AppLocalizations.of(context)!.endOfRange,
|
||||||
|
controller: ipv6EndRangeController,
|
||||||
|
onChanged: (value) => validateIpV6(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid),
|
||||||
|
error: ipv6EndRangeError
|
||||||
|
),
|
||||||
|
_DhcpField(
|
||||||
|
icon: Icons.timer,
|
||||||
|
label: AppLocalizations.of(context)!.leaseTime,
|
||||||
|
controller: ipv6LeaseTimeController,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (int.tryParse(value).runtimeType == int) {
|
||||||
|
setState(() => ipv6LeaseTimeError = null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => ipv6LeaseTimeError = AppLocalizations.of(context)!.leaseTimeNotValid);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: ipv6LeaseTimeError
|
||||||
|
)
|
||||||
|
],
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
SectionLabel(
|
||||||
|
label: AppLocalizations.of(context)!.dhcpLeases,
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
),
|
||||||
|
if (width <= 900) Material(
|
||||||
|
color: Colors.transparent,
|
||||||
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DhcpLeases(
|
||||||
|
items: dhcpProvider.dhcp!.dhcpStatus!.leases,
|
||||||
|
staticLeases: false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.dhcpLeases,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_rounded,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
),
|
||||||
ElevatedButton(
|
if (width <= 900) Material(
|
||||||
onPressed: selectInterface,
|
color: Colors.transparent,
|
||||||
child: Text(AppLocalizations.of(context)!.selectInterface)
|
child: InkWell(
|
||||||
|
onTap: () {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DhcpLeases(
|
||||||
|
items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases,
|
||||||
|
staticLeases: true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
child: Container(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.dhcpStatic,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.arrow_forward_rounded,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
if (width > 900) Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
|
children: [
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (!(Platform.isAndroid || Platform.isIOS)) {
|
||||||
|
SplitView.of(context).push(
|
||||||
|
DhcpLeases(
|
||||||
|
items: dhcpProvider.dhcp!.dhcpStatus!.leases,
|
||||||
|
staticLeases: false,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DhcpLeases(
|
||||||
|
items: dhcpProvider.dhcp!.dhcpStatus!.leases,
|
||||||
|
staticLeases: false,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(context)!.dhcpLeases),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Icon(Icons.arrow_forward_rounded)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: () {
|
||||||
|
if (!(Platform.isAndroid || Platform.isIOS)) {
|
||||||
|
SplitView.of(context).push(
|
||||||
|
DhcpLeases(
|
||||||
|
items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases,
|
||||||
|
staticLeases: true,
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Navigator.of(context).push(
|
||||||
|
MaterialPageRoute(
|
||||||
|
builder: (context) => DhcpLeases(
|
||||||
|
items: dhcpProvider.dhcp!.dhcpStatus!.staticLeases,
|
||||||
|
staticLeases: true,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Text(AppLocalizations.of(context)!.dhcpStatic),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
const Icon(Icons.arrow_forward_rounded)
|
||||||
|
],
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: Column(
|
||||||
|
mainAxisSize: MainAxisSize.max,
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.neededSelectInterface,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface.withOpacity(0.5)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: selectInterface,
|
||||||
|
child: Text(AppLocalizations.of(context)!.selectInterface)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
case LoadStatus.error:
|
||||||
|
return SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.dhcpSettingsNotLoaded,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
case LoadStatus.error:
|
default:
|
||||||
return SizedBox(
|
return const SizedBox();
|
||||||
width: double.maxFinite,
|
}
|
||||||
child: Column(
|
},
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
),
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.dhcpSettingsNotLoaded,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,10 +22,10 @@ class DhcpLeases extends StatelessWidget {
|
||||||
final bool staticLeases;
|
final bool staticLeases;
|
||||||
|
|
||||||
const DhcpLeases({
|
const DhcpLeases({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.items,
|
required this.items,
|
||||||
required this.staticLeases,
|
required this.staticLeases,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -130,32 +130,34 @@ class DhcpLeases extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
body: items.isNotEmpty
|
body: items.isNotEmpty
|
||||||
? ListView.builder(
|
? SafeArea(
|
||||||
padding: const EdgeInsets.only(top: 0),
|
child: ListView.builder(
|
||||||
itemCount: items.length,
|
padding: const EdgeInsets.only(top: 0),
|
||||||
itemBuilder: (context, index) => ListTile(
|
itemCount: items.length,
|
||||||
isThreeLine: true,
|
itemBuilder: (context, index) => ListTile(
|
||||||
title: Text(items[index].ip),
|
isThreeLine: true,
|
||||||
subtitle: Column(
|
title: Text(items[index].ip),
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
subtitle: Column(
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
Text(items[index].mac),
|
children: [
|
||||||
Text(items[index].hostname),
|
Text(items[index].mac),
|
||||||
],
|
Text(items[index].hostname),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
trailing: staticLeases == true
|
||||||
|
? IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
showModal(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DeleteStaticLeaseModal(
|
||||||
|
onConfirm: () => deleteLease(items[index])
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.delete)
|
||||||
|
)
|
||||||
|
: null,
|
||||||
),
|
),
|
||||||
trailing: staticLeases == true
|
|
||||||
? IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
showModal(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => DeleteStaticLeaseModal(
|
|
||||||
onConfirm: () => deleteLease(items[index])
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.delete)
|
|
||||||
)
|
|
||||||
: null,
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
: Center(
|
: Center(
|
||||||
|
|
|
@ -137,16 +137,17 @@ class SelectInterfaceModal extends StatelessWidget {
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Expanded(
|
Expanded(
|
||||||
child: ListView.builder(
|
child: SafeArea(
|
||||||
controller: controller,
|
child: ListView.builder(
|
||||||
itemCount: interfaces.length,
|
controller: controller,
|
||||||
itemBuilder: (context, index) => DhcpInterfaceItem(
|
itemCount: interfaces.length,
|
||||||
networkInterface: interfaces[index],
|
itemBuilder: (context, index) => DhcpInterfaceItem(
|
||||||
onSelect: onSelect
|
networkInterface: interfaces[index],
|
||||||
)
|
onSelect: onSelect
|
||||||
|
)
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -117,105 +117,107 @@ class _BootstrapDnsScreenState extends State<BootstrapDnsScreen> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
child: ListView(
|
||||||
children: [
|
padding: const EdgeInsets.only(top: 10),
|
||||||
Card(
|
children: [
|
||||||
margin: const EdgeInsets.only(
|
Card(
|
||||||
left: 16, right: 16, bottom: 20
|
margin: const EdgeInsets.only(
|
||||||
),
|
left: 16, right: 16, bottom: 20
|
||||||
child: Padding(
|
),
|
||||||
padding: const EdgeInsets.all(20),
|
child: Padding(
|
||||||
child: Row(
|
padding: const EdgeInsets.all(20),
|
||||||
children: [
|
child: Row(
|
||||||
Icon(
|
children: [
|
||||||
Icons.info_rounded,
|
Icon(
|
||||||
color: Theme.of(context).listTileTheme.iconColor,
|
Icons.info_rounded,
|
||||||
),
|
color: Theme.of(context).listTileTheme.iconColor,
|
||||||
const SizedBox(width: 20),
|
),
|
||||||
Flexible(
|
const SizedBox(width: 20),
|
||||||
child: Text(
|
Flexible(
|
||||||
AppLocalizations.of(context)!.bootstrapDnsServersInfo,
|
child: Text(
|
||||||
style: TextStyle(
|
AppLocalizations.of(context)!.bootstrapDnsServersInfo,
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
style: TextStyle(
|
||||||
),
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 10),
|
||||||
const SizedBox(height: 10),
|
if (bootstrapControllers.isEmpty) Column(
|
||||||
if (bootstrapControllers.isEmpty) Column(
|
children: [
|
||||||
children: [
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.all(10),
|
||||||
padding: const EdgeInsets.all(10),
|
child: Center(
|
||||||
child: Center(
|
child: Text(
|
||||||
child: Text(
|
AppLocalizations.of(context)!.noBootstrapDns,
|
||||||
AppLocalizations.of(context)!.noBootstrapDns,
|
style: TextStyle(
|
||||||
style: TextStyle(
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
fontSize: 16
|
||||||
fontSize: 16
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
...bootstrapControllers.map((c) => Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 16, right: 6, bottom: 20
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TextFormField(
|
|
||||||
controller: c['controller'],
|
|
||||||
onChanged: (value) => validateIp(c, value),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.dns_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
errorText: c['error'],
|
|
||||||
labelText: AppLocalizations.of(context)!.dnsServer,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => bootstrapControllers = bootstrapControllers.where((con) => con != c).toList());
|
|
||||||
checkValidValues();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.remove_circle_outline)
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)).toList(),
|
...bootstrapControllers.map((c) => Padding(
|
||||||
Row(
|
padding: const EdgeInsets.only(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
left: 16, right: 6, bottom: 20
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => bootstrapControllers.add({
|
|
||||||
'controller': TextEditingController(),
|
|
||||||
'error': null
|
|
||||||
}));
|
|
||||||
checkValidValues();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
label: Text(AppLocalizations.of(context)!.addItem)
|
|
||||||
),
|
),
|
||||||
],
|
child: Row(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
const SizedBox(height: 20)
|
children: [
|
||||||
],
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: c['controller'],
|
||||||
|
onChanged: (value) => validateIp(c, value),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.dns_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: c['error'],
|
||||||
|
labelText: AppLocalizations.of(context)!.dnsServer,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => bootstrapControllers = bootstrapControllers.where((con) => con != c).toList());
|
||||||
|
checkValidValues();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.remove_circle_outline)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)).toList(),
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => bootstrapControllers.add({
|
||||||
|
'controller': TextEditingController(),
|
||||||
|
'error': null
|
||||||
|
}));
|
||||||
|
checkValidValues();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: Text(AppLocalizations.of(context)!.addItem)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -169,81 +169,83 @@ class _CacheConfigDnsScreenState extends State<CacheConfigDnsScreen> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
child: ListView(
|
||||||
children: [
|
padding: const EdgeInsets.only(top: 10),
|
||||||
numericField(
|
children: [
|
||||||
controller: cacheSizeController,
|
numericField(
|
||||||
label: AppLocalizations.of(context)!.cacheSize,
|
controller: cacheSizeController,
|
||||||
helper: AppLocalizations.of(context)!.inBytes,
|
label: AppLocalizations.of(context)!.cacheSize,
|
||||||
error: cacheSizeError,
|
helper: AppLocalizations.of(context)!.inBytes,
|
||||||
onChanged: (value) {
|
error: cacheSizeError,
|
||||||
if (int.tryParse(value) != null) {
|
onChanged: (value) {
|
||||||
setState(() => cacheSizeError = null);
|
if (int.tryParse(value) != null) {
|
||||||
|
setState(() => cacheSizeError = null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => cacheSizeError = AppLocalizations.of(context)!.valueNotNumber);
|
||||||
|
}
|
||||||
|
checkValidData();
|
||||||
}
|
}
|
||||||
else {
|
),
|
||||||
setState(() => cacheSizeError = AppLocalizations.of(context)!.valueNotNumber);
|
const SizedBox(height: 30),
|
||||||
|
numericField(
|
||||||
|
controller: overrideMinTtlController,
|
||||||
|
label: AppLocalizations.of(context)!.overrideMinimumTtl,
|
||||||
|
helper: AppLocalizations.of(context)!.overrideMinimumTtlDescription,
|
||||||
|
error: overrideMinTtlError,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (int.tryParse(value) != null) {
|
||||||
|
setState(() => overrideMinTtlError = null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => overrideMinTtlError = AppLocalizations.of(context)!.valueNotNumber);
|
||||||
|
}
|
||||||
|
checkValidData();
|
||||||
}
|
}
|
||||||
checkValidData();
|
),
|
||||||
}
|
const SizedBox(height: 30),
|
||||||
),
|
numericField(
|
||||||
const SizedBox(height: 30),
|
controller: overrideMaxTtlController,
|
||||||
numericField(
|
label: AppLocalizations.of(context)!.overrideMaximumTtl,
|
||||||
controller: overrideMinTtlController,
|
helper: AppLocalizations.of(context)!.overrideMaximumTtlDescription,
|
||||||
label: AppLocalizations.of(context)!.overrideMinimumTtl,
|
error: overrideMaxTtlError,
|
||||||
helper: AppLocalizations.of(context)!.overrideMinimumTtlDescription,
|
onChanged: (value) {
|
||||||
error: overrideMinTtlError,
|
if (int.tryParse(value) != null) {
|
||||||
onChanged: (value) {
|
setState(() => overrideMaxTtlError = null);
|
||||||
if (int.tryParse(value) != null) {
|
}
|
||||||
setState(() => overrideMinTtlError = null);
|
else {
|
||||||
|
setState(() => overrideMaxTtlError = AppLocalizations.of(context)!.valueNotNumber);
|
||||||
|
}
|
||||||
|
checkValidData();
|
||||||
}
|
}
|
||||||
else {
|
),
|
||||||
setState(() => overrideMinTtlError = AppLocalizations.of(context)!.valueNotNumber);
|
const SizedBox(height: 10),
|
||||||
}
|
CustomSwitchListTile(
|
||||||
checkValidData();
|
value: optimisticCache,
|
||||||
}
|
onChanged: (value) => setState(() => optimisticCache = value),
|
||||||
),
|
title: AppLocalizations.of(context)!.optimisticCaching,
|
||||||
const SizedBox(height: 30),
|
subtitle: AppLocalizations.of(context)!.optimisticCachingDescription,
|
||||||
numericField(
|
),
|
||||||
controller: overrideMaxTtlController,
|
const SizedBox(height: 12),
|
||||||
label: AppLocalizations.of(context)!.overrideMaximumTtl,
|
Row(
|
||||||
helper: AppLocalizations.of(context)!.overrideMaximumTtlDescription,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
error: overrideMaxTtlError,
|
children: [
|
||||||
onChanged: (value) {
|
ElevatedButton.icon(
|
||||||
if (int.tryParse(value) != null) {
|
onPressed: () => showDialog(
|
||||||
setState(() => overrideMaxTtlError = null);
|
context: context,
|
||||||
}
|
builder: (context) => ClearDnsCacheDialog(
|
||||||
else {
|
onConfirm: clearCache
|
||||||
setState(() => overrideMaxTtlError = AppLocalizations.of(context)!.valueNotNumber);
|
)
|
||||||
}
|
),
|
||||||
checkValidData();
|
icon: const Icon(Icons.delete_rounded),
|
||||||
}
|
label: Text(AppLocalizations.of(context)!.clearDnsCache),
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: optimisticCache,
|
|
||||||
onChanged: (value) => setState(() => optimisticCache = value),
|
|
||||||
title: AppLocalizations.of(context)!.optimisticCaching,
|
|
||||||
subtitle: AppLocalizations.of(context)!.optimisticCachingDescription,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 12),
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => ClearDnsCacheDialog(
|
|
||||||
onConfirm: clearCache
|
|
||||||
)
|
|
||||||
),
|
),
|
||||||
icon: const Icon(Icons.delete_rounded),
|
],
|
||||||
label: Text(AppLocalizations.of(context)!.clearDnsCache),
|
),
|
||||||
),
|
const SizedBox(height: 16)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const SizedBox(height: 16)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,141 +1,30 @@
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
|
|
||||||
class CommentModal extends StatefulWidget {
|
class CommentModal extends StatelessWidget {
|
||||||
final String? comment;
|
final String? comment;
|
||||||
final void Function(String) onConfirm;
|
final void Function(String) onConfirm;
|
||||||
final bool dialog;
|
final bool dialog;
|
||||||
|
|
||||||
const CommentModal({
|
const CommentModal({
|
||||||
Key? key,
|
super.key,
|
||||||
this.comment,
|
this.comment,
|
||||||
required this.onConfirm,
|
required this.onConfirm,
|
||||||
required this.dialog
|
required this.dialog
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
|
||||||
State<CommentModal> createState() => _CommentModalState();
|
|
||||||
}
|
|
||||||
|
|
||||||
class _CommentModalState extends State<CommentModal> {
|
|
||||||
final TextEditingController commentController = TextEditingController();
|
|
||||||
|
|
||||||
bool validData = false;
|
|
||||||
|
|
||||||
@override
|
|
||||||
void initState() {
|
|
||||||
if (widget.comment != null) {
|
|
||||||
commentController.text = widget.comment!.replaceFirst(RegExp(r'#(\s)?'), "");
|
|
||||||
}
|
|
||||||
super.initState();
|
|
||||||
}
|
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
Widget content() {
|
if (dialog == true) {
|
||||||
return Column(
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
Flexible(
|
|
||||||
child: SingleChildScrollView(
|
|
||||||
child: Wrap(
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
children: [
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.only(top: 24),
|
|
||||||
child: Icon(
|
|
||||||
Icons.comment_rounded,
|
|
||||||
size: 24,
|
|
||||||
color: Theme.of(context).colorScheme.secondary,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.comment,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 24,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 16),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: commentController,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (value != '') {
|
|
||||||
setState(() => validData = true);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(() => validData = false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.comment_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
labelText: AppLocalizations.of(context)!.comment,
|
|
||||||
helperText: AppLocalizations.of(context)!.commentsDescription,
|
|
||||||
helperMaxLines: 3
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Padding(
|
|
||||||
padding: const EdgeInsets.all(24),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.end,
|
|
||||||
children: [
|
|
||||||
TextButton(
|
|
||||||
onPressed: () => Navigator.pop(context),
|
|
||||||
child: Text(AppLocalizations.of(context)!.cancel)
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
TextButton(
|
|
||||||
onPressed: validData == true
|
|
||||||
? () {
|
|
||||||
Navigator.pop(context);
|
|
||||||
widget.onConfirm("# ${commentController.text}");
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.confirm,
|
|
||||||
style: TextStyle(
|
|
||||||
color: validData == true
|
|
||||||
? Theme.of(context).colorScheme.primary
|
|
||||||
: Colors.grey
|
|
||||||
),
|
|
||||||
)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (widget.dialog == true) {
|
|
||||||
return Dialog(
|
return Dialog(
|
||||||
child: ConstrainedBox(
|
child: ConstrainedBox(
|
||||||
constraints: const BoxConstraints(
|
constraints: const BoxConstraints(
|
||||||
maxWidth: 400
|
maxWidth: 400
|
||||||
),
|
),
|
||||||
child: content()
|
child: _Content(
|
||||||
|
comment: comment,
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -150,9 +39,139 @@ class _CommentModalState extends State<CommentModal> {
|
||||||
),
|
),
|
||||||
color: Theme.of(context).dialogBackgroundColor
|
color: Theme.of(context).dialogBackgroundColor
|
||||||
),
|
),
|
||||||
child: content()
|
child: SafeArea(
|
||||||
|
child: _Content(
|
||||||
|
comment: comment,
|
||||||
|
onConfirm: onConfirm,
|
||||||
|
),
|
||||||
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _Content extends StatefulWidget {
|
||||||
|
final String? comment;
|
||||||
|
final void Function(String) onConfirm;
|
||||||
|
|
||||||
|
const _Content({
|
||||||
|
required this.comment,
|
||||||
|
required this.onConfirm
|
||||||
|
});
|
||||||
|
|
||||||
|
@override
|
||||||
|
State<_Content> createState() => __ContentState();
|
||||||
|
}
|
||||||
|
|
||||||
|
class __ContentState extends State<_Content> {
|
||||||
|
final TextEditingController commentController = TextEditingController();
|
||||||
|
|
||||||
|
bool validData = false;
|
||||||
|
|
||||||
|
@override
|
||||||
|
void initState() {
|
||||||
|
if (widget.comment != null) {
|
||||||
|
commentController.text = widget.comment!.replaceFirst(RegExp(r'#(\s)?'), "");
|
||||||
|
}
|
||||||
|
super.initState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context) {
|
||||||
|
return Column(
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
Flexible(
|
||||||
|
child: SingleChildScrollView(
|
||||||
|
child: Wrap(
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
children: [
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(top: 24),
|
||||||
|
child: Icon(
|
||||||
|
Icons.comment_rounded,
|
||||||
|
size: 24,
|
||||||
|
color: Theme.of(context).colorScheme.secondary,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.comment,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 24,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: commentController,
|
||||||
|
onChanged: (value) {
|
||||||
|
if (value != '') {
|
||||||
|
setState(() => validData = true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => validData = false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.comment_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
labelText: AppLocalizations.of(context)!.comment,
|
||||||
|
helperText: AppLocalizations.of(context)!.commentsDescription,
|
||||||
|
helperMaxLines: 3
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.all(24),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.end,
|
||||||
|
children: [
|
||||||
|
TextButton(
|
||||||
|
onPressed: () => Navigator.pop(context),
|
||||||
|
child: Text(AppLocalizations.of(context)!.cancel)
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
TextButton(
|
||||||
|
onPressed: validData == true
|
||||||
|
? () {
|
||||||
|
Navigator.pop(context);
|
||||||
|
widget.onConfirm("# ${commentController.text}");
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.confirm,
|
||||||
|
style: TextStyle(
|
||||||
|
color: validData == true
|
||||||
|
? Theme.of(context).colorScheme.primary
|
||||||
|
: Colors.grey
|
||||||
|
),
|
||||||
|
)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,9 +25,9 @@ class DnsSettings extends StatefulWidget {
|
||||||
final bool splitView;
|
final bool splitView;
|
||||||
|
|
||||||
const DnsSettings({
|
const DnsSettings({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.splitView,
|
required this.splitView,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<DnsSettings> createState() => _DnsSettingsState();
|
State<DnsSettings> createState() => _DnsSettingsState();
|
||||||
|
@ -118,95 +118,97 @@ class _DnsSettingsState extends State<DnsSettings> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (dnsProvider.loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (dnsProvider.loadStatus) {
|
||||||
return SizedBox(
|
case LoadStatus.loading:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const CircularProgressIndicator(),
|
children: [
|
||||||
const SizedBox(height: 30),
|
const CircularProgressIndicator(),
|
||||||
Text(
|
const SizedBox(height: 30),
|
||||||
AppLocalizations.of(context)!.loadingDnsConfig,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
AppLocalizations.of(context)!.loadingDnsConfig,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.center,
|
||||||
fontSize: 22,
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
fontSize: 22,
|
||||||
),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
)
|
],
|
||||||
);
|
)
|
||||||
|
);
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
case LoadStatus.loaded:
|
||||||
return ListView(
|
return ListView(
|
||||||
children: [
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.upstreamDns,
|
|
||||||
subtitle: AppLocalizations.of(context)!.upstreamDnsDescription,
|
|
||||||
onTap: () => navigate(const UpstreamDnsScreen()),
|
|
||||||
icon: Icons.upload_rounded,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.bootstrapDns,
|
|
||||||
subtitle: AppLocalizations.of(context)!.bootstrapDnsDescription,
|
|
||||||
onTap: () => navigate(const BootstrapDnsScreen()),
|
|
||||||
icon: Icons.dns_rounded,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.privateReverseDnsServers,
|
|
||||||
subtitle: AppLocalizations.of(context)!.privateReverseDnsDescription,
|
|
||||||
onTap: () => navigate(const PrivateReverseDnsServersScreen()),
|
|
||||||
icon: Icons.person_rounded,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.dnsServerSettings,
|
|
||||||
subtitle: AppLocalizations.of(context)!.dnsServerSettingsDescription,
|
|
||||||
onTap: () => navigate(const DnsServerSettingsScreen()),
|
|
||||||
icon: Icons.settings,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.dnsCacheConfig,
|
|
||||||
subtitle: AppLocalizations.of(context)!.dnsCacheConfigDescription,
|
|
||||||
onTap: () => navigate(const CacheConfigDnsScreen()),
|
|
||||||
icon: Icons.storage_rounded,
|
|
||||||
),
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.error:
|
|
||||||
return SizedBox(
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
CustomListTile(
|
||||||
Icons.error,
|
title: AppLocalizations.of(context)!.upstreamDns,
|
||||||
color: Colors.red,
|
subtitle: AppLocalizations.of(context)!.upstreamDnsDescription,
|
||||||
size: 50,
|
onTap: () => navigate(const UpstreamDnsScreen()),
|
||||||
|
icon: Icons.upload_rounded,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.bootstrapDns,
|
||||||
|
subtitle: AppLocalizations.of(context)!.bootstrapDnsDescription,
|
||||||
|
onTap: () => navigate(const BootstrapDnsScreen()),
|
||||||
|
icon: Icons.dns_rounded,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.privateReverseDnsServers,
|
||||||
|
subtitle: AppLocalizations.of(context)!.privateReverseDnsDescription,
|
||||||
|
onTap: () => navigate(const PrivateReverseDnsServersScreen()),
|
||||||
|
icon: Icons.person_rounded,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.dnsServerSettings,
|
||||||
|
subtitle: AppLocalizations.of(context)!.dnsServerSettingsDescription,
|
||||||
|
onTap: () => navigate(const DnsServerSettingsScreen()),
|
||||||
|
icon: Icons.settings,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.dnsCacheConfig,
|
||||||
|
subtitle: AppLocalizations.of(context)!.dnsCacheConfigDescription,
|
||||||
|
onTap: () => navigate(const CacheConfigDnsScreen()),
|
||||||
|
icon: Icons.storage_rounded,
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.dnsConfigNotLoaded,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
case LoadStatus.error:
|
||||||
return const SizedBox();
|
return SizedBox(
|
||||||
}
|
width: double.maxFinite,
|
||||||
},
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.dnsConfigNotLoaded,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -167,141 +167,143 @@ class _DnsServerSettingsScreenState extends State<DnsServerSettingsScreen> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
child: ListView(
|
||||||
children: [
|
padding: const EdgeInsets.only(top: 10),
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: TextFormField(
|
|
||||||
controller: limitRequestsController,
|
|
||||||
onChanged: (value) {
|
|
||||||
if (int.tryParse(value) != null) {
|
|
||||||
setState(() => limitRequestsError = null);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(() => limitRequestsError = AppLocalizations.of(context)!.valueNotNumber);
|
|
||||||
}
|
|
||||||
validateData();
|
|
||||||
},
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.looks_one_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
labelText: AppLocalizations.of(context)!.limitRequestsSecond,
|
|
||||||
errorText: limitRequestsError
|
|
||||||
),
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: enableEdns,
|
|
||||||
onChanged: (value) => setState(() => enableEdns = value),
|
|
||||||
title: AppLocalizations.of(context)!.enableEdns,
|
|
||||||
subtitle: AppLocalizations.of(context)!.enableEdnsDescription,
|
|
||||||
),
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: enableDnssec,
|
|
||||||
onChanged: (value) => setState(() => enableDnssec = value),
|
|
||||||
title: AppLocalizations.of(context)!.enableDnssec,
|
|
||||||
subtitle: AppLocalizations.of(context)!.enableDnssecDescription,
|
|
||||||
),
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: disableIpv6Resolving,
|
|
||||||
onChanged: (value) => setState(() => disableIpv6Resolving = value),
|
|
||||||
title: AppLocalizations.of(context)!.disableResolvingIpv6,
|
|
||||||
subtitle: AppLocalizations.of(context)!.disableResolvingIpv6Description,
|
|
||||||
),
|
|
||||||
SectionLabel(label: AppLocalizations.of(context)!.blockingMode),
|
|
||||||
CustomRadioListTile(
|
|
||||||
groupValue: blockingMode,
|
|
||||||
value: "default",
|
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
|
||||||
title: AppLocalizations.of(context)!.defaultMode,
|
|
||||||
subtitle: AppLocalizations.of(context)!.defaultDescription,
|
|
||||||
onChanged: updateBlockingMode,
|
|
||||||
),
|
|
||||||
CustomRadioListTile(
|
|
||||||
groupValue: blockingMode,
|
|
||||||
value: "refused",
|
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
|
||||||
title: "REFUSED",
|
|
||||||
subtitle: AppLocalizations.of(context)!.refusedDescription,
|
|
||||||
onChanged: updateBlockingMode,
|
|
||||||
),
|
|
||||||
CustomRadioListTile(
|
|
||||||
groupValue: blockingMode,
|
|
||||||
value: "nxdomain",
|
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
|
||||||
title: "NXDOMAIN",
|
|
||||||
subtitle: AppLocalizations.of(context)!.nxdomainDescription,
|
|
||||||
onChanged: updateBlockingMode,
|
|
||||||
),
|
|
||||||
CustomRadioListTile(
|
|
||||||
groupValue: blockingMode,
|
|
||||||
value: "null_ip",
|
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
|
||||||
title: AppLocalizations.of(context)!.nullIp,
|
|
||||||
subtitle: AppLocalizations.of(context)!.nullIpDescription,
|
|
||||||
onChanged: updateBlockingMode,
|
|
||||||
),
|
|
||||||
CustomRadioListTile(
|
|
||||||
groupValue: blockingMode,
|
|
||||||
value: "custom_ip",
|
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
|
||||||
title: AppLocalizations.of(context)!.customIp,
|
|
||||||
subtitle: AppLocalizations.of(context)!.customIpDescription,
|
|
||||||
onChanged: updateBlockingMode,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
if (blockingMode == 'custom_ip') ...[
|
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
child: TextFormField(
|
child: TextFormField(
|
||||||
controller: ipv4controller,
|
controller: limitRequestsController,
|
||||||
onChanged: validateIpv4,
|
onChanged: (value) {
|
||||||
|
if (int.tryParse(value) != null) {
|
||||||
|
setState(() => limitRequestsError = null);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setState(() => limitRequestsError = AppLocalizations.of(context)!.valueNotNumber);
|
||||||
|
}
|
||||||
|
validateData();
|
||||||
|
},
|
||||||
decoration: InputDecoration(
|
decoration: InputDecoration(
|
||||||
prefixIcon: const Icon(Icons.link_rounded),
|
prefixIcon: const Icon(Icons.looks_one_rounded),
|
||||||
border: const OutlineInputBorder(
|
border: const OutlineInputBorder(
|
||||||
borderRadius: BorderRadius.all(
|
borderRadius: BorderRadius.all(
|
||||||
Radius.circular(10)
|
Radius.circular(10)
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
errorText: ipv4error,
|
labelText: AppLocalizations.of(context)!.limitRequestsSecond,
|
||||||
helperText: AppLocalizations.of(context)!.blockingIpv4Description,
|
errorText: limitRequestsError
|
||||||
helperMaxLines: 10,
|
|
||||||
labelText: AppLocalizations.of(context)!.blockingIpv4,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
const SizedBox(height: 10),
|
||||||
Padding(
|
CustomSwitchListTile(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 24),
|
value: enableEdns,
|
||||||
child: TextFormField(
|
onChanged: (value) => setState(() => enableEdns = value),
|
||||||
controller: ipv6controller,
|
title: AppLocalizations.of(context)!.enableEdns,
|
||||||
onChanged: validateIpv6,
|
subtitle: AppLocalizations.of(context)!.enableEdnsDescription,
|
||||||
decoration: InputDecoration(
|
),
|
||||||
prefixIcon: const Icon(Icons.link_rounded),
|
CustomSwitchListTile(
|
||||||
border: const OutlineInputBorder(
|
value: enableDnssec,
|
||||||
borderRadius: BorderRadius.all(
|
onChanged: (value) => setState(() => enableDnssec = value),
|
||||||
Radius.circular(10)
|
title: AppLocalizations.of(context)!.enableDnssec,
|
||||||
)
|
subtitle: AppLocalizations.of(context)!.enableDnssecDescription,
|
||||||
|
),
|
||||||
|
CustomSwitchListTile(
|
||||||
|
value: disableIpv6Resolving,
|
||||||
|
onChanged: (value) => setState(() => disableIpv6Resolving = value),
|
||||||
|
title: AppLocalizations.of(context)!.disableResolvingIpv6,
|
||||||
|
subtitle: AppLocalizations.of(context)!.disableResolvingIpv6Description,
|
||||||
|
),
|
||||||
|
SectionLabel(label: AppLocalizations.of(context)!.blockingMode),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: blockingMode,
|
||||||
|
value: "default",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: AppLocalizations.of(context)!.defaultMode,
|
||||||
|
subtitle: AppLocalizations.of(context)!.defaultDescription,
|
||||||
|
onChanged: updateBlockingMode,
|
||||||
|
),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: blockingMode,
|
||||||
|
value: "refused",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: "REFUSED",
|
||||||
|
subtitle: AppLocalizations.of(context)!.refusedDescription,
|
||||||
|
onChanged: updateBlockingMode,
|
||||||
|
),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: blockingMode,
|
||||||
|
value: "nxdomain",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: "NXDOMAIN",
|
||||||
|
subtitle: AppLocalizations.of(context)!.nxdomainDescription,
|
||||||
|
onChanged: updateBlockingMode,
|
||||||
|
),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: blockingMode,
|
||||||
|
value: "null_ip",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: AppLocalizations.of(context)!.nullIp,
|
||||||
|
subtitle: AppLocalizations.of(context)!.nullIpDescription,
|
||||||
|
onChanged: updateBlockingMode,
|
||||||
|
),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: blockingMode,
|
||||||
|
value: "custom_ip",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: AppLocalizations.of(context)!.customIp,
|
||||||
|
subtitle: AppLocalizations.of(context)!.customIpDescription,
|
||||||
|
onChanged: updateBlockingMode,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
if (blockingMode == 'custom_ip') ...[
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
|
child: TextFormField(
|
||||||
|
controller: ipv4controller,
|
||||||
|
onChanged: validateIpv4,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.link_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: ipv4error,
|
||||||
|
helperText: AppLocalizations.of(context)!.blockingIpv4Description,
|
||||||
|
helperMaxLines: 10,
|
||||||
|
labelText: AppLocalizations.of(context)!.blockingIpv4,
|
||||||
),
|
),
|
||||||
errorText: ipv6error,
|
keyboardType: TextInputType.number,
|
||||||
helperText: AppLocalizations.of(context)!.blockingIpv6Description,
|
|
||||||
helperMaxLines: 10,
|
|
||||||
labelText: AppLocalizations.of(context)!.blockingIpv6,
|
|
||||||
),
|
),
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 30),
|
||||||
const SizedBox(height: 30)
|
Padding(
|
||||||
]
|
padding: const EdgeInsets.symmetric(horizontal: 24),
|
||||||
],
|
child: TextFormField(
|
||||||
|
controller: ipv6controller,
|
||||||
|
onChanged: validateIpv6,
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.link_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: ipv6error,
|
||||||
|
helperText: AppLocalizations.of(context)!.blockingIpv6Description,
|
||||||
|
helperMaxLines: 10,
|
||||||
|
labelText: AppLocalizations.of(context)!.blockingIpv6,
|
||||||
|
),
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30)
|
||||||
|
]
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,7 +13,7 @@ import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
|
||||||
class PrivateReverseDnsServersScreen extends StatefulWidget {
|
class PrivateReverseDnsServersScreen extends StatefulWidget {
|
||||||
const PrivateReverseDnsServersScreen({Key? key}) : super(key: key);
|
const PrivateReverseDnsServersScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<PrivateReverseDnsServersScreen> createState() => _PrivateReverseDnsServersScreenState();
|
State<PrivateReverseDnsServersScreen> createState() => _PrivateReverseDnsServersScreenState();
|
||||||
|
@ -149,107 +149,40 @@ class _PrivateReverseDnsServersScreenState extends State<PrivateReverseDnsServer
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
child: ListView(
|
||||||
children: [
|
padding: const EdgeInsets.only(top: 10),
|
||||||
Card(
|
children: [
|
||||||
margin: const EdgeInsets.only(
|
Card(
|
||||||
left: 16, right: 16, bottom: 10
|
margin: const EdgeInsets.only(
|
||||||
),
|
left: 16, right: 16, bottom: 10
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(16),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.info_rounded,
|
|
||||||
color: Theme.of(context).listTileTheme.iconColor,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 16),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.privateReverseDnsServersDescription,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
),
|
child: Padding(
|
||||||
),
|
padding: const EdgeInsets.all(16),
|
||||||
if (editReverseResolvers == false) ...[
|
child: Row(
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(20),
|
Icon(
|
||||||
child: Text(
|
Icons.info_rounded,
|
||||||
"${AppLocalizations.of(context)!.reverseDnsDefault}:\n\n${defaultReverseResolvers.map((item) => item).join(', ').toString().replaceAll(RegExp(r'\(|\)'), '')}",
|
color: Theme.of(context).listTileTheme.iconColor,
|
||||||
textAlign: TextAlign.center,
|
),
|
||||||
style: TextStyle(
|
const SizedBox(width: 16),
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
Flexible(
|
||||||
fontSize: 16
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.privateReverseDnsServersDescription,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
Padding(
|
if (editReverseResolvers == false) ...[
|
||||||
padding: const EdgeInsets.only(top: 10, bottom: 20),
|
Padding(
|
||||||
child: Row(
|
padding: const EdgeInsets.all(20),
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => editReverseResolvers = true);
|
|
||||||
checkDataValid();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.edit),
|
|
||||||
label: Text(AppLocalizations.of(context)!.edit)
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
if (editReverseResolvers == true) ...[
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
...reverseResolversControllers.map((c) => Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 16, right: 6, bottom: 20
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Expanded(
|
|
||||||
child: TextFormField(
|
|
||||||
controller: c['controller'],
|
|
||||||
onChanged: (value) => validateAddress(c, value),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.dns_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
errorText: c['error'],
|
|
||||||
labelText: AppLocalizations.of(context)!.serverAddress,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => reverseResolversControllers = reverseResolversControllers.where((con) => con != c).toList());
|
|
||||||
checkDataValid();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.remove_circle_outline)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
)),
|
|
||||||
if (reverseResolversControllers.isEmpty) Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 20, right: 20, bottom: 20
|
|
||||||
),
|
|
||||||
child: Center(
|
|
||||||
child: Text(
|
child: Text(
|
||||||
AppLocalizations.of(context)!.noServerAddressesAdded,
|
"${AppLocalizations.of(context)!.reverseDnsDefault}:\n\n${defaultReverseResolvers.map((item) => item).join(', ').toString().replaceAll(RegExp(r'\(|\)'), '')}",
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
@ -257,41 +190,110 @@ class _PrivateReverseDnsServersScreenState extends State<PrivateReverseDnsServer
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.only(top: 10, bottom: 20),
|
||||||
padding: const EdgeInsets.only(bottom: 20),
|
child: Row(
|
||||||
child: Row(
|
mainAxisSize: MainAxisSize.min,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
mainAxisSize: MainAxisSize.min,
|
children: [
|
||||||
children: [
|
ElevatedButton.icon(
|
||||||
ElevatedButton.icon(
|
onPressed: () {
|
||||||
onPressed: () {
|
setState(() => editReverseResolvers = true);
|
||||||
setState(() => reverseResolversControllers.add({
|
checkDataValid();
|
||||||
'controller': TextEditingController(),
|
},
|
||||||
'error': null
|
icon: const Icon(Icons.edit),
|
||||||
}));
|
label: Text(AppLocalizations.of(context)!.edit)
|
||||||
checkDataValid();
|
),
|
||||||
},
|
],
|
||||||
icon: const Icon(Icons.add),
|
),
|
||||||
label: Text(AppLocalizations.of(context)!.addItem)
|
)
|
||||||
|
],
|
||||||
|
if (editReverseResolvers == true) ...[
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
...reverseResolversControllers.map((c) => Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16, right: 6, bottom: 20
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Expanded(
|
||||||
|
child: TextFormField(
|
||||||
|
controller: c['controller'],
|
||||||
|
onChanged: (value) => validateAddress(c, value),
|
||||||
|
decoration: InputDecoration(
|
||||||
|
prefixIcon: const Icon(Icons.dns_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
errorText: c['error'],
|
||||||
|
labelText: AppLocalizations.of(context)!.serverAddress,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => reverseResolversControllers = reverseResolversControllers.where((con) => con != c).toList());
|
||||||
|
checkDataValid();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.remove_circle_outline)
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
)),
|
||||||
|
if (reverseResolversControllers.isEmpty) Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 20, right: 20, bottom: 20
|
||||||
|
),
|
||||||
|
child: Center(
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.noServerAddressesAdded,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
fontSize: 16
|
||||||
|
),
|
||||||
),
|
),
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.only(bottom: 20),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
mainAxisSize: MainAxisSize.min,
|
||||||
|
children: [
|
||||||
|
ElevatedButton.icon(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => reverseResolversControllers.add({
|
||||||
|
'controller': TextEditingController(),
|
||||||
|
'error': null
|
||||||
|
}));
|
||||||
|
checkDataValid();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.add),
|
||||||
|
label: Text(AppLocalizations.of(context)!.addItem)
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
CustomSwitchListTile(
|
||||||
|
value: usePrivateReverseDnsResolvers,
|
||||||
|
onChanged: (value) => setState(() => usePrivateReverseDnsResolvers = value),
|
||||||
|
title: AppLocalizations.of(context)!.usePrivateReverseDnsResolvers,
|
||||||
|
subtitle: AppLocalizations.of(context)!.usePrivateReverseDnsResolversDescription
|
||||||
|
),
|
||||||
|
CustomSwitchListTile(
|
||||||
|
value: enableReverseResolve,
|
||||||
|
onChanged: (value) => setState(() => enableReverseResolve = value),
|
||||||
|
title: AppLocalizations.of(context)!.enableReverseResolving,
|
||||||
|
subtitle: AppLocalizations.of(context)!.enableReverseResolvingDescription
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
CustomSwitchListTile(
|
),
|
||||||
value: usePrivateReverseDnsResolvers,
|
|
||||||
onChanged: (value) => setState(() => usePrivateReverseDnsResolvers = value),
|
|
||||||
title: AppLocalizations.of(context)!.usePrivateReverseDnsResolvers,
|
|
||||||
subtitle: AppLocalizations.of(context)!.usePrivateReverseDnsResolversDescription
|
|
||||||
),
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: enableReverseResolve,
|
|
||||||
onChanged: (value) => setState(() => enableReverseResolve = value),
|
|
||||||
title: AppLocalizations.of(context)!.enableReverseResolving,
|
|
||||||
subtitle: AppLocalizations.of(context)!.enableReverseResolvingDescription
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -189,129 +189,131 @@ class _UpstreamDnsScreenState extends State<UpstreamDnsScreen> {
|
||||||
const SizedBox(width: 10)
|
const SizedBox(width: 10)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
padding: const EdgeInsets.only(top: 10),
|
child: ListView(
|
||||||
children: [
|
padding: const EdgeInsets.only(top: 10),
|
||||||
if (dnsServers.isEmpty) Column(
|
children: [
|
||||||
children: [
|
if (dnsServers.isEmpty) Column(
|
||||||
Padding(
|
children: [
|
||||||
padding: const EdgeInsets.all(10),
|
Padding(
|
||||||
child: Center(
|
padding: const EdgeInsets.all(10),
|
||||||
child: Text(
|
child: Center(
|
||||||
AppLocalizations.of(context)!.noUpstreamDns,
|
child: Text(
|
||||||
style: TextStyle(
|
AppLocalizations.of(context)!.noUpstreamDns,
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
style: TextStyle(
|
||||||
fontSize: 16
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
fontSize: 16
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 20),
|
||||||
const SizedBox(height: 20),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
...dnsServers.map((item) => Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 16, right: 6, bottom: 24
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
if (item['controller'] != null) Expanded(
|
|
||||||
child: TextFormField(
|
|
||||||
controller: item['controller'],
|
|
||||||
onChanged: (_) => checkValidValues(),
|
|
||||||
decoration: InputDecoration(
|
|
||||||
prefixIcon: const Icon(Icons.dns_rounded),
|
|
||||||
border: const OutlineInputBorder(
|
|
||||||
borderRadius: BorderRadius.all(
|
|
||||||
Radius.circular(10)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
labelText: AppLocalizations.of(context)!.dnsServer,
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(width: 8),
|
|
||||||
if (item['comment'] != null) Expanded(
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
item['comment'],
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 16,
|
|
||||||
color: Theme.of(context).listTileTheme.iconColor
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () => openEditCommentModal(item, dnsServers.indexOf(item)),
|
|
||||||
icon: const Icon(Icons.edit),
|
|
||||||
tooltip: AppLocalizations.of(context)!.edit,
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
IconButton(
|
|
||||||
onPressed: () {
|
|
||||||
setState(() => dnsServers = dnsServers.where((i) => i != item).toList());
|
|
||||||
checkValidValues();
|
|
||||||
},
|
|
||||||
icon: const Icon(Icons.remove_circle_outline),
|
|
||||||
tooltip: AppLocalizations.of(context)!.remove,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 4),
|
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
)).toList(),
|
...dnsServers.map((item) => Padding(
|
||||||
const SizedBox(height: 12),
|
padding: const EdgeInsets.only(
|
||||||
Row(
|
left: 16, right: 6, bottom: 24
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
|
||||||
mainAxisSize: MainAxisSize.min,
|
|
||||||
children: [
|
|
||||||
ElevatedButton.icon(
|
|
||||||
onPressed: openAddCommentModal,
|
|
||||||
icon: const Icon(Icons.add),
|
|
||||||
label: Text(AppLocalizations.of(context)!.comment)
|
|
||||||
),
|
),
|
||||||
ElevatedButton.icon(
|
child: Row(
|
||||||
onPressed: () {
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
setState(() => dnsServers.add({
|
children: [
|
||||||
'controller': TextEditingController()
|
if (item['controller'] != null) Expanded(
|
||||||
}));
|
child: TextFormField(
|
||||||
checkValidValues();
|
controller: item['controller'],
|
||||||
},
|
onChanged: (_) => checkValidValues(),
|
||||||
icon: const Icon(Icons.add),
|
decoration: InputDecoration(
|
||||||
label: Text(AppLocalizations.of(context)!.address)
|
prefixIcon: const Icon(Icons.dns_rounded),
|
||||||
|
border: const OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.all(
|
||||||
|
Radius.circular(10)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
labelText: AppLocalizations.of(context)!.dnsServer,
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
if (item['comment'] != null) Expanded(
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
item['comment'],
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 16,
|
||||||
|
color: Theme.of(context).listTileTheme.iconColor
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () => openEditCommentModal(item, dnsServers.indexOf(item)),
|
||||||
|
icon: const Icon(Icons.edit),
|
||||||
|
tooltip: AppLocalizations.of(context)!.edit,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
),
|
||||||
|
IconButton(
|
||||||
|
onPressed: () {
|
||||||
|
setState(() => dnsServers = dnsServers.where((i) => i != item).toList());
|
||||||
|
checkValidValues();
|
||||||
|
},
|
||||||
|
icon: const Icon(Icons.remove_circle_outline),
|
||||||
|
tooltip: AppLocalizations.of(context)!.remove,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 4),
|
||||||
|
],
|
||||||
),
|
),
|
||||||
],
|
)).toList(),
|
||||||
),
|
const SizedBox(height: 12),
|
||||||
const SizedBox(height: 16),
|
Row(
|
||||||
SectionLabel(label: AppLocalizations.of(context)!.dnsMode),
|
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
|
||||||
CustomRadioListTile(
|
mainAxisSize: MainAxisSize.min,
|
||||||
groupValue: upstreamMode,
|
children: [
|
||||||
value: "",
|
ElevatedButton.icon(
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
onPressed: openAddCommentModal,
|
||||||
title: AppLocalizations.of(context)!.loadBalancing,
|
icon: const Icon(Icons.add),
|
||||||
subtitle: AppLocalizations.of(context)!.loadBalancingDescription,
|
label: Text(AppLocalizations.of(context)!.comment)
|
||||||
onChanged: (value) => setState(() => upstreamMode = value),
|
),
|
||||||
),
|
ElevatedButton.icon(
|
||||||
CustomRadioListTile(
|
onPressed: () {
|
||||||
groupValue: upstreamMode,
|
setState(() => dnsServers.add({
|
||||||
value: "parallel",
|
'controller': TextEditingController()
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
}));
|
||||||
title: AppLocalizations.of(context)!.parallelRequests,
|
checkValidValues();
|
||||||
subtitle: AppLocalizations.of(context)!.parallelRequestsDescription,
|
},
|
||||||
onChanged: (value) => setState(() => upstreamMode = value),
|
icon: const Icon(Icons.add),
|
||||||
),
|
label: Text(AppLocalizations.of(context)!.address)
|
||||||
CustomRadioListTile(
|
),
|
||||||
groupValue: upstreamMode,
|
],
|
||||||
value: "fastest_addr",
|
),
|
||||||
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
const SizedBox(height: 16),
|
||||||
title: AppLocalizations.of(context)!.fastestIpAddress,
|
SectionLabel(label: AppLocalizations.of(context)!.dnsMode),
|
||||||
subtitle: AppLocalizations.of(context)!.fastestIpAddressDescription,
|
CustomRadioListTile(
|
||||||
onChanged: (value) => setState(() => upstreamMode = value),
|
groupValue: upstreamMode,
|
||||||
),
|
value: "",
|
||||||
],
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: AppLocalizations.of(context)!.loadBalancing,
|
||||||
|
subtitle: AppLocalizations.of(context)!.loadBalancingDescription,
|
||||||
|
onChanged: (value) => setState(() => upstreamMode = value),
|
||||||
|
),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: upstreamMode,
|
||||||
|
value: "parallel",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: AppLocalizations.of(context)!.parallelRequests,
|
||||||
|
subtitle: AppLocalizations.of(context)!.parallelRequestsDescription,
|
||||||
|
onChanged: (value) => setState(() => upstreamMode = value),
|
||||||
|
),
|
||||||
|
CustomRadioListTile(
|
||||||
|
groupValue: upstreamMode,
|
||||||
|
value: "fastest_addr",
|
||||||
|
radioBackgroundColor: Theme.of(context).dialogBackgroundColor,
|
||||||
|
title: AppLocalizations.of(context)!.fastestIpAddress,
|
||||||
|
subtitle: AppLocalizations.of(context)!.fastestIpAddressDescription,
|
||||||
|
onChanged: (value) => setState(() => upstreamMode = value),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -46,10 +46,12 @@ class DnsRewriteModal extends StatelessWidget {
|
||||||
),
|
),
|
||||||
color: Theme.of(context).dialogBackgroundColor,
|
color: Theme.of(context).dialogBackgroundColor,
|
||||||
),
|
),
|
||||||
child: _Content(
|
child: SafeArea(
|
||||||
onConfirm: onConfirm,
|
child: _Content(
|
||||||
onDelete: onDelete,
|
onConfirm: onConfirm,
|
||||||
rule: rule,
|
onDelete: onDelete,
|
||||||
|
rule: rule,
|
||||||
|
),
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -136,239 +136,241 @@ class _DnsRewritesScreenState extends State<DnsRewritesScreen> {
|
||||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
),
|
),
|
||||||
body: Stack(
|
body: SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
Builder(
|
children: [
|
||||||
builder: (context) {
|
Builder(
|
||||||
switch (rewriteRulesProvider.loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (rewriteRulesProvider.loadStatus) {
|
||||||
return SizedBox(
|
case LoadStatus.loading:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const CircularProgressIndicator(),
|
children: [
|
||||||
const SizedBox(height: 30),
|
const CircularProgressIndicator(),
|
||||||
Text(
|
const SizedBox(height: 30),
|
||||||
AppLocalizations.of(context)!.loadingRewriteRules,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
AppLocalizations.of(context)!.loadingRewriteRules,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.center,
|
||||||
fontSize: 22,
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
fontSize: 22,
|
||||||
),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
case LoadStatus.loaded:
|
||||||
if (rewriteRulesProvider.rewriteRules!.isNotEmpty) {
|
if (rewriteRulesProvider.rewriteRules!.isNotEmpty) {
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: () async {
|
onRefresh: () async {
|
||||||
final result = await rewriteRulesProvider.fetchRules();
|
final result = await rewriteRulesProvider.fetchRules();
|
||||||
if (result == false) {
|
if (result == false) {
|
||||||
showSnacbkar(
|
showSnacbkar(
|
||||||
appConfigProvider: appConfigProvider,
|
appConfigProvider: appConfigProvider,
|
||||||
label: AppLocalizations.of(context)!.rewriteRulesNotLoaded,
|
label: AppLocalizations.of(context)!.rewriteRulesNotLoaded,
|
||||||
color: Colors.red
|
color: Colors.red
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
child: ListView.builder(
|
child: ListView.builder(
|
||||||
controller: scrollController,
|
controller: scrollController,
|
||||||
padding: const EdgeInsets.only(top: 0),
|
padding: const EdgeInsets.only(top: 0),
|
||||||
itemCount: rewriteRulesProvider.rewriteRules!.length,
|
itemCount: rewriteRulesProvider.rewriteRules!.length,
|
||||||
itemBuilder: (context, index) => Card(
|
itemBuilder: (context, index) => Card(
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||||
child: InkWell(
|
child: InkWell(
|
||||||
onTap: () => {
|
onTap: () => {
|
||||||
if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
|
if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
|
||||||
showDialog(
|
showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DnsRewriteModal(
|
builder: (context) => DnsRewriteModal(
|
||||||
onConfirm: updateRewriteRule,
|
onConfirm: updateRewriteRule,
|
||||||
dialog: true,
|
dialog: true,
|
||||||
rule: rewriteRulesProvider.rewriteRules![index],
|
rule: rewriteRulesProvider.rewriteRules![index],
|
||||||
onDelete: (rule) => showDialog(
|
onDelete: (rule) => showDialog(
|
||||||
context: context,
|
context: context,
|
||||||
builder: (context) => DeleteDnsRewrite(
|
builder: (context) => DeleteDnsRewrite(
|
||||||
onConfirm: () => deleteDnsRewrite(rule)
|
onConfirm: () => deleteDnsRewrite(rule)
|
||||||
)
|
)
|
||||||
),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
showModalBottomSheet(
|
|
||||||
context: context,
|
|
||||||
useRootNavigator: true,
|
|
||||||
builder: (context) => DnsRewriteModal(
|
|
||||||
onConfirm: updateRewriteRule,
|
|
||||||
dialog: false,
|
|
||||||
rule: rewriteRulesProvider.rewriteRules![index],
|
|
||||||
onDelete: (rule) => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => DeleteDnsRewrite(
|
|
||||||
onConfirm: () => deleteDnsRewrite(rule)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
),
|
|
||||||
backgroundColor: Colors.transparent,
|
|
||||||
isScrollControlled: true,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
borderRadius: BorderRadius.circular(10),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.only(
|
|
||||||
left: 16, top: 16, bottom: 16, right: 8
|
|
||||||
),
|
|
||||||
child: Row(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
|
||||||
children: [
|
|
||||||
Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"${AppLocalizations.of(context)!.domain}: ",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
rewriteRulesProvider.rewriteRules![index].domain,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 3),
|
),
|
||||||
Row(
|
|
||||||
children: [
|
|
||||||
Text(
|
|
||||||
"${AppLocalizations.of(context)!.answer}: ",
|
|
||||||
style: TextStyle(
|
|
||||||
fontWeight: FontWeight.w500,
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
Text(
|
|
||||||
rewriteRulesProvider.rewriteRules![index].answer,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
Icon(
|
|
||||||
Icons.keyboard_arrow_right_rounded,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
)
|
)
|
||||||
],
|
}
|
||||||
|
else {
|
||||||
|
showModalBottomSheet(
|
||||||
|
context: context,
|
||||||
|
useRootNavigator: true,
|
||||||
|
builder: (context) => DnsRewriteModal(
|
||||||
|
onConfirm: updateRewriteRule,
|
||||||
|
dialog: false,
|
||||||
|
rule: rewriteRulesProvider.rewriteRules![index],
|
||||||
|
onDelete: (rule) => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DeleteDnsRewrite(
|
||||||
|
onConfirm: () => deleteDnsRewrite(rule)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
isScrollControlled: true,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
borderRadius: BorderRadius.circular(10),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
left: 16, top: 16, bottom: 16, right: 8
|
||||||
|
),
|
||||||
|
child: Row(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
|
children: [
|
||||||
|
Column(
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.start,
|
||||||
|
children: [
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"${AppLocalizations.of(context)!.domain}: ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
rewriteRulesProvider.rewriteRules![index].domain,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
const SizedBox(height: 3),
|
||||||
|
Row(
|
||||||
|
children: [
|
||||||
|
Text(
|
||||||
|
"${AppLocalizations.of(context)!.answer}: ",
|
||||||
|
style: TextStyle(
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
Text(
|
||||||
|
rewriteRulesProvider.rewriteRules![index].answer,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
Icon(
|
||||||
|
Icons.keyboard_arrow_right_rounded,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return Center(
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.noRewriteRules,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
),
|
||||||
),
|
);
|
||||||
);
|
}
|
||||||
}
|
else {
|
||||||
|
return Center(
|
||||||
case LoadStatus.error:
|
child: Text(
|
||||||
return SizedBox(
|
AppLocalizations.of(context)!.noRewriteRules,
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.rewriteRulesNotLoaded,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
)
|
),
|
||||||
],
|
);
|
||||||
),
|
}
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
case LoadStatus.error:
|
||||||
return const SizedBox();
|
return SizedBox(
|
||||||
}
|
width: double.maxFinite,
|
||||||
},
|
child: Column(
|
||||||
),
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
AnimatedPositioned(
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
duration: const Duration(milliseconds: 100),
|
children: [
|
||||||
curve: Curves.easeInOut,
|
const Icon(
|
||||||
bottom: isVisible ?
|
Icons.error,
|
||||||
appConfigProvider.showingSnackbar
|
color: Colors.red,
|
||||||
? 70 : 20
|
size: 50,
|
||||||
: -70,
|
),
|
||||||
right: 20,
|
const SizedBox(height: 30),
|
||||||
child: FloatingActionButton(
|
Text(
|
||||||
onPressed: () => {
|
AppLocalizations.of(context)!.rewriteRulesNotLoaded,
|
||||||
if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
|
textAlign: TextAlign.center,
|
||||||
showDialog(
|
style: TextStyle(
|
||||||
context: context,
|
fontSize: 22,
|
||||||
builder: (context) => DnsRewriteModal(
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
onConfirm: addDnsRewrite,
|
),
|
||||||
dialog: true,
|
|
||||||
onDelete: (rule) => showDialog(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => DeleteDnsRewrite(
|
|
||||||
onConfirm: () => deleteDnsRewrite(rule)
|
|
||||||
)
|
)
|
||||||
),
|
],
|
||||||
),
|
),
|
||||||
)
|
);
|
||||||
}
|
|
||||||
else {
|
default:
|
||||||
showModalBottomSheet(
|
return const SizedBox();
|
||||||
context: context,
|
}
|
||||||
useRootNavigator: true,
|
},
|
||||||
builder: (context) => DnsRewriteModal(
|
),
|
||||||
onConfirm: addDnsRewrite,
|
AnimatedPositioned(
|
||||||
dialog: false,
|
duration: const Duration(milliseconds: 100),
|
||||||
onDelete: (rule) => showDialog(
|
curve: Curves.easeInOut,
|
||||||
context: context,
|
bottom: isVisible ?
|
||||||
builder: (context) => DeleteDnsRewrite(
|
appConfigProvider.showingSnackbar
|
||||||
onConfirm: () => deleteDnsRewrite(rule)
|
? 70 : 20
|
||||||
)
|
: -70,
|
||||||
|
right: 20,
|
||||||
|
child: FloatingActionButton(
|
||||||
|
onPressed: () => {
|
||||||
|
if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
|
||||||
|
showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DnsRewriteModal(
|
||||||
|
onConfirm: addDnsRewrite,
|
||||||
|
dialog: true,
|
||||||
|
onDelete: (rule) => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DeleteDnsRewrite(
|
||||||
|
onConfirm: () => deleteDnsRewrite(rule)
|
||||||
|
)
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
backgroundColor: Colors.transparent,
|
}
|
||||||
isScrollControlled: true
|
else {
|
||||||
)
|
showModalBottomSheet(
|
||||||
}
|
context: context,
|
||||||
},
|
useRootNavigator: true,
|
||||||
child: const Icon(Icons.add),
|
builder: (context) => DnsRewriteModal(
|
||||||
),
|
onConfirm: addDnsRewrite,
|
||||||
)
|
dialog: false,
|
||||||
],
|
onDelete: (rule) => showDialog(
|
||||||
|
context: context,
|
||||||
|
builder: (context) => DeleteDnsRewrite(
|
||||||
|
onConfirm: () => deleteDnsRewrite(rule)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
),
|
||||||
|
backgroundColor: Colors.transparent,
|
||||||
|
isScrollControlled: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
child: const Icon(Icons.add),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import 'package:flutter_gen/gen_l10n/app_localizations.dart';
|
||||||
class ServerVersionNeeded extends StatelessWidget {
|
class ServerVersionNeeded extends StatelessWidget {
|
||||||
final String version;
|
final String version;
|
||||||
|
|
||||||
|
// ignore: use_super_parameters
|
||||||
const ServerVersionNeeded({
|
const ServerVersionNeeded({
|
||||||
Key? key,
|
Key? key,
|
||||||
required this.version
|
required this.version
|
||||||
|
|
|
@ -283,376 +283,378 @@ class _EncryptionSettingsState extends State<EncryptionSettings> {
|
||||||
const SizedBox(width: 10),
|
const SizedBox(width: 10),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (loadStatus) {
|
||||||
return SizedBox(
|
case LoadStatus.loading:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const CircularProgressIndicator(),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.loadingEncryptionSettings,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
|
||||||
return ListView(
|
|
||||||
children: [
|
|
||||||
EncryptionMasterSwitch(
|
|
||||||
value: enabled,
|
|
||||||
onChange: (value) {
|
|
||||||
setState(() => enabled = value);
|
|
||||||
onEditValidate();
|
|
||||||
}
|
|
||||||
),
|
|
||||||
SectionLabel(
|
|
||||||
label: AppLocalizations.of(context)!.serverConfiguration,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
|
||||||
),
|
|
||||||
EncryptionTextField(
|
|
||||||
enabled: enabled,
|
|
||||||
controller: domainNameController,
|
|
||||||
icon: Icons.link_rounded,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => domainError = validateDomain(context, value));
|
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
errorText: domainError,
|
|
||||||
label: AppLocalizations.of(context)!.domainName,
|
|
||||||
helperText: AppLocalizations.of(context)!.domainNameDescription,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
CustomSwitchListTile(
|
|
||||||
value: redirectHttps,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => redirectHttps = value);
|
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
title: AppLocalizations.of(context)!.redirectHttps,
|
|
||||||
disabled: !enabled,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
Wrap(
|
|
||||||
children: [
|
children: [
|
||||||
FractionallySizedBox(
|
const CircularProgressIndicator(),
|
||||||
widthFactor: width > 900 ? 0.33 : 1,
|
const SizedBox(height: 30),
|
||||||
child: EncryptionTextField(
|
Text(
|
||||||
enabled: enabled,
|
AppLocalizations.of(context)!.loadingEncryptionSettings,
|
||||||
controller: httpsPortController,
|
textAlign: TextAlign.center,
|
||||||
icon: Icons.numbers_rounded,
|
style: TextStyle(
|
||||||
onChanged: (value) {
|
fontSize: 22,
|
||||||
setState(() => httpsPortError = validatePort(context, value));
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
errorText: httpsPortError,
|
|
||||||
label: AppLocalizations.of(context)!.httpsPort,
|
|
||||||
keyboardType: TextInputType.number,
|
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
Padding(
|
],
|
||||||
padding: width <= 900
|
)
|
||||||
? const EdgeInsets.symmetric(vertical: 24)
|
);
|
||||||
: const EdgeInsets.all(0),
|
|
||||||
child: FractionallySizedBox(
|
case LoadStatus.loaded:
|
||||||
|
return ListView(
|
||||||
|
children: [
|
||||||
|
EncryptionMasterSwitch(
|
||||||
|
value: enabled,
|
||||||
|
onChange: (value) {
|
||||||
|
setState(() => enabled = value);
|
||||||
|
onEditValidate();
|
||||||
|
}
|
||||||
|
),
|
||||||
|
SectionLabel(
|
||||||
|
label: AppLocalizations.of(context)!.serverConfiguration,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
),
|
||||||
|
EncryptionTextField(
|
||||||
|
enabled: enabled,
|
||||||
|
controller: domainNameController,
|
||||||
|
icon: Icons.link_rounded,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => domainError = validateDomain(context, value));
|
||||||
|
onEditValidate();
|
||||||
|
},
|
||||||
|
errorText: domainError,
|
||||||
|
label: AppLocalizations.of(context)!.domainName,
|
||||||
|
helperText: AppLocalizations.of(context)!.domainNameDescription,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
CustomSwitchListTile(
|
||||||
|
value: redirectHttps,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => redirectHttps = value);
|
||||||
|
onEditValidate();
|
||||||
|
},
|
||||||
|
title: AppLocalizations.of(context)!.redirectHttps,
|
||||||
|
disabled: !enabled,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
Wrap(
|
||||||
|
children: [
|
||||||
|
FractionallySizedBox(
|
||||||
widthFactor: width > 900 ? 0.33 : 1,
|
widthFactor: width > 900 ? 0.33 : 1,
|
||||||
child: EncryptionTextField(
|
child: EncryptionTextField(
|
||||||
enabled: enabled,
|
enabled: enabled,
|
||||||
controller: tlsPortController,
|
controller: httpsPortController,
|
||||||
icon: Icons.numbers_rounded,
|
icon: Icons.numbers_rounded,
|
||||||
onChanged: (value) {
|
onChanged: (value) {
|
||||||
setState(() => tlsPortError = validatePort(context, value));
|
setState(() => httpsPortError = validatePort(context, value));
|
||||||
onEditValidate();
|
onEditValidate();
|
||||||
},
|
},
|
||||||
errorText: tlsPortError,
|
errorText: httpsPortError,
|
||||||
label: AppLocalizations.of(context)!.tlsPort,
|
label: AppLocalizations.of(context)!.httpsPort,
|
||||||
keyboardType: TextInputType.number,
|
keyboardType: TextInputType.number,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Padding(
|
||||||
FractionallySizedBox(
|
padding: width <= 900
|
||||||
widthFactor: width > 900 ? 0.33 : 1,
|
? const EdgeInsets.symmetric(vertical: 24)
|
||||||
child: EncryptionTextField(
|
: const EdgeInsets.all(0),
|
||||||
enabled: enabled,
|
child: FractionallySizedBox(
|
||||||
controller: dnsOverQuicPortController,
|
widthFactor: width > 900 ? 0.33 : 1,
|
||||||
icon: Icons.numbers_rounded,
|
child: EncryptionTextField(
|
||||||
onChanged: (value) {
|
enabled: enabled,
|
||||||
setState(() => dnsOverQuicPortError = validatePort(context, value));
|
controller: tlsPortController,
|
||||||
onEditValidate();
|
icon: Icons.numbers_rounded,
|
||||||
},
|
onChanged: (value) {
|
||||||
errorText: dnsOverQuicPortError,
|
setState(() => tlsPortError = validatePort(context, value));
|
||||||
label: AppLocalizations.of(context)!.dnsOverQuicPort,
|
onEditValidate();
|
||||||
keyboardType: TextInputType.number,
|
},
|
||||||
|
errorText: tlsPortError,
|
||||||
|
label: AppLocalizations.of(context)!.tlsPort,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
FractionallySizedBox(
|
||||||
|
widthFactor: width > 900 ? 0.33 : 1,
|
||||||
|
child: EncryptionTextField(
|
||||||
|
enabled: enabled,
|
||||||
|
controller: dnsOverQuicPortController,
|
||||||
|
icon: Icons.numbers_rounded,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => dnsOverQuicPortError = validatePort(context, value));
|
||||||
|
onEditValidate();
|
||||||
|
},
|
||||||
|
errorText: dnsOverQuicPortError,
|
||||||
|
label: AppLocalizations.of(context)!.dnsOverQuicPort,
|
||||||
|
keyboardType: TextInputType.number,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
SectionLabel(
|
||||||
|
label: AppLocalizations.of(context)!.certificates,
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
|
),
|
||||||
|
Card(
|
||||||
|
margin: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
|
child: Padding(
|
||||||
|
padding: const EdgeInsets.all(20),
|
||||||
|
child: Row(
|
||||||
|
children: [
|
||||||
|
Icon(
|
||||||
|
Icons.info_rounded,
|
||||||
|
color: Theme.of(context).listTileTheme.iconColor,
|
||||||
|
),
|
||||||
|
const SizedBox(width: 20),
|
||||||
|
Flexible(
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.certificatesDescription,
|
||||||
|
style: TextStyle(
|
||||||
|
color: Theme.of(context).colorScheme.onSurface,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
],
|
|
||||||
),
|
|
||||||
SectionLabel(
|
|
||||||
label: AppLocalizations.of(context)!.certificates,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
|
||||||
),
|
|
||||||
Card(
|
|
||||||
margin: const EdgeInsets.symmetric(horizontal: 16),
|
|
||||||
child: Padding(
|
|
||||||
padding: const EdgeInsets.all(20),
|
|
||||||
child: Row(
|
|
||||||
children: [
|
|
||||||
Icon(
|
|
||||||
Icons.info_rounded,
|
|
||||||
color: Theme.of(context).listTileTheme.iconColor,
|
|
||||||
),
|
|
||||||
const SizedBox(width: 20),
|
|
||||||
Flexible(
|
|
||||||
child: Text(
|
|
||||||
AppLocalizations.of(context)!.certificatesDescription,
|
|
||||||
style: TextStyle(
|
|
||||||
color: Theme.of(context).colorScheme.onSurface,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
RadioListTile(
|
|
||||||
value: 0,
|
|
||||||
groupValue: certificateOption,
|
|
||||||
onChanged: enabled == true
|
|
||||||
? (value) {
|
|
||||||
setState(() => certificateOption = int.parse(value.toString()));
|
|
||||||
onEditValidate();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.certificateFilePath,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.normal
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
RadioListTile(
|
|
||||||
value: 1,
|
|
||||||
groupValue: certificateOption,
|
|
||||||
onChanged: enabled == true
|
|
||||||
? (value) {
|
|
||||||
setState(() => certificateOption = int.parse(value.toString()));
|
|
||||||
onEditValidate();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.pasteCertificateContent,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.normal
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
if (certificateOption == 0) EncryptionTextField(
|
|
||||||
enabled: enabled,
|
|
||||||
controller: certificatePathController,
|
|
||||||
icon: Icons.description_rounded,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => certificatePathError = validatePath(context, value));
|
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
label: AppLocalizations.of(context)!.certificatePath,
|
|
||||||
errorText: certificatePathError,
|
|
||||||
),
|
|
||||||
if (certificateOption == 1) EncryptionTextField(
|
|
||||||
enabled: enabled,
|
|
||||||
controller: certificateContentController,
|
|
||||||
icon: Icons.description_rounded,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => certificateContentError = validateCertificate(context, value));
|
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
label: AppLocalizations.of(context)!.certificateContent,
|
|
||||||
errorText: certificateContentError,
|
|
||||||
multiline: true,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
),
|
|
||||||
if (certKeyValid != null && (certificateContentController.text != '' || certificatePathController.text != '')) ...[
|
|
||||||
const SizedBox(height: 20),
|
const SizedBox(height: 20),
|
||||||
if (certKeyValid!.validChain != null) ...[
|
RadioListTile(
|
||||||
Status(
|
value: 0,
|
||||||
valid: certKeyValid!.validChain ?? false,
|
groupValue: certificateOption,
|
||||||
label: certKeyValid!.validChain == true
|
onChanged: enabled == true
|
||||||
? AppLocalizations.of(context)!.validCertificateChain
|
? (value) {
|
||||||
: AppLocalizations.of(context)!.invalidCertificateChain,
|
setState(() => certificateOption = int.parse(value.toString()));
|
||||||
|
onEditValidate();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!.certificateFilePath,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.normal
|
||||||
|
),
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
),
|
||||||
],
|
RadioListTile(
|
||||||
if (certKeyValid!.subject != null) ...[
|
value: 1,
|
||||||
Status(
|
groupValue: certificateOption,
|
||||||
|
onChanged: enabled == true
|
||||||
|
? (value) {
|
||||||
|
setState(() => certificateOption = int.parse(value.toString()));
|
||||||
|
onEditValidate();
|
||||||
|
}
|
||||||
|
: null,
|
||||||
|
title: Text(
|
||||||
|
AppLocalizations.of(context)!.pasteCertificateContent,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontWeight: FontWeight.normal
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
if (certificateOption == 0) EncryptionTextField(
|
||||||
|
enabled: enabled,
|
||||||
|
controller: certificatePathController,
|
||||||
|
icon: Icons.description_rounded,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => certificatePathError = validatePath(context, value));
|
||||||
|
onEditValidate();
|
||||||
|
},
|
||||||
|
label: AppLocalizations.of(context)!.certificatePath,
|
||||||
|
errorText: certificatePathError,
|
||||||
|
),
|
||||||
|
if (certificateOption == 1) EncryptionTextField(
|
||||||
|
enabled: enabled,
|
||||||
|
controller: certificateContentController,
|
||||||
|
icon: Icons.description_rounded,
|
||||||
|
onChanged: (value) {
|
||||||
|
setState(() => certificateContentError = validateCertificate(context, value));
|
||||||
|
onEditValidate();
|
||||||
|
},
|
||||||
|
label: AppLocalizations.of(context)!.certificateContent,
|
||||||
|
errorText: certificateContentError,
|
||||||
|
multiline: true,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
),
|
||||||
|
if (certKeyValid != null && (certificateContentController.text != '' || certificatePathController.text != '')) ...[
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (certKeyValid!.validChain != null) ...[
|
||||||
|
Status(
|
||||||
|
valid: certKeyValid!.validChain ?? false,
|
||||||
|
label: certKeyValid!.validChain == true
|
||||||
|
? AppLocalizations.of(context)!.validCertificateChain
|
||||||
|
: AppLocalizations.of(context)!.invalidCertificateChain,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
if (certKeyValid!.subject != null) ...[
|
||||||
|
Status(
|
||||||
|
valid: true,
|
||||||
|
label: "${AppLocalizations.of(context)!.subject}: ${certKeyValid?.subject}"
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
if (certKeyValid!.issuer != null) ...[
|
||||||
|
Status(
|
||||||
valid: true,
|
valid: true,
|
||||||
label: "${AppLocalizations.of(context)!.subject}: ${certKeyValid?.subject}"
|
label: "${AppLocalizations.of(context)!.issuer}: ${certKeyValid?.issuer}"
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
if (certKeyValid!.notAfter != null) ...[
|
||||||
|
Status(
|
||||||
|
valid: true,
|
||||||
|
label: "${AppLocalizations.of(context)!.expirationDate}: ${certKeyValid?.notAfter}"
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
if (certKeyValid!.dnsNames != null) ...[
|
||||||
|
Status(
|
||||||
|
valid: true,
|
||||||
|
label: "${AppLocalizations.of(context)!.hostNames}: ${certKeyValid?.dnsNames?.join(', ')}"
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
],
|
],
|
||||||
if (certKeyValid!.issuer != null) ...[
|
SectionLabel(
|
||||||
Status(
|
label: AppLocalizations.of(context)!.privateKey,
|
||||||
valid: true,
|
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
||||||
label: "${AppLocalizations.of(context)!.issuer}: ${certKeyValid?.issuer}"
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
RadioListTile(
|
||||||
],
|
value: 0,
|
||||||
if (certKeyValid!.notAfter != null) ...[
|
groupValue: privateKeyOption,
|
||||||
Status(
|
onChanged: enabled == true
|
||||||
valid: true,
|
? (value) {
|
||||||
label: "${AppLocalizations.of(context)!.expirationDate}: ${certKeyValid?.notAfter}"
|
setState(() => privateKeyOption = int.parse(value.toString()));
|
||||||
),
|
onEditValidate();
|
||||||
const SizedBox(height: 10),
|
}
|
||||||
],
|
: null,
|
||||||
if (certKeyValid!.dnsNames != null) ...[
|
title: Text(
|
||||||
Status(
|
AppLocalizations.of(context)!.privateKeyFile,
|
||||||
valid: true,
|
style: const TextStyle(
|
||||||
label: "${AppLocalizations.of(context)!.hostNames}: ${certKeyValid?.dnsNames?.join(', ')}"
|
fontWeight: FontWeight.normal
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10),
|
|
||||||
],
|
|
||||||
],
|
|
||||||
SectionLabel(
|
|
||||||
label: AppLocalizations.of(context)!.privateKey,
|
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
|
|
||||||
),
|
|
||||||
RadioListTile(
|
|
||||||
value: 0,
|
|
||||||
groupValue: privateKeyOption,
|
|
||||||
onChanged: enabled == true
|
|
||||||
? (value) {
|
|
||||||
setState(() => privateKeyOption = int.parse(value.toString()));
|
|
||||||
onEditValidate();
|
|
||||||
}
|
|
||||||
: null,
|
|
||||||
title: Text(
|
|
||||||
AppLocalizations.of(context)!.privateKeyFile,
|
|
||||||
style: const TextStyle(
|
|
||||||
fontWeight: FontWeight.normal
|
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
RadioListTile(
|
||||||
RadioListTile(
|
value: 1,
|
||||||
value: 1,
|
groupValue: privateKeyOption,
|
||||||
groupValue: privateKeyOption,
|
onChanged: enabled == true
|
||||||
onChanged: enabled == true
|
? (value) {
|
||||||
? (value) {
|
setState(() => privateKeyOption = int.parse(value.toString()));
|
||||||
setState(() => privateKeyOption = int.parse(value.toString()));
|
onEditValidate();
|
||||||
onEditValidate();
|
}
|
||||||
}
|
: null,
|
||||||
: null,
|
title: Text(
|
||||||
title: Text(
|
AppLocalizations.of(context)!.pastePrivateKey,
|
||||||
AppLocalizations.of(context)!.pastePrivateKey,
|
style: const TextStyle(
|
||||||
style: const TextStyle(
|
fontWeight: FontWeight.normal
|
||||||
fontWeight: FontWeight.normal
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
if (privateKeyOption == 0) const SizedBox(height: 10),
|
||||||
if (privateKeyOption == 0) const SizedBox(height: 10),
|
if (privateKeyOption == 1) ...[
|
||||||
if (privateKeyOption == 1) ...[
|
CustomSwitchListTile(
|
||||||
CustomSwitchListTile(
|
value: usePreviouslySavedKey,
|
||||||
value: usePreviouslySavedKey,
|
onChanged: (value) => setState(() => usePreviouslySavedKey = value),
|
||||||
onChanged: (value) => setState(() => usePreviouslySavedKey = value),
|
title: AppLocalizations.of(context)!.usePreviousKey,
|
||||||
title: AppLocalizations.of(context)!.usePreviousKey,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10)
|
|
||||||
],
|
|
||||||
if (privateKeyOption == 0) EncryptionTextField(
|
|
||||||
enabled: enabled,
|
|
||||||
controller: privateKeyPathController,
|
|
||||||
icon: Icons.description_rounded,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => privateKeyPathError = validatePath(context, value));
|
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
label: AppLocalizations.of(context)!.privateKeyPath,
|
|
||||||
errorText: privateKeyPathError,
|
|
||||||
),
|
|
||||||
if (privateKeyOption == 1) EncryptionTextField(
|
|
||||||
enabled: enabled == true
|
|
||||||
? !usePreviouslySavedKey
|
|
||||||
: false,
|
|
||||||
controller: pastePrivateKeyController,
|
|
||||||
icon: Icons.description_rounded,
|
|
||||||
onChanged: (value) {
|
|
||||||
setState(() => pastePrivateKeyError = validatePrivateKey(context, value));
|
|
||||||
onEditValidate();
|
|
||||||
},
|
|
||||||
label: AppLocalizations.of(context)!.pastePrivateKey,
|
|
||||||
errorText: pastePrivateKeyError,
|
|
||||||
keyboardType: TextInputType.multiline,
|
|
||||||
multiline: true,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 20),
|
|
||||||
if (certKeyValid != null && (privateKeyPathController.text != '' || pastePrivateKeyController.text != '' || usePreviouslySavedKey == true)) ...[
|
|
||||||
if (certKeyValid!.validKey != null) ...[
|
|
||||||
Status(
|
|
||||||
valid: certKeyValid!.validKey ?? false,
|
|
||||||
label: certKeyValid!.validKey == true
|
|
||||||
? AppLocalizations.of(context)!.validPrivateKey
|
|
||||||
: AppLocalizations.of(context)!.invalidPrivateKey,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 10)
|
const SizedBox(height: 10)
|
||||||
],
|
],
|
||||||
if (certKeyValid!.validPair != null && certKeyValid!.validPair == false) ...[
|
if (privateKeyOption == 0) EncryptionTextField(
|
||||||
Status(
|
enabled: enabled,
|
||||||
valid: false,
|
controller: privateKeyPathController,
|
||||||
label: AppLocalizations.of(context)!.keysNotMatch,
|
icon: Icons.description_rounded,
|
||||||
),
|
onChanged: (value) {
|
||||||
const SizedBox(height: 10)
|
setState(() => privateKeyPathError = validatePath(context, value));
|
||||||
],
|
onEditValidate();
|
||||||
if (certKeyValid!.keyType != null) ...[
|
},
|
||||||
Status(
|
label: AppLocalizations.of(context)!.privateKeyPath,
|
||||||
valid: true,
|
errorText: privateKeyPathError,
|
||||||
label: "${AppLocalizations.of(context)!.keyType}: ${certKeyValid!.keyType}"
|
|
||||||
),
|
|
||||||
const SizedBox(height: 10),
|
|
||||||
],
|
|
||||||
const SizedBox(height: 10)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.error:
|
|
||||||
return SizedBox(
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 50,
|
|
||||||
),
|
),
|
||||||
const SizedBox(height: 30),
|
if (privateKeyOption == 1) EncryptionTextField(
|
||||||
Text(
|
enabled: enabled == true
|
||||||
AppLocalizations.of(context)!.encryptionSettingsNotLoaded,
|
? !usePreviouslySavedKey
|
||||||
textAlign: TextAlign.center,
|
: false,
|
||||||
style: TextStyle(
|
controller: pastePrivateKeyController,
|
||||||
fontSize: 22,
|
icon: Icons.description_rounded,
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
onChanged: (value) {
|
||||||
),
|
setState(() => pastePrivateKeyError = validatePrivateKey(context, value));
|
||||||
)
|
onEditValidate();
|
||||||
|
},
|
||||||
|
label: AppLocalizations.of(context)!.pastePrivateKey,
|
||||||
|
errorText: pastePrivateKeyError,
|
||||||
|
keyboardType: TextInputType.multiline,
|
||||||
|
multiline: true,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 20),
|
||||||
|
if (certKeyValid != null && (privateKeyPathController.text != '' || pastePrivateKeyController.text != '' || usePreviouslySavedKey == true)) ...[
|
||||||
|
if (certKeyValid!.validKey != null) ...[
|
||||||
|
Status(
|
||||||
|
valid: certKeyValid!.validKey ?? false,
|
||||||
|
label: certKeyValid!.validKey == true
|
||||||
|
? AppLocalizations.of(context)!.validPrivateKey
|
||||||
|
: AppLocalizations.of(context)!.invalidPrivateKey,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10)
|
||||||
|
],
|
||||||
|
if (certKeyValid!.validPair != null && certKeyValid!.validPair == false) ...[
|
||||||
|
Status(
|
||||||
|
valid: false,
|
||||||
|
label: AppLocalizations.of(context)!.keysNotMatch,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10)
|
||||||
|
],
|
||||||
|
if (certKeyValid!.keyType != null) ...[
|
||||||
|
Status(
|
||||||
|
valid: true,
|
||||||
|
label: "${AppLocalizations.of(context)!.keyType}: ${certKeyValid!.keyType}"
|
||||||
|
),
|
||||||
|
const SizedBox(height: 10),
|
||||||
|
],
|
||||||
|
const SizedBox(height: 10)
|
||||||
|
]
|
||||||
],
|
],
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
case LoadStatus.error:
|
||||||
return const SizedBox();
|
return SizedBox(
|
||||||
}
|
width: double.maxFinite,
|
||||||
},
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.encryptionSettingsNotLoaded,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return const SizedBox();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -124,173 +124,175 @@ class _GeneralSettingsState extends State<GeneralSettings> {
|
||||||
title: Text(AppLocalizations.of(context)!.generalSettings),
|
title: Text(AppLocalizations.of(context)!.generalSettings),
|
||||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
children: [
|
child: ListView(
|
||||||
SectionLabel(label: AppLocalizations.of(context)!.home),
|
children: [
|
||||||
CustomListTile(
|
SectionLabel(label: AppLocalizations.of(context)!.home),
|
||||||
icon: Icons.exposure_zero_rounded,
|
CustomListTile(
|
||||||
title: AppLocalizations.of(context)!.hideZeroValues,
|
icon: Icons.exposure_zero_rounded,
|
||||||
subtitle: AppLocalizations.of(context)!.hideZeroValuesDescription,
|
title: AppLocalizations.of(context)!.hideZeroValues,
|
||||||
trailing: Switch(
|
subtitle: AppLocalizations.of(context)!.hideZeroValuesDescription,
|
||||||
value: appConfigProvider.hideZeroValues,
|
trailing: Switch(
|
||||||
onChanged: (value) => updateSettings(
|
value: appConfigProvider.hideZeroValues,
|
||||||
newStatus: value,
|
onChanged: (value) => updateSettings(
|
||||||
|
newStatus: value,
|
||||||
|
function: appConfigProvider.setHideZeroValues
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => updateSettings(
|
||||||
|
newStatus: !appConfigProvider.hideZeroValues,
|
||||||
function: appConfigProvider.setHideZeroValues
|
function: appConfigProvider.setHideZeroValues
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 16,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
CustomListTile(
|
||||||
newStatus: !appConfigProvider.hideZeroValues,
|
icon: Icons.show_chart_rounded,
|
||||||
function: appConfigProvider.setHideZeroValues
|
title: AppLocalizations.of(context)!.combinedChart,
|
||||||
),
|
subtitle: AppLocalizations.of(context)!.combinedChartDescription,
|
||||||
padding: const EdgeInsets.only(
|
trailing: Switch(
|
||||||
top: 10,
|
value: appConfigProvider.combinedChartHome,
|
||||||
bottom: 10,
|
onChanged: (value) => updateSettings(
|
||||||
left: 16,
|
newStatus: value,
|
||||||
right: 10
|
function: appConfigProvider.setCombinedChartHome
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
CustomListTile(
|
onTap: () => updateSettings(
|
||||||
icon: Icons.show_chart_rounded,
|
newStatus: !appConfigProvider.combinedChartHome,
|
||||||
title: AppLocalizations.of(context)!.combinedChart,
|
|
||||||
subtitle: AppLocalizations.of(context)!.combinedChartDescription,
|
|
||||||
trailing: Switch(
|
|
||||||
value: appConfigProvider.combinedChartHome,
|
|
||||||
onChanged: (value) => updateSettings(
|
|
||||||
newStatus: value,
|
|
||||||
function: appConfigProvider.setCombinedChartHome
|
function: appConfigProvider.setCombinedChartHome
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 16,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
CustomListTile(
|
||||||
newStatus: !appConfigProvider.combinedChartHome,
|
icon: Icons.remove_red_eye_rounded,
|
||||||
function: appConfigProvider.setCombinedChartHome
|
title: AppLocalizations.of(context)!.hideServerAddress,
|
||||||
),
|
subtitle: AppLocalizations.of(context)!.hideServerAddressDescription,
|
||||||
padding: const EdgeInsets.only(
|
trailing: Switch(
|
||||||
top: 10,
|
value: appConfigProvider.hideServerAddress,
|
||||||
bottom: 10,
|
onChanged: (value) => updateSettings(
|
||||||
left: 16,
|
newStatus: value,
|
||||||
right: 10
|
function: appConfigProvider.setHideServerAddress
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
CustomListTile(
|
onTap: () => updateSettings(
|
||||||
icon: Icons.remove_red_eye_rounded,
|
newStatus: !appConfigProvider.hideServerAddress,
|
||||||
title: AppLocalizations.of(context)!.hideServerAddress,
|
|
||||||
subtitle: AppLocalizations.of(context)!.hideServerAddressDescription,
|
|
||||||
trailing: Switch(
|
|
||||||
value: appConfigProvider.hideServerAddress,
|
|
||||||
onChanged: (value) => updateSettings(
|
|
||||||
newStatus: value,
|
|
||||||
function: appConfigProvider.setHideServerAddress
|
function: appConfigProvider.setHideServerAddress
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 16,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
CustomListTile(
|
||||||
newStatus: !appConfigProvider.hideServerAddress,
|
icon: Icons.reorder_rounded,
|
||||||
function: appConfigProvider.setHideServerAddress
|
title: AppLocalizations.of(context)!.topItemsOrder,
|
||||||
),
|
subtitle: AppLocalizations.of(context)!.topItemsOrderDescription,
|
||||||
padding: const EdgeInsets.only(
|
onTap: () => widget.splitView == true
|
||||||
top: 10,
|
? SplitView.of(context).push(const ReorderableTopItemsHome())
|
||||||
bottom: 10,
|
: Navigator.of(context).push(
|
||||||
left: 16,
|
MaterialPageRoute(
|
||||||
right: 10
|
builder: (context) => const ReorderableTopItemsHome()
|
||||||
)
|
)
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
icon: Icons.reorder_rounded,
|
|
||||||
title: AppLocalizations.of(context)!.topItemsOrder,
|
|
||||||
subtitle: AppLocalizations.of(context)!.topItemsOrderDescription,
|
|
||||||
onTap: () => widget.splitView == true
|
|
||||||
? SplitView.of(context).push(const ReorderableTopItemsHome())
|
|
||||||
: Navigator.of(context).push(
|
|
||||||
MaterialPageRoute(
|
|
||||||
builder: (context) => const ReorderableTopItemsHome()
|
|
||||||
)
|
)
|
||||||
)
|
),
|
||||||
),
|
CustomListTile(
|
||||||
CustomListTile(
|
icon: Icons.donut_large_rounded,
|
||||||
icon: Icons.donut_large_rounded,
|
title: AppLocalizations.of(context)!.showTopItemsChart,
|
||||||
title: AppLocalizations.of(context)!.showTopItemsChart,
|
subtitle: AppLocalizations.of(context)!.showTopItemsChartDescription,
|
||||||
subtitle: AppLocalizations.of(context)!.showTopItemsChartDescription,
|
trailing: Switch(
|
||||||
trailing: Switch(
|
value: appConfigProvider.showTopItemsChart,
|
||||||
value: appConfigProvider.showTopItemsChart,
|
onChanged: (value) => updateSettings(
|
||||||
onChanged: (value) => updateSettings(
|
newStatus: value,
|
||||||
newStatus: value,
|
function: appConfigProvider.setShowTopItemsChart
|
||||||
|
),
|
||||||
|
),
|
||||||
|
onTap: () => updateSettings(
|
||||||
|
newStatus: !appConfigProvider.showTopItemsChart,
|
||||||
function: appConfigProvider.setShowTopItemsChart
|
function: appConfigProvider.setShowTopItemsChart
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 16,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
SectionLabel(label: AppLocalizations.of(context)!.logs),
|
||||||
newStatus: !appConfigProvider.showTopItemsChart,
|
CustomListTile(
|
||||||
function: appConfigProvider.setShowTopItemsChart
|
icon: Icons.timer_rounded,
|
||||||
),
|
title: AppLocalizations.of(context)!.timeLogs,
|
||||||
padding: const EdgeInsets.only(
|
subtitle: AppLocalizations.of(context)!.timeLogsDescription,
|
||||||
top: 10,
|
trailing: Switch(
|
||||||
bottom: 10,
|
value: appConfigProvider.showTimeLogs,
|
||||||
left: 16,
|
onChanged: (value) => updateSettings(
|
||||||
right: 10
|
newStatus: value,
|
||||||
)
|
function: appConfigProvider.setshowTimeLogs
|
||||||
),
|
),
|
||||||
SectionLabel(label: AppLocalizations.of(context)!.logs),
|
),
|
||||||
CustomListTile(
|
onTap: () => updateSettings(
|
||||||
icon: Icons.timer_rounded,
|
newStatus: !appConfigProvider.showTimeLogs,
|
||||||
title: AppLocalizations.of(context)!.timeLogs,
|
|
||||||
subtitle: AppLocalizations.of(context)!.timeLogsDescription,
|
|
||||||
trailing: Switch(
|
|
||||||
value: appConfigProvider.showTimeLogs,
|
|
||||||
onChanged: (value) => updateSettings(
|
|
||||||
newStatus: value,
|
|
||||||
function: appConfigProvider.setshowTimeLogs
|
function: appConfigProvider.setshowTimeLogs
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 16,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
CustomListTile(
|
||||||
newStatus: !appConfigProvider.showTimeLogs,
|
icon: Icons.more,
|
||||||
function: appConfigProvider.setshowTimeLogs
|
title: AppLocalizations.of(context)!.ipLogs,
|
||||||
),
|
subtitle: AppLocalizations.of(context)!.ipLogsDescription,
|
||||||
padding: const EdgeInsets.only(
|
trailing: Switch(
|
||||||
top: 10,
|
value: appConfigProvider.showIpLogs,
|
||||||
bottom: 10,
|
onChanged: (value) => updateSettings(
|
||||||
left: 16,
|
newStatus: value,
|
||||||
right: 10
|
function: appConfigProvider.setShowIpLogs
|
||||||
)
|
),
|
||||||
),
|
),
|
||||||
CustomListTile(
|
onTap: () => updateSettings(
|
||||||
icon: Icons.more,
|
newStatus: !appConfigProvider.showIpLogs,
|
||||||
title: AppLocalizations.of(context)!.ipLogs,
|
|
||||||
subtitle: AppLocalizations.of(context)!.ipLogsDescription,
|
|
||||||
trailing: Switch(
|
|
||||||
value: appConfigProvider.showIpLogs,
|
|
||||||
onChanged: (value) => updateSettings(
|
|
||||||
newStatus: value,
|
|
||||||
function: appConfigProvider.setShowIpLogs
|
function: appConfigProvider.setShowIpLogs
|
||||||
),
|
),
|
||||||
|
padding: const EdgeInsets.only(
|
||||||
|
top: 10,
|
||||||
|
bottom: 10,
|
||||||
|
left: 16,
|
||||||
|
right: 10
|
||||||
|
)
|
||||||
),
|
),
|
||||||
onTap: () => updateSettings(
|
if (
|
||||||
newStatus: !appConfigProvider.showIpLogs,
|
!(Platform.isAndroid || Platform.isIOS) ||
|
||||||
function: appConfigProvider.setShowIpLogs
|
(Platform.isAndroid && (
|
||||||
),
|
appConfigProvider.installationSource == Source.IS_INSTALLED_FROM_LOCAL_SOURCE ||
|
||||||
padding: const EdgeInsets.only(
|
appConfigProvider.installationSource == Source.IS_INSTALLED_FROM_PLAY_PACKAGE_INSTALLER ||
|
||||||
top: 10,
|
appConfigProvider.installationSource == Source.UNKNOWN
|
||||||
bottom: 10,
|
))
|
||||||
left: 16,
|
) ...[
|
||||||
right: 10
|
SectionLabel(label: AppLocalizations.of(context)!.application),
|
||||||
)
|
CustomListTile(
|
||||||
),
|
icon: Icons.system_update_rounded,
|
||||||
if (
|
title: AppLocalizations.of(context)!.appUpdates,
|
||||||
!(Platform.isAndroid || Platform.isIOS) ||
|
subtitle: appConfigProvider.appUpdatesAvailable != null
|
||||||
(Platform.isAndroid && (
|
? AppLocalizations.of(context)!.updateAvailable
|
||||||
appConfigProvider.installationSource == Source.IS_INSTALLED_FROM_LOCAL_SOURCE ||
|
: AppLocalizations.of(context)!.usingLatestVersion,
|
||||||
appConfigProvider.installationSource == Source.IS_INSTALLED_FROM_PLAY_PACKAGE_INSTALLER ||
|
trailing: generateAppUpdateStatus()
|
||||||
appConfigProvider.installationSource == Source.UNKNOWN
|
)
|
||||||
))
|
]
|
||||||
) ...[
|
],
|
||||||
SectionLabel(label: AppLocalizations.of(context)!.application),
|
),
|
||||||
CustomListTile(
|
|
||||||
icon: Icons.system_update_rounded,
|
|
||||||
title: AppLocalizations.of(context)!.appUpdates,
|
|
||||||
subtitle: appConfigProvider.appUpdatesAvailable != null
|
|
||||||
? AppLocalizations.of(context)!.updateAvailable
|
|
||||||
: AppLocalizations.of(context)!.usingLatestVersion,
|
|
||||||
trailing: generateAppUpdateStatus()
|
|
||||||
)
|
|
||||||
]
|
|
||||||
],
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ import 'package:adguard_home_manager/functions/snackbar.dart';
|
||||||
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
import 'package:adguard_home_manager/providers/app_config_provider.dart';
|
||||||
|
|
||||||
class SafeSearchSettingsScreen extends StatefulWidget {
|
class SafeSearchSettingsScreen extends StatefulWidget {
|
||||||
const SafeSearchSettingsScreen({Key? key}) : super(key: key);
|
const SafeSearchSettingsScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<SafeSearchSettingsScreen> createState() => _SafeSearchSettingsScreenState();
|
State<SafeSearchSettingsScreen> createState() => _SafeSearchSettingsScreenState();
|
||||||
|
@ -123,166 +123,168 @@ class _SafeSearchSettingsScreenState extends State<SafeSearchSettingsScreen> {
|
||||||
const SizedBox(width: 8)
|
const SizedBox(width: 8)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (statusProvider.loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (statusProvider.loadStatus) {
|
||||||
return SizedBox(
|
case LoadStatus.loading:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
children: [
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
const CircularProgressIndicator(),
|
children: [
|
||||||
const SizedBox(height: 30),
|
const CircularProgressIndicator(),
|
||||||
Text(
|
const SizedBox(height: 30),
|
||||||
AppLocalizations.of(context)!.loadingSafeSearchSettings,
|
Text(
|
||||||
textAlign: TextAlign.center,
|
AppLocalizations.of(context)!.loadingSafeSearchSettings,
|
||||||
style: TextStyle(
|
textAlign: TextAlign.center,
|
||||||
fontSize: 22,
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
fontSize: 22,
|
||||||
),
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
)
|
),
|
||||||
],
|
)
|
||||||
),
|
],
|
||||||
);
|
),
|
||||||
|
);
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
case LoadStatus.loaded:
|
||||||
return RefreshIndicator(
|
return RefreshIndicator(
|
||||||
onRefresh: requestSafeSearchSettings,
|
onRefresh: requestSafeSearchSettings,
|
||||||
child: ListView(
|
child: ListView(
|
||||||
children: [
|
children: [
|
||||||
Padding(
|
Padding(
|
||||||
padding: const EdgeInsets.only(
|
padding: const EdgeInsets.only(
|
||||||
top: 16,
|
top: 16,
|
||||||
left: 16,
|
left: 16,
|
||||||
right: 16,
|
right: 16,
|
||||||
bottom: 8
|
bottom: 8
|
||||||
),
|
),
|
||||||
child: Material(
|
child: Material(
|
||||||
color: Colors.transparent,
|
color: Colors.transparent,
|
||||||
borderRadius: BorderRadius.circular(28),
|
|
||||||
child: InkWell(
|
|
||||||
borderRadius: BorderRadius.circular(28),
|
borderRadius: BorderRadius.circular(28),
|
||||||
onTap: () => setState(() => generalEnabled = !generalEnabled),
|
child: InkWell(
|
||||||
child: Container(
|
borderRadius: BorderRadius.circular(28),
|
||||||
padding: const EdgeInsets.symmetric(
|
onTap: () => setState(() => generalEnabled = !generalEnabled),
|
||||||
horizontal: 24,
|
child: Container(
|
||||||
vertical: 12
|
padding: const EdgeInsets.symmetric(
|
||||||
),
|
horizontal: 24,
|
||||||
decoration: BoxDecoration(
|
vertical: 12
|
||||||
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
),
|
||||||
borderRadius: BorderRadius.circular(28)
|
decoration: BoxDecoration(
|
||||||
),
|
color: Theme.of(context).colorScheme.primary.withOpacity(0.1),
|
||||||
child: Row(
|
borderRadius: BorderRadius.circular(28)
|
||||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
),
|
||||||
children: [
|
child: Row(
|
||||||
Flexible(
|
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||||
child: Text(
|
children: [
|
||||||
AppLocalizations.of(context)!.enableSafeSearch,
|
Flexible(
|
||||||
style: const TextStyle(
|
child: Text(
|
||||||
fontSize: 18
|
AppLocalizations.of(context)!.enableSafeSearch,
|
||||||
|
style: const TextStyle(
|
||||||
|
fontSize: 18
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
Switch(
|
||||||
Switch(
|
value: generalEnabled,
|
||||||
value: generalEnabled,
|
onChanged: (value) => setState(() => generalEnabled = value)
|
||||||
onChanged: (value) => setState(() => generalEnabled = value)
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
CustomCheckboxListTile(
|
||||||
CustomCheckboxListTile(
|
value: bingEnabled,
|
||||||
value: bingEnabled,
|
onChanged: (value) => setState(() => bingEnabled = value),
|
||||||
onChanged: (value) => setState(() => bingEnabled = value),
|
title: "Bing",
|
||||||
title: "Bing",
|
padding: const EdgeInsets.only(
|
||||||
padding: const EdgeInsets.only(
|
top: 8, left: 40, right: 40, bottom: 8
|
||||||
top: 8, left: 40, right: 40, bottom: 8
|
),
|
||||||
|
disabled: !generalEnabled,
|
||||||
),
|
),
|
||||||
disabled: !generalEnabled,
|
CustomCheckboxListTile(
|
||||||
),
|
value: duckduckgoEnabled,
|
||||||
CustomCheckboxListTile(
|
onChanged: (value) => setState(() => duckduckgoEnabled = value),
|
||||||
value: duckduckgoEnabled,
|
title: "DuckDuckGo",
|
||||||
onChanged: (value) => setState(() => duckduckgoEnabled = value),
|
padding: const EdgeInsets.only(
|
||||||
title: "DuckDuckGo",
|
top: 8, left: 40, right: 40, bottom: 8
|
||||||
padding: const EdgeInsets.only(
|
),
|
||||||
top: 8, left: 40, right: 40, bottom: 8
|
disabled: !generalEnabled,
|
||||||
),
|
),
|
||||||
disabled: !generalEnabled,
|
CustomCheckboxListTile(
|
||||||
),
|
value: googleEnabled,
|
||||||
CustomCheckboxListTile(
|
onChanged: (value) => setState(() => googleEnabled = value),
|
||||||
value: googleEnabled,
|
title: "Google",
|
||||||
onChanged: (value) => setState(() => googleEnabled = value),
|
padding: const EdgeInsets.only(
|
||||||
title: "Google",
|
top: 8, left: 40, right: 40, bottom: 8
|
||||||
padding: const EdgeInsets.only(
|
),
|
||||||
top: 8, left: 40, right: 40, bottom: 8
|
disabled: !generalEnabled,
|
||||||
),
|
),
|
||||||
disabled: !generalEnabled,
|
CustomCheckboxListTile(
|
||||||
),
|
value: pixabayEnabled,
|
||||||
CustomCheckboxListTile(
|
onChanged: (value) => setState(() => pixabayEnabled = value),
|
||||||
value: pixabayEnabled,
|
title: "Pixabay",
|
||||||
onChanged: (value) => setState(() => pixabayEnabled = value),
|
padding: const EdgeInsets.only(
|
||||||
title: "Pixabay",
|
top: 8, left: 40, right: 40, bottom: 8
|
||||||
padding: const EdgeInsets.only(
|
),
|
||||||
top: 8, left: 40, right: 40, bottom: 8
|
disabled: !generalEnabled,
|
||||||
),
|
),
|
||||||
disabled: !generalEnabled,
|
CustomCheckboxListTile(
|
||||||
),
|
value: yandexEnabled,
|
||||||
CustomCheckboxListTile(
|
onChanged: (value) => setState(() => yandexEnabled = value),
|
||||||
value: yandexEnabled,
|
title: "Yandex",
|
||||||
onChanged: (value) => setState(() => yandexEnabled = value),
|
padding: const EdgeInsets.only(
|
||||||
title: "Yandex",
|
top: 8, left: 40, right: 40, bottom: 8
|
||||||
padding: const EdgeInsets.only(
|
),
|
||||||
top: 8, left: 40, right: 40, bottom: 8
|
disabled: !generalEnabled,
|
||||||
),
|
),
|
||||||
disabled: !generalEnabled,
|
CustomCheckboxListTile(
|
||||||
),
|
value: youtubeEnabled,
|
||||||
CustomCheckboxListTile(
|
onChanged: (value) => setState(() => youtubeEnabled = value),
|
||||||
value: youtubeEnabled,
|
title: "YouTube",
|
||||||
onChanged: (value) => setState(() => youtubeEnabled = value),
|
padding: const EdgeInsets.only(
|
||||||
title: "YouTube",
|
top: 8, left: 40, right: 40, bottom: 8
|
||||||
padding: const EdgeInsets.only(
|
),
|
||||||
top: 8, left: 40, right: 40, bottom: 8
|
disabled: !generalEnabled,
|
||||||
),
|
),
|
||||||
disabled: !generalEnabled,
|
],
|
||||||
),
|
),
|
||||||
],
|
);
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.error:
|
case LoadStatus.error:
|
||||||
return SizedBox(
|
return SizedBox(
|
||||||
width: double.maxFinite,
|
width: double.maxFinite,
|
||||||
child: Column(
|
child: Column(
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
children: [
|
children: [
|
||||||
const Icon(
|
const Icon(
|
||||||
Icons.error,
|
Icons.error,
|
||||||
color: Colors.red,
|
color: Colors.red,
|
||||||
size: 50,
|
size: 50,
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.safeSearchSettingsNotLoaded,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
),
|
||||||
)
|
const SizedBox(height: 30),
|
||||||
],
|
Text(
|
||||||
),
|
AppLocalizations.of(context)!.safeSearchSettingsNotLoaded,
|
||||||
);
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return const SizedBox();
|
return const SizedBox();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
),
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,114 +51,116 @@ class _ServerInformationState extends State<ServerInformation> {
|
||||||
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
surfaceTintColor: isDesktop(width) ? Colors.transparent : null,
|
||||||
centerTitle: false,
|
centerTitle: false,
|
||||||
),
|
),
|
||||||
body: Builder(
|
body: SafeArea(
|
||||||
builder: (context) {
|
child: Builder(
|
||||||
switch (serverInfo.loadStatus) {
|
builder: (context) {
|
||||||
case LoadStatus.loading:
|
switch (serverInfo.loadStatus) {
|
||||||
return SizedBox(
|
case LoadStatus.loading:
|
||||||
width: double.maxFinite,
|
return SizedBox(
|
||||||
child: Column(
|
width: double.maxFinite,
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
child: Column(
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const CircularProgressIndicator(),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Padding(
|
||||||
|
padding: const EdgeInsets.symmetric(horizontal: 20),
|
||||||
|
child: Text(
|
||||||
|
AppLocalizations.of(context)!.loadingServerInfo,
|
||||||
|
textAlign: TextAlign.center,
|
||||||
|
style: TextStyle(
|
||||||
|
fontSize: 22,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
case LoadStatus.loaded:
|
||||||
|
return ListView(
|
||||||
children: [
|
children: [
|
||||||
const CircularProgressIndicator(),
|
CustomListTile(
|
||||||
const SizedBox(height: 30),
|
title: AppLocalizations.of(context)!.dnsAddresses,
|
||||||
Padding(
|
subtitle: AppLocalizations.of(context)!.seeDnsAddresses,
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 20),
|
onTap: () {
|
||||||
child: Text(
|
showModal(
|
||||||
AppLocalizations.of(context)!.loadingServerInfo,
|
context: context,
|
||||||
|
builder: (context) => DnsAddressesModal(
|
||||||
|
dnsAddresses: serverInfo.data!.dnsAddresses
|
||||||
|
)
|
||||||
|
);
|
||||||
|
},
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.dnsPort,
|
||||||
|
subtitle: serverInfo.data!.dnsPort.toString(),
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.httpPort,
|
||||||
|
subtitle: serverInfo.data!.httpPort.toString(),
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.protectionEnabled,
|
||||||
|
subtitle: serverInfo.data!.protectionEnabled == true
|
||||||
|
? AppLocalizations.of(context)!.yes
|
||||||
|
: AppLocalizations.of(context)!.no,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.dhcpAvailable,
|
||||||
|
subtitle: serverInfo.data!.dhcpAvailable == true
|
||||||
|
? AppLocalizations.of(context)!.yes
|
||||||
|
: AppLocalizations.of(context)!.no,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.serverRunning,
|
||||||
|
subtitle: serverInfo.data!.running == true
|
||||||
|
? AppLocalizations.of(context)!.yes
|
||||||
|
: AppLocalizations.of(context)!.no,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.serverVersion,
|
||||||
|
subtitle: serverInfo.data!.version,
|
||||||
|
),
|
||||||
|
CustomListTile(
|
||||||
|
title: AppLocalizations.of(context)!.serverLanguage,
|
||||||
|
subtitle: serverInfo.data!.language,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
case LoadStatus.error:
|
||||||
|
return SizedBox(
|
||||||
|
width: double.maxFinite,
|
||||||
|
child: Column(
|
||||||
|
mainAxisAlignment: MainAxisAlignment.center,
|
||||||
|
crossAxisAlignment: CrossAxisAlignment.center,
|
||||||
|
children: [
|
||||||
|
const Icon(
|
||||||
|
Icons.error,
|
||||||
|
color: Colors.red,
|
||||||
|
size: 50,
|
||||||
|
),
|
||||||
|
const SizedBox(height: 30),
|
||||||
|
Text(
|
||||||
|
AppLocalizations.of(context)!.serverInfoNotLoaded,
|
||||||
textAlign: TextAlign.center,
|
textAlign: TextAlign.center,
|
||||||
style: TextStyle(
|
style: TextStyle(
|
||||||
fontSize: 22,
|
fontSize: 22,
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
||||||
),
|
),
|
||||||
),
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
),
|
);
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.loaded:
|
default:
|
||||||
return ListView(
|
return const SizedBox();
|
||||||
children: [
|
}
|
||||||
CustomListTile(
|
},
|
||||||
title: AppLocalizations.of(context)!.dnsAddresses,
|
),
|
||||||
subtitle: AppLocalizations.of(context)!.seeDnsAddresses,
|
|
||||||
onTap: () {
|
|
||||||
showModal(
|
|
||||||
context: context,
|
|
||||||
builder: (context) => DnsAddressesModal(
|
|
||||||
dnsAddresses: serverInfo.data!.dnsAddresses
|
|
||||||
)
|
|
||||||
);
|
|
||||||
},
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.dnsPort,
|
|
||||||
subtitle: serverInfo.data!.dnsPort.toString(),
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.httpPort,
|
|
||||||
subtitle: serverInfo.data!.httpPort.toString(),
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.protectionEnabled,
|
|
||||||
subtitle: serverInfo.data!.protectionEnabled == true
|
|
||||||
? AppLocalizations.of(context)!.yes
|
|
||||||
: AppLocalizations.of(context)!.no,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.dhcpAvailable,
|
|
||||||
subtitle: serverInfo.data!.dhcpAvailable == true
|
|
||||||
? AppLocalizations.of(context)!.yes
|
|
||||||
: AppLocalizations.of(context)!.no,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.serverRunning,
|
|
||||||
subtitle: serverInfo.data!.running == true
|
|
||||||
? AppLocalizations.of(context)!.yes
|
|
||||||
: AppLocalizations.of(context)!.no,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.serverVersion,
|
|
||||||
subtitle: serverInfo.data!.version,
|
|
||||||
),
|
|
||||||
CustomListTile(
|
|
||||||
title: AppLocalizations.of(context)!.serverLanguage,
|
|
||||||
subtitle: serverInfo.data!.language,
|
|
||||||
),
|
|
||||||
]
|
|
||||||
);
|
|
||||||
|
|
||||||
case LoadStatus.error:
|
|
||||||
return SizedBox(
|
|
||||||
width: double.maxFinite,
|
|
||||||
child: Column(
|
|
||||||
mainAxisAlignment: MainAxisAlignment.center,
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.center,
|
|
||||||
children: [
|
|
||||||
const Icon(
|
|
||||||
Icons.error,
|
|
||||||
color: Colors.red,
|
|
||||||
size: 50,
|
|
||||||
),
|
|
||||||
const SizedBox(height: 30),
|
|
||||||
Text(
|
|
||||||
AppLocalizations.of(context)!.serverInfoNotLoaded,
|
|
||||||
textAlign: TextAlign.center,
|
|
||||||
style: TextStyle(
|
|
||||||
fontSize: 22,
|
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
|
|
||||||
default:
|
|
||||||
return const SizedBox();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,10 @@ class ThemeModal extends StatefulWidget {
|
||||||
final int selectedTheme;
|
final int selectedTheme;
|
||||||
|
|
||||||
const ThemeModal({
|
const ThemeModal({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.statusBarHeight,
|
required this.statusBarHeight,
|
||||||
required this.selectedTheme,
|
required this.selectedTheme,
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
State<ThemeModal> createState() => _ThemeModalState();
|
State<ThemeModal> createState() => _ThemeModalState();
|
||||||
|
|
|
@ -17,7 +17,7 @@ import 'package:adguard_home_manager/constants/enums.dart';
|
||||||
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
import 'package:adguard_home_manager/providers/servers_provider.dart';
|
||||||
|
|
||||||
class UpdateScreen extends StatelessWidget {
|
class UpdateScreen extends StatelessWidget {
|
||||||
const UpdateScreen({Key? key}) : super(key: key);
|
const UpdateScreen({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -166,33 +166,38 @@ class UpdateScreen extends StatelessWidget {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final changelog = serversProvider.updateAvailable.loadStatus == LoadStatus.loaded && serversProvider.updateAvailable.data!.changelog != null
|
final SafeArea? changelog;
|
||||||
? ListView(
|
if (serversProvider.updateAvailable.loadStatus == LoadStatus.loaded && serversProvider.updateAvailable.data!.changelog != null) {
|
||||||
children: [
|
changelog = SafeArea(
|
||||||
Padding(
|
child: ListView(
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
children: [
|
||||||
child: Text(
|
Padding(
|
||||||
"Changelog ${serversProvider.updateAvailable.data!.canAutoupdate == true
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
? serversProvider.updateAvailable.data!.newVersion
|
child: Text(
|
||||||
: serversProvider.updateAvailable.data!.currentVersion}",
|
"Changelog ${serversProvider.updateAvailable.data!.canAutoupdate == true
|
||||||
style: TextStyle(
|
? serversProvider.updateAvailable.data!.newVersion
|
||||||
fontSize: 20,
|
: serversProvider.updateAvailable.data!.currentVersion}",
|
||||||
fontWeight: FontWeight.w500,
|
style: TextStyle(
|
||||||
color: Theme.of(context).colorScheme.onSurfaceVariant
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w500,
|
||||||
|
color: Theme.of(context).colorScheme.onSurfaceVariant
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
const SizedBox(height: 8),
|
||||||
const SizedBox(height: 8),
|
Padding(
|
||||||
Padding(
|
padding: const EdgeInsets.symmetric(horizontal: 16),
|
||||||
padding: const EdgeInsets.symmetric(horizontal: 16),
|
child: Html(
|
||||||
child: Html(
|
data: html.parse(md.markdownToHtml(serversProvider.updateAvailable.data!.changelog!)).outerHtml,
|
||||||
data: html.parse(md.markdownToHtml(serversProvider.updateAvailable.data!.changelog!)).outerHtml,
|
onLinkTap: (url, context, attributes) => url != null ? openUrl(url) : null,
|
||||||
onLinkTap: (url, context, attributes) => url != null ? openUrl(url) : null,
|
)
|
||||||
)
|
)
|
||||||
)
|
],
|
||||||
],
|
),
|
||||||
)
|
);
|
||||||
: null;
|
} else {
|
||||||
|
changelog = null;
|
||||||
|
}
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
body: Column(
|
body: Column(
|
||||||
|
|
|
@ -550,8 +550,10 @@ class _AddServerModalState extends State<AddServerModal> {
|
||||||
const SizedBox(width: 8)
|
const SizedBox(width: 8)
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
body: ListView(
|
body: SafeArea(
|
||||||
children: form()
|
child: ListView(
|
||||||
|
children: form()
|
||||||
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,12 +5,14 @@ class OverlayStyle extends StatelessWidget {
|
||||||
final Widget child;
|
final Widget child;
|
||||||
|
|
||||||
const OverlayStyle({
|
const OverlayStyle({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.child
|
required this.child
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final systemGestureInsets = MediaQuery.of(context).systemGestureInsets;
|
||||||
|
|
||||||
return AnnotatedRegion<SystemUiOverlayStyle>(
|
return AnnotatedRegion<SystemUiOverlayStyle>(
|
||||||
value: SystemUiOverlayStyle(
|
value: SystemUiOverlayStyle(
|
||||||
statusBarColor: Colors.transparent,
|
statusBarColor: Colors.transparent,
|
||||||
|
@ -20,7 +22,13 @@ class OverlayStyle extends StatelessWidget {
|
||||||
statusBarIconBrightness: Theme.of(context).brightness == Brightness.light
|
statusBarIconBrightness: Theme.of(context).brightness == Brightness.light
|
||||||
? Brightness.dark
|
? Brightness.dark
|
||||||
: Brightness.light,
|
: Brightness.light,
|
||||||
systemNavigationBarColor: Theme.of(context).colorScheme.background,
|
systemNavigationBarColor: systemGestureInsets.left > 0 // If true gestures navigation
|
||||||
|
? Colors.transparent
|
||||||
|
: ElevationOverlay.applySurfaceTint(
|
||||||
|
Theme.of(context).colorScheme.surface,
|
||||||
|
Theme.of(context).colorScheme.surfaceTint,
|
||||||
|
3
|
||||||
|
),
|
||||||
systemNavigationBarIconBrightness: Theme.of(context).brightness == Brightness.light
|
systemNavigationBarIconBrightness: Theme.of(context).brightness == Brightness.light
|
||||||
? Brightness.dark
|
? Brightness.dark
|
||||||
: Brightness.light,
|
: Brightness.light,
|
||||||
|
|
|
@ -19,7 +19,7 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
final EdgeInsets? listPadding;
|
final EdgeInsets? listPadding;
|
||||||
|
|
||||||
const CustomTabContentList({
|
const CustomTabContentList({
|
||||||
Key? key,
|
super.key,
|
||||||
required this.loadingGenerator,
|
required this.loadingGenerator,
|
||||||
required this.itemsCount,
|
required this.itemsCount,
|
||||||
required this.contentWidget,
|
required this.contentWidget,
|
||||||
|
@ -32,7 +32,7 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
this.fabVisible,
|
this.fabVisible,
|
||||||
this.noSliver,
|
this.noSliver,
|
||||||
this.listPadding
|
this.listPadding
|
||||||
}) : super(key: key);
|
});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
@ -49,7 +49,6 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
else {
|
else {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (BuildContext context) => CustomScrollView(
|
builder: (BuildContext context) => CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
|
@ -72,41 +71,45 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
case LoadStatus.loaded:
|
case LoadStatus.loaded:
|
||||||
if (noSliver == true) {
|
if (noSliver == true) {
|
||||||
if (itemsCount > 0) {
|
if (itemsCount > 0) {
|
||||||
return Stack(
|
return SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
ListView.builder(
|
children: [
|
||||||
padding: listPadding,
|
ListView.builder(
|
||||||
itemCount: itemsCount,
|
padding: listPadding,
|
||||||
itemBuilder: (context, index) => contentWidget(index),
|
itemCount: itemsCount,
|
||||||
),
|
itemBuilder: (context, index) => contentWidget(index),
|
||||||
if (fab != null) AnimatedPositioned(
|
),
|
||||||
duration: const Duration(milliseconds: 100),
|
if (fab != null) AnimatedPositioned(
|
||||||
curve: Curves.easeInOut,
|
duration: const Duration(milliseconds: 100),
|
||||||
bottom: fabVisible != null && fabVisible == true ?
|
curve: Curves.easeInOut,
|
||||||
appConfigProvider.showingSnackbar
|
bottom: fabVisible != null && fabVisible == true ?
|
||||||
? 70 : 20
|
appConfigProvider.showingSnackbar
|
||||||
: -70,
|
? 70 : 20
|
||||||
right: 20,
|
: -70,
|
||||||
child: fab!
|
right: 20,
|
||||||
),
|
child: fab!
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return Stack(
|
return SafeArea(
|
||||||
children: [
|
child: Stack(
|
||||||
noData,
|
children: [
|
||||||
if (fab != null) AnimatedPositioned(
|
noData,
|
||||||
duration: const Duration(milliseconds: 100),
|
if (fab != null) AnimatedPositioned(
|
||||||
curve: Curves.easeInOut,
|
duration: const Duration(milliseconds: 100),
|
||||||
bottom: fabVisible != null && fabVisible == true ?
|
curve: Curves.easeInOut,
|
||||||
appConfigProvider.showingSnackbar
|
bottom: fabVisible != null && fabVisible == true ?
|
||||||
? 70 : 20
|
appConfigProvider.showingSnackbar
|
||||||
: -70,
|
? 70 : 20
|
||||||
right: 20,
|
: -70,
|
||||||
child: fab!
|
right: 20,
|
||||||
),
|
child: fab!
|
||||||
],
|
),
|
||||||
|
],
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -146,10 +149,10 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
curve: Curves.easeInOut,
|
curve: Curves.easeInOut,
|
||||||
bottom: fabVisible != null && fabVisible == true ?
|
bottom: fabVisible != null && fabVisible == true ?
|
||||||
appConfigProvider.showingSnackbar
|
appConfigProvider.showingSnackbar
|
||||||
? 70 : 20
|
? 90 : 20
|
||||||
: -70,
|
: -90,
|
||||||
right: 20,
|
right: 20,
|
||||||
child: fab!
|
child: SafeArea(child: fab!)
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
|
@ -169,7 +172,6 @@ class CustomTabContentList extends StatelessWidget {
|
||||||
else {
|
else {
|
||||||
return SafeArea(
|
return SafeArea(
|
||||||
top: false,
|
top: false,
|
||||||
bottom: false,
|
|
||||||
child: Builder(
|
child: Builder(
|
||||||
builder: (BuildContext context) => CustomScrollView(
|
builder: (BuildContext context) => CustomScrollView(
|
||||||
slivers: [
|
slivers: [
|
||||||
|
|
Loading…
Add table
Reference in a new issue