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

Package to manage agreements with users. It allows you to display the latest versions of agreements, see which agreements a user has approved, and record the approval of an agreement.

IWS Agreements Package #

A comprehensive Flutter package for managing user agreements, terms of service, privacy policies, and other legal documents. This package provides a complete solution for displaying, tracking, and managing user agreement acceptance with version control and device information tracking.

Features #

  • Agreement Management: Display and retrieve the latest versions of published agreements
  • User Tracking: Track which agreements users have accepted and when
  • Pending Agreements: Identify agreements that need user acceptance or have newer versions
  • Bulk Operations: Accept multiple agreements at once
  • Device Information: Track device details when agreements are accepted
  • Version Control: Handle agreement versioning and changes over time
  • UI Components: Pre-built widgets for displaying agreement lists
  • Signature Support: Optional signature tracking for agreement acceptance

Getting started #

Configuration #

Add the IWS API key as a DART variable at compile time:

--dart-define=IWS_API_KEY=your_api_key_here

Data Models #

AgreementVersion #

Represents a specific version of an agreement with all its metadata:

class AgreementVersion {
  int id;
  int version;
  String? content;           // Delta formatted content (Quill)
  String? rawContent;        // Plain text content
  bool published;
  DateTime? startDate;       // When this version becomes active
  DateTime? endDate;         // When this version expires
  String? summaryOfChanges;  // Description of changes in this version
  Agreement agreement;       // Basic agreement information
  DateTime? createdAt;
  DateTime? updatedAt;
}

Agreement #

Basic agreement information:

class Agreement {
  int id;
  String name;               // Unique identifier
  String title;              // Display title
  bool requiresAcceptance;   // Whether user must explicitly accept
  DateTime? createdAt;
  DateTime? updatedAt;
}

PendingAgreement #

Represents an agreement that needs user action:

class PendingAgreement {
  AgreementVersion agreementVersion;
  bool isNewVersion;         // true if user has accepted older version
}

AcceptedAgreement #

Contains user's agreement acceptance history:

class AcceptedAgreement {
  List<Accepted> accepted;           // List of user's accepted agreements
  List<AgreementVersion> lastAgreements; // Latest versions of all agreements
}

Accepted #

Individual agreement acceptance record:

class Accepted {
  int id;
  String agreementName;
  int agreementVersion;
  DateTime acceptatedAt;     // When user accepted
  String? signature;         // Optional signature
}

Usage #

Initialize the Service #

The IwsAgreements class is a singleton that handles all agreement operations:

import 'package:iws_agreements/iws_agreements.dart';

final iwsAgreements = IwsAgreements();

Retrieve All Available Agreements #

Get the latest published versions of all agreements:

try {
  final List<AgreementVersion> agreements = await iwsAgreements.getAllAgreements();
  
  for (var agreement in agreements) {
    print('Agreement: ${agreement.agreement.title}');
    print('Version: ${agreement.version}');
    print('Requires Acceptance: ${agreement.agreement.requiresAcceptance}');
    print('Content: ${agreement.content}');
  }
} catch (e) {
  print('Error retrieving agreements: $e');
}

Check User's Accepted Agreements #

See which agreements a specific user has already accepted:

try {
  final AcceptedAgreement userAgreements = await iwsAgreements
      .getAcceptedAgreements('user_123');
  
  print('User has accepted ${userAgreements.accepted.length} agreements');
  
  for (var accepted in userAgreements.accepted) {
    print('Accepted: ${accepted.agreementName} v${accepted.agreementVersion}');
    print('Accepted on: ${accepted.acceptatedAt}');
  }
} catch (e) {
  print('Error retrieving accepted agreements: $e');
}

Get Pending Agreements for User #

Identify agreements that need user attention (not accepted or newer versions available):

try {
  // Get all pending agreements
  final List<PendingAgreement> allPending = await iwsAgreements
      .getPendingAgreements('user_123');
  
  // Get only agreements that require acceptance
  final List<PendingAgreement> requiredPending = await iwsAgreements
      .getPendingAgreements('user_123', onlyWithRequiredAcceptance: true);
  
  for (var pending in requiredPending) {
    if (pending.isNewVersion) {
      print('New version available for: ${pending.agreementVersion.agreement.title}');
    } else {
      print('Not yet accepted: ${pending.agreementVersion.agreement.title}');
    }
    print('Summary of changes: ${pending.agreementVersion.summaryOfChanges}');
  }
} catch (e) {
  print('Error retrieving pending agreements: $e');
}

Get Specific Agreement #

Retrieve the latest version of a specific agreement by name:

try {
  final AgreementVersion? agreement = await iwsAgreements
      .getAgreement('terms_of_service');
  
  if (agreement != null) {
    print('Latest version: ${agreement.version}');
    print('Content: ${agreement.content}');
    print('Active from: ${agreement.startDate}');
  } else {
    print('Agreement not found');
  }
} catch (e) {
  print('Error retrieving agreement: $e');
}

Accept Single Agreement #

Record user acceptance of a specific agreement:

import 'package:iws_device_info/iws_device_info.dart';

try {
  // Basic acceptance
  await iwsAgreements.acceptAgreement(
    'privacy_policy',
    userIdentifier: 'user_123',
  );
  
  // Acceptance with additional information
  final device = await AppDevice.init();
  await iwsAgreements.acceptAgreement(
    'terms_of_service',
    userIdentifier: 'user_123',
    device: device,
    appVersionCode: 123,
    signature: 'user_digital_signature',
  );
  
  print('Agreement accepted successfully');
} catch (e) {
  print('Error accepting agreement: $e');
}

Accept Multiple Agreements #

Accept several agreements at once:

try {
  final agreementsToAccept = [
    'terms_of_service',
    'privacy_policy',
    'cookie_policy'
  ];
  
  final device = await AppDevice.init();
  
  await iwsAgreements.acceptAgreements(
    agreementsToAccept,
    userIdentifier: 'user_123',
    device: device,
    appVersionCode: 123,
  );
  
  print('All agreements accepted successfully');
} catch (e) {
  print('Error accepting agreements: $e');
}

Using the Agreement List Widget #

Display agreements in your UI using the pre-built widget:

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

class AgreementsScreen extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Agreements')),
      body: IwsAgreementList(
        // Optional: Custom item builder
        itemBuilder: (AgreementVersion agreement) {
          return Card(
            margin: EdgeInsets.all(8.0),
            child: ListTile(
              title: Text(agreement.agreement.title),
              subtitle: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Text('Version: ${agreement.version}'),
                  if (agreement.summaryOfChanges != null)
                    Text('Changes: ${agreement.summaryOfChanges}'),
                ],
              ),
              trailing: agreement.agreement.requiresAcceptance
                  ? Icon(Icons.warning, color: Colors.orange)
                  : Icon(Icons.info, color: Colors.blue),
              onTap: () {
                // Navigate to agreement details
                _showAgreementDetails(context, agreement);
              },
            ),
          );
        },
      ),
    );
  }
  
  void _showAgreementDetails(BuildContext context, AgreementVersion agreement) {
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: Text(agreement.agreement.title),
        content: SingleChildScrollView(
          child: Text(agreement.content ?? 'No content available'),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(),
            child: Text('Close'),
          ),
          if (agreement.agreement.requiresAcceptance)
            ElevatedButton(
              onPressed: () async {
                // Accept the agreement
                await IwsAgreements().acceptAgreement(
                  agreement.agreement.name,
                  userIdentifier: 'current_user_id',
                );
                Navigator.of(context).pop();
              },
              child: Text('Accept'),
            ),
        ],
      ),
    );
  }
}

Complete User Flow Example #

Here's a complete example showing how to check for pending agreements and handle user acceptance:

import 'package:flutter/material.dart';
import 'package:iws_agreements/iws_agreements.dart';
import 'package:iws_device_info/iws_device_info.dart';

class AgreementManager {
  final IwsAgreements _iwsAgreements = IwsAgreements();
  
  /// Check if user needs to accept any agreements
  Future<bool> hasUserAcceptedAllRequiredAgreements(String userId) async {
    try {
      final pendingAgreements = await _iwsAgreements.getPendingAgreements(
        userId,
        onlyWithRequiredAcceptance: true,
      );
      
      return pendingAgreements.isEmpty;
    } catch (e) {
      print('Error checking agreements: $e');
      return false;
    }
  }
  
  /// Show pending agreements to user and handle acceptance
  Future<void> showPendingAgreements(BuildContext context, String userId) async {
    try {
      final pendingAgreements = await _iwsAgreements.getPendingAgreements(
        userId,
        onlyWithRequiredAcceptance: true,
      );
      
      if (pendingAgreements.isEmpty) {
        return; // No pending agreements
      }
      
      final agreementsToAccept = <String>[];
      
      for (var pending in pendingAgreements) {
        final shouldAccept = await _showAgreementDialog(
          context,
          pending.agreementVersion,
          pending.isNewVersion,
        );
        
        if (shouldAccept) {
          agreementsToAccept.add(pending.agreementVersion.agreement.name);
        }
      }
      
      if (agreementsToAccept.isNotEmpty) {
        await _acceptAgreements(userId, agreementsToAccept);
      }
    } catch (e) {
      print('Error handling pending agreements: $e');
    }
  }
  
  Future<bool> _showAgreementDialog(
    BuildContext context,
    AgreementVersion agreement,
    bool isNewVersion,
  ) async {
    return await showDialog<bool>(
      context: context,
      barrierDismissible: false,
      builder: (context) => AlertDialog(
        title: Text(agreement.agreement.title),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              if (isNewVersion)
                Container(
                  padding: EdgeInsets.all(8),
                  decoration: BoxDecoration(
                    color: Colors.orange.withOpacity(0.1),
                    borderRadius: BorderRadius.circular(4),
                  ),
                  child: Row(
                    children: [
                      Icon(Icons.update, color: Colors.orange),
                      SizedBox(width: 8),
                      Expanded(
                        child: Text(
                          'This is a new version of an agreement you previously accepted.',
                          style: TextStyle(fontWeight: FontWeight.bold),
                        ),
                      ),
                    ],
                  ),
                ),
              if (isNewVersion) SizedBox(height: 16),
              if (agreement.summaryOfChanges != null) ...[
                Text(
                  'Summary of Changes:',
                  style: TextStyle(fontWeight: FontWeight.bold),
                ),
                Text(agreement.summaryOfChanges!),
                SizedBox(height: 16),
              ],
              Text(
                'Agreement Content:',
                style: TextStyle(fontWeight: FontWeight.bold),
              ),
              SizedBox(height: 8),
              Container(
                height: 200,
                child: SingleChildScrollView(
                  child: Text(agreement.content ?? 'No content available'),
                ),
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.of(context).pop(false),
            child: Text('Decline'),
          ),
          ElevatedButton(
            onPressed: () => Navigator.of(context).pop(true),
            child: Text('Accept'),
          ),
        ],
      ),
    ) ?? false;
  }
  
  Future<void> _acceptAgreements(String userId, List<String> agreements) async {
    try {
      final device = await AppDevice.init();
      
      await _iwsAgreements.acceptAgreements(
        agreements,
        userIdentifier: userId,
        device: device,
        appVersionCode: 100, // Your app version
      );
      
      print('Successfully accepted ${agreements.length} agreements');
    } catch (e) {
      print('Error accepting agreements: $e');
      rethrow;
    }
  }
}

Error Handling #

The package uses the iws_http library for API communication. Common errors include:

  • DataNotFoundException: When a specific agreement is not found
  • Network errors: When API is unreachable
  • Authentication errors: When API key is invalid

Always wrap API calls in try-catch blocks:

try {
  final agreements = await iwsAgreements.getAllAgreements();
  // Handle success
} on DataNotFoundException {
  // Handle specific not found error
} catch (e) {
  // Handle general errors
  print('Error: $e');
}

Best Practices #

  1. Check for required agreements on app start
  2. Use device information for compliance tracking
  3. Handle network errors gracefully
  4. Store user acceptance status locally for offline access
  5. Show agreement summaries for version updates
  6. Implement proper loading states in your UI

API Configuration #

The package connects to ws.interaad.com.ar with the base path api/sdk/. Ensure your API key has the necessary permissions for agreement management.

0
likes
130
points
11
downloads

Documentation

API reference

Publisher

unverified uploader

Weekly Downloads

Package to manage agreements with users. It allows you to display the latest versions of agreements, see which agreements a user has approved, and record the approval of an agreement.

Homepage

License

MIT (license)

Dependencies

flutter, iws_device_info, iws_http

More

Packages that depend on iws_agreements