advance_ruler_slider 0.0.1
advance_ruler_slider: ^0.0.1 copied to clipboard
A highly customizable and reusable Flutter ruler/scale slider widget with optional haptic feedback, scroll animations, and custom indicator support.
📏 Advance Ruler Slider
A highly customizable and reusable Flutter ruler/scale slider widget designed for precise value selection. This package offers a flexible and visually rich UI component that can be adapted to a wide range of applications, from health trackers to audio editors.
✨ Features
The advance_ruler_slider comes packed with a wide array of features to give you full control over its appearance and behavior:
Customizable Value Range: Easily define the minValue and maxValue for your ruler.
Granular Tick Control:
Set majorTickInterval for primary markers (e.g., every 10 units).
Control minorTickCount between major ticks for finer subdivisions.
Customize majorTickColor, minorTickColor, majorTickWidth, minorTickWidth, majorTickLength, and minorTickLength.
Choose the tickStrokeCap (e.g., StrokeCap.round, StrokeCap.square) for tick ends.
Directional Flexibility: Render the ruler as either Axis.horizontal or Axis.vertical.
Dynamic Value Display:
Central Indicator: A prominent indicator at the center of the ruler's viewport.
Optional Numeric Value: Toggle the visibility of the numeric value displayed at the indicator using showIndicatorValue.
Customizable Indicator:
Define indicatorColor and indicatorWidth.
Apply indicatorGradient for a visually rich indicator (overrides indicatorColor).
Add an optional triangular pointer/arrow using showIndicatorPointer, with customizable indicatorPointerLength, indicatorPointerWidth, and indicatorPointerColor.
Full Customization: Provide your own customIndicator widget for ultimate design freedom (overrides all default indicator styling).
Background Styling:
Set a solid backgroundColor.
Apply a backgroundGradient for dynamic backgrounds (overrides backgroundColor).
Add borderRadius to the ruler's container for rounded corners.
User Interaction & Feedback:
Smooth Scroll Animations: Programmatic jumps (jumpToValue) and initial scroll can use animateTo for smooth transitions by setting useScrollAnimation to true. Customize scrollAnimationDuration and scrollAnimationCurve.
Haptic Feedback: Enable hapticFeedbackEnabled to provide tactile feedback (HapticFeedback.selectionClick()) whenever the ruler's value changes.
Read-Only Mode: Disable user scrolling interaction with isReadOnly, while still allowing programmatic control.
Custom Scroll Physics: Apply any ScrollPhysics (e.g., BouncingScrollPhysics, ClampingScrollPhysics) using the scrollPhysics property.
Snap to Ticks on Release: Automatically animate the ruler to snap its indicator to the nearest major or minor tick after a user completes a scroll gesture using snapToTicksOnRelease.
Label Customization:
Define labelStyle for the tick labels.
Provide a labelFormatter function for advanced text formatting (e.g., adding units, custom text).
Adjust labelOffset to control the spacing between tick marks and their labels.
Option to always show minValue and maxValue labels, even if they don't align with major ticks, using showBoundaryLabels.
Active Range Highlighting: Visually highlight the currently visible portion of the ruler with activeRangeColor and activeRangeOpacity.
Scrollable Value Clamping: Restrict the effective scrollable range of the ruler using minScrollValue and maxScrollValue, which can be a sub-range of minValue and maxValue.
📦 Installation
To use this package, add advance_ruler_slider to your pubspec.yaml file:
dependencies: advance_ruler_slider: ^0.0.1 # Always use the latest version
Then, run flutter pub get in your project's root directory.
🚀 Basic Usage
Here's a simple example demonstrating a horizontal weight ruler:
import 'package:flutter/material.dart'; import 'package:advance_ruler_slider/advance_ruler_slider.dart'; // Import your package
class BasicRulerDemo extends StatefulWidget { const BasicRulerDemo({super.key});
@override State
class _BasicRulerDemoState extends State
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Basic Ruler Slider Demo'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Selected Weight: ${_currentWeight.toStringAsFixed(1)} kg', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), Container( height: 80, // Height for the horizontal ruler margin: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.blue.shade50, borderRadius: BorderRadius.circular(15), border: Border.all(color: Colors.blue.shade200, width: 2), ), child: RulerScale( controller: _weightRulerController, minValue: 0.0, maxValue: 150.0, initialValue: _currentWeight, majorTickInterval: 10.0, minorTickCount: 9, // 9 minor ticks means 10 segments (1.0 granularity) pixelsPerUnit: 25.0, // Adjust for desired spacing indicatorColor: Colors.blueAccent, indicatorWidth: 3.0, labelStyle: const TextStyle(color: Colors.black87, fontSize: 13), onValueChanged: (value) { setState(() { _currentWeight = value; }); }, showIndicatorPointer: true, indicatorPointerLength: 12.0, indicatorPointerWidth: 12.0, indicatorPointerColor: Colors.blueAccent, hapticFeedbackEnabled: true, useScrollAnimation: true, borderRadius: BorderRadius.circular(15), ), ), const SizedBox(height: 30), ElevatedButton( onPressed: () { _weightRulerController.jumpToValue(95.0); // Programmatic jump }, child: const Text('Jump to 95.0 kg'), ), ], ), ), ); } }
// To run this example, replace your main.dart content with: // void main() { // runApp(const MaterialApp(home: BasicRulerDemo())); // }
🖼️ Screenshots / GIFs
(Replace this section with actual screenshots or GIFs of your ruler in action. This is crucial for attracting users!)
⚙️ Advanced Usage & Customization Examples
The RulerScale widget is highly configurable. Here are more examples demonstrating various properties:
Vertical Ruler with Custom Formatting and Physics
import 'package:flutter/material.dart'; import 'package:advance_ruler_slider/advance_ruler_slider.dart';
class AdvancedVerticalRuler extends StatefulWidget { const AdvancedVerticalRuler({super.key});
@override State
class _AdvancedVerticalRulerState extends State
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Vertical Ruler Demo')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Selected Height: ${_currentHeight.toStringAsFixed(1)}m', style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold), ), const SizedBox(height: 20), Container( width: 100, // Width for the vertical ruler height: 300, // Height for the vertical ruler decoration: BoxDecoration( color: Colors.green.shade50, borderRadius: BorderRadius.circular(10), gradient: LinearGradient( colors: [Colors.green.shade100, Colors.green.shade300], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), ), child: RulerScale( direction: Axis.vertical, minValue: 0.0, maxValue: 20.0, majorTickInterval: 5.0, minorTickCount: 4, // 4 minor ticks = 5 segments between major ticks (0.5 granularity) pixelsPerUnit: 50.0, // More space for vertical ruler initialValue: _currentHeight, indicatorColor: Colors.green.shade700, indicatorWidth: 3.0, rulerExtent: 80.0, // Width of the ruler component itself labelStyle: const TextStyle(color: Colors.green, fontSize: 14), onValueChanged: (value) { setState(() { _currentHeight = value; }); }, showIndicatorValue: true, showIndicatorPointer: true, indicatorPointerLength: 15.0, indicatorPointerWidth: 15.0, indicatorPointerColor: Colors.green.shade900, hapticFeedbackEnabled: true, labelFormatter: (value) { if (value == 0) return 'Start'; if (value == 20) return 'End'; return '${value.toStringAsFixed(1)}m'; // Custom unit }, activeRangeColor: Colors.green, activeRangeOpacity: 0.1, showBoundaryLabels: true, onScrollStart: () => print('Vertical ruler scroll started!'), onScrollEnd: () => print('Vertical ruler scroll ended!'), snapToTicksOnRelease: true, // Snap to nearest tick after scroll scrollPhysics: const BouncingScrollPhysics(), // iOS-like bounce indicatorGradient: LinearGradient( colors: [Colors.lime.shade200, Colors.lime.shade700], begin: Alignment.topCenter, end: Alignment.bottomCenter, ), minScrollValue: 5.0, // User can only scroll between 5.0 and 15.0 maxScrollValue: 15.0, ), ), ], ), ), ); } }
// To run this example, replace your main.dart content with: // void main() { // runApp(const MaterialApp(home: AdvancedVerticalRuler())); // }
Ruler with a Custom Indicator Widget
import 'package:flutter/material.dart'; import 'package:advance_ruler_slider/advance_ruler_slider.dart';
class CustomIndicatorRuler extends StatefulWidget { const CustomIndicatorRuler({super.key});
@override State
class _CustomIndicatorRulerState extends State
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Custom Indicator Demo')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Selected Value: ${_currentValue.toStringAsFixed(0)}%', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), Container( height: 100, margin: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.orange.shade50, borderRadius: BorderRadius.circular(15), ), child: RulerScale( direction: Axis.horizontal, minValue: 0.0, maxValue: 100.0, majorTickInterval: 25.0, minorTickCount: 4, pixelsPerUnit: 40.0, initialValue: _currentValue, rulerExtent: 70.0, labelStyle: const TextStyle(color: Colors.orange, fontSize: 14), onValueChanged: (value) { setState(() { _currentValue = value; }); }, // Provide a custom indicator widget customIndicator: Container( width: 40, height: 40, decoration: BoxDecoration( color: Colors.orange.shade700, shape: BoxShape.circle, boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.2), blurRadius: 5, spreadRadius: 2, ), ], ), child: const Icon(Icons.star, color: Colors.white, size: 24), ), showIndicatorValue: true, // Value will still appear below custom indicator labelOffset: 10.0, // Adjust label offset if needed ), ), ], ), ), ); } }
// To run this example, replace your main.dart content with: // void main() { // runApp(const MaterialApp(home: CustomIndicatorRuler())); // }
Read-Only Ruler
import 'package:flutter/material.dart'; import 'package:advance_ruler_slider/advance_ruler_slider.dart';
class ReadOnlyRulerDemo extends StatefulWidget { const ReadOnlyRulerDemo({super.key});
@override State
class _ReadOnlyRulerDemoState extends State
@override Widget build(BuildContext context) { return Scaffold( appBar: AppBar(title: const Text('Read-Only Ruler Demo')), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text( 'Display Value: ${_displayValue.toStringAsFixed(1)}', style: const TextStyle(fontSize: 20, fontWeight: FontWeight.bold), ), const SizedBox(height: 30), Container( height: 80, margin: const EdgeInsets.symmetric(horizontal: 20), decoration: BoxDecoration( color: Colors.grey.shade100, borderRadius: BorderRadius.circular(10), border: Border.all(color: Colors.grey.shade300, width: 1), ), child: RulerScale( controller: _readOnlyRulerController, minValue: 0.0, maxValue: 200.0, initialValue: _displayValue, majorTickInterval: 20.0, minorTickCount: 9, pixelsPerUnit: 15.0, indicatorColor: Colors.purple, labelStyle: const TextStyle(color: Colors.black54, fontSize: 12), isReadOnly: true, // Make the ruler read-only showIndicatorValue: true, showIndicatorPointer: true, indicatorPointerColor: Colors.purple, hapticFeedbackEnabled: false, // No haptic feedback for read-only ), ), const SizedBox(height: 30), ElevatedButton( onPressed: () { // You can still programmatically change the value setState(() { _displayValue = (_displayValue + 10).clamp(0.0, 200.0); }); _readOnlyRulerController.jumpToValue(_displayValue); }, child: const Text('Increment Value'), ), ], ), ), ); } }
// To run this example, replace your main.dart content with: // void main() { // runApp(const MaterialApp(home: ReadOnlyRulerDemo())); // }
📚 API Reference
Here's a quick overview of the available properties for the RulerScale widget:
Property
Type
Default Value
Description
minValue
double
0.0
The minimum value of the ruler.
maxValue
double
100.0
The maximum value of the ruler.
majorTickInterval
double
10.0
The interval between major ticks (e.g., 10.0 for every 10 units).
minorTickCount
int
9
The number of minor ticks between major ticks. (9 minor ticks means 10 segments).
direction
Axis
Axis.horizontal
The orientation of the ruler (Axis.horizontal or Axis.vertical).
majorTickColor
Color
Colors.black
Color of the major ticks.
minorTickColor
Color
Colors.grey
Color of the minor ticks.
majorTickWidth
double
2.0
Width of the major ticks.
minorTickWidth
double
1.0
Width of the minor ticks.
majorTickLength
double
20.0
Length of the major ticks.
minorTickLength
double
10.0
Length of the minor ticks.
labelStyle
TextStyle
TextStyle(...)
Text style for the labels displayed on major ticks.
indicatorColor
Color
Colors.red
Color of the central indicator line. Ignored if customIndicator or indicatorGradient is provided.
indicatorWidth
double
2.0
Width of the central indicator line.
backgroundColor
Color
Colors.white
Background color of the ruler. Ignored if backgroundGradient is provided.
rulerExtent
double
60.0
The fixed dimension of the ruler (height for horizontal, width for vertical).
onValueChanged
ValueChanged
null
Callback triggered when the value at the center of the ruler changes (during scroll and on jump).
onScrollStart
VoidCallback?
null
Callback triggered when user scrolling starts.
onScrollEnd
VoidCallback?
null
Callback triggered when user scrolling stops.
initialValue
double
0.0
The initial value to set the ruler to.
pixelsPerUnit
double
20.0
How many pixels represent one unit of value. Controls ruler density/spacing. Increase this value to make the ruler appear more spread out and allow for finer manual control.
step
double
1.0
The smallest logical increment for the ruler's value. The displayed value will snap to multiples of this step.
controller
RulerScaleController?
null
An optional controller to programmatically control the ruler (e.g., jumpToValue).
showIndicatorValue
bool
true
Whether to show the numeric value at the indicator.
showIndicatorPointer
bool
false
Whether to show a triangular pointer on the indicator. Ignored if customIndicator is provided.
indicatorPointerLength
double
10.0
The length (height) of the triangular pointer.
indicatorPointerWidth
double
10.0
The width (base) of the triangular pointer.
indicatorPointerColor
Color?
null
The color of the indicator pointer. Defaults to indicatorColor if null.
useScrollAnimation
bool
false
Whether to use smooth animation for programmatic jumps and initial scroll.
scrollAnimationDuration
Duration
Duration(300ms)
The duration of the scroll animation when useScrollAnimation is true.
scrollAnimationCurve
Curve
Curves.easeOutCubic
The curve of the scroll animation when useScrollAnimation is true.
hapticFeedbackEnabled
bool
false
Whether to enable haptic feedback (HapticFeedback.selectionClick()) when the value changes.
borderRadius
BorderRadiusGeometry?
null
The border radius for the ruler's background container.
isReadOnly
bool
false
Whether the ruler is read-only (disables user scrolling).
labelFormatter
String Function(double)?
null
A custom function to format the labels displayed on the ticks.
activeRangeColor
Color?
null
The color to use for highlighting the currently visible range. If null, no highlight.
activeRangeOpacity
double
0.2
The opacity of the active range highlight (0.0 to 1.0).
showBoundaryLabels
bool
false
Whether to always show labels for minValue and maxValue.
scrollPhysics
ScrollPhysics?
null
Custom scroll physics for the SingleChildScrollView (e.g., BouncingScrollPhysics).
customIndicator
Widget?
null
A custom widget to use as the indicator. Overrides default indicator styling.
labelOffset
double
5.0
The offset from the tick mark where the label should be painted.
tickStrokeCap
StrokeCap
StrokeCap.round
The stroke cap style for the tick marks.
backgroundGradient
Gradient?
null
A gradient to use for the background of the ruler. Overrides backgroundColor.
indicatorGradient
Gradient?
null
A gradient to use for the default line indicator. Overrides indicatorColor.
minScrollValue
double?
null (uses minValue)
The minimum value that the ruler can scroll to or report. Clamps the effective scrollable range.
maxScrollValue
double?
null (uses maxValue)
The maximum value that the ruler can scroll to or report. Clamps the effective scrollable range.
snapToTicksOnRelease
bool
false
If true, the ruler will animate to snap its indicator to the nearest major or minor tick after a user finishes scrolling.
🤝 Contributing
Contributions are welcome! If you find a bug, have a feature request, or want to contribute code, please feel free to:
Open an issue: Describe the bug or feature you'd like to see.
Submit a pull request: Fork the repository, make your changes, and submit a pull request.
Please ensure your code adheres to the existing style and includes relevant tests.
📄 License
This package is distributed under the MIT License. See the LICENSE file for more information.