Skip to content

Commit 808054d

Browse files
committed
add bypass proxy list feature in settings
1 parent 06ec022 commit 808054d

File tree

5 files changed

+619
-2
lines changed

5 files changed

+619
-2
lines changed

lib/controller/bypass_controller.dart

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import 'package:get/get.dart';
2+
import '/model/bypass_model.dart';
3+
4+
class BypassController extends GetxController {
5+
final BypassModel bypassModel = BypassModel();
6+
7+
// Set the Bypass proxy list
8+
void setBypassProxyList(String bypassList) {
9+
bypassModel.bypassList.value = bypassList;
10+
}
11+
12+
// Set the value of a boolean observable (RxBool)
13+
void setSwitchValue(RxBool oldValue, bool newValue) {
14+
oldValue.value = newValue;
15+
}
16+
}

lib/model/bypass_model.dart

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import 'package:get/get.dart';
2+
3+
// Model for Bypass proxy list
4+
class BypassModel {
5+
// Observable Bypass list
6+
final RxString bypassList = '*.local 169.254/16'.obs;
7+
// Observable for enabling/disabling
8+
final RxBool isEnabled = false.obs;
9+
}

lib/utils/custom_commands.dart

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
import 'dart:io';
22
import 'package:flutter/material.dart';
3-
import '../utils/custom_snackbar.dart';
3+
import '/utils/custom_snackbar.dart';
44

55
class CustomCommands {
66
// Open a link in the user's browser
7-
static void openURLInBrowser(BuildContext context, {required String link}) async {
7+
static void openURLInBrowser(BuildContext context,
8+
{required String link}) async {
89
ProcessResult result = await Process.run(
910
'open',
1011
['https://$link'],
@@ -84,4 +85,26 @@ class CustomCommands {
8485
runInShell: true,
8586
);
8687
}
88+
89+
// Set bypass proxy list command
90+
static Future<ProcessResult> setBypassList({
91+
required String bypassList,
92+
}) async {
93+
final result = await Process.run(
94+
'networksetup',
95+
['-setproxybypassdomains', 'Wi-Fi', bypassList],
96+
runInShell: true,
97+
);
98+
99+
return result;
100+
}
101+
102+
// Clear bypass proxy list command
103+
static Future<void> clearBypassList() async {
104+
await Process.run(
105+
'networksetup',
106+
['-setproxybypassdomains', 'Wi-Fi', 'Empty'],
107+
runInShell: true,
108+
);
109+
}
87110
}

lib/view/pages/home_view.dart

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
import 'dart:io';
2+
import 'package:flutter/material.dart';
3+
import 'package:audioplayers/audioplayers.dart';
4+
import 'package:get/get.dart';
5+
6+
import '/view/widgets/appbar.dart';
7+
import '/view/widgets/drawer.dart';
8+
import '/controller/proxy_controller.dart';
9+
import '/controller/timer_controller.dart';
10+
import '/utils/custom_commands.dart';
11+
import '/utils/custom_snackbar.dart';
12+
13+
// HomeView widget responsible for the main UI.
14+
class HomeView extends StatelessWidget {
15+
const HomeView({Key? key, required this.title}) : super(key: key);
16+
17+
final String title;
18+
19+
@override
20+
Widget build(BuildContext context) {
21+
// Initialize controllers and variables
22+
final TimerController timerController = Get.put(TimerController());
23+
final ProxyController proxyController = Get.put(ProxyController());
24+
final player = AudioPlayer();
25+
final isConnected = RxBool(false);
26+
final tapScale = RxDouble(1);
27+
final GlobalKey<ScaffoldState> _key = GlobalKey();
28+
29+
ProcessResult httpProxyResult = ProcessResult(1, 2, 3, 4);
30+
ProcessResult httpsProxyResult = ProcessResult(1, 2, 3, 4);
31+
ProcessResult socksProxyResult = ProcessResult(1, 2, 3, 4);
32+
33+
return Scaffold(
34+
key: _key,
35+
backgroundColor: const Color(0xFF0A0726),
36+
appBar: CustomAppBar(
37+
scaffoldKey: _key,
38+
isConnected: isConnected,
39+
title: title,
40+
),
41+
drawer: CustomDrawer(scaffoldKey: _key),
42+
body: Column(
43+
crossAxisAlignment: CrossAxisAlignment.center,
44+
mainAxisAlignment: MainAxisAlignment.center,
45+
children: [
46+
const Expanded(
47+
child: SizedBox(),
48+
flex: 4,
49+
),
50+
Obx(
51+
() => Center(
52+
child: AnimatedScale(
53+
scale: tapScale.value,
54+
duration: const Duration(microseconds: 100),
55+
child: Container(
56+
height: 172,
57+
width: 172,
58+
decoration: BoxDecoration(
59+
shape: BoxShape.circle,
60+
color: isConnected.value
61+
? const Color(0xFF362D95)
62+
: const Color(0xFF4237B1),
63+
boxShadow: [
64+
BoxShadow(
65+
blurRadius: 90,
66+
color: isConnected.value
67+
? const Color(0xFF6354FF).withOpacity(0.6)
68+
: const Color(0xFF6354FF).withOpacity(0.4),
69+
),
70+
],
71+
),
72+
child: GetBuilder(
73+
init: proxyController,
74+
builder: (controller) {
75+
return customButton(
76+
tapScale,
77+
timerController,
78+
player,
79+
isConnected,
80+
context,
81+
proxyController,
82+
httpProxyResult,
83+
httpsProxyResult,
84+
socksProxyResult,
85+
);
86+
},
87+
),
88+
),
89+
),
90+
),
91+
),
92+
const Expanded(
93+
child: SizedBox(),
94+
flex: 9,
95+
),
96+
],
97+
),
98+
);
99+
}
100+
101+
Widget customButton(
102+
RxDouble tapScale,
103+
TimerController timerController,
104+
AudioPlayer player,
105+
RxBool isConnected,
106+
BuildContext context,
107+
ProxyController proxyController,
108+
ProcessResult httpProxyResult,
109+
ProcessResult httpsProxyResult,
110+
ProcessResult socksProxyResult,
111+
) {
112+
return Padding(
113+
padding: const EdgeInsets.all(8.0),
114+
child: ClipRRect(
115+
borderRadius: BorderRadius.circular(360),
116+
child: GestureDetector(
117+
onTapDown: (details) {
118+
tapScale.value = 0.97;
119+
},
120+
onTapUp: (details) {
121+
tapScale.value = 1;
122+
},
123+
// Toggle the proxy connection on button tap
124+
onTap: () async {
125+
timerController.isTimerRunning
126+
// If the timer is running, stop it
127+
? timerController.stopTimer()
128+
// If the timer is not running, start it
129+
: timerController.startTimer();
130+
await player.play(
131+
AssetSource('click-sound.mp3'),
132+
volume: 0.3,
133+
);
134+
if (isConnected.value) {
135+
// Disconnect from proxy
136+
try {
137+
CustomCommands.disableHTTPProxy();
138+
CustomCommands.disableHTTPSProxy();
139+
CustomCommands.disableSocksProxy();
140+
} catch (e) {
141+
CustomSnackBar.red(
142+
context,
143+
content: 'Error: $e',
144+
);
145+
}
146+
isConnected.value = false;
147+
CustomSnackBar.red(
148+
context,
149+
content: 'Disconnected',
150+
);
151+
} else if (!isConnected.value) {
152+
// Connect to proxy
153+
if (proxyController.httpModel.isEnabled.value) {
154+
httpProxyResult = await CustomCommands.enableHTTPProxy(
155+
server: proxyController.httpModel.server.value,
156+
port: proxyController.httpModel.port.value,
157+
);
158+
}
159+
if (proxyController.httpsModel.isEnabled.value) {
160+
httpsProxyResult = await CustomCommands.enableHTTPSProxy(
161+
server: proxyController.httpsModel.server.value,
162+
port: proxyController.httpsModel.port.value,
163+
);
164+
}
165+
if (proxyController.socksModel.isEnabled.value) {
166+
socksProxyResult = await CustomCommands.enableSocksProxy(
167+
server: proxyController.socksModel.server.value,
168+
port: proxyController.socksModel.port.value,
169+
);
170+
}
171+
172+
if (!proxyController.httpModel.isEnabled.value &&
173+
!proxyController.httpsModel.isEnabled.value &&
174+
!proxyController.socksModel.isEnabled.value) {
175+
CustomSnackBar.red(context,
176+
content: 'Error: Enable a proxy in Settings.');
177+
} else if (httpProxyResult.exitCode == 0 ||
178+
httpsProxyResult.exitCode == 0 ||
179+
socksProxyResult.exitCode == 0) {
180+
CustomSnackBar.green(context, content: 'Connected');
181+
isConnected.value = true;
182+
} else {
183+
CustomSnackBar.red(context,
184+
content: 'Error: Unable to connect');
185+
}
186+
}
187+
await player.play(AssetSource('click-sound.mp3'), volume: 0.3);
188+
},
189+
child: Container(
190+
decoration: BoxDecoration(
191+
shape: BoxShape.circle,
192+
gradient: LinearGradient(
193+
begin: Alignment.topLeft,
194+
end: Alignment.bottomRight,
195+
colors: isConnected.value
196+
? [
197+
const Color(0xFF5245D4),
198+
const Color(0xFF292277),
199+
]
200+
: [
201+
const Color(0xFF0A0726),
202+
const Color(0xFF0A0726),
203+
],
204+
),
205+
),
206+
child: Center(
207+
child: isConnected.value
208+
? Column(
209+
mainAxisAlignment: MainAxisAlignment.center,
210+
crossAxisAlignment: CrossAxisAlignment.center,
211+
children: [
212+
Obx(() {
213+
return Text(
214+
timerController.formattedTimer(),
215+
style: const TextStyle(
216+
fontSize: 20,
217+
fontWeight: FontWeight.w500,
218+
color: Colors.white,
219+
),
220+
);
221+
}),
222+
const Text(
223+
'STOP',
224+
style: TextStyle(
225+
fontSize: 16,
226+
fontWeight: FontWeight.w400,
227+
color: Colors.white,
228+
),
229+
),
230+
],
231+
)
232+
: const Text(
233+
'GO',
234+
style: TextStyle(
235+
fontSize: 40,
236+
fontWeight: FontWeight.w600,
237+
color: Color(0xFF4237B1),
238+
),
239+
),
240+
),
241+
),
242+
),
243+
),
244+
);
245+
}
246+
}

0 commit comments

Comments
 (0)