worker_bee 0.3.11 copy "worker_bee: ^0.3.11" to clipboard
worker_bee: ^0.3.11 copied to clipboard

A cross-platform isolated worker runtime for Dart Web, VM, and Flutter.

example/lib/main.dart

import 'package:flutter/material.dart';
import 'package:worker_bee/worker_bee.dart';
import 'models/echo_message.dart';
import 'workers/echo_worker.dart';

// Conditional import: web gets the real JS interop, other platforms get a no-op stub.
import 'js_interop_stub.dart'
    if (dart.library.js_interop) 'js_interop_web.dart';

// Global key to access the demo state
final GlobalKey<_EchoWorkerDemoState> _demoKey =
    GlobalKey<_EchoWorkerDemoState>();

void main() {
  setupJsInterop(_demoKey);
  runApp(const WorkerBeeExampleApp());
}

/// Main application widget
class WorkerBeeExampleApp extends StatelessWidget {
  const WorkerBeeExampleApp({super.key});

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Worker Bee Example',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: EchoWorkerDemo(key: _demoKey),
    );
  }
}

/// Demo page showing EchoWorker in action
class EchoWorkerDemo extends StatefulWidget {
  const EchoWorkerDemo({super.key});

  @override
  State<EchoWorkerDemo> createState() => _EchoWorkerDemoState();
}

class _EchoWorkerDemoState extends State<EchoWorkerDemo> {
  EchoWorker? _worker;
  final _messageController = TextEditingController(text: '12345');
  final _messages = <String>[];
  final _logs = <String>[];
  bool _isWorkerRunning = false;
  int _delayMs = 0;

  // ── Public API used by JS interop (via dynamic dispatch) ──

  /// Called from JS interop to start the worker.
  Future<void> startWorkerFromJs() => _startWorker();

  /// Called from JS interop to stop the worker.
  void stopWorkerFromJs() => _stopWorker();

  /// Called from JS interop to send a message.
  void sendMessageFromJs() => _sendMessage();

  /// Called from JS interop to set the message text field.
  void setMessageText(String text) {
    _messageController.text = text;
  }

  /// Called from JS interop to get all log lines.
  String getLogsText() => _logs.join('\n');

  /// Called from JS interop to get all message lines.
  String getMessagesText() => _messages.join('\n');

  // ── Private implementation ──

  @override
  void dispose() {
    _worker?.close();
    _messageController.dispose();
    super.dispose();
  }

  Future<void> _startWorker() async {
    if (_isWorkerRunning) return;

    setState(() {
      _messages.clear();
      _logs.clear();
      _messages.add('🚀 Starting worker...');
    });

    try {
      _worker = EchoWorker.create();

      // Listen to responses from the worker
      _worker!.stream.listen(
        (response) {
          setState(() {
            _messages.add('✅ Echoed: "${response.echoedMessage}"');
            _messages.add('   🔄 Reversed: "${response.reversedMessage}"');
            _messages.add('   📊 Message #${response.messageCount}');
            _messages.add(
              '   ⏰ Processed at: ${_formatTime(response.processedAt)}',
            );
          });
        },
        onError: (Object error, StackTrace stackTrace) {
          setState(() {
            _messages.add('❌ Error: $error');
          });
        },
      );

      // Listen to logs from the worker
      _worker!.logs.listen((log) {
        setState(() {
          final icon = _getLogIcon(log.level);
          var logLine =
              '$icon [${log.level.name.toUpperCase()}] ${log.message}';
          if (log.error != null) {
            logLine += '\n   Error: ${log.error}';
          }
          if (log.stackTrace != null) {
            logLine +=
                '\n   StackTrace:\n${log.stackTrace.toString().split('\n').map((l) => '      $l').join('\n')}';
          }
          _logs.add(logLine);
        });
      });

      // Spawn the worker
      await _worker!.spawn();

      setState(() {
        _isWorkerRunning = true;
        _messages.add('✅ Worker started successfully!');
        _messages.add('');
      });
    } catch (e) {
      setState(() {
        _messages.add('❌ Failed to start worker: $e');
        _isWorkerRunning = false;
      });
    }
  }

  Future<void> _stopWorker() async {
    if (!_isWorkerRunning || _worker == null) return;

    setState(() {
      _messages.add('');
      _messages.add('🛑 Stopping worker...');
    });

    try {
      await _worker!.close();
      setState(() {
        _isWorkerRunning = false;
        _messages.add('✅ Worker stopped successfully!');
      });
    } catch (e) {
      setState(() {
        _messages.add('❌ Error stopping worker: $e');
      });
    }
  }

  void _sendMessage() {
    if (!_isWorkerRunning || _worker == null) {
      setState(() {
        _messages.add('⚠️ Worker is not running!');
      });
      return;
    }

    final text = _messageController.text.trim();
    if (text.isEmpty) return;

    setState(() {
      _messages.add('');
      _messages.add('📤 Sending: "$text"');
    });

    final message = EchoMessage(
      (b) => b
        ..message = text
        ..delayMs = _delayMs > 0 ? _delayMs : null,
    );

    _worker!.add(message);
    _messageController.clear();
  }

  String _formatTime(DateTime time) {
    return '${time.hour.toString().padLeft(2, '0')}:'
        '${time.minute.toString().padLeft(2, '0')}:'
        '${time.second.toString().padLeft(2, '0')}.'
        '${time.millisecond.toString().padLeft(3, '0')}';
  }

  String _getLogIcon(LogLevel level) {
    switch (level) {
      case LogLevel.none:
        return '⚪';
      case LogLevel.verbose:
        return '🔍';
      case LogLevel.debug:
        return '🐛';
      case LogLevel.info:
        return 'ℹ️';
      case LogLevel.warn:
        return '⚠️';
      case LogLevel.error:
        return '❌';
    }
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: const Text('Worker Bee Example'),
      ),
      body: Column(
        children: [
          // Control Panel
          Card(
            margin: const EdgeInsets.all(16),
            child: Padding(
              padding: const EdgeInsets.all(16),
              child: Column(
                crossAxisAlignment: CrossAxisAlignment.stretch,
                children: [
                  Text(
                    'Worker Control',
                    style: Theme.of(context).textTheme.titleLarge,
                  ),
                  const SizedBox(height: 16),
                  Row(
                    children: [
                      Expanded(
                        child: Semantics(
                          identifier: 'start-worker-button',
                          child: ElevatedButton.icon(
                            key: const ValueKey('start-worker-button'),
                            onPressed: _isWorkerRunning ? null : _startWorker,
                            icon: const Icon(Icons.play_arrow),
                            label: const Text('Start Worker'),
                          ),
                        ),
                      ),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Semantics(
                          identifier: 'stop-worker-button',
                          child: ElevatedButton.icon(
                            key: const ValueKey('stop-worker-button'),
                            onPressed: _isWorkerRunning ? _stopWorker : null,
                            icon: const Icon(Icons.stop),
                            label: const Text('Stop Worker'),
                            style: ElevatedButton.styleFrom(
                              backgroundColor: Colors.red[100],
                              foregroundColor: Colors.red[900],
                            ),
                          ),
                        ),
                      ),
                    ],
                  ),
                  const SizedBox(height: 16),
                  Row(
                    children: [
                      const Text('Delay (ms):'),
                      const SizedBox(width: 8),
                      Expanded(
                        child: Slider(
                          value: _delayMs.toDouble(),
                          min: 0,
                          max: 2000,
                          divisions: 20,
                          label: _delayMs.toString(),
                          onChanged: (value) {
                            setState(() {
                              _delayMs = value.toInt();
                            });
                          },
                        ),
                      ),
                      SizedBox(
                        width: 60,
                        child: Text('$_delayMs ms', textAlign: TextAlign.right),
                      ),
                    ],
                  ),
                ],
              ),
            ),
          ),

          // Message Input
          Padding(
            padding: const EdgeInsets.symmetric(horizontal: 16),
            child: Row(
              children: [
                Expanded(
                  child: Semantics(
                    identifier: 'message-input',
                    child: TextField(
                      key: const ValueKey('message-input'),
                      controller: _messageController,
                      decoration: const InputDecoration(
                        labelText: 'Message to echo',
                        border: OutlineInputBorder(),
                      ),
                      onSubmitted: (_) => _sendMessage(),
                      enabled: _isWorkerRunning,
                    ),
                  ),
                ),
                const SizedBox(width: 8),
                Semantics(
                  identifier: 'send-button',
                  child: IconButton(
                    key: const ValueKey('send-button'),
                    onPressed: _isWorkerRunning ? _sendMessage : null,
                    icon: const Icon(Icons.send),
                    style: IconButton.styleFrom(
                      backgroundColor: Theme.of(context).colorScheme.primary,
                      foregroundColor: Theme.of(context).colorScheme.onPrimary,
                    ),
                  ),
                ),
              ],
            ),
          ),

          // Tabs for Messages and Logs
          Expanded(
            child: DefaultTabController(
              length: 2,
              initialIndex: 1,
              child: Column(
                children: [
                  const TabBar(
                    tabs: [
                      Tab(text: 'Messages', icon: Icon(Icons.message)),
                      Tab(text: 'Worker Logs', icon: Icon(Icons.bug_report)),
                    ],
                  ),
                  Expanded(
                    child: TabBarView(
                      children: [
                        _buildMessageList(_messages, 'messages-list'),
                        _buildMessageList(_logs, 'logs-list'),
                      ],
                    ),
                  ),
                ],
              ),
            ),
          ),
        ],
      ),
    );
  }

  Widget _buildMessageList(List<String> items, String identifier) {
    if (items.isEmpty) {
      return Semantics(
        identifier: identifier,
        child: Center(
          key: ValueKey(identifier),
          child: const Text('No messages yet'),
        ),
      );
    }

    return Semantics(
      identifier: identifier,
      child: SelectionArea(
        child: ListView.builder(
          key: ValueKey(identifier),
          padding: const EdgeInsets.all(16),
          itemCount: items.length,
          itemBuilder: (context, index) {
            return Padding(
              padding: const EdgeInsets.only(bottom: 4),
              child: Text(
                items[index],
                style: const TextStyle(fontFamily: 'monospace', fontSize: 12),
              ),
            );
          },
        ),
      ),
    );
  }
}
2
likes
150
points
95.5k
downloads

Documentation

API reference

Publisher

verified publisheraws-amplify.com

Weekly Downloads

A cross-platform isolated worker runtime for Dart Web, VM, and Flutter.

License

Apache-2.0 (license)

Dependencies

async, aws_common, built_collection, built_value, collection, meta, path, stack_trace, stream_channel, stream_transform, web

More

Packages that depend on worker_bee