flutter / flutter

Flutter makes it easy and fast to build beautiful apps for mobile and beyond
https://flutter.dev
BSD 3-Clause "New" or "Revised" License
165.72k stars 27.37k forks source link

Date picker overlay colors aren't applied on `MaterialState.selected` state. #130586

Open TahaTesser opened 1 year ago

TahaTesser commented 1 year ago

Is there an existing issue for this?

Steps to reproduce

Apply selected overlay color in DatePickerTheme.dayOverlayColor and DatePickerTheme.yearOverlayColor

            if (states.contains(MaterialState.selected)) {
              return Colors.green;
            }

Expected results

Screenshot 2023-07-14 at 18 46 42

Actual results

Screenshot 2023-07-14 at 18 46 48

Code sample

Code sample ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( theme: ThemeData( datePickerTheme: DatePickerThemeData( yearOverlayColor: MaterialStateProperty.resolveWith((Set states) { print('yearOverlayColor.states: $states'); if (states.contains(MaterialState.selected)) { return Colors.green; } return Colors.transparent; }), dayOverlayColor: MaterialStateProperty.resolveWith((Set states) { print('dayOverlayColor.states: $states'); if (states.contains(MaterialState.selected)) { return Colors.red; } return Colors.transparent; }), ), useMaterial3: true, ), home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: DatePickerDialog( initialDate: DateTime(2023, DateTime.january, 25), firstDate: DateTime(2022), lastDate: DateTime(2024, DateTime.december, 31), currentDate: DateTime(2023, DateTime.january, 24), ), ), ), ), ); } } ```

Screenshots or Video

Screenshots / Video demonstration [Upload media here]

Logs

Logs ```console [Paste your logs here] ```

Flutter Doctor output

Doctor output ```console [!] Flutter (Channel [user-branch], 3.13.0-3.0.pre.64, on macOS 13.4.1 22F82 darwin-arm64, locale en-EE) ! Flutter version 3.13.0-3.0.pre.64 on channel [user-branch] at /Users/tahatesser/Code/flutter Currently on an unknown channel. Run `flutter channel` to switch to an official channel. If that doesn't fix the issue, reinstall Flutter by following instructions at https://flutter.dev/docs/get-started/install. ! Upstream repository git@github.com:TahaTesser/flutter.git is not the same as FLUTTER_GIT_URL • FLUTTER_GIT_URL = git@github.com:NevercodeHQ/flutter.git • Framework revision 418d6a56f9 (5 hours ago), 2023-07-14 13:59:16 +0300 • Engine revision 3a1b12a2fa • Dart version 3.1.0 (build 3.1.0-310.0.dev) • DevTools version 2.25.0 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [✓] Android toolchain - develop for Android devices (Android SDK version 34.0.0) • Android SDK at /Users/tahatesser/Code/android-sdk • Platform android-34, build-tools 34.0.0 • ANDROID_SDK_ROOT = /Users/tahatesser/Code/android-sdk • Java binary at: /Applications/Android Studio Preview.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b829.16-10353782) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 14.3.1) • Xcode at /Applications/Xcode-14.3.1.app/Contents/Developer • Build 14E300c • CocoaPods version 1.12.1 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [!] Android Studio (version unknown) • Android Studio at /Applications/Android Studio Preview.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 ✗ Unable to determine Android Studio version. • Java version OpenJDK Runtime Environment (build 17.0.7+0-17.0.7b829.16-10353782) [✓] VS Code (version 1.80.1) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.68.0 [✓] Connected device (2 available) • macOS (desktop) • macos • darwin-arm64 • macOS 13.4.1 22F82 darwin-arm64 • Chrome (web) • chrome • web-javascript • Google Chrome 114.0.5735.198 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 2 categories. ```
TahaTesser commented 1 year ago

cc: @rydmike Did you notice this issue? Am I correct to expect the selected overlay color here? I can see there is a selected state but color isn't picked up.

Hover, Focus, & Press work just for fine but internal inkwell uses MaterialStateController for passing selected state which doesn't work to overlay color.

TahaTesser commented 1 year ago

I noticed this issue while writing a bunch of overlay tests for https://github.com/flutter/flutter/issues/130051

huycozy commented 1 year ago

Thanks for the report, @TahaTesser

I can see the issue if the check condition compares to MaterialState.selected, noticed below output log and black overlay color.

flutter: dayOverlayColor.states: {MaterialState.selected, MaterialState.hovered}
flutter: dayOverlayColor.states: {MaterialState.hovered}

But it seems to work if I change the check condition compares to MaterialState.hovered. The same states are logged but can see the expected result (green overlay color when hovering and pressing). I am not sure about the output state here. Looks like this is a MaterialState issue? Or is using MaterialState.hovered applicable for this case?

TahaTesser commented 1 year ago

Other states work because Inkwell automatically handles but the selected state is provided via MaterialStateController.

Looks like this is a MaterialState issue?

It seems to be related to MaterialStateController. I can produce with just InkWell with MaterialStateController for the selected state.

I think other widgets which also use MaterialStateController to provide a selected state might reproduce this issue.

Code sample

expand to view the code sample ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatefulWidget { const MyApp({super.key}); @override State createState() => _MyAppState(); } class _MyAppState extends State { late MaterialStatesController _controller; @override void initState() { super.initState(); _controller = MaterialStatesController({MaterialState.selected}); } @override void dispose() { _controller.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final MaterialStateProperty overlayColor = MaterialStateProperty.resolveWith((Set states) { print('states: $states'); if (states.contains(MaterialState.selected)) { return Colors.green; } return Colors.transparent; }); return MaterialApp( home: Directionality( textDirection: TextDirection.ltr, child: Material( child: Center( child: InkWell( statesController: _controller, overlayColor: overlayColor, onTap: () {}, child: Container( decoration: BoxDecoration(border: Border.all()), width: 100, height: 100, ), ), ), ), ), ); } } ```
TahaTesser commented 1 year ago
futter: states: {MaterialState.selected, MaterialState.hovered}
flutter: states: {MaterialState.hovered}

Since InkWell fires MaterialState.selected, MaterialState.hovered then only MaterialState.hovered before the color is drawn so the selected state is never picked up for the color to be drawn.

huycozy commented 1 year ago

Thanks for the update.

Regarding hovered state with mouse interaction, I see this in M3 specs:

Hover states can be combined with focused, activated, selected, or pressed states.

Also with focused state when using Tab key to focus on widget:

Focus states can be represented in combination with hover, activated, or selected states.

flutter: states: {MaterialState.selected, MaterialState.focused}
flutter: states: {MaterialState.focused}

But I don't see any M3 specs mention to state order. It's quite more detailed on M2 specs, though. In Flutter, it seems the overlay color is only drawn by the latest state (hovered), ignoring intermediate states (selected). This corresponds to the original issue. Let's check below sample code with timeDilation = 20.0:

final MaterialStateProperty<Color> overlayColor = MaterialStateProperty.resolveWith((Set<MaterialState> states) {
  print('states: $states');
  if (states.contains(MaterialState.selected)) {
    return Colors.green;
  }
  if (states.contains(MaterialState.hovered)) {
    return Colors.red;
  }
  return Colors.transparent;
});
Demo video (timeDilation = 20.0) https://github.com/flutter/flutter/assets/104349824/c858bd18-42ac-4982-8c62-251b620bec2a

Reproduced this issue on latest stable and master channels.

flutter doctor -v (stable and master) ```bash [✓] Flutter (Channel stable, 3.10.6, on macOS 13.0.1 22A400 darwin-x64, locale en-VN) • Flutter version 3.10.6 on channel stable at /Users/huynq/Documents/GitHub/flutter • Upstream repository https://github.com/flutter/flutter.git • Framework revision f468f3366c (5 hours ago), 2023-07-12 15:19:05 -0700 • Engine revision cdbeda788a • Dart version 3.0.6 • DevTools version 2.23.1 [✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0) • Android SDK at /Users/huynq/Library/Android/sdk • Platform android-33, build-tools 32.0.0 • ANDROID_HOME = /Users/huynq/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 14.3) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 14E222b • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.2) • 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 17.0.6+0-17.0.6b802.4-9586694) [✓] VS Code (version 1.80.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.68.0 [✓] Connected device (3 available) • RMX2001 (mobile) • EUYTFEUSQSRGDA6D • android-arm64 • Android 11 (API 30) • macOS (desktop) • macos • darwin-x64 • macOS 13.0.1 22A400 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 114.0.5735.198 [✓] Network resources • All expected network resources are available. • No issues found! ``` ```bash [!] Flutter (Channel master, 3.13.0-5.0.pre.34, on macOS 13.0.1 22A400 darwin-x64, locale en-VN) • Flutter version 3.13.0-5.0.pre.34 on channel master at /Users/huynq/Documents/GitHub/flutter_master ! Warning: `flutter` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/flutter, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path. ! Warning: `dart` on your path resolves to /Users/huynq/Documents/GitHub/flutter/bin/dart, which is not inside your current Flutter SDK checkout at /Users/huynq/Documents/GitHub/flutter_master. Consider adding /Users/huynq/Documents/GitHub/flutter_master/bin to the front of your path. • Upstream repository https://github.com/flutter/flutter.git • Framework revision 8a8f023466 (26 minutes ago), 2023-07-17 22:46:27 -0400 • Engine revision f2958f9229 • Dart version 3.1.0 (build 3.1.0-320.0.dev) • DevTools version 2.25.0 • If those were intentional, you can disregard the above warnings; however it is recommended to use "git" directly to perform update checks and upgrades. [✓] Android toolchain - develop for Android devices (Android SDK version 32.0.0) • Android SDK at /Users/huynq/Library/Android/sdk • Platform android-33, build-tools 32.0.0 • ANDROID_HOME = /Users/huynq/Library/Android/sdk • Java binary at: /Applications/Android Studio.app/Contents/jbr/Contents/Home/bin/java • Java version OpenJDK Runtime Environment (build 17.0.6+0-17.0.6b802.4-9586694) • All Android licenses accepted. [✓] Xcode - develop for iOS and macOS (Xcode 14.3) • Xcode at /Applications/Xcode.app/Contents/Developer • Build 14E222b • CocoaPods version 1.11.3 [✓] Chrome - develop for the web • Chrome at /Applications/Google Chrome.app/Contents/MacOS/Google Chrome [✓] Android Studio (version 2022.2) • 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 17.0.6+0-17.0.6b802.4-9586694) [✓] VS Code (version 1.80.0) • VS Code at /Applications/Visual Studio Code.app/Contents • Flutter extension version 3.68.0 [✓] Connected device (4 available) • RMX2001 (mobile) • EUYTFEUSQSRGDA6D • android-arm64 • Android 11 (API 30) • iPhone (mobile) • d9a94afe2b649fef56ba0bfeb052f0f2a7dae95e • ios • iOS 15.7.2 19H218 • macOS (desktop) • macos • darwin-x64 • macOS 13.0.1 22A400 darwin-x64 • Chrome (web) • chrome • web-javascript • Google Chrome 114.0.5735.198 [✓] Network resources • All expected network resources are available. ! Doctor found issues in 1 category. ```
TahaTesser commented 1 year ago

This is reproducible in buttons as well.

It looks like the last state reported isn't selected

flutter: selected
flutter: no state

Code sample

expand to view the code sample ```dart import 'package:flutter/material.dart'; void main() => runApp(const MyApp()); class MyApp extends StatelessWidget { const MyApp({super.key}); @override Widget build(BuildContext context) { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData(useMaterial3: true), home: const Example(), ); } } class Example extends StatefulWidget { const Example({super.key}); @override State createState() => _ExampleState(); } class _ExampleState extends State { late MaterialStatesController statesController; @override void initState() { super.initState(); statesController = MaterialStatesController({MaterialState.selected}); } @override void dispose() { statesController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final MaterialStateProperty overlayColor = MaterialStateProperty.resolveWith((Set states) { if (states.contains(MaterialState.selected)) { print('selected'); return Colors.blue; } print('no state'); return Colors.transparent; }); return Scaffold( appBar: AppBar( title: const Text('Sample'), ), body: Center( child: Column( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ TextButton( statesController: statesController, style: ButtonStyle( overlayColor: overlayColor, ), onPressed: () {}, child: const Text('TextButton'), ), InkWell( statesController: statesController, overlayColor: overlayColor, onTap: () {}, child: Container( decoration: BoxDecoration(border: Border.all()), width: 100, height: 100, ), ) ], ), ), ); } } ```
TahaTesser commented 1 year ago

cc: @bleroux If you've any insight or fix for this.

TahaTesser commented 1 year ago

Similarly IconButton has an internal MaterialStatesController and it also doesn't return the color in the selected state.

    final MaterialStateProperty<Color?> overlayColor =
        MaterialStateProperty.resolveWith((Set<MaterialState> states) {
      if (states.contains(MaterialState.selected)) {
        print('selected');
        return Colors.blue;
      }
      print('no state');
      return Colors.transparent;
    });

          IconButton(
              style: ButtonStyle(
                overlayColor: overlayColor,
              ),
              onPressed: () {},
              isSelected: true,
              icon: const Icon(Icons.abc),
            ),
flutter-triage-bot[bot] commented 10 months ago

This issue is assigned to @HansMuller but has had no recent status updates. Please consider unassigning this issue if it is not going to be addressed in the near future. This allows people to have a clearer picture of what work is actually planned. Thanks!

Pkloni commented 5 months ago
 dayBackgroundColor: MaterialStateColor.resolveWith(
            (Set<MaterialState> states) {
              Color? color;
              if (states.contains(MaterialState.selected)) {
                color = Colors.green;
              }
              return color ?? Colors.transparent;
            },
          ),
TesteurManiak commented 3 weeks ago

I'm encountering a similar issue with the dayOverlayColor when hovering a day cell, the new color for dayBackgroundColor is never resolved properly and the overlay is not displayed.

Reproducible Sample

import 'package:flutter/material.dart';

class MyPage extends StatelessWidget {
  const MyPage({super.key});

  @override
  Widget build(BuildContext context) {
    final now = DateTime.now();
    final firstDate = now.subtract(const Duration(days: 365));
    final lastDate = now.add(const Duration(days: 365));

    return Scaffold(
      body: DatePickerTheme(
        data: DatePickerThemeData(
          dayBackgroundColor: WidgetStateProperty.resolveWith((states) {
            // Never called for WidgetState.hovered
            print('Updated states: $states');
            final isHovered = states.contains(WidgetState.hovered);
            return isHovered ? null : Colors.blue;
          }),
          dayOverlayColor: WidgetStateProperty.resolveWith((_) => Colors.red),
        ),
        child: DatePickerDialog(firstDate: firstDate, lastDate: lastDate),
      ),
    );
  }
}

Enregistrementdelcran2024-09-29221618-ezgif com-video-to-gif-converter