flutterflutter-providerflutter-workmanager

Update external values with WorkManager


I have a provider where I have a list of tasks, it happens that, I want to implement a way in which after a specific time, I would like to remove a task from the list.

class TaskProvider extends ChangeNotifier {
  final List<TaskModel> _tasks = [];
  List<TaskModel> get tasks => _tasks;
  final String _tasksKey = "tasks";

  Future<void> addTask({
    required String task,
    required DateTime scheduledTime,
    required TaskStatus status,
  }) async {
    scheduleTaskManager(task, scheduledTime);
    _tasks.add(
        TaskModel(task: task, limitDateTime: scheduledTime, status: status));
    notifyListeners();
  }

  Future<void> clearTasks() async {
    dev.log("Tasks deleted!");
    tasks.clear();
    notifyListeners();  
  }
}

Normally I can do this from the UI by importing and calling clearTasks(), however, this I cannot use with WorkManager, my understanding is that WorkManager processes are separate.

So, I come to ask, what ways are there to be able to modify an external value using WorkManager?

This is the callbackDispatcher:

@pragma(
    'vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    dev.log(
        "Native called background task: $task"); //simpleTask will be emitted here.
    dev.log("Time limit, clearing all tasks!");
    //Call clearTasks() or find a way to be able to modify the list of tasks of TaskProvider.
    return Future.value(true);
  });
}

Although I initially tried calling clearTasks() anyway, I can see the log for 'Tasks deleted! (log from TaskProvider.clearTasks)' in the console, but in the widget where I show the task list, I don't see the task being deleted.

@pragma(
    'vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
void callbackDispatcher() {
  Workmanager().executeTask((task, inputData) {
    dev.log(
        "Native called background task: $task"); //simpleTask will be emitted here.
    dev.log("Time limit, clearing all tasks!");
    TaskProvider taskProvider = TaskProvider();
    taskProvider.clearTasks();
    return Future.value(true);
  });
}

My idea is to modify various things in the task list, such as deleting them, updating the task status, etc. All this data comes from my provider.


Solution

  • The TaskProvider created in this function is not the same being instanced in your provider. Because the function has to be top level maybe an option would be creating a static ChangeNotifier that works as a WorkManagerNotifier sending a message to all who wants to update:

    class WorkManagerNotifier extends ChangeNotifer {
      WorkManagerNotifier._();
      static final WorkManagerNotifier _instance = WorkManagerNotifier._();
      factory WorkManagerNotifier() => _instance;
      
      String? task;
      /// maybe an enum or bool to say is completed or failed  
    
      void updatedTask(String newTask) {
        task = newTask;
        notifiyListeners();
      }
    }
    
    @pragma(
        'vm:entry-point') // Mandatory if the App is obfuscated or using Flutter 3.1+
    void callbackDispatcher() {
      Workmanager().executeTask((task, inputData) {
        dev.log(
            "Native called background task: $task"); //simpleTask will be emitted here.
        dev.log("Time limit, clearing all tasks!");
        WorkManagerNotifier().updateTask(task); /// maybe you're interested in the data too?
        return Future.value(true);
      });
    }
    

    And now you can either listen WorkManagerNotifier inside your other providers or use it in your context:

    /// main
    
    ChangeNotifierValue(
      value: WorkManagerNotifier()
      child: MaterialApp...
    )
    
    
    /// where you create your TaskProvider
    ChangeNotifierProxyProvider<WorkManagerNotifier, TaskProvider>(
      create: (_) => TaskProvider(...), ///
      update: (_, workManager, taskProvider) {
        if (workManager.task == 'task') {
          taskProvider.clear();
        }
        return taskProvider;
      },
      child: ...
    );
    

    Now when workmanager notifies a new task and it has the name 'task' or something you understand you can clear your provider and use it for as many tasks and input data you desire