Dhcp and dns settings optimizations

This commit is contained in:
Juan Gilsanz Polo 2023-05-01 02:50:42 +02:00
parent 4c86727b5d
commit 5d23f3c3e7
8 changed files with 676 additions and 515 deletions

View file

@ -5,10 +5,12 @@ import 'package:adguard_home_manager/models/dhcp.dart';
class AddStaticLeaseModal extends StatefulWidget {
final void Function(Lease) onConfirm;
final bool dialog;
const AddStaticLeaseModal({
Key? key,
required this.onConfirm,
required this.dialog
}) : super(key: key);
@override
@ -65,24 +67,19 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
@override
Widget build(BuildContext context) {
return Padding(
padding: MediaQuery.of(context).viewInsets,
child: Container(
height: 510,
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28)
)
),
child: Column(
Widget content() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ListView(
physics: 550 < MediaQuery.of(context).size.height
? const NeverScrollableScrollPhysics()
: null,
SingleChildScrollView(
child: Wrap(
children: [
Padding(
padding: const EdgeInsets.only(bottom: 16),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 24),
@ -101,9 +98,15 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
color: Theme.of(context).colorScheme.onSurface
),
),
const SizedBox(height: 16),
],
),
],
),
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
padding: const EdgeInsets.only(
left: 24, right: 24, bottom: 12
),
child: TextFormField(
controller: macController,
onChanged: validateMac,
@ -119,9 +122,8 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
),
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 12),
child: TextFormField(
controller: ipController,
onChanged: validateIp,
@ -137,9 +139,10 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
),
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 28),
padding: const EdgeInsets.only(
left: 24, right: 24, top: 12
),
child: TextFormField(
controller: hostNameController,
onChanged: (value) {
@ -167,7 +170,7 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
),
),
Padding(
padding: const EdgeInsets.all(20),
padding: const EdgeInsets.all(24),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
@ -202,8 +205,33 @@ class _AddStaticLeaseModalState extends State<AddStaticLeaseModal> {
),
)
],
);
}
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(
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28)
)
),
child: content()
),
);
}
}
}

View file

@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:bottom_sheet/bottom_sheet.dart';
@ -213,6 +215,8 @@ class _DhcpWidgetState extends State<DhcpWidget> {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
final width = MediaQuery.of(context).size.width;
void saveSettings() async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.savingSettings);
@ -354,24 +358,32 @@ class _DhcpWidgetState extends State<DhcpWidget> {
void selectInterface() {
ScaffoldMessenger.of(context).clearSnackBars();
Future.delayed(const Duration(seconds: 0), () {
showFlexibleBottomSheet(
minHeight: 0.6,
initHeight: 0.6,
maxHeight: 0.95,
isCollapsible: true,
duration: const Duration(milliseconds: 250),
anchors: [0.95],
if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
showDialog(
context: context,
builder: (ctx, controller, offset) => SelectInterfaceModal(
builder: (context) => SelectInterfaceModal(
interfaces: serversProvider.dhcp.data!.networkInterfaces,
scrollController: controller,
onSelect: (interface) => setState(() {
clearAll();
selectedInterface = interface;
})
),
bottomSheetColor: Colors.transparent
}),
dialog: true,
)
);
}
else {
showModalBottomSheet(
context: context,
builder: (context) => SelectInterfaceModal(
interfaces: serversProvider.dhcp.data!.networkInterfaces,
onSelect: (i) => setState(() {
clearAll();
selectedInterface = i;
}),
dialog: false,
),
);
}
});
}
@ -399,7 +411,8 @@ class _DhcpWidgetState extends State<DhcpWidget> {
case 1:
if (selectedInterface != null) {
return ListView(
return SingleChildScrollView(
child: Wrap(
children: [
Padding(
padding: const EdgeInsets.only(
@ -459,10 +472,16 @@ class _DhcpWidgetState extends State<DhcpWidget> {
if (selectedInterface!.ipv4Addresses.isNotEmpty) ...[
SectionLabel(
label: AppLocalizations.of(context)!.ipv4settings,
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 24),
padding: const EdgeInsets.only(
top: 24, left: 16, right: 16, bottom: 8
)
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
FractionallySizedBox(
widthFactor: width > 900 ? 0.5 : 1,
child: Padding(
padding: width > 900
? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv4StartRangeController,
onChanged: (value) => validateIpV4(value, 'ipv4StartRangeError', AppLocalizations.of(context)!.ipNotValid),
@ -479,9 +498,13 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
),
FractionallySizedBox(
widthFactor: width > 900 ? 0.5 : 1,
child: Padding(
padding: width > 900
? const EdgeInsets.only(top: 12, bottom: 12, left: 8, right: 16)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv4EndRangeController,
onChanged: (value) => validateIpV4(value, 'ipv4EndRangeError', AppLocalizations.of(context)!.ipNotValid),
@ -498,9 +521,13 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
),
FractionallySizedBox(
widthFactor: width > 900 ? 0.5 : 1,
child: Padding(
padding: width > 900
? const EdgeInsets.only(top: 12, bottom: 12, left: 16, right: 8)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv4SubnetMaskController,
onChanged: (value) => validateIpV4(value, 'ipv4SubnetMaskError', AppLocalizations.of(context)!.subnetMaskNotValid),
@ -517,9 +544,13 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
),
FractionallySizedBox(
widthFactor: width > 900 ? 0.5 : 1,
child: Padding(
padding: width > 900
? const EdgeInsets.only(top: 12, bottom: 12, left: 8, right: 16)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv4GatewayController,
onChanged: (value) => validateIpV4(value, 'ipv4GatewayError', AppLocalizations.of(context)!.gatewayNotValid),
@ -536,9 +567,11 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
),
FractionallySizedBox(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv4LeaseTimeController,
onChanged: (value) {
@ -562,13 +595,19 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
),
],
if (selectedInterface!.ipv6Addresses.isNotEmpty) ...[
SectionLabel(
label: AppLocalizations.of(context)!.ipv6settings,
padding: const EdgeInsets.all(16)
),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
FractionallySizedBox(
widthFactor: width > 900 ? 0.5 : 1,
child: Padding(
padding: width > 900
? const EdgeInsets.only(top: 8, bottom: 12, left: 16, right: 8)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv6StartRangeController,
onChanged: (value) => validateIpV4(value, 'ipv6StartRangeError', AppLocalizations.of(context)!.ipNotValid),
@ -585,9 +624,13 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
),
FractionallySizedBox(
widthFactor: width > 900 ? 0.5 : 1,
child: Padding(
padding: width > 900
? const EdgeInsets.only(top: 8, bottom: 12, left: 8, right: 16)
: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv6EndRangeController,
onChanged: (value) => validateIpV4(value, 'ipv6EndRangeError', AppLocalizations.of(context)!.ipNotValid),
@ -604,9 +647,11 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
const SizedBox(height: 30),
Padding(
padding: const EdgeInsets.symmetric(horizontal: 16),
),
FractionallySizedBox(
widthFactor: 1,
child: Padding(
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
child: TextFormField(
controller: ipv6LeaseTimeController,
onChanged: (value) {
@ -630,12 +675,14 @@ class _DhcpWidgetState extends State<DhcpWidget> {
keyboardType: TextInputType.number,
),
),
),
],
const SizedBox(height: 20),
SectionLabel(
label: AppLocalizations.of(context)!.dhcpLeases,
padding: const EdgeInsets.all(16),
),
Material(
if (width <= 900) Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
@ -668,7 +715,7 @@ class _DhcpWidgetState extends State<DhcpWidget> {
),
),
),
Material(
if (width <= 900) Material(
color: Colors.transparent,
child: InkWell(
onTap: () {
@ -701,12 +748,51 @@ class _DhcpWidgetState extends State<DhcpWidget> {
),
),
),
if (width > 900) Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => DhcpLeases(
items: serversProvider.dhcp.data!.dhcpStatus.leases,
staticLeases: false,
)
)),
child: Row(
children: [
Text(AppLocalizations.of(context)!.dhcpLeases),
const SizedBox(width: 8),
const Icon(Icons.arrow_forward_rounded)
],
)
),
ElevatedButton(
onPressed: () => Navigator.push(context, MaterialPageRoute(
builder: (context) => DhcpLeases(
items: serversProvider.dhcp.data!.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 Column(
return Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
mainAxisSize: MainAxisSize.max,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
@ -728,6 +814,8 @@ class _DhcpWidgetState extends State<DhcpWidget> {
child: Text(AppLocalizations.of(context)!.selectInterface)
),
],
),
],
);
}

View file

@ -1,5 +1,7 @@
// ignore_for_file: use_build_context_synchronously
import 'dart:io';
import 'package:flutter/material.dart';
import 'package:provider/provider.dart';
import 'package:animations/animations.dart';
@ -30,6 +32,8 @@ class DhcpLeases extends StatelessWidget {
final serversProvider = Provider.of<ServersProvider>(context);
final appConfigProvider = Provider.of<AppConfigProvider>(context);
final width = MediaQuery.of(context).size.width;
void deleteLease(Lease lease) async {
ProcessModal processModal = ProcessModal(context: context);
processModal.open(AppLocalizations.of(context)!.deleting);
@ -119,15 +123,27 @@ class DhcpLeases extends StatelessWidget {
}
void openAddStaticLease() {
if (width > 900 || !(Platform.isAndroid || Platform.isIOS)) {
showDialog(
context: context,
builder: (context) => AddStaticLeaseModal(
onConfirm: createLease,
dialog: true,
),
);
}
else {
showModalBottomSheet(
context: context,
builder: (context) => AddStaticLeaseModal(
onConfirm: createLease
onConfirm: createLease,
dialog: false,
),
backgroundColor: Colors.transparent,
isScrollControlled: true
);
}
}
return Scaffold(
appBar: AppBar(

View file

@ -7,31 +7,29 @@ import 'package:adguard_home_manager/models/dhcp.dart';
class SelectInterfaceModal extends StatelessWidget {
final List<NetworkInterface> interfaces;
final ScrollController scrollController;
final void Function(NetworkInterface) onSelect;
final bool dialog;
const SelectInterfaceModal({
Key? key,
required this.interfaces,
required this.scrollController,
required this.onSelect,
required this.dialog
}) : super(key: key);
@override
Widget build(BuildContext context) {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28)
)
),
child: Column(
Widget content() {
return Column(
mainAxisSize: MainAxisSize.min,
children: [
Expanded(
child: ListView(
controller: scrollController,
SingleChildScrollView(
child: Wrap(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Column(
children: [
Padding(
padding: const EdgeInsets.only(top: 24),
@ -50,6 +48,10 @@ class SelectInterfaceModal extends StatelessWidget {
color: Theme.of(context).colorScheme.onSurface
),
),
],
),
],
),
const SizedBox(height: 16),
ListView.builder(
primary: false,
@ -190,7 +192,30 @@ class SelectInterfaceModal extends StatelessWidget {
),
if (Platform.isIOS) const SizedBox(height: 16)
],
);
}
if (dialog == true) {
return Dialog(
child: ConstrainedBox(
constraints: const BoxConstraints(
maxWidth: 500
),
child: content()
),
);
}
else {
return Container(
decoration: BoxDecoration(
color: Theme.of(context).dialogBackgroundColor,
borderRadius: const BorderRadius.only(
topLeft: Radius.circular(28),
topRight: Radius.circular(28)
)
),
child: content()
);
}
}
}

View file

@ -182,8 +182,7 @@ class _BootstrapDnsScreenState extends State<BootstrapDnsScreen> {
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: MediaQuery.of(context).size.width-74,
Expanded(
child: TextFormField(
controller: c['controller'],
onChanged: (value) => validateIp(c, value),
@ -199,6 +198,7 @@ class _BootstrapDnsScreenState extends State<BootstrapDnsScreen> {
)
),
),
const SizedBox(width: 8),
IconButton(
onPressed: () {
setState(() => bootstrapControllers = bootstrapControllers.where((con) => con != c).toList());

View file

@ -229,8 +229,7 @@ class _PrivateReverseDnsServersScreenState extends State<PrivateReverseDnsServer
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: MediaQuery.of(context).size.width-74,
Expanded(
child: TextFormField(
controller: c['controller'],
onChanged: (value) => validateAddress(c, value),
@ -246,6 +245,7 @@ class _PrivateReverseDnsServersScreenState extends State<PrivateReverseDnsServer
)
),
),
const SizedBox(width: 8),
IconButton(
onPressed: () {
setState(() => reverseResolversControllers = reverseResolversControllers.where((con) => con != c).toList());

View file

@ -185,13 +185,12 @@ class _UpstreamDnsScreenState extends State<UpstreamDnsScreen> {
),
...dnsServers.map((item) => Padding(
padding: const EdgeInsets.only(
left: 16, right: 6, bottom: 20
left: 16, right: 6, bottom: 24
),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (item['controller'] != null) SizedBox(
width: MediaQuery.of(context).size.width-74,
if (item['controller'] != null) Expanded(
child: TextFormField(
controller: item['controller'],
onChanged: (_) => checkValidValues(),
@ -206,6 +205,7 @@ class _UpstreamDnsScreenState extends State<UpstreamDnsScreen> {
)
),
),
const SizedBox(width: 8),
if (item['comment'] != null) Expanded(
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
@ -232,10 +232,12 @@ class _UpstreamDnsScreenState extends State<UpstreamDnsScreen> {
},
icon: const Icon(Icons.remove_circle_outline),
tooltip: AppLocalizations.of(context)!.remove,
)
),
const SizedBox(width: 4),
],
),
)).toList(),
const SizedBox(height: 12),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
mainAxisSize: MainAxisSize.min,

View file

@ -41,7 +41,8 @@ class CustomRadioListTile extends StatelessWidget {
backgroundColor: radioBackgroundColor,
),
const SizedBox(width: 24),
Column(
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
SizedBox(
@ -69,6 +70,7 @@ class CustomRadioListTile extends StatelessWidget {
]
],
),
),
],
),
),