Flutter虚拟专用网络插件blackhole_vpn的使用

发布于 1周前 作者 h691938207 来自 Flutter

Flutter虚拟专用网络插件blackhole_vpn的使用

Blackhole Vpn设置了一个虚拟专用网络(VPN)客户端,用于阻止选定应用的连接。

Android有VpnManager API,可以为指定的应用创建一个VPN连接。通过此插件,选定应用的网络将由一个不进行任何实际连接的虚拟专用网络管理。

无需root权限。

支持的平台

  • Android

欢迎提交其他平台的支持请求。

使用场景

  • 防火墙应用
  • 节省数据的应用

如何使用

此插件使用BIND_VPN_SERVICE权限来设置VPN服务。

如果你希望在Google Play商店发布你的应用,你需要在商店列表中记录使用VPN服务的情况。更多信息可以在这里查看。

不需要编辑AndroidManifest.xml

确保在android/app/build.gradle中的最低SDK版本为21或更高:

defaultConfig {
    minSdkVersion 21
}

导入包

所有方法在导入包并获取插件实例后可用:

import 'package:blackhole_vpn/blackhole_vpn.dart';
final blackholeVpn = BlackholeVpnPlatform.instance;

启动VPN以指定应用

你必须知道你想使用的应用的包名。例如应用使用了android_package_manager插件。

// 请求权限然后启动黑洞VPN以指定应用
await blackholeVpn.runVpnService(["com.google.youtube", "com.android.chrome"]);

指定应用将无法连接到互联网和本地网络。返回一个bool值表示用户是否接受权限。

检查黑洞VPN是否激活

await blackholeVpn.isActive(); // 返回一个bool值

停止黑洞VPN

await blackholeVpn.stopVpn(); // 用户也可以通过VPN通知或系统设置停止它

跟踪VPN状态变化

Android允许用户通过VPN通知或系统设置启动或停止VPN服务。你可以通过特定的方法跟踪VPN状态的变化。

  • 使用VpnStateObserver小部件
VpnStateObserver(
    builder: (isActive) => switch (isActive) {
        true => Text("Blackhole vpn active"),
        false => Text("Blackhole vpn not active"),
        null => Text("Loading")
    })
  • 监听vpnStatusStream
blackholeVpn.vpnStatusStream.listen((isActive) {
    if (isActive) {
        print("Blackhole vpn active");
    } else {
        print("Blackhole vpn not active");
    }
});

// `vpnStatusStream`是一个广播流,它可以被多次监听。如果VPN状态未改变,它可能不会发出任何值。在监听之前检查状态与`isActive()`推荐。

测试

import 'package:flutter_test/flutter_test.dart';
import 'package:blackhole_vpn/blackhole_vpn.dart';
import 'package:blackhole_vpn/blackhole_vpn_platform_interface.dart';
import 'package:plugin_platform_interface/plugin_platform_interface.dart';

class TestBlackholeVpnPlatform
    with MockPlatformInterfaceMixin
    implements BlackholeVpnPlatform {
  var _isActive = false;
  [@override](/user/override)
  Future<bool> runVpnService(_) async {
    _isActive = true;
    return true;
  }

  [@override](/user/override)
  Future<bool> isActive() async {
    return _isActive;
  }

  [@override](/user/override)
  Future<void> stopVpnService() async {
    _isActive = false;
  }

  [@override](/user/override)
  Stream<bool> getVpnStatusStream() => throw UnimplementedError();
}

void main() {
  BlackholeVpnPlatform.instance = TestBlackholeVpnPlatform();
  test('test runVpnService', () async {
    final blackholeVpn = BlackholeVpnPlatform.instance;
    expect(await blackholeVpn.runVpnService([]), isTrue);
    expect(await blackholeVpn.isActive(), isTrue);
  });
}

示例代码

import 'dart:developer' show log;

import 'package:flutter/material.dart';
import 'package:blackhole_vpn/blackhole_vpn.dart';
import 'package:android_package_manager/android_package_manager.dart';

final blackholeVpn = BlackholeVpnPlatform.instance;
void main() async {
  WidgetsFlutterBinding.ensureInitialized(); // 添加这行以便在运行应用前使用flutter插件
  final isVpnActive = await blackholeVpn.isActive(); // 获取VPN是否已激活

  // 在示例应用中,我们使用了一个来自这里的flutter插件: https://pub.dev/packages/android_package_manager
  final installedApps = await AndroidPackageManager().getInstalledApplications();

  // 使用Set而不是List,因为相同的app名称可能会重复。
  final installedAppsNames = {
    for (final app in installedApps!) if (app.name != null) app.name!
  };

  runApp(MyApp(
    isVpnActive: isVpnActive,
    installedAppNames: installedAppsNames.toList(),
  ));
}

class MyApp extends StatefulWidget {
  const MyApp({
    super.key,
    required this.isVpnActive,
    required this.installedAppNames,
  });
  final bool isVpnActive;
  final List<String> installedAppNames;
  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  late var _isVpnOn = widget.isVpnActive;

  // 选择要传递给blackhole vpn的应用
  final _selectedApps = <String>[];

  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Blackhole Vpn Example'),
        ),
        body: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            VpnStateObserver(
                builder: (isActive) => switch (isActive) {
                      true => const Text("Blackhole vpn active"),
                      false => const Text(
                          "Select the apps which you want to prevent to connect internet"),
                      null => const Text("Loading")
                    }),
            Expanded(
              child: ListView.builder(
                itemCount: widget.installedAppNames.length,
                itemBuilder: (context, index) {
                  final appName = widget.installedAppNames[index];
                  return SwitchListTile(
                    title: Text(appName),
                    value: _selectedApps.contains(appName),
                    onChanged: (bool value) {
                      if (value) {
                        setState(() {
                          _selectedApps.add(appName);
                        });
                      } else {
                        setState(() {
                          _selectedApps.remove(appName);
                        });
                      }
                    },
                  );
                },
              ),
            ),
            ElevatedButton(
                onPressed: _isVpnOn
                    ? () async {
                        await blackholeVpn
                            .stopVpnService(); // 停止Blackhole Vpn
                        setState(() {
                          _isVpnOn = false;
                        });
                      }
                    : () async {
                        // 启动vpn服务
                        final isActivated =
                            await blackholeVpn.runVpnService(_selectedApps);
                        if (isActivated) {
                          // Vpn权限已授予
                          setState(() {
                            _isVpnOn = true;
                          });
                        } else {
                          // Vpn权限未授予
                          log("Vpn permission not granted");
                        }
                      },
                child: Text(
                    _isVpnOn ? "Stop Blackhole Vpn" : "Start Blackhole Vpn"))
          ],
        ),
      ),
    );
  }
}

更多关于Flutter虚拟专用网络插件blackhole_vpn的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter虚拟专用网络插件blackhole_vpn的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在使用Flutter进行开发时,集成和使用虚拟专用网络(VPN)插件可能会涉及到敏感操作和系统权限,因此在实际项目中需要格外小心,确保遵守相关法律法规和平台政策。此外,由于VPN功能通常涉及到底层系统调用,直接使用第三方插件可能存在一定的风险和不确定性。

不过,为了回答你的问题,我可以提供一个基本的示例,展示如何在Flutter项目中集成和使用一个假设的VPN插件(请注意,由于“blackhole_vpn”并非一个真实存在的Flutter官方或广泛认可的插件名称,我将以一个假设的VPN插件vpn_plugin为例进行说明)。在实际操作中,你需要替换为真实可用的VPN插件。

步骤 1: 添加依赖

首先,在你的pubspec.yaml文件中添加对假设的VPN插件的依赖:

dependencies:
  flutter:
    sdk: flutter
  vpn_plugin: ^0.0.1  # 假设的版本号,实际使用时请替换为真实版本号

然后运行flutter pub get来安装依赖。

步骤 2: 请求权限

VPN功能通常需要特定的系统权限,例如ACCESS_NETWORK_STATECHANGE_NETWORK_STATE等。在Android平台上,你需要在AndroidManifest.xml中声明这些权限:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.yourapp">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
    <!-- 其他必要的权限 -->

    <application
        ... >
        ...
    </application>
</manifest>

在iOS平台上,VPN功能通常受到更严格的限制,可能需要配置特定的网络扩展或获得App Store的特别批准。

步骤 3: 使用插件

在你的Flutter代码中,你可以这样使用假设的VPN插件:

import 'package:flutter/material.dart';
import 'package:vpn_plugin/vpn_plugin.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  String vpnStatus = "Unknown";

  @override
  void initState() {
    super.initState();
    // 检查VPN状态
    _checkVpnStatus();
  }

  Future<void> _checkVpnStatus() async {
    try {
      bool isVpnEnabled = await VpnPlugin.isVpnEnabled();
      setState(() {
        vpnStatus = isVpnEnabled ? "Enabled" : "Disabled";
      });
    } catch (e) {
      print("Error checking VPN status: $e");
    }
  }

  Future<void> _toggleVpn() async {
    try {
      bool result = await VpnPlugin.toggleVpn();
      setState(() {
        vpnStatus = result ? "Enabled" : "Disabled";
      });
    } catch (e) {
      print("Error toggling VPN: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('VPN Example'),
        ),
        body: Center(
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              Text(
                'VPN Status: $vpnStatus',
                style: TextStyle(fontSize: 24),
              ),
              SizedBox(height: 20),
              ElevatedButton(
                onPressed: _toggleVpn,
                child: Text('Toggle VPN'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}

在这个示例中,我们创建了一个简单的Flutter应用,它使用假设的VpnPlugin插件来检查VPN状态并切换VPN的启用/禁用状态。请注意,VpnPlugin.isVpnEnabled()VpnPlugin.toggleVpn()是假设的方法名,实际使用时需要替换为插件提供的真实方法。

注意事项

  1. 插件可用性:确保你使用的VPN插件是可靠且经过充分测试的。
  2. 平台差异:Android和iOS在VPN功能的实现和权限管理上可能存在差异,需要分别处理。
  3. 法律法规:在使用VPN功能时,务必遵守当地法律法规和平台政策。
  4. 用户隐私:VPN功能可能涉及用户隐私和数据安全,务必妥善处理。

由于“blackhole_vpn”并非一个真实存在的Flutter插件,上述示例中的VpnPlugin应替换为实际可用的VPN插件,并参考该插件的官方文档进行集成和使用。

回到顶部