vpn_notice 0.2.0 copy "vpn_notice: ^0.2.0" to clipboard
vpn_notice: ^0.2.0 copied to clipboard

Detect active VPN connections and show a customizable notice — banner, snackbar, dialog, or bottom sheet — with zero third-party Dart dependencies.

example/lib/main.dart

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

void main() {
  runApp(const VpnNoticeExampleApp());
}

class VpnNoticeExampleApp extends StatelessWidget {
  const VpnNoticeExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'vpn_notice example',
      home: const ExampleHomePage(),
    );
  }
}

class ExampleHomePage extends StatefulWidget {
  const ExampleHomePage({super.key});

  @override
  State<ExampleHomePage> createState() => _ExampleHomePageState();
}

class _ExampleHomePageState extends State<ExampleHomePage> {
  final _controller = VpnNoticeController();
  OverlayEntry? _customNoticeEntry;

  @override
  void initState() {
    super.initState();
    _controller.start();
  }

  @override
  void dispose() {
    _customNoticeEntry?.remove();
    _controller.dispose();
    super.dispose();
  }

  static const _style = VpnNoticeStyle(
    title: 'VPN detected',
    message: 'Some content may load differently while a VPN is active.',
    actionLabel: 'Got it',
  );

  void _showCustomNotice() {
    _customNoticeEntry?.remove();

    late final OverlayEntry entry;
    entry = OverlayEntry(
      builder: (context) => _CustomVpnNotice(
        onDismiss: () {
          if (entry.mounted) entry.remove();
          if (identical(_customNoticeEntry, entry)) {
            _customNoticeEntry = null;
          }
        },
      ),
    );

    _customNoticeEntry = entry;
    Overlay.of(context).insert(entry);
  }

  @override
  Widget build(BuildContext context) {
    return VpnNoticeGate(
      controller: _controller,
      builder: (_, dismiss) => _CustomVpnNotice(onDismiss: dismiss),
      child: Scaffold(
        appBar: AppBar(title: const Text('vpn_notice example')),
        body: Center(
          child: Padding(
            padding: const EdgeInsets.all(24),
            child: Column(
              mainAxisSize: MainAxisSize.min,
              children: [
                ValueListenableBuilder<bool>(
                  valueListenable: _controller,
                  builder: (context, isVpnActive, _) => Text(
                    isVpnActive
                        ? 'Live status: VPN is ACTIVE'
                        : 'Live status: VPN is not active',
                    style: Theme.of(context).textTheme.titleMedium,
                  ),
                ),
                const SizedBox(height: 8),
                Text(
                  'Use the buttons below to preview each presentation '
                  'regardless of your real VPN status.',
                  textAlign: TextAlign.center,
                  style: Theme.of(context).textTheme.bodySmall,
                ),
                const SizedBox(height: 24),
                Wrap(
                  spacing: 12,
                  runSpacing: 12,
                  alignment: WrapAlignment.center,
                  children: [
                    FilledButton(
                      onPressed: () =>
                          VpnNoticeBanner.show(context, style: _style),
                      child: const Text('Show banner'),
                    ),
                    FilledButton(
                      onPressed: () =>
                          VpnNoticeSnackBar.show(context, style: _style),
                      child: const Text('Show snack bar'),
                    ),
                    FilledButton(
                      onPressed: () =>
                          VpnNoticeDialog.show(context, style: _style),
                      child: const Text('Show dialog'),
                    ),
                    FilledButton(
                      onPressed: () =>
                          VpnNoticeBottomSheet.show(context, style: _style),
                      child: const Text('Show bottom sheet'),
                    ),
                    FilledButton(
                      onPressed: _showCustomNotice,
                      child: const Text('Show custom notice'),
                    ),
                  ],
                ),
              ],
            ),
          ),
        ),
      ),
    );
  }
}

class _CustomVpnNotice extends StatelessWidget {
  const _CustomVpnNotice({required this.onDismiss});

  static const _horizontalInset = 16.0;
  static const _topInset = 12.0;
  static const _contentPadding = EdgeInsets.all(16);
  static const _borderRadius = BorderRadius.all(Radius.circular(12));

  final VoidCallback onDismiss;

  @override
  Widget build(BuildContext context) {
    return Positioned(
      top: MediaQuery.paddingOf(context).top + _topInset,
      left: _horizontalInset,
      right: _horizontalInset,
      child: Material(
        color: Colors.black,
        borderRadius: _borderRadius,
        child: Padding(
          padding: _contentPadding,
          child: Row(
            children: [
              const Icon(Icons.vpn_lock_outlined, color: Colors.white),
              const SizedBox(width: 12),
              const Expanded(
                child: Column(
                  crossAxisAlignment: CrossAxisAlignment.start,
                  mainAxisSize: MainAxisSize.min,
                  children: [
                    Text(
                      'Custom VPN notice',
                      style: TextStyle(
                        color: Colors.white,
                        fontWeight: FontWeight.w600,
                      ),
                    ),
                    Text(
                      'This shared layout is identical on every platform.',
                      style: TextStyle(color: Colors.white),
                    ),
                  ],
                ),
              ),
              TextButton(
                onPressed: onDismiss,
                style: TextButton.styleFrom(foregroundColor: Colors.white),
                child: const Text('Close'),
              ),
            ],
          ),
        ),
      ),
    );
  }
}
2
likes
160
points
136
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Detect active VPN connections and show a customizable notice — banner, snackbar, dialog, or bottom sheet — with zero third-party Dart dependencies.

Repository (GitHub)
View/report issues

Topics

#vpn #networking #banner #notification

License

MIT (license)

Dependencies

flutter, plugin_platform_interface

More

Packages that depend on vpn_notice

Packages that implement vpn_notice