consent_mode_flutter 1.0.2 copy "consent_mode_flutter: ^1.0.2" to clipboard
consent_mode_flutter: ^1.0.2 copied to clipboard

A comprehensive Flutter library for managing user consent according to GDPR, CCPA, and other privacy regulations.

Consent Mode Flutter #

A comprehensive Flutter library for managing user consent according to GDPR, CCPA, and other privacy regulations.

Features #

  • πŸͺ Complete consent management (Essential, Analytics, Marketing, Functional)
  • 🎨 Customizable UI with banner and bottom sheet
  • 🎨 Fully custom UI with complete control
  • πŸ”„ Listeners to react to consent changes
  • πŸ’Ύ Automatic preference persistence
  • 🎯 Simple integration with analytics and marketing tools

Installation #

Add the dependency to your pubspec.yaml:

dependencies:
  consent_mode_flutter: ^1.0.2

Then run:

flutter pub get

Initial Setup #

1. App Initialization #

void main() async {
  WidgetsFlutterBinding.ensureInitialized();

  // Initialize consent mode
  await ConsentModeFlutter.initialize();

  runApp(const MyApp());
}

2. Basic Configuration #

class MyApp extends StatelessWidget {
  const MyApp({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Consent Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: const ConsentWrapper(),
    );
  }
}

Available Implementations #

The simplest way to integrate the consent banner:

class ConsentWrapper extends StatelessWidget {
  const ConsentWrapper({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ConsentModeFlutterWrapper(
      bannerConfig: ConsentModeFlutterConfigHelper.bannerConfig(context),
      bannerStyle: ConsentModeFlutterUiStyleHelper.bannerStyle(context),
      child: const MyHomePage(),
    );
  }
}

Option 2: Custom Bottom Sheet #

For greater control over the user experience:

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  final ConsentModeFlutterManager _manager = ConsentModeFlutterManager();
  bool _showBanner = false;

  @override
  void initState() {
    super.initState();
    _checkBannerVisibility();
  }

  void _checkBannerVisibility() {
    setState(() {
      _showBanner = _manager.shouldShowBanner;
    });
  }

  void _showConsentBottomSheet() {
    ConsentModeFlutterUtils.showCustomBottomSheet(
      context,
      ConsentModeFlutterBanner(
        config: ConsentModeFlutterConfigHelper.bannerConfig(context),
        style: ConsentModeFlutterUiStyleHelper.bannerStyle(context),
        onConsentChanged: (Map<ConsentModeFlutterType, bool> preferences) {
          // Handle consent changes
          _handleConsentChanges(preferences);
        },
        onDismissed: () {
          setState(() {
            _showBanner = false;
          });
        },
      ),
    );
  }

  void _handleConsentChanges(Map<ConsentModeFlutterType, bool> preferences) {
    // Initialize services based on consent
    if (preferences[ConsentModeFlutterType.analytics] == true) {
      // Initialize Google Analytics, Firebase Analytics, etc.
    }

    if (preferences[ConsentModeFlutterType.marketing] == true) {
      // Initialize Facebook Pixel, Google Ads, etc.
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Consent Mode Demo'),
        actions: [
          IconButton(
            icon: const Icon(Icons.settings),
            onPressed: _showConsentBottomSheet,
          ),
        ],
      ),
      body: Column(
        children: [
          if (_showBanner)
            Container(
              padding: const EdgeInsets.all(16),
              color: Theme.of(context).primaryColor.withOpacity(0.1),
              child: Row(
                children: [
                  const Expanded(
                    child: Text('This site uses cookies to improve your experience.'),
                  ),
                  TextButton(
                    onPressed: _showConsentBottomSheet,
                    child: const Text('Manage'),
                  ),
                ],
              ),
            ),
          const Expanded(
            child: Center(
              child: Text('Main app content'),
            ),
          ),
        ],
      ),
    );
  }
}

Option 3: Complete Listener #

For total control over consent changes:

class ConsentListenerPage extends StatefulWidget {
  const ConsentListenerPage({Key? key}) : super(key: key);

  @override
  State<ConsentListenerPage> createState() => _ConsentListenerPageState();
}

class _ConsentListenerPageState extends State<ConsentListenerPage> {
  Map<ConsentModeFlutterType, bool> _currentPreferences = {};

  @override
  void initState() {
    super.initState();
    _loadPreferences();

    // Listen to consent changes
    ConsentModeFlutter.addListener(_onConsentChanged);
  }

  @override
  void dispose() {
    ConsentModeFlutter.removeListener(_onConsentChanged);
    super.dispose();
  }

  void _loadPreferences() {
    setState(() {
      _currentPreferences = ConsentModeFlutter.preferences;
    });
  }

  void _onConsentChanged(Map<ConsentModeFlutterType, bool> preferences) {
    setState(() {
      _currentPreferences = preferences;
    });

    // Initialize services based on consent
    _initializeServices(preferences);
  }

  void _initializeServices(Map<ConsentModeFlutterType, bool> preferences) {
    if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.essential)) {
      debugPrint('Essential consent granted - initialize essential services');
      // Initialize essential services
    }

    if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.analytics)) {
      debugPrint('Analytics consent granted - initialize analytics');
      // Initialize Google Analytics, Firebase Analytics, etc.
    }

    if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.marketing)) {
      debugPrint('Marketing consent granted - initialize marketing tools');
      // Initialize Facebook Pixel, Google Ads, etc.
    }

    if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.functional)) {
      debugPrint('Functional consent granted - initialize functional services');
      // Initialize functional services
    }
  }

  Future<void> _resetConsent() async {
    await ConsentModeFlutter.reset();
    setState(() {
      _currentPreferences = ConsentModeFlutter.preferences;
    });

    if (mounted) {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text(
            'Consent preferences reset. Restart app to see banner again.',
          ),
        ),
      );
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Consent Listener'),
        actions: [
          IconButton(
            icon: const Icon(Icons.refresh),
            onPressed: _resetConsent,
          ),
        ],
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            const Text(
              'Current Consent Status:',
              style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 16),
            ...ConsentModeFlutterType.values.map((type) {
              final granted = _currentPreferences[type] ?? false;
              return ListTile(
                leading: Icon(
                  granted ? Icons.check_circle : Icons.cancel,
                  color: granted ? Colors.green : Colors.red,
                ),
                title: Text(type.name),
                subtitle: Text(granted ? 'Granted' : 'Denied'),
              );
            }).toList(),
          ],
        ),
      ),
    );
  }
}

Option 4: Fully Custom UI (NEW) #

🎨 For complete control over the consent UI design

When you need to create a completely custom consent interface that doesn't follow the standard design patterns, you can use the custom UI approach:

class ConsentCustomUIWrapper extends StatelessWidget {
  const ConsentCustomUIWrapper({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return ConsentModeFlutterWrapper(
      bannerCustomConfigUIStyle: ConsentModeFlutterCustomConfigUiStyleHelper
          .bannerCustomConfigUIStyle(context),
      child: const MyHomePage(),
    );
  }
}

Custom UI Helper Class

Create a helper class for your completely custom consent UI:

class ConsentModeFlutterCustomConfigUiStyleHelper {
  static ConsentModeFlutterBannerCustomConfigUIStyle bannerCustomConfigUIStyle(
    BuildContext context,
  ) {
    double width = MediaQuery.of(context).size.width;
    final ConsentModeFlutterManager manager = ConsentModeFlutterManager();

    return ConsentModeFlutterBannerCustomConfigUIStyle(
      child: Container(
        padding: const EdgeInsets.all(20),
        decoration: BoxDecoration(
          color: Colors.white,
          borderRadius: BorderRadius.circular(16),
          boxShadow: [
            BoxShadow(
              color: Colors.black.withValues(alpha: 0.1),
              blurRadius: 10,
              offset: const Offset(0, 5),
            ),
          ],
        ),
        width: width,
        child: Column(
          mainAxisSize: MainAxisSize.min,
          children: [
            // Your completely custom UI here
            const Icon(Icons.cookie, size: 48, color: Colors.brown),
            const SizedBox(height: 16),
            const Text(
              'Cookie Preferences',
              style: TextStyle(fontSize: 24, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 12),
            const Text(
              'We use cookies to enhance your experience. Please choose your preferences.',
              textAlign: TextAlign.center,
              style: TextStyle(fontSize: 16),
            ),
            const SizedBox(height: 20),
            // Custom buttons with your own logic
            Row(
              children: [
                Expanded(
                  child: ElevatedButton(
                    onPressed: () {
                      // Handle custom consent logic
                      _handleAcceptAll(context, manager);
                    },
                    child: const Text('Accept All'),
                  ),
                ),
                const SizedBox(width: 12),
                Expanded(
                  child: ElevatedButton(
                    onPressed: () {
                      // Handle custom consent logic
                      _handleRejectAll(context, manager);
                    },
                    child: const Text('Reject All'),
                  ),
                ),
              ],
            ),
          ],
        ),
      ),
    );
  }

  static Future<void> _handleAcceptAll(
    BuildContext context,
    ConsentModeFlutterManager manager,
  ) async {
    try {
      await manager.acceptAll();
    } catch (e) {
      if (context.mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('Error saving preferences: $e')));
      }
    }
  }

  static Future<void> _handleRejectAll(
    BuildContext context,
    ConsentModeFlutterManager manager,
  ) async {
    try {
      await manager.rejectAll();
    } catch (e) {
      if (context.mounted) {
        ScaffoldMessenger.of(
          context,
        ).showSnackBar(SnackBar(content: Text('Error saving preferences: $e')));
      }
    }
  }
}

Using Custom UI in Bottom Sheet

You can also use the custom UI approach with the bottom sheet method:

void _showCustomConsentBottomSheet() {
  ConsentModeFlutterUtils.showCustomBottomSheet(
    context,
    ConsentModeFlutterCustomConfigUiStyleHelper
        .bannerCustomConfigUIStyle(context)
        .child,
  );
}

Advantages of Custom UI Approach

  • βœ… Complete Design Control: Create any UI design you want
  • βœ… Brand Consistency: Match exactly your app's design system
  • βœ… Custom Interactions: Implement custom animations and interactions
  • βœ… Flexible Layout: Not limited by predefined banner structures
  • βœ… Advanced UX: Create multi-step flows, tabs, or any complex UI pattern

When to Use Custom UI

  • Brand Requirements: When your brand guidelines require a specific design
  • Complex Flows: When you need multi-step consent processes
  • Advanced UX: When you want to implement custom animations or interactions
  • Unique Layouts: When standard banner layouts don't fit your needs
  • Integration Requirements: When you need to integrate consent UI with other app features

Advanced Configuration #

It's strongly recommended to create helper classes to centralize configuration. This approach offers numerous advantages:

  • βœ… Reusability: Once created, you can call them anywhere in the app
  • βœ… Maintenance: Changes in one place only
  • βœ… Consistency: Uniform style throughout the app
  • βœ… Ease of use: Cleaner and more readable code
// Create these helper classes in your project
class ConsentModeFlutterConfigHelper {
  static ConsentModeFlutterBannerStandardConfig bannerConfig(
    BuildContext context,
  ) {
    return ConsentModeFlutterBannerStandardConfig(
      privacyPolicyUrl: 'https://example.com/privacy',
      title: 'Privacy & Cookies',
      description: 'We use cookies and similar technologies to improve your experience...',
      acceptAllText: 'Accept All',
      rejectAllText: 'Reject All',
      essentialTitle: 'Essential Cookies',
      analyticsTitle: 'Analytics Cookies',
      marketingTitle: 'Marketing Cookies',
      functionalTitle: 'Functional Cookies',
      showRejectAll: true,
      showMoreInformation: true,
    );
  }
}

class ConsentModeFlutterUiStyleHelper {
  static ConsentModeFlutterStandardUIStyle bannerStyle(BuildContext context) {
    return ConsentModeFlutterStandardUIStyle(
      image: Image.asset(
        'assets/images/brand_flutter.webp',
        width: 70,
        height: 70,
      ),
      title: const TextStyle(
        fontSize: 25,
        fontWeight: FontWeight.bold,
        color: Colors.black,
      ),
      subTitle: const TextStyle(
        fontSize: 16,
        fontWeight: FontWeight.bold,
        color: Colors.black,
      ),
      description: const TextStyle(
        fontSize: 16,
        fontWeight: FontWeight.normal,
      ),
      urlPolicy: const TextStyle(color: CustomColors.kBlueBottone),
      buttonStyle: ButtonStyle(
        backgroundColor: WidgetStateProperty.all(CustomColors.kBlueBottone),
        foregroundColor: WidgetStateProperty.all(Colors.black),
        padding: WidgetStateProperty.all(
          const EdgeInsets.symmetric(
            horizontal: 24,
            vertical: 16,
          ),
        ),
        shape: WidgetStateProperty.all(
          RoundedRectangleBorder(
            borderRadius: BorderRadius.circular(8),
          ),
        ),
        textStyle: WidgetStateProperty.all(
          const TextStyle(fontSize: 16, fontWeight: FontWeight.bold),
        ),
      ),
    );
  }
}

Now you can use them anywhere in the app simply like this:

// Anywhere in your app
ConsentModeFlutterBanner(
  config: ConsentModeFlutterConfigHelper.bannerConfig(context),
  style: ConsentModeFlutterUiStyleHelper.bannerStyle(context),
  onConsentChanged: (preferences) {
    // Handle changes
  },
  onDismissed: () {
    // Handle dismissal
  },
)

// Or in the wrapper
ConsentModeFlutterWrapper(
  bannerConfig: ConsentModeFlutterConfigHelper.bannerConfig(context),
  bannerStyle: ConsentModeFlutterUiStyleHelper.bannerStyle(context),
  child: MyHomePage(),
)

// Or in the bottom sheet
ConsentModeFlutterUtils.showCustomBottomSheet(
  context,
  ConsentModeFlutterBanner(
    config: ConsentModeFlutterConfigHelper.bannerConfig(context),
    style: ConsentModeFlutterUiStyleHelper.bannerStyle(context),
    onConsentChanged: (preferences) {},
    onDismissed: () {},
  ),
);

Alternative Approach: Direct Configuration

If you prefer a more direct approach (less recommended for large projects):

// Direct custom configuration
ConsentModeFlutterConfig customConfig = ConsentModeFlutterConfig(
  title: 'Privacy & Cookies',
  description: 'We use cookies and similar technologies to improve your experience...',
  acceptAllText: 'Accept All',
  rejectAllText: 'Reject All',
  settingsText: 'Manage Preferences',
  essentialTitle: 'Essential Cookies',
  analyticsTitle: 'Analytics Cookies',
  marketingTitle: 'Marketing Cookies',
  functionalTitle: 'Functional Cookies',
);

// Direct custom style
ConsentModeFlutterUiStyle customStyle = ConsentModeFlutterUiStyle(
  backgroundColor: Colors.white,
  textColor: Colors.black87,
  buttonColor: Colors.blue,
  buttonTextColor: Colors.white,
  borderRadius: 12.0,
);

Advantages of the Helper Approach

  1. Centralization: All configurations in one place
  2. Dynamic Theme: Can automatically adapt to the app's theme
  3. Reusability: Once written, usable everywhere
  4. Maintainability: Easy to update and modify
  5. Consistency: Ensures the same look throughout the app

Analytics Integration #

void _initializeAnalytics() {
  if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.analytics)) {
    // Initialize Firebase Analytics
    FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(true);

    // Initialize Google Analytics
    // GoogleAnalytics.initialize();
  } else {
    // Disable analytics
    FirebaseAnalytics.instance.setAnalyticsCollectionEnabled(false);
  }
}

The library supports four types of consent:

  • Essential: Cookies and services essential for site functionality
  • Analytics: Analysis tools and performance measurement
  • Marketing: Cookies for personalized advertising and remarketing
  • Functional: Services that improve user experience (chat, maps, etc.)

Main APIs #

Static Methods #

// Check if consent has been granted
bool isGranted = ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.analytics);

// Get all preferences
Map<ConsentModeFlutterType, bool> preferences = ConsentModeFlutter.preferences;

// Reset all consents
await ConsentModeFlutter.reset();

// Add/Remove listeners
ConsentModeFlutter.addListener(callback);
ConsentModeFlutter.removeListener(callback);

Manager #

final manager = ConsentModeFlutterManager();

// Check if banner should be shown
bool shouldShow = manager.shouldShowBanner;

// Force banner display
manager.showBanner();

Best Practices #

1. Early Initialization #

Always initialize consent mode before any other service that requires consent.

2. Service Management #

void _manageServices() {
  // Analytics
  if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.analytics)) {
    _enableAnalytics();
  } else {
    _disableAnalytics();
  }

  // Marketing
  if (ConsentModeFlutter.isConsentGranted(ConsentModeFlutterType.marketing)) {
    _enableMarketing();
  } else {
    _disableMarketing();
  }
}

3. Privacy Respect #

  • Always ask for consent before tracking
  • Provide granular options
  • Allow users to change their mind
  • Document what your cookies do

4. UI Implementation Choice #

Choose the right implementation based on your needs:

  • Use Standard Banner (Option 1-2) for quick implementation with good customization
  • Use Custom UI (Option 4) when you need complete design control
  • Use Listeners (Option 3) when you need to react to consent changes in real-time

Troubleshooting #

  1. Verify that ConsentModeFlutter.initialize() is called
  2. Check that the banner hasn't already been accepted/rejected
  3. Use ConsentModeFlutter.reset() for testing

Consents not persistent #

  1. Verify app write permissions
  2. Check that initialization is completed before use

Listeners not working #

  1. Make sure to call addListener after initState
  2. Don't forget removeListener in dispose

Custom UI not working #

  1. Ensure bannerCustomConfigUIStyle is properly configured
  2. Verify that your custom widget handles consent logic properly
  3. Check that navigation/dismissal is implemented correctly

Complete Example #

See the example/lib/main.dart file in the repository for a complete working example.

License #

MIT License - see the LICENSE file for details.

Contributions #

Contributions are welcome! Open an issue or pull request for improvements.


Note: This library helps you manage consents, but it's your responsibility to ensure the implementation complies with local privacy laws (GDPR, CCPA, etc.).

0
likes
150
points
53
downloads

Publisher

unverified uploader

Weekly Downloads

A comprehensive Flutter library for managing user consent according to GDPR, CCPA, and other privacy regulations.

Repository (GitHub)
View/report issues

Topics

#gdpr #ccpa #consent #privacy #flutter-plugin

Documentation

API reference

License

MIT (license)

Dependencies

flutter, plugin_platform_interface, shared_preferences, url_launcher

More

Packages that depend on consent_mode_flutter

Packages that implement consent_mode_flutter