dynamic_popup 1.0.8 copy "dynamic_popup: ^1.0.8" to clipboard
dynamic_popup: ^1.0.8 copied to clipboard

A flexible and customizable dynamic popup system for Flutter with markdown support and interactive components.

Dynamic Popup #

A flexible and customizable dynamic popup system for Flutter with markdown support and interactive components.

๐Ÿ“š Table of Contents #

Features #

  • Configurable popups from backend APIs
  • Markdown parser with support for dynamic component placeholders
  • Interactive components: RadioButton, Checkbox, TextArea, TextField, Dropdown
  • Automatic validation of required fields
  • Persistent state management (show once, completed/dismissed)
  • Blocking and non-blocking popup behaviors
  • Easy to customize and extend
  • No dependency on GetX - works with any Flutter app
  • Minimal API requirements - only two endpoints required
  • Custom slots support - add custom titles, footers, and action buttons
  • Smart scrolling - automatically shows scroll buttons for long content
  • Advanced conditional logic - show/hide fields or change required status based on other field values with three-tier priority system
  • Dynamic required indicators - real-time asterisk updates based on conditional logic

๐Ÿš€ Installation #

Add this to your package's pubspec.yaml file:

dependencies:
  dynamic_popup: ^1.0.7

Then run:

flutter pub get

Note: The http package is only required if you use the example implementations. The core library does not depend on http.

๐Ÿ“ฑ Popup Types #

1. Non-blocking Popup #

  • User can close without completing
  • Ideal for information, optional surveys, feature discovery

2. Blocking Popup #

  • User must complete to continue
  • Ideal for terms of service, privacy policy, required data

๐Ÿ›  Supported Components #

RadioButton #

<!-- New HTML-like syntax with custom initiator -->
:::dc<radiobutton id="component_id" required label="Label">
  <option id="opt1">Option1</option>
  <option id="opt2">Option2</option>
  <option id="opt3">Option3</option>
</radiobutton>dc:::

Checkbox (single/multiple) #

<!-- New HTML-like syntax with custom initiator -->
:::dc<checkbox id="component_id" label="Label">
  <option id="opt1">Option1</option>
  <option id="opt2">Option2</option>
  <option id="opt3">Option3</option>
</checkbox>dc:::

TextArea #

<!-- New HTML-like syntax with custom initiator -->
:::dc<textarea id="component_id" required label="Label" placeholder="Placeholder text" />dc:::

TextField #

<!-- New HTML-like syntax with custom initiator -->
:::dc<textfield id="component_id" label="Label" placeholder="Placeholder text" />dc:::
<!-- New HTML-like syntax with custom initiator -->
:::dc<dropdown id="component_id" required label="Label">
  <option id="opt1">Option1</option>
  <option id="opt2">Option2</option>
  <option id="opt3">Option3</option>
</dropdown>dc:::

๐Ÿ”ง Placeholder Syntax #

New Syntax (HTML-like with Named Attributes and Custom Initiator) #

:::dc<component_type id="component_id" required label="Label" placeholder="Placeholder" />dc:::

Custom Initiator: :::dc< and >dc::: - This prevents conflicts with regular HTML tags in markdown content.

Multiline Support: The syntax supports newlines and spaces between the initiator and terminator:

:::dc
<component_type id="component_id" required label="Label">
  <option id="opt1">Option1</option>
</component_type>
dc:::

Attributes:

  • id: Unique component ID (required)
  • required: Presence indicates the field is required (optional)
  • label: Label shown to user (required)
  • placeholder: Placeholder text for text inputs (optional)
  • default: Default value (optional)

Container Components with Option IDs: For components with options (RadioButton, Checkbox, Dropdown), use container syntax with option IDs:

:::dc<component_type id="component_id" required label="Label">
  <option id="option_id_1">Option 1</option>
  <option id="option_id_2">Option 2</option>
</component_type>dc:::

Benefits of Option IDs:

  • Send option IDs instead of text to the backend
  • Language-independent option identification
  • Easier to maintain when option text changes

๐Ÿ’ป Usage #

Setup Service #

import 'package:dynamic_popup/dynamic_popup.dart';

// Create a repository that implements your API calls
class MyPopupRepository extends BaseDynamicPopupRepository {
  @override
  Future<PopupApiResponse?> checkForPopup({
    required String screenName,
    String? userId,
  }) async {
    // Implement your API call here
    // Only these two methods are required
  }

  @override
  Future<bool> submitPopupResponse({
    required PopupResponse popupResponse,
  }) async {
    // Implement your API call here
    // Only these two methods are required
  }
}

// In your app initialization (e.g., in initState of your main widget)
final popupService = DynamicPopupService(
  repository: MyPopupRepository(),
);
popupService.init(); // Initialize the service

Show Manual Popup #

// Show specific popup by ID
await popupService.showPopupById('privacy_update_2024', context: context);

// Check for and show popup for a screen
await popupService.checkAndShowPopup(
  screenName: 'home_screen',
  context: context,
);

// Reset state for testing
await popupService.resetPopupState('privacy_update_2024');

// Reset all states
popupService.resetAllPopupStates();

Using Custom Slots #

// Add custom title, footer, and action buttons
final config = PopupConfig(
  id: 'custom_popup',
  title: 'Custom Popup',
  markdownContent: '''
## Welcome!

:::dc<textfield id="name" label="Your Name" placeholder="Enter your name" />dc:::
  ''',
  isBlocking: false,
);

showDialog(
  context: context,
  builder: (BuildContext context) {
    return DynamicPopupWidget(
      config: config,
      customTitle: const Text('Custom Title', style: TextStyle(color: Colors.white, fontSize: 24)),
      customFooter: const Padding(
        padding: EdgeInsets.all(16),
        child: Text('This is a custom footer', style: TextStyle(fontStyle: FontStyle.italic)),
      ),
      customActions: [
        TextButton(
          onPressed: () {
            // Custom action
            print('Custom button pressed');
          },
          child: const Text('Custom Action'),
        ),
      ],
      onCompleted: (response) => print('Response: ${response.responses}'),
    );
  },
);

Manual Popup Display #

``dart // Test specific component final config = PopupConfig( id: 'test_popup', title: 'Test', markdownContent: ''' :::dc

dc::: ''', isBlocking: false, );

showDialog( context: context, builder: (BuildContext context) { return DynamicPopupWidget( config: config, onCompleted: (response) => print('Response: ${response.responses}'), ); }, );


## ๐Ÿ”Œ Custom API Integration

The package is designed to work with any backend API with minimal requirements. Here's how to integrate with your custom API:

### 1. Minimal Implementation (Only 2 Required Methods)

```dart
import 'package:dynamic_popup/dynamic_popup.dart';

class MySimplePopupRepository extends BaseDynamicPopupRepository {
  @override
  Future<PopupApiResponse?> checkForPopup({
    required String screenName,
    String? userId,
  }) async {
    // Only implement these two required methods
    // All other methods are optional
  }

  @override
  Future<bool> submitPopupResponse({
    required PopupResponse popupResponse,
  }) async {
    // Only implement these two required methods
    // All other methods are optional
  }
}

2. Direct Usage Without Service #

// Fetch popup config from your API directly
final popupConfig = await MyApiService.getPopupForScreen('home_screen');

if (popupConfig != null) {
  await showDialog(
    context: context,
    builder: (BuildContext context) {
      return DynamicPopupWidget(
        config: popupConfig,
        onCompleted: (response) async {
          // Submit response to your API directly
          await MyApiService.submitResponse(response);
          Navigator.of(context).pop();
        },
      );
    },
  );
}

๐Ÿงช Testing #

Test Page #

The example app now includes a comprehensive test page with all popup types and a documentation screen.

๐Ÿ“‹ Complete Examples #

Privacy Policy Popup #

# Privacy Policy Update

Our privacy policy has been updated. Please review and confirm your preferences.

## Required Consent
:::dc<radiobutton id="privacy_accept" required label="Do you accept the new privacy policy?">
  <option id="accept">Accept</option>
  <option id="decline">Decline</option>
</radiobutton>dc:::

## Data Usage (Optional)
:::dc<checkbox id="data_usage" label="What data can we use?">
  <option id="analytics">Analytics</option>
  <option id="marketing">Marketing</option>
  <option id="performance">Performance</option>
  <option id="crash">Crash Reports</option>
</checkbox>dc:::

## Feedback
:::dc<textarea id="feedback" label="Comments or questions?" placeholder="Share your thoughts..." />dc:::

**Thank you for your attention.**

Complete Survey #

# Satisfaction Survey

Help us improve the app by completing this brief survey.

:::dc<textfield id="name" required label="Full Name" placeholder="Enter your name" />dc:::

:::dc<dropdown id="frequency" required label="Usage Frequency">
  <option id="daily">Daily</option>
  <option id="weekly">Weekly</option>
  <option id="monthly">Monthly</option>
  <option id="occasional">Occasional</option>
</dropdown>dc:::

:::dc<checkbox id="features" required label="Which features do you use?">
  <option id="restaurant">Restaurant</option>
  <option id="sales">Special Sales</option>
  <option id="pcm">PCM</option>
  <option id="snack">Snack Bar</option>
</checkbox>dc:::

:::dc<textarea id="suggestions" required label="Improvement suggestions" placeholder="Describe your ideas..." />dc:::

:::dc<radiobutton id="recommend" required label="Would you recommend the app?">
  <option id="definitely">Definitely</option>
  <option id="probably">Probably</option>
  <option id="not_sure">Not Sure</option>
  <option id="probably_not">Probably Not</option>
  <option id="definitely_not">Definitely Not</option>
</radiobutton>dc:::

๐Ÿ— Architecture #

File Structure #

lib/
โ”œโ”€โ”€ src/
โ”‚   โ”œโ”€โ”€ data/
โ”‚   โ”‚   โ”œโ”€โ”€ model/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ popup_config.dart
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dynamic_component.dart
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ popup_models.dart
โ”‚   โ”‚   โ””โ”€โ”€ repository/
โ”‚   โ”‚       โ”œโ”€โ”€ dynamic_popup_repository.dart
โ”‚   โ”‚       โ””โ”€โ”€ base_dynamic_popup_repository.dart
โ”‚   โ”œโ”€โ”€ parser/
โ”‚   โ”‚   โ””โ”€โ”€ markdown_dynamic_parser.dart
โ”‚   โ”œโ”€โ”€ service/
โ”‚   โ”‚   โ””โ”€โ”€ dynamic_popup_service.dart
โ”‚   โ”œโ”€โ”€ ui/
โ”‚   โ”‚   โ”œโ”€โ”€ components/
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dynamic_radio_button.dart
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dynamic_checkbox.dart
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dynamic_text_area.dart
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dynamic_text_field.dart
โ”‚   โ”‚   โ”‚   โ”œโ”€โ”€ dynamic_dropdown.dart
โ”‚   โ”‚   โ”‚   โ””โ”€โ”€ dynamic_component_factory.dart
โ”‚   โ”‚   โ””โ”€โ”€ dynamic_popup_widget.dart
โ””โ”€โ”€ dynamic_popup.dart

๐Ÿ”’ Security and Privacy #

  • All data is validated client-side
  • Responses are sent in secure JSON format
  • Local storage is encrypted for popup states
  • No sensitive data stored permanently

๐ŸŽจ Customization #

Themes and Styles #

The system uses the current app theme. To customize:

// In dynamic_popup_widget.dart, modify colors:
primaryColor: Theme.of(context).primaryColor,
errorColor: Colors.red.shade600,
backgroundColor: Colors.grey.shade50,

Custom Components #

Add new components by extending DynamicComponentFactory:

case DynamicComponentType.newType:
  return NewCustomWidget(/* ... */);

๐Ÿšจ Troubleshooting #

  • Verify service is initialized
  • Check API logs for errors
  • Verify target screen is correct

Parsing errors #

  • Check placeholder markdown syntax
  • Verify all components have unique IDs
  • Ensure options don't contain special characters

Validation issues #

  • Check that required fields have values
  • Verify data types are correct
  • Test with simple popups first

๐Ÿ“ Changelog #

[1.0.4] - 2025-10-09 #

Added #

  • Support for conditional required status in addition to conditional visibility
  • New required-when-visible attribute for components to control required status based on visibility
  • Case-insensitive conditional logic evaluation for better user experience
  • Comprehensive documentation for conditional logic features
  • New examples demonstrating both conditional visibility and conditional required status

[1.0.3] - 2025-09-29 #

Added #

  • Support for option IDs in radio buttons, checkboxes, and dropdowns
  • Smart scrolling for long content in popups
  • Support for multiline syntax with newlines and spaces in component definitions
  • Custom slots support for titles, footers, and action buttons
  • Dedicated examples/documentation screen in the example app
  • View Markdown button in example popups

Changed #

  • Improved markdown syntax from positional parameters to HTML-like syntax
  • Updated component initiator from ::: to :::dc<component>dc::: for better identification
  • Removed support for old positional parameter syntax
  • Updated all examples to use new HTML-like syntax with option IDs
  • Enhanced UI components to send option IDs instead of text values in responses
  • Consolidated test interface into main example screen
  • Improved dialog dismissal handling to prevent overlay issues

Fixed #

  • Ensured option IDs are properly used in popup responses instead of text values
  • Fixed overlay persistence issue when dismissing non-required popups

1.0.2 #

  • Removed http dependency from core library (moved to example project only)
  • Removed unused DefaultDynamicPopupRepository from core library
  • Added .pubignore file to prevent publishing unnecessary files
  • Added LICENSE file with MIT license
  • Added CONTRIBUTING.md with contribution guidelines
  • Added CODE_OF_CONDUCT.md with community standards
  • Fixed flutter analyze issues:
    • Removed unused local variable in dynamic_popup_service.dart
    • Removed unnecessary imports in dynamic_popup_widget.dart
    • Updated deprecated onPopInvoked to onPopInvokedWithResult
    • Updated deprecated value parameter to initialValue in dropdown
  • Updated documentation to clarify dependency requirements
  • Improved package structure for pub.dev publishing

1.0.1 #

  • Simplified repository pattern by reducing required methods from 7 to 3
  • Removed complex optional methods (markPopupAsShown, markPopupAsDismissed) for easier implementation
  • Streamlined example code by removing redundant buttons and simplifying demonstrations
  • Improved documentation with clearer, more concise instructions
  • Added JSON logging in example apps for better debugging of popup responses
  • Enhanced snackbar colors in examples for better readability and user experience
  • Fixed undefined variable error in API integration example
  • Removed unnecessary http dependency from core library (now only in example)
  • Removed unused DefaultDynamicPopupRepository from core library
  • Maintained all core functionality while reducing complexity

1.0.0 #

  • โœ… Base dynamic popup system
  • โœ… Markdown parser with placeholders
  • โœ… Interactive UI components
  • โœ… API repository and service
  • โœ… Controller integration
  • โœ… Testing system
  • โœ… Removed GetX dependency for better compatibility
  • โœ… Simplified API requirements (only 2 endpoints required)

๐Ÿ“ž Support #

For questions or issues with the dynamic popup system, please open an issue on GitHub.

๐Ÿ”„ Conditional Logic #

Dynamic Popup supports conditional logic to show/hide fields or change their required status based on other field values.

Conditional Visibility #

Fields can be shown or hidden based on the value of another field:

:::dc<radiobutton id="consent" required label="Do you consent?">
  <option id="yes">Yes</option>
  <option id="no">No</option>
</radiobutton>dc:::

:::dc<textfield id="reason" label="Reason for consent" depends-on="consent" when-value="yes" />dc:::

In this example, the "reason" text field will only be visible when the "consent" radio button has the value "yes".

Conditional Required Status #

Fields can change their required status based on the value of another field:

Required When Visible

:::dc<radiobutton id="employment" required label="Are you employed?">
  <option id="yes">Yes</option>
  <option id="no">No</option>
</radiobutton>dc:::

:::dc<textfield id="employer" label="Employer Name" required-when-visible="true" depends-on="employment" when-value="yes" />dc:::

In this example, the "employer" text field is always visible but becomes required only when the "employment" radio button has the value "yes".

Required When Value

:::dc<radiobutton id="info_needed" required label="Do you have additional information?">
  <option id="yes">Yes</option>
  <option id="no">No</option>
</radiobutton>dc:::

:::dc<textfield id="additional_info" label="Additional Information" depends-on="info_needed" required-when-value="yes" />dc:::

In this example, the "additional_info" text field is always visible but becomes required only when the "info_needed" radio button has the value "yes", regardless of its visibility.

Available Conditional Attributes #

  • depends-on: The ID of the component this field depends on
  • when-value: The value that triggers the visibility condition
  • required-when-visible: Whether the field should be required when visible (true/false)
  • required-when-value: The value of the depends-on field that makes this field required (instead of a boolean flag)
  • condition: The type of condition to check (default: equals)
  • disable-when-hidden: Whether to disable validation when hidden (default: true)

Supported Conditions #

  • equals: Field value equals the specified value (default)
  • notEquals: Field value does not equal the specified value
  • contains: Field value contains the specified value (for list values like checkboxes)
  • notContains: Field value does not contain the specified value (for list values like checkboxes)

Priority Order #

When multiple conditional required attributes are specified, they are evaluated in this order:

  1. required-when-value (highest priority)
  2. required-when-visible (medium priority)
  3. Component's default required attribute (lowest priority)
0
likes
145
points
43
downloads

Publisher

unverified uploader

Weekly Downloads

A flexible and customizable dynamic popup system for Flutter with markdown support and interactive components.

Repository (GitHub)
View/report issues
Contributing

Documentation

API reference

License

MIT (license)

Dependencies

flutter, flutter_html, shared_preferences

More

Packages that depend on dynamic_popup