abtasty_qa_assistant 1.0.0 copy "abtasty_qa_assistant: ^1.0.0" to clipboard
abtasty_qa_assistant: ^1.0.0 copied to clipboard

Flutter package providing quality assurance tools and utilities for ABTasty testing workflows.

example/lib/main.dart

import 'package:flagship/hits/event.dart';
import 'package:flagship/hits/screen.dart';
import 'package:flagship/hits/transaction.dart';
import 'package:flutter/material.dart' hide Page;
import 'package:abtasty_qa_assistant/abtasty_qa_assistant.dart';
import 'package:flagship/flagship.dart';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'ABTasty QA Demo',
      debugShowCheckedModeBanner: false,
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2196F3),
          brightness: Brightness.light,
        ),
        useMaterial3: true,
        appBarTheme: const AppBarTheme(centerTitle: true, elevation: 2),
      ),
      darkTheme: ThemeData(
        colorScheme: ColorScheme.fromSeed(
          seedColor: const Color(0xFF2196F3),
          brightness: Brightness.dark,
        ),
        useMaterial3: true,
      ),
      home: const DemoHomePage(),
    );
  }
}

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

  @override
  State<DemoHomePage> createState() => _DemoHomePageState();
}

class _DemoHomePageState extends State<DemoHomePage> {
  ABTastyQAAssistant? _qaAssistant;

  String _btnTitleValue = "Loading...";
  String _btnColorValue = "Loading...";
  String _flag1Value = "Loading...";
  int _flag2Value = 0;
  String _key1Value = "Loading...";
  bool _isVIPMode = false;
  String _vipValue = "Loading...";

  @override
  void initState() {
    super.initState();
    _initFlagship();
    // Ne pas appeler _setupFlagUpdateListener() ici
  }

  Future<void> _initFlagship() async {
    try {
      // Init flagship sdk
      await Flagship.start(
        "bkk9glocmjcg0vtmdlng",
        "DxAcxlnRB9yFBZYtLDue1q01dcXZCw6aM49CQB23",
      );
      Flagship.newVisitor(visitorId: "qaUser", hasConsented: true).withContext({
        "isQA": true,
        "country": "FR",
        "customer": "customer",
        "accountID": "1234",
        "isVip": _isVIPMode
      }).build();
      print("Flagship SDK initialized successfully");

      // Fetch Flags
      await Flagship.getCurrentVisitor()?.fetchFlags();

      // Récupérer les valeurs initiales des flags
      _updateFlagValues();

      // ✅ Configurer le listener APRÈS que le visitor soit créé
      _setupFlagUpdateListener();
    } catch (e) {
      print("Error initializing Flagship SDK: $e");
      setState(() {
        _btnTitleValue = "Error loading flag";
      });
    }
  }

  /// Setup listener for live flag updates from QA Assistant
  void _setupFlagUpdateListener() {
    final visitor = Flagship.getCurrentVisitor();
    if (visitor != null) {
      visitor.onFlagUpdate = (changedKeys) {
        print('🔔 Live update received for flags: $changedKeys');

        // Show a snackbar to indicate live update
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            SnackBar(
              content: Text('🔄 Live update: ${changedKeys.join(", ")}'),
              backgroundColor: Colors.orange,
              duration: const Duration(seconds: 2),
            ),
          );
        }

        // Update the flag values
        _updateFlagValues();
      };
      print('✅ Flag update listener registered');
    }
  }

  /// Update all flag values and refresh UI
  void _updateFlagValues() {
    final visitor = Flagship.getCurrentVisitor();
    if (visitor != null) {
      setState(() {
        _btnTitleValue = visitor.getFlag("btnTitle").value("Default Button");
        _btnColorValue = visitor.getFlag("btnColor").value("Default Color");
        _flag1Value = visitor.getFlag("payKey1").value("Default Flag1");
        _flag2Value = visitor.getFlag("payKey2").value(0);
        _key1Value = visitor.getFlag("rejectedKey").value("Rejected Key");
        _vipValue = visitor.getFlag("Delivery cost").value("25 €").toString();
      });

      print("Flag values updated:");
      print("  btnTitle: $_btnTitleValue");
      print("  btnColor: $_btnColorValue");
      print("  payKey1: $_flag1Value");
      print("  payKey2: $_flag2Value");
      print("  rejectedKey: $_key1Value");
      print("  Delivery cost: $_vipValue");
    }
  }

  @override
  void dispose() {
    // Clean up the flag update listener
    final visitor = Flagship.getCurrentVisitor();
    if (visitor != null) {
      visitor.onFlagUpdate = null;
    }
    _destroyQAAssistant();
    super.dispose();
  }

  void _initializeQAAssistant() {
    if (_qaAssistant == null) {
      _qaAssistant = ABTastyQAAssistant(
        "bkk9glocmjcg0vtmdlng",
        "DxAcxlnRB9yFBZYtLDue1q01dcXZCw6aM49CQB23",
        onClose: () {
          // Automatically refresh flags when QA Assistant is closed
          print('🔄 QA Assistant closed, fetching updated flags...');
          _fetchFlagsFromSDK();
        },
      );
      print('✅ QA Assistant initialized and bucketing preloading started');
    }
  }

  void _destroyQAAssistant() {
    if (_qaAssistant != null) {
      _qaAssistant!.hideOverlayButton();
      _qaAssistant!.dispose();
      _qaAssistant = null;
      print('🗑️ QA Assistant destroyed');
    }
  }

  void _toggleQAAssistant() {
    if (_qaAssistant?.isOverlayVisible ?? false) {
      _destroyQAAssistant();
    } else {
      _initializeQAAssistant();
      _qaAssistant?.showOverlayButton(context);
    }
    setState(() {});
  }

  Future<void> _refreshFlags() async {
    try {
      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Refreshing flag values...'),
          duration: Duration(seconds: 1),
        ),
      );

      _updateFlagValues();

      ScaffoldMessenger.of(context).showSnackBar(
        const SnackBar(
          content: Text('Flag values refreshed successfully!'),
          backgroundColor: Colors.green,
          duration: Duration(seconds: 2),
        ),
      );
    } catch (e) {
      print("Error refreshing flags: $e");
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Error refreshing flags: $e'),
          backgroundColor: Colors.red,
          duration: const Duration(seconds: 3),
        ),
      );
    }
  }

  /// Fetch flags from SDK and update UI
  Future<void> _fetchFlagsFromSDK() async {
    try {
      final visitor = Flagship.getCurrentVisitor();
      if (visitor != null) {
        // Fetch fresh flags from SDK
        await visitor.fetchFlags();
        print('✅ Flags fetched from SDK successfully');

        // Update UI with new values
        _updateFlagValues();

        // Show notification if context is available
        if (mounted) {
          ScaffoldMessenger.of(context).showSnackBar(
            const SnackBar(
              content: Text('🔄 Flags automatically refreshed'),
              backgroundColor: Colors.green,
              duration: Duration(seconds: 2),
            ),
          );
        }
      }
    } catch (e) {
      print('❌ Error fetching flags from SDK: $e');
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        leading: IconButton(
          onPressed: _toggleQAAssistant,
          icon: Icon(
            _qaAssistant?.isOverlayVisible ?? false
                ? Icons.visibility_off
                : Icons.bug_report,
          ),
          tooltip: _qaAssistant?.isOverlayVisible ?? false
              ? 'Hide QA Assistant'
              : 'Show QA Assistant',
        ),
        title: const Text('ABTasty QA Demo'),
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        actions: [
          IconButton(
            onPressed: () async {
              await Flagship.getCurrentVisitor()?.fetchFlags();
              _refreshFlags();
            },
            icon: const Icon(Icons.sync),
            tooltip: 'Fetch Flags',
          ),
          IconButton(
            onPressed: _refreshFlags,
            icon: const Icon(Icons.refresh),
            tooltip: 'Refresh Flags',
          ),
        ],
      ),
      body: _buildOverviewTab(),
    );
  }

  Widget _buildOverviewTab() {
    return SingleChildScrollView(
      padding: const EdgeInsets.all(16),
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.stretch,
        children: [
          const SizedBox(height: 10),
          // Flagship Flag Display
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.start,
                children: [
                  Row(
                    children: [
                      Icon(Icons.flag, color: Theme.of(context).primaryColor),
                      const SizedBox(width: 8),
                      const Text(
                        'Flagship Flags',
                        style: TextStyle(
                          fontSize: 18,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 16),
                  _buildFlagRow('btnTitle - Login A/B', _btnTitleValue),
                  const Divider(),
                  _buildFlagRow('btnColor - Login A/B', _btnColorValue),
                  const Divider(),
                  _buildFlagRow('payKey1 - Payment', _flag1Value),
                  const Divider(),
                  _buildFlagRow('payKey2 - Payment', _flag2Value.toString()),
                  const Divider(),
                  _buildFlagRow('rejectedKey', _key1Value),
                  const Divider(),
                  _buildFlagRow('Delivery cost - VIP Mode', _vipValue)
                ],
              ),
            ),
          ),

          const SizedBox(height: 20),

          // Send Screen Event Button
          ElevatedButton.icon(
            onPressed: _sendEvents,
            icon: const Icon(Icons.send),
            label: const Text('Send Events'),
            style: ElevatedButton.styleFrom(
              padding: const EdgeInsets.all(16),
              backgroundColor: Colors.blue,
              foregroundColor: Colors.white,
            ),
          ),
          const SizedBox(height: 20),

          // VIP Mode Toggle
          Card(
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Row(
                mainAxisAlignment: MainAxisAlignment.spaceBetween,
                children: [
                  Column(
                    crossAxisAlignment: CrossAxisAlignment.start,
                    children: [
                      const Text(
                        'VIP Mode',
                        style: TextStyle(
                          fontSize: 16,
                          fontWeight: FontWeight.bold,
                        ),
                      ),
                      const SizedBox(height: 4),
                      Text(
                        _isVIPMode ? 'Enabled' : 'Disabled',
                        style: TextStyle(
                          fontSize: 12,
                          color: _isVIPMode ? Colors.green : Colors.grey,
                        ),
                      ),
                    ],
                  ),
                  Switch(
                    value: _isVIPMode,
                    onChanged: (bool newValue) async {
                      setState(() {
                        _isVIPMode = newValue;
                      });

                      // Update context
                      Flagship.getCurrentVisitor()
                          ?.updateContext("isVip", newValue);

                      // Fetch flags with the new context
                      await Flagship.getCurrentVisitor()?.fetchFlags();

                      // Update UI
                      _updateFlagValues();

                      // Show feedback
                      if (mounted) {
                        ScaffoldMessenger.of(context).showSnackBar(
                          SnackBar(
                            content: Text(
                              newValue
                                  ? '✅ VIP Mode enabled'
                                  : '❌ VIP Mode disabled',
                            ),
                            backgroundColor:
                                newValue ? Colors.green : Colors.orange,
                            duration: const Duration(seconds: 2),
                          ),
                        );
                      }
                    },
                  ),
                ],
              ),
            ),
          )
        ],
      ),
    );
  }

  void _sendEvents() {
    try {
      final visitor = Flagship.getCurrentVisitor();
      if (visitor != null) {
        // Send a screen hit
        visitor.sendHit(Screen(location: "HomeScreen"));

        // Send Event
        visitor.sendHit(
          Event(action: "eventQA", category: EventCategory.Action_Tracking),
        );

        visitor.sendHit(
          Event(action: "eventQA", category: EventCategory.User_Engagement),
        );

        // Send Transaction Event
        visitor.sendHit(
          Transaction(
            transactionId: "transactionId",
            affiliation: "affiliationQA",
          ),
        );

        ScaffoldMessenger.of(context).showSnackBar(
          const SnackBar(
            content: Text('📤 Events sent successfully!'),
            backgroundColor: Colors.green,
            duration: Duration(seconds: 2),
          ),
        );

        print('✅ Events sent successfully!');
      } else {
        throw Exception('Visitor not initialized');
      }
    } catch (e) {
      print('❌ Error sending events: $e');
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(
          content: Text('Error sending events: $e'),
          backgroundColor: Colors.red,
          duration: const Duration(seconds: 3),
        ),
      );
    }
  }

  Widget _buildFlagRow(String flagName, String flagValue) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8),
      child: Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween,
        children: [
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.start,
              children: [
                Text(
                  'Flag Name:',
                  style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                ),
                const SizedBox(height: 4),
                Text(
                  flagName,
                  style: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: Colors.blue[700],
                  ),
                ),
              ],
            ),
          ),
          Container(width: 1, height: 40, color: Colors.grey[300]),
          Expanded(
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.end,
              children: [
                Text(
                  'Flag Value:',
                  style: TextStyle(fontSize: 12, color: Colors.grey[600]),
                ),
                const SizedBox(height: 4),
                Text(
                  flagValue,
                  style: TextStyle(
                    fontSize: 16,
                    fontWeight: FontWeight.bold,
                    color: Colors.green[700],
                  ),
                  textAlign: TextAlign.right,
                ),
              ],
            ),
          ),
        ],
      ),
    );
  }
}
1
likes
130
points
42
downloads

Documentation

API reference

Publisher

verified publisherflagship.io

Weekly Downloads

Flutter package providing quality assurance tools and utilities for ABTasty testing workflows.

Repository (GitHub)
View/report issues

License

unknown (license)

Dependencies

flagship, flutter, get_it, http, shared_preferences

More

Packages that depend on abtasty_qa_assistant