diff --git a/docs/pages/docs/form/slider.mdx b/docs/pages/docs/form/slider.mdx index c75764f76..fb115b952 100644 --- a/docs/pages/docs/form/slider.mdx +++ b/docs/pages/docs/form/slider.mdx @@ -31,7 +31,7 @@ An input where the user selects a value from within a given range. ```dart FSlider( - layout: Layout.rtl, + layout: FLayout.rtl, tooltipBuilder: (style, value) => Text('${(value.toStringAsFixed(2)}%'), controller: FContinuousSliderController( allowedInteraction: FSliderInteraction.tap, @@ -231,7 +231,7 @@ describes the mark's value. FSlider( label: const Text('Volume'), description: const Text('Adjust the volume by dragging the slider.'), - layout: Layout.btt, + layout: FLayout.btt, controller: FContinuousSliderController(selection: FSliderSelection(max: 0.35)), trackMainAxisExtent: 350, marks: const [ diff --git a/forui/.gitignore b/forui/.gitignore index 4e0566141..efb7b56ef 100644 --- a/forui/.gitignore +++ b/forui/.gitignore @@ -32,3 +32,4 @@ build/ test/golden/failures/ test/**/*_test.mocks.dart /.temp/ +custom_lint.log diff --git a/forui/CHANGELOG.md b/forui/CHANGELOG.md index 6505e7c0c..7c9f88a49 100644 --- a/forui/CHANGELOG.md +++ b/forui/CHANGELOG.md @@ -38,6 +38,10 @@ * Change `FSelectMenuTile` to be scrollable. +* Change `ThemeBuildContext` to `FThemeBuildContext`. + +* **Breaking** Change `Layout` to `FLayout`. + * **Breaking** Change `FTileData.index` to `FTileData.last`. * **Breaking** Change `FPopoverMenu.controller` to `FPopoverMenu.popoverController`. diff --git a/forui/README.md b/forui/README.md index 2ee9c67ef..3287df86f 100644 --- a/forui/README.md +++ b/forui/README.md @@ -1,41 +1,3 @@ - -

- Forui -

-
+# Forui Internal Lints -

- GitHub Actions Workflow Status - Pub Version - GitHub Repo stars - GitHub commit activity - Discord -

- -

- πŸ“š Documentation β€’ - πŸ–ΌοΈ Widgets β€’ - πŸ€“ API Reference β€’ - πŸ—ΊοΈ Road Map -

- -

- Forui is a Flutter UI library that provides a set of beautifully designed, minimalistic widgets. -

- -
-
- Forui -
- -## Documentation - -Visit [forui.dev/docs](https://forui.dev/docs) to view the documentation. - -## Contributing - -Please read the [contributing guide](https://github.com/forus-labs/forui/blob/main/CONTRIBUTING.md). - -## License - -Licensed under the [MIT License](https://github.com/forus-labs/forui/blob/main/forui/LICENSE) and [Open Font License](https://github.com/forus-labs/forui/blob/main/forui/LICENSE). \ No newline at end of file +Internal Lints used by the Forui project. Not intended for public use. \ No newline at end of file diff --git a/forui/analysis_options.yaml b/forui/analysis_options.yaml index 61f85845a..5de2367a6 100644 --- a/forui/analysis_options.yaml +++ b/forui/analysis_options.yaml @@ -1,9 +1,11 @@ include: package:flint/analysis_options.flutter.yaml analyzer: + plugins: + - custom_lint errors: unused_result: ignore linter: rules: - use_key_in_widget_constructors - - require_trailing_commas + - require_trailing_commas \ No newline at end of file diff --git a/forui/example/pubspec.lock b/forui/example/pubspec.lock index 498cffe37..b35981699 100644 --- a/forui/example/pubspec.lock +++ b/forui/example/pubspec.lock @@ -236,10 +236,10 @@ packages: dependency: transitive description: name: flutter_svg - sha256: "936d9c1c010d3e234d1672574636f3352b4941ca3decaddd3cafaeb9ad49c471" + sha256: "54900a1a1243f3c4a5506d853a2b5c2dbc38d5f27e52a52618a8054401431123" url: "https://pub.dev" source: hosted - version: "2.0.15" + version: "2.0.16" flutter_test: dependency: "direct dev" description: flutter @@ -501,18 +501,18 @@ packages: dependency: transitive description: name: path_provider_android - sha256: c464428172cb986b758c6d1724c603097febb8fb855aa265aeecc9280c294d4a + sha256: "8c4967f8b7cb46dc914e178daa29813d83ae502e0529d7b0478330616a691ef7" url: "https://pub.dev" source: hosted - version: "2.2.12" + version: "2.2.14" path_provider_foundation: dependency: transitive description: name: path_provider_foundation - sha256: f234384a3fdd67f989b4d54a5d73ca2a6c422fa55ae694381ae0f4375cd1ea16 + sha256: "4843174df4d288f5e29185bd6e72a6fbdf5a4a4602717eed565497429f179942" url: "https://pub.dev" source: hosted - version: "2.4.0" + version: "2.4.1" path_provider_linux: dependency: transitive description: @@ -714,10 +714,10 @@ packages: dependency: transitive description: name: vector_graphics_compiler - sha256: ab9ff38fc771e9ee1139320adbe3d18a60327370c218c60752068ebee4b49ab1 + sha256: "1b4b9e706a10294258727674a340ae0d6e64a7231980f9f9a3d12e4b42407aad" url: "https://pub.dev" source: hosted - version: "1.1.15" + version: "1.1.16" vector_math: dependency: transitive description: @@ -786,10 +786,10 @@ packages: dependency: transitive description: name: win32 - sha256: "84ba388638ed7a8cb3445a320c8273136ab2631cd5f2c57888335504ddab1bc2" + sha256: "8b338d4486ab3fbc0ba0db9f9b4f5239b6697fcee427939a40e720cbb9ee0a69" url: "https://pub.dev" source: hosted - version: "5.8.0" + version: "5.9.0" xdg_directories: dependency: transitive description: diff --git a/forui/lib/src/foundation/rendering.dart b/forui/lib/src/foundation/rendering.dart index 736256478..62e9e6049 100644 --- a/forui/lib/src/foundation/rendering.dart +++ b/forui/lib/src/foundation/rendering.dart @@ -3,7 +3,7 @@ import 'package:flutter/rendering.dart'; import 'package:meta/meta.dart'; /// Possible way to layout a sequence of items. -enum Layout { +enum FLayout { /// Lays out the items horizontally from left to right. ltr(vertical: false), @@ -19,7 +19,7 @@ enum Layout { /// Whether the layout is vertical. final bool vertical; - const Layout({required this.vertical}); + const FLayout({required this.vertical}); } @internal diff --git a/forui/lib/src/theme/theme.dart b/forui/lib/src/theme/theme.dart index 66fd7d208..a83df7636 100644 --- a/forui/lib/src/theme/theme.dart +++ b/forui/lib/src/theme/theme.dart @@ -8,7 +8,7 @@ import 'package:forui/forui.dart'; /// Applies a theme to descendant widgets. /// /// A theme configures the colors and typographic choices of Forui widgets. The actual configuration is stored in -/// a [FThemeData]. Descendant widgets obtain the current theme's [FThemeData] via either [ThemeBuildContext.theme], +/// a [FThemeData]. Descendant widgets obtain the current theme's [FThemeData] via either [FThemeBuildContext.theme], /// or [FTheme.of]. When a widget uses either, it is automatically rebuilt if the theme later changes. /// /// ```dart @@ -35,7 +35,7 @@ import 'package:forui/forui.dart'; class FTheme extends StatelessWidget { /// Returns the current [FThemeData], or `FThemes.zinc.light` if there is no ancestor [FTheme]. /// - /// It is recommended to use the terser [ThemeBuildContext.theme] getter instead. + /// It is recommended to use the terser [FThemeBuildContext.theme] getter instead. /// /// ## Troubleshooting: /// @@ -141,7 +141,7 @@ class _InheritedTheme extends InheritedTheme { } /// Provides functions for accessing the current [FThemeData]. -extension ThemeBuildContext on BuildContext { +extension FThemeBuildContext on BuildContext { /// Returns the current [FThemeData], or `FThemes.zinc.light` if there is no ancestor [FTheme]. /// /// ## Troubleshooting: diff --git a/forui/lib/src/widgets/slider/inherited_data.dart b/forui/lib/src/widgets/slider/inherited_data.dart index 533525716..da4cfa686 100644 --- a/forui/lib/src/widgets/slider/inherited_data.dart +++ b/forui/lib/src/widgets/slider/inherited_data.dart @@ -15,7 +15,7 @@ final class InheritedData extends InheritedWidget { } final FSliderStyle style; - final Layout layout; + final FLayout layout; final List marks; final double? trackMainAxisExtent; final double? trackHitRegionCrossExtent; diff --git a/forui/lib/src/widgets/slider/slider.dart b/forui/lib/src/widgets/slider/slider.dart index f4890e0ce..c90e3cbe2 100644 --- a/forui/lib/src/widgets/slider/slider.dart +++ b/forui/lib/src/widgets/slider/slider.dart @@ -40,7 +40,7 @@ class FSlider extends StatelessWidget { final FSliderStyle? style; /// The layout. Defaults to the current [TextDirection]. - final Layout? layout; + final FLayout? layout; /// The label. final Widget? label; @@ -158,8 +158,8 @@ class FSlider extends StatelessWidget { final styles = context.theme.sliderStyles; final layout = switch (this.layout) { final layout? => layout, - _ when Directionality.maybeOf(context) == TextDirection.rtl => Layout.rtl, - _ => Layout.ltr, + _ when Directionality.maybeOf(context) == TextDirection.rtl => FLayout.rtl, + _ => FLayout.ltr, }; final sliderStyle = style ?? (layout.vertical ? styles.verticalStyle : styles.horizontalStyle); @@ -219,7 +219,7 @@ class FSlider extends StatelessWidget { class _Slider extends StatefulWidget { final FSliderController controller; final FSliderStyle style; - final Layout layout; + final FLayout layout; final Widget? label; final Widget? description; final Widget Function(BuildContext, String) errorBuilder; diff --git a/forui/lib/src/widgets/slider/slider_mark.dart b/forui/lib/src/widgets/slider/slider_mark.dart index 797e6f8c3..c1ad73f79 100644 --- a/forui/lib/src/widgets/slider/slider_mark.dart +++ b/forui/lib/src/widgets/slider/slider_mark.dart @@ -75,7 +75,7 @@ final class FSliderMarkStyle with Diagnosticable { /// The label's offset from the slider, along its cross axis, in logical pixels. The top-left corner is always the /// origin, regardless of the layout. /// - /// For example, if the layout is [Layout.ltr] and the cross axis offset is 3, the label will be 3 pixels below the + /// For example, if the layout is [FLayout.ltr] and the cross axis offset is 3, the label will be 3 pixels below the /// slider's edge. /// /// ``` diff --git a/forui/lib/src/widgets/slider/slider_render_object.dart b/forui/lib/src/widgets/slider/slider_render_object.dart index 24caba26b..95ea4a9cc 100644 --- a/forui/lib/src/widgets/slider/slider_render_object.dart +++ b/forui/lib/src/widgets/slider/slider_render_object.dart @@ -175,7 +175,7 @@ abstract class _RenderSlider extends RenderBox with ContainerRenderObjectMixin, RenderBoxContainerDefaultsMixin { FSliderStyle _style; FSliderStateStyle _stateStyle; - Layout _layout; + FLayout _layout; List _marks; double? _mainAxisExtent; late Rect Function(RenderBox, Size, FSliderMark, FSliderMarkStyle) _positionMark; @@ -237,22 +237,22 @@ abstract class _RenderSlider extends RenderBox Rect Function(RenderBox, Size, FSliderMark, FSliderMarkStyle) get _position { final insets = _style.labelLayoutStyle.childPadding; return switch (_layout) { - Layout.ltr => (track, label, mark, style) { + FLayout.ltr => (track, label, mark, style) { final extent = track.size.width - insets.left - insets.right; final offset = _anchor(extent, mark.value, insets.left, insets.top, style); return _rect(label, mark, Offset(offset.$1, offset.$2), style); }, - Layout.rtl => (track, size, mark, style) { + FLayout.rtl => (track, size, mark, style) { final extent = track.size.width - insets.left - insets.right; final offset = _anchor(extent, 1 - mark.value, insets.left, insets.top, style); return _rect(size, mark, Offset(offset.$1, offset.$2), style); }, - Layout.ttb => (track, size, mark, style) { + FLayout.ttb => (track, size, mark, style) { final extent = track.size.height - insets.top - insets.bottom; final offset = _anchor(extent, mark.value, insets.top, insets.left, style); return _rect(size, mark, Offset(offset.$2, offset.$1), style); }, - Layout.btt => (track, size, mark, style) { + FLayout.btt => (track, size, mark, style) { final extent = track.size.height - insets.top - insets.bottom; final offset = _anchor(extent, 1 - mark.value, insets.top, insets.left, style); return _rect(size, mark, Offset(offset.$2, offset.$1), style); @@ -332,9 +332,9 @@ abstract class _RenderSlider extends RenderBox markNeedsLayout(); } - Layout get sliderLayout => _layout; + FLayout get sliderLayout => _layout; - set sliderLayout(Layout value) { + set sliderLayout(FLayout value) { if (_layout == value) { return; } diff --git a/forui/lib/src/widgets/slider/thumb.dart b/forui/lib/src/widgets/slider/thumb.dart index 395e87118..d32131bad 100644 --- a/forui/lib/src/widgets/slider/thumb.dart +++ b/forui/lib/src/widgets/slider/thumb.dart @@ -186,26 +186,26 @@ class _ThumbState extends State with SingleTickerProviderStateMixin { double _offset(FSliderSelection selection) => widget.min ? selection.offset.min : selection.offset.max; - Map _shortcuts(Layout layout) => switch ((layout, widget.min)) { - (Layout.ltr, true) || (Layout.rtl, false) => const { + Map _shortcuts(FLayout layout) => switch ((layout, widget.min)) { + (FLayout.ltr, true) || (FLayout.rtl, false) => const { SingleActivator(LogicalKeyboardKey.arrowLeft): _ExtendIntent(), SingleActivator(LogicalKeyboardKey.arrowRight): _ShrinkIntent(), }, - (Layout.ltr, false) || (Layout.rtl, true) => const { + (FLayout.ltr, false) || (FLayout.rtl, true) => const { SingleActivator(LogicalKeyboardKey.arrowLeft): _ShrinkIntent(), SingleActivator(LogicalKeyboardKey.arrowRight): _ExtendIntent(), }, - (Layout.ttb, true) || (Layout.btt, false) => const { + (FLayout.ttb, true) || (FLayout.btt, false) => const { SingleActivator(LogicalKeyboardKey.arrowUp): _ExtendIntent(), SingleActivator(LogicalKeyboardKey.arrowDown): _ShrinkIntent(), }, - (Layout.ttb, false) || (Layout.btt, true) => const { + (FLayout.ttb, false) || (FLayout.btt, true) => const { SingleActivator(LogicalKeyboardKey.arrowUp): _ShrinkIntent(), SingleActivator(LogicalKeyboardKey.arrowDown): _ExtendIntent(), }, }; - GestureDragUpdateCallback? _drag(FSliderController controller, double thumbSize, Layout layout) { + GestureDragUpdateCallback? _drag(FSliderController controller, double thumbSize, FLayout layout) { if (controller.allowedInteraction == FSliderInteraction.tap) { return null; } @@ -297,11 +297,11 @@ final class FSliderThumbStyle with Diagnosticable { } @internal -extension Layouts on Layout { +extension Layouts on FLayout { double Function(Offset) translateThumbDrag(double thumbSize) => switch (this) { - Layout.ltr => (delta) => delta.dx - thumbSize / 2, - Layout.rtl => (delta) => -delta.dx + thumbSize / 2, - Layout.ttb => (delta) => delta.dy - thumbSize / 2, - Layout.btt => (delta) => -delta.dy + thumbSize / 2, + FLayout.ltr => (delta) => delta.dx - thumbSize / 2, + FLayout.rtl => (delta) => -delta.dx + thumbSize / 2, + FLayout.ttb => (delta) => delta.dy - thumbSize / 2, + FLayout.btt => (delta) => -delta.dy + thumbSize / 2, }; } diff --git a/forui/lib/src/widgets/slider/track.dart b/forui/lib/src/widgets/slider/track.dart index d56b15fe1..1d6667b1b 100644 --- a/forui/lib/src/widgets/slider/track.dart +++ b/forui/lib/src/widgets/slider/track.dart @@ -122,7 +122,7 @@ class _GestureDetectorState extends State<_GestureDetector> { } } - GestureTapDownCallback? _tap(FSliderController controller, double thumbSize, Layout layout) { + GestureTapDownCallback? _tap(FSliderController controller, double thumbSize, FLayout layout) { final translate = layout.translateTrackTap(controller.selection.rawExtent.total, thumbSize); void down(TapDownDetails details) { @@ -144,7 +144,7 @@ class _GestureDetectorState extends State<_GestureDetector> { return tappable.contains(controller.allowedInteraction) ? down : null; } - GestureDragUpdateCallback? _drag(FSliderController controller, Layout layout) { + GestureDragUpdateCallback? _drag(FSliderController controller, FLayout layout) { if (controller.allowedInteraction != FSliderInteraction.slide) { return null; } @@ -242,25 +242,25 @@ class ActiveTrack extends StatelessWidget { } @internal -extension Layouts on Layout { +extension Layouts on FLayout { double Function(Offset) translateTrackTap(double extent, double thumbSize) => switch (this) { - Layout.ltr => (offset) => offset.dx - thumbSize / 2, - Layout.rtl => (offset) => extent - offset.dx + thumbSize / 2, - Layout.ttb => (offset) => offset.dy - thumbSize / 2, - Layout.btt => (offset) => extent - offset.dy + thumbSize / 2, + FLayout.ltr => (offset) => offset.dx - thumbSize / 2, + FLayout.rtl => (offset) => extent - offset.dx + thumbSize / 2, + FLayout.ttb => (offset) => offset.dy - thumbSize / 2, + FLayout.btt => (offset) => extent - offset.dy + thumbSize / 2, }; double Function(Offset) translateTrackDrag() => switch (this) { - Layout.ltr => (delta) => delta.dx, - Layout.rtl => (delta) => -delta.dx, - Layout.ttb => (delta) => delta.dy, - Layout.btt => (delta) => -delta.dy, + FLayout.ltr => (delta) => delta.dx, + FLayout.rtl => (delta) => -delta.dx, + FLayout.ttb => (delta) => delta.dy, + FLayout.btt => (delta) => -delta.dy, }; Positioned Function({required double offset, required Widget child}) get position => switch (this) { - Layout.ltr => ({required offset, required child}) => Positioned(left: offset, child: child), - Layout.rtl => ({required offset, required child}) => Positioned(right: offset, child: child), - Layout.ttb => ({required offset, required child}) => Positioned(top: offset, child: child), - Layout.btt => ({required offset, required child}) => Positioned(bottom: offset, child: child), + FLayout.ltr => ({required offset, required child}) => Positioned(left: offset, child: child), + FLayout.rtl => ({required offset, required child}) => Positioned(right: offset, child: child), + FLayout.ttb => ({required offset, required child}) => Positioned(top: offset, child: child), + FLayout.btt => ({required offset, required child}) => Positioned(bottom: offset, child: child), }; } diff --git a/forui/pubspec.yaml b/forui/pubspec.yaml index 140eba6eb..90ed6af70 100644 --- a/forui/pubspec.yaml +++ b/forui/pubspec.yaml @@ -28,12 +28,14 @@ dependencies: nitrogen_types: ^0.3.0+1 sugar: ^3.1.0 - dev_dependencies: build_runner: ^2.4.0 + custom_lint: ^0.6.6 flint: ^2.8.1 flutter_test: sdk: flutter + forui_internal_lints: + path: ../forui_internal_lints http: ^1.2.2 mockito: ^5.4.4 diff --git a/forui/test/src/widgets/slider/slider_golden_test.dart b/forui/test/src/widgets/slider/slider_golden_test.dart index 9723ea784..b52ea1856 100644 --- a/forui/test/src/widgets/slider/slider_golden_test.dart +++ b/forui/test/src/widgets/slider/slider_golden_test.dart @@ -36,7 +36,7 @@ void main() { }); for (final theme in TestScaffold.themes) { - for (final layout in Layout.values) { + for (final layout in FLayout.values) { for (final touch in [true, false]) { for (final enabled in [true, false]) { testWidgets('${theme.name} - $layout - ${enabled ? 'enabled' : 'disabled'}', (tester) async { @@ -176,7 +176,7 @@ void main() { } } - for (final layout in Layout.values) { + for (final layout in FLayout.values) { for (final min in [true, false]) { testWidgets('single value - $layout - ${min ? 'min' : 'max'}', (tester) async { await tester.pumpWidget( @@ -214,7 +214,7 @@ void main() { } } - for (final layout in Layout.values) { + for (final layout in FLayout.values) { group('label offset - $layout', () { late FSliderStyle sliderStyle; late Alignment positive; diff --git a/forui/test/src/widgets/slider/slider_test.dart b/forui/test/src/widgets/slider/slider_test.dart index 71c8feace..ac5a21fc5 100644 --- a/forui/test/src/widgets/slider/slider_test.dart +++ b/forui/test/src/widgets/slider/slider_test.dart @@ -116,7 +116,7 @@ void main() { }); }); - for (final layout in Layout.values) { + for (final layout in FLayout.values) { Widget slider(FSliderController controller) => TestScaffold.app( padded: false, child: FSlider( @@ -343,26 +343,26 @@ void main() { } extension on Rect { - Offset min(Layout layout) => switch (layout) { - Layout.ltr => centerLeft, - Layout.rtl => centerRight, - Layout.ttb => topCenter, - Layout.btt => bottomCenter, + Offset min(FLayout layout) => switch (layout) { + FLayout.ltr => centerLeft, + FLayout.rtl => centerRight, + FLayout.ttb => topCenter, + FLayout.btt => bottomCenter, }; - Offset max(Layout layout) => switch (layout) { - Layout.ltr => centerRight, - Layout.rtl => centerLeft, - Layout.ttb => bottomCenter, - Layout.btt => topCenter, + Offset max(FLayout layout) => switch (layout) { + FLayout.ltr => centerRight, + FLayout.rtl => centerLeft, + FLayout.ttb => bottomCenter, + FLayout.btt => topCenter, }; } -extension on Layout { +extension on FLayout { Offset directional(double value) => switch (this) { - Layout.ltr => Offset(value, 0), - Layout.rtl => Offset(-value, 0), - Layout.ttb => Offset(0, value), - Layout.btt => Offset(0, -value), + FLayout.ltr => Offset(value, 0), + FLayout.rtl => Offset(-value, 0), + FLayout.ttb => Offset(0, value), + FLayout.btt => Offset(0, -value), }; } diff --git a/forui_internal_lints/.gitignore b/forui_internal_lints/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/forui_internal_lints/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/forui_internal_lints/.metadata b/forui_internal_lints/.metadata new file mode 100644 index 000000000..7025a6406 --- /dev/null +++ b/forui_internal_lints/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668" + channel: "stable" + +project_type: package diff --git a/forui_internal_lints/CHANGELOG.md b/forui_internal_lints/CHANGELOG.md new file mode 100644 index 000000000..41cc7d819 --- /dev/null +++ b/forui_internal_lints/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.0.1 + +* TODO: Describe initial release. diff --git a/forui_internal_lints/LICENSE b/forui_internal_lints/LICENSE new file mode 100644 index 000000000..ba75c69f7 --- /dev/null +++ b/forui_internal_lints/LICENSE @@ -0,0 +1 @@ +TODO: Add your license here. diff --git a/forui_internal_lints/README.md b/forui_internal_lints/README.md new file mode 100644 index 000000000..4a260d8d2 --- /dev/null +++ b/forui_internal_lints/README.md @@ -0,0 +1,39 @@ + + +TODO: Put a short description of the package here that helps potential users +know whether this package might be useful for them. + +## Features + +TODO: List what your package can do. Maybe include images, gifs, or videos. + +## Getting started + +TODO: List prerequisites and provide or point to information on how to +start using the package. + +## Usage + +TODO: Include short and useful examples for package users. Add longer examples +to `/example` folder. + +```dart +const like = 'sample'; +``` + +## Additional information + +TODO: Tell users more about the package: where to find more information, how to +contribute to the package, how to file issues, what response they can expect +from the package authors, and more. diff --git a/forui_internal_lints/analysis_options.yaml b/forui_internal_lints/analysis_options.yaml new file mode 100644 index 000000000..61f85845a --- /dev/null +++ b/forui_internal_lints/analysis_options.yaml @@ -0,0 +1,9 @@ +include: package:flint/analysis_options.flutter.yaml +analyzer: + errors: + unused_result: ignore + +linter: + rules: + - use_key_in_widget_constructors + - require_trailing_commas diff --git a/forui_internal_lints/lib/forui_internal_lints.dart b/forui_internal_lints/lib/forui_internal_lints.dart new file mode 100644 index 000000000..948b78472 --- /dev/null +++ b/forui_internal_lints/lib/forui_internal_lints.dart @@ -0,0 +1,17 @@ +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +import 'package:forui_internal_lints/src/diagnosticable_styles.dart'; +import 'package:forui_internal_lints/src/prefer_specific_diagnostics_properties.dart'; +import 'package:forui_internal_lints/src/prefix_public_types.dart'; + +/// Creates a linter for Forui, +PluginBase createPlugin() => _ForuiLinter(); + +class _ForuiLinter extends PluginBase { + @override + List getLintRules(CustomLintConfigs configs) => const [ + DiagnosticableStylesRule(), + PreferSpecificDiagnosticsProperties(), + PrefixPublicTypesRule(), + ]; +} diff --git a/forui_internal_lints/lib/src/diagnosticable_styles.dart b/forui_internal_lints/lib/src/diagnosticable_styles.dart new file mode 100644 index 000000000..87cafa46b --- /dev/null +++ b/forui_internal_lints/lib/src/diagnosticable_styles.dart @@ -0,0 +1,32 @@ +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +const _code = LintCode( + name: 'diagnosticable_styles', + problemMessage: 'Style(s) should be assignable to Diagnosticable.', +); + +const _suffixes = {'Style', 'Styles'}; + +const _checker = TypeChecker.fromName('Diagnosticable', packageName: 'flutter'); + +/// A lint rule that checks if a class ending with 'Style' or 'Styles' is assignable to Diagnosticable. +class DiagnosticableStylesRule extends DartLintRule { + /// Creates a new [DiagnosticableStylesRule]. + const DiagnosticableStylesRule() : super(code: _code); + + @override + void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) { + context.registry.addClassDeclaration((node) { + final declared = node.declaredElement; + + if (declared?.name case final name? when _suffixes.every((s) => !name.endsWith(s))) { + return; + } + + if (declared case final declared? when declared.isConstructable && !_checker.isAssignableFrom(declared)) { + reporter.atElement(declared, _code); + } + }); + } +} diff --git a/forui_internal_lints/lib/src/prefer_specific_diagnostics_properties.dart b/forui_internal_lints/lib/src/prefer_specific_diagnostics_properties.dart new file mode 100644 index 000000000..9f08cec3b --- /dev/null +++ b/forui_internal_lints/lib/src/prefer_specific_diagnostics_properties.dart @@ -0,0 +1,112 @@ +import 'package:analyzer/error/error.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +const _code = LintCode( + name: 'prefer_specific_diagnostics_properties', + problemMessage: + 'Prefer specific diagnostic properties for colors, enums, functions, iterables, primitives and icons.', +); + +const _diagnosticsProperty = TypeChecker.fromName('DiagnosticsProperty', packageName: 'flutter'); + +const _bool = TypeChecker.fromName('bool'); +const _double = TypeChecker.fromName('double'); +const _iconData = TypeChecker.fromName('IconData', packageName: 'flutter'); +const _int = TypeChecker.fromName('int'); +const _string = TypeChecker.fromName('String'); + +const _exact = TypeChecker.any([ + _bool, + _double, + _iconData, + _int, + _string, +]); + +const _color = TypeChecker.fromName('Color', packageName: 'flutter'); +const _iterable = TypeChecker.fromName('Iterable'); + +/// A lint rule that checks if a DiagnosticsProperty is created with a type. +class PreferSpecificDiagnosticsProperties extends DartLintRule { + /// Creates a new [PreferSpecificDiagnosticsProperties]. + const PreferSpecificDiagnosticsProperties() : super(code: _code); + + @override + void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) { + context.registry.addInstanceCreationExpression((node) { + if (node.staticType case final type when type == null || !_diagnosticsProperty.isExactlyType(type)) { + return; + } + + if (node.argumentList.length < 2) { + return; + } + + final type = node.argumentList.arguments[1].staticType; + if (type == null) { + return; + } + + if (_exact.isExactlyType(type) || + type.isDartCoreEnum || + type.isDartCoreFunction || + _color.isAssignableFromType(type) || + _iterable.isAssignableFromType(type)) { + reporter.atNode(node, _code); + } + }); + } + + @override + List getFixes() => [_Fix()]; +} + +class _Fix extends DartFix { + @override + void run( + CustomLintResolver resolver, + ChangeReporter reporter, + CustomLintContext context, + AnalysisError analysisError, + List others, + ) { + context.registry.addInstanceCreationExpression((node) { + if (!node.sourceRange.intersects(analysisError.sourceRange)) { + return; + } + + final type = node.staticType; // DiagnosticsProperty.lazy is not supported. + if (type == null || node.constructorName.name != null) { + return; + } + + reporter + .createChangeBuilder(message: 'Use specific DiagnosticsProperties.', priority: 100) + .addDartFileEdit((builder) { + switch (type) { + // FlagProperty (bool) is not supported. + case _ when _double.isExactlyType(type): + // It may be more appropriate to use a PercentageProperty but there is no simple way to infer that. + builder.addSimpleReplacement(node.constructorName.sourceRange, 'DoubleProperty'); + case _ when _int.isExactlyType(type): + builder.addSimpleReplacement(node.constructorName.sourceRange, 'IntProperty'); + case _ when _string.isExactlyType(type): + builder.addSimpleReplacement(node.constructorName.sourceRange, 'StringProperty'); + case _ when _iconData.isExactlyType(type): + builder.addSimpleReplacement(node.constructorName.sourceRange, 'IconDataProperty'); + + case _ when type.isDartCoreEnum: + builder.addSimpleReplacement(node.constructorName.sourceRange, 'EnumProperty'); + case _ when type.isDartCoreFunction: + builder.addSimpleReplacement(node.constructorName.sourceRange, 'ObjectFlagProperty.has'); + + case _ when _color.isAssignableFromType(type): + builder.addSimpleReplacement(node.constructorName.sourceRange, 'ColorProperty'); + case _ when _iterable.isAssignableFromType(type): + builder.addSimpleReplacement(node.constructorName.sourceRange, 'IterableProperty'); + } + }); + }); + } +} diff --git a/forui_internal_lints/lib/src/prefix_public_types.dart b/forui_internal_lints/lib/src/prefix_public_types.dart new file mode 100644 index 000000000..51c57b30d --- /dev/null +++ b/forui_internal_lints/lib/src/prefix_public_types.dart @@ -0,0 +1,49 @@ +import 'package:analyzer/dart/ast/ast.dart'; +import 'package:analyzer/dart/element/element.dart'; +import 'package:analyzer/error/listener.dart'; +import 'package:collection/collection.dart'; +import 'package:custom_lint_builder/custom_lint_builder.dart'; + +const _code = LintCode( + name: 'prefix_public_types', + problemMessage: 'Public types should be prefixed with F.', +); + +const _checker = TypeChecker.fromName('internal', packageName: 'meta'); + +/// A lint rule that checks if a public type is prefixed with F. +class PrefixPublicTypesRule extends DartLintRule { + /// Creates a new [PrefixPublicTypesRule]. + const PrefixPublicTypesRule() : super(code: _code); + + @override + void run(CustomLintResolver resolver, ErrorReporter reporter, CustomLintContext context) { + void check(NodeList annotations, Element? element) { + if (element?.source?.fullName case final name? when name.contains('test/src')) { + return; + } + + if (annotations.map((a) => a.element).whereNotNull().any(_checker.isAssignableFrom)) { + return; + } + + if (element == null || element.isPrivate || element.isSynthetic) { + return; + } + + if (element.name case final name? when RegExp('^F[A-Z].*').matchAsPrefix(name) != null) { + return; + } + + reporter.atElement(element, _code); + } + + context.registry + ..addClassDeclaration((node) => check(node.metadata, node.declaredElement)) + ..addEnumDeclaration((node) => check(node.metadata, node.declaredElement)) + ..addMixinDeclaration((node) => check(node.metadata, node.declaredElement)) + ..addExtensionDeclaration((node) => check(node.metadata, node.declaredElement)) + ..addExtensionTypeDeclaration((node) => check(node.metadata, node.declaredElement)) + ..addTypeAlias((node) => check(node.metadata, node.declaredElement)); + } +} diff --git a/forui_internal_lints/pubspec.yaml b/forui_internal_lints/pubspec.yaml new file mode 100644 index 000000000..847a781e7 --- /dev/null +++ b/forui_internal_lints/pubspec.yaml @@ -0,0 +1,65 @@ +name: forui_internal_lints +description: Internal lints used by Forui. Not intended for public use. +version: 0.7.0 +homepage: https://forui.dev/ +documentation: https://forui.dev/docs +repository: https://github.com/forus-labs/forui/tree/main/forui_lints +issue_tracker: https://github.com/forus-labs/forui/issues +publish_to: 'none' + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" + +dependencies: + analyzer: any + analyzer_plugin: any + collection: ^1.18.0 + custom_lint_builder: ^0.6.6 + flutter: + sdk: flutter + +dev_dependencies: + flint: ^2.8.1 + flutter_test: + sdk: flutter + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpeg + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package + +import_sorter: + comments: false diff --git a/forui_internal_lints/testbed/.gitignore b/forui_internal_lints/testbed/.gitignore new file mode 100644 index 000000000..ac5aa9893 --- /dev/null +++ b/forui_internal_lints/testbed/.gitignore @@ -0,0 +1,29 @@ +# Miscellaneous +*.class +*.log +*.pyc +*.swp +.DS_Store +.atom/ +.buildlog/ +.history +.svn/ +migrate_working_dir/ + +# IntelliJ related +*.iml +*.ipr +*.iws +.idea/ + +# The .vscode folder contains launch configuration and tasks you configure in +# VS Code which you may wish to be included in version control, so this line +# is commented out by default. +#.vscode/ + +# Flutter/Dart/Pub related +# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock. +/pubspec.lock +**/doc/api/ +.dart_tool/ +build/ diff --git a/forui_internal_lints/testbed/.metadata b/forui_internal_lints/testbed/.metadata new file mode 100644 index 000000000..7025a6406 --- /dev/null +++ b/forui_internal_lints/testbed/.metadata @@ -0,0 +1,10 @@ +# This file tracks properties of this Flutter project. +# Used by Flutter tool to assess capabilities and perform upgrades etc. +# +# This file should be version controlled and should not be manually edited. + +version: + revision: "dec2ee5c1f98f8e84a7d5380c05eb8a3d0a81668" + channel: "stable" + +project_type: package diff --git a/forui_internal_lints/testbed/README.md b/forui_internal_lints/testbed/README.md new file mode 100644 index 000000000..5cfd4fc7c --- /dev/null +++ b/forui_internal_lints/testbed/README.md @@ -0,0 +1 @@ +Tests for the `forui_internal_lints` package. \ No newline at end of file diff --git a/forui_internal_lints/testbed/analysis_options.yaml b/forui_internal_lints/testbed/analysis_options.yaml new file mode 100644 index 000000000..ffc2f9f18 --- /dev/null +++ b/forui_internal_lints/testbed/analysis_options.yaml @@ -0,0 +1,6 @@ +analyzer: + plugins: + - custom_lint + errors: + unused_result: ignore + unused_element: ignore diff --git a/forui_internal_lints/testbed/lib/src/diagnosticable_styles.dart b/forui_internal_lints/testbed/lib/src/diagnosticable_styles.dart new file mode 100644 index 000000000..0a321ab0e --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/diagnosticable_styles.dart @@ -0,0 +1,13 @@ +import 'package:flutter/foundation.dart'; + +class FGood {} + +class FGoodStyle with Diagnosticable {} + +class FGoodStyles with Diagnosticable {} + +// expect_lint: diagnosticable_styles +class FBadStyle {} + +// expect_lint: diagnosticable_styles +class FBadStyles {} \ No newline at end of file diff --git a/forui_internal_lints/testbed/lib/src/prefix_public_types/class.dart b/forui_internal_lints/testbed/lib/src/prefix_public_types/class.dart new file mode 100644 index 000000000..9987300b8 --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/prefix_public_types/class.dart @@ -0,0 +1,21 @@ +// ignore_for_file: unused_element + +import 'package:meta/meta.dart'; + +class _Good {} + +@internal +class GoodSingle {} + +@internal +@visibleForTesting +class GoodMultiple {} + +class FGood {} + +// expect_lint: prefix_public_types +class Bad {} + +// expect_lint: prefix_public_types +class Fbad {} + diff --git a/forui_internal_lints/testbed/lib/src/prefix_public_types/enum.dart b/forui_internal_lints/testbed/lib/src/prefix_public_types/enum.dart new file mode 100644 index 000000000..afb786fa6 --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/prefix_public_types/enum.dart @@ -0,0 +1,20 @@ +// ignore_for_file: unused_element, unused_field + +import 'package:meta/meta.dart'; + +enum _Good { foo } + +@internal +enum GoodSingle { foo } + +@internal +@visibleForTesting +enum GoodMultiple { foo } + +enum FGood { foo } + +// expect_lint: prefix_public_types +enum Bad { foo } + +// expect_lint: prefix_public_types +enum Fbad { foo } diff --git a/forui_internal_lints/testbed/lib/src/prefix_public_types/extension.dart b/forui_internal_lints/testbed/lib/src/prefix_public_types/extension.dart new file mode 100644 index 000000000..59816aac1 --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/prefix_public_types/extension.dart @@ -0,0 +1,21 @@ +// ignore_for_file: unused_element + +import 'package:meta/meta.dart'; + +extension _Good on Never {} + +@internal +extension GoodSingle on Never {} + +@internal +@visibleForTesting +extension GoodMultiple on Never {} + +extension FGood on Never {} + +// expect_lint: prefix_public_types +extension Bad on Never {} + +// expect_lint: prefix_public_types +extension Fbad on Never {} + diff --git a/forui_internal_lints/testbed/lib/src/prefix_public_types/extension_type.dart b/forui_internal_lints/testbed/lib/src/prefix_public_types/extension_type.dart new file mode 100644 index 000000000..f5f01e7ed --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/prefix_public_types/extension_type.dart @@ -0,0 +1,21 @@ +// ignore_for_file: unused_element + +import 'package:meta/meta.dart'; + +extension type _Good(int i) {} + +@internal +extension type GoodSingle(int i) {} + +@internal +@visibleForTesting +extension type GoodMultiple(int i) {} + +extension type FGood(int i) {} + +// expect_lint: prefix_public_types +extension type Bad(int i) {} + +// expect_lint: prefix_public_types +extension type Fbad(int i) {} + diff --git a/forui_internal_lints/testbed/lib/src/prefix_public_types/mixin.dart b/forui_internal_lints/testbed/lib/src/prefix_public_types/mixin.dart new file mode 100644 index 000000000..6f019469f --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/prefix_public_types/mixin.dart @@ -0,0 +1,21 @@ +// ignore_for_file: unused_element + +import 'package:meta/meta.dart'; + +mixin _Good {} + +@internal +mixin GoodSingle {} + +@internal +@visibleForTesting +mixin GoodMultiple {} + +mixin FGood {} + +// expect_lint: prefix_public_types +mixin Bad {} + +// expect_lint: prefix_public_types +mixin Fbad {} + diff --git a/forui_internal_lints/testbed/lib/src/prefix_public_types/type_alias.dart b/forui_internal_lints/testbed/lib/src/prefix_public_types/type_alias.dart new file mode 100644 index 000000000..4d1ac8a07 --- /dev/null +++ b/forui_internal_lints/testbed/lib/src/prefix_public_types/type_alias.dart @@ -0,0 +1,21 @@ +// ignore_for_file: unused_element + +import 'package:meta/meta.dart'; + +typedef _Good = String; + +@internal +typedef GoodSingle = String; + +@internal +@visibleForTesting +typedef GoodMultiple = String; + +typedef FGood = String; + +// expect_lint: prefix_public_types +typedef Bad = String; + +// expect_lint: prefix_public_types +typedef Fbad = String; + diff --git a/forui_internal_lints/testbed/pubspec.yaml b/forui_internal_lints/testbed/pubspec.yaml new file mode 100644 index 000000000..7a8f59c3e --- /dev/null +++ b/forui_internal_lints/testbed/pubspec.yaml @@ -0,0 +1,59 @@ +name: testbed +description: "A new Flutter project." +version: 0.0.1 +publish_to: 'none' + +environment: + sdk: ">=3.5.0 <4.0.0" + flutter: ">=3.24.0" + +dependencies: + flutter: + sdk: flutter + meta: ^1.11.0 + +dev_dependencies: + custom_lint: ^0.7.0 + flutter_test: + sdk: flutter + forui_internal_lints: + path: ../ + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # To add assets to your package, add an assets section, like this: + # assets: + # - images/a_dot_burr.jpegAct + # - images/a_dot_ham.jpeg + # + # For details regarding assets in packages, see + # https://flutter.dev/to/asset-from-package + # + # An image asset can refer to one or more resolution-specific "variants", see + # https://flutter.dev/to/resolution-aware-images + + # To add custom fonts to your package, add a fonts section here, + # in this "flutter" section. Each entry in this list should have a + # "family" key with the font family name, and a "fonts" key with a + # list giving the asset and other descriptors for the font. For + # example: + # fonts: + # - family: Schyler + # fonts: + # - asset: fonts/Schyler-Regular.ttf + # - asset: fonts/Schyler-Italic.ttf + # style: italic + # - family: Trajan Pro + # fonts: + # - asset: fonts/TrajanPro.ttf + # - asset: fonts/TrajanPro_Bold.ttf + # weight: 700 + # + # For details regarding fonts in packages, see + # https://flutter.dev/to/font-from-package +import_sorter: + comments: false diff --git a/samples/lib/widgets/slider.dart b/samples/lib/widgets/slider.dart index bdf79de18..d0f83caa5 100644 --- a/samples/lib/widgets/slider.dart +++ b/samples/lib/widgets/slider.dart @@ -128,7 +128,7 @@ class VerticalSliderPage extends Sample { child: FSlider( label: const Text('Volume'), description: const Text('Adjust the volume by dragging the slider.'), - layout: Layout.btt, + layout: FLayout.btt, controller: FContinuousSliderController(selection: FSliderSelection(max: 0.35)), trackMainAxisExtent: 350, marks: const [