Skip to content

Update flutter_command.dart getter methods for command line options #101595

Closed as not planned

Description

@Jasguerrero

Use case

Currently this 4 methods (https://github.com/flutter/flutter/blob/flutter-2.13-candidate.0/packages/flutter_tools/lib/src/runner/flutter_command.dart#L1505) checks if argResults is null and it defaults to dummy values if so. They make an assumption that the argument passed to them is valid therefore if the key is not found this methods raise an ArgumentError

  /// Gets the parsed command-line option named [name] as a bool.
  bool boolArg(String name) => argResults?[name] as bool? ?? false;

  /// Gets the parsed command-line option named [name] as a String.
  String? stringArg(String name) => argResults?[name] as String?;

  /// Gets the parsed command-line option named [name] as an int.
  int? intArg(String name) => argResults?[name] as int?;

  /// Gets the parsed command-line option named [name] as List<String>.
  List<String> stringsArg(String name) => argResults?[name] as List<String>? ?? <String>[];

Proposal

Make new boolArg and stringArg functions to return bool? and List? respectively and for stringArg and intArg to check if the given argument is valid to avoid the ArgumentError; then start incrementally using the new functions in all the places needed; the caller should handle what default value to use if a null is returned.

bool? boolArgSafe(String name) {
     if (argResult == null) {
          return null
     } else {
          bool? result;
          if (argParser.options.containsKey(name)) {
               result = argResults[name] as bool? ?? false
          }
          return result;
     }
}
// eg: argResults = {"key1": bool}
void myCaller(){
    bool? key1 = boolArg("key1")
    bool? key2 = boolArg("key2")
    if (key1 == null || key2== null){
         print("Missing params")
    }
    ...
}
  • rename current functions fnName to fnNameDeprecated
  • make new function handling nulls bool? boolArg
  • make new function handling nulls String? stringArg
  • make new function handling nulls int? intArg
  • migrate boolArgDeprecated to boolArg
  • migrate stringArgDeprecated to stringArg
  • migrate intArgDeprecated to intArg
  • rename stringsArgDeprecated to stringsArg

Activity

  1. added
    P3Issues that are less important to the Flutter project
    toolAffects the "flutter" command-line tool. See also t: labels.
    on Apr 8, 2022
  2. christopherfujino commented on Apr 11, 2022

    @christopherfujino
    Contributor

    Interesting that stringArg and intArg are nullable, but the other aren't. Given this, I feel like it would be desirable to to pick a consistent API, whether it's to have two versions of each or a single for each.

    bool? boolArgSafe(String name) {
         if (argResult == null) {
              return null
         } else {
              bool? result;
              if (argParser.options.containsKey(name)) {
                   result = argResults[name] as bool? ?? false
              }
              return result;
         }
    }

    I think this could just be:

    bool? boolArgSafe(String name) => argResults?[name] as bool?;
  3. christopherfujino commented on Apr 11, 2022

    @christopherfujino
    Contributor

    In thinking more, I guess it makes sense that stringsArg would have a default value, because it's pretty obvious what the default should be (empty list).

    That makes the only outlier boolArg(). Can I suggest that we machine-rename the boolArg function and all of its uses to boolArgDeprecated, and then create a new nullable version named boolArg(). All new invocations must use bool? boolArg(), and we would incrementally migrate all existing invocations of boolArgDeprecated() -> boolArg().

  4. Jasguerrero commented on Apr 11, 2022

    @Jasguerrero
    ContributorAuthor

    That makes the only outlier boolArg(). Can I suggest that we machine-rename the boolArg function and all of its uses to boolArgDeprecated, and then create a new nullable version named boolArg(). All new invocations must use bool? boolArg(), and we would incrementally migrate all existing invocations of boolArgDeprecated() -> boolArg().

    Agree. I like that approach better, and for the stringsArg update the function to handle the argResults[name] being empty (return an empty list too if this happens) and avoid the ArgumentError exception; we can follow the same approach as the boolArgDeprecated()

  5. christopherfujino commented on Apr 11, 2022

    @christopherfujino
    Contributor

    and for the stringsArg update the function to handle the argResults[name] being empty (return an empty list too if this happens) and avoid the ArgumentError exception; we can follow the same approach as the boolArgDeprecated()

    Not sure what you mean here. Doesn't it already handle argResults[name] being null with the fallthrough at the end?

      List<String> stringsArg(String name) => argResults?[name] as List<String>? ?? <String>[];
  6. Jasguerrero commented on Apr 11, 2022

    @Jasguerrero
    ContributorAuthor

    Not sure what you mean here. Doesn't it already handle argResults[name] being null with the fallthrough at the end?

    No. The fallthrough dosen't go throw the end, it fails in this part argResults?[name] because argResults is not null but argResults[name] does not exist and therefore the ArgumentError is raised; here is an stack trace as an example

    ## exception
    ArgumentError: Invalid argument(s): Could not find an option named "abc".
    
    #0      ArgResults.[] (package:args/src/arg_results.dart:65:7)
    #1      FlutterCommand.stringsArg (package:flutter_tools/src/runner/flutter_command.dart:1532:54)
    #2      FlutterCommand.verifyThenRunCommand (package:flutter_tools/src/runner/flutter_command.dart:1305:7)
    #3      FlutterCommand.run.<anonymous closure> (package:flutter_tools/src/runner/flutter_command.dart:1194:33)
    <asynchronous suspension>
    #4      AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
    <asynchronous suspension>
    #5      CommandRunner.runCommand (package:args/command_runner.dart:209:13)
    <asynchronous suspension>
    #6      FlutterCommandRunner.runCommand.<anonymous closure> (package:flutter_tools/src/runner/flutter_command_runner.dart:281:9)
    <asynchronous suspension>
    #7      AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
    <asynchronous suspension>
    #8      FlutterCommandRunner.runCommand (package:flutter_tools/src/runner/flutter_command_runner.dart:229:5)
    <asynchronous suspension>
    #9      run.<anonymous closure>.<anonymous closure> (package:flutter_tools/runner.dart:62:9)
    <asynchronous suspension>
    #10     AppContext.run.<anonymous closure> (package:flutter_tools/src/base/context.dart:150:19)
    <asynchronous suspension>
    #11     main (package:flutter_tools/executable.dart:94:3)
    <asynchronous suspension>
    ```
    
  7. christopherfujino commented on Apr 11, 2022

    @christopherfujino
    Contributor

    No. The fallthrough dosen't go throw the end, it fails in this part argResults?[name] because argResults is not null but argResults[name] does not exist and therefore the ArgumentError is raised; here is an stack trace as an example

    Ohh, I see what you mean. So intArg and stringArg are not safe either. We should probably migrate all four of these to methods that check if the key was parsed first, and only if so read the value.

  8. Jasguerrero commented on Apr 11, 2022

    @Jasguerrero
    ContributorAuthor

    Ohh, I see what you mean. So intArg and stringArg are not safe either.

    They are not. So yeah those need to be migrated as well.

  9. christopherfujino commented on Apr 11, 2022

    @christopherfujino
    Contributor

    Ohh, I see what you mean. So intArg and stringArg are not safe either.

    They are not. So yeah those need to be migrated as well.

    Sounds good to me, I think we need four new methods, but to ensure we don't add new invocations to the old methods, rename the existing ones first, and then have the new methods that check if the name was parsed first use the existing method names.

  10. jmagman commented on Mar 8, 2023

    @jmagman
    Member

    intArg unused, removed in #122124

  11. matanlurey commented on Jul 11, 2025

    @matanlurey
    Contributor

    We don't plan to make these changes, or will when they are useful versus a holistic cleanup.

  12. github-actions commented on Jul 25, 2025

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  13. locked as resolved and limited conversation to collaborators on Jul 25, 2025
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

    P3Issues that are less important to the Flutter projectteam-toolOwned by Flutter Tool teamtoolAffects the "flutter" command-line tool. See also t: labels.triaged-toolTriaged by Flutter Tool team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions