linear_date_selector 0.0.2
linear_date_selector: ^0.0.2 copied to clipboard
A lightweight and customizable Flutter widget for selecting dates in a horizontal or vertical linear list with support for disabled dates and custom UI.
linear_date_selector π #
A lightweight, customizable horizontal/vertical date selector widget for Flutter.
linear_date_selector provides a simple, robust UI for displaying a linear sequence of dates (e.g., today + next N days) with built-in selection, disabled-date support, animations, and a fully custom tile builderβsimilar to ListView.builder.
Screenshots #
![]() |
![]() |
![]() |
|---|
| Default (Horizontal) | Default (Vertical) | Custom Tiles (Horizontal) |
β¨ Features #
- Auto-generated date list starting from a provided
todaysDateTime - Horizontal or vertical scrolling
- Default, polished tile UI out of the box
- Fully customizable tiles with
LinearDateSelector.builder(...) - Disable specific dates (non-selectable)
- Optional icon with flexible alignment:
top,bottom,left,right - Tap scale (click) animation
- Optional background and text color animations
- Customizable animation durations
- Simple API and small footprint β no heavy calendar widgets
π Getting started #
Add to your pubspec.yaml after publishing:
dependencies:
linear_date_selector: ^0.0.1
Then run:
flutter pub get
Usage #
Important: this package's constructors use startDateTime (item index 0 == startDateTime).
Default tiles (quick start) #
import 'package:flutter/material.dart';
import 'package:linear_date_selector/linear_date_selector.dart';
// inside a widget build
LinearDateSelector(
startDateTime: DateTime.now(),
itemCount: 7,
onDateTimeSelected: (selected) {
print('selected: $selected');
},
// optional: an icon shown inside each tile
icon: Icon(Icons.event),
iconAlignment: LinearDateSelectorIconAlignment.bottom,
// optional sizing
itemWidth: 72,
itemHeight: 80,
);
π§ͺ Custom builder (full control) #
Use .builder to get (context, date, isSelected, isDisabled, index, style) and return any widget:
LinearDateSelector.builder(
listPadding: const EdgeInsets.all(8),
startDateTime: DateTime.now(),
itemCount: 10,
itemHeight: 120,
axis: Axis.horizontal,
disabledDateTimes: [
DateTime.now().add(const Duration(days: 4)),
],
onDateTimeSelected: (d) => print('selected $d'),
itemBuilder: (
context,
date,
isSelected,
isDisabled,
index,
_,
) {
return AnimatedContainer(
duration: const Duration(milliseconds: 200),
margin: const EdgeInsets.symmetric(horizontal: 6, vertical: 8),
decoration: BoxDecoration(
color: isDisabled
? Colors.grey.shade200
: (isSelected ? const Color(0xFF1e1405) : const Color(0xFFf8e9d7)),
borderRadius: BorderRadius.circular(10),
border: Border.all(
color: isDisabled
? Colors.grey
: (isSelected ? const Color(0xFF1e1405) : const Color(0xFFf8e9d7)),
),
),
child: Column(
children: [
Center(
child: Text.rich(
TextSpan(
children: [
TextSpan(
text: '${DateFormat('dd').format(date)}\n',
style: const TextStyle(fontSize: 28),
),
TextSpan(
text: DateFormat('MMM\nyyyy').format(date),
style: const TextStyle(fontSize: 14),
),
],
),
textAlign: TextAlign.center,
style: TextStyle(
color: isDisabled
? Colors.grey
: (isSelected
? const Color(0xFFf8e9d7)
: const Color(0xFF1e1405)),
fontWeight: isSelected ? FontWeight.bold : FontWeight.normal,
),
),
),
],
),
);
},
);
Note: The
indexparameter corresponds to the tile's position (0 istodaysDateTime). Use it for special styling (e.g., first/last tile) or animations.
π Date-range (default tiles) #
LinearDateSelector.dateRange(
startDateTime: DateTime(2025, 1, 1),
endDateTime: DateTime(2025, 1, 7), // inclusive β 1,2,3,4,5,6,7
onDateTimeSelected: (selected) {
print('selected: $selected');
},
// optional extras
icon: const Icon(Icons.event),
iconAlignment: LinearDateSelectorIconAlignment.top,
itemWidth: 70,
itemHeight: 85,
enableColorAnimation: true,
listPadding: const EdgeInsets.symmetric(horizontal: 12),
);
π¨ π§ͺ Date-range + custom builder (full control) #
LinearDateSelector.dateRangeBuilder(
startDateTime: DateTime(2025, 2, 10),
endDateTime: DateTime(2025, 2, 15), // inclusive (6 days)
disabledDateTimes: [
DateTime(2025, 2, 12), // middle day disabled
],
onDateTimeSelected: (selected) {
print('picked: $selected');
},
listPadding: const EdgeInsets.all(10),
axis: Axis.horizontal,
itemWidth: 90,
itemHeight: 120,
itemBuilder: (
context,
date,
isSelected,
isDisabled,
index,
style,
) {
final day = DateFormat('dd').format(date);
final month = DateFormat('MMM').format(date);
return AnimatedContainer(
duration: const Duration(milliseconds: 250),
margin: const EdgeInsets.symmetric(horizontal: 6),
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: isDisabled
? Colors.grey.shade300
: (isSelected ? Colors.blue : Colors.white),
borderRadius: BorderRadius.circular(12),
border: Border.all(
color: isSelected ? Colors.blue : Colors.grey.shade400,
width: 2,
),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(
day,
style: TextStyle(
fontSize: 28,
fontWeight: FontWeight.bold,
color: isDisabled ? Colors.grey : Colors.black,
),
),
Text(
month,
style: TextStyle(
fontSize: 16,
color: isDisabled ? Colors.grey : Colors.black87,
),
),
],
),
);
},
);
π When to use:
-
You want different UI for weekdays vs weekends
-
You want animations, badges, chips, colors, shadows
-
UI designers give you custom Figma layouts
-
You need total control but want range logic for free
Note: .
dateRangeand .dateRangeBuilderignore hours and minutes in the given DateTimes. Only the calendar day is used:
DateTime(2025, 1, 1, 23:00)
β treated as 2025-01-01
API #
LinearDateSelector #
Constructors:
LinearDateSelector(...)β default tiles and optional animations.LinearDateSelector.builder(...)β accept a customitemBuilderfunction.LinearDateSelector.dateRange(...)β inclusive date-range selector (default tile UI). Time-of-day is ignored (dates are normalized to year/month/day). -LinearDateSelector.dateRangeBuilder(...)- Same asdateRange, but allows a fully customitemBuilderfor each date tile. Range generation is handled for you; you only render the tiles.
Important properties:
-
startDateTimeβDateTimewhere the list starts (index 0). -
endDateTimeβDateTimewhere the list ends (optional). -
onDateTimeSelectedβFunction(DateTime)callback when a date is chosen. -
disabledDateTimesβList<DateTime>of dates that should be rendered disabled. -
itemCountβ number of tiles to show (must be > 0). -
axisβAxis.horizontal(default) orAxis.vertical. -
itemWidth,itemHeightβ optional size of each tile. -
icon,iconAlignmentβ optional icon and position. -
listPaddingβ padding around the scrollable list. -
enableClickAnimationβ whether tap-scale animation is enabled (defaulttrue). -
scaleAnimationDurationβ duration for the tap-scale animation. -
enableColorAnimationβ enables background/text color animation for default tiles. -
backgroundColorChangeDuration,textColorChangeDurationβ durations for color animations (used by the default tile implementation). -
physics,controllerβ optionalScrollPhysicsandScrollControllerfor the list. -
itemBuilderβWidget Function(BuildContext context, DateTime date, bool isSelected, bool isDisabled, int index, DateSelectorStyle style)?for fully-custom tile rendering.
DateSelectorStyle #
A simple style holder to customize tile colors, border colors and icon padding. Pass it into the widget's style: parameter to tweak the default tile appearance.
class DateSelectorStyle {
final Color tileBackgroundColor;
final Color selectedTileBackgroundColor;
final Color borderColor;
final Color selectedBorderColor;
final Color selectedTextColor;
final Color disabledTileBackgroundColor;
final Color disabledTextColor;
final Color disabledBorderColor;
final EdgeInsets? iconPadding;
const DateSelectorStyle({...});
}
π§ͺExample: disabling dates #
final today = DateTime.now();
final disabled = [
DateTime(today.year, today.month, today.day + 2),
];
LinearDateSelector(
todaysDateTime: today,
itemCount: 7,
disabledDateTimes: disabled,
onDateTimeSelected: (date) {
// will not be fired for disabled dates
},
);
π§ Accessibility & Tips #
-
Consider wrapping your custom builder tile in
Semanticsto exposeselected/disabledto screen readers. -
The default tile uses
AnimatedContainerandAnimatedDefaultTextStyleto animate color changes whenenableColorAnimationis true. -
For timezone-sensitive apps, normalize dates to avoid surprising disabled/selected behavior.
-
If you want the selector to start with a tile pre-selected, coordinate the initial selection via the parent: call
onDateTimeSelectedafter widget build or extend the widget to accept aninitialSelectedDateparameter.
Behavior & Notes #
-
itemCountmust be > 0 β an assert will fail in debug if it's not. -
disabledDateTimesare matched by calendar day (year-month-day). Time components are ignored, soDateTime(2025, 12, 1, 0, 0)andDateTime(2025, 12, 1, 13, 0)refer to the same disabled date. -
Selection state (
selectedIndex) is stored locally inside the widget. UseonDateTimeSelectedto communicate selection to parent widgets and keep external state in sync if needed. -
The widget precomputes the list of consecutive dates for performance. If you update
startDateTime,itemCount, ordisabledDateTimes, the selector will recompute its internal lists (so changes are reflected). -
If you use different
DateTimetimezones or pass dates with time components, you may want to normalize them (e.g., to local midnight or UTC) before passing to disabledDateTimes for predictable comparisons.
Contributing #
Contributions, bug reports, and feature requests are welcome. Open an issue or submit a PR.
When contributing, please:
- Follow the repository's
analysis_options.dartand formatting conventions. - Add tests for behavior-critical changes (selection, disabled logic, and builder behavior).
Changelog #
- 0.0.1 β Initial release: default tiles, builder API, disabled-date support.
- Recomputes internal lists when important inputs change.
π License #
This package is distributed under the MIT LICENSE. See LICENSE for more information.
Author #
Created by the package author. Feel free to open issues or PRs for improvements.


