Skip to content

Commit f381149

Browse files
Merge pull request #39 from VPNclient/refactoring-branch
Refactor: General code refactoring
2 parents 7a19f3d + c65e450 commit f381149

15 files changed

Lines changed: 670 additions & 942 deletions

.vscode/settings.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"IDX.aI.enableInlineCompletion": true,
3+
"IDX.aI.enableCodebaseIndexing": true
4+
}

lib/main.dart

Lines changed: 12 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,18 @@
11
import 'package:flutter/material.dart';
2-
import 'package:flutter_localizations/flutter_localizations.dart';
3-
import 'package:vpn_client/l10n/app_localizations.dart';
42
import 'package:provider/provider.dart';
53
import 'package:vpn_client/pages/apps/apps_page.dart';
64
import 'package:vpn_client/pages/main/main_page.dart';
75
import 'package:vpn_client/pages/servers/servers_page.dart';
6+
import 'package:vpn_client/pages/settings/settings_page.dart';
7+
import 'package:vpn_client/pages/speed/speed_page.dart';
8+
import 'package:vpn_client/providers/vpn_provider.dart';
89
import 'package:vpn_client/theme_provider.dart';
910

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

1314
void main() {
14-
runApp(
15-
ChangeNotifierProvider(create: (_) => ThemeProvider(), child: const App()),
16-
);
15+
runApp(MultiProvider(providers: [ChangeNotifierProvider(create: (_) => ThemeProvider()), ChangeNotifierProvider(create: (_) => VPNProvider())], child: const App()));
1716
}
1817

1918
class App extends StatelessWidget {
@@ -22,89 +21,56 @@ class App extends StatelessWidget {
2221
@override
2322
Widget build(BuildContext context) {
2423
final themeProvider = Provider.of<ThemeProvider>(context);
25-
final Locale? manualLocale = null;
24+
2625
return MaterialApp(
2726
debugShowCheckedModeBanner: false,
2827
title: 'VPN Client',
2928
theme: lightTheme,
3029
darkTheme: darkTheme,
31-
locale: manualLocale,
32-
localeResolutionCallback: (locale, supportedLocales) {
33-
if (locale == null) return const Locale('en');
34-
for (var supportedLocale in supportedLocales) {
35-
if (supportedLocale.languageCode == locale.languageCode &&
36-
(supportedLocale.countryCode == null ||
37-
supportedLocale.countryCode == locale.countryCode)) {
38-
return supportedLocale;
39-
}
40-
}
41-
if (locale.languageCode == 'zh') {
42-
return supportedLocales.contains(const Locale('zh'))
43-
? const Locale('zh')
44-
: const Locale('en');
45-
}
46-
return const Locale('en');
47-
},
4830
themeMode: themeProvider.themeMode,
4931
home: const MainScreen(),
50-
localizationsDelegates: const [
51-
AppLocalizations.delegate,
52-
GlobalMaterialLocalizations.delegate,
53-
GlobalWidgetsLocalizations.delegate,
54-
GlobalCupertinoLocalizations.delegate,
55-
],
56-
supportedLocales: const [
57-
Locale('en'),
58-
Locale('ru'),
59-
Locale('th'),
60-
Locale('zh'),
61-
],
6232
);
6333
}
6434
}
6535

6636
class MainScreen extends StatefulWidget {
6737
const MainScreen({super.key});
38+
6839
@override
6940
State<MainScreen> createState() => _MainScreenState();
7041
}
7142

7243
class _MainScreenState extends State<MainScreen> {
7344
int _currentIndex = 2;
7445
late List<Widget> _pages;
46+
7547
@override
7648
void initState() {
7749
super.initState();
7850
_pages = [
7951
const AppsPage(),
8052
ServersPage(onNavBarTap: _handleNavBarTap),
8153
const MainPage(),
82-
const PlaceholderPage(text: 'Speed Page'),
83-
const PlaceholderPage(text: 'Settings Page'),
54+
const SpeedPage(),
55+
const SettingsPage(),
8456
];
8557
}
58+
8659
void _handleNavBarTap(int index) {
8760
setState(() {
8861
_currentIndex = index;
8962
});
9063
}
64+
9165
@override
9266
Widget build(BuildContext context) {
9367
return Scaffold(
9468
body: _pages[_currentIndex],
9569
bottomNavigationBar: NavBar(
9670
initialIndex: _currentIndex,
9771
onItemTapped: _handleNavBarTap,
72+
selectedColor: Theme.of(context).colorScheme.primary,
9873
),
9974
);
10075
}
10176
}
102-
103-
class PlaceholderPage extends StatelessWidget {
104-
final String text;
105-
const PlaceholderPage({super.key, required this.text});
106-
@override
107-
Widget build(BuildContext context) {
108-
return Center(child: Text(text));
109-
}
110-
}

lib/models/nav_item.dart

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import 'package:flutter/material.dart';
2+
3+
class NavItem {
4+
final Widget inactiveIcon;
5+
final Widget activeIcon;
6+
7+
NavItem({required this.inactiveIcon, required this.activeIcon});
8+
}

lib/nav_bar.dart

Lines changed: 34 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,54 @@
11
import 'package:flutter/material.dart';
22
import 'design/images.dart';
3+
import 'package:vpn_client/models/nav_item.dart';
34

4-
class NavBar extends StatefulWidget {
5+
class NavBar extends StatelessWidget {
56
final int initialIndex;
67
final Function(int) onItemTapped;
8+
final Color selectedColor;
79

8-
const NavBar({super.key, this.initialIndex = 2, required this.onItemTapped});
9-
10-
@override
11-
State<NavBar> createState() => NavBarState();
12-
}
13-
14-
class NavBarState extends State<NavBar> {
15-
late int _selectedIndex;
16-
17-
final List<Widget> _inactiveIcons = [
18-
appIcon,
19-
serverIcon,
20-
homeIcon,
21-
speedIcon,
22-
settingsIcon,
23-
];
24-
25-
final List<Widget> _activeIcons = [
26-
activeAppIcon,
27-
activeServerIcon,
28-
activeHomeIcon,
29-
speedIcon,
30-
settingsIcon,
31-
];
32-
33-
@override
34-
void initState() {
35-
super.initState();
36-
_selectedIndex = widget.initialIndex;
37-
}
38-
39-
void _onItemTapped(int index) {
40-
setState(() {
41-
_selectedIndex = index;
42-
});
43-
widget.onItemTapped(index);
44-
}
10+
const NavBar({
11+
super.key,
12+
this.initialIndex = 2,
13+
required this.onItemTapped,
14+
required this.selectedColor,
15+
});
4516

4617
@override
4718
Widget build(BuildContext context) {
19+
final List<NavItem> navItems = [
20+
NavItem(inactiveIcon: appIcon, activeIcon: activeAppIcon),
21+
NavItem(inactiveIcon: serverIcon, activeIcon: activeServerIcon),
22+
NavItem(inactiveIcon: homeIcon, activeIcon: activeHomeIcon),
23+
NavItem(inactiveIcon: speedIcon, activeIcon: speedIcon),
24+
NavItem(inactiveIcon: settingsIcon, activeIcon: settingsIcon),
25+
];
26+
4827
return Container(
4928
alignment: Alignment.center,
5029
width: MediaQuery.of(context).size.width,
5130
height: 60,
5231
margin: const EdgeInsets.only(bottom: 30),
5332
padding: const EdgeInsets.symmetric(horizontal: 30),
54-
decoration: BoxDecoration(color: Theme.of(context).colorScheme.surface),
33+
decoration:
34+
BoxDecoration(color: Theme.of(context).colorScheme.surface),
5535
child: Row(
56-
children: List.generate(_inactiveIcons.length, (index) {
57-
bool isActive = _selectedIndex == index;
36+
mainAxisAlignment: MainAxisAlignment.spaceAround,
37+
children: List.generate(navItems.length, (index) {
38+
bool isActive = initialIndex == index;
5839
return GestureDetector(
59-
onTap: () => _onItemTapped(index),
60-
child: SizedBox(
61-
width: (MediaQuery.of(context).size.width - 60) / 5,
62-
child: AnimatedContainer(
63-
duration: const Duration(milliseconds: 200),
64-
curve: Curves.easeInOut,
65-
padding: const EdgeInsets.all(8),
66-
child: isActive ? _activeIcons[index] : _inactiveIcons[index],
67-
),
40+
onTap: () => onItemTapped(index),
41+
child: AnimatedContainer(
42+
duration: const Duration(milliseconds: 200),
43+
curve: Curves.easeInOut,
44+
padding: const EdgeInsets.all(8),
45+
child: isActive
46+
? ColorFiltered(
47+
colorFilter: ColorFilter.mode(
48+
selectedColor, BlendMode.srcIn),
49+
child: navItems[index].activeIcon,
50+
)
51+
: navItems[index].inactiveIcon,
6852
),
6953
);
7054
}),

lib/pages/main/location_widget.dart

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,67 @@ import 'package:flutter/material.dart';
22
import 'package:flutter_svg/svg.dart';
33
import 'package:vpn_client/l10n/app_localizations.dart';
44

5+
56
class LocationWidget extends StatelessWidget {
7+
final String title;
68
final Map<String, dynamic>? selectedServer;
9+
final VoidCallback? onTap;
710

8-
const LocationWidget({super.key, this.selectedServer});
11+
const LocationWidget({
12+
super.key,
13+
required this.title,
14+
this.selectedServer,
15+
this.onTap,
16+
});
917

1018
@override
1119
Widget build(BuildContext context) {
12-
final String locationName = selectedServer?['text'] ?? '...';
13-
final String iconPath =
14-
selectedServer?['icon'] ?? 'assets/images/flags/auto.svg';
20+
final String locationName = selectedServer?['text'] ?? '...'; final String iconPath = selectedServer?['icon'] ?? 'assets/images/flags/auto.svg';
1521

16-
return Container(
17-
margin: const EdgeInsets.all(30),
18-
padding: const EdgeInsets.only(left: 14),
22+
return GestureDetector( onTap: onTap,
23+
child: Container(
24+
padding: const EdgeInsets.only(left: 14),
25+
decoration: BoxDecoration(
26+
color: Theme.of(context).colorScheme.onSurface,
27+
borderRadius: BorderRadius.circular(12),
28+
),
29+
child: Row(
30+
children: [
31+
Column(
32+
mainAxisAlignment: MainAxisAlignment.spaceBetween,
33+
crossAxisAlignment: CrossAxisAlignment.start,
34+
children: [
35+
Text(
36+
title,
37+
style: TextStyle(
38+
fontSize: 14,
39+
fontWeight: FontWeight.w400,
40+
color: Theme.of(context).colorScheme.secondary,
41+
),
42+
),
43+
Text(
44+
locationName,
45+
style: TextStyle(
46+
fontSize: 17,
47+
fontWeight: FontWeight.w400,
48+
color: Theme.of(context).colorScheme.primary,
49+
),
50+
),
51+
],
52+
),
53+
const Spacer(),
54+
Column(
55+
children: [
56+
const SizedBox(height: 20),
57+
SvgPicture.asset(iconPath, width: 48, height: 48),
58+
],
59+
),
60+
],
61+
),
62+
),
63+
);
64+
}
65+
}
1966
decoration: BoxDecoration(
2067
color: Theme.of(context).colorScheme.onSurface,
2168
borderRadius: BorderRadius.circular(12),

0 commit comments

Comments
 (0)