Fixing label & hint style issues

Moving to a more generic architecture
Updating the analysis_options.yaml file
This commit is contained in:
Gaspard Merten 2021-09-22 20:06:57 +02:00
parent ddcb665d2f
commit 13eaa0df3a
10 changed files with 431 additions and 427 deletions

View file

@ -1,3 +1,9 @@
##2.1.0
* Fixing label & hint style issues
* Moving to a more generic architecture
* Updating the analysis_options.yaml file
##2.0.1 ##2.0.1
* Adding the ability to specify the entry mode for the material date picker. * Adding the ability to specify the entry mode for the material date picker.

View file

@ -15,7 +15,7 @@ In the `pubspec.yaml` of your flutter project, add the following dependency:
```yaml ```yaml
dependencies: dependencies:
... ...
date_field: ^2.0.1 date_field: ^2.1.0
``` ```
In your library add the following import: In your library add the following import:
@ -63,4 +63,4 @@ DateTimeFormField(
), ),
``` ```
You can check the Github repo for a complete example. You can check the GitHub repo for a complete example.

View file

@ -1,43 +1,14 @@
# Specify analysis options.
#
# Until there are meta linter rules, each desired lint must be explicitly enabled.
# See: https://github.com/dart-lang/linter/issues/288
#
# For a list of lints, see: http://dart-lang.github.io/linter/lints/
# See the configuration guide for more
# https://github.com/dart-lang/sdk/tree/master/pkg/analyzer#configuring-the-analyzer
#
# There are other similar analysis options files in the flutter repos,
# which should be kept in sync with this file:
#
# - analysis_options.yaml (this file)
# - packages/flutter/lib/analysis_options_user.yaml
# - https://github.com/flutter/plugins/blob/master/analysis_options.yaml
# - https://github.com/flutter/engine/blob/master/analysis_options.yaml
#
# This file contains the analysis options used by Flutter tools, such as IntelliJ,
# Android Studio, and the `flutter analyze` command.
analyzer: analyzer:
strong-mode: strong-mode:
implicit-casts: false
implicit-dynamic: false implicit-dynamic: false
errors: errors:
# treat missing required parameters as a warning (not a hint)
missing_required_param: warning missing_required_param: warning
# treat missing returns as a warning (not a hint)
missing_return: warning missing_return: warning
# allow having TODOs in the code
todo: ignore
# Ignore analyzer hints for updating pubspecs when using Future or
# Stream and not importing dart:async
# Please see https://github.com/flutter/flutter/pull/24528 for details.
sdk_version_async_exported_from_core: ignore
exclude: exclude:
- "bin/cache/**" - "bin/cache/**"
# the following two are relative to the stocks example and the flutter package respectively # Ignore protoc generated files
# see https://github.com/dart-lang/sdk/issues/28463 - "dev/conductor/lib/proto/*"
- "lib/i18n/stock_messages_*.dart"
- "lib/src/http/**"
linter: linter:
rules: rules:
@ -45,18 +16,22 @@ linter:
# the Dart Lint rules page to make maintenance easier # the Dart Lint rules page to make maintenance easier
# https://github.com/dart-lang/linter/blob/master/example/all.yaml # https://github.com/dart-lang/linter/blob/master/example/all.yaml
- always_declare_return_types - always_declare_return_types
- always_put_control_body_on_new_line
# - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219 # - always_put_required_named_parameters_first # we prefer having parameters in the same order as fields https://github.com/flutter/flutter/issues/10219
- always_require_non_null_named_parameters - always_require_non_null_named_parameters
# - always_specify_types - always_specify_types
# - always_use_package_imports # we do this commonly
- annotate_overrides - annotate_overrides
# - avoid_annotating_with_dynamic # conflicts with always_specify_types # - avoid_annotating_with_dynamic # conflicts with always_specify_types
- avoid_as
- avoid_bool_literals_in_conditional_expressions - avoid_bool_literals_in_conditional_expressions
# - avoid_catches_without_on_clauses # we do this commonly # - avoid_catches_without_on_clauses # we do this commonly
# - avoid_catching_errors # we do this commonly # - avoid_catching_errors # we do this commonly
- avoid_classes_with_only_static_members - avoid_classes_with_only_static_members
# - avoid_double_and_int_checks # only useful when targeting JS runtime # - avoid_double_and_int_checks # only useful when targeting JS runtime
- avoid_dynamic_calls
- avoid_empty_else - avoid_empty_else
- avoid_equals_and_hash_code_on_mutable_classes
- avoid_escaping_inner_quotes
- avoid_field_initializers_in_const_classes - avoid_field_initializers_in_const_classes
- avoid_function_literals_in_foreach_calls - avoid_function_literals_in_foreach_calls
# - avoid_implementing_value_types # not yet tested # - avoid_implementing_value_types # not yet tested
@ -64,7 +39,9 @@ linter:
# - avoid_js_rounded_ints # only useful when targeting JS runtime # - avoid_js_rounded_ints # only useful when targeting JS runtime
- avoid_null_checks_in_equality_operators - avoid_null_checks_in_equality_operators
# - avoid_positional_boolean_parameters # not yet tested # - avoid_positional_boolean_parameters # not yet tested
# - avoid_print # not yet tested
# - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356) # - avoid_private_typedef_functions # we prefer having typedef (discussion in https://github.com/flutter/flutter/pull/16356)
# - avoid_redundant_argument_values # not yet tested
- avoid_relative_lib_imports - avoid_relative_lib_imports
- avoid_renaming_method_parameters - avoid_renaming_method_parameters
- avoid_return_types_on_setters - avoid_return_types_on_setters
@ -73,42 +50,56 @@ linter:
- avoid_returning_null_for_void - avoid_returning_null_for_void
# - avoid_returning_this # there are plenty of valid reasons to return this # - avoid_returning_this # there are plenty of valid reasons to return this
# - avoid_setters_without_getters # not yet tested # - avoid_setters_without_getters # not yet tested
# - avoid_shadowing_type_parameters # not yet tested - avoid_shadowing_type_parameters
# - avoid_single_cascade_in_expression_statements # not yet tested - avoid_single_cascade_in_expression_statements
- avoid_slow_async_io - avoid_slow_async_io
- avoid_type_to_string
- avoid_types_as_parameter_names - avoid_types_as_parameter_names
# - avoid_types_on_closure_parameters # conflicts with always_specify_types # - avoid_types_on_closure_parameters # conflicts with always_specify_types
- avoid_unnecessary_containers
- avoid_unused_constructor_parameters - avoid_unused_constructor_parameters
- avoid_void_async - avoid_void_async
# - avoid_web_libraries_in_flutter # not yet tested
- await_only_futures - await_only_futures
- camel_case_extensions
- camel_case_types - camel_case_types
- cancel_subscriptions - cancel_subscriptions
# - cascade_invocations # not yet tested # - cascade_invocations # not yet tested
- cast_nullable_to_non_nullable
# - close_sinks # not reliable enough # - close_sinks # not reliable enough
# - comment_references # blocked on https://github.com/flutter/flutter/issues/20765 # - comment_references # blocked on https://github.com/dart-lang/linter/issues/1142
# - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204 # - constant_identifier_names # needs an opt-out https://github.com/dart-lang/linter/issues/204
- control_flow_in_finally - control_flow_in_finally
# - curly_braces_in_flow_control_structures # not yet tested # - curly_braces_in_flow_control_structures # not required by flutter style
# - diagnostic_describe_all_properties # not yet tested # - diagnostic_describe_all_properties # not yet tested
- directives_ordering - directives_ordering
# - do_not_use_environment # we do this commonly
- empty_catches - empty_catches
- empty_constructor_bodies - empty_constructor_bodies
- empty_statements - empty_statements
# - file_names # not yet tested - exhaustive_cases
- file_names
- flutter_style_todos
- hash_and_equals - hash_and_equals
- implementation_imports - implementation_imports
# - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811 # - invariant_booleans # too many false positives: https://github.com/dart-lang/linter/issues/811
- iterable_contains_unrelated_type - iterable_contains_unrelated_type
# - join_return_with_assignment # not yet tested # - join_return_with_assignment # not required by flutter style
- leading_newlines_in_multiline_strings
- library_names - library_names
- library_prefixes - library_prefixes
# - lines_longer_than_80_chars # not yet tested # - lines_longer_than_80_chars # not required by flutter style
- list_remove_unrelated_type - list_remove_unrelated_type
# - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181 # - literal_only_boolean_expressions # too many false positives: https://github.com/dart-lang/sdk/issues/34181
- missing_whitespace_between_adjacent_strings
- no_adjacent_strings_in_list - no_adjacent_strings_in_list
# - no_default_cases # too many false positives
- no_duplicate_case_values - no_duplicate_case_values
- no_logic_in_create_state
# - no_runtimeType_toString # ok in tests; we enable this only in packages/
- non_constant_identifier_names - non_constant_identifier_names
# - null_closures # not yet tested - null_check_on_nullable_type_parameter
- null_closures
# - omit_local_variable_types # opposite of always_specify_types # - omit_local_variable_types # opposite of always_specify_types
# - one_member_abstracts # too many false positives # - one_member_abstracts # too many false positives
# - only_throw_errors # https://github.com/flutter/flutter/issues/5792 # - only_throw_errors # https://github.com/flutter/flutter/issues/5792
@ -119,14 +110,14 @@ linter:
# - parameter_assignments # we do this commonly # - parameter_assignments # we do this commonly
- prefer_adjacent_string_concatenation - prefer_adjacent_string_concatenation
- prefer_asserts_in_initializer_lists - prefer_asserts_in_initializer_lists
# - prefer_asserts_with_message # not yet tested # - prefer_asserts_with_message # not required by flutter style
- prefer_collection_literals - prefer_collection_literals
- prefer_conditional_assignment - prefer_conditional_assignment
- prefer_const_constructors - prefer_const_constructors
- prefer_const_constructors_in_immutables - prefer_const_constructors_in_immutables
- prefer_const_declarations - prefer_const_declarations
- prefer_const_literals_to_create_immutables - prefer_const_literals_to_create_immutables
# - prefer_constructors_over_static_methods # not yet tested # - prefer_constructors_over_static_methods # far too many false positives
- prefer_contains - prefer_contains
# - prefer_double_quotes # opposite of prefer_single_quotes # - prefer_double_quotes # opposite of prefer_single_quotes
- prefer_equal_for_default_values - prefer_equal_for_default_values
@ -134,57 +125,72 @@ linter:
- prefer_final_fields - prefer_final_fields
- prefer_final_in_for_each - prefer_final_in_for_each
- prefer_final_locals - prefer_final_locals
# - prefer_for_elements_to_map_fromIterable # not yet tested - prefer_for_elements_to_map_fromIterable
- prefer_foreach - prefer_foreach
# - prefer_function_declarations_over_variables # not yet tested - prefer_function_declarations_over_variables
# - prefer_generic_function_type_aliases - prefer_generic_function_type_aliases
# - prefer_if_elements_to_conditional_expressions # not yet tested - prefer_if_elements_to_conditional_expressions
- prefer_if_null_operators - prefer_if_null_operators
- prefer_initializing_formals - prefer_initializing_formals
- prefer_inlined_adds - prefer_inlined_adds
# - prefer_int_literals # not yet tested # - prefer_int_literals # conflicts with https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-double-literals-for-double-constants
# - prefer_interpolation_to_compose_strings # not yet tested - prefer_interpolation_to_compose_strings
- prefer_is_empty - prefer_is_empty
- prefer_is_not_empty - prefer_is_not_empty
- prefer_is_not_operator
- prefer_iterable_whereType - prefer_iterable_whereType
# - prefer_mixin # https://github.com/dart-lang/language/issues/32 # - prefer_mixin # https://github.com/dart-lang/language/issues/32
# - prefer_null_aware_operators # disable until NNBD, see https://github.com/flutter/flutter/pull/32711#issuecomment-492930932 - prefer_null_aware_operators
# - prefer_relative_imports # incompatible with sub-package imports
- prefer_single_quotes - prefer_single_quotes
- prefer_spread_collections - prefer_spread_collections
- prefer_typing_uninitialized_variables - prefer_typing_uninitialized_variables
- prefer_void_to_null - prefer_void_to_null
# - provide_deprecation_message # not yet tested - provide_deprecation_message
# - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml # - public_member_api_docs # enabled on a case-by-case basis; see e.g. packages/analysis_options.yaml
- recursive_getters - recursive_getters
- sized_box_for_whitespace
- slash_for_doc_comments - slash_for_doc_comments
# - sort_child_properties_last # not yet tested # - sort_child_properties_last # not yet tested
- sort_constructors_first - sort_constructors_first
- sort_pub_dependencies # - sort_pub_dependencies # prevents separating pinned transitive dependencies
- sort_unnamed_constructors_first - sort_unnamed_constructors_first
- test_types_in_equals - test_types_in_equals
- throw_in_finally - throw_in_finally
- tighten_type_of_initializing_formals
# - type_annotate_public_apis # subset of always_specify_types # - type_annotate_public_apis # subset of always_specify_types
- type_init_formals - type_init_formals
# - unawaited_futures # too many false positives # - unawaited_futures # too many false positives
# - unnecessary_await_in_return # not yet tested - unnecessary_await_in_return
- unnecessary_brace_in_string_interps - unnecessary_brace_in_string_interps
- unnecessary_const - unnecessary_const
# - unnecessary_final # conflicts with prefer_final_locals
- unnecessary_getters_setters - unnecessary_getters_setters
# - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498 # - unnecessary_lambdas # has false positives: https://github.com/dart-lang/linter/issues/498
- unnecessary_new - unnecessary_new
- unnecessary_null_aware_assignments - unnecessary_null_aware_assignments
- unnecessary_null_checks
- unnecessary_null_in_if_null_operators - unnecessary_null_in_if_null_operators
- unnecessary_nullable_for_final_variable_declarations
- unnecessary_overrides - unnecessary_overrides
- unnecessary_parenthesis - unnecessary_parenthesis
# - unnecessary_raw_strings # not yet tested
- unnecessary_statements - unnecessary_statements
- unnecessary_string_escapes
- unnecessary_string_interpolations
- unnecessary_this - unnecessary_this
- unrelated_type_equality_checks - unrelated_type_equality_checks
# - unsafe_html # not yet tested # - unsafe_html # not yet tested
- use_full_hex_values_for_flutter_colors - use_full_hex_values_for_flutter_colors
# - use_function_type_syntax_for_parameters # not yet tested - use_function_type_syntax_for_parameters
# - use_if_null_to_convert_nulls_to_bools # not yet tested
- use_is_even_rather_than_modulo
- use_key_in_widget_constructors
- use_late_for_private_fields_and_variables
- use_raw_strings
- use_rethrow_when_possible - use_rethrow_when_possible
# - use_setters_to_change_properties # not yet tested # - use_setters_to_change_properties # not yet tested
# - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182 # - use_string_buffers # has false positives: https://github.com/dart-lang/sdk/issues/34182
# - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review # - use_to_and_as_if_applicable # has false positives, so we prefer to catch this by code-review
- valid_regexps - valid_regexps
# - void_checks # not yet tested - void_checks

View file

@ -1,33 +1,38 @@
import 'package:flutter/material.dart';
import 'package:date_field/date_field.dart'; import 'package:date_field/date_field.dart';
import 'package:flutter/material.dart';
void main() { void main() {
runApp(MyApp()); runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application. // This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
title: 'Flutter Demo', title: 'Flutter Demo',
theme: ThemeData( theme: ThemeData(
inputDecorationTheme: inputDecorationTheme: const InputDecorationTheme(
const InputDecorationTheme(border: OutlineInputBorder()), border: OutlineInputBorder(),
),
primarySwatch: Colors.blue, primarySwatch: Colors.blue,
), ),
home: MyHomePage(), home: const MyHomePage(),
); );
} }
} }
class MyHomePage extends StatefulWidget { class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override @override
_MyHomePageState createState() => _MyHomePageState(); _MyHomePageState createState() => _MyHomePageState();
} }
class _MyHomePageState extends State<MyHomePage> { class _MyHomePageState extends State<MyHomePage> {
DateTime selectedDate; DateTime? selectedDate;
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
@ -36,7 +41,7 @@ class _MyHomePageState extends State<MyHomePage> {
padding: const EdgeInsets.all(20.0), padding: const EdgeInsets.all(20.0),
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: <Widget>[
const FlutterLogo(size: 100), const FlutterLogo(size: 100),
const SizedBox(height: 20), const SizedBox(height: 20),
const Text('DateField package showcase'), const Text('DateField package showcase'),
@ -59,7 +64,7 @@ class _MyHomePageState extends State<MyHomePage> {
), ),
Form( Form(
child: Column( child: Column(
children: [ children: <Widget>[
DateTimeFormField( DateTimeFormField(
decoration: const InputDecoration( decoration: const InputDecoration(
hintStyle: TextStyle(color: Colors.black45), hintStyle: TextStyle(color: Colors.black45),
@ -69,7 +74,7 @@ class _MyHomePageState extends State<MyHomePage> {
labelText: 'My Super Date Time Field', labelText: 'My Super Date Time Field',
), ),
autovalidateMode: AutovalidateMode.always, autovalidateMode: AutovalidateMode.always,
validator: (e) => validator: (DateTime? e) =>
(e?.day ?? 0) == 1 ? 'Please not the first day' : null, (e?.day ?? 0) == 1 ? 'Please not the first day' : null,
onDateSelected: (DateTime value) { onDateSelected: (DateTime value) {
print(value); print(value);
@ -86,8 +91,11 @@ class _MyHomePageState extends State<MyHomePage> {
), ),
mode: DateTimeFieldPickerMode.time, mode: DateTimeFieldPickerMode.time,
autovalidateMode: AutovalidateMode.always, autovalidateMode: AutovalidateMode.always,
validator: (e) => validator: (DateTime? e) {
(e?.day ?? 0) == 1 ? 'Please not the first day' : null, return (e?.day ?? 0) == 1
? 'Please not the first day'
: null;
},
onDateSelected: (DateTime value) { onDateSelected: (DateTime value) {
print(value); print(value);
}, },

View file

@ -5,7 +5,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1 version: 1.0.0+1
environment: environment:
sdk: ">=2.7.0 <3.0.0" sdk: ">=2.12.0 <3.0.0"
dev_dependencies: dev_dependencies:
flutter_test: flutter_test:
@ -21,7 +21,7 @@ dependencies:
# The following adds the Cupertino Icons font to your application. # The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons. # Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^1.0.2 cupertino_icons: ^1.0.3
flutter: flutter:

View file

@ -1,30 +0,0 @@
// This is a basic Flutter widget test.
//
// To perform an interaction with a widget in your test, use the WidgetTester
// utility that Flutter provides. For example, you can send tap and scroll
// gestures. You can also use WidgetTester to find child widgets in the widget
// tree, read text, and verify that the values of widget properties are correct.
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:example/main.dart';
void main() {
testWidgets('Counter increments smoke test', (WidgetTester tester) async {
// Build our app and trigger a frame.
await tester.pumpWidget(MyApp());
// Verify that our counter starts at 0.
expect(find.text('0'), findsOneWidget);
expect(find.text('1'), findsNothing);
// Tap the '+' icon and trigger a frame.
await tester.tap(find.byIcon(Icons.add));
await tester.pump();
// Verify that our counter has incremented.
expect(find.text('0'), findsNothing);
expect(find.text('1'), findsOneWidget);
});
}

View file

@ -1,325 +1,2 @@
import 'package:flutter/cupertino.dart'; export 'package:date_field/src/field.dart';
import 'package:flutter/material.dart'; export 'package:date_field/src/form_field.dart';
import 'package:intl/intl.dart';
final DateTime _kDefaultFirstSelectableDate = DateTime(1900);
final DateTime _kDefaultLastSelectableDate = DateTime(2100);
const double _kCupertinoDatePickerHeight = 216;
/// A [FormField] that contains a [DateTimeField].
///
/// This is a convenience widget that wraps a [DateTimeField] widget in a
/// [FormField].
///
/// A [Form] ancestor is not required. The [Form] simply makes it easier to
/// save, reset, or validate multiple fields at once. To use without a [Form],
/// pass a [GlobalKey] to the constructor and use [GlobalKey.currentState] to
/// save or reset the form field.
class DateTimeFormField extends FormField<DateTime> {
DateTimeFormField({
Key? key,
FormFieldSetter<DateTime>? onSaved,
FormFieldValidator<DateTime>? validator,
DateTime? initialValue,
AutovalidateMode? autovalidateMode,
bool enabled = true,
TextStyle? dateTextStyle,
DateFormat? dateFormat,
DateTime? firstDate,
DateTime? lastDate,
ValueChanged<DateTime>? onDateSelected,
InputDecoration? decoration,
DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
DatePickerMode initialDatePickerMode = DatePickerMode.day,
DateTimeFieldPickerMode mode = DateTimeFieldPickerMode.dateAndTime,
}) : super(
key: key,
initialValue: initialValue,
onSaved: onSaved,
validator: validator,
autovalidateMode: autovalidateMode,
enabled: enabled,
builder: (FormFieldState<DateTime> field) {
// Theme defaults are applied inside the _InputDropdown widget
final InputDecoration _decorationWithThemeDefaults =
decoration ?? const InputDecoration();
final InputDecoration effectiveDecoration =
_decorationWithThemeDefaults.copyWith(
errorText: field.errorText);
void onChangedHandler(DateTime value) {
if (onDateSelected != null) {
onDateSelected(value);
}
field.didChange(value);
}
return DateTimeField(
firstDate: firstDate,
lastDate: lastDate,
decoration: effectiveDecoration,
initialDatePickerMode: initialDatePickerMode,
dateFormat: dateFormat,
onDateSelected: onChangedHandler,
selectedDate: field.value,
enabled: enabled,
mode: mode,
initialEntryMode: initialEntryMode,
dateTextStyle: dateTextStyle,
);
},
);
@override
_DateFormFieldState createState() => _DateFormFieldState();
}
class _DateFormFieldState extends FormFieldState<DateTime> {}
/// [DateTimeField]
///
/// Shows an [_InputDropdown] that'll trigger [DateTimeField._selectDate] whenever the user
/// clicks on it ! The date picker is **platform responsive** (ios date picker style for ios, ...)
class DateTimeField extends StatelessWidget {
DateTimeField({
Key? key,
required this.onDateSelected,
required this.selectedDate,
this.initialDatePickerMode = DatePickerMode.day,
this.decoration,
this.enabled = true,
this.mode = DateTimeFieldPickerMode.dateAndTime,
this.initialEntryMode = DatePickerEntryMode.calendar,
this.dateTextStyle,
DateTime? firstDate,
DateTime? lastDate,
DateFormat? dateFormat,
}) : dateFormat = dateFormat ?? getDateFormatFromDateFieldPickerMode(mode),
firstDate = firstDate ?? _kDefaultFirstSelectableDate,
lastDate = lastDate ?? _kDefaultLastSelectableDate,
super(key: key);
DateTimeField.time({
Key? key,
this.onDateSelected,
this.selectedDate,
this.decoration,
this.enabled,
this.dateTextStyle,
this.initialEntryMode = DatePickerEntryMode.calendar,
DateTime? firstDate,
DateTime? lastDate,
}) : initialDatePickerMode = null,
mode = DateTimeFieldPickerMode.time,
dateFormat = DateFormat.jm(),
firstDate = firstDate ?? DateTime(2000),
lastDate = lastDate ?? DateTime(2001),
super(key: key);
/// Callback for whenever the user selects a [DateTime]
final ValueChanged<DateTime>? onDateSelected;
/// The current selected date to display inside the field
final DateTime? selectedDate;
/// The first date that the user can select (default is 1900)
final DateTime firstDate;
/// The last date that the user can select (default is 2100)
final DateTime lastDate;
/// Let you choose the [DatePickerMode] for the date picker! (default is [DatePickerMode.day]
final DatePickerMode? initialDatePickerMode;
/// Custom [InputDecoration] for the [InputDecorator] widget
final InputDecoration? decoration;
/// How to display the [DateTime] for the user (default is [DateFormat.yMMMD])
final DateFormat dateFormat;
/// Whether the field is usable. If false the user won't be able to select any date
final bool? enabled;
/// Whether to ask the user to pick only the date, the time or both.
final DateTimeFieldPickerMode mode;
/// [TextStyle] of the selected date inside the field.
final TextStyle? dateTextStyle;
/// The initial entry mode for the material date picker dialog
final DatePickerEntryMode initialEntryMode;
/// Shows a dialog asking the user to pick a date !
Future<void> _selectDate(BuildContext context) async {
final DateTime initialDateTime = selectedDate ?? DateTime.now();
if (Theme.of(context).platform == TargetPlatform.iOS) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext builder) {
return Container(
height: _kCupertinoDatePickerHeight,
child: CupertinoDatePicker(
mode: _cupertinoModeFromPickerMode(mode),
onDateTimeChanged: onDateSelected!,
initialDateTime: initialDateTime,
minimumDate: firstDate,
maximumDate: lastDate,
),
);
},
);
} else {
DateTime _selectedDateTime = initialDateTime;
if ([DateTimeFieldPickerMode.dateAndTime, DateTimeFieldPickerMode.date]
.contains(mode)) {
final DateTime? _selectedDate = await showDatePicker(
context: context,
initialDatePickerMode: initialDatePickerMode!,
initialDate: initialDateTime,
initialEntryMode: initialEntryMode,
firstDate: firstDate,
lastDate: lastDate,
);
if (_selectedDate != null) {
_selectedDateTime = _selectedDate;
} else {
return;
}
}
if ([DateTimeFieldPickerMode.dateAndTime, DateTimeFieldPickerMode.time]
.contains(mode)) {
final TimeOfDay? _selectedTime = await showTimePicker(
initialTime: TimeOfDay.fromDateTime(initialDateTime),
context: context,
);
if (_selectedTime != null) {
_selectedDateTime = DateTime(
_selectedDateTime.year,
_selectedDateTime.month,
_selectedDateTime.day,
_selectedTime.hour,
_selectedTime.minute,
);
}
}
onDateSelected!(_selectedDateTime);
}
}
@override
Widget build(BuildContext context) {
String? text;
if (selectedDate != null) text = dateFormat.format(selectedDate!);
TextStyle? textStyle;
if (text == null) {
textStyle = decoration!.hintStyle ??
Theme.of(context).inputDecorationTheme.hintStyle;
} else {
textStyle = dateTextStyle ?? dateTextStyle;
}
final bool shouldDisplayLabelText = (text ?? decoration!.hintText) != null;
InputDecoration? effectiveDecoration = decoration;
if (!shouldDisplayLabelText) {
effectiveDecoration = effectiveDecoration!.copyWith(labelText: '');
}
return _InputDropdown(
text: text ??
decoration!.hintText ??
decoration!.labelText ??
'Select date',
textStyle: textStyle,
decoration: effectiveDecoration,
onPressed: enabled! ? () => _selectDate(context) : null,
);
}
}
/// Those values are used by the [DateTimeField] widget to determine whether to ask
/// the user for the time, the date or both.
enum DateTimeFieldPickerMode { time, date, dateAndTime }
/// Returns the [CupertinoDatePickerMode] corresponding to the selected
/// [DateTimeFieldPickerMode]. This exists to prevent redundancy in the [DateTimeField]
/// widget parameters.
CupertinoDatePickerMode _cupertinoModeFromPickerMode(
DateTimeFieldPickerMode mode) {
switch (mode) {
case DateTimeFieldPickerMode.time:
return CupertinoDatePickerMode.time;
case DateTimeFieldPickerMode.date:
return CupertinoDatePickerMode.date;
default:
return CupertinoDatePickerMode.dateAndTime;
}
}
/// Returns the corresponding default [DateFormat] for the selected [DateTimeFieldPickerMode]
DateFormat getDateFormatFromDateFieldPickerMode(DateTimeFieldPickerMode mode) {
switch (mode) {
case DateTimeFieldPickerMode.time:
return DateFormat.jm();
case DateTimeFieldPickerMode.date:
return DateFormat.yMMMMd();
default:
return DateFormat.yMd().add_jm();
}
}
///
/// [_InputDropdown]
///
/// Shows a field with a dropdown arrow !
/// It does not show any popup menu, it'll just trigger onPressed whenever the
/// user does click on it !
class _InputDropdown extends StatelessWidget {
const _InputDropdown({
Key? key,
required this.text,
this.decoration,
this.textStyle,
this.onPressed,
}) : super(key: key);
/// The text that should be displayed inside the field
final String text;
/// Custom [InputDecoration] for the [InputDecorator] widget
final InputDecoration? decoration;
/// TextStyle for the field
final TextStyle? textStyle;
/// Callbacks triggered whenever the user presses on the field!
final VoidCallback? onPressed;
@override
Widget build(BuildContext context) {
final InputDecoration effectiveDecoration = decoration ??
const InputDecoration(
suffixIcon: Icon(Icons.arrow_drop_down),
).applyDefaults(Theme.of(context).inputDecorationTheme);
return GestureDetector(
onTap: onPressed,
child: InputDecorator(
decoration: effectiveDecoration,
child: Text(text, style: textStyle),
),
);
}
}

262
lib/src/field.dart Normal file
View file

@ -0,0 +1,262 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
final DateTime _kDefaultFirstSelectableDate = DateTime(1900);
final DateTime _kDefaultLastSelectableDate = DateTime(2100);
const double _kCupertinoDatePickerHeight = 216;
/// [DateTimeField]
///
/// Shows an [_InputDropdown] that'll trigger [DateTimeField._selectDate] whenever the user
/// clicks on it ! The date picker is **platform responsive** (ios date picker style for ios, ...)
class DateTimeField extends StatelessWidget {
DateTimeField({
Key? key,
required this.onDateSelected,
required this.selectedDate,
this.initialDatePickerMode = DatePickerMode.day,
this.decoration,
this.enabled = true,
this.mode = DateTimeFieldPickerMode.dateAndTime,
this.initialEntryMode = DatePickerEntryMode.calendar,
this.dateTextStyle,
DateTime? firstDate,
DateTime? lastDate,
DateFormat? dateFormat,
}) : dateFormat = dateFormat ?? getDateFormatFromDateFieldPickerMode(mode),
firstDate = firstDate ?? _kDefaultFirstSelectableDate,
lastDate = lastDate ?? _kDefaultLastSelectableDate,
super(key: key);
DateTimeField.time({
Key? key,
this.onDateSelected,
this.selectedDate,
this.decoration,
this.enabled,
this.dateTextStyle,
this.initialEntryMode = DatePickerEntryMode.calendar,
DateTime? firstDate,
DateTime? lastDate,
}) : initialDatePickerMode = null,
mode = DateTimeFieldPickerMode.time,
dateFormat = DateFormat.jm(),
firstDate = firstDate ?? DateTime(2000),
lastDate = lastDate ?? DateTime(2001),
super(key: key);
/// Callback for whenever the user selects a [DateTime]
final ValueChanged<DateTime>? onDateSelected;
/// The current selected date to display inside the field
final DateTime? selectedDate;
/// The first date that the user can select (default is 1900)
final DateTime firstDate;
/// The last date that the user can select (default is 2100)
final DateTime lastDate;
/// Let you choose the [DatePickerMode] for the date picker! (default is [DatePickerMode.day]
final DatePickerMode? initialDatePickerMode;
/// Custom [InputDecoration] for the [InputDecorator] widget
final InputDecoration? decoration;
/// How to display the [DateTime] for the user (default is [DateFormat.yMMMD])
final DateFormat dateFormat;
/// Whether the field is usable. If false the user won't be able to select any date
final bool? enabled;
/// Whether to ask the user to pick only the date, the time or both.
final DateTimeFieldPickerMode mode;
/// [TextStyle] of the selected date inside the field.
final TextStyle? dateTextStyle;
/// The initial entry mode for the material date picker dialog
final DatePickerEntryMode initialEntryMode;
/// Shows a dialog asking the user to pick a date !
Future<void> _selectDate(BuildContext context) async {
final DateTime initialDateTime = selectedDate ?? DateTime.now();
if (Theme.of(context).platform == TargetPlatform.iOS) {
showModalBottomSheet<void>(
context: context,
builder: (BuildContext builder) {
return SizedBox(
height: _kCupertinoDatePickerHeight,
child: CupertinoDatePicker(
mode: _cupertinoModeFromPickerMode(mode),
onDateTimeChanged: onDateSelected!,
initialDateTime: initialDateTime,
minimumDate: firstDate,
maximumDate: lastDate,
),
);
},
);
} else {
DateTime _selectedDateTime = initialDateTime;
const List<DateTimeFieldPickerMode> modesWithDate =
<DateTimeFieldPickerMode>[
DateTimeFieldPickerMode.dateAndTime,
DateTimeFieldPickerMode.date
];
if (modesWithDate.contains(mode)) {
final DateTime? _selectedDate = await showDatePicker(
context: context,
initialDatePickerMode: initialDatePickerMode!,
initialDate: initialDateTime,
initialEntryMode: initialEntryMode,
firstDate: firstDate,
lastDate: lastDate,
);
if (_selectedDate != null) {
_selectedDateTime = _selectedDate;
} else {
return;
}
}
final List<DateTimeFieldPickerMode> modesWithTime =
<DateTimeFieldPickerMode>[
DateTimeFieldPickerMode.dateAndTime,
DateTimeFieldPickerMode.time
];
if (modesWithTime.contains(mode)) {
final TimeOfDay? _selectedTime = await showTimePicker(
initialTime: TimeOfDay.fromDateTime(initialDateTime),
context: context,
);
if (_selectedTime != null) {
_selectedDateTime = DateTime(
_selectedDateTime.year,
_selectedDateTime.month,
_selectedDateTime.day,
_selectedTime.hour,
_selectedTime.minute,
);
}
}
onDateSelected!(_selectedDateTime);
}
}
@override
Widget build(BuildContext context) {
String? text;
if (selectedDate != null) {
text = dateFormat.format(selectedDate!);
}
TextStyle? textStyle;
textStyle = dateTextStyle ?? dateTextStyle;
return _InputDropdown(
text: text,
textStyle: textStyle,
isEmpty: selectedDate == null,
decoration: decoration,
onPressed: enabled! ? () => _selectDate(context) : null,
);
}
}
/// Those values are used by the [DateTimeField] widget to determine whether to ask
/// the user for the time, the date or both.
enum DateTimeFieldPickerMode { time, date, dateAndTime }
/// Returns the [CupertinoDatePickerMode] corresponding to the selected
/// [DateTimeFieldPickerMode]. This exists to prevent redundancy in the [DateTimeField]
/// widget parameters.
CupertinoDatePickerMode _cupertinoModeFromPickerMode(
DateTimeFieldPickerMode mode) {
switch (mode) {
case DateTimeFieldPickerMode.time:
return CupertinoDatePickerMode.time;
case DateTimeFieldPickerMode.date:
return CupertinoDatePickerMode.date;
default:
return CupertinoDatePickerMode.dateAndTime;
}
}
/// Returns the corresponding default [DateFormat] for the selected [DateTimeFieldPickerMode]
DateFormat getDateFormatFromDateFieldPickerMode(DateTimeFieldPickerMode mode) {
switch (mode) {
case DateTimeFieldPickerMode.time:
return DateFormat.jm();
case DateTimeFieldPickerMode.date:
return DateFormat.yMMMMd();
default:
return DateFormat.yMd().add_jm();
}
}
///
/// [_InputDropdown]
///
/// Shows a field with a dropdown arrow !
/// It does not show any popup menu, it'll just trigger onPressed whenever the
/// user does click on it !
class _InputDropdown extends StatelessWidget {
const _InputDropdown({
Key? key,
required this.text,
this.decoration,
this.textStyle,
this.onPressed,
required this.isEmpty,
}) : super(key: key);
/// The text that should be displayed inside the field
final String? text;
/// Custom [InputDecoration] for the [InputDecorator] widget
final InputDecoration? decoration;
/// TextStyle for the field
final TextStyle? textStyle;
/// Callbacks triggered whenever the user presses on the field!
final VoidCallback? onPressed;
/// Whether the input field is empty.
///
/// Determines the position of the label text and whether to display the hint
/// text.
///
/// Defaults to false.
final bool isEmpty;
@override
Widget build(BuildContext context) {
final InputDecoration effectiveDecoration = decoration ??
const InputDecoration(
suffixIcon: Icon(Icons.arrow_drop_down),
);
return GestureDetector(
onTap: onPressed,
child: InputDecorator(
decoration: effectiveDecoration.applyDefaults(
Theme.of(context).inputDecorationTheme,
),
isEmpty: isEmpty,
child: text == null ? null : Text(text!, style: textStyle),
),
);
}
}

75
lib/src/form_field.dart Normal file
View file

@ -0,0 +1,75 @@
import 'package:date_field/src/field.dart';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
/// A [FormField] that contains a [DateTimeField].
///
/// This is a convenience widget that wraps a [DateTimeField] widget in a
/// [FormField].
///
/// A [Form] ancestor is not required. The [Form] simply makes it easier to
/// save, reset, or validate multiple fields at once. To use without a [Form],
/// pass a [GlobalKey] to the constructor and use [GlobalKey.currentState] to
/// save or reset the form field.
class DateTimeFormField extends FormField<DateTime> {
DateTimeFormField({
Key? key,
FormFieldSetter<DateTime>? onSaved,
FormFieldValidator<DateTime>? validator,
DateTime? initialValue,
AutovalidateMode? autovalidateMode,
bool enabled = true,
TextStyle? dateTextStyle,
DateFormat? dateFormat,
DateTime? firstDate,
DateTime? lastDate,
ValueChanged<DateTime>? onDateSelected,
InputDecoration? decoration,
DatePickerEntryMode initialEntryMode = DatePickerEntryMode.calendar,
DatePickerMode initialDatePickerMode = DatePickerMode.day,
DateTimeFieldPickerMode mode = DateTimeFieldPickerMode.dateAndTime,
}) : super(
key: key,
initialValue: initialValue,
onSaved: onSaved,
validator: validator,
autovalidateMode: autovalidateMode,
enabled: enabled,
builder: (FormFieldState<DateTime> field) {
// Theme defaults are applied inside the _InputDropdown widget
final InputDecoration _decorationWithThemeDefaults =
decoration ?? const InputDecoration();
final InputDecoration effectiveDecoration =
_decorationWithThemeDefaults.copyWith(
errorText: field.errorText);
void onChangedHandler(DateTime value) {
if (onDateSelected != null) {
onDateSelected(value);
}
field.didChange(value);
}
return DateTimeField(
firstDate: firstDate,
lastDate: lastDate,
decoration: effectiveDecoration,
initialDatePickerMode: initialDatePickerMode,
dateFormat: dateFormat,
onDateSelected: onChangedHandler,
selectedDate: field.value,
enabled: enabled,
mode: mode,
initialEntryMode: initialEntryMode,
dateTextStyle: dateTextStyle,
);
},
);
@override
_DateFormFieldState createState() => _DateFormFieldState();
}
class _DateFormFieldState extends FormFieldState<DateTime> {}

View file

@ -1,6 +1,6 @@
name: date_field name: date_field
description: A widget in the form of a field that lets people choose a date, a time or both. description: A widget in the form of a field that lets people choose a date, a time or both.
version: 2.0.1 version: 2.1.0
homepage: 'https://github.com/GaspardMerten/date_field' homepage: 'https://github.com/GaspardMerten/date_field'
environment: environment: