mirror of
https://github.com/flutter/samples.git
synced 2026-05-08 07:56:48 +00:00
[linting_tool] Add lint rules list (#856)
This commit is contained in:
committed by
GitHub
parent
3b65403631
commit
6bac1b9694
@@ -3,9 +3,11 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:linting_tool/model/rules_store.dart';
|
||||
import 'package:linting_tool/theme/app_theme.dart';
|
||||
import 'package:linting_tool/widgets/adaptive_nav.dart';
|
||||
import 'package:linting_tool/routes.dart' as routes;
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class LintingTool extends StatefulWidget {
|
||||
const LintingTool({Key? key}) : super(key: key);
|
||||
@@ -19,22 +21,25 @@ class LintingTool extends StatefulWidget {
|
||||
class _LintingToolState extends State<LintingTool> {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MaterialApp(
|
||||
title: 'Flutter Linting Tool',
|
||||
theme: AppTheme.buildReplyLightTheme(context),
|
||||
darkTheme: AppTheme.buildReplyDarkTheme(context),
|
||||
themeMode: ThemeMode.light,
|
||||
initialRoute: LintingTool.homeRoute,
|
||||
onGenerateRoute: (settings) {
|
||||
switch (settings.name) {
|
||||
case LintingTool.homeRoute:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (context) => const AdaptiveNav(),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
return ChangeNotifierProvider<RuleStore>(
|
||||
create: (context) => RuleStore(),
|
||||
child: MaterialApp(
|
||||
title: 'Flutter Linting Tool',
|
||||
theme: AppTheme.buildReplyLightTheme(context),
|
||||
darkTheme: AppTheme.buildReplyDarkTheme(context),
|
||||
themeMode: ThemeMode.light,
|
||||
initialRoute: LintingTool.homeRoute,
|
||||
onGenerateRoute: (settings) {
|
||||
switch (settings.name) {
|
||||
case LintingTool.homeRoute:
|
||||
return MaterialPageRoute<void>(
|
||||
builder: (context) => const AdaptiveNav(),
|
||||
settings: settings,
|
||||
);
|
||||
}
|
||||
return null;
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
36
experimental/linting_tool/lib/model/rule.dart
Normal file
36
experimental/linting_tool/lib/model/rule.dart
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2021 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:json_annotation/json_annotation.dart';
|
||||
|
||||
part 'rule.g.dart';
|
||||
|
||||
@JsonSerializable()
|
||||
class Rule extends Equatable {
|
||||
final String name;
|
||||
final String description;
|
||||
final String group;
|
||||
final String maturity;
|
||||
final List<String> incompatible;
|
||||
final List<String> sets;
|
||||
final String details;
|
||||
|
||||
const Rule({
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.group,
|
||||
required this.maturity,
|
||||
required this.incompatible,
|
||||
required this.sets,
|
||||
required this.details,
|
||||
});
|
||||
|
||||
factory Rule.fromJson(Map<String, dynamic> json) => _$RuleFromJson(json);
|
||||
|
||||
Map<String, dynamic> toJson() => _$RuleToJson(this);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [name];
|
||||
}
|
||||
31
experimental/linting_tool/lib/model/rule.g.dart
Normal file
31
experimental/linting_tool/lib/model/rule.g.dart
Normal file
@@ -0,0 +1,31 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'rule.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// JsonSerializableGenerator
|
||||
// **************************************************************************
|
||||
|
||||
Rule _$RuleFromJson(Map<String, dynamic> json) {
|
||||
return Rule(
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String,
|
||||
group: json['group'] as String,
|
||||
maturity: json['maturity'] as String,
|
||||
incompatible: (json['incompatible'] as List<dynamic>)
|
||||
.map((e) => e as String)
|
||||
.toList(),
|
||||
sets: (json['sets'] as List<dynamic>).map((e) => e as String).toList(),
|
||||
details: json['details'] as String,
|
||||
);
|
||||
}
|
||||
|
||||
Map<String, dynamic> _$RuleToJson(Rule instance) => <String, dynamic>{
|
||||
'name': instance.name,
|
||||
'description': instance.description,
|
||||
'group': instance.group,
|
||||
'maturity': instance.maturity,
|
||||
'incompatible': instance.incompatible,
|
||||
'sets': instance.sets,
|
||||
'details': instance.details,
|
||||
};
|
||||
44
experimental/linting_tool/lib/model/rules_store.dart
Normal file
44
experimental/linting_tool/lib/model/rules_store.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2021 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:developer';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
import 'package:linting_tool/repository/repository.dart';
|
||||
|
||||
class RuleStore extends ChangeNotifier {
|
||||
RuleStore() {
|
||||
fetchRules();
|
||||
}
|
||||
bool _isLoading = true;
|
||||
|
||||
bool get isLoading => _isLoading;
|
||||
|
||||
List<Rule> _rules = [];
|
||||
|
||||
List<Rule> get rules => _rules;
|
||||
|
||||
String? _error;
|
||||
|
||||
String? get error => _error;
|
||||
|
||||
Future<void> fetchRules() async {
|
||||
if (!_isLoading) _isLoading = true;
|
||||
notifyListeners();
|
||||
try {
|
||||
var rules = await Repository().getRulesList();
|
||||
_rules = rules;
|
||||
} on SocketException catch (e) {
|
||||
log(e.toString());
|
||||
_error = 'Check internet connection.';
|
||||
} on Exception catch (e) {
|
||||
log(e.toString());
|
||||
}
|
||||
_isLoading = false;
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
}
|
||||
@@ -3,13 +3,70 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:linting_tool/model/rules_store.dart';
|
||||
import 'package:linting_tool/layout/adaptive.dart';
|
||||
import 'package:linting_tool/widgets/lint_expansion_tile.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class HomePage extends StatelessWidget {
|
||||
const HomePage({Key? key}) : super(key: key);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
// TODO(abd99): Implement HomePage, showing a list of supported lint rules.
|
||||
return const Text('Home');
|
||||
return Consumer<RuleStore>(
|
||||
builder: (context, rulesStore, child) {
|
||||
if (rulesStore.isLoading) {
|
||||
return const CircularProgressIndicator.adaptive();
|
||||
}
|
||||
|
||||
if (!rulesStore.isLoading) {
|
||||
if (rulesStore.rules.isNotEmpty) {
|
||||
final isDesktop = isDisplayLarge(context);
|
||||
final isTablet = isDisplayMedium(context);
|
||||
final startPadding = isTablet
|
||||
? 60.0
|
||||
: isDesktop
|
||||
? 120.0
|
||||
: 4.0;
|
||||
final endPadding = isTablet
|
||||
? 60.0
|
||||
: isDesktop
|
||||
? 120.0
|
||||
: 4.0;
|
||||
|
||||
return ListView.separated(
|
||||
padding: EdgeInsetsDirectional.only(
|
||||
start: startPadding,
|
||||
end: endPadding,
|
||||
top: isDesktop ? 28 : 0,
|
||||
bottom: isDesktop ? kToolbarHeight : 0,
|
||||
),
|
||||
itemCount: rulesStore.rules.length,
|
||||
cacheExtent: 5,
|
||||
itemBuilder: (context, index) {
|
||||
return LintExpansionTile(
|
||||
rule: rulesStore.rules[index],
|
||||
);
|
||||
},
|
||||
separatorBuilder: (context, index) => const SizedBox(height: 4),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Text(rulesStore.error ?? 'Failed to load rules.'),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
IconButton(
|
||||
onPressed: () => rulesStore.fetchRules(),
|
||||
icon: const Icon(Icons.refresh),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
26
experimental/linting_tool/lib/repository/api_provider.dart
Normal file
26
experimental/linting_tool/lib/repository/api_provider.dart
Normal file
@@ -0,0 +1,26 @@
|
||||
// Copyright 2021 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
|
||||
class APIProvider {
|
||||
final _baseURL = 'https://dart-lang.github.io/linter';
|
||||
Future<List<Rule>> getRulesList() async {
|
||||
http.Response response =
|
||||
await http.get(Uri.parse('$_baseURL//lints/machine/rules.json'));
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
List<Rule> rulesList = [];
|
||||
final data = json.decode(response.body) as List;
|
||||
for (var item in data) {
|
||||
rulesList.add(Rule.fromJson(item as Map<String, dynamic>));
|
||||
}
|
||||
return rulesList;
|
||||
} else {
|
||||
throw Exception('Failed to load rules');
|
||||
}
|
||||
}
|
||||
}
|
||||
12
experimental/linting_tool/lib/repository/repository.dart
Normal file
12
experimental/linting_tool/lib/repository/repository.dart
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright 2021 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
import 'package:linting_tool/repository/api_provider.dart';
|
||||
|
||||
class Repository {
|
||||
final _apiProvider = APIProvider();
|
||||
|
||||
Future<List<Rule>> getRulesList() => _apiProvider.getRulesList();
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:google_fonts/google_fonts.dart';
|
||||
import 'package:linting_tool/theme/colors.dart';
|
||||
|
||||
@@ -205,4 +206,22 @@ class AppTheme {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static MarkdownStyleSheet buildMarkDownTheme(ThemeData theme) {
|
||||
final textTheme = theme.textTheme;
|
||||
return MarkdownStyleSheet.largeFromTheme(theme).copyWith(
|
||||
strong: textTheme.subtitle2!,
|
||||
em: textTheme.bodyText2!.copyWith(
|
||||
fontWeight: FontWeight.w900,
|
||||
fontStyle: FontStyle.italic,
|
||||
),
|
||||
codeblockPadding: const EdgeInsets.all(8),
|
||||
codeblockDecoration: BoxDecoration(
|
||||
color: Colors.grey.shade100,
|
||||
),
|
||||
code: TextStyle(
|
||||
backgroundColor: Colors.grey.shade100,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
141
experimental/linting_tool/lib/widgets/lint_expansion_tile.dart
Normal file
141
experimental/linting_tool/lib/widgets/lint_expansion_tile.dart
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2021 The Flutter team. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||
import 'package:linting_tool/model/rule.dart';
|
||||
import 'package:linting_tool/theme/app_theme.dart';
|
||||
import 'package:linting_tool/theme/colors.dart';
|
||||
|
||||
class LintExpansionTile extends StatefulWidget {
|
||||
final Rule rule;
|
||||
const LintExpansionTile({
|
||||
required this.rule,
|
||||
Key? key,
|
||||
}) : super(key: key);
|
||||
|
||||
@override
|
||||
_LintExpansionTileState createState() => _LintExpansionTileState();
|
||||
}
|
||||
|
||||
class _LintExpansionTileState extends State<LintExpansionTile> {
|
||||
var isExpanded = false;
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
var theme = Theme.of(context);
|
||||
var textTheme = theme.textTheme;
|
||||
final rule = widget.rule;
|
||||
final incompatibleString =
|
||||
rule.incompatible.isNotEmpty ? rule.incompatible.join(', ') : 'none';
|
||||
final setsString = rule.sets.isNotEmpty ? rule.sets.join(', ') : 'none';
|
||||
return ExpansionTile(
|
||||
collapsedBackgroundColor: AppColors.white50,
|
||||
title: Text(
|
||||
rule.name,
|
||||
style: textTheme.subtitle1!.copyWith(
|
||||
fontWeight: FontWeight.w700,
|
||||
),
|
||||
),
|
||||
subtitle: Text(
|
||||
rule.description,
|
||||
style: textTheme.caption!,
|
||||
),
|
||||
initiallyExpanded: isExpanded,
|
||||
onExpansionChanged: (value) {
|
||||
setState(() {
|
||||
isExpanded = value;
|
||||
});
|
||||
},
|
||||
expandedAlignment: Alignment.centerLeft,
|
||||
childrenPadding: const EdgeInsets.symmetric(
|
||||
horizontal: 16.0,
|
||||
vertical: 8.0,
|
||||
),
|
||||
backgroundColor: AppColors.white50,
|
||||
maintainState: true,
|
||||
expandedCrossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Group:',
|
||||
style: textTheme.subtitle2,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' ${rule.group}',
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Maturity:',
|
||||
style: textTheme.subtitle2,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' ${rule.maturity}',
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Incompatible:',
|
||||
style: textTheme.subtitle2,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' $incompatibleString',
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
Text.rich(
|
||||
TextSpan(
|
||||
children: [
|
||||
TextSpan(
|
||||
text: 'Sets:',
|
||||
style: textTheme.subtitle2,
|
||||
),
|
||||
TextSpan(
|
||||
text: ' $setsString',
|
||||
),
|
||||
],
|
||||
),
|
||||
textAlign: TextAlign.left,
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
MarkdownBody(
|
||||
data: rule.details,
|
||||
selectable: true,
|
||||
styleSheet: AppTheme.buildMarkDownTheme(theme),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 8.0,
|
||||
),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ElevatedButton(
|
||||
child: const Text('Add to profile'),
|
||||
onPressed: () {
|
||||
// TODO(abd99): Iplement adding to a profile.
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(
|
||||
height: 16.0,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user