mirror of
https://github.com/flutter/samples.git
synced 2026-03-30 16:23:23 +00:00
[linting_tool] Implement exporting profiles (#869)
This commit is contained in:
committed by
GitHub
parent
a46327ef80
commit
9986fe2f2c
@@ -11,6 +11,8 @@ import 'package:linting_tool/routes.dart' as routes;
|
||||
import 'package:provider/provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
|
||||
final client = http.Client();
|
||||
|
||||
class LintingTool extends StatefulWidget {
|
||||
const LintingTool({Key? key}) : super(key: key);
|
||||
|
||||
@@ -26,10 +28,10 @@ class _LintingToolState extends State<LintingTool> {
|
||||
return MultiProvider(
|
||||
providers: [
|
||||
ChangeNotifierProvider<RuleStore>(
|
||||
create: (context) => RuleStore(http.Client()),
|
||||
create: (context) => RuleStore(client),
|
||||
),
|
||||
ChangeNotifierProvider<ProfilesStore>(
|
||||
create: (context) => ProfilesStore(),
|
||||
create: (context) => ProfilesStore(client),
|
||||
),
|
||||
],
|
||||
child: MaterialApp(
|
||||
|
||||
@@ -2,16 +2,26 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:json2yaml/json2yaml.dart';
|
||||
import 'package:linting_tool/model/profile.dart';
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
import 'package:linting_tool/repository/hive_service.dart';
|
||||
import 'package:linting_tool/repository/repository.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:file_selector/file_selector.dart' as file_selector;
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
const _boxName = 'rules_profile';
|
||||
|
||||
class ProfilesStore extends ChangeNotifier {
|
||||
ProfilesStore() {
|
||||
late final Repository repository;
|
||||
ProfilesStore(http.Client httpClient) {
|
||||
repository = Repository(httpClient);
|
||||
fetchSavedProfiles();
|
||||
}
|
||||
bool _isLoading = true;
|
||||
@@ -66,4 +76,84 @@ class ProfilesStore extends ChangeNotifier {
|
||||
await fetchSavedProfiles();
|
||||
});
|
||||
}
|
||||
|
||||
Future<bool> exportProfileFile(
|
||||
RulesProfile profile, {
|
||||
RulesStyle rulesStyle = RulesStyle.booleanMap,
|
||||
}) async {
|
||||
_isLoading = true;
|
||||
notifyListeners();
|
||||
|
||||
var resultSaved = false;
|
||||
|
||||
try {
|
||||
var templateFileData = await repository.getTemplateFile();
|
||||
|
||||
String newYamlFile =
|
||||
_prepareYamlFile(profile, templateFileData, rulesStyle);
|
||||
|
||||
resultSaved = await _saveFileToDisk(newYamlFile);
|
||||
} on SocketException catch (e) {
|
||||
log(e.toString());
|
||||
_error = 'Check internet connection.';
|
||||
resultSaved = false;
|
||||
} on Exception catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
|
||||
_isLoading = false;
|
||||
notifyListeners();
|
||||
|
||||
return resultSaved;
|
||||
}
|
||||
|
||||
Future<bool> _saveFileToDisk(String newYamlFile) async {
|
||||
const name = 'analysis_options.yaml';
|
||||
|
||||
/// Get file path using file picker.
|
||||
var savePath = await file_selector.getSavePath(
|
||||
suggestedName: name,
|
||||
);
|
||||
|
||||
final data = Uint8List.fromList(newYamlFile.codeUnits);
|
||||
final file = file_selector.XFile.fromData(data, name: name);
|
||||
|
||||
/// Save file to disk if path was provided.
|
||||
if (savePath != null) {
|
||||
file.saveTo(savePath);
|
||||
return true;
|
||||
}
|
||||
|
||||
var errorMessage = 'File path not found.';
|
||||
_error = errorMessage;
|
||||
throw Exception(errorMessage);
|
||||
}
|
||||
|
||||
String _prepareYamlFile(
|
||||
RulesProfile profile, YamlMap templateFile, RulesStyle rulesStyle) {
|
||||
var rules = profile.rules.map((e) => e.name).toList();
|
||||
|
||||
var rulesData =
|
||||
json.decode(json.encode(templateFile)) as Map<String, dynamic>;
|
||||
|
||||
/// Add rules to existing template according to formatting style.
|
||||
if (rulesStyle == RulesStyle.booleanMap) {
|
||||
var rulesMap = Map.fromEntries(
|
||||
rules.map(
|
||||
(e) => MapEntry(e, true),
|
||||
),
|
||||
);
|
||||
rulesData.update('linter', (dynamic value) => {'rules': rulesMap});
|
||||
} else {
|
||||
rulesData.update('linter', (dynamic value) => {'rules': rules});
|
||||
}
|
||||
|
||||
return json2yaml(rulesData, yamlStyle: YamlStyle.pubspecYaml);
|
||||
}
|
||||
}
|
||||
|
||||
/// Formatting style for rules.
|
||||
enum RulesStyle {
|
||||
list,
|
||||
booleanMap,
|
||||
}
|
||||
|
||||
@@ -45,7 +45,7 @@ class SavedLintsPage extends StatelessWidget {
|
||||
),
|
||||
itemCount: profilesStore.savedProfiles.length,
|
||||
cacheExtent: 5,
|
||||
itemBuilder: (context, index) {
|
||||
itemBuilder: (itemBuilderContext, index) {
|
||||
var profile = profilesStore.savedProfiles[index];
|
||||
return ListTile(
|
||||
title: Text(
|
||||
@@ -74,10 +74,21 @@ class SavedLintsPage extends StatelessWidget {
|
||||
),
|
||||
PopupMenuButton<String>(
|
||||
icon: const Icon(Icons.more_vert),
|
||||
onSelected: (value) {
|
||||
onSelected: (value) async {
|
||||
switch (value) {
|
||||
case 'Export file':
|
||||
// TODO(abd99): Implement exporting files.
|
||||
// TODO(abd99): Add option to select formatting style.
|
||||
|
||||
var saved = await profilesStore
|
||||
.exportProfileFile(profile);
|
||||
|
||||
if (!saved) {
|
||||
_showSnackBar(
|
||||
context,
|
||||
profilesStore.error ?? 'Failed to save file.',
|
||||
);
|
||||
}
|
||||
|
||||
break;
|
||||
case 'Delete':
|
||||
profilesStore.deleteProfile(profile);
|
||||
@@ -123,4 +134,12 @@ class SavedLintsPage extends StatelessWidget {
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
void _showSnackBar(BuildContext context, String data) {
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(data),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class APIProvider {
|
||||
final _baseURL = 'https://dart-lang.github.io/linter';
|
||||
@@ -12,7 +13,7 @@ class APIProvider {
|
||||
APIProvider(this.httpClient);
|
||||
|
||||
Future<List<Rule>> getRulesList() async {
|
||||
http.Response response =
|
||||
final response =
|
||||
await httpClient.get(Uri.parse('$_baseURL//lints/machine/rules.json'));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
@@ -26,4 +27,14 @@ class APIProvider {
|
||||
throw Exception('Failed to load rules');
|
||||
}
|
||||
}
|
||||
|
||||
Future<YamlMap> getTemplateFile() async {
|
||||
final response = await httpClient.get(Uri.parse(
|
||||
'https://raw.githubusercontent.com/flutter/flutter/master/packages/flutter_tools/templates/app_shared/analysis_options.yaml.tmpl'));
|
||||
if (response.statusCode == 200) {
|
||||
return loadYaml(response.body) as YamlMap;
|
||||
} else {
|
||||
throw Exception('Failed to load template file');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
import 'package:linting_tool/repository/api_provider.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:yaml/yaml.dart';
|
||||
|
||||
class Repository {
|
||||
late final APIProvider _apiProvider;
|
||||
@@ -14,4 +15,6 @@ class Repository {
|
||||
}
|
||||
|
||||
Future<List<Rule>> getRulesList() => _apiProvider.getRulesList();
|
||||
|
||||
Future<YamlMap> getTemplateFile() => _apiProvider.getTemplateFile();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user