[linting_tool] Implement editing profile (#874)

pull/875/head
Abdullah Deshmukh 4 years ago committed by GitHub
parent bd4fa28584
commit 1e00fd0bde
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -0,0 +1,51 @@
// 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:linting_tool/model/profile.dart';
import 'package:linting_tool/model/profiles_store.dart';
import 'package:linting_tool/model/rule.dart';
class EditingController extends ChangeNotifier {
bool _isEditing;
EditingController({bool? isEditing}) : _isEditing = isEditing ?? false;
bool get isEditing => _isEditing;
set isEditing(bool enabled) {
_selectedRules.clear();
_isEditing = enabled;
notifyListeners();
}
final List<Rule> _selectedRules = [];
List<Rule> get selectedRules => _selectedRules;
void selectRule(Rule rule) {
_selectedRules.add(rule);
notifyListeners();
}
void deselectRule(Rule rule) {
_selectedRules.remove(rule);
notifyListeners();
}
Future deleteSelected(
RulesProfile profile, ProfilesStore profilesStore) async {
var rules = profile.rules;
for (var rule in _selectedRules) {
rules.remove(rule);
}
RulesProfile newProfile = RulesProfile(name: profile.name, rules: rules);
await profilesStore.updateProfile(profile, newProfile);
isEditing = false;
notifyListeners();
}
}

@ -76,6 +76,21 @@ class ProfilesStore extends ChangeNotifier {
});
}
Future<void> updateProfile(
RulesProfile oldProfile, RulesProfile newProfile) async {
await HiveService.updateBox<RulesProfile>(oldProfile, newProfile, _boxName);
await Future.delayed(const Duration(milliseconds: 100), () async {
await fetchSavedProfiles();
});
}
Future<void> removeRuleFromProfile(RulesProfile profile, Rule rule) async {
var newProfile =
RulesProfile(name: profile.name, rules: profile.rules..remove(rule));
await updateProfile(profile, newProfile);
}
Future<void> deleteProfile(RulesProfile profile) async {
await HiveService.deleteBox<RulesProfile>(profile, _boxName);

@ -2,16 +2,19 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:context_menus/context_menus.dart';
import 'package:flutter/material.dart';
import 'package:linting_tool/layout/adaptive.dart';
import 'package:linting_tool/model/profile.dart';
import 'package:linting_tool/model/editing_controller.dart';
import 'package:linting_tool/model/profiles_store.dart';
import 'package:linting_tool/widgets/saved_rule_tile.dart';
import 'package:provider/provider.dart';
class RulesPage extends StatelessWidget {
final RulesProfile profile;
final int selectedProfileIndex;
const RulesPage({
required this.profile,
required this.selectedProfileIndex,
Key? key,
}) : super(key: key);
@ -33,7 +36,10 @@ class RulesPage extends StatelessWidget {
return Scaffold(
appBar: AppBar(
title: Text(
profile.name,
context
.read<ProfilesStore>()
.savedProfiles[selectedProfileIndex]
.name,
style: textTheme.subtitle2!.copyWith(
color: textTheme.bodyText1!.color,
),
@ -53,7 +59,19 @@ class RulesPage extends StatelessWidget {
backgroundColor: Colors.white,
brightness: Brightness.light,
),
body: ListView.separated(
body: ContextMenuOverlay(
child: Consumer<ProfilesStore>(
builder: (context, profilesStore, child) {
var profile = profilesStore.savedProfiles[selectedProfileIndex];
return profile.rules.isEmpty
? const Center(
child: Text('There are no rules added to the profile.'),
)
: Row(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Expanded(
child: ListView.separated(
padding: EdgeInsetsDirectional.only(
start: startPadding,
end: endPadding,
@ -63,11 +81,81 @@ class RulesPage extends StatelessWidget {
itemCount: profile.rules.length,
cacheExtent: 5,
itemBuilder: (context, index) {
return SavedRuleTile(
return ContextMenuRegion(
contextMenu: GenericContextMenu(
buttonConfigs: [
ContextMenuButtonConfig(
'Remove from profile',
onPressed: () {
context
.read<ProfilesStore>()
.removeRuleFromProfile(
profile, profile.rules[index]);
},
),
],
),
child: SavedRuleTile(
rule: profile.rules[index],
),
);
},
separatorBuilder: (context, index) =>
const SizedBox(height: 4),
),
),
Padding(
padding: const EdgeInsetsDirectional.only(top: 28),
child: Row(
children: [
Consumer<EditingController>(
builder: (context, editingController, child) {
var isEditing = editingController.isEditing;
return isEditing
? Column(
children: [
IconButton(
icon: const Icon(Icons.done),
onPressed: () {
editingController.isEditing =
false;
},
),
if (editingController
.selectedRules.isNotEmpty)
IconButton(
icon: const Icon(Icons.delete),
onPressed: () {
editingController
.deleteSelected(
profile,
profilesStore,
);
},
separatorBuilder: (context, index) => const SizedBox(height: 4),
),
],
)
: IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
editingController.isEditing = true;
},
);
},
),
SizedBox(
width: isTablet
? 30
: isDesktop
? 60
: 16),
],
),
),
],
);
},
),
),
);
}

@ -5,6 +5,7 @@
import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:linting_tool/layout/adaptive.dart';
import 'package:linting_tool/model/editing_controller.dart';
import 'package:linting_tool/model/profiles_store.dart';
import 'package:linting_tool/pages/rules_page.dart';
import 'package:linting_tool/theme/colors.dart';
@ -56,7 +57,10 @@ class SavedLintsPage extends StatelessWidget {
Navigator.push<void>(
context,
MaterialPageRoute(
builder: (context) => RulesPage(profile: profile),
builder: (context) => ChangeNotifierProvider(
create: (context) => EditingController(),
child: RulesPage(selectedProfileIndex: index),
),
),
);
},
@ -66,7 +70,16 @@ class SavedLintsPage extends StatelessWidget {
IconButton(
icon: const Icon(Icons.edit),
onPressed: () {
// TODO(abd99): Implement edit functionality.
Navigator.push<void>(
context,
MaterialPageRoute(
builder: (context) => ChangeNotifierProvider(
create: (context) =>
EditingController(isEditing: true),
child: RulesPage(selectedProfileIndex: index),
),
),
);
},
),
const SizedBox(

@ -4,9 +4,11 @@
import 'package:flutter/material.dart';
import 'package:flutter_markdown/flutter_markdown.dart';
import 'package:linting_tool/model/editing_controller.dart';
import 'package:linting_tool/model/rule.dart';
import 'package:linting_tool/theme/app_theme.dart';
import 'package:linting_tool/theme/colors.dart';
import 'package:provider/provider.dart';
class SavedRuleTile extends StatefulWidget {
final Rule rule;
@ -21,6 +23,8 @@ class SavedRuleTile extends StatefulWidget {
class _SavedRuleTileState extends State<SavedRuleTile> {
var isExpanded = false;
var isSelected = false;
@override
Widget build(BuildContext context) {
var theme = Theme.of(context);
@ -30,10 +34,29 @@ class _SavedRuleTileState extends State<SavedRuleTile> {
rule.incompatible.isNotEmpty ? rule.incompatible.join(', ') : 'none';
final setsString = rule.sets.isNotEmpty ? rule.sets.join(', ') : 'none';
// TODO(abd99): Add option to remove rule from profile.
// TODO(abd99): Add right click functionality.
return Consumer<EditingController>(
builder: (context, editingController, child) {
return ExpansionTile(
collapsedBackgroundColor: AppColors.white50,
leading: editingController.isEditing
? Checkbox(
value: isSelected &&
editingController.selectedRules.contains(rule),
onChanged: (value) {
if (value!) {
editingController.selectRule(rule);
setState(() {
isSelected = value;
});
} else {
editingController.deselectRule(rule);
setState(() {
isSelected = value;
});
}
},
)
: null,
title: Text(
rule.name,
style: textTheme.subtitle1!.copyWith(
@ -128,5 +151,7 @@ class _SavedRuleTileState extends State<SavedRuleTile> {
),
],
);
},
);
}
}

@ -5,9 +5,13 @@
#include "generated_plugin_registrant.h"
#include <file_selector_linux/file_selector_plugin.h>
#include <url_launcher_linux/url_launcher_plugin.h>
void fl_register_plugins(FlPluginRegistry* registry) {
g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "FileSelectorPlugin");
file_selector_plugin_register_with_registrar(file_selector_linux_registrar);
g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =
fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin");
url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);
}

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_linux
url_launcher_linux
)
set(PLUGIN_BUNDLED_LIBRARIES)

@ -7,8 +7,10 @@ import Foundation
import file_selector_macos
import path_provider_macos
import url_launcher_macos
func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {
FileSelectorPlugin.register(with: registry.registrar(forPlugin: "FileSelectorPlugin"))
PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin"))
UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin"))
}

@ -4,11 +4,14 @@ PODS:
- FlutterMacOS (1.0.0)
- path_provider_macos (0.0.1):
- FlutterMacOS
- url_launcher_macos (0.0.1):
- FlutterMacOS
DEPENDENCIES:
- file_selector_macos (from `Flutter/ephemeral/.symlinks/plugins/file_selector_macos/macos`)
- FlutterMacOS (from `Flutter/ephemeral`)
- path_provider_macos (from `Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos`)
- url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`)
EXTERNAL SOURCES:
file_selector_macos:
@ -17,11 +20,14 @@ EXTERNAL SOURCES:
:path: Flutter/ephemeral
path_provider_macos:
:path: Flutter/ephemeral/.symlinks/plugins/path_provider_macos/macos
url_launcher_macos:
:path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos
SPEC CHECKSUMS:
file_selector_macos: ff6dc948d4ddd34e8602a1f60b7d0b4cc6051a47
FlutterMacOS: 57701585bf7de1b3fc2bb61f6378d73bbdea8424
path_provider_macos: a0a3fd666cb7cd0448e936fb4abad4052961002b
url_launcher_macos: 45af3d61de06997666568a7149c1be98b41c95d4
PODFILE CHECKSUM: 6eac6b3292e5142cfc23bdeb71848a40ec51c14c

@ -148,6 +148,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.15.0"
context_menus:
dependency: "direct main"
description:
name: context_menus
url: "https://pub.dartlang.org"
source: hosted
version: "0.1.0+5"
convert:
dependency: transitive
description:
@ -651,6 +658,48 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.3.0"
url_launcher:
dependency: transitive
description:
name: url_launcher
url: "https://pub.dartlang.org"
source: hosted
version: "6.0.9"
url_launcher_linux:
dependency: transitive
description:
name: url_launcher_linux
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
url_launcher_macos:
dependency: transitive
description:
name: url_launcher_macos
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
url_launcher_platform_interface:
dependency: transitive
description:
name: url_launcher_platform_interface
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.4"
url_launcher_web:
dependency: transitive
description:
name: url_launcher_web
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.2"
url_launcher_windows:
dependency: transitive
description:
name: url_launcher_windows
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
vector_math:
dependency: transitive
description:

@ -27,6 +27,7 @@ dependencies:
mockito: ^5.0.13
provider: ^5.0.0
yaml: ^3.1.0
context_menus: ^0.1.0+5
dev_dependencies:
flutter_test:

@ -5,8 +5,11 @@
#include "generated_plugin_registrant.h"
#include <file_selector_windows/file_selector_plugin.h>
#include <url_launcher_windows/url_launcher_plugin.h>
void RegisterPlugins(flutter::PluginRegistry* registry) {
FileSelectorPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("FileSelectorPlugin"));
UrlLauncherPluginRegisterWithRegistrar(
registry->GetRegistrarForPlugin("UrlLauncherPlugin"));
}

@ -4,6 +4,7 @@
list(APPEND FLUTTER_PLUGIN_LIST
file_selector_windows
url_launcher_windows
)
set(PLUGIN_BUNDLED_LIBRARIES)

Loading…
Cancel
Save