diff --git a/lib/views/dashboard/dashboard.dart b/lib/views/dashboard/dashboard.dart index 10b73da6e..1f84b9cbc 100644 --- a/lib/views/dashboard/dashboard.dart +++ b/lib/views/dashboard/dashboard.dart @@ -250,7 +250,14 @@ class _DashboardViewState extends ConsumerState { (isEdit) => CommonScaffold( title: appLocalizations.dashboard, actions: _buildActions(isEdit), - floatingActionButton: const StartButton(), + floatingActionButton: Row( + mainAxisSize: MainAxisSize.min, + children: const [ + LaunchBrowserButton(), + SizedBox(width: 8), + StartButton(), + ], + ), body: Align( alignment: Alignment.topCenter, child: SingleChildScrollView( diff --git a/lib/views/dashboard/widgets/start_button.dart b/lib/views/dashboard/widgets/start_button.dart index 74d5d8950..3eb95872f 100644 --- a/lib/views/dashboard/widgets/start_button.dart +++ b/lib/views/dashboard/widgets/start_button.dart @@ -1,3 +1,5 @@ +import 'dart:io'; + import 'package:fl_clash/common/common.dart'; import 'package:fl_clash/controller.dart'; import 'package:fl_clash/enum/enum.dart'; @@ -135,3 +137,72 @@ class _StartButtonState extends ConsumerState ); } } + +class LaunchBrowserButton extends ConsumerWidget { + const LaunchBrowserButton({super.key}); + + Future _launchWithProxy(int port) async { + final proxyArg = '--proxy-server=http://127.0.0.1:$port'; + if (system.isWindows) { + // Follow v2rayN style: start browser with proxy args. + await Process.start('cmd', ['/c', 'start', '', 'msedge', proxyArg]); + return; + } + if (system.isMacOS) { + await Process.start('open', [ + '-a', + 'Google Chrome', + '--args', + proxyArg, + ]); + return; + } + if (system.isLinux) { + final commands = [ + 'google-chrome', + 'google-chrome-stable', + 'chromium-browser', + 'chromium', + ]; + for (final command in commands) { + try { + await Process.start(command, [proxyArg]); + return; + } catch (_) { + // try next command + } + } + throw 'Chrome is not found on this Linux system.'; + } + } + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (system.isAndroid) { + return const SizedBox.shrink(); + } + final hasProfile = ref.watch( + profilesProvider.select((state) => state.isNotEmpty), + ); + if (!hasProfile) { + return const SizedBox.shrink(); + } + final isStart = ref.watch(isStartProvider); + final port = ref.watch(proxyStateProvider.select((state) => state.port)); + return FloatingActionButton( + heroTag: null, + mini: true, + tooltip: 'Launch browser with proxy', + onPressed: !isStart + ? null + : () async { + try { + await _launchWithProxy(port); + } catch (e) { + globalState.showNotifier('Launch browser failed: $e'); + } + }, + child: const Icon(Icons.language), + ); + } +}