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 Internal Lints
-
-
-
-
-
-
-
-
-
- π Documentation β’
- πΌοΈ Widgets β’
- π€ API Reference β’
- πΊοΈ Road Map
-
-
-
- Forui is a Flutter UI library that provides a set of beautifully designed, minimalistic widgets.
-
-
-
-
-
-
-
-## 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 [