Skip to content

Commit d2ac49d

Browse files
committed
maib vpn btn fix and add vpn state
1 parent 8ebe4fd commit d2ac49d

7 files changed

Lines changed: 169 additions & 125 deletions

File tree

lib/main.dart

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,20 @@ import 'package:vpn_client/pages/apps/apps_page.dart';
77
import 'package:vpn_client/pages/main/main_page.dart';
88
import 'package:vpn_client/pages/servers/servers_page.dart';
99
import 'package:vpn_client/theme_provider.dart';
10+
import 'package:vpn_client/vpn_state.dart';
1011

1112
import 'design/colors.dart';
1213
import 'nav_bar.dart';
1314

1415
void main() {
1516
runApp(
16-
ChangeNotifierProvider(create: (_) => ThemeProvider(), child: const App()),
17+
MultiProvider(
18+
providers: [
19+
ChangeNotifierProvider(create: (_) => ThemeProvider()),
20+
ChangeNotifierProvider(create: (_) => VpnState()),
21+
],
22+
child: const App(),
23+
),
1724
);
1825
}
1926

@@ -125,4 +132,4 @@ class PlaceholderPage extends StatelessWidget {
125132
Widget build(BuildContext context) {
126133
return Center(child: Text(text));
127134
}
128-
}
135+
}

lib/pages/main/main_btn.dart

Lines changed: 59 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import 'dart:async';
21
import 'dart:developer';
32
import 'package:flutter/material.dart';
3+
import 'package:provider/provider.dart';
44
import 'package:vpn_client/design/colors.dart';
55
import 'package:vpn_client/design/dimensions.dart';
66
import 'package:vpnclient_engine_flutter/vpnclient_engine_flutter.dart';
7+
import 'package:flutter_v2ray/flutter_v2ray.dart';
8+
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
9+
import 'package:vpn_client/vpn_state.dart';
10+
11+
final FlutterV2ray flutterV2ray = FlutterV2ray(
12+
onStatusChanged: (status) {
13+
// Handle status changes if needed
14+
},
15+
);
716

817
class MainBtn extends StatefulWidget {
918
const MainBtn({super.key});
@@ -13,142 +22,99 @@ class MainBtn extends StatefulWidget {
1322
}
1423

1524
class MainBtnState extends State<MainBtn> with SingleTickerProviderStateMixin {
16-
///static const platform = MethodChannel('vpnclient_engine2');
17-
///
18-
late CustomString statusText;
19-
late String connectionStatus;
20-
late String connectionStatusDisconnected;
21-
late String connectionStatusDisconnecting;
22-
late String connectionStatusConnected;
23-
late String connectionStatusConnecting;
24-
25-
@override
26-
void didChangeDependencies() {
27-
super.didChangeDependencies();
28-
final statusText = CustomString(context);
29-
connectionStatus = statusText.disconnected;
30-
connectionStatusDisconnected = statusText.disconnected;
31-
connectionStatusConnected = statusText.connected;
32-
connectionStatusDisconnecting = statusText.disconnecting;
33-
connectionStatusConnecting = statusText.connecting;
34-
}
35-
36-
String connectionTime = "00:00:00";
37-
Timer? _timer;
3825
late AnimationController _animationController;
3926
late Animation<double> _sizeAnimation;
4027

4128
@override
4229
void initState() {
4330
super.initState();
44-
4531
_animationController = AnimationController(
4632
vsync: this,
4733
duration: const Duration(seconds: 1),
4834
);
4935
_sizeAnimation = Tween<double>(begin: 0, end: 150).animate(
5036
CurvedAnimation(parent: _animationController, curve: Curves.ease),
5137
);
38+
39+
// Синхронизировать анимацию с текущим статусом подключения
40+
WidgetsBinding.instance.addPostFrameCallback((_) {
41+
final vpnState = Provider.of<VpnState>(context, listen: false);
42+
final localizations = AppLocalizations.of(context)!;
43+
if (vpnState.connectionStatus == localizations.connected) {
44+
_animationController.forward();
45+
}
46+
});
5247
}
5348

5449
@override
5550
void dispose() {
56-
_timer?.cancel();
5751
_animationController.dispose();
5852
super.dispose();
5953
}
6054

61-
void startTimer() {
62-
int seconds = 1;
63-
_timer = Timer.periodic(const Duration(seconds: 1), (timer) {
64-
setState(() {
65-
int hours = seconds ~/ 3600;
66-
int minutes = (seconds % 3600) ~/ 60;
67-
int remainingSeconds = seconds % 60;
68-
connectionTime =
69-
'${hours.toString().padLeft(2, '0')}:${minutes.toString().padLeft(2, '0')}:${remainingSeconds.toString().padLeft(2, '0')}';
70-
});
71-
seconds++;
72-
});
73-
}
74-
75-
void stopTimer() {
76-
_timer?.cancel();
77-
setState(() {
78-
connectionTime = "00:00:00";
79-
connectionStatus = connectionStatusDisconnected;
80-
});
81-
}
55+
Future<void> _handleConnection(BuildContext context) async {
56+
final vpnState = Provider.of<VpnState>(context, listen: false);
57+
final localizations = AppLocalizations.of(context)!;
8258

83-
Future<void> _handleConnection() async {
84-
if (connectionStatus != connectionStatusConnected &&
85-
connectionStatus != connectionStatusDisconnected) {
59+
if (vpnState.connectionStatus != localizations.connected &&
60+
vpnState.connectionStatus != localizations.disconnected) {
8661
return;
8762
}
8863

89-
setState(() {
90-
if (connectionStatus == connectionStatusConnected) {
91-
connectionStatus = connectionStatusDisconnecting;
92-
} else if (connectionStatus == connectionStatusDisconnected) {
93-
connectionStatus = connectionStatusConnecting;
94-
}
95-
});
96-
97-
if (connectionStatus == connectionStatusConnecting) {
64+
if (vpnState.connectionStatus == localizations.connected) {
65+
vpnState.setConnectionStatus(localizations.disconnecting);
66+
_animationController.repeat(reverse: true);
67+
await flutterV2ray.stopV2Ray();
68+
await VPNclientEngine.disconnect();
69+
vpnState.stopTimer();
70+
vpnState.setConnectionStatus(localizations.disconnected);
71+
await _animationController.reverse();
72+
_animationController.stop();
73+
} else if (vpnState.connectionStatus == localizations.disconnected) {
74+
vpnState.setConnectionStatus(localizations.connecting);
9875
_animationController.repeat(reverse: true);
9976

100-
VPNclientEngine.ClearSubscriptions();
101-
VPNclientEngine.addSubscription(
102-
subscriptionURL: "https://pastebin.com/raw/ZCYiJ98W",
103-
);
104-
await VPNclientEngine.updateSubscription(subscriptionIndex: 0);
105-
VPNclientEngine.pingServer(subscriptionIndex: 0, index: 1);
106-
VPNclientEngine.onPingResult.listen((result) {
107-
log(
108-
"Ping result: ${result.latencyInMs} ms",
109-
name: 'PingLogger',
110-
); // <- Use dev.log instead of print.(It build to log meta data)
111-
});
112-
113-
///final result = await platform.invokeMethod('startVPN');
77+
String link = "vless://c61daf3e-83ff-424f-a4ff-5bfcb46f0b30@45.77.190.146:8443?encryption=none&flow=&security=reality&sni=www.gstatic.com&fp=chrome&pbk=rLCmXWNVoRBiknloDUsbNS5ONjiI70v-BWQpWq0HCQ0&sid=108108108108#%F0%9F%87%BA%F0%9F%87%B8+%F0%9F%99%8F+USA+%231";
78+
V2RayURL parser = FlutterV2ray.parseFromURL(link);
79+
80+
if (await flutterV2ray.requestPermission()) {
81+
await flutterV2ray.startV2Ray(
82+
remark: parser.remark,
83+
config: parser.getFullConfiguration(),
84+
blockedApps: null,
85+
bypassSubnets: null,
86+
proxyOnly: false,
87+
);
88+
}
11489

11590
await VPNclientEngine.connect(subscriptionIndex: 0, serverIndex: 1);
116-
startTimer();
117-
setState(() {
118-
connectionStatus = connectionStatusConnected;
119-
});
91+
vpnState.startTimer();
92+
vpnState.setConnectionStatus(localizations.connected);
12093
await _animationController.forward();
12194
_animationController.stop();
122-
} else if (connectionStatus == connectionStatusDisconnecting) {
123-
_animationController.repeat(reverse: true);
124-
stopTimer();
125-
await VPNclientEngine.disconnect();
126-
setState(() {
127-
connectionStatus = connectionStatusDisconnected;
128-
});
129-
await _animationController.reverse();
130-
_animationController.stop();
13195
}
13296
}
13397

13498
@override
13599
Widget build(BuildContext context) {
100+
final vpnState = Provider.of<VpnState>(context);
101+
final localizations = AppLocalizations.of(context)!;
102+
136103
return Column(
137104
children: [
138105
Text(
139-
connectionTime,
106+
vpnState.connectionTime,
140107
style: TextStyle(
141108
fontSize: 40,
142109
fontWeight: FontWeight.w600,
143-
color:
144-
connectionStatus == connectionStatusConnected
145-
? Theme.of(context).colorScheme.primary
146-
: Theme.of(context).colorScheme.secondary,
110+
color: vpnState.connectionStatus == localizations.connected
111+
? Theme.of(context).colorScheme.primary
112+
: Theme.of(context).colorScheme.secondary,
147113
),
148114
),
149115
const SizedBox(height: 70),
150116
GestureDetector(
151-
onTap: _handleConnection,
117+
onTap: () => _handleConnection(context),
152118
child: Stack(
153119
alignment: Alignment.center,
154120
children: [
@@ -188,7 +154,7 @@ class MainBtnState extends State<MainBtn> with SingleTickerProviderStateMixin {
188154
),
189155
const SizedBox(height: 20),
190156
Text(
191-
connectionStatus,
157+
vpnState.connectionStatus,
192158
style: TextStyle(
193159
fontSize: 16,
194160
fontWeight: FontWeight.w500,
@@ -198,8 +164,4 @@ class MainBtnState extends State<MainBtn> with SingleTickerProviderStateMixin {
198164
],
199165
);
200166
}
201-
}
202-
203-
void main() {
204-
runApp(MaterialApp(home: Scaffold(body: Center(child: MainBtn()))));
205-
}
167+
}

lib/pages/main/main_page.dart

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import 'package:flutter/material.dart';
2+
import 'package:provider/provider.dart';
23
import 'package:shared_preferences/shared_preferences.dart';
34
import 'dart:convert';
45
import 'package:vpn_client/pages/main/main_btn.dart';
56
import 'package:vpn_client/pages/main/location_widget.dart';
67
import 'package:vpn_client/pages/main/stat_bar.dart';
8+
import 'package:vpn_client/vpn_state.dart';
79
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
810

911
class MainPage extends StatefulWidget {
@@ -15,27 +17,45 @@ class MainPage extends StatefulWidget {
1517

1618
class MainPageState extends State<MainPage> {
1719
Map<String, dynamic>? _selectedServer;
20+
bool _isInitialized = false;
1821

1922
@override
2023
void initState() {
2124
super.initState();
2225
_loadSelectedServer();
2326
}
2427

28+
@override
29+
void didChangeDependencies() {
30+
super.didChangeDependencies();
31+
if (!_isInitialized) {
32+
// Schedule VpnState connection status update after build
33+
WidgetsBinding.instance.addPostFrameCallback((_) {
34+
final vpnState = Provider.of<VpnState>(context, listen: false);
35+
final localizations = AppLocalizations.of(context)!;
36+
// Инициализировать статус только если он пустой или равен начальному значению
37+
if (vpnState.connectionStatus.isEmpty || vpnState.connectionStatus == 'Disconnected') {
38+
vpnState.setConnectionStatus(localizations.disconnected);
39+
}
40+
});
41+
_isInitialized = true;
42+
}
43+
}
44+
2545
Future<void> _loadSelectedServer() async {
2646
final prefs = await SharedPreferences.getInstance();
2747
final String? savedServers = prefs.getString('selected_servers');
2848
if (savedServers != null) {
2949
final List<dynamic> serversList = jsonDecode(savedServers);
3050
final activeServer = serversList.firstWhere(
31-
(server) => server['isActive'] == true,
51+
(server) => server['isActive'] == true,
3252
orElse: () => null,
3353
);
3454
setState(() {
3555
_selectedServer =
36-
activeServer != null
37-
? Map<String, dynamic>.from(activeServer)
38-
: null;
56+
activeServer != null
57+
? Map<String, dynamic>.from(activeServer)
58+
: null;
3959
});
4060
}
4161
}
@@ -54,20 +74,15 @@ class MainPageState extends State<MainPage> {
5474
elevation: 0,
5575
),
5676
body: SafeArea(
57-
// I change to SafeArea to prevent screen over flow
58-
child: SingleChildScrollView(
5977
child: Column(
60-
mainAxisAlignment: MainAxisAlignment.start,
78+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
6179
children: [
6280
const StatBar(),
63-
const SizedBox(height: 20),
6481
const MainBtn(),
65-
const SizedBox(height: 20),
6682
LocationWidget(selectedServer: _selectedServer),
6783
],
6884
),
69-
),
7085
),
7186
);
7287
}
73-
}
88+
}

lib/pages/main/stat_bar.dart

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,16 @@ class StatBar extends StatefulWidget {
1313
class StatBarState extends State<StatBar> {
1414
@override
1515
Widget build(BuildContext context) {
16-
return Row(
17-
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
18-
children: [
19-
_buildStatItem(CustomIcons.download, '0 Mb/s', context),
20-
_buildStatItem(CustomIcons.upload, '0 Mb/s', context),
21-
_buildStatItem(CustomIcons.ping, '0 ms', context),
22-
],
16+
return Container(
17+
margin: EdgeInsets.only(top: 37),
18+
child: Row(
19+
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
20+
children: [
21+
_buildStatItem(CustomIcons.download, '0 Mb/s', context),
22+
_buildStatItem(CustomIcons.upload, '0 Mb/s', context),
23+
_buildStatItem(CustomIcons.ping, '0 ms', context),
24+
],
25+
),
2326
);
2427
}
2528

0 commit comments

Comments
 (0)