Skip to content

Commit

Permalink
feat: date picker (#170)
Browse files Browse the repository at this point in the history
  • Loading branch information
nank1ro authored Nov 4, 2024
1 parent 356ef04 commit ccf0646
Show file tree
Hide file tree
Showing 27 changed files with 4,174 additions and 17 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
## 0.15.0

- **FEAT**: New `ShadDatePicker` component.
- **FEAT**: Add `allowDeselection` property to `ShadCalendar`.
- **FIX**: `ShadSelect` crash when using `optionsBuilder`.
- **FEAT**: Add `itemCount` and `shrinkWrap` to `ShadSelect` and `ShadSelectFormField`.

Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ See the [documentation](https://flutter-shadcn-ui.mariuti.com/) to interact with
- [ ] Command
- [x] [Context Menu](https://flutter-shadcn-ui.mariuti.com/components/context-menu/)
- [ ] Data Table
- [ ] Date Picker
- [x] [Date Picker](https://flutter-shadcn-ui.mariuti.com/components/date-picker/)
- [ ] Drawer
- [x] <strike>Dropdown Menu</strike> Use Context Menu instead
- [x] [Form](https://flutter-shadcn-ui.mariuti.com/components/form/)
Expand Down
154 changes: 154 additions & 0 deletions docs/src/content/docs/Components/date-picker.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
---
title: Date Picker
sidebar:
order: 4
---

import Preview from "../../../components/Preview.astro";

A date picker component with range and presets.

<Preview src="https://shadcn-ui-playground.pages.dev/date-picker?style=single" minHeight="450px">

```dart
class SingleDatePicker extends StatelessWidget {
const SingleDatePicker({super.key});
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: const ShadDatePicker(),
);
}
}
```

</Preview>

## Date Range Picker

<Preview src="https://shadcn-ui-playground.pages.dev/date-picker?style=range" minHeight="450px">

```dart
class RangeDatePicker extends StatelessWidget {
const RangeDatePicker({super.key});
@override
Widget build(BuildContext context) {
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: const ShadDatePicker.range(),
);
}
}
```

</Preview>

## With Presets

<Preview src="https://shadcn-ui-playground.pages.dev/date-picker?style=range" minHeight="450px">

```dart
const presets = {
0: 'Today',
1: 'Tomorrow',
3: 'In 3 days',
7: 'In a week',
};
class PresetsDatePicker extends StatefulWidget {
const PresetsDatePicker({super.key});
@override
State<PresetsDatePicker> createState() => _PresetsDatePickerState();
}
class _PresetsDatePickerState extends State<PresetsDatePicker> {
final groupId = UniqueKey();
final today = DateTime.now().startOfDay;
DateTime? selected;
@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ShadDatePicker(
// Using the same groupId to keep the date picker popover open when the
// select popover is closed.
groupId: groupId,
header: ShadSelect<int>(
groupId: groupId,
minWidth: 284,
placeholder: const Text('Select'),
options: presets.entries
.map((e) => ShadOption(value: e.key, child: Text(e.value)))
.toList(),
selectedOptionBuilder: (context, value) {
return Text(presets[value]!);
},
onChanged: (value) {
if (value == null) return;
setState(() {
selected = today.add(Duration(days: value));
});
},
),
selected: selected,
calendarDecoration: theme.calendarTheme.decoration,
popoverPadding: const EdgeInsets.all(4),
),
);
}
}
```

</Preview>

## Form

<Preview src="https://shadcn-ui-playground.pages.dev/form?style=datePickerField" minHeight="700px">

```dart
ShadDatePickerFormField(
label: const Text('Date of birth'),
onChanged: print,
description: const Text(
'Your date of birth is used to calculate your age.'),
validator: (v) {
if (v == null) {
return 'A date of birth is required.';
}
return null;
},
),
```

</Preview>

## DateRangePickerFormField

<Preview src="https://shadcn-ui-playground.pages.dev/form?style=dateRangePickerField" minHeight="700px">

```dart
ShadDateRangePickerFormField(
label: const Text('Range of dates'),
onChanged: print,
description: const Text(
'Select the range of dates you want to search between.'),
validator: (v) {
if (v == null) return 'A range of dates is required.';
if (v.start == null) {
return 'The start date is required.';
}
if (v.end == null) return 'The end date is required.';
return null;
},
),
```

</Preview>
9 changes: 7 additions & 2 deletions docs/src/content/docs/Components/form.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ title: Form
sidebar:
order: 4
---
import Preview from '../../../components/Preview.astro';

import Preview from "../../../components/Preview.astro";

Builds a form with validation and easy access to form fields values.

<Preview src="https://shadcn-ui-playground.pages.dev/form" minHeight="300px">
```dart

```dart
class FormPage extends StatefulWidget {
const FormPage({
super.key,
Expand Down Expand Up @@ -66,13 +68,16 @@ class _FormPageState extends State<FormPage> {
}
}
```

</Preview>

## Examples

See the following links for more examples on how to use the `ShadForm` component with other components:

- [Checkbox](../checkbox#form)
- [Switch](../switch#form)
- [Input](../input#form)
- [Select](../select#form)
- [RadioGroup](../radio-group#form)
- [DatePicker](../date-picker#form)
4 changes: 4 additions & 0 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import 'package:example/pages/card.dart';
import 'package:example/pages/checkbox.dart';
import 'package:example/pages/checkbox_form_field.dart';
import 'package:example/pages/context_menu.dart';
import 'package:example/pages/date_picker.dart';
import 'package:example/pages/date_picker_form_field.dart';
import 'package:example/pages/dialog.dart';
import 'package:example/pages/image.dart';
import 'package:example/pages/input.dart';
Expand Down Expand Up @@ -51,6 +53,8 @@ final routes = <String, WidgetBuilder>{
'/checkbox': (_) => const CheckboxPage(),
'/checkbox-form-field': (_) => const CheckboxFormFieldPage(),
'/context-menu': (_) => const ContextMenuPage(),
'/date-picker': (_) => const DatePickerPage(),
'/date-picker-form-field': (_) => const DatePickerFormFieldPage(),
'/dialog': (_) => const DialogPage(),
'/image': (_) => const ImagePage(),
'/input': (_) => const InputPage(),
Expand Down
98 changes: 98 additions & 0 deletions example/lib/pages/date_picker.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
import 'package:example/common/base_scaffold.dart';
import 'package:example/common/properties/bool_property.dart';
import 'package:flutter/material.dart';
import 'package:shadcn_ui/shadcn_ui.dart';

const presets = {
0: 'Today',
1: 'Tomorrow',
3: 'In 3 days',
7: 'In a week',
};

class DatePickerPage extends StatefulWidget {
const DatePickerPage({super.key});

@override
State<DatePickerPage> createState() => _DatePickerPageState();
}

class _DatePickerPageState extends State<DatePickerPage> {
bool closeOnSelection = false;
bool allowDeselection = true;
final today = DateTime.now().startOfDay;
final groupId = UniqueKey();

DateTime? selected;

@override
Widget build(BuildContext context) {
final theme = ShadTheme.of(context);
return BaseScaffold(
appBarTitle: 'DatePicker',
editable: [
MyBoolProperty(
label: 'closeOnSelection',
value: closeOnSelection,
onChanged: (value) => setState(() => closeOnSelection = value),
),
MyBoolProperty(
label: 'allowDeselection',
value: allowDeselection,
onChanged: (value) => setState(() => allowDeselection = value),
),
],
children: [
Text('Single', style: theme.textTheme.h4),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ShadDatePicker(
closeOnSelection: closeOnSelection,
allowDeselection: allowDeselection,
),
),
const Divider(),
Text('Range', style: theme.textTheme.h4),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ShadDatePicker.range(
closeOnSelection: closeOnSelection,
allowDeselection: allowDeselection,
),
),
const Divider(),
Text('With Presets', style: theme.textTheme.h4),
ConstrainedBox(
constraints: const BoxConstraints(maxWidth: 600),
child: ShadDatePicker(
// Using the same groupId to keep the date picker popover open when the
// select popover is closed.
groupId: groupId,
header: ShadSelect<int>(
groupId: groupId,
minWidth: 284,
placeholder: const Text('Select'),
options: presets.entries
.map((e) => ShadOption(value: e.key, child: Text(e.value)))
.toList(),
selectedOptionBuilder: (context, value) {
return Text(presets[value]!);
},
onChanged: (value) {
if (value == null) return;
setState(() {
selected = today.add(Duration(days: value));
});
},
),
closeOnSelection: closeOnSelection,
allowDeselection: allowDeselection,
selected: selected,
calendarDecoration: theme.calendarTheme.decoration,
popoverPadding: const EdgeInsets.all(4),
),
),
],
);
}
}
Loading

0 comments on commit ccf0646

Please sign in to comment.