🧠 task_sequencer

A lightweight Flutter/Dart utility package for managing sequential callbacks and debounced/repeated tasks.

This package is ideal for handling asynchronous operations safely, especially when working with shared datasets, or when controlling the execution frequency of repetitive tasks like search fields or timed jobs.


✨ Features

  • TaskSequencer<T> – Ensures callbacks are executed sequentially.
  • TaskSequencerList<T> – Ensures callbacks for the given data list are executed sequentially.
  • Job – Debounced or repeated task execution, similar to Kotlin's Job.

🚀 Getting Started

Add latest version to your pubspec.yaml:

dependencies:
  task_sequencer: ^1.0.2

Alternatively, execute this command:

flutter pub add task_sequencer

📦 Usage

▶️ TaskSequencer

Ensures the provided callback is executed only after the previous one finishes, avoiding race conditions.

class FakePersonsApi implements PersonsApi {
  final _list = PersonDtoFaker.fakeList();
  final _manager = TaskSequencer();

  @override
  Future<PersonDto?> updatePerson(RequestBody body, String personId) async {
    try {
      return _manager.enqueue((_) async {
        final person = _list.firstWhere((p) => p.id == personId);
        _list[_list.indexOf(person)] = person.copyWith(name: body.name);
        return person;
      });
    } catch (e) {
      return null;
    }
  }

  @override
  Future<PersonDto?> updatePersonPhoneNumbers(String personId, List<int> phoneNumbers) async {
    try {
      return _manager.enqueue((_) async {
        final person = _list.firstWhere((p) => p.id == personId);
        _list[_list.indexOf(person)] = person.copyWith(phoneNumbers: phoneNumbers);
        return person;
      });
    } catch (e) {
      return null;
    }
  }
}

📚 TaskSequencerList

Useful for managing sequencers for multiple items based on an initial list.

class FakePersonsApi implements PersonsApi {
  final _manager = TaskSequencerList(PersonDtoFaker.fakeList());

  // Getter for read-only purposes
  List<PersonDto> get persons => _manager.items;

  @override
  Future<PersonDto?> updatePerson(RequestBody body, String personId) async {
    try {
      return _manager.enqueue((list) async {
        final person = list.firstWhere((p) => p.id == personId);
        list[list.indexOf(person)] = person.copyWith(name: body.name);
        return person;
      });
    } catch (e) {
      return null;
    }
  }

  @override
  Future<PersonDto?> updatePersonPhoneNumbers(String personId, List<int> phoneNumbers) async {
    try {
      return _manager.enqueue((list) async {
        final person = list.firstWhere((p) => p.id == personId);
        list[list.indexOf(person)] = person.copyWith(phoneNumbers: phoneNumbers);
        return person;
      });
    } catch (e) {
      return null;
    }
  }
}

⏱️ Job

Debounces the execution of a callback. Repeated calls reset the timer.

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

  @override
  State<SearchTextField> createState() => _SearchTextFieldState();
}

class _SearchTextFieldState extends State<SearchTextField> {
  final _job = Job();

  @override
  void dispose() {
    _job.cancel();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return TextFormField(
      onChanged: (value) {
        _job(Duration(milliseconds: 300), () {
          // Call search api call
        });
      },
    );
  }
}

🔁 Job.repeat

Repeats a callback many times within the given duration (1 sec default).

  final job = Job();
  job.repeat(
    count: 10,
    callback: (counter) {
      // do something 10 times
    },
  );

📄 License

BSD 3-clause license


🧑‍💻 Author

Created by Jan Subert.

Libraries

task_sequencer