Skip to content

Need a way to tell if a command has not yet started execution #2

Open

Description

@escamoteur

@minkyn cloned issue escamoteur/flutter_command#22 on 2025-03-08:

According to the comment for CommandResult class:

/// Combined execution state of a `Command` represented using four of its fields.
/// A [CommandResult] will be issued for any state change of any of its fields
/// During normal command execution you will get this items by listening at the command's [.results] ValueListenable.
/// 1. If the command was just newly created you will get `param data, null, null, false` (paramData, data, error, isExecuting)
/// 2. When calling execute: `param data, null, null, true`
/// 3. When execution finishes: `param data, the result, null, false`
/// `param data` is the data that you pass as parameter when calling the command

In short, the way to tell whether a command has yet to execute or has completed execution is to see if result.data is null.

However, the actual implementation is inconsistent. The result.data always returns the initial value set on command creation before executing. If the command returns the same value after execution, there is no way to tell them apart. Ideally, CommandResult should have both a getter that returns the initial value before the actual data, as well as a getter that follows the comment to indicate a newly created command before execution.

Moreover, even when CommandResult were fixed to follow its comments, for commands that have no return value, there is still no way to tell if execution has yet started, as the result.data will always be null before and after the execution.

I know I can use another field to track that info, but it's not an elegant solution. The Command class should embed that state internally, just like in FutureBuilder you can use snapshot.connectionState to differentiate ConnectionState.none against ConnectionState.done.

Activity

  1. escamoteur commented on Jul 19, 2025

    @escamoteur
    MemberAuthor

    @escamoteur commented on 2025-03-08:

    Hmm, I am honestly surprised, why you would need that information. You mean you can't differentiate if the value is the initial value or the result of a previous call?

    When a command start executing, "isExecuting" get immediately true.

    How would you then differentiate if the command has not executed at all or if it was executed before? That would need a resetCommandState function then or something like that.

    I am not against an improvement but I need to get a better understanding when you would need that information.

    Typically such more detailed state information is stored in the class containing the command.

    Am 8. März 2025, 08:45 +0100 schrieb Ming Jiang @.***>:

    According to the comment for CommandResult class:

    /// Combined execution state of a Command represented using four of its fields.

    /// A [CommandResult] will be issued for any state change of any of its fields

    /// During normal command execution you will get this items by listening at the command's [.results] ValueListenable.

    /// 1. If the command was just newly created you will get param data, null, null, false (paramData, data, error, isExecuting)

    /// 2. When calling execute: param data, null, null, true

    /// 3. When execution finishes: param data, the result, null, false

    /// param data is the data that you pass as parameter when calling the command

    In short, the way to tell whether a command has yet to execute or has completed execution is to see if result.data is null.

    However, the actual implementation is inconsistent. The result.data always returns the initial value set on command creation before executing. If the command returns the same value after execution, there is no way to tell them apart. Ideally, CommandResult should have both a getter that returns the initial value before the actual data, as well as a getter that follows the comment to indicate a newly created command before execution.

    Moreover, even when CommandResult were fixed to follow its comments, for commands that have no return value, there is still no way to tell if execution has yet started, as the result.data will always be null before and after the execution.

    I know I can use another field to track that info, but it's not an elegant solution. The Command class should embed that state internally, just like in FutureBuilder you can use snapshot.connectionState to differentiate ConnectionState.none against ConnectionState.done.

    —

    Reply to this email directly, view it on GitHub, or unsubscribe.

    You are receiving this because you are subscribed to this thread.Message ID: @.***>

    minkyn created an issue (escamoteur#22)

    According to the comment for CommandResult class:

    /// Combined execution state of a Command represented using four of its fields.

    /// A [CommandResult] will be issued for any state change of any of its fields

    /// During normal command execution you will get this items by listening at the command's [.results] ValueListenable.

    /// 1. If the command was just newly created you will get param data, null, null, false (paramData, data, error, isExecuting)

    /// 2. When calling execute: param data, null, null, true

    /// 3. When execution finishes: param data, the result, null, false

    /// param data is the data that you pass as parameter when calling the command

    In short, the way to tell whether a command has yet to execute or has completed execution is to see if result.data is null.

    However, the actual implementation is inconsistent. The result.data always returns the initial value set on command creation before executing. If the command returns the same value after execution, there is no way to tell them apart. Ideally, CommandResult should have both a getter that returns the initial value before the actual data, as well as a getter that follows the comment to indicate a newly created command before execution.

    Moreover, even when CommandResult were fixed to follow its comments, for commands that have no return value, there is still no way to tell if execution has yet started, as the result.data will always be null before and after the execution.

    I know I can use another field to track that info, but it's not an elegant solution. The Command class should embed that state internally, just like in FutureBuilder you can use snapshot.connectionState to differentiate ConnectionState.none against ConnectionState.done.

    —

    Reply to this email directly, view it on GitHub, or unsubscribe.

    You are receiving this because you are subscribed to this thread.Message ID: @.***>

  2. escamoteur commented on Jul 19, 2025

    @escamoteur
    MemberAuthor

    @minkyn commented on 2025-03-08:

    Two reasons I would like that feature:

    1. I am trying to refactor my code from using FutureBuilder to the command pattern, where instead of listening to the command, I was listening to the returned future result of the command function. Without the feature, there is no easy way to correspond to ConnectionState.none case.

    2. The official flutter sample repo compass_app demonstrates a lot on command design pattern. Some examples include load from home_viewmodel, and search from results_viewmodel, where those commands are async initialization the ViewModel needs to perform on construction. In the widget code, the UI will show something before search completes, and a progress indicator when the command is running, and finally something else after search completes or when error happens. In other words, UI will display differently for ready, running, completed and error.

    I think implementation wise, it should suffice to just add a completed field, like what the official sample does. This way I can tell if the command has yet to execute by !completed && !isExecuting. And I feel that onSuccess in CommandBuilder would be more useful if it also implies the completion status.

  3. escamoteur commented on Jul 19, 2025

    @escamoteur
    MemberAuthor

    @minkyn commented on 2025-03-08:

    Also, being able to reset the command state would be nice, but not necessary for me. All I need is to tell when the command hasn't executed at all. If it's executed before, and will be executed again, my UI is fine with displaying the previous results first.

    So, my expected completed field will start with false, and turns into true after execution regardless of errors, and stays true until the command becomes isExecuting for the next run where it turns into false again, and finally becomes true after the second run.

  4. escamoteur commented on Jul 19, 2025

    @escamoteur
    MemberAuthor

    @escamoteur commented on 2025-03-08:

    First of all, are you aware, that I did  full refactoring of the compass app using flutter_command and watch_it?

    https://github.com/escamoteur/compass_fork

    Normally I don't need the done state because I listen to the command itself or use a CommandBuilder.

    And receiving a CommandResult with hasData=true means success.

    Especially when you refer to an additional "ready State" this gets difficult because when will it be ready after success?

    The underlying difference to  a Future is that Futures are one time use objects

    Am 8. März 2025, 18:40 +0100 schrieb Ming Jiang @.***>:

    Two reasons I would like that feature:

    I am trying to refactor my code from using FutureBuilder to the command pattern, where instead of listening to the command, I was listening to the returned future result of the command function. Without the feature, there is no easy way to correspond to ConnectionState.none case.

    1. The official flutter sample repo compass_app demonstrates a lot on command design pattern. Some examples include load from home_viewmodel, and search from results_viewmodel, where those commands are async initialization the ViewModel needs to perform on construction. In the widget code, the UI will show something before search completes, and a progress indicator when the command is running, and finally something else after search completes or when error happens. In other words, UI will display differently for ready, running, completed and error.

    I think implementation wise, it should suffice to just add a completed field, like what the official sample does. And I feel that onSuccess in CommandBuilder would be more useful if it also implies the completion status.

    —

    Reply to this email directly, view it on GitHub, or unsubscribe.

    You are receiving this because you commented.Message ID: @.***>

    minkyn left a comment (escamoteur#22)

    Two reasons I would like that feature:

    1. I am trying to refactor my code from using FutureBuilder to the command pattern, where instead of listening to the command, I was listening to the returned future result of the command function. Without the feature, there is no easy way to correspond to ConnectionState.none case.
    1. The official flutter sample repo compass_app demonstrates a lot on command design pattern. Some examples include load from home_viewmodel, and search from results_viewmodel, where those commands are async initialization the ViewModel needs to perform on construction. In the widget code, the UI will show something before search completes, and a progress indicator when the command is running, and finally something else after search completes or when error happens. In other words, UI will display differently for ready, running, completed and error.

    I think implementation wise, it should suffice to just add a completed field, like what the official sample does. And I feel that onSuccess in CommandBuilder would be more useful if it also implies the completion status.

    —

    Reply to this email directly, view it on GitHub, or unsubscribe.

    You are receiving this because you commented.Message ID: @.***>

  5. escamoteur commented on Jul 19, 2025

    @escamoteur
    MemberAuthor

    @minkyn commented on 2025-03-08:

    Wow, I didn't know your refactoring work. That looks pretty cool. I will definitely take a look.

    Now I understand your concern is on the following runs of the command after the first execution. Again, as I mentioned above, in my case I deal mostly with just one-time init async command, where I only care about the very first unstarted state.

  6. escamoteur commented on Jul 19, 2025

    @escamoteur
    MemberAuthor

    @escamoteur commented on 2025-03-08:

    Why not just defining the Command with a nullable return type and pass null as initial value?

    Thing is storing if a Command has already run can be easily done by yourself as that is more a state of your business logic than of  command itself.

    BTW I have to apologize, I just realized that the Readme of the package needs some updating

    Am 8. März 2025, 18:48 +0100 schrieb Ming Jiang @.***>:

    Also, being able to reset the command state would be nice, but not necessary for me. All I need is to tell when the command hasn't executed at all. If it's executed before, and will be executed again, my UI is fine with displaying the previous results first.

    So, my expected completed field will start with false, and turns into true after execution regardless of errors, and stays true until the command becomes isExecuting for the next run where it turns into false again, and finally becomes true after the second run.

    —

    Reply to this email directly, view it on GitHub, or unsubscribe.

    You are receiving this because you commented.Message ID: @.***>

    minkyn left a comment (escamoteur#22)

    Also, being able to reset the command state would be nice, but not necessary for me. All I need is to tell when the command hasn't executed at all. If it's executed before, and will be executed again, my UI is fine with displaying the previous results first.

    So, my expected completed field will start with false, and turns into true after execution regardless of errors, and stays true until the command becomes isExecuting for the next run where it turns into false again, and finally becomes true after the second run.

    —

    Reply to this email directly, view it on GitHub, or unsubscribe.

    You are receiving this because you commented.Message ID: @.***>

  7. minkyn commented on Sep 14, 2025

    @minkyn

    Back on the thread for a quick question.

    /// Combined execution state of an `Command`
    /// Will be updated for any state change of any of the fields
    /// 1. If the command was just newly created `results.value` has the value:
    ///    `param data,null, null, false` (paramData,data, error, isExecuting)
    /// 2. When calling execute: `param data, null, null, true`
    /// 3. When execution finishes: `param data, the result, null, false`
    /// If an error occurs: `param data, null, error, false`
    /// `param data` is the data that you pass as parameter when calling the command
    class CommandResult<TParam, TResult>
      final TParam? paramData;
      final TResult? data;

    The comment here seems to suggest that data would be null for a newly created command that has never executed. However, the only factory to create such a command is via Command.createAsync(<func>, initialValue: <TResult>) where the initialValue cannot be null unless TResult itself allows null value. So in reality, the data of the command results can never be null unless TResult itself allows null, is that correct? If so, the comment here seems to be misleading, making people think data would always start as null, especially when we see the data member takes the type TResult? instead of TResult.

  8. escamoteur commented on Sep 14, 2025

    @escamoteur
    MemberAuthor
  9. minkyn commented on Sep 14, 2025

    @minkyn

    That's not my question. And what I did eventually is to use a TResult that is something like String? to include null value.

    My question is about the misleading comments above CommandResult, and the fact that you are using TResult? in the struct. This would make me think that even when I use a TResult that is not nullable, the CommandResult would still take data as a nullable TResult? at some point.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions