Skip to content

Unexpected showDatePicker behavior when changing year #81547

Description

@jjoelson

It's not clear if this is a bug or intended behavior, but at any rate the Flutter Material date picker has a behavior that differs from that of the native Android date picker in such a way that is likely to cause users accustomed to the native behavior to select the wrong date in a Flutter app.

Steps to Reproduce

  1. Open a date picker with showDatePicker with initialDate: DateTime(2021, 4, 30)
  2. Tap the year control and select 2015 (or any other year)

Expected results:

4/30/2015 ought to be the newly selected date i.e. the month/day in the newly selected year that corresponds to that of the previously selected date (or closest available).

Actual results:

The date picker displays April in 2015, but 4/30/2021 remains the selected date. If the user taps OK at this point intending to select 4/30/2015 then they will actually select 4/30/2021.

RPReplay_Final1619795575.MP4
Logs
[✓] Flutter (Channel stable, 2.0.3, on Mac OS X 10.15.7 19H1030 darwin-x64, locale en-US)
    • Flutter version 2.0.3 at /Users/jonathan.joelson/repos/flutter
    • Framework revision 4d7946a68d (6 weeks ago), 2021-03-18 17:24:33 -0700
    • Engine revision 3459eb2436
    • Dart version 2.12.2

[✓] Android toolchain - develop for Android devices (Android SDK version 30.0.3)
    • Android SDK at /Users/jonathan.joelson/Library/Android/sdk
    • Platform android-30, build-tools 30.0.3
    • Java binary at: /Applications/Android Studio.app/Contents/jre/jdk/Contents/Home/bin/java
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)
    • All Android licenses accepted.

[✓] Xcode - develop for iOS and macOS
    • Xcode at /Applications/Xcode.app/Contents/Developer
    • Xcode 12.4, Build version 12D4e
    • CocoaPods version 1.10.1

[✓] Chrome - develop for the web
    • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome

[✓] Android Studio (version 4.1)
    • Android Studio at /Applications/Android Studio.app/Contents
    • Flutter plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/9212-flutter
    • Dart plugin can be installed from:
      🔨 https://plugins.jetbrains.com/plugin/6351-dart
    • Java version OpenJDK Runtime Environment (build 1.8.0_242-release-1644-b3-6915495)

[✓] VS Code (version 1.55.2)
    • VS Code at /Applications/Visual Studio Code.app/Contents
    • Flutter extension version 3.21.0

Activity

  1. added
    p: material_uimaterial_ui package in flutter/packages
    frameworkflutter/packages/flutter repository. See also f: labels.
    on Apr 30, 2021
  2. HansMuller commented on Apr 30, 2021

    @HansMuller
    Contributor
  3. jjoelson commented on Aug 11, 2021

    @jjoelson
    ContributorAuthor

    @HansMuller @darrenaustin Having shipped this to real users, I can confirm that this is extremely confusing in practice. We have lots of users entering the wrong date by mistake.

    I would consider this a dire usability issue in an extremely popular component. I can see that others have reported this issue and I would be surprised if there is any Flutter app that uses this component for inputting, say, a user's birthday that isn't having many users select the wrong date by mistake.

    It turns out that it is an extremely common user behavior to select the month and day first, and then change the year. In the native Android date picker this works as expected, but in Flutter the user will select the wrong year.

    To drive home how big a problem this is, we are considering switching have switched to the Cupertino date picker even for our Android users.

  4. TahaTesser commented on Oct 1, 2021

    @TahaTesser
    Contributor

    Hi @jjoelson
    Thanks for filing the issue

    minimal code sample
    import 'package:flutter/material.dart';
    
    void main() => runApp(MyApp());
    
    class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) {
        return MaterialApp(
          title: 'Material App',
          theme: ThemeData.dark(),
          home: Home(),
        );
      }
    }
    
    class Home extends StatelessWidget {
    
      @override
      Widget build(BuildContext context) {
        return Scaffold(
          appBar: AppBar(
            title: Text('Material App Bar'),
          ),
          body: Center(
            child: Container(
              child: Text('Hello World'),
            ),
          ),
          floatingActionButton: FloatingActionButton(
            child: Icon(Icons.add),
            onPressed: () async {
              showDatePicker(
      context: context,
      initialDate: DateTime(2021, 4, 30),
      firstDate: DateTime(2000),
      lastDate: DateTime(2030),
    );
            },
          ),
        );
      }
    }
    stable master
    ❗️ ❗️

    Check flutter doctor -v outputs for each channel below

    flutter doctor -v
    [✓] Flutter (Channel stable, 2.5.2, on macOS 12.0 21A5534d darwin-x64, locale en-GB)
        • Flutter version 2.5.2 at /Users/tahatesser/Code/flutter_stable
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 3595343e20 (17 hours ago), 2021-09-30 12:58:18 -0700
        • Engine revision 6ac856380f
        • Dart version 2.14.3
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
        • Android SDK at /Users/tahatesser/Code/SDK
        • Platform android-30, build-tools 29.0.2
        • ANDROID_HOME = /Users/tahatesser/Code/SDK
        • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • Xcode 13.0, Build version 13A233
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 2020.3)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
    
    [✓] VS Code (version 1.60.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.26.0
    
    [✓] Connected device (3 available)
        • Redmi K20 Pro (mobile) • def0ad20 • android-arm64  • Android 11 (API 30)
        • macOS (desktop)        • macos    • darwin-x64     • macOS 12.0 21A5534d darwin-x64
        • Chrome (web)           • chrome   • web-javascript • Google Chrome 94.0.4606.61
    
    • No issues found!
    [✓] Flutter (Channel master, 2.6.0-12.0.pre.167, on macOS 12.0 21A5534d darwin-x64, locale en-GB)
        • Flutter version 2.6.0-12.0.pre.167 at /Users/tahatesser/Code/flutter_master
        • Upstream repository https://github.com/flutter/flutter.git
        • Framework revision 50604c614e (7 hours ago), 2021-09-30 23:18:02 -0700
        • Engine revision aed327b84d
        • Dart version 2.15.0 (build 2.15.0-168.0.dev)
    
    [✓] Android toolchain - develop for Android devices (Android SDK version 29.0.2)
        • Android SDK at /Users/tahatesser/Code/SDK
        • Platform android-30, build-tools 29.0.2
        • ANDROID_HOME = /Users/tahatesser/Code/SDK
        • Java binary at: /Applications/Android Studio.app/Contents/jre/Contents/Home/bin/java
        • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
        • All Android licenses accepted.
    
    [✓] Xcode - develop for iOS and macOS (Xcode 13.0)
        • Xcode at /Applications/Xcode.app/Contents/Developer
        • CocoaPods version 1.11.2
    
    [✓] Chrome - develop for the web
        • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
    
    [✓] Android Studio (version 2020.3)
        • Android Studio at /Applications/Android Studio.app/Contents
        • Flutter plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/9212-flutter
        • Dart plugin can be installed from:
          🔨 https://plugins.jetbrains.com/plugin/6351-dart
        • Java version OpenJDK Runtime Environment (build 11.0.10+0-b96-7281165)
    
    [✓] VS Code (version 1.60.2)
        • VS Code at /Applications/Visual Studio Code.app/Contents
        • Flutter extension version 3.26.0
    
    [✓] Connected device (3 available)
        • Redmi K20 Pro (mobile) • def0ad20 • android-arm64  • Android 11 (API 30)
        • macOS (desktop)        • macos    • darwin-x64     • macOS 12.0 21A5534d darwin-x64
        • Chrome (web)           • chrome   • web-javascript • Google Chrome 94.0.4606.61
    
    • No issues found!

    ✅: No Issue ❗️: Issue reproduced

  5. TahaTesser commented on Oct 13, 2021

    @TahaTesser
    Contributor

    @jjoelson

    It turns out that it is an extremely common user behavior to select the month and day first, and then change the year. In the native Android date picker this works as expected, but in Flutter the user will select the wrong year.

    I just tested this behavior, if you set the initial date and go to 2015, it still returns 4/30/2021 (which matches Flutter behavior). if you omit the initial date, "okay" is not active, forcing the user to choose a date when scrolling back to 2015

    minimal code sample
    package com.tahatesser.myapplication
    
    import android.os.Bundle
    import android.util.Log
    import androidx.fragment.app.Fragment
    import android.view.LayoutInflater
    import android.view.View
    import android.view.ViewGroup
    import androidx.fragment.app.FragmentManager
    import androidx.navigation.fragment.findNavController
    import com.google.android.material.datepicker.CalendarConstraints
    import com.google.android.material.datepicker.MaterialDatePicker
    import com.google.android.material.timepicker.MaterialTimePicker
    import com.google.android.material.timepicker.TimeFormat
    import com.tahatesser.myapplication.databinding.FragmentFirstBinding
    import java.util.*
    
    /**
     * A simple [Fragment] subclass as the default destination in the navigation.
     */
    class FirstFragment : Fragment() {
    
        private var _binding: FragmentFirstBinding? = null
    
        // This property is only valid between onCreateView and
        // onDestroyView.
        private val binding get() = _binding!!
    
        override fun onCreateView(
            inflater: LayoutInflater, container: ViewGroup?,
            savedInstanceState: Bundle?
        ): View? {
    
            _binding = FragmentFirstBinding.inflate(inflater, container, false)
            return binding.root
    
        }
    
        override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
            super.onViewCreated(view, savedInstanceState)
    
            binding.datePickerButton.setOnClickListener {
    
                val calendar = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
                 calendar.set(2015,0,1)
                val startdDate = calendar.timeInMillis
                calendar.set(2021,3,30)
                val endDate = calendar.timeInMillis
    
                val constraintsBuilder =
                    CalendarConstraints.Builder()
                        .setStart(startdDate)
                        .setEnd(endDate)
    
                val materialTimePicker =  MaterialDatePicker.Builder.datePicker()
                    .setTitleText("Select dates")
                    .setSelection(endDate)
                    .setCalendarConstraints(constraintsBuilder.build())
                    .build()
    
                materialTimePicker.addOnPositiveButtonClickListener {
                    Log.d("materialTimePicker", "${materialTimePicker.selection}")
                }
    
                val supportFragmentManager = this.childFragmentManager.beginTransaction()
    
                materialTimePicker.show(supportFragmentManager, "fragment_tag")
            }
    
        }
    
        override fun onDestroyView() {
            super.onDestroyView()
            _binding = null
        }
    }
    Screen.Recording.2021-10-13.at.2.02.34.PM.mov

    It returns 1619740800000 which is April 30, 2021

  6. added
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Oct 13, 2021
  7. jjoelson commented on Oct 13, 2021

    @jjoelson
    ContributorAuthor

    Hi @TahaTesser, thanks for looking into this!

    I am not an Android developer or user, so I'm not terribly surprised to find that my information about the native material picker is incorrect 😅. When I created this issue, I created a simple HTML date input, assuming that Chrome uses the native material date picker:

    Android.-.Date.Picker.mov

    So with the native picker's behavior acknowledged, we still have a major usability issue on our hands here, even if it's not exclusive to Flutter's Material implementation.

    If you go to the Material date picker documentation and scroll down to the video captioned with "Tapping the year menu accesses the year picker. Scrolling vertically navigates across years", you'll see that changing the year from 2018 to 2016 also changes the selected date: direct video link

    Obviously that video is just a mockup, but it seems possible to me that the intended design here is for the date to change when the year changes. Is it possible that in replicating the native Material date picker component, Flutter has also replicated a bug in that component?

    if you omit the initial date, "okay" is not active, forcing the user to choose a date when scrolling back to 2015

    This is a smaller issue, but Flutter's Material date picker doesn't allow you to omit initialDate in order to disable the "OK" button as far as I can tell.

  8. removed
    waiting for responseThe Flutter team cannot make further progress on this issue until the original reporter responds
    on Oct 13, 2021
  9. TahaTesser commented on Oct 13, 2021

    @TahaTesser
    Contributor

    @jjoelson
    Thanks for the quick response, this really helps.

  10. TahaTesser commented on Nov 11, 2021

    @TahaTesser
    Contributor

    While the design video clearly shows that is not working as expected but even Android 12 doesn't have match the behavior. This is a breaking change, most people will compare the flutter calendar with the native android calendar, and might think this is odd. So I can't move forward with this for now

    cc: @darrenaustin

  11. vitalnik commented on May 31, 2022

    @vitalnik

    Still experiencing this issue in May 2022. Are there any updates/fixes?

  12. github-actions commented on Aug 7, 2023

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  13. locked as resolved and limited conversation to collaborators on Aug 7, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    f: date/time pickerDate or time picker widgetsfound in release: 2.5Found to occur in 2.5found in release: 2.6Found to occur in 2.6frameworkflutter/packages/flutter repository. See also f: labels.has reproducible stepsThe issue has been confirmed reproducible and is ready to work onp: material_uimaterial_ui package in flutter/packagesteam-designOwned by Design Languages teamtriaged-designTriaged by Design Languages team

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions