Seamless Multiplatform Solutions
App solutions at any scale.
Delivering acceleration, flexibility, and cost efficiency.
App solutions at any scale.
Delivering acceleration, flexibility, and cost efficiency.
Through:
One of Flutter's unique advantages on the web is its single codebase for rich, pixel-perfect UI and consistent behavior across browsers and devices. Flutter uses its own rendering engine Skia rather than relying on the DOM, allowing it to bypass typical web limitations.
For fast, native-like performance due to hardware-accelerated rendering on the web, Flutter leverages its Skia engine to achieve consistent
for most animations and interactions, with some optimized experiences reaching on supported hardware and browsers (e.g., those with high-refresh-rate displays).Customizable and expressive designs goes beyond traditional HTML/CSS constraints, Hardware-accelerated rendering provides fast, native-like performance with smooth animations and transition, PWA (Progressive Web App) capabilities for offline support and native-like installation. These unique capabilities make Flutter ideal for creating modern, immersive web experiences.
Through:
Developing an app for N platforms with a common codebase in Dart/Flutter saves up to (1 - 1 / N) x 100 percent of the time and costs with equal resources, because separate apps for each platform do not have to be programmed.
For instance developing an app for iOS and Android saves up to (1 - 1 / 2) x 100 =
, for iOS, Android and Web up to (1 - 1 / 3) x 100 = of the time and costs needed to develop them separately.The developer experience and just-in-time (JIT) compilation of Flutter accelerate the app development process by up to
.Boost efficiency and speed with my over
ready-to-use assets, libs and services for common cases and functionalities.Through:
Flutter as an open-source project from Google comes with continuous development and the use of the latest concepts and technologies with over Flutter libraries available for free use, stars on GitHub, posts on Stackoverflow, members on Reddit, and more, ensures resources and provides efficient support for app development.
stable releases since 2018 as well as a large and dedicated community with overWith an enthusiastic and supportive roadmap for further platform development, the Flutter team plan to have four stable releases and 12 beta releases during 2024, mirroring their achievements in 2023, and integrate with the astonishing work of the Google's IDX team.
Through:
Flutter apps typically achieve startup times under
milliseconds, which is comparable to native apps. This is due to the AOT compilation and efficient rendering pipeline. In various benchmarks, Flutter has been shown to start in of the time it takes for comparable React Native apps due to its direct native code compilation.Thanks to its efficient use of Skia for rendering, the same graphics engine used by Google Chrome and Android, Flutter stays well within the limit of keeping rendering per frame below
milliseconds to maintain refresh rate, ensuring fluid UI interactions. Flutter's UI thread typically consumes less than milliseconds per frame, leaving ample headroom for complex animations and interactions without dropping frames. On devices that support higher refresh rates, Flutter can achieve up to , offering even smoother visual performance.Flutter applications tend to have optimized memory usage due to the efficient garbage collection in Dart. While exact memory usage can vary based on the app, Flutter's memory footprint is competitive with native applications. The CPU usage of Flutter apps is optimized through direct native code execution. Benchmarks show that Flutter apps often utilize less CPU compared to hybrid frameworks that rely on a JavaScript bridge, such as React Native.
The Flutter Gallery app, which showcases various Flutter widgets and layouts, scores consistently high on rendering benchmarks. For instance, it can render complex UIs at with minimal CPU and memory overhead.
Beautiful user interaction with animations and transitions is to be integrated into an existing website without altering a single line of code, and without the need for third parties or untrusted sources at runtime, fully meeting your compliance and privacy needs.
An app for Android and iOS is to be developed. With an assumed lifespan of 5 years and 20% annual costs for operation and support, the app implemented with Flutter only just reaches the initial costs of the native approach, whereas these have already doubled since implementation when the end-of-life is reached.
Save time and budget with integrated compliance as a service, leveraging
existing solutions to streamline processes, reduce complexity, and avoid
extended timelines. Boost efficiency and speed without re-creating functions
or adding extra payroll costs.
See examples below.
[ ... ]
A worker framework for seamlessly integrating with view states for executing tasks in separate isolates to prevent the UI from being blocked.
// Copyright (c) 2020, Gunther Hoppe.
// All rights reserved.
import 'dart:math';
import 'worker-api.dart';
import 'worker-utils.dart';
/// Represents the parameters for the worker.
class MyWorkerParams {
/// The count value for the worker.
final int count;
/// The add value for the worker.
final int add;
/// Constructor for initializing the parameters.
MyWorkerParams(this.count, this.add);
}
/// Represents the result produced by the worker.
class MyWorkerResult {
/// The count value in the result.
final int count;
/// Constructor for initializing the result.
MyWorkerResult(this.count);
}
/// Implementation of a worker that processes MyWorkerParams and produces MyWorkerResult.
class MyWorker extends Worker<MyWorkerParams, MyWorkerResult> {
/// Constructor for creating an instance of MyWorker.
MyWorker() : super(debugLabel: "MyWorker");
/// Getter for obtaining the static task associated with this worker.
@override
void Function(WorkerProcessInterface<MyWorkerParams, MyWorkerResult>) get staticTask => staticWorkerTask;
/// Static method representing the task performed by the worker.
static void staticWorkerTask(WorkerProcessInterface<MyWorkerParams, MyWorkerResult> process) async =>
process.onMessage((message) async => MyWorkerResult(message.count + message.add));
}
/// Execute [MyWorker] ten times
///
/// Output sample:
/// [log] result for 0 + 1 == 1
/// [log] result for 1 + 2 == 3
/// [log] result for 2 + 2 == 4
/// [log] result for 3 + 3 == 6
/// [log] result for 4 + 6 == 10
/// [log] result for 5 + 8 == 13
/// [log] result for 6 + 8 == 14
/// [log] result for 7 + 1 == 8
/// [log] result for 8 + 9 == 17
/// [log] result for 9 + 6 == 15
void main(List<String> args) {
Log.level = Log.info;
final worker = MyWorker();
for (int i = 0; i < 10; i++) {
final add = Random().nextInt(10);
worker.execute(MyWorkerParams(i, add)).then((value) {
Log.i("result for $i + $add == ${value.count}");
if (i == 9) {
worker.destroy();
}
});
}
}
// Copyright (c) 2020, Gunther Hoppe.
// All rights reserved.
import 'dart:async';
import 'worker-platform-stub.dart'
if (dart.library.io) 'worker-platform-native.dart'
if (dart.library.js) 'worker-platform-web.dart';
import 'worker-utils.dart';
/// Defines the interface for a WorkerProcess with generic parameters and results
abstract class WorkerProcessInterface<Tparams, Tresult> {
/// Method to handle incoming messages, taking a function that returns a future result
///
/// - [function] is the process function
void onMessage(Future<Tresult> Function(Tparams) function);
/// Method to handle sending results
///
/// - [result] is the process result provided by the process function
void onSend(Tresult result);
}
/// Defines the interface for a WorkerExecutor with generic parameters and results
abstract class WorkerExecutorInterface<Tparams, Tresult> {
/// Stream to listen to result events
Stream<Tresult> get stream;
/// Stream to listen to error events
Stream get error;
/// Method to execute the worker with given parameters
///
/// - [worker] is the worker to be executed
/// - [params] are the processing parameters
void execute(Worker<Tparams, Tresult> worker, Tparams params);
/// Method to teardown the worker, cleaning up resources
///
/// - [worker] is the worker to be torn down
Future<void> teardown(Worker<Tparams, Tresult> worker);
/// Method to setup the worker before execution
///
/// - [worker] is the worker to be set up
Future<void> setup(Worker<Tparams, Tresult> worker);
}
/// Represents a Worker with generic parameters and results
abstract class Worker<Tparams, Tresult> {
// Executor interface to manage worker execution
late final WorkerExecutorInterface<Tparams, Tresult> _executor;
// List of stream subscriptions to manage resource cleanup
final List<StreamSubscription> _subscriptions = [];
// Lock to manage concurrent access and execution
final Lock _lock = Lock();
// Optional debug label for logging and identification
final String? debugLabel;
// Average execution duration for performance measurement
Duration? avgExecutionDuration;
// Completer to manage the result of the worker execution
Completer<Tresult>? _completer;
// Start time of the worker execution for duration measurement
DateTime? _startTime;
/// Constructor initializing the worker with an optional debug label
///
/// - [debugLabel] is the label to be logged
Worker({this.debugLabel}) {
// Initialize the executor and set up stream listeners for result and error events
_executor = WorkerExecutor<Tparams, Tresult>();
_subscriptions.add(_executor.stream.listen((event) {
_lock.release();
_completer?.complete(event);
_completer = null;
_measureDuration();
}));
_subscriptions.add(_executor.error.listen((event) {
_lock.release();
_completer?.completeError(event);
_completer = null;
_measureDuration();
}));
}
/// Abstract getter to be implemented by subclasses to define the static task for the worker process
void Function(WorkerProcessInterface<Tparams, Tresult>) get staticTask;
/// Method to execute the worker with given parameters, returning a future result
///
/// - [args] are the processing parameters
Future<Tresult> execute(Tparams args) async {
await _lock.acquire(name: "worker $hashCode ($args; $debugLabel)");
_startTime = DateTime.now();
_completer = Completer();
_executor.execute(this, args);
return _completer!.future;
}
/// Method to destroy the worker, cleaning up resources
void destroy() {
Log.d("destroy worker $hashCode");
// Cancel all stream subscriptions
for (StreamSubscription subscription in _subscriptions) {
subscription.cancel();
}
_subscriptions.clear();
// Complete any open completer with an error
_completer?.completeError("destroyed");
// Teardown the underlying executor
_executor.teardown(this);
// Release the lock
_lock.release();
}
/// Function to measure and update the average execution duration
void _measureDuration() {
if (_startTime != null) {
final Duration duration = DateTime.now().difference(_startTime!);
if (avgExecutionDuration == null) {
avgExecutionDuration = duration;
} else {
avgExecutionDuration =
Duration(microseconds: ((avgExecutionDuration!.inMicroseconds + duration.inMicroseconds) / 2).round());
}
}
}
}
// Copyright (c) 2020, Gunther Hoppe.
// All rights reserved.
import 'worker-api.dart';
/// Stub implementation of the [WorkerExecutorInterface] interface.
class WorkerExecutor<Tparams, Tresult> implements WorkerExecutorInterface<Tparams, Tresult> {
@override
Stream<Tresult> get stream => throw UnsupportedError("Stub!");
@override
Stream get error => throw UnsupportedError("Stub!");
@override
void execute(Worker<Tparams, Tresult> worker, Tparams params) => throw UnsupportedError("Stub!");
@override
Future<void> teardown(Worker<Tparams, Tresult> worker) async => throw UnsupportedError("Stub!");
@override
Future<void> setup(Worker<Tparams, Tresult> worker) async => throw UnsupportedError("Stub!");
}
/// Stub implementation of the [WorkerProcessInterface] interface.
class WorkerProcess<Tparams, Tresult> implements WorkerProcessInterface<Tparams, Tresult> {
@override
void onMessage(Future<Tresult> Function(Tparams) fn) => throw UnsupportedError("Stub!");
@override
void onSend(Tresult result) => throw UnsupportedError("Stub!");
}
// Copyright (c) 2020, Gunther Hoppe.
// All rights reserved.
import 'dart:async';
import 'dart:isolate';
import 'worker-api.dart';
import 'worker-platform-stub.dart' as package_stub;
import 'worker-utils.dart';
/// Manages the execution of workers in isolates, handling communication and resource cleanup.
class WorkerExecutor<Tparams, Tresult> extends package_stub.WorkerExecutor<Tparams, Tresult> {
/// Static map to keep track of all isolates by their worker's hash code
static final Map<int, Isolate> _isolates = {};
/// Lock to ensure only one isolate is created at a time
final Lock _lock = Lock();
/// Capability to control the isolate, used to pause and resume
Capability? _capability;
/// Ports for communication between the main isolate and child isolate
late ReceivePort _mainIsolatePort;
late ReceivePort _mainIsolateErrorPort;
late SendPort _childIsolatePort;
/// Stream controllers to manage result and error streams
final StreamController<Tresult> _streamController = StreamController<Tresult>();
final StreamController _errorStreamController = StreamController();
/// List to hold all stream subscriptions to be able to cancel them later
final List<StreamSubscription> _subscriptions = [];
/// Getter to expose the result stream
@override
Stream<Tresult> get stream => _streamController.stream;
/// Getter to expose the error stream
@override
Stream get error => _errorStreamController.stream;
/// Method to execute the worker with the given parameters
///
/// - [worker] is the worker to be executed
/// - [params] are the processing parameters
@override
void execute(Worker<Tparams, Tresult> worker, Tparams params) {
// Setup the worker and then send the parameters to the child isolate
setup(worker).then((_) {
Isolate isolate = WorkerExecutor._isolates[worker.hashCode]!;
_childIsolatePort.send(params);
isolate.resume(_capability!);
}).onError((error, stackTrace) {
Log.e("$error", error: error, stackTrace: stackTrace);
});
}
/// Method to tear down the worker, clean up resources and kill the isolate
///
/// - [worker] is the worker to be torn down
@override
Future<void> teardown(Worker<Tparams, Tresult> worker) async {
Log.d("teardown worker executor ${worker.hashCode}");
// Cancel all stream subscriptions
for (StreamSubscription subscription in _subscriptions) {
await subscription.cancel();
}
_subscriptions.clear();
// Close the stream controllers
_streamController.close();
_errorStreamController.close();
// Kill the isolate and remove it from the map
Isolate? isolate = WorkerExecutor._isolates[worker.hashCode];
WorkerExecutor._isolates.remove(worker.hashCode);
isolate?.kill(priority: Isolate.immediate);
// Release the lock to allow other isolate creations
_lock.release();
}
/// Method to set up the worker and create an isolate if necessary
///
/// - [worker] is the worker to be set up
@override
Future<void> setup(Worker<Tparams, Tresult> worker) async {
Log.d("setup worker executor ${worker.hashCode}#${worker.debugLabel}");
if (WorkerExecutor._isolates[worker.hashCode] == null || _capability == null) {
// Ensure isolate is created only once
await _lock.acquire(name: "worker isolate creator ${worker.hashCode}#${worker.debugLabel}");
try {
if (WorkerExecutor._isolates[worker.hashCode] == null || _capability == null) {
// Initialize the communication ports
_mainIsolatePort = ReceivePort();
_mainIsolateErrorPort = ReceivePort();
ReceivePort acknowledgePort = ReceivePort();
// Create the worker process with necessary ports
WorkerProcess<Tparams, Tresult> process = WorkerProcess<Tparams, Tresult>(
worker.hashCode, acknowledgePort.sendPort, _mainIsolatePort.sendPort, _mainIsolateErrorPort.sendPort);
// Spawn a new isolate running the worker's static task
Isolate isolate = await Isolate.spawn<WorkerProcess<Tparams, Tresult>>(worker.staticTask, process);
Completer<SendPort> initializer = Completer();
// Listen for the child isolate's acknowledgement and setup communication
_subscriptions.add(acknowledgePort.listen((childSendPort) {
_capability = isolate.pause();
// Listen for results and errors from the child isolate
_subscriptions.add(_mainIsolatePort.listen((result) {
Log.d('worker executor receives result: $result');
_streamController.add(result);
_capability = isolate.pause();
}));
_subscriptions.add(_mainIsolateErrorPort.listen((error) {
Log.d('worker executor receives error: $error', error: error);
_errorStreamController.add(error);
_capability = isolate.pause();
}));
// Complete the initializer with the child isolate's send port
initializer.complete(childSendPort);
}));
// Wait for the initializer to complete and get the child isolate's send port
_childIsolatePort = await initializer.future;
WorkerExecutor._isolates[worker.hashCode] = isolate;
}
} catch (error) {
Log.d("worker executor receives error: $error", error: error);
} finally {
_lock.release();
}
}
}
}
/// Represents a worker process running in a child isolate, handling communication with the main isolate.
class WorkerProcess<Tparams, Tresult> extends package_stub.WorkerProcess<Tparams, Tresult> {
/// Unique identifier for the worker process.
final int id;
/// Send ports for acknowledging the setup, sending results, and sending errors back to the main isolate.
final SendPort _acknowledgePort;
final SendPort _mainIsolatePort;
final SendPort _mainIsolateErrorPort;
/// Receive port for the child isolate to receive messages from the main isolate.
ReceivePort? _childIsolatePort;
/// Constructor to initialize the worker process with required ports.
WorkerProcess(this.id, this._acknowledgePort, this._mainIsolatePort, this._mainIsolateErrorPort);
/// Method to handle messages received by the child isolate.
///
/// - [function] is the process function @override
void onMessage(Future<Tresult> Function(Tparams) function) {
// If the child isolate's receive port is not set, create it and send its send port to the main isolate.
if (_childIsolatePort == null) {
_childIsolatePort = ReceivePort();
_acknowledgePort.send(_childIsolatePort!.sendPort);
}
// Listen for incoming messages on the child isolate's receive port.
_childIsolatePort!.listen((message) async {
Log.d("worker process receives message: $message");
try {
// Execute the provided function with the received message and send the result back to the main isolate.
Tresult result = await function(message as Tparams);
onSend(result);
} catch (error) {
// Log and send any errors back to the main isolate.
Log.d("worker process receives error: $error", error: error);
_mainIsolateErrorPort.send(error);
}
});
}
/// Method to send the result back to the main isolate.
///
/// - [result] is the process result provided by the process function
@override
void onSend(Tresult result) => _mainIsolatePort.send(result);
}
// Copyright (c) 2020, Gunther Hoppe.
// All rights reserved.
import 'dart:async';
import 'worker-api.dart';
import 'worker-utils.dart';
import 'worker-platform-stub.dart' as package_stub;
/// WorkerExecutor class to manage the execution of tasks in separate isolates.
///
/// This class extends a stub implementation to provide concrete functionality for
/// executing workers, handling results, and managing errors. It uses a lock to ensure
/// only one worker is executed at a time and utilizes stream controllers for communication.
class WorkerExecutor<Tparams, Tresult> extends package_stub.WorkerExecutor<Tparams, Tresult> {
// Stream controller for managing results.
final StreamController<Tresult> _streamController = StreamController<Tresult>();
// Stream controller for managing errors.
final StreamController _errorStreamController = StreamController();
// Lock to ensure only one worker is executed at a time.
final Lock _lock = Lock();
/// Getter for the result stream.
@override
Stream<Tresult> get stream => _streamController.stream;
/// Getter for the error stream.
@override
Stream get error => _errorStreamController.stream;
/// Executes the given worker with the provided parameters.
///
/// [worker] is the worker to be executed.
/// [params] are the processing parameters.
@override
void execute(Worker<Tparams, Tresult> worker, Tparams params) {
// Create a WorkerProcess instance with the given parameters.
WorkerProcess<Tparams, Tresult> process = WorkerProcess(params)
// Add the result to the stream or handle the error.
..future
.then((value) => _streamController.add(value))
.onError((error, stackTrace) => _errorStreamController.add(error));
// Setup the worker and then start the static task.
setup(worker)
.then((_) => worker.staticTask(process))
.onError((error, stackTrace) => Log.e("$error", error: error, stackTrace: stackTrace));
}
/// Tears down the worker, cleaning up resources.
///
/// [worker] is the worker to be torn down.
@override
Future<void> teardown(Worker<Tparams, Tresult> worker) async {
Log.d("teardown worker executor ${worker.hashCode}");
// Close the stream controllers.
_streamController.close();
_errorStreamController.close();
// Release the lock to allow other worker executions.
_lock.release();
}
/// Sets up the worker.
///
/// [worker] is the worker to be set up.
@override
Future<void> setup(Worker<Tparams, Tresult> worker) async {
Log.d("setup worker executor ${worker.hashCode}");
// Currently, no specific setup actions are required.
// The lock acquire functionality is commented out as it might not be necessary.
// this._lock.acquire(name: "worker ${worker.hashCode}#${worker.debugLabel}");
}
}
/// WorkerProcess class that handles the execution of a task in an isolate.
///
/// This class extends a stub implementation to provide concrete functionality for
/// executing a task with provided parameters and handling the results. It uses a
/// Completer to manage the asynchronous result of the task.
class WorkerProcess<Tparams, Tresult> extends package_stub.WorkerProcess<Tparams, Tresult> {
// Completer to manage the future result of the task.
final Completer<Tresult> _completer = Completer();
// Parameters for the task.
final Tparams args;
// Constructor to initialize the worker process with the given parameters.
WorkerProcess(this.args);
// Getter for the future result of the task.
Future<Tresult> get future => _completer.future;
/// Handles the received message and executes the provided function.
///
/// [function] is the function to be executed with the given parameters.
@override
void onMessage(Future<Tresult> Function(Tparams) function) async {
try {
// Execute the function with the provided parameters and send the result.
Tresult result = await function(args);
onSend(result);
} catch (error) {
// Log and complete the completer with the error if an exception occurs.
Log.d("worker process receives error: $error", error: error);
_completer.completeError(error);
}
}
/// Sends the result back to the main isolate.
///
/// [result] is the result of the task execution.
@override
void onSend(Tresult result) => _completer.complete(result);
}
// Copyright (c) 2020, Gunther Hoppe.
// All rights reserved.
import 'dart:async';
import 'dart:developer';
/// Class representing a simple Log interface
class Log {
static const int none = 0;
static const int trace = 1 << 0; // 1
static const int debug = 1 << 1; // 2
static const int info = 1 << 2; // 4
static const int warn = 1 << 3; // 8
static const int error = 1 << 4; // 16
static const int fatal = 1 << 5; // 32
static const int all = trace | debug | info | warn | error | fatal;
/// The bitmask representing the current log levels that are enabled
static int level = info;
/// Logs a trace message with optional error and stack trace information
///
/// - [message] is the log message
/// - [error] (optional) an error object associated with this log event
/// - [stackTrace] (optional) a stack trace associated with this log event
static void t(String message, {Object? error, StackTrace? stackTrace}) =>
_log(message, Log.trace, error: error, stackTrace: stackTrace);
/// Logs a debug message with optional error and stack trace information
///
/// - [message] is the log message
/// - [error] (optional) an error object associated with this log event
/// - [stackTrace] (optional) a stack trace associated with this log event
static void d(String message, {Object? error, StackTrace? stackTrace}) =>
_log(message, Log.debug, error: error, stackTrace: stackTrace);
/// Logs an info message with optional error and stack trace information
///
/// - [message] is the log message
/// - [error] (optional) an error object associated with this log event
/// - [stackTrace] (optional) a stack trace associated with this log event
static void i(String message, {Object? error, StackTrace? stackTrace}) =>
_log(message, Log.info, error: error, stackTrace: stackTrace);
/// Logs a warning message with optional error and stack trace information
///
/// - [message] is the log message
/// - [error] (optional) an error object associated with this log event
/// - [stackTrace] (optional) a stack trace associated with this log event
static void w(String message, {Object? error, StackTrace? stackTrace}) =>
_log(message, Log.warn, error: error, stackTrace: stackTrace);
/// Logs an error message with optional error and stack trace information
///
/// - [message] is the log message
/// - [error] (optional) an error object associated with this log event
/// - [stackTrace] (optional) a stack trace associated with this log event
static void e(String message, {Object? error, StackTrace? stackTrace}) =>
_log(message, Log.error, error: error, stackTrace: stackTrace);
/// Logs an error message with optional error and stack trace information
///
/// - [message] is the log message
/// - [error] (optional) an error object associated with this log event
/// - [stackTrace] (optional) a stack trace associated with this log event
static void _log(String message, int level, {Object? error, StackTrace? stackTrace}) {
if ((Log.level & level) == level) {
log(message, error: error, stackTrace: stackTrace);
}
}
}
/// Class representing an exclusive lock
class Lock {
/// Timeout duration for the lock
late final Duration timeout;
/// List to keep track of the lock states
final List<_LockState> _states = [];
/// Constructor to initialize the lock with an optional timeout
///
/// - [timeout] (optional) is the timeout
Lock({this.timeout = const Duration(seconds: 30)});
/// Check if the lock is currently acquired
bool get acquired => _states.isNotEmpty;
/// Acquire the lock, optionally providing a name for the lock state
///
/// - [name] (optional) is the name
Future<void> acquire({String? name}) {
final _LockState lockState = _LockState(this, name, timeout);
Log.t("acquire lock: $lockState ($acquired)");
// Ensure lock is set in sync for fast consecutive calls
bool alreadyAcquired = acquired;
_states.add(lockState);
// If lock is not already acquired, complete directly
if (!alreadyAcquired) {
Log.d("can complete directly: $lockState");
lockState._waitToProceed.complete();
} else {
Log.d("already acquired: $lockState");
}
return lockState._waitToProceed.future;
}
/// Release the lock
void release() {
if (acquired) {
final _LockState current = _states.removeAt(0);
Log.t("release lock: $current");
// If there are other states waiting, unlock the next one
if (_states.isNotEmpty) {
final _LockState next = _states.elementAt(0);
Log.d("unlock next waiting: $next");
if (!next._waitToProceed.isCompleted) {
next._waitToProceed.complete();
} else {
Log.w("next waiting is already completed: $next");
// Release immediately if already completed
release();
}
}
} else {
Log.t("calling release without preceding acquire call");
}
}
/// Wait for the lock without immediately acquiring it
///
/// - [name] (optional) is the name
Future<void> wait({String? name}) async {
_LockState lockState = _LockState(this, name, timeout);
_states.add(lockState);
return lockState._waitToProceed.future;
}
}
/// Class representing the state of the lock.
class _LockState {
final Lock _lock;
final Completer _waitToProceed = Completer();
final DateTime _timestamp = DateTime.now();
final String? _name;
/// Constructor to initialize the lock state with the lock, name, and optional timeout
///
/// - [_lock] is the lock object
/// - [_name] (optional) is the name
/// - [autoUnlockAfter] (optional) is the timeout
_LockState(this._lock, this._name, Duration? autoUnlockAfter) {
if (autoUnlockAfter != null) {
// Set a timeout to complete with error if not proceeded within the timeout duration
Future.delayed(autoUnlockAfter).then((value) {
if (!_waitToProceed.isCompleted) {
Log.w("lock state timed out: $_name");
_lock._states.remove(this);
_waitToProceed.completeError("timeout");
}
});
}
}
@override
String toString() => "$_name (${_timestamp.toIso8601String()})";
}
[ ... ]
Software Engineer and Solution Architect | Two decades of experience in full stack development | Industrial solutions, web applications, mobile apps | Multi-Platform specialist | Early Flutter adopter
Flutter is a groundbreaking technology that redefines true multi-platform development, delivering massive business value by dramatically increasing the efficiency of building modern, cross-platform apps.