Use XDG_CONFIG_HOME dir by default for config files (#66645)

This PR changes the Config class in flutter_tools to use the XDG Base directory specification instead of putting files directly in the user's home directory. If those files are already present in the home directory, they are used instead.
diff --git a/packages/flutter_tools/lib/src/base/config.dart b/packages/flutter_tools/lib/src/base/config.dart
index 529bb76..c14b2d2 100644
--- a/packages/flutter_tools/lib/src/base/config.dart
+++ b/packages/flutter_tools/lib/src/base/config.dart
@@ -14,18 +14,23 @@
 /// A class to abstract configuration files.
 class Config {
   /// Constructs a new [Config] object from a file called [name] in the
-  /// current user's home directory as determined by the [Platform] and
-  /// [FileSystem].
+  /// current user's configuration directory as determined by the [Platform]
+  /// and [FileSystem].
+  ///
+  /// The configuration directory defaults to $XDG_CONFIG_HOME on Linux and
+  /// macOS, but falls back to the home directory if a file named
+  /// `.flutter_$name` already exists there. On other platforms the
+  /// configuration file will always be a file named `.flutter_$name` in the
+  /// home directory.
   factory Config(
     String name, {
     @required FileSystem fileSystem,
     @required Logger logger,
     @required Platform platform,
   }) {
-    final File file = fileSystem.file(fileSystem.path.join(
-      _userHomePath(platform),
-      name,
-    ));
+    final String filePath = _configPath(platform, fileSystem, name);
+    final File file = fileSystem.file(filePath);
+    file.parent.createSync(recursive: true);
     return Config.createForTesting(file, logger);
   }
 
@@ -35,7 +40,7 @@
     String name, {
     @required Directory directory,
     @required Logger logger,
-  }) => Config.createForTesting(directory.childFile(name), logger);
+  }) => Config.createForTesting(directory.childFile('.${kConfigDir}_$name'), logger);
 
   /// Test only access to the Config constructor.
   @visibleForTesting
@@ -65,8 +70,24 @@
     }
   }
 
+  /// The default directory name for Flutter's configs.
+
+  /// Configs will be written to the user's config path. If there is already a
+  /// file with the name `.${kConfigDir}_$name` in the user's home path, that
+  /// file will be used instead.
+  static const String kConfigDir = 'flutter';
+
+  /// Environment variable specified in the XDG Base Directory
+  /// [specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
+  /// to specify the user's configuration directory.
+  static const String kXdgConfigHome = 'XDG_CONFIG_HOME';
+
+  /// Fallback directory in the user's home directory if `XDG_CONFIG_HOME` is
+  /// not defined.
+  static const String kXdgConfigFalback = '.config';
+
   /// The default name for the Flutter config file.
-  static const String kFlutterSettings = '.flutter_settings';
+  static const String kFlutterSettings = 'settings';
 
   final Logger _logger;
 
@@ -104,9 +125,22 @@
   //
   // Note that this is different from FileSystemUtils.homeDirPath.
   static String _userHomePath(Platform platform) {
-    final String envKey = platform.operatingSystem == 'windows'
-      ? 'APPDATA'
-      : 'HOME';
+    final String envKey = platform.isWindows ? 'APPDATA' : 'HOME';
     return platform.environment[envKey] ?? '.';
   }
+
+  static String _configPath(
+      Platform platform, FileSystem fileSystem, String name) {
+    final String homeDirFile =
+        fileSystem.path.join(_userHomePath(platform), '.${kConfigDir}_$name');
+    if (platform.isLinux || platform.isMacOS) {
+      if (fileSystem.isFileSync(homeDirFile)) {
+        return homeDirFile;
+      }
+      final String configDir = platform.environment[kXdgConfigHome] ??
+          fileSystem.path.join(_userHomePath(platform), '.config', kConfigDir);
+      return fileSystem.path.join(configDir, name);
+    }
+    return homeDirFile;
+  }
 }
diff --git a/packages/flutter_tools/lib/src/persistent_tool_state.dart b/packages/flutter_tools/lib/src/persistent_tool_state.dart
index bb3872b..d596390 100644
--- a/packages/flutter_tools/lib/src/persistent_tool_state.dart
+++ b/packages/flutter_tools/lib/src/persistent_tool_state.dart
@@ -73,7 +73,7 @@
       logger: logger,
     );
 
-  static const String _kFileName = '.flutter_tool_state';
+  static const String _kFileName = 'tool_state';
   static const String _kRedisplayWelcomeMessage = 'redisplay-welcome-message';
   static const Map<Channel, String> _lastActiveVersionKeys = <Channel,String>{
     Channel.master: 'last-active-master-version',
diff --git a/packages/flutter_tools/test/general.shard/config_test.dart b/packages/flutter_tools/test/general.shard/config_test.dart
index 295450d..425c19f 100644
--- a/packages/flutter_tools/test/general.shard/config_test.dart
+++ b/packages/flutter_tools/test/general.shard/config_test.dart
@@ -65,7 +65,7 @@
 
   testWithoutContext('Config parse error', () {
     final BufferLogger bufferLogger = BufferLogger.test();
-    final File file = memoryFileSystem.file('example')
+    final File file = memoryFileSystem.file('.flutter_example')
       ..writeAsStringSync('{"hello":"bar');
     config = Config(
       'example',
@@ -106,6 +106,30 @@
     // Also contains original error message:
     expect(bufferLogger.errorText, contains('The flutter tool cannot access the file or directory'));
   });
+
+  testWithoutContext('Config in home dir is used if it exists', () {
+    memoryFileSystem.file('.flutter_example').writeAsStringSync('{"hello":"bar"}');
+    config = Config(
+      'example',
+      fileSystem: memoryFileSystem,
+      logger: BufferLogger.test(),
+      platform: fakePlatform,
+    );
+    expect(config.getValue('hello'), 'bar');
+    expect(memoryFileSystem.file('.config/flutter/example').existsSync(), false);
+  });
+
+  testWithoutContext('Config is created in config dir if it does not already exist in home dir', () {
+    config = Config(
+      'example',
+      fileSystem: memoryFileSystem,
+      logger: BufferLogger.test(),
+      platform: fakePlatform,
+    );
+
+    config.setValue('foo', 'bar');
+    expect(memoryFileSystem.file('.config/flutter/example').existsSync(), true);
+  });
 }
 
 class FakeFile extends Fake implements File {