Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/formbricks/formbricks/llms.txt

Use this file to discover all available pages before exploring further.

The Formbricks Flutter SDK allows you to integrate surveys into native iOS and Android applications built with Flutter.
The Flutter SDK uses WebView to render surveys. Make sure your app has the necessary permissions to display web content.

Installation

1

Add dependencies

Add the required packages to your pubspec.yaml:
dependencies:
  flutter:
    sdk: flutter
  webview_flutter: ^4.0.0
  http: ^1.0.0
2

Install packages

Run the package installation:
flutter pub get
3

Configure platform permissions

iOS (ios/Runner/Info.plist):
<key>NSAppTransportSecurity</key>
<dict>
  <key>NSAllowsArbitraryLoads</key>
  <true/>
</dict>
<key>io.flutter.embedded_views_preview</key>
<true/>
Android (android/app/src/main/AndroidManifest.xml):
<uses-permission android:name="android.permission.INTERNET" />

Core Implementation

Formbricks Service

Create a service to manage Formbricks functionality:
// lib/services/formbricks_service.dart
import 'dart:convert';
import 'package:http/http.dart' as http;

class FormbricksService {
  final String environmentId;
  final String appUrl;
  String? _userId;
  String? _contactId;
  Map<String, String> _attributes = {};

  FormbricksService({
    required this.environmentId,
    required this.appUrl,
  });

  /// Initialize the SDK
  Future<void> setup() async {
    try {
      // Fetch environment state
      final response = await http.get(
        Uri.parse('$appUrl/api/v1/client/$environmentId/environment'),
      );

      if (response.statusCode == 200) {
        print('Formbricks initialized successfully');
      } else {
        print('Failed to initialize Formbricks: ${response.statusCode}');
      }
    } catch (e) {
      print('Error initializing Formbricks: $e');
    }
  }

  /// Set user ID
  Future<void> setUserId(String userId) async {
    _userId = userId;
    await _updateUser();
  }

  /// Set user email
  Future<void> setEmail(String email) async {
    _attributes['email'] = email;
    await _updateUser();
  }

  /// Set user attributes
  Future<void> setAttributes(Map<String, String> attributes) async {
    _attributes.addAll(attributes);
    await _updateUser();
  }

  /// Set single attribute
  Future<void> setAttribute(String key, String value) async {
    _attributes[key] = value;
    await _updateUser();
  }

  /// Set user language
  Future<void> setLanguage(String language) async {
    _attributes['language'] = language;
    await _updateUser();
  }

  /// Track custom event
  Future<void> track(String eventName) async {
    if (_userId == null) {
      print('Cannot track event: User ID not set');
      return;
    }

    try {
      final response = await http.post(
        Uri.parse('$appUrl/api/v1/client/$environmentId/actions'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'userId': _userId,
          'name': eventName,
          'environmentId': environmentId,
        }),
      );

      if (response.statusCode == 200) {
        print('Event tracked: $eventName');
      }
    } catch (e) {
      print('Error tracking event: $e');
    }
  }

  /// Logout user
  Future<void> logout() async {
    _userId = null;
    _contactId = null;
    _attributes.clear();
  }

  /// Update user information on backend
  Future<void> _updateUser() async {
    if (_userId == null) return;

    try {
      final response = await http.post(
        Uri.parse('$appUrl/api/v1/client/$environmentId/update/contacts/$_userId'),
        headers: {'Content-Type': 'application/json'},
        body: jsonEncode({
          'userId': _userId,
          'attributes': _attributes,
        }),
      );

      if (response.statusCode == 200) {
        final data = jsonDecode(response.body);
        _contactId = data['state']?['data']?['contactId'];
        print('User updated successfully');
      }
    } catch (e) {
      print('Error updating user: $e');
    }
  }

  /// Get user ID
  String? get userId => _userId;

  /// Get contact ID
  String? get contactId => _contactId;
}

Survey Display Widget

Create a widget to display surveys:
// lib/widgets/formbricks_survey_widget.dart
import 'package:flutter/material.dart';
import 'package:webview_flutter/webview_flutter.dart';

class FormbricksSurveyWidget extends StatefulWidget {
  final String surveyUrl;
  final VoidCallback? onComplete;
  final VoidCallback? onClose;

  const FormbricksSurveyWidget({
    Key? key,
    required this.surveyUrl,
    this.onComplete,
    this.onClose,
  }) : super(key: key);

  @override
  State<FormbricksSurveyWidget> createState() => _FormbricksSurveyWidgetState();
}

class _FormbricksSurveyWidgetState extends State<FormbricksSurveyWidget> {
  late WebViewController _controller;
  bool _isLoading = true;

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

  void _initializeWebView() {
    _controller = WebViewController()
      ..setJavaScriptMode(JavaScriptMode.unrestricted)
      ..setNavigationDelegate(
        NavigationDelegate(
          onPageStarted: (String url) {
            setState(() {
              _isLoading = true;
            });
          },
          onPageFinished: (String url) {
            setState(() {
              _isLoading = false;
            });
          },
        ),
      )
      ..loadRequest(Uri.parse(widget.surveyUrl));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Survey'),
        leading: IconButton(
          icon: const Icon(Icons.close),
          onPressed: () {
            widget.onClose?.call();
            Navigator.of(context).pop();
          },
        ),
      ),
      body: Stack(
        children: [
          WebViewWidget(controller: _controller),
          if (_isLoading)
            const Center(
              child: CircularProgressIndicator(),
            ),
        ],
      ),
    );
  }
}

Usage Examples

Initialize in Main App

// lib/main.dart
import 'package:flutter/material.dart';
import 'services/formbricks_service.dart';

void main() async {
  WidgetsFlutterBinding.ensureInitialized();
  
  // Initialize Formbricks
  final formbricks = FormbricksService(
    environmentId: 'your-environment-id',
    appUrl: 'https://app.formbricks.com',
  );
  
  await formbricks.setup();
  
  runApp(MyApp(formbricks: formbricks));
}

class MyApp extends StatelessWidget {
  final FormbricksService formbricks;

  const MyApp({Key? key, required this.formbricks}) : super(key: key);

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

User Authentication

// lib/screens/login_screen.dart
import 'package:flutter/material.dart';
import '../services/formbricks_service.dart';

class LoginScreen extends StatelessWidget {
  final FormbricksService formbricks;

  const LoginScreen({Key? key, required this.formbricks}) : super(key: key);

  Future<void> _handleLogin(String userId, String email) async {
    // Your authentication logic
    await authenticate(userId);

    // Identify user in Formbricks
    await formbricks.setUserId(userId);
    await formbricks.setEmail(email);
    await formbricks.setAttributes({
      'loginDate': DateTime.now().toIso8601String(),
      'platform': 'mobile',
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: ElevatedButton(
          onPressed: () => _handleLogin('user-123', 'user@example.com'),
          child: const Text('Login'),
        ),
      ),
    );
  }
}

Track Events

// lib/screens/home_screen.dart
import 'package:flutter/material.dart';
import '../services/formbricks_service.dart';

class HomeScreen extends StatelessWidget {
  final FormbricksService formbricks;

  const HomeScreen({Key? key, required this.formbricks}) : super(key: key);

  Future<void> _handleFeatureUsage(String feature) async {
    // Your feature logic
    
    // Track the event
    await formbricks.track('feature_${feature}_used');
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: const Text('Home')),
      body: Column(
        children: [
          ElevatedButton(
            onPressed: () => _handleFeatureUsage('export'),
            child: const Text('Export Data'),
          ),
          ElevatedButton(
            onPressed: () => _handleFeatureUsage('share'),
            child: const Text('Share'),
          ),
        ],
      ),
    );
  }
}

Display Survey

// lib/screens/survey_screen.dart
import 'package:flutter/material.dart';
import '../widgets/formbricks_survey_widget.dart';

class SurveyScreen extends StatelessWidget {
  final String surveyId;

  const SurveyScreen({Key? key, required this.surveyId}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    final surveyUrl = 'https://app.formbricks.com/s/$surveyId';

    return FormbricksSurveyWidget(
      surveyUrl: surveyUrl,
      onComplete: () {
        print('Survey completed');
        Navigator.of(context).pop();
      },
      onClose: () {
        print('Survey closed');
      },
    );
  }
}

// Show survey from anywhere
void showSurvey(BuildContext context, String surveyId) {
  Navigator.of(context).push(
    MaterialPageRoute(
      builder: (context) => SurveyScreen(surveyId: surveyId),
      fullscreenDialog: true,
    ),
  );
}

Provider Pattern

For state management with Provider:
// lib/providers/formbricks_provider.dart
import 'package:flutter/foundation.dart';
import '../services/formbricks_service.dart';

class FormbricksProvider extends ChangeNotifier {
  final FormbricksService _service;

  FormbricksProvider({
    required String environmentId,
    required String appUrl,
  }) : _service = FormbricksService(
          environmentId: environmentId,
          appUrl: appUrl,
        );

  Future<void> initialize() async {
    await _service.setup();
    notifyListeners();
  }

  Future<void> identifyUser(String userId, String email,
      {Map<String, String>? attributes}) async {
    await _service.setUserId(userId);
    await _service.setEmail(email);
    if (attributes != null) {
      await _service.setAttributes(attributes);
    }
    notifyListeners();
  }

  Future<void> trackEvent(String eventName) async {
    await _service.track(eventName);
  }

  Future<void> logout() async {
    await _service.logout();
    notifyListeners();
  }

  String? get userId => _service.userId;
  String? get contactId => _service.contactId;
}
Usage:
import 'package:provider/provider.dart';
import 'providers/formbricks_provider.dart';

void main() {
  runApp(
    ChangeNotifierProvider(
      create: (_) => FormbricksProvider(
        environmentId: 'your-environment-id',
        appUrl: 'https://app.formbricks.com',
      )..initialize(),
      child: const MyApp(),
    ),
  );
}

// In a widget
class MyWidget extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    final formbricks = Provider.of<FormbricksProvider>(context);

    return ElevatedButton(
      onPressed: () => formbricks.trackEvent('button_clicked'),
      child: const Text('Track Event'),
    );
  }
}

Environment Configuration

Create environment-specific configurations:
// lib/config/environment.dart
class Environment {
  static const String formbricksEnvId = String.fromEnvironment(
    'FORMBRICKS_ENV_ID',
    defaultValue: 'your-default-env-id',
  );

  static const String formbricksUrl = String.fromEnvironment(
    'FORMBRICKS_URL',
    defaultValue: 'https://app.formbricks.com',
  );
}
Run with environment variables:
flutter run --dart-define=FORMBRICKS_ENV_ID=your-env-id

Best Practices

  1. Error Handling: Always wrap API calls in try-catch blocks
  2. Loading States: Show loading indicators during network operations
  3. Offline Support: Cache user data locally for offline scenarios
  4. Permissions: Request necessary permissions before displaying surveys
  5. User Privacy: Only track essential user information

Troubleshooting

WebView Not Loading

Ensure you have the proper permissions and that the WebView package is properly initialized:
import 'package:webview_flutter/webview_flutter.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  // Initialize WebView
  if (Platform.isAndroid) {
    WebView.platform = SurfaceAndroidWebView();
  }
  runApp(MyApp());
}

CORS Issues

If you encounter CORS errors, ensure your Formbricks instance allows requests from your app domain.

Next Steps

JavaScript SDK

Learn about the core JavaScript SDK

React SDK

Integrate Formbricks with React applications