import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/enum/enum.dart'; import 'package:fl_clash/models/models.dart'; import 'package:fl_clash/state.dart'; import 'package:fl_clash/widgets/widgets.dart'; import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; class ConfigFragment extends StatefulWidget { const ConfigFragment({super.key}); @override State createState() => _ConfigFragmentState(); } class _ConfigFragmentState extends State { _modifyMixedPort(num mixedPort) async { final port = await globalState.showCommonDialog( child: MixedPortFormDialog( mixedPort: mixedPort, ), ); if (port != null && port != mixedPort && mounted) { try { final mixedPort = int.parse(port); if (mixedPort < 1024 || mixedPort > 49151) throw "Invalid port"; globalState.appController.clashConfig.mixedPort = mixedPort; globalState.appController.updateClashConfigDebounce(); } catch (e) { globalState.showMessage( title: appLocalizations.proxyPort, message: TextSpan( text: e.toString(), ), ); } } } _updateLoglevel(LogLevel? logLevel) { if (logLevel == null || logLevel == globalState.appController.clashConfig.logLevel) return; globalState.appController.clashConfig.logLevel = logLevel; globalState.appController.updateClashConfigDebounce(); } @override Widget build(BuildContext context) { List items = [ Selector( selector: (_, clashConfig) => clashConfig.mixedPort, builder: (_, mixedPort, __) { return ListItem( onTab: () { _modifyMixedPort(mixedPort); }, padding: const EdgeInsets.symmetric(horizontal: 16,vertical: 4), leading: const Icon(Icons.adjust), title: Text(appLocalizations.proxyPort), trailing: FilledButton.tonal( onPressed: () { _modifyMixedPort(mixedPort); }, child: Text( "$mixedPort", ), ), ); }, ), Selector( selector: (_, clashConfig) => clashConfig.allowLan, builder: (_, allowLan, __) { return ListItem.switchItem( leading: const Icon(Icons.device_hub), title: Text(appLocalizations.allowLan), subtitle: Text(appLocalizations.allowLanDesc), delegate: SwitchDelegate( value: allowLan, onChanged: (bool value) async { final clashConfig = context.read(); clashConfig.allowLan = value; globalState.appController.updateClashConfigDebounce(); }, ), ); }, ), // if (system.isDesktop) // Selector( // selector: (_, clashConfig) => clashConfig.tun.enable, // builder: (_, tunEnable, __) { // return ListItem.switchItem( // leading: const Icon(Icons.support), // title: Text(appLocalizations.tun), // subtitle: Text(appLocalizations.tunDesc), // delegate: SwitchDelegate( // value: tunEnable, // onChanged: (bool value) async { // final clashConfig = context.read(); // clashConfig.tun = Tun(enable: value); // globalState.appController.updateClashConfigDebounce(); // }, // ), // ); // }, // ), Selector( selector: (_, config) => config.isCompatible, builder: (_, isCompatible, __) { return ListItem.switchItem( leading: const Icon(Icons.expand), title: Text(appLocalizations.compatible), subtitle: Text(appLocalizations.compatibleDesc), delegate: SwitchDelegate( value: isCompatible, onChanged: (bool value) async { final appController = globalState.appController; appController.config.isCompatible = value; await appController.updateClashConfig(isPatch: false); await appController.updateGroups(); appController.changeProxy(); }, ), ); }, ), Padding( padding: kMaterialListPadding, child: Selector( selector: (_, clashConfig) => clashConfig.logLevel, builder: (_, value, __) { return ListItem( leading: const Icon(Icons.feedback), title: Text(appLocalizations.logLevel), trailing: SizedBox( height: 48, child: DropdownMenu( width: 124, inputDecorationTheme: const InputDecorationTheme( filled: true, contentPadding: EdgeInsets.symmetric( vertical: 5, horizontal: 16, ), ), initialSelection: value, dropdownMenuEntries: [ for (final logLevel in LogLevel.values) DropdownMenuEntry( value: logLevel, label: logLevel.name, ) ], onSelected: _updateLoglevel, ), ), ); }, ), ), ]; return ListView.separated( itemBuilder: (_, index) { return Container( alignment: Alignment.center, child: items[index], ); }, separatorBuilder: (_, __) { return const Divider( height: 0, ); }, itemCount: items.length, ); } } class MixedPortFormDialog extends StatefulWidget { final num mixedPort; const MixedPortFormDialog({super.key, required this.mixedPort}); @override State createState() => _MixedPortFormDialogState(); } class _MixedPortFormDialogState extends State { late TextEditingController portController; @override void initState() { super.initState(); portController = TextEditingController(text: "${widget.mixedPort}"); } _handleAddProfileFormURL() async { final port = portController.value.text; if (port.isEmpty) return; Navigator.of(context).pop(port); } @override Widget build(BuildContext context) { return AlertDialog( title: Text(appLocalizations.proxyPort), content: SizedBox( width: 300, child: Wrap( runSpacing: 16, children: [ TextField( controller: portController, decoration: const InputDecoration( border: OutlineInputBorder(), ), ), ], ), ), actions: [ TextButton( onPressed: _handleAddProfileFormURL, child: Text(appLocalizations.submit), ) ], ); } }