by FFCLOUD

Seamless Multiplatform Solutions

App solutions at any scale.
Delivering acceleration, flexibility, and cost efficiency.

Contact

Details

Work in action

Stock Embed

Web

A web embed for effortless stock data exploration, uncovering key trends and performance insights in an intuitive format that activates, engages, and inspires.

Details

Sunandbass App

Android iOS

A slick and engaging app was crafted for the global SUNANDBASS music festival and rocked the scene from 2015 to 2022.

Details

SafeViewKids App

Android iOS

A fun and engaging app empowers parents to control their children's YouTube experience with custom playlists, media time management, and a privacy-focused design for safe and enjoyable viewing.

Details

Primelogon

Android iOS MacOS Windows Chrome

Secure, passwordless login for efficient online shop order processing, private group blogs, simplified agency and administrators user management, and more.

Landingpage

Benefits and added value

Rich web support

Through:

  • Hardware-accelerated rendering
  • PWA capabilities
  • Additional libs+services

Rich web support

  • Hardware-accelerated rendering
  • PWA capabilities
  • Additional libs+services

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 60fps for most animations and interactions, with some optimized experiences reaching 120fps 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.

Time-to-Market and
cost savings

Through:

  • Single codebase
  • Developer experience
  • Additional libs+services

Time-to-market and cost savings

  • Single codebase
  • Developer experience
  • Additional code assets and libs

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 = 50%, for iOS, Android and Web up to (1 - 1 / 3) x 100 = 66% 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 20%.

Boost efficiency and speed with my over 20 ready-to-use assets, libs and services for common cases and functionalities.

Future-proofness

Through:

  • Open-source by Google
  • Strong community & extensive ecosystem
  • Long-term commitment

Future-proofness

  • Open-source by Google
  • Strong community & extensive ecosystem
  • Long-term commitment

Flutter as an open-source project from Google comes with continuous development and the use of the latest concepts and technologies with over 100 stable releases since 2018 as well as a large and dedicated community with over 45K Flutter libraries available for free use, 163K stars on GitHub, 178K posts on Stackoverflow, 132K members on Reddit, and more, ensures resources and provides efficient support for app development.

With 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.

High Performance

Through:

  • Platform-native compilation
  • Dart programming language
  • Highly skilled & specialized

High Performance

  • Platform-native compilation
  • Dart programming language
  • Highly skilled & specialized

Flutter apps typically achieve startup times under 200 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 50% 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 16 milliseconds to maintain 60fps refresh rate, ensuring fluid UI interactions. Flutter's UI thread typically consumes less than 10 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 120fps, 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 60fps with minimal CPU and memory overhead.

Use Case 1:
Web embed to enrich website

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.

Use Case 2:
Multiplatform App to reduce costs

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.

0 1 2 3 4 5
Costs --> Years -->

Flutter-based approach

  • High performance through high-quality expertise and specialization in the technology stack.
  • Seamless integration with the backend, enabling efficient and cohesive development.
  • Accelerated development with decreased costs through specialization and utilizing pre-built, robust code assets and libs for free.
Architectural layers

Fast lane with pre-built assets

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.

Client-side code assets and libs

Navigation routing enabled for deeplinking
Push & local notifications
Background processing worker
Remote data retrieval with offline caching
Video and audio streaming
Analytics integration

[ ... ]

Code sample

Code example: A pure Dart worker framework

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()})"; }

Server-side (micro)services

Image Conversion and Processing
PDF Generation
Video Streaming
Push notifications
Privacy-conform analytics solution
Content Management solution

[ ... ]

Available Services

Me

Gunther

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.

More

Gunther