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

Isolate management for Flutter with task cancellation, zero-copy transfer, priority queue, progress tracking, and auto-dispose.

example/lib/example.dart

import 'dart:isolate';
import 'dart:typed_data';
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:isolate_kit/isolate_kit.dart';
import 'package:crypto/crypto.dart';
import 'dart:async';

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

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'IsolateKit Demo',
      theme: ThemeData(
        colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
        useMaterial3: true,
      ),
      home: const MyHomePage(title: 'IsolateKit Demo'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  const MyHomePage({super.key, required this.title});

  final String title;

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  late final IsolateKit _isolateKit;
  String _result = 'Ready';
  double _progress = 0.0;
  String _status = 'Ready';
  bool _isProcessing = false;
  TaskHandle? _currentTask;

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

  void _initializeIsolateKit() {
    final registry = getTaskRegistry();

    _isolateKit = IsolateKit.instance(
      name: 'demo',
      taskRegistry: registry,
      maxConcurrentTasks: 2,
      usePool: true,
      poolSize: 2,
      debugName: 'DemoIsolateKit',
    );

    _isolateKit.warmup();
  }

  void _runTask(IsolateTask task, String taskName) async {
    if (_isProcessing) return;

    setState(() {
      _isProcessing = true;
      _status = 'Starting $taskName...';
      _progress = 0.0;
      _result = 'Processing...';
    });

    _currentTask = _isolateKit.runTask(
      task,
      timeout: const Duration(seconds: 60),
      onProgress: (progress) {
        setState(() {
          _progress = progress.percentage;
          _status = progress.message ?? 'Processing...';
        });
      },
    );

    try {
      final result = await _currentTask!.future;
      setState(() {
        _result = result.toString();
        _status = '$taskName completed!';
        _progress = 1.0;
        _isProcessing = false;
      });
    } on TaskCancelledException {
      setState(() {
        _status = 'Cancelled';
        _result = 'Task was cancelled';
        _isProcessing = false;
      });
    } on TaskTimeoutException {
      setState(() {
        _status = 'Timeout!';
        _result = 'Task timed out';
        _isProcessing = false;
      });
    } catch (e) {
      setState(() {
        _status = 'Error: $e';
        _result = 'Error occurred';
        _isProcessing = false;
      });
    }
  }

  void _runImageGrayscale() {
    // Create a dummy 1920x1080 RGB image
    final imageData = Uint8List(1920 * 1080 * 3);
    for (int i = 0; i < imageData.length; i++) {
      imageData[i] = (i % 256);
    }

    final task = ImageGrayscaleTask({'width': 1920, 'height': 1080}, imageData);

    _runTask(task, 'Image Grayscale');
  }

  void _runImageResize() {
    // Create a dummy 1920x1080 RGBA image
    final imageData = Uint8List(1920 * 1080 * 4);
    for (int i = 0; i < imageData.length; i++) {
      imageData[i] = (i % 256);
    }

    final task = ImageResizeTask({
      'sourceWidth': 1920,
      'sourceHeight': 1080,
      'targetWidth': 640,
      'targetHeight': 360,
    }, imageData);

    _runTask(task, 'Image Resize');
  }

  void _runCsvParser() {
    // DON'T generate CSV in UI thread!
    // Pass only parameters, generate inside isolate
    final task = CsvChunkParserTask({
      'rows': 500000, // Generate 500k rows in isolate
      'chunkSize': 10000,
    });

    _runTask(task, 'CSV Parser (500k rows)');
  }

  void _runFileHash() {
    // Generate 200MB file for real heavy computation
    final sizeMB = 200;
    final fileData = Uint8List(sizeMB * 1024 * 1024);
    for (int i = 0; i < fileData.length; i++) {
      fileData[i] = (i % 256);
    }

    final task = FileChunkHashTask({
      'chunkSize': 4 * 1024 * 1024, // 4MB chunks
      'passes': 3, // 3 full passes
    }, fileData);

    _runTask(task, 'SHA-256 File Hash (Real Crypto)');
  }

  void _runBatchNormalize() {
    // Generate dummy data
    final data = List.generate(100000, (i) => (i % 1000).toDouble());

    final task = BatchNormalizeTask({'batchSize': 1000, 'data': data});

    _runTask(task, 'Batch Normalize');
  }

  void _runFibonacci() {
    final task = FibonacciTask({'n': 1000000, 'repeats': 200});
    _runTask(task, 'Matrix Fibonacci (Heavy)');
  }

  void _cancelTask() {
    _currentTask?.cancel();
  }

  void _showStatus() {
    final status = _isolateKit.getStatus();
    showDialog(
      context: context,
      builder: (context) => AlertDialog(
        title: const Text('IsolateKit Status'),
        content: SingleChildScrollView(
          child: Column(
            crossAxisAlignment: CrossAxisAlignment.start,
            mainAxisSize: MainAxisSize.min,
            children: [
              Text('Active Tasks: ${status['activeTasks']}'),
              Text('Queued Tasks: ${status['queuedTasks']}'),
              Text('Total Completed: ${status['totalCompleted']}'),
              Text('Warmed Up: ${status['warmedUp']}'),
              Text('Use Pool: ${status['usePool']}'),
              const SizedBox(height: 8),
              Text(
                'Pool Status:',
                style: Theme.of(context).textTheme.titleSmall,
              ),
              Text('  Workers: ${status['poolStatus']?['poolSize'] ?? 'N/A'}'),
              Text(
                '  Total Active: ${status['poolStatus']?['totalActive'] ?? 'N/A'}',
              ),
            ],
          ),
        ),
        actions: [
          TextButton(
            onPressed: () => Navigator.pop(context),
            child: const Text('Close'),
          ),
        ],
      ),
    );
  }

  @override
  void dispose() {
    _isolateKit.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: Theme.of(context).colorScheme.inversePrimary,
        title: Text(widget.title),
        actions: [
          IconButton(
            icon: const Icon(Icons.info_outline),
            onPressed: _showStatus,
            tooltip: 'Show Status',
          ),
        ],
      ),
      body: Center(
        child: SingleChildScrollView(
          padding: const EdgeInsets.all(24.0),
          child: Column(
            mainAxisAlignment: MainAxisAlignment.center,
            children: <Widget>[
              const Text(
                'Heavy Background Tasks Demo',
                style: TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
              ),
              const SizedBox(height: 16),

              // Result display
              Container(
                width: double.infinity,
                padding: const EdgeInsets.all(16),
                decoration: BoxDecoration(
                  color: Theme.of(context).colorScheme.primaryContainer,
                  borderRadius: BorderRadius.circular(12),
                ),
                child: Column(
                  children: [
                    const Text('Result:', style: TextStyle(fontSize: 14)),
                    const SizedBox(height: 8),
                    Text(
                      _result,
                      style: const TextStyle(
                        fontSize: 16,
                        fontWeight: FontWeight.bold,
                      ),
                      textAlign: TextAlign.center,
                    ),
                  ],
                ),
              ),

              const SizedBox(height: 24),

              // Progress indicator
              if (_isProcessing) ...[
                LinearProgressIndicator(value: _progress),
                const SizedBox(height: 8),
              ],

              // Status text
              Text(
                _status,
                style: TextStyle(
                  fontSize: 14,
                  color: Theme.of(context).colorScheme.secondary,
                ),
                textAlign: TextAlign.center,
              ),

              const SizedBox(height: 32),

              // Math Tasks
              _buildSectionTitle(context, 'Math Tasks'),
              Wrap(
                spacing: 8,
                runSpacing: 8,
                alignment: WrapAlignment.center,
                children: [
                  _buildTaskButton('Fibonacci', Icons.functions, _runFibonacci),
                  _buildTaskButton(
                    'Normalize Data',
                    Icons.analytics,
                    _runBatchNormalize,
                  ),
                ],
              ),

              const SizedBox(height: 16),

              // Image Processing Tasks
              _buildSectionTitle(context, 'Image Processing'),
              Wrap(
                spacing: 8,
                runSpacing: 8,
                alignment: WrapAlignment.center,
                children: [
                  _buildTaskButton(
                    'Grayscale',
                    Icons.image,
                    _runImageGrayscale,
                  ),
                  _buildTaskButton(
                    'Resize',
                    Icons.photo_size_select_large,
                    _runImageResize,
                  ),
                ],
              ),

              const SizedBox(height: 16),

              // Data Processing Tasks
              _buildSectionTitle(context, 'Data Processing'),
              Wrap(
                spacing: 8,
                runSpacing: 8,
                alignment: WrapAlignment.center,
                children: [
                  _buildTaskButton(
                    'Parse CSV',
                    Icons.table_chart,
                    _runCsvParser,
                  ),
                  _buildTaskButton(
                    'Hash File',
                    Icons.fingerprint,
                    _runFileHash,
                  ),
                ],
              ),

              const SizedBox(height: 24),

              if (_isProcessing)
                ElevatedButton.icon(
                  onPressed: _cancelTask,
                  icon: const Icon(Icons.cancel),
                  label: const Text('Cancel Task'),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.red,
                    foregroundColor: Colors.white,
                  ),
                ),

              const SizedBox(height: 24),

              const Text(
                'Monitor the indicators below while the task is in progress!\nUI stays responsive.',
                textAlign: TextAlign.center,
                style: TextStyle(fontSize: 12, fontStyle: FontStyle.italic),
              ),

              const SizedBox(height: 32),

              CircularProgressIndicator(),
            ],
          ),
        ),
      ),
    );
  }

  Widget _buildSectionTitle(BuildContext context, String title) {
    return Padding(
      padding: const EdgeInsets.symmetric(vertical: 8.0),
      child: Text(
        title,
        style: Theme.of(
          context,
        ).textTheme.titleMedium?.copyWith(fontWeight: FontWeight.bold),
      ),
    );
  }

  Widget _buildTaskButton(String label, IconData icon, VoidCallback onPressed) {
    return ElevatedButton.icon(
      onPressed: _isProcessing ? null : onPressed,
      icon: Icon(icon, size: 18),
      label: Text(label),
      style: ElevatedButton.styleFrom(
        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),
      ),
    );
  }
}

// ======================= GLOBAL TASK REGISTRY =======================

IsolateTaskRegistry getTaskRegistry() {
  final registry = IsolateTaskRegistry();

  registry.register<FibonacciTask>('FibonacciTask', _createFibonacciTask);

  registry.register<ImageGrayscaleTask>(
    'ImageGrayscaleTask',
    _createImageGrayscaleTask,
  );

  registry.register<ImageResizeTask>('ImageResizeTask', _createImageResizeTask);

  registry.register<CsvChunkParserTask>(
    'CsvChunkParserTask',
    _createCsvChunkParserTask,
  );

  registry.register<FileChunkHashTask>(
    'FileChunkHashTask',
    _createFileChunkHashTask,
  );

  registry.register<BatchNormalizeTask>(
    'BatchNormalizeTask',
    _createBatchNormalizeTask,
  );

  return registry;
}

// Factory functions
FibonacciTask _createFibonacciTask(
  Map<String, dynamic> payload,
  List<TransferableTypedData>? transferables,
) {
  return FibonacciTask(payload);
}

ImageGrayscaleTask _createImageGrayscaleTask(
  Map<String, dynamic> payload,
  List<TransferableTypedData>? transferables,
) {
  final imageData = transferables != null && transferables.isNotEmpty
      ? transferables[0].materialize().asUint8List()
      : Uint8List(0);
  return ImageGrayscaleTask(payload, imageData);
}

ImageResizeTask _createImageResizeTask(
  Map<String, dynamic> payload,
  List<TransferableTypedData>? transferables,
) {
  final imageData = transferables != null && transferables.isNotEmpty
      ? transferables[0].materialize().asUint8List()
      : Uint8List(0);
  return ImageResizeTask(payload, imageData);
}

CsvChunkParserTask _createCsvChunkParserTask(
  Map<String, dynamic> payload,
  List<TransferableTypedData>? transferables,
) {
  return CsvChunkParserTask(payload);
}

FileChunkHashTask _createFileChunkHashTask(
  Map<String, dynamic> payload,
  List<TransferableTypedData>? transferables,
) {
  final fileData = transferables != null && transferables.isNotEmpty
      ? transferables[0].materialize().asUint8List()
      : Uint8List(0);
  return FileChunkHashTask(payload, fileData);
}

BatchNormalizeTask _createBatchNormalizeTask(
  Map<String, dynamic> payload,
  List<TransferableTypedData>? transferables,
) {
  return BatchNormalizeTask(payload);
}

// ======================= TASK IMPLEMENTATIONS =======================

/// Fibonacci calculation task
/// Matrix-based Fibonacci - O(log n) but repeated many times for heavy load
class FibonacciTask extends IsolateTask<Map<String, dynamic>, int> {
  final Map<String, dynamic> _payload;
  FibonacciTask(this._payload);

  @override
  Map<String, dynamic> get command => _payload;
  @override
  Map<String, dynamic> get payload => _payload;
  @override
  String get taskType => 'FibonacciTask';

  @override
  Future<int> execute({
    void Function(TaskProgress)? sendProgress,
    CancellationToken? cancellationToken,
  }) async {
    final n = _payload['n'] as int? ?? 1000000; // Very large number
    final repeats = _payload['repeats'] as int? ?? 100; // Repeat calculation

    sendProgress?.call(
      TaskProgress(
        percentage: 0.0,
        message: 'Starting matrix exponentiation ($repeats passes)...',
      ),
    );

    // Matrix multiplication helper
    List<List<int>> multiply(List<List<int>> a, List<List<int>> b) {
      return [
        [
          a[0][0] * b[0][0] + a[0][1] * b[1][0],
          a[0][0] * b[0][1] + a[0][1] * b[1][1],
        ],
        [
          a[1][0] * b[0][0] + a[1][1] * b[1][0],
          a[1][0] * b[0][1] + a[1][1] * b[1][1],
        ],
      ];
    }

    // Matrix power using binary exponentiation
    List<List<int>> matrixPower(List<List<int>> base, int exp) {
      var result = [
        [1, 0],
        [0, 1],
      ]; // Identity matrix
      var power = base;

      while (exp > 0) {
        cancellationToken?.throwIfCancelled();
        if (exp & 1 == 1) {
          result = multiply(result, power);
        }
        power = multiply(power, power);
        exp >>= 1;
      }
      return result;
    }

    final baseMatrix = [
      [1, 1],
      [1, 0],
    ];
    int lastResult = 0;

    // Perform multiple passes to create sustained CPU load
    for (int i = 0; i < repeats; i++) {
      cancellationToken?.throwIfCancelled();

      final result = matrixPower(baseMatrix, n ~/ repeats);
      lastResult = result[0][0];

      sendProgress?.call(
        TaskProgress(
          percentage: (i + 1) / repeats,
          message: 'Matrix pass ${i + 1}/$repeats',
        ),
      );
    }

    sendProgress?.call(
      TaskProgress(percentage: 1.0, message: 'Matrix exponentiation complete!'),
    );

    return lastResult;
  }
}

/// Image Grayscale Conversion Task
class ImageGrayscaleTask extends IsolateTask<Uint8List, String> {
  final Map<String, dynamic> _payload;
  final Uint8List _imageData;

  ImageGrayscaleTask(this._payload, this._imageData);

  @override
  Uint8List get command => _imageData;
  @override
  Map<String, dynamic> get payload => _payload;
  @override
  String get taskType => 'ImageGrayscaleTask';
  @override
  List<TransferableTypedData>? get transferables => [
    TransferableTypedData.fromList([_imageData]),
  ];

  @override
  Future<String> execute({
    void Function(TaskProgress)? sendProgress,
    CancellationToken? cancellationToken,
  }) async {
    final width = _payload['width'] as int;
    final height = _payload['height'] as int;
    final totalPixels = width * height;

    // Step 1: Grayscale conversion with gamma correction
    final grayscale = Uint8List(totalPixels);

    for (int i = 0; i < totalPixels; i++) {
      cancellationToken?.throwIfCancelled();

      final pixelIndex = i * 3;
      final r = _imageData[pixelIndex] / 255.0;
      final g = _imageData[pixelIndex + 1] / 255.0;
      final b = _imageData[pixelIndex + 2] / 255.0;

      // Apply gamma correction (heavy floating-point ops)
      final gamma = 2.2;
      final rLinear = math.pow(r, gamma);
      final gLinear = math.pow(g, gamma);
      final bLinear = math.pow(b, gamma);

      // Weighted grayscale
      final gray = 0.299 * rLinear + 0.587 * gLinear + 0.114 * bLinear;
      grayscale[i] = (gray * 255).clamp(0, 255).toInt();

      if (i % 50000 == 0) {
        sendProgress?.call(
          TaskProgress(
            percentage: i / totalPixels * 0.6,
            message:
                'Grayscale with gamma: ${(i / totalPixels * 100).toStringAsFixed(1)}%',
          ),
        );
      }
    }

    // Step 2: Apply Gaussian blur (3x3 convolution - HEAVY!)
    final blurred = Uint8List(totalPixels);
    final kernel = [1, 2, 1, 2, 4, 2, 1, 2, 1];
    final kernelSum = 16;

    for (int y = 1; y < height - 1; y++) {
      cancellationToken?.throwIfCancelled();

      for (int x = 1; x < width - 1; x++) {
        int sum = 0;

        // Apply 3x3 kernel
        for (int ky = -1; ky <= 1; ky++) {
          for (int kx = -1; kx <= 1; kx++) {
            final idx = (y + ky) * width + (x + kx);
            final kernelIdx = (ky + 1) * 3 + (kx + 1);
            sum += grayscale[idx] * kernel[kernelIdx];
          }
        }

        blurred[y * width + x] = (sum ~/ kernelSum);
      }

      if (y % 50 == 0) {
        sendProgress?.call(
          TaskProgress(
            percentage: 0.6 + (y / height * 0.4),
            message: 'Applying blur: row $y/$height',
          ),
        );
      }
    }

    sendProgress?.call(
      TaskProgress(percentage: 1.0, message: 'Grayscale + blur complete!'),
    );

    return 'Processed ${width}x$height with gamma correction + Gaussian blur (${blurred.length} bytes)';
  }
}

/// Image Resize Task (Nearest Neighbor)
class ImageResizeTask extends IsolateTask<Uint8List, String> {
  final Map<String, dynamic> _payload;
  final Uint8List _imageData;

  ImageResizeTask(this._payload, this._imageData);

  @override
  Uint8List get command => _imageData;

  @override
  Map<String, dynamic> get payload => _payload;

  @override
  String get taskType => 'ImageResizeTask';

  @override
  List<TransferableTypedData>? get transferables => [
    TransferableTypedData.fromList([_imageData]),
  ];

  @override
  Future<String> execute({
    void Function(TaskProgress)? sendProgress,
    CancellationToken? cancellationToken,
  }) async {
    final srcWidth = _payload['sourceWidth'] as int;
    final srcHeight = _payload['sourceHeight'] as int;
    final dstWidth = _payload['targetWidth'] as int;
    final dstHeight = _payload['targetHeight'] as int;

    sendProgress?.call(
      TaskProgress(percentage: 0.0, message: 'Resizing image...'),
    );

    final output = Uint8List(dstWidth * dstHeight * 4);
    final xRatio = srcWidth / dstWidth;
    final yRatio = srcHeight / dstHeight;

    for (int y = 0; y < dstHeight; y++) {
      cancellationToken?.throwIfCancelled();

      for (int x = 0; x < dstWidth; x++) {
        final srcX = (x * xRatio).floor();
        final srcY = (y * yRatio).floor();

        final srcIndex = (srcY * srcWidth + srcX) * 4;
        final dstIndex = (y * dstWidth + x) * 4;

        output[dstIndex] = _imageData[srcIndex];
        output[dstIndex + 1] = _imageData[srcIndex + 1];
        output[dstIndex + 2] = _imageData[srcIndex + 2];
        output[dstIndex + 3] = _imageData[srcIndex + 3];
      }

      if (y % 10 == 0) {
        sendProgress?.call(
          TaskProgress(
            percentage: y / dstHeight,
            message: 'Processing row $y/$dstHeight',
          ),
        );
      }
    }

    sendProgress?.call(
      TaskProgress(percentage: 1.0, message: 'Resize complete!'),
    );

    return 'Resized from ${srcWidth}x$srcHeight to ${dstWidth}x$dstHeight';
  }
}

/// CSV Chunk Parser Task
class CsvChunkParserTask extends IsolateTask<String, String> {
  final Map<String, dynamic> _payload;

  CsvChunkParserTask(this._payload);

  @override
  String get command => ''; // No command needed
  @override
  Map<String, dynamic> get payload => _payload;
  @override
  String get taskType => 'CsvChunkParserTask';

  @override
  Future<String> execute({
    void Function(TaskProgress)? sendProgress,
    CancellationToken? cancellationToken,
  }) async {
    final rows = _payload['rows'] as int? ?? 100000;
    final chunkSize = _payload['chunkSize'] as int;

    sendProgress?.call(
      TaskProgress(
        percentage: 0.0,
        message: 'Generating CSV data in isolate...',
      ),
    );

    // Generate CSV INSIDE isolate (not in UI!)
    final csvLines = <String>['id,name,age,score,email,city,country'];

    for (int i = 0; i < rows; i++) {
      cancellationToken?.throwIfCancelled();

      csvLines.add(
        '$i,User$i,${20 + (i % 50)},${i % 100},user$i@example.com,City${i % 100},Country${i % 20}',
      );

      if (i % chunkSize == 0 && i > 0) {
        sendProgress?.call(
          TaskProgress(
            percentage: i / rows * 0.5,
            message: 'Generated $i/$rows rows',
          ),
        );
      }
    }

    // Heavy parsing work
    final results = <Map<String, String>>[];
    final headers = csvLines[0].split(',');

    for (int i = 1; i < csvLines.length; i++) {
      cancellationToken?.throwIfCancelled();

      final values = csvLines[i].split(',');
      final row = <String, String>{};

      for (int j = 0; j < headers.length && j < values.length; j++) {
        row[headers[j]] = values[j];
      }

      results.add(row);

      if (i % chunkSize == 0) {
        sendProgress?.call(
          TaskProgress(
            percentage: 0.5 + (i / csvLines.length * 0.5),
            message: 'Parsed ${results.length}/${csvLines.length - 1} rows',
          ),
        );
      }
    }

    sendProgress?.call(
      TaskProgress(percentage: 1.0, message: 'CSV parsing complete!'),
    );

    return 'Parsed ${results.length} rows from CSV (${csvLines.length - 1} total)';
  }
}

/// File Chunk Hash Task
class FileChunkHashTask extends IsolateTask<Uint8List, String> {
  final Map<String, dynamic> _payload;
  final Uint8List _fileData;

  FileChunkHashTask(this._payload, this._fileData);

  @override
  Uint8List get command => _fileData;
  @override
  Map<String, dynamic> get payload => _payload;
  @override
  String get taskType => 'FileChunkHashTask';
  @override
  List<TransferableTypedData>? get transferables => [
    TransferableTypedData.fromList([_fileData]),
  ];

  @override
  Future<String> execute({
    void Function(TaskProgress)? sendProgress,
    CancellationToken? cancellationToken,
  }) async {
    final chunkSize = _payload['chunkSize'] as int;
    final passes = _payload['passes'] as int? ?? 3;
    final totalChunks = (_fileData.length / chunkSize).ceil();

    sendProgress?.call(
      TaskProgress(
        percentage: 0.0,
        message: 'Starting SHA-256 hashing ($passes passes)...',
      ),
    );

    final hashes = <String>[];

    // Perform multiple passes for heavier computation
    for (int pass = 0; pass < passes; pass++) {
      cancellationToken?.throwIfCancelled();

      // Simple approach: hash the entire data each pass
      // For chunked processing, we'll process in chunks but still compute full hash
      final chunks = <List<int>>[];

      for (int i = 0; i < totalChunks; i++) {
        cancellationToken?.throwIfCancelled();

        final start = i * chunkSize;
        final end = math.min(start + chunkSize, _fileData.length);
        chunks.add(_fileData.sublist(start, end));

        if (i % 10 == 0) {
          sendProgress?.call(
            TaskProgress(
              percentage: (pass + (i / totalChunks)) / passes,
              message: 'Pass ${pass + 1}/$passes - Chunk ${i + 1}/$totalChunks',
            ),
          );
        }
      }

      // Compute SHA-256 hash of all chunks combined
      final digest = sha256.convert(_fileData);
      hashes.add(digest.toString());

      sendProgress?.call(
        TaskProgress(
          percentage: (pass + 1) / passes,
          message: 'Pass ${pass + 1}/$passes completed',
        ),
      );
    }

    sendProgress?.call(
      TaskProgress(percentage: 1.0, message: 'SHA-256 hashing complete!'),
    );

    return 'SHA-256 hash (${_fileData.length} bytes, $passes passes):\n${hashes.first.substring(0, 32)}...';
  }
}

/// Batch Normalize Task
class BatchNormalizeTask extends IsolateTask<List<double>, String> {
  final Map<String, dynamic> _payload;

  BatchNormalizeTask(this._payload);

  @override
  List<double> get command => _payload['data'] as List<double>;

  @override
  Map<String, dynamic> get payload => _payload;

  @override
  String get taskType => 'BatchNormalizeTask';

  @override
  Future<String> execute({
    void Function(TaskProgress)? sendProgress,
    CancellationToken? cancellationToken,
  }) async {
    final data = _payload['data'] as List<double>;
    final batchSize = _payload['batchSize'] as int;
    final totalBatches = (data.length / batchSize).ceil();

    sendProgress?.call(
      TaskProgress(percentage: 0.0, message: 'Normalizing data...'),
    );

    final normalized = <double>[];

    for (int i = 0; i < totalBatches; i++) {
      cancellationToken?.throwIfCancelled();

      final start = i * batchSize;
      final end = math.min(start + batchSize, data.length);
      final batch = data.sublist(start, end);

      // Calculate mean
      final mean = batch.reduce((a, b) => a + b) / batch.length;

      // Calculate standard deviation
      final variance =
          batch.map((x) => math.pow(x - mean, 2)).reduce((a, b) => a + b) /
          batch.length;
      final stdDev = math.sqrt(variance);

      // Normalize
      for (final value in batch) {
        normalized.add((value - mean) / (stdDev + 1e-8));
      }

      sendProgress?.call(
        TaskProgress(
          percentage: (i + 1) / totalBatches,
          message: 'Normalized batch ${i + 1}/$totalBatches',
        ),
      );
    }

    sendProgress?.call(
      TaskProgress(percentage: 1.0, message: 'Normalization complete!'),
    );

    return 'Normalized ${normalized.length} values in $totalBatches batches (mean ≈ 0, std ≈ 1)';
  }
}
0
likes
160
points
136
downloads

Publisher

unverified uploader

Weekly Downloads

Isolate management for Flutter with task cancellation, zero-copy transfer, priority queue, progress tracking, and auto-dispose.

Repository (GitHub)
View/report issues

Topics

#isolate #threading #performance #background #parallel

Documentation

API reference

Funding

Consider supporting this project:


License

MIT (license)

Dependencies

collection, flutter, synchronized, uuid

More

Packages that depend on isolate_kit