From 003608f182ff93fa83596c57bfb43c43ce8c0c4d Mon Sep 17 00:00:00 2001 From: hangyu Date: Thu, 8 Dec 2022 10:27:46 -0800 Subject: [PATCH 01/71] Update text field input width when there are prefix/suffix icons (#116690) * Update input_decorator_test.dart Update input_decorator.dart Update input_decorator.dart Update input_decorator.dart Update input_decorator.dart Update input_decorator.dart Revert "Update input_decorator.dart" This reverts commit 6a6d2fd0c145c15440405060190ef714b78441c9. Update input_decorator.dart Update input_decorator_test.dart Update input_decorator.dart lint * Update input_decorator.dart --- .../lib/src/material/input_decorator.dart | 4 +- .../test/material/input_decorator_test.dart | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 1f32086e4d0e4..773eb235dcb90 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -978,12 +978,12 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin 0.0, constraints.maxWidth - ( _boxSize(icon).width - + contentPadding.left + + (prefixIcon != null ? 0 : (textDirection == TextDirection.ltr ? contentPadding.left : contentPadding.right)) + _boxSize(prefixIcon).width + _boxSize(prefix).width + _boxSize(suffix).width + _boxSize(suffixIcon).width - + contentPadding.right), + + (suffixIcon != null ? 0 : (textDirection == TextDirection.ltr ? contentPadding.right : contentPadding.left))), ); // Increase the available width for the label when it is scaled down. final double invertedLabelScale = lerpDouble(1.00, 1 / _kFinalLabelScale, decoration.floatingLabelProgress)!; diff --git a/packages/flutter/test/material/input_decorator_test.dart b/packages/flutter/test/material/input_decorator_test.dart index 545f20e6985dc..0343d183d28bf 100644 --- a/packages/flutter/test/material/input_decorator_test.dart +++ b/packages/flutter/test/material/input_decorator_test.dart @@ -3182,6 +3182,65 @@ void main() { expect(FloatingLabelAlignment.center.toString(), 'FloatingLabelAlignment.center'); }); + group('inputText width', () { + testWidgets('outline textField', (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator( + useMaterial3: useMaterial3, + decoration: const InputDecoration( + border: OutlineInputBorder(), + ), + ), + ); + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); + expect(tester.getTopLeft(find.text('text')).dx, 12.0); + expect(tester.getTopRight(find.text('text')).dx, 788.0); + }); + testWidgets('outline textField with prefix and suffix icons', (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator( + useMaterial3: useMaterial3, + decoration: const InputDecoration( + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.visibility), + suffixIcon: Icon(Icons.close), + ), + ), + ); + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 56.0)); + expect(tester.getTopLeft(find.text('text')).dx, 48.0); + expect(tester.getTopRight(find.text('text')).dx, 752.0); + }); + testWidgets('filled textField', (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator( + useMaterial3: useMaterial3, + decoration: const InputDecoration( + filled: true, + ), + ), + ); + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); + expect(tester.getTopLeft(find.text('text')).dx, 12.0); + expect(tester.getTopRight(find.text('text')).dx, 788.0); + }); + testWidgets('filled textField with prefix and suffix icons', (WidgetTester tester) async { + await tester.pumpWidget( + buildInputDecorator( + useMaterial3: useMaterial3, + decoration: const InputDecoration( + filled: true, + prefixIcon: Icon(Icons.visibility), + suffixIcon: Icon(Icons.close), + ), + ), + ); + expect(tester.getSize(find.byType(InputDecorator)), const Size(800.0, 48.0)); + expect(tester.getTopLeft(find.text('text')).dx, 48.0); + expect(tester.getTopRight(find.text('text')).dx, 752.0); + }); + }); + group('floatingLabelAlignment', () { Widget buildInputDecoratorWithFloatingLabel({required TextDirection textDirection, required bool hasIcon, From bebea0cc6c13da7673f597d71616f443ed4672d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Thu, 8 Dec 2022 14:05:53 -0800 Subject: [PATCH 02/71] [Windows] Add a `flutter run` console output test (#116626) Adds a test to verify the console output of `flutter run --release` on Windows. Part of: https://github.com/flutter/flutter/issues/111577 --- .ci.yaml | 18 ++ TESTOWNERS | 1 + .../bin/tasks/run_release_test_windows.dart | 12 + dev/devicelab/lib/tasks/run_tests.dart | 12 +- dev/integration_tests/ui/windows/.gitignore | 17 + .../ui/windows/CMakeLists.txt | 101 ++++++ .../ui/windows/flutter/CMakeLists.txt | 104 +++++++ .../windows/flutter/generated_plugins.cmake | 23 ++ .../ui/windows/runner/CMakeLists.txt | 40 +++ .../ui/windows/runner/Runner.rc | 121 ++++++++ .../ui/windows/runner/flutter_window.cpp | 70 +++++ .../ui/windows/runner/flutter_window.h | 37 +++ .../ui/windows/runner/main.cpp | 47 +++ .../ui/windows/runner/resource.h | 20 ++ .../ui/windows/runner/runner.exe.manifest | 20 ++ .../ui/windows/runner/utils.cpp | 68 ++++ .../ui/windows/runner/utils.h | 23 ++ .../ui/windows/runner/win32_window.cpp | 292 ++++++++++++++++++ .../ui/windows/runner/win32_window.h | 106 +++++++ 19 files changed, 1130 insertions(+), 2 deletions(-) create mode 100644 dev/devicelab/bin/tasks/run_release_test_windows.dart create mode 100644 dev/integration_tests/ui/windows/.gitignore create mode 100644 dev/integration_tests/ui/windows/CMakeLists.txt create mode 100644 dev/integration_tests/ui/windows/flutter/CMakeLists.txt create mode 100644 dev/integration_tests/ui/windows/flutter/generated_plugins.cmake create mode 100644 dev/integration_tests/ui/windows/runner/CMakeLists.txt create mode 100644 dev/integration_tests/ui/windows/runner/Runner.rc create mode 100644 dev/integration_tests/ui/windows/runner/flutter_window.cpp create mode 100644 dev/integration_tests/ui/windows/runner/flutter_window.h create mode 100644 dev/integration_tests/ui/windows/runner/main.cpp create mode 100644 dev/integration_tests/ui/windows/runner/resource.h create mode 100644 dev/integration_tests/ui/windows/runner/runner.exe.manifest create mode 100644 dev/integration_tests/ui/windows/runner/utils.cpp create mode 100644 dev/integration_tests/ui/windows/runner/utils.h create mode 100644 dev/integration_tests/ui/windows/runner/win32_window.cpp create mode 100644 dev/integration_tests/ui/windows/runner/win32_window.h diff --git a/.ci.yaml b/.ci.yaml index e030f2fb9e663..b19031069c2c7 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -4193,6 +4193,24 @@ targets: - bin/** - .ci.yaml + - name: Windows run_release_test_windows + bringup: true + recipe: devicelab/devicelab_drone + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "vs_build", "version": "version:vs2019"} + ] + tags: > + ["devicelab", "hostonly", "windows"] + task_name: run_release_test_windows + runIf: + - dev/** + - packages/flutter_tools/** + - bin/** + - .ci.yaml + - name: Windows tool_integration_tests_1_6 recipe: flutter/flutter_drone timeout: 60 diff --git a/TESTOWNERS b/TESTOWNERS index c75bbb742fa09..0043b10ae757b 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -250,6 +250,7 @@ /dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios /dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin /dev/devicelab/bin/tasks/run_release_test_macos.dart @cbracken @flutter/tool +/dev/devicelab/bin/tasks/run_release_test_windows.dart @loic-sharma @flutter/tool /dev/devicelab/bin/tasks/technical_debt__cost.dart @HansMuller @flutter/framework /dev/devicelab/bin/tasks/web_benchmarks_canvaskit.dart @yjbanov @flutter/web /dev/devicelab/bin/tasks/web_benchmarks_html.dart @yjbanov @flutter/web diff --git a/dev/devicelab/bin/tasks/run_release_test_windows.dart b/dev/devicelab/bin/tasks/run_release_test_windows.dart new file mode 100644 index 0000000000000..d350b04c25cd0 --- /dev/null +++ b/dev/devicelab/bin/tasks/run_release_test_windows.dart @@ -0,0 +1,12 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/run_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.windows; + await task(createWindowsRunReleaseTest()); +} diff --git a/dev/devicelab/lib/tasks/run_tests.dart b/dev/devicelab/lib/tasks/run_tests.dart index 96db35f72b759..bec82e55a2ce9 100644 --- a/dev/devicelab/lib/tasks/run_tests.dart +++ b/dev/devicelab/lib/tasks/run_tests.dart @@ -26,6 +26,14 @@ TaskFunction createMacOSRunReleaseTest() { ); } +TaskFunction createWindowsRunReleaseTest() { + return DesktopRunOutputTest( + '${flutterDirectory.path}/dev/integration_tests/ui', + 'lib/empty.dart', + release: true, + ); +} + class AndroidRunOutputTest extends RunOutputTask { AndroidRunOutputTest({required super.release}) : super( '${flutterDirectory.path}/dev/integration_tests/ui', @@ -130,9 +138,9 @@ class DesktopRunOutputTest extends RunOutputTask { TaskResult verify(List stdout, List stderr) { _findNextMatcherInList( stdout, - (String line) => line.startsWith('Launching lib/main.dart on ') && + (String line) => line.startsWith('Launching $testTarget on ') && line.endsWith(' in ${release ? 'release' : 'debug'} mode...'), - 'Launching lib/main.dart on', + 'Launching $testTarget on', ); _findNextMatcherInList( diff --git a/dev/integration_tests/ui/windows/.gitignore b/dev/integration_tests/ui/windows/.gitignore new file mode 100644 index 0000000000000..d492d0d98c8fd --- /dev/null +++ b/dev/integration_tests/ui/windows/.gitignore @@ -0,0 +1,17 @@ +flutter/ephemeral/ + +# Visual Studio user-specific files. +*.suo +*.user +*.userosscache +*.sln.docstates + +# Visual Studio build-related files. +x64/ +x86/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ diff --git a/dev/integration_tests/ui/windows/CMakeLists.txt b/dev/integration_tests/ui/windows/CMakeLists.txt new file mode 100644 index 0000000000000..950267e782ab9 --- /dev/null +++ b/dev/integration_tests/ui/windows/CMakeLists.txt @@ -0,0 +1,101 @@ +# Project-level configuration. +cmake_minimum_required(VERSION 3.14) +project(ui LANGUAGES CXX) + +# The name of the executable created for the application. Change this to change +# the on-disk name of your application. +set(BINARY_NAME "ui") + +# Explicitly opt in to modern CMake behaviors to avoid warnings with recent +# versions of CMake. +cmake_policy(SET CMP0063 NEW) + +# Define build configuration option. +get_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(IS_MULTICONFIG) + set(CMAKE_CONFIGURATION_TYPES "Debug;Profile;Release" + CACHE STRING "" FORCE) +else() + if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + set(CMAKE_BUILD_TYPE "Debug" CACHE + STRING "Flutter build mode" FORCE) + set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS + "Debug" "Profile" "Release") + endif() +endif() +# Define settings for the Profile build mode. +set(CMAKE_EXE_LINKER_FLAGS_PROFILE "${CMAKE_EXE_LINKER_FLAGS_RELEASE}") +set(CMAKE_SHARED_LINKER_FLAGS_PROFILE "${CMAKE_SHARED_LINKER_FLAGS_RELEASE}") +set(CMAKE_C_FLAGS_PROFILE "${CMAKE_C_FLAGS_RELEASE}") +set(CMAKE_CXX_FLAGS_PROFILE "${CMAKE_CXX_FLAGS_RELEASE}") + +# Use Unicode for all projects. +add_definitions(-DUNICODE -D_UNICODE) + +# Compilation settings that should be applied to most targets. +# +# Be cautious about adding new options here, as plugins use this function by +# default. In most cases, you should add new options to specific targets instead +# of modifying this function. +function(APPLY_STANDARD_SETTINGS TARGET) + target_compile_features(${TARGET} PUBLIC cxx_std_17) + target_compile_options(${TARGET} PRIVATE /W4 /WX /wd"4100") + target_compile_options(${TARGET} PRIVATE /EHsc) + target_compile_definitions(${TARGET} PRIVATE "_HAS_EXCEPTIONS=0") + target_compile_definitions(${TARGET} PRIVATE "$<$:_DEBUG>") +endfunction() + +# Flutter library and tool build rules. +set(FLUTTER_MANAGED_DIR "${CMAKE_CURRENT_SOURCE_DIR}/flutter") +add_subdirectory(${FLUTTER_MANAGED_DIR}) + +# Application build; see runner/CMakeLists.txt. +add_subdirectory("runner") + +# Generated plugin build rules, which manage building the plugins and adding +# them to the application. +include(flutter/generated_plugins.cmake) + + +# === Installation === +# Support files are copied into place next to the executable, so that it can +# run in place. This is done instead of making a separate bundle (as on Linux) +# so that building and running from within Visual Studio will work. +set(BUILD_BUNDLE_DIR "$") +# Make the "install" step default, as it's required to run. +set(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1) +if(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT) + set(CMAKE_INSTALL_PREFIX "${BUILD_BUNDLE_DIR}" CACHE PATH "..." FORCE) +endif() + +set(INSTALL_BUNDLE_DATA_DIR "${CMAKE_INSTALL_PREFIX}/data") +set(INSTALL_BUNDLE_LIB_DIR "${CMAKE_INSTALL_PREFIX}") + +install(TARGETS ${BINARY_NAME} RUNTIME DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_ICU_DATA_FILE}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + COMPONENT Runtime) + +install(FILES "${FLUTTER_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) + +if(PLUGIN_BUNDLED_LIBRARIES) + install(FILES "${PLUGIN_BUNDLED_LIBRARIES}" + DESTINATION "${INSTALL_BUNDLE_LIB_DIR}" + COMPONENT Runtime) +endif() + +# Fully re-copy the assets directory on each build to avoid having stale files +# from a previous install. +set(FLUTTER_ASSET_DIR_NAME "flutter_assets") +install(CODE " + file(REMOVE_RECURSE \"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\") + " COMPONENT Runtime) +install(DIRECTORY "${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}" + DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" COMPONENT Runtime) + +# Install the AOT library on non-Debug builds only. +install(FILES "${AOT_LIBRARY}" DESTINATION "${INSTALL_BUNDLE_DATA_DIR}" + CONFIGURATIONS Profile;Release + COMPONENT Runtime) diff --git a/dev/integration_tests/ui/windows/flutter/CMakeLists.txt b/dev/integration_tests/ui/windows/flutter/CMakeLists.txt new file mode 100644 index 0000000000000..930d2071a324e --- /dev/null +++ b/dev/integration_tests/ui/windows/flutter/CMakeLists.txt @@ -0,0 +1,104 @@ +# This file controls Flutter-level build steps. It should not be edited. +cmake_minimum_required(VERSION 3.14) + +set(EPHEMERAL_DIR "${CMAKE_CURRENT_SOURCE_DIR}/ephemeral") + +# Configuration provided via flutter tool. +include(${EPHEMERAL_DIR}/generated_config.cmake) + +# TODO: Move the rest of this into files in ephemeral. See +# https://github.com/flutter/flutter/issues/57146. +set(WRAPPER_ROOT "${EPHEMERAL_DIR}/cpp_client_wrapper") + +# === Flutter Library === +set(FLUTTER_LIBRARY "${EPHEMERAL_DIR}/flutter_windows.dll") + +# Published to parent scope for install step. +set(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE) +set(FLUTTER_ICU_DATA_FILE "${EPHEMERAL_DIR}/icudtl.dat" PARENT_SCOPE) +set(PROJECT_BUILD_DIR "${PROJECT_DIR}/build/" PARENT_SCOPE) +set(AOT_LIBRARY "${PROJECT_DIR}/build/windows/app.so" PARENT_SCOPE) + +list(APPEND FLUTTER_LIBRARY_HEADERS + "flutter_export.h" + "flutter_windows.h" + "flutter_messenger.h" + "flutter_plugin_registrar.h" + "flutter_texture_registrar.h" +) +list(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND "${EPHEMERAL_DIR}/") +add_library(flutter INTERFACE) +target_include_directories(flutter INTERFACE + "${EPHEMERAL_DIR}" +) +target_link_libraries(flutter INTERFACE "${FLUTTER_LIBRARY}.lib") +add_dependencies(flutter flutter_assemble) + +# === Wrapper === +list(APPEND CPP_WRAPPER_SOURCES_CORE + "core_implementations.cc" + "standard_codec.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_PLUGIN + "plugin_registrar.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND "${WRAPPER_ROOT}/") +list(APPEND CPP_WRAPPER_SOURCES_APP + "flutter_engine.cc" + "flutter_view_controller.cc" +) +list(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND "${WRAPPER_ROOT}/") + +# Wrapper sources needed for a plugin. +add_library(flutter_wrapper_plugin STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} +) +apply_standard_settings(flutter_wrapper_plugin) +set_target_properties(flutter_wrapper_plugin PROPERTIES + POSITION_INDEPENDENT_CODE ON) +set_target_properties(flutter_wrapper_plugin PROPERTIES + CXX_VISIBILITY_PRESET hidden) +target_link_libraries(flutter_wrapper_plugin PUBLIC flutter) +target_include_directories(flutter_wrapper_plugin PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_plugin flutter_assemble) + +# Wrapper sources needed for the runner. +add_library(flutter_wrapper_app STATIC + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_APP} +) +apply_standard_settings(flutter_wrapper_app) +target_link_libraries(flutter_wrapper_app PUBLIC flutter) +target_include_directories(flutter_wrapper_app PUBLIC + "${WRAPPER_ROOT}/include" +) +add_dependencies(flutter_wrapper_app flutter_assemble) + +# === Flutter tool backend === +# _phony_ is a non-existent file to force this command to run every time, +# since currently there's no way to get a full input/output list from the +# flutter tool. +set(PHONY_OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/_phony_") +set_source_files_properties("${PHONY_OUTPUT}" PROPERTIES SYMBOLIC TRUE) +add_custom_command( + OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} + ${PHONY_OUTPUT} + COMMAND ${CMAKE_COMMAND} -E env + ${FLUTTER_TOOL_ENVIRONMENT} + "${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat" + windows-x64 $ + VERBATIM +) +add_custom_target(flutter_assemble DEPENDS + "${FLUTTER_LIBRARY}" + ${FLUTTER_LIBRARY_HEADERS} + ${CPP_WRAPPER_SOURCES_CORE} + ${CPP_WRAPPER_SOURCES_PLUGIN} + ${CPP_WRAPPER_SOURCES_APP} +) diff --git a/dev/integration_tests/ui/windows/flutter/generated_plugins.cmake b/dev/integration_tests/ui/windows/flutter/generated_plugins.cmake new file mode 100644 index 0000000000000..b93c4c30c1670 --- /dev/null +++ b/dev/integration_tests/ui/windows/flutter/generated_plugins.cmake @@ -0,0 +1,23 @@ +# +# Generated file, do not edit. +# + +list(APPEND FLUTTER_PLUGIN_LIST +) + +list(APPEND FLUTTER_FFI_PLUGIN_LIST +) + +set(PLUGIN_BUNDLED_LIBRARIES) + +foreach(plugin ${FLUTTER_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin}) + target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin) + list(APPEND PLUGIN_BUNDLED_LIBRARIES $) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries}) +endforeach(plugin) + +foreach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST}) + add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin}) + list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries}) +endforeach(ffi_plugin) diff --git a/dev/integration_tests/ui/windows/runner/CMakeLists.txt b/dev/integration_tests/ui/windows/runner/CMakeLists.txt new file mode 100644 index 0000000000000..394917c053a04 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.14) +project(runner LANGUAGES CXX) + +# Define the application target. To change its name, change BINARY_NAME in the +# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer +# work. +# +# Any new source files that you add to the application should be added here. +add_executable(${BINARY_NAME} WIN32 + "flutter_window.cpp" + "main.cpp" + "utils.cpp" + "win32_window.cpp" + "${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc" + "Runner.rc" + "runner.exe.manifest" +) + +# Apply the standard set of build settings. This can be removed for applications +# that need different build settings. +apply_standard_settings(${BINARY_NAME}) + +# Add preprocessor definitions for the build version. +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION=\"${FLUTTER_VERSION}\"") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}") +target_compile_definitions(${BINARY_NAME} PRIVATE "FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}") + +# Disable Windows macros that collide with C++ standard library functions. +target_compile_definitions(${BINARY_NAME} PRIVATE "NOMINMAX") + +# Add dependency libraries and include directories. Add any application-specific +# dependencies here. +target_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app) +target_link_libraries(${BINARY_NAME} PRIVATE "dwmapi.lib") +target_include_directories(${BINARY_NAME} PRIVATE "${CMAKE_SOURCE_DIR}") + +# Run the Flutter tool portions of the build. This must not be removed. +add_dependencies(${BINARY_NAME} flutter_assemble) diff --git a/dev/integration_tests/ui/windows/runner/Runner.rc b/dev/integration_tests/ui/windows/runner/Runner.rc new file mode 100644 index 0000000000000..f74da7eb54f26 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/Runner.rc @@ -0,0 +1,121 @@ +// Microsoft Visual C++ generated resource script. +// +#pragma code_page(65001) +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#include ""winres.h""\r\n" + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +// IDI_APP_ICON ICON "resources\\app_icon.ico" + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD) +#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD +#else +#define VERSION_AS_NUMBER 1,0,0,0 +#endif + +#if defined(FLUTTER_VERSION) +#define VERSION_AS_STRING FLUTTER_VERSION +#else +#define VERSION_AS_STRING "1.0.0" +#endif + +VS_VERSION_INFO VERSIONINFO + FILEVERSION VERSION_AS_NUMBER + PRODUCTVERSION VERSION_AS_NUMBER + FILEFLAGSMASK VS_FFI_FILEFLAGSMASK +#ifdef _DEBUG + FILEFLAGS VS_FF_DEBUG +#else + FILEFLAGS 0x0L +#endif + FILEOS VOS__WINDOWS32 + FILETYPE VFT_APP + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "io.flutter" "\0" + VALUE "FileDescription", "ui" "\0" + VALUE "FileVersion", VERSION_AS_STRING "\0" + VALUE "InternalName", "ui" "\0" + VALUE "LegalCopyright", "Copyright (C) 2022 io.flutter. All rights reserved." "\0" + VALUE "OriginalFilename", "ui.exe" "\0" + VALUE "ProductName", "ui" "\0" + VALUE "ProductVersion", VERSION_AS_STRING "\0" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED diff --git a/dev/integration_tests/ui/windows/runner/flutter_window.cpp b/dev/integration_tests/ui/windows/runner/flutter_window.cpp new file mode 100644 index 0000000000000..f68aa9c26c0f5 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/flutter_window.cpp @@ -0,0 +1,70 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter_window.h" + +#include + +#include "flutter/generated_plugin_registrant.h" + +FlutterWindow::FlutterWindow(const flutter::DartProject& project) + : project_(project) {} + +FlutterWindow::~FlutterWindow() {} + +bool FlutterWindow::OnCreate() { + if (!Win32Window::OnCreate()) { + return false; + } + + RECT frame = GetClientArea(); + + // The size here must match the window dimensions to avoid unnecessary surface + // creation / destruction in the startup path. + flutter_controller_ = std::make_unique( + frame.right - frame.left, frame.bottom - frame.top, project_); + // Ensure that basic setup of the controller was successful. + if (!flutter_controller_->engine() || !flutter_controller_->view()) { + return false; + } + RegisterPlugins(flutter_controller_->engine()); + SetChildContent(flutter_controller_->view()->GetNativeWindow()); + + flutter_controller_->engine()->SetNextFrameCallback([&]() { + this->Show(); + }); + + return true; +} + +void FlutterWindow::OnDestroy() { + if (flutter_controller_) { + flutter_controller_ = nullptr; + } + + Win32Window::OnDestroy(); +} + +LRESULT +FlutterWindow::MessageHandler(HWND hwnd, UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + // Give Flutter, including plugins, an opportunity to handle window messages. + if (flutter_controller_) { + std::optional result = + flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam, + lparam); + if (result) { + return *result; + } + } + + switch (message) { + case WM_FONTCHANGE: + flutter_controller_->engine()->ReloadSystemFonts(); + break; + } + + return Win32Window::MessageHandler(hwnd, message, wparam, lparam); +} diff --git a/dev/integration_tests/ui/windows/runner/flutter_window.h b/dev/integration_tests/ui/windows/runner/flutter_window.h new file mode 100644 index 0000000000000..bbc5836c018a2 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/flutter_window.h @@ -0,0 +1,37 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_FLUTTER_WINDOW_H_ +#define RUNNER_FLUTTER_WINDOW_H_ + +#include +#include + +#include + +#include "win32_window.h" + +// A window that does nothing but host a Flutter view. +class FlutterWindow : public Win32Window { + public: + // Creates a new FlutterWindow hosting a Flutter view running |project|. + explicit FlutterWindow(const flutter::DartProject& project); + virtual ~FlutterWindow(); + + protected: + // Win32Window: + bool OnCreate() override; + void OnDestroy() override; + LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam, + LPARAM const lparam) noexcept override; + + private: + // The project to run. + flutter::DartProject project_; + + // The Flutter instance hosted by this window. + std::unique_ptr flutter_controller_; +}; + +#endif // RUNNER_FLUTTER_WINDOW_H_ diff --git a/dev/integration_tests/ui/windows/runner/main.cpp b/dev/integration_tests/ui/windows/runner/main.cpp new file mode 100644 index 0000000000000..1358eeeac850a --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/main.cpp @@ -0,0 +1,47 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include +#include +#include + +#include "flutter_window.h" +#include "utils.h" + +int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev, + _In_ wchar_t *command_line, _In_ int show_command) { + // Attach to console when present (e.g., 'flutter run') or create a + // new console when running with a debugger. + if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) { + CreateAndAttachConsole(); + } + + // Initialize COM, so that it is available for use in the library and/or + // plugins. + ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); + + flutter::DartProject project(L"data"); + + std::vector command_line_arguments = + GetCommandLineArguments(); + + project.set_dart_entrypoint_arguments(std::move(command_line_arguments)); + + FlutterWindow window(project); + Win32Window::Point origin(10, 10); + Win32Window::Size size(1280, 720); + if (!window.Create(L"ui", origin, size)) { + return EXIT_FAILURE; + } + window.SetQuitOnClose(true); + + ::MSG msg; + while (::GetMessage(&msg, nullptr, 0, 0)) { + ::TranslateMessage(&msg); + ::DispatchMessage(&msg); + } + + ::CoUninitialize(); + return EXIT_SUCCESS; +} diff --git a/dev/integration_tests/ui/windows/runner/resource.h b/dev/integration_tests/ui/windows/runner/resource.h new file mode 100644 index 0000000000000..c245ff19cb580 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/resource.h @@ -0,0 +1,20 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by Runner.rc +// +#define IDI_APP_ICON 101 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/dev/integration_tests/ui/windows/runner/runner.exe.manifest b/dev/integration_tests/ui/windows/runner/runner.exe.manifest new file mode 100644 index 0000000000000..a42ea7687cb67 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/runner.exe.manifest @@ -0,0 +1,20 @@ + + + + + PerMonitorV2 + + + + + + + + + + + + + + + diff --git a/dev/integration_tests/ui/windows/runner/utils.cpp b/dev/integration_tests/ui/windows/runner/utils.cpp new file mode 100644 index 0000000000000..7ae65329f704d --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/utils.cpp @@ -0,0 +1,68 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "utils.h" + +#include +#include +#include +#include + +#include + +void CreateAndAttachConsole() { + if (::AllocConsole()) { + FILE *unused; + if (freopen_s(&unused, "CONOUT$", "w", stdout)) { + _dup2(_fileno(stdout), 1); + } + if (freopen_s(&unused, "CONOUT$", "w", stderr)) { + _dup2(_fileno(stdout), 2); + } + std::ios::sync_with_stdio(); + FlutterDesktopResyncOutputStreams(); + } +} + +std::vector GetCommandLineArguments() { + // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use. + int argc; + wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + if (argv == nullptr) { + return std::vector(); + } + + std::vector command_line_arguments; + + // Skip the first argument as it's the binary name. + for (int i = 1; i < argc; i++) { + command_line_arguments.push_back(Utf8FromUtf16(argv[i])); + } + + ::LocalFree(argv); + + return command_line_arguments; +} + +std::string Utf8FromUtf16(const wchar_t* utf16_string) { + if (utf16_string == nullptr) { + return std::string(); + } + int target_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, nullptr, 0, nullptr, nullptr); + std::string utf8_string; + if (target_length == 0 || target_length > utf8_string.max_size()) { + return utf8_string; + } + utf8_string.resize(target_length); + int converted_length = ::WideCharToMultiByte( + CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string, + -1, utf8_string.data(), + target_length, nullptr, nullptr); + if (converted_length == 0) { + return std::string(); + } + return utf8_string; +} diff --git a/dev/integration_tests/ui/windows/runner/utils.h b/dev/integration_tests/ui/windows/runner/utils.h new file mode 100644 index 0000000000000..54414c989ba71 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/utils.h @@ -0,0 +1,23 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_UTILS_H_ +#define RUNNER_UTILS_H_ + +#include +#include + +// Creates a console for the process, and redirects stdout and stderr to +// it for both the runner and the Flutter library. +void CreateAndAttachConsole(); + +// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string +// encoded in UTF-8. Returns an empty std::string on failure. +std::string Utf8FromUtf16(const wchar_t* utf16_string); + +// Gets the command line arguments passed in as a std::vector, +// encoded in UTF-8. Returns an empty std::vector on failure. +std::vector GetCommandLineArguments(); + +#endif // RUNNER_UTILS_H_ diff --git a/dev/integration_tests/ui/windows/runner/win32_window.cpp b/dev/integration_tests/ui/windows/runner/win32_window.cpp new file mode 100644 index 0000000000000..6a753c7633ef3 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/win32_window.cpp @@ -0,0 +1,292 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "win32_window.h" + +#include +#include + +#include "resource.h" + +namespace { + +/// Window attribute that enables dark mode window decorations. +/// +/// Redefined in case the developer's machine has a Windows SDK older than +/// version 10.0.22000.0. +/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute +#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE +#define DWMWA_USE_IMMERSIVE_DARK_MODE 20 +#endif + +constexpr const wchar_t kWindowClassName[] = L"FLUTTER_RUNNER_WIN32_WINDOW"; + +/// Registry key for app theme preference. +/// +/// A value of 0 indicates apps should use dark mode. A non-zero or missing +/// value indicates apps should use light mode. +constexpr const wchar_t kGetPreferredBrightnessRegKey[] = + L"Software\\Microsoft\\Windows\\CurrentVersion\\Themes\\Personalize"; +constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L"AppsUseLightTheme"; + +// The number of Win32Window objects that currently exist. +static int g_active_window_count = 0; + +using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd); + +// Scale helper to convert logical scaler values to physical using passed in +// scale factor +int Scale(int source, double scale_factor) { + return static_cast(source * scale_factor); +} + +// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module. +// This API is only needed for PerMonitor V1 awareness mode. +void EnableFullDpiSupportIfAvailable(HWND hwnd) { + HMODULE user32_module = LoadLibraryA("User32.dll"); + if (!user32_module) { + return; + } + auto enable_non_client_dpi_scaling = + reinterpret_cast( + GetProcAddress(user32_module, "EnableNonClientDpiScaling")); + if (enable_non_client_dpi_scaling != nullptr) { + enable_non_client_dpi_scaling(hwnd); + } + FreeLibrary(user32_module); +} + +} // namespace + +// Manages the Win32Window's window class registration. +class WindowClassRegistrar { + public: + ~WindowClassRegistrar() = default; + + // Returns the singleton registar instance. + static WindowClassRegistrar* GetInstance() { + if (!instance_) { + instance_ = new WindowClassRegistrar(); + } + return instance_; + } + + // Returns the name of the window class, registering the class if it hasn't + // previously been registered. + const wchar_t* GetWindowClass(); + + // Unregisters the window class. Should only be called if there are no + // instances of the window. + void UnregisterWindowClass(); + + private: + WindowClassRegistrar() = default; + + static WindowClassRegistrar* instance_; + + bool class_registered_ = false; +}; + +WindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr; + +const wchar_t* WindowClassRegistrar::GetWindowClass() { + if (!class_registered_) { + WNDCLASS window_class{}; + window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); + window_class.lpszClassName = kWindowClassName; + window_class.style = CS_HREDRAW | CS_VREDRAW; + window_class.cbClsExtra = 0; + window_class.cbWndExtra = 0; + window_class.hInstance = GetModuleHandle(nullptr); + window_class.hIcon = + LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON)); + window_class.hbrBackground = 0; + window_class.lpszMenuName = nullptr; + window_class.lpfnWndProc = Win32Window::WndProc; + RegisterClass(&window_class); + class_registered_ = true; + } + return kWindowClassName; +} + +void WindowClassRegistrar::UnregisterWindowClass() { + UnregisterClass(kWindowClassName, nullptr); + class_registered_ = false; +} + +Win32Window::Win32Window() { + ++g_active_window_count; +} + +Win32Window::~Win32Window() { + --g_active_window_count; + Destroy(); +} + +bool Win32Window::Create(const std::wstring& title, + const Point& origin, + const Size& size) { + Destroy(); + + const wchar_t* window_class = + WindowClassRegistrar::GetInstance()->GetWindowClass(); + + const POINT target_point = {static_cast(origin.x), + static_cast(origin.y)}; + HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST); + UINT dpi = FlutterDesktopGetDpiForMonitor(monitor); + double scale_factor = dpi / 96.0; + + HWND window = CreateWindow( + window_class, title.c_str(), WS_OVERLAPPEDWINDOW, + Scale(origin.x, scale_factor), Scale(origin.y, scale_factor), + Scale(size.width, scale_factor), Scale(size.height, scale_factor), + nullptr, nullptr, GetModuleHandle(nullptr), this); + + if (!window) { + return false; + } + + UpdateTheme(window); + + return OnCreate(); +} + +bool Win32Window::Show() { + return ShowWindow(window_handle_, SW_SHOWNORMAL); +} + +// static +LRESULT CALLBACK Win32Window::WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + if (message == WM_NCCREATE) { + auto window_struct = reinterpret_cast(lparam); + SetWindowLongPtr(window, GWLP_USERDATA, + reinterpret_cast(window_struct->lpCreateParams)); + + auto that = static_cast(window_struct->lpCreateParams); + EnableFullDpiSupportIfAvailable(window); + that->window_handle_ = window; + } else if (Win32Window* that = GetThisFromHandle(window)) { + return that->MessageHandler(window, message, wparam, lparam); + } + + return DefWindowProc(window, message, wparam, lparam); +} + +LRESULT +Win32Window::MessageHandler(HWND hwnd, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept { + switch (message) { + case WM_DESTROY: + window_handle_ = nullptr; + Destroy(); + if (quit_on_close_) { + PostQuitMessage(0); + } + return 0; + + case WM_DPICHANGED: { + auto newRectSize = reinterpret_cast(lparam); + LONG newWidth = newRectSize->right - newRectSize->left; + LONG newHeight = newRectSize->bottom - newRectSize->top; + + SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth, + newHeight, SWP_NOZORDER | SWP_NOACTIVATE); + + return 0; + } + case WM_SIZE: { + RECT rect = GetClientArea(); + if (child_content_ != nullptr) { + // Size and position the child window. + MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left, + rect.bottom - rect.top, TRUE); + } + return 0; + } + + case WM_ACTIVATE: + if (child_content_ != nullptr) { + SetFocus(child_content_); + } + return 0; + + case WM_DWMCOLORIZATIONCOLORCHANGED: + UpdateTheme(hwnd); + return 0; + } + + return DefWindowProc(window_handle_, message, wparam, lparam); +} + +void Win32Window::Destroy() { + OnDestroy(); + + if (window_handle_) { + DestroyWindow(window_handle_); + window_handle_ = nullptr; + } + if (g_active_window_count == 0) { + WindowClassRegistrar::GetInstance()->UnregisterWindowClass(); + } +} + +Win32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept { + return reinterpret_cast( + GetWindowLongPtr(window, GWLP_USERDATA)); +} + +void Win32Window::SetChildContent(HWND content) { + child_content_ = content; + SetParent(content, window_handle_); + RECT frame = GetClientArea(); + + MoveWindow(content, frame.left, frame.top, frame.right - frame.left, + frame.bottom - frame.top, true); + + SetFocus(child_content_); +} + +RECT Win32Window::GetClientArea() { + RECT frame; + GetClientRect(window_handle_, &frame); + return frame; +} + +HWND Win32Window::GetHandle() { + return window_handle_; +} + +void Win32Window::SetQuitOnClose(bool quit_on_close) { + quit_on_close_ = quit_on_close; +} + +bool Win32Window::OnCreate() { + // No-op; provided for subclasses. + return true; +} + +void Win32Window::OnDestroy() { + // No-op; provided for subclasses. +} + +void Win32Window::UpdateTheme(HWND const window) { + DWORD light_mode; + DWORD light_mode_size = sizeof(light_mode); + LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey, + kGetPreferredBrightnessRegValue, + RRF_RT_REG_DWORD, nullptr, &light_mode, + &light_mode_size); + + if (result == ERROR_SUCCESS) { + BOOL enable_dark_mode = light_mode == 0; + DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE, + &enable_dark_mode, sizeof(enable_dark_mode)); + } +} diff --git a/dev/integration_tests/ui/windows/runner/win32_window.h b/dev/integration_tests/ui/windows/runner/win32_window.h new file mode 100644 index 0000000000000..84a27dc75dfe0 --- /dev/null +++ b/dev/integration_tests/ui/windows/runner/win32_window.h @@ -0,0 +1,106 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef RUNNER_WIN32_WINDOW_H_ +#define RUNNER_WIN32_WINDOW_H_ + +#include + +#include +#include +#include + +// A class abstraction for a high DPI-aware Win32 Window. Intended to be +// inherited from by classes that wish to specialize with custom +// rendering and input handling +class Win32Window { + public: + struct Point { + unsigned int x; + unsigned int y; + Point(unsigned int x, unsigned int y) : x(x), y(y) {} + }; + + struct Size { + unsigned int width; + unsigned int height; + Size(unsigned int width, unsigned int height) + : width(width), height(height) {} + }; + + Win32Window(); + virtual ~Win32Window(); + + // Creates a win32 window with |title| that is positioned and sized using + // |origin| and |size|. New windows are created on the default monitor. Window + // sizes are specified to the OS in physical pixels, hence to ensure a + // consistent size this function will scale the inputted width and height as + // as appropriate for the default monitor. The window is invisible until + // |Show| is called. Returns true if the window was created successfully. + bool Create(const std::wstring& title, const Point& origin, const Size& size); + + // Show the current window. Returns true if the window was successfully shown. + bool Show(); + + // Release OS resources associated with window. + void Destroy(); + + // Inserts |content| into the window tree. + void SetChildContent(HWND content); + + // Returns the backing Window handle to enable clients to set icon and other + // window properties. Returns nullptr if the window has been destroyed. + HWND GetHandle(); + + // If true, closing this window will quit the application. + void SetQuitOnClose(bool quit_on_close); + + // Return a RECT representing the bounds of the current client area. + RECT GetClientArea(); + + protected: + // Processes and route salient window messages for mouse handling, + // size change and DPI. Delegates handling of these to member overloads that + // inheriting classes can handle. + virtual LRESULT MessageHandler(HWND window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Called when CreateAndShow is called, allowing subclass window-related + // setup. Subclasses should return false if setup fails. + virtual bool OnCreate(); + + // Called when Destroy is called. + virtual void OnDestroy(); + + private: + friend class WindowClassRegistrar; + + // OS callback called by message pump. Handles the WM_NCCREATE message which + // is passed when the non-client area is being created and enables automatic + // non-client DPI scaling so that the non-client area automatically + // responsponds to changes in DPI. All other messages are handled by + // MessageHandler. + static LRESULT CALLBACK WndProc(HWND const window, + UINT const message, + WPARAM const wparam, + LPARAM const lparam) noexcept; + + // Retrieves a class instance pointer for |window| + static Win32Window* GetThisFromHandle(HWND const window) noexcept; + + // Update the window frame's theme to match the system theme. + static void UpdateTheme(HWND const window); + + bool quit_on_close_ = false; + + // window handle for top level window. + HWND window_handle_ = nullptr; + + // window handle for hosted content. + HWND child_content_ = nullptr; +}; + +#endif // RUNNER_WIN32_WINDOW_H_ From 117a83a4a7178c328d296748bd93ff388724cb67 Mon Sep 17 00:00:00 2001 From: Tae Hyung Kim Date: Thu, 8 Dec 2022 14:29:14 -0800 Subject: [PATCH 03/71] Throw error when plural case had undefined behavior (#116622) * init * add comment * make error more actionable --- .../lib/src/localizations/gen_l10n.dart | 15 ++++++++- .../generate_localizations_test.dart | 32 +++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart index 51d6e534f19ab..30bb27f28db73 100644 --- a/packages/flutter_tools/lib/src/localizations/gen_l10n.dart +++ b/packages/flutter_tools/lib/src/localizations/gen_l10n.dart @@ -1167,7 +1167,20 @@ class LocalizationsGenerator { } if (!pluralLogicArgs.containsKey(pluralCases[pluralCase])) { final String pluralPartExpression = generateVariables(pluralMessage); - pluralLogicArgs[pluralCases[pluralCase]!] = ' ${pluralCases[pluralCase]}: $pluralPartExpression,'; + final String? transformedPluralCase = pluralCases[pluralCase]; + // A valid plural case is one of "=0", "=1", "=2", "zero", "one", "two", "few", "many", or "other". + if (transformedPluralCase == null) { + throw L10nParserException( + ''' +The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "many", or "other. + $pluralCase is not a valid plural case.''', + _inputFileNames[locale]!, + message.resourceId, + translationForMessage, + pluralPart.positionInMessage, + ); + } + pluralLogicArgs[transformedPluralCase] = ' ${pluralCases[pluralCase]}: $pluralPartExpression,'; } else if (!suppressWarnings) { logger.printWarning(''' [${_inputFileNames[locale]}:${message.resourceId}] ICU Syntax Warning: The plural part specified below is overridden by a later plural part. diff --git a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart index 9da376bc977ed..604b82144e4ab 100644 --- a/packages/flutter_tools/test/general.shard/generate_localizations_test.dart +++ b/packages/flutter_tools/test/general.shard/generate_localizations_test.dart @@ -2027,6 +2027,38 @@ import 'output-localization-file_en.dart' deferred as output-localization-file_e ^''')); }); + testWithoutContext('undefined plural cases throws syntax error', () { + const String pluralMessageWithUndefinedParts = ''' +{ + "count": "{count,plural, =0{None} =1{One} =2{Two} =3{Undefined Behavior!} other{Hmm...}}" +}'''; + final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n') + ..createSync(recursive: true); + l10nDirectory.childFile(defaultTemplateArbFileName) + .writeAsStringSync(pluralMessageWithUndefinedParts); + try { + LocalizationsGenerator( + fileSystem: fs, + inputPathString: defaultL10nPathString, + outputPathString: defaultL10nPathString, + templateArbFileName: defaultTemplateArbFileName, + outputFileString: defaultOutputFileString, + classNameString: defaultClassNameString, + logger: logger, + ) + ..loadResources() + ..writeOutputFiles(); + } on L10nException catch (error) { + expect(error.message, contains('Found syntax errors.')); + expect(logger.hadErrorOutput, isTrue); + expect(logger.errorText, contains(''' +[app_en.arb:count] The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "many", or "other. + 3 is not a valid plural case. + {count,plural, =0{None} =1{One} =2{Two} =3{Undefined Behavior!} other{Hmm...}} + ^''')); + } + }); + testWithoutContext('should automatically infer plural placeholders that are not explicitly defined', () { const String pluralMessageWithoutPlaceholdersAttribute = ''' { From afdc4840110decfddd6426d6e8b265e7ff4ee48f Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Thu, 8 Dec 2022 17:00:22 -0800 Subject: [PATCH 04/71] add test of flutter update-packages --transitive-closure --consumer-only (#116747) --- .../hermetic/update_packages_test.dart | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart index cc2e71490b0d6..2f205b53e98b0 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart @@ -5,6 +5,7 @@ import 'package:file/file.dart'; import 'package:file/memory.dart'; import 'package:flutter_tools/src/base/file_system.dart'; +import 'package:flutter_tools/src/base/logger.dart'; import 'package:flutter_tools/src/cache.dart'; import 'package:flutter_tools/src/commands/update_packages.dart'; import 'package:flutter_tools/src/dart/pub.dart'; @@ -88,9 +89,11 @@ void main() { late Directory flutter; late FakePub pub; late FakeProcessManager processManager; + late BufferLogger logger; setUpAll(() { Cache.disableLocking(); + logger = BufferLogger.test(); }); setUp(() { @@ -174,6 +177,35 @@ void main() { ), }); + testUsingContext('--transitive-closure --consumer-only', () async { + final UpdatePackagesCommand command = UpdatePackagesCommand(); + await createTestCommandRunner(command).run([ + 'update-packages', + '--transitive-closure', + '--consumer-only', + ]); + expect(pub.pubGetDirectories, equals([ + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', + ])); + expect(pub.pubBatchDirectories, equals([ + '/.tmp_rand0/flutter_update_packages.rand0/synthetic_package', + ])); + // Expecting a line like: + // 'flutter -> {collection, meta, typed_data, vector_math}' + expect( + logger.statusText, + contains(RegExp(r'flutter -> {([a-z_]+, )*([a-z_]+)+}')), + ); + }, overrides: { + Pub: () => pub, + FileSystem: () => fileSystem, + ProcessManager: () => processManager, + Cache: () => Cache.test( + processManager: processManager, + ), + Logger: () => logger, + }); + testUsingContext('force updates packages --synthetic-package-path', () async { final UpdatePackagesCommand command = UpdatePackagesCommand(); const String dir = '/path/to/synthetic/package'; From 7c8e171320505c72444ff49a019f9d79c5cf170c Mon Sep 17 00:00:00 2001 From: Siva Date: Thu, 8 Dec 2022 18:03:51 -0800 Subject: [PATCH 05/71] Manual Roll of Flutter Engine from 67254d6e4b03 to 8d83b98c55b3 (#116635) * Roll Flutter Engine from 67254d6e4b03 to 8d83b98c55b3 * Roll Dart SDK from 35a9facce191 to e517487c5679 (Dart 3.0) (#38105) * Bump SDK versions. * Bump Dart SDK version constraints * Update shrine package to 2.0.1 (null safe version) * Fix more tests. * Include patches from Jason for min android sdk version * Fix analyzer warning --- bin/internal/engine.version | 2 +- dev/automated_tests/pubspec.yaml | 2 +- dev/benchmarks/complex_layout/pubspec.yaml | 2 +- dev/benchmarks/macrobenchmarks/pubspec.yaml | 2 +- dev/benchmarks/microbenchmarks/pubspec.yaml | 2 +- .../multiple_flutters/module/pubspec.yaml | 2 +- .../platform_channels_benchmarks/pubspec.yaml | 2 +- dev/benchmarks/platform_views_layout/pubspec.yaml | 2 +- .../pubspec.yaml | 2 +- dev/benchmarks/test_apps/stocks/pubspec.yaml | 2 +- dev/bots/pubspec.yaml | 2 +- dev/conductor/core/pubspec.yaml | 2 +- dev/customer_testing/pubspec.yaml | 2 +- .../bin/tasks/gradle_desugar_classes_test.dart | 3 ++- dev/devicelab/bin/tasks/module_test_ios.dart | 2 +- .../bin/tasks/plugin_dependencies_test.dart | 2 +- dev/devicelab/lib/framework/apk_utils.dart | 14 ++++++++++++++ dev/devicelab/pubspec.yaml | 2 +- dev/docs/platform_integration/pubspec.yaml | 2 +- dev/forbidden_from_release_tests/pubspec.yaml | 2 +- .../abstract_method_smoke_test/pubspec.yaml | 2 +- .../android_embedding_v2_smoke_test/pubspec.yaml | 2 +- .../android_semantics_testing/pubspec.yaml | 2 +- dev/integration_tests/android_views/pubspec.yaml | 2 +- dev/integration_tests/channels/pubspec.yaml | 2 +- .../deferred_components_test/pubspec.yaml | 2 +- dev/integration_tests/external_ui/pubspec.yaml | 2 +- dev/integration_tests/flavors/pubspec.yaml | 2 +- dev/integration_tests/flutter_gallery/pubspec.yaml | 4 ++-- .../gradle_deprecated_settings/pubspec.yaml | 2 +- .../hybrid_android_views/pubspec.yaml | 2 +- .../ios_add2app_life_cycle/flutterapp/pubspec.yaml | 2 +- .../ios_app_with_extensions/pubspec.yaml | 2 +- .../ios_platform_view_tests/pubspec.yaml | 2 +- dev/integration_tests/non_nullable/pubspec.yaml | 2 +- .../platform_interaction/pubspec.yaml | 2 +- .../release_smoke_test/pubspec.yaml | 2 +- dev/integration_tests/spell_check/pubspec.yaml | 2 +- dev/integration_tests/ui/pubspec.yaml | 2 +- dev/integration_tests/web/pubspec.yaml | 2 +- .../web_compile_tests/pubspec.yaml | 2 +- dev/integration_tests/web_e2e_tests/pubspec.yaml | 2 +- .../windows_startup_test/pubspec.yaml | 2 +- dev/manual_tests/pubspec.yaml | 2 +- dev/missing_dependency_tests/pubspec.yaml | 2 +- dev/tools/dartdoc.dart | 2 +- dev/tools/gen_defaults/pubspec.yaml | 2 +- dev/tools/gen_keycodes/pubspec.yaml | 2 +- dev/tools/pubspec.yaml | 2 +- dev/tools/vitool/pubspec.yaml | 2 +- dev/tracing_tests/pubspec.yaml | 2 +- examples/api/pubspec.yaml | 2 +- examples/flutter_view/pubspec.yaml | 2 +- examples/hello_world/pubspec.yaml | 2 +- examples/image_list/pubspec.yaml | 2 +- examples/layers/pubspec.yaml | 2 +- examples/platform_channel/pubspec.yaml | 2 +- examples/platform_channel_swift/pubspec.yaml | 2 +- examples/platform_view/pubspec.yaml | 2 +- examples/splash/pubspec.yaml | 2 +- packages/flutter/pubspec.yaml | 2 +- packages/flutter/test_private/pubspec.yaml | 2 +- packages/flutter/test_private/test/pubspec.yaml | 2 +- packages/flutter_driver/pubspec.yaml | 2 +- packages/flutter_goldens/pubspec.yaml | 2 +- packages/flutter_goldens_client/pubspec.yaml | 2 +- packages/flutter_localizations/pubspec.yaml | 2 +- packages/flutter_test/pubspec.yaml | 2 +- .../test/test_config/project_root/pubspec.yaml | 2 +- .../lib/src/commands/update_packages.dart | 6 +++--- packages/flutter_tools/pubspec.yaml | 2 +- .../hermetic/analyze_continuously_test.dart | 2 +- .../hermetic/update_packages_test.dart | 4 ++-- .../test/data/asset_test/font/pubspec.yaml | 2 +- .../test/data/asset_test/main/pubspec.yaml | 2 +- .../targets/dart_plugin_registrant_test.dart | 2 +- .../general.shard/test/test_compiler_test.dart | 2 +- .../test/general.shard/update_packages_test.dart | 6 +++--- .../test/integration.shard/analyze_once_test.dart | 2 +- .../break_on_framework_exceptions_test.dart | 2 +- .../flutter_build_null_unsafe_test.dart | 2 +- .../test_data/background_project.dart | 4 ++-- .../integration.shard/test_data/basic_project.dart | 12 +++++------- .../test_data/compile_error_project.dart | 2 +- .../test_data/deferred_components_project.dart | 2 +- .../test_data/gen_l10n_project.dart | 2 +- .../test_data/hot_reload_const_project.dart | 2 +- .../test_data/hot_reload_project.dart | 2 +- .../test_data/hot_reload_with_asset.dart | 2 +- .../test_data/integration_tests_project.dart | 2 +- .../test_data/migrate_project.dart | 2 +- .../test_data/multidex_project.dart | 2 +- .../test_data/project_with_early_error.dart | 2 +- .../test_data/single_widget_reload_project.dart | 2 +- .../test_data/stateless_stateful_project.dart | 2 +- .../test_data/stepping_project.dart | 4 ++-- .../integration.shard/test_data/test_project.dart | 2 +- .../integration.shard/test_data/tests_project.dart | 2 +- .../web_plugin_registrant_test.dart | 8 +++----- packages/flutter_web_plugins/pubspec.yaml | 2 +- .../fuchsia_remote_debug_protocol/pubspec.yaml | 2 +- packages/integration_test/example/pubspec.yaml | 2 +- .../integration_test_macos/pubspec.yaml | 2 +- packages/integration_test/pubspec.yaml | 2 +- 104 files changed, 132 insertions(+), 121 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 9471c512ccddd..072bbac42d53b 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -67254d6e4b0369fa83418cf9e9ff68b21c529765 +8d83b98c55b3d0839de0c4201f5a8d56dbf92d2f diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml index fc9014775a6c4..5ddc94cd7f58d 100644 --- a/dev/automated_tests/pubspec.yaml +++ b/dev/automated_tests/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_automated_tests environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml index 2525ce9d1fa11..2b87c0adc9aae 100644 --- a/dev/benchmarks/complex_layout/pubspec.yaml +++ b/dev/benchmarks/complex_layout/pubspec.yaml @@ -2,7 +2,7 @@ name: complex_layout description: A benchmark of a relatively complex layout. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/macrobenchmarks/pubspec.yaml b/dev/benchmarks/macrobenchmarks/pubspec.yaml index b1e58fa563782..d8f6b9887184b 100644 --- a/dev/benchmarks/macrobenchmarks/pubspec.yaml +++ b/dev/benchmarks/macrobenchmarks/pubspec.yaml @@ -2,7 +2,7 @@ name: macrobenchmarks description: Performance benchmarks using flutter drive. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml index 93fb7995434c0..8cac43e6f38db 100644 --- a/dev/benchmarks/microbenchmarks/pubspec.yaml +++ b/dev/benchmarks/microbenchmarks/pubspec.yaml @@ -2,7 +2,7 @@ name: microbenchmarks description: Small benchmarks for very specific parts of the Flutter framework. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: meta: 1.8.0 diff --git a/dev/benchmarks/multiple_flutters/module/pubspec.yaml b/dev/benchmarks/multiple_flutters/module/pubspec.yaml index 12d128d2a6bfe..f601fd8abc805 100644 --- a/dev/benchmarks/multiple_flutters/module/pubspec.yaml +++ b/dev/benchmarks/multiple_flutters/module/pubspec.yaml @@ -4,7 +4,7 @@ description: A module that is embedded in the multiple_flutters benchmark test. version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml index 908500cf2b8d5..7a65a39e6c25d 100644 --- a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml +++ b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/platform_views_layout/pubspec.yaml b/dev/benchmarks/platform_views_layout/pubspec.yaml index 40b3cf9ce90f0..ecbe18af8f5be 100644 --- a/dev/benchmarks/platform_views_layout/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout/pubspec.yaml @@ -2,7 +2,7 @@ name: platform_views_layout description: A benchmark for platform views. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml index 1f0097bb0f48f..563229913acc2 100644 --- a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml @@ -2,7 +2,7 @@ name: platform_views_layout_hybrid_composition description: A benchmark for platform views, using hybrid composition on android. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/benchmarks/test_apps/stocks/pubspec.yaml b/dev/benchmarks/test_apps/stocks/pubspec.yaml index ed85f8bae33e6..e62b8ff9a998b 100644 --- a/dev/benchmarks/test_apps/stocks/pubspec.yaml +++ b/dev/benchmarks/test_apps/stocks/pubspec.yaml @@ -1,7 +1,7 @@ name: stocks environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/bots/pubspec.yaml b/dev/bots/pubspec.yaml index 48a361df46af3..9747eff910263 100644 --- a/dev/bots/pubspec.yaml +++ b/dev/bots/pubspec.yaml @@ -2,7 +2,7 @@ name: tests_on_bots description: Scripts which run on bots. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: args: 2.3.1 diff --git a/dev/conductor/core/pubspec.yaml b/dev/conductor/core/pubspec.yaml index aca9675a11893..d06e422805299 100644 --- a/dev/conductor/core/pubspec.yaml +++ b/dev/conductor/core/pubspec.yaml @@ -4,7 +4,7 @@ description: Flutter Automated Release Tool publish_to: none environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: archive: 3.3.2 diff --git a/dev/customer_testing/pubspec.yaml b/dev/customer_testing/pubspec.yaml index 761d57f8648d5..fa0c77905e886 100644 --- a/dev/customer_testing/pubspec.yaml +++ b/dev/customer_testing/pubspec.yaml @@ -2,7 +2,7 @@ name: customer_testing description: Tool to run the tests listed in the flutter/tests repository. environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: args: 2.3.1 diff --git a/dev/devicelab/bin/tasks/gradle_desugar_classes_test.dart b/dev/devicelab/bin/tasks/gradle_desugar_classes_test.dart index 4fe64d52d081d..ec6cec85adcf1 100644 --- a/dev/devicelab/bin/tasks/gradle_desugar_classes_test.dart +++ b/dev/devicelab/bin/tasks/gradle_desugar_classes_test.dart @@ -15,7 +15,8 @@ Future main() async { try { await runProjectTest((FlutterProject flutterProject) async { section('APK contains plugin classes'); - flutterProject.addPlugin('google_maps_flutter', value: '^1.0.10'); + await flutterProject.setMinSdkVersion(20); + flutterProject.addPlugin('google_maps_flutter', value: '^2.2.1'); await inDirectory(flutterProject.rootPath, () async { await flutter('build', options: [ diff --git a/dev/devicelab/bin/tasks/module_test_ios.dart b/dev/devicelab/bin/tasks/module_test_ios.dart index f2322c3c0a724..86d47afe35763 100644 --- a/dev/devicelab/bin/tasks/module_test_ios.dart +++ b/dev/devicelab/bin/tasks/module_test_ios.dart @@ -170,7 +170,7 @@ Future main() async { ''' dependencies: url_launcher: 6.0.20 - android_alarm_manager: 0.4.5+11 + android_alarm_manager: 2.0.2 google_sign_in_ios: 5.5.0 $dartPluginName: path: ../$dartPluginName diff --git a/dev/devicelab/bin/tasks/plugin_dependencies_test.dart b/dev/devicelab/bin/tasks/plugin_dependencies_test.dart index 26d8dada3afaf..fa2bd9e90dc2d 100644 --- a/dev/devicelab/bin/tasks/plugin_dependencies_test.dart +++ b/dev/devicelab/bin/tasks/plugin_dependencies_test.dart @@ -101,7 +101,7 @@ dependencies: sdk: flutter environment: - sdk: ">=2.0.0-dev.28.0 <3.0.0" + sdk: ">=2.0.0-dev.28.0 <4.0.0" flutter: ">=1.5.0" ''', flush: true); diff --git a/dev/devicelab/lib/framework/apk_utils.dart b/dev/devicelab/lib/framework/apk_utils.dart index e9a989cefd2d5..e79bd6b5e1d13 100644 --- a/dev/devicelab/lib/framework/apk_utils.dart +++ b/dev/devicelab/lib/framework/apk_utils.dart @@ -287,6 +287,20 @@ android { pubspec.writeAsStringSync(content, flush: true); } + Future setMinSdkVersion(int sdkVersion) async { + final File buildScript = File( + path.join(androidPath, 'app', 'build.gradle'), + ); + + buildScript.openWrite(mode: FileMode.append).write(''' +android { + defaultConfig { + minSdkVersion $sdkVersion + } +} + '''); + } + Future getPackages() async { await inDirectory(Directory(rootPath), () async { await flutter('pub', options: ['get']); diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml index c988e9a6a71d1..a6dc98aaeb3b0 100644 --- a/dev/devicelab/pubspec.yaml +++ b/dev/devicelab/pubspec.yaml @@ -3,7 +3,7 @@ description: Flutter continuous integration performance and correctness tests. homepage: https://github.com/flutter/flutter environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: archive: 3.3.2 diff --git a/dev/docs/platform_integration/pubspec.yaml b/dev/docs/platform_integration/pubspec.yaml index e56e23d2b0574..bb60d621c47a3 100644 --- a/dev/docs/platform_integration/pubspec.yaml +++ b/dev/docs/platform_integration/pubspec.yaml @@ -1,4 +1,4 @@ name: platform_integration environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" diff --git a/dev/forbidden_from_release_tests/pubspec.yaml b/dev/forbidden_from_release_tests/pubspec.yaml index f1ca8df588612..cdee1c9ba6463 100644 --- a/dev/forbidden_from_release_tests/pubspec.yaml +++ b/dev/forbidden_from_release_tests/pubspec.yaml @@ -2,7 +2,7 @@ name: forbidden_from_release_tests publish_to: 'none' environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: args: 2.3.1 diff --git a/dev/integration_tests/abstract_method_smoke_test/pubspec.yaml b/dev/integration_tests/abstract_method_smoke_test/pubspec.yaml index 574e7d7087fa6..ad67cfa39caf1 100644 --- a/dev/integration_tests/abstract_method_smoke_test/pubspec.yaml +++ b/dev/integration_tests/abstract_method_smoke_test/pubspec.yaml @@ -4,7 +4,7 @@ description: A new Flutter project. version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml b/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml index a9ed6c6f180e5..bf241be79f0e6 100644 --- a/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml +++ b/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml @@ -14,7 +14,7 @@ description: A new Flutter project. version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/android_semantics_testing/pubspec.yaml b/dev/integration_tests/android_semantics_testing/pubspec.yaml index e09fa1c4196f4..bacd717bab8b6 100644 --- a/dev/integration_tests/android_semantics_testing/pubspec.yaml +++ b/dev/integration_tests/android_semantics_testing/pubspec.yaml @@ -1,7 +1,7 @@ name: android_semantics_testing description: Integration testing library for Android semantics environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: flutter: diff --git a/dev/integration_tests/android_views/pubspec.yaml b/dev/integration_tests/android_views/pubspec.yaml index 6d38686f89ffb..d02be80efb9a6 100644 --- a/dev/integration_tests/android_views/pubspec.yaml +++ b/dev/integration_tests/android_views/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none description: An integration test for embedded platform views version: 1.0.0+1 environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: flutter: diff --git a/dev/integration_tests/channels/pubspec.yaml b/dev/integration_tests/channels/pubspec.yaml index fd430d273cfe6..a4f5fbbcd5364 100644 --- a/dev/integration_tests/channels/pubspec.yaml +++ b/dev/integration_tests/channels/pubspec.yaml @@ -2,7 +2,7 @@ name: channels description: Integration test for platform channels. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/deferred_components_test/pubspec.yaml b/dev/integration_tests/deferred_components_test/pubspec.yaml index 06dc851c3d79c..55e62304ae957 100644 --- a/dev/integration_tests/deferred_components_test/pubspec.yaml +++ b/dev/integration_tests/deferred_components_test/pubspec.yaml @@ -3,7 +3,7 @@ description: Integration test application for basic deferred components function publish_to: 'none' version: 1.0.0+1 environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: flutter: diff --git a/dev/integration_tests/external_ui/pubspec.yaml b/dev/integration_tests/external_ui/pubspec.yaml index ca9407b7f4ff4..36f3db98a8788 100644 --- a/dev/integration_tests/external_ui/pubspec.yaml +++ b/dev/integration_tests/external_ui/pubspec.yaml @@ -2,7 +2,7 @@ name: external_ui description: A test of Flutter integrating external UIs. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml index a87e62550f9c6..94293f889a836 100644 --- a/dev/integration_tests/flavors/pubspec.yaml +++ b/dev/integration_tests/flavors/pubspec.yaml @@ -2,7 +2,7 @@ name: flavors description: Integration test for build flavors. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/flutter_gallery/pubspec.yaml b/dev/integration_tests/flutter_gallery/pubspec.yaml index 9440b69a84ca1..cfc5f5a7dfdfc 100644 --- a/dev/integration_tests/flutter_gallery/pubspec.yaml +++ b/dev/integration_tests/flutter_gallery/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_gallery environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: @@ -20,7 +20,7 @@ dependencies: git: url: https://github.com/kevmoo/scoped_model.git ref: null_safety - shrine_images: 1.1.2 + shrine_images: 2.0.1 # Also update dev/benchmarks/complex_layout/pubspec.yaml # and dev/benchmarks/macrobenchmarks/pubspec.yaml diff --git a/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml b/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml index cd92473dc0e87..527084dd4348f 100644 --- a/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml +++ b/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml @@ -2,7 +2,7 @@ name: gradle_deprecated_settings description: Integration test for the current settings.gradle. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/hybrid_android_views/pubspec.yaml b/dev/integration_tests/hybrid_android_views/pubspec.yaml index 5fe5c7dd323e2..1f7dab5296908 100644 --- a/dev/integration_tests/hybrid_android_views/pubspec.yaml +++ b/dev/integration_tests/hybrid_android_views/pubspec.yaml @@ -4,7 +4,7 @@ publish_to: none description: An integration test for hybrid composition on Android version: 1.0.0+1 environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: flutter: diff --git a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml index 6ac8a9405c661..82dd676480ad6 100644 --- a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml +++ b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml @@ -14,7 +14,7 @@ description: A new flutter module project. version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml index 1f8d016396c44..b9912f64ead0d 100644 --- a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml +++ b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml @@ -13,7 +13,7 @@ name: ios_app_with_extensions version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml index f535c7ee5c738..1e2af467372ad 100644 --- a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml +++ b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml @@ -3,7 +3,7 @@ name: ios_platform_view_tests version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/non_nullable/pubspec.yaml b/dev/integration_tests/non_nullable/pubspec.yaml index 595ce00d5c019..daea3229aca89 100644 --- a/dev/integration_tests/non_nullable/pubspec.yaml +++ b/dev/integration_tests/non_nullable/pubspec.yaml @@ -5,7 +5,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/platform_interaction/pubspec.yaml b/dev/integration_tests/platform_interaction/pubspec.yaml index f700ef0a9dacb..b6ce71adad2bf 100644 --- a/dev/integration_tests/platform_interaction/pubspec.yaml +++ b/dev/integration_tests/platform_interaction/pubspec.yaml @@ -2,7 +2,7 @@ name: platform_interaction description: Integration test for platform interactions. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/release_smoke_test/pubspec.yaml b/dev/integration_tests/release_smoke_test/pubspec.yaml index efb3269e72fe1..37b2a4feb5efd 100644 --- a/dev/integration_tests/release_smoke_test/pubspec.yaml +++ b/dev/integration_tests/release_smoke_test/pubspec.yaml @@ -1,7 +1,7 @@ name: release_smoke_test environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/spell_check/pubspec.yaml b/dev/integration_tests/spell_check/pubspec.yaml index 228c9fee62805..816bbcdf08207 100644 --- a/dev/integration_tests/spell_check/pubspec.yaml +++ b/dev/integration_tests/spell_check/pubspec.yaml @@ -18,7 +18,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: '>=2.18.0-149.0.dev <3.0.0' + sdk: '>=2.18.0-149.0.dev <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml index 4a0cc9b104142..94d7a13eecabe 100644 --- a/dev/integration_tests/ui/pubspec.yaml +++ b/dev/integration_tests/ui/pubspec.yaml @@ -2,7 +2,7 @@ name: integration_ui description: Flutter non-plugin UI integration tests. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/integration_tests/web/pubspec.yaml b/dev/integration_tests/web/pubspec.yaml index 891688a221c56..17171c06c404b 100644 --- a/dev/integration_tests/web/pubspec.yaml +++ b/dev/integration_tests/web/pubspec.yaml @@ -2,7 +2,7 @@ name: web_integration description: Integration test for web compilation. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" flutter: assets: diff --git a/dev/integration_tests/web_compile_tests/pubspec.yaml b/dev/integration_tests/web_compile_tests/pubspec.yaml index e3af760f7a3f7..764ef90b33261 100644 --- a/dev/integration_tests/web_compile_tests/pubspec.yaml +++ b/dev/integration_tests/web_compile_tests/pubspec.yaml @@ -1,6 +1,6 @@ name: web_compile_tests environment: - sdk: '>=2.17.0-0 <3.0.0' + sdk: '>=2.17.0-0 <4.0.0' dependencies: flutter: diff --git a/dev/integration_tests/web_e2e_tests/pubspec.yaml b/dev/integration_tests/web_e2e_tests/pubspec.yaml index d68faff9bfd88..16c32a05f5dcd 100644 --- a/dev/integration_tests/web_e2e_tests/pubspec.yaml +++ b/dev/integration_tests/web_e2e_tests/pubspec.yaml @@ -2,7 +2,7 @@ name: web_e2e_tests publish_to: none environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" flutter: assets: diff --git a/dev/integration_tests/windows_startup_test/pubspec.yaml b/dev/integration_tests/windows_startup_test/pubspec.yaml index 9615ec6be0e00..c364506ec7f2c 100644 --- a/dev/integration_tests/windows_startup_test/pubspec.yaml +++ b/dev/integration_tests/windows_startup_test/pubspec.yaml @@ -2,7 +2,7 @@ name: windows_startup_test description: Integration test for Windows app's startup. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml index 403c342c5a515..fd31fe3bf797f 100644 --- a/dev/manual_tests/pubspec.yaml +++ b/dev/manual_tests/pubspec.yaml @@ -1,7 +1,7 @@ name: manual_tests environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/missing_dependency_tests/pubspec.yaml b/dev/missing_dependency_tests/pubspec.yaml index bd8d667b1ea54..ebed8816e9816 100644 --- a/dev/missing_dependency_tests/pubspec.yaml +++ b/dev/missing_dependency_tests/pubspec.yaml @@ -1,7 +1,7 @@ name: missing_dependency_tests environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/tools/dartdoc.dart b/dev/tools/dartdoc.dart index 7c9ffaf009d1b..f577cadd9465c 100644 --- a/dev/tools/dartdoc.dart +++ b/dev/tools/dartdoc.dart @@ -59,7 +59,7 @@ Future main(List arguments) async { buf.writeln('homepage: https://flutter.dev'); buf.writeln('version: 0.0.0'); buf.writeln('environment:'); - buf.writeln(" sdk: '>=2.10.0 <3.0.0'"); + buf.writeln(" sdk: '>=2.10.0 <4.0.0'"); buf.writeln('dependencies:'); for (final String package in findPackageNames()) { buf.writeln(' $package:'); diff --git a/dev/tools/gen_defaults/pubspec.yaml b/dev/tools/gen_defaults/pubspec.yaml index 2f42ef652a031..ed298d766c6e6 100644 --- a/dev/tools/gen_defaults/pubspec.yaml +++ b/dev/tools/gen_defaults/pubspec.yaml @@ -3,7 +3,7 @@ description: A command line script to generate Material component defaults from version: 1.0.0 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: diff --git a/dev/tools/gen_keycodes/pubspec.yaml b/dev/tools/gen_keycodes/pubspec.yaml index 985e4195d1b3a..08c98045681b5 100644 --- a/dev/tools/gen_keycodes/pubspec.yaml +++ b/dev/tools/gen_keycodes/pubspec.yaml @@ -2,7 +2,7 @@ name: gen_keycodes description: Generates keycode source files from various resources. environment: - sdk: ">=2.18.0-0 <3.0.0" + sdk: ">=2.18.0-0 <4.0.0" dependencies: args: 2.3.1 diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml index 07931974c71d2..7fb6252daaa9a 100644 --- a/dev/tools/pubspec.yaml +++ b/dev/tools/pubspec.yaml @@ -2,7 +2,7 @@ name: dev_tools description: Various repository development tools for flutter. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: archive: 3.3.2 diff --git a/dev/tools/vitool/pubspec.yaml b/dev/tools/vitool/pubspec.yaml index 2cb22f6082f11..d2e8ee37fc2ba 100644 --- a/dev/tools/vitool/pubspec.yaml +++ b/dev/tools/vitool/pubspec.yaml @@ -4,7 +4,7 @@ version: 0.0.1 homepage: https://flutter.dev environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/dev/tracing_tests/pubspec.yaml b/dev/tracing_tests/pubspec.yaml index ba218b47eae36..e8937ed969142 100644 --- a/dev/tracing_tests/pubspec.yaml +++ b/dev/tracing_tests/pubspec.yaml @@ -2,7 +2,7 @@ name: tracing_tests description: Various tests for tracing in flutter/flutter environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/api/pubspec.yaml b/examples/api/pubspec.yaml index 9646c0c927820..40695ebde9547 100644 --- a/examples/api/pubspec.yaml +++ b/examples/api/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' version: 1.0.0 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" flutter: ">=2.5.0-6.0.pre.30 <3.0.0" dependencies: diff --git a/examples/flutter_view/pubspec.yaml b/examples/flutter_view/pubspec.yaml index 4be70dd1bde88..03b0f21e4299b 100644 --- a/examples/flutter_view/pubspec.yaml +++ b/examples/flutter_view/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_view description: A new flutter project. environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml index 9b92c2858a1db..1fd49c7803b9e 100644 --- a/examples/hello_world/pubspec.yaml +++ b/examples/hello_world/pubspec.yaml @@ -1,7 +1,7 @@ name: hello_world environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/image_list/pubspec.yaml b/examples/image_list/pubspec.yaml index a918140076164..12a7b7ddf577e 100644 --- a/examples/image_list/pubspec.yaml +++ b/examples/image_list/pubspec.yaml @@ -4,7 +4,7 @@ description: Simple Flutter project used for benchmarking image loading over net version: 1.0.0+1 environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/layers/pubspec.yaml b/examples/layers/pubspec.yaml index b56c8e79734f9..7a32fc09fa073 100644 --- a/examples/layers/pubspec.yaml +++ b/examples/layers/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_examples_layers environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/platform_channel/pubspec.yaml b/examples/platform_channel/pubspec.yaml index 13036da9fd6d3..166c45ad02973 100644 --- a/examples/platform_channel/pubspec.yaml +++ b/examples/platform_channel/pubspec.yaml @@ -1,7 +1,7 @@ name: platform_channel environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/platform_channel_swift/pubspec.yaml b/examples/platform_channel_swift/pubspec.yaml index e2f38d0c0da83..c6b168a170e62 100644 --- a/examples/platform_channel_swift/pubspec.yaml +++ b/examples/platform_channel_swift/pubspec.yaml @@ -1,7 +1,7 @@ name: platform_channel_swift environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/platform_view/pubspec.yaml b/examples/platform_view/pubspec.yaml index 025e7316214f3..62aad0c1078a7 100644 --- a/examples/platform_view/pubspec.yaml +++ b/examples/platform_view/pubspec.yaml @@ -1,7 +1,7 @@ name: platform_view environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/examples/splash/pubspec.yaml b/examples/splash/pubspec.yaml index 7e1bc369c8ac9..8a5ccb9ecf24a 100644 --- a/examples/splash/pubspec.yaml +++ b/examples/splash/pubspec.yaml @@ -1,7 +1,7 @@ name: splash environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index ede82b6dac822..559f90ebc0460 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -3,7 +3,7 @@ description: A framework for writing Flutter applications homepage: https://flutter.dev environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter/test_private/pubspec.yaml b/packages/flutter/test_private/pubspec.yaml index 30fabe9a80ef0..ebfc1efaffae2 100644 --- a/packages/flutter/test_private/pubspec.yaml +++ b/packages/flutter/test_private/pubspec.yaml @@ -2,7 +2,7 @@ name: flutter_test_private description: Tests private interfaces of the flutter environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter/test_private/test/pubspec.yaml b/packages/flutter/test_private/test/pubspec.yaml index 37e60ba555e7d..0a62c7c130f4a 100644 --- a/packages/flutter/test_private/test/pubspec.yaml +++ b/packages/flutter/test_private/test/pubspec.yaml @@ -1,7 +1,7 @@ name: animated_icons_private_test environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml index dff59deeb748d..7b7295a8be142 100644 --- a/packages/flutter_driver/pubspec.yaml +++ b/packages/flutter_driver/pubspec.yaml @@ -3,7 +3,7 @@ description: Integration and performance test API for Flutter applications homepage: https://flutter.dev environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: file: 6.1.4 diff --git a/packages/flutter_goldens/pubspec.yaml b/packages/flutter_goldens/pubspec.yaml index d69dcd5fd5090..26b5489159d93 100644 --- a/packages/flutter_goldens/pubspec.yaml +++ b/packages/flutter_goldens/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_goldens environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_goldens_client/pubspec.yaml b/packages/flutter_goldens_client/pubspec.yaml index d9efaed2c5dbb..9cfec36f06ce5 100644 --- a/packages/flutter_goldens_client/pubspec.yaml +++ b/packages/flutter_goldens_client/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_goldens_client environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_localizations/pubspec.yaml b/packages/flutter_localizations/pubspec.yaml index 3cfda8d2a93e8..79da1e0d363b5 100644 --- a/packages/flutter_localizations/pubspec.yaml +++ b/packages/flutter_localizations/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_localizations environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml index fe6b7dcfb864c..71b0e31f37846 100644 --- a/packages/flutter_test/pubspec.yaml +++ b/packages/flutter_test/pubspec.yaml @@ -1,7 +1,7 @@ name: flutter_test environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_test/test/test_config/project_root/pubspec.yaml b/packages/flutter_test/test/test_config/project_root/pubspec.yaml index 8a39bec6b8f5c..30c83394c6c86 100644 --- a/packages/flutter_test/test/test_config/project_root/pubspec.yaml +++ b/packages/flutter_test/test/test_config/project_root/pubspec.yaml @@ -4,6 +4,6 @@ name: dummy environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' # PUBSPEC CHECKSUM: 0000 diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index ffaf2264572a6..aa83b5a8564bb 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -1427,7 +1427,7 @@ String generateFakePubspec( final bool verbose = doUpgrade; result.writeln('name: flutter_update_packages'); result.writeln('environment:'); - result.writeln(" sdk: '>=2.10.0 <3.0.0'"); + result.writeln(" sdk: '>=2.10.0 <4.0.0'"); result.writeln('dependencies:'); overrides.writeln('dependency_overrides:'); if (kManuallyPinnedDependencies.isNotEmpty) { @@ -1640,7 +1640,7 @@ Directory createTemporaryFlutterSdk( // Fill in SDK dependency constraint. output.write(''' environment: - sdk: ">=2.7.0 <3.0.0" + sdk: ">=2.7.0 <4.0.0" '''); output.writeln('dependencies:'); @@ -1672,7 +1672,7 @@ description: Dart SDK extensions for dart:ui homepage: http://flutter.io # sky_engine requires sdk_ext support in the analyzer which was added in 1.11.x environment: - sdk: '>=1.11.0 <3.0.0' + sdk: '>=1.11.0 <4.0.0' '''); return directory; diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index 91dafbe172c1a..acacff48c6860 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -3,7 +3,7 @@ description: Tools for building Flutter applications homepage: https://flutter.dev environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart index 3039279c1f866..fabc5afe6077e 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/analyze_continuously_test.dart @@ -60,7 +60,7 @@ void main() { pubspecFile.writeAsStringSync(''' name: foo_project environment: - sdk: '>=2.10.0 <3.0.0' + sdk: '>=2.10.0 <4.0.0' '''); final File dartFile = fileSystem.file(fileSystem.path.join(directory.path, 'lib', 'main.dart')); diff --git a/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart b/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart index 2f205b53e98b0..ced2dd0c45cf4 100644 --- a/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/hermetic/update_packages_test.dart @@ -24,7 +24,7 @@ description: A framework for writing Flutter applications homepage: http://flutter.dev environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.2.2 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". @@ -61,7 +61,7 @@ homepage: http://flutter.dev version: 1.0.0 environment: - sdk: ">=2.14.0-383.0.dev <3.0.0" + sdk: ">=2.14.0-383.0.dev <4.0.0" flutter: ">=2.5.0-6.0.pre.30 <3.0.0" dependencies: diff --git a/packages/flutter_tools/test/data/asset_test/font/pubspec.yaml b/packages/flutter_tools/test/data/asset_test/font/pubspec.yaml index 10e62a856414b..654dc75902819 100644 --- a/packages/flutter_tools/test/data/asset_test/font/pubspec.yaml +++ b/packages/flutter_tools/test/data/asset_test/font/pubspec.yaml @@ -2,7 +2,7 @@ name: font description: A test project that contains a font. environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" flutter: uses-material-design: true diff --git a/packages/flutter_tools/test/data/asset_test/main/pubspec.yaml b/packages/flutter_tools/test/data/asset_test/main/pubspec.yaml index 5a898f9ec0107..2e1e9152ca7a9 100644 --- a/packages/flutter_tools/test/data/asset_test/main/pubspec.yaml +++ b/packages/flutter_tools/test/data/asset_test/main/pubspec.yaml @@ -2,7 +2,7 @@ name: main description: A test project that has a package with a font as a dependency. environment: - sdk: ">=2.12.0 <3.0.0" + sdk: ">=2.12.0 <4.0.0" dependencies: font: diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/dart_plugin_registrant_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/dart_plugin_registrant_test.dart index 9bce7978277cb..67d79aa9987b9 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/dart_plugin_registrant_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/dart_plugin_registrant_test.dart @@ -86,7 +86,7 @@ flutter: pluginClass: none environment: - sdk: ">=2.12.0-259.9.beta <3.0.0" + sdk: ">=2.12.0-259.9.beta <4.0.0" flutter: ">=1.20.0" '''; diff --git a/packages/flutter_tools/test/general.shard/test/test_compiler_test.dart b/packages/flutter_tools/test/general.shard/test/test_compiler_test.dart index bd2bb06d079a8..9d2da36686874 100644 --- a/packages/flutter_tools/test/general.shard/test/test_compiler_test.dart +++ b/packages/flutter_tools/test/general.shard/test/test_compiler_test.dart @@ -166,7 +166,7 @@ flutter: linux: dartPluginClass: APlugin environment: - sdk: ">=2.14.0 <3.0.0" + sdk: ">=2.14.0 <4.0.0" flutter: ">=2.5.0" '''); diff --git a/packages/flutter_tools/test/general.shard/update_packages_test.dart b/packages/flutter_tools/test/general.shard/update_packages_test.dart index 32f7445543501..9a57293a9b9f2 100644 --- a/packages/flutter_tools/test/general.shard/update_packages_test.dart +++ b/packages/flutter_tools/test/general.shard/update_packages_test.dart @@ -17,7 +17,7 @@ description: A framework for writing Flutter applications homepage: http://flutter.dev environment: - sdk: '>=2.2.2 <3.0.0' + sdk: '>=2.2.2 <4.0.0' dependencies: # To update these, use "flutter update-packages --force-upgrade". @@ -51,7 +51,7 @@ description: A dummy pubspec with no dependencies homepage: http://flutter.dev environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.2.2 <4.0.0" '''; const String kInvalidGitPubspec = ''' @@ -60,7 +60,7 @@ description: A framework for writing Flutter applications homepage: http://flutter.dev environment: - sdk: ">=2.2.2 <3.0.0" + sdk: ">=2.2.2 <4.0.0" dependencies: # To update these, use "flutter update-packages --force-upgrade". diff --git a/packages/flutter_tools/test/integration.shard/analyze_once_test.dart b/packages/flutter_tools/test/integration.shard/analyze_once_test.dart index 5b3b3488a9262..72f13e6890e08 100644 --- a/packages/flutter_tools/test/integration.shard/analyze_once_test.dart +++ b/packages/flutter_tools/test/integration.shard/analyze_once_test.dart @@ -465,7 +465,7 @@ class _MyHomePageState extends State { const String pubspecYamlSrc = r''' name: flutter_project environment: - sdk: ">=2.1.0 <3.0.0" + sdk: ">=2.1.0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/break_on_framework_exceptions_test.dart b/packages/flutter_tools/test/integration.shard/break_on_framework_exceptions_test.dart index 960361dec9e30..57762ba6a6a1b 100644 --- a/packages/flutter_tools/test/integration.shard/break_on_framework_exceptions_test.dart +++ b/packages/flutter_tools/test/integration.shard/break_on_framework_exceptions_test.dart @@ -731,7 +731,7 @@ class TestProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart b/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart index 10326feed5b71..b4f6705c5c83d 100644 --- a/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart +++ b/packages/flutter_tools/test/integration.shard/flutter_build_null_unsafe_test.dart @@ -45,7 +45,7 @@ void main() { writeFile(fileSystem.path.join(projectRoot.path, 'pubspec.yaml'), ''' name: hello environment: - sdk: '>=2.12.0 <3.0.0' + sdk: '>=2.12.0 <4.0.0' '''); writeFile(fileSystem.path.join(projectRoot.path, 'lib', 'main.dart'), ''' import 'unsafe.dart'; diff --git a/packages/flutter_tools/test/integration.shard/test_data/background_project.dart b/packages/flutter_tools/test/integration.shard/test_data/background_project.dart index 270f3a454167f..0fccff366c262 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/background_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/background_project.dart @@ -12,7 +12,7 @@ class BackgroundProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: @@ -63,7 +63,7 @@ class RepeatingBackgroundProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/basic_project.dart b/packages/flutter_tools/test/integration.shard/test_data/basic_project.dart index 12a77fed9c866..3f3c031cddc6a 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/basic_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/basic_project.dart @@ -10,7 +10,7 @@ class BasicProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: @@ -63,7 +63,7 @@ class BasicProjectThatThrows extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: @@ -120,7 +120,7 @@ class BasicProjectWithTimelineTraces extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: @@ -169,7 +169,7 @@ class BasicProjectWithFlutterGen extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: @@ -181,8 +181,6 @@ class BasicProjectWithFlutterGen extends Project { @override final String main = r''' - // @dart = 2.8 - // generated package does not support null safety. import 'dart:async'; import 'package:flutter_gen/flutter_gen.dart'; @@ -196,7 +194,7 @@ class BasicProjectWithUnaryMain extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: sdk: flutter diff --git a/packages/flutter_tools/test/integration.shard/test_data/compile_error_project.dart b/packages/flutter_tools/test/integration.shard/test_data/compile_error_project.dart index 2732b38ba979e..1ba2f5b62804d 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/compile_error_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/compile_error_project.dart @@ -10,7 +10,7 @@ class CompileErrorProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/deferred_components_project.dart b/packages/flutter_tools/test/integration.shard/test_data/deferred_components_project.dart index b5ea7c6ea3e61..b135962465fbb 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/deferred_components_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/deferred_components_project.dart @@ -13,7 +13,7 @@ class DeferredComponentsProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart b/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart index 0723fe5b7235c..13639aa3efb56 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart @@ -34,7 +34,7 @@ class GenL10nProject extends Project { final String pubspec = ''' name: test_l10n_project environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/hot_reload_const_project.dart b/packages/flutter_tools/test/integration.shard/test_data/hot_reload_const_project.dart index 0a2a1456ac97e..d6d9c086a8612 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/hot_reload_const_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/hot_reload_const_project.dart @@ -10,7 +10,7 @@ class HotReloadConstProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/hot_reload_project.dart b/packages/flutter_tools/test/integration.shard/test_data/hot_reload_project.dart index 4ddb5be40ada0..c61082b8a1ec5 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/hot_reload_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/hot_reload_project.dart @@ -12,7 +12,7 @@ class HotReloadProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/hot_reload_with_asset.dart b/packages/flutter_tools/test/integration.shard/test_data/hot_reload_with_asset.dart index 6cd7dc7dca018..ae26ac4e68bb8 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/hot_reload_with_asset.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/hot_reload_with_asset.dart @@ -10,7 +10,7 @@ class HotReloadWithAssetProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/integration_tests_project.dart b/packages/flutter_tools/test/integration.shard/test_data/integration_tests_project.dart index 8d24a8523c10b..1a5c0ad67af9f 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/integration_tests_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/integration_tests_project.dart @@ -14,7 +14,7 @@ class IntegrationTestsProject extends Project implements TestsProject { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/migrate_project.dart b/packages/flutter_tools/test/integration.shard/test_data/migrate_project.dart index e14d8b15978d8..7b1b18725286b 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/migrate_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/migrate_project.dart @@ -176,7 +176,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 1.0.0+1 environment: - sdk: ">=2.6.0 <3.0.0" + sdk: ">=2.6.0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/multidex_project.dart b/packages/flutter_tools/test/integration.shard/test_data/multidex_project.dart index 2b0fc25b2893d..26964c4f444a3 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/multidex_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/multidex_project.dart @@ -56,7 +56,7 @@ class MultidexProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/project_with_early_error.dart b/packages/flutter_tools/test/integration.shard/test_data/project_with_early_error.dart index 7578d6559c707..9c90f4981acea 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/project_with_early_error.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/project_with_early_error.dart @@ -10,7 +10,7 @@ class ProjectWithEarlyError extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/single_widget_reload_project.dart b/packages/flutter_tools/test/integration.shard/test_data/single_widget_reload_project.dart index 083a0c04d3004..690c71a2af916 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/single_widget_reload_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/single_widget_reload_project.dart @@ -10,7 +10,7 @@ class SingleWidgetReloadProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/stateless_stateful_project.dart b/packages/flutter_tools/test/integration.shard/test_data/stateless_stateful_project.dart index 5d89cb914e24a..e1c312ce94de6 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/stateless_stateful_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/stateless_stateful_project.dart @@ -10,7 +10,7 @@ class HotReloadProject extends Project { final String pubspec = ''' name: test environment: - sdk: ">=2.12.0-0 <3.0.0" + sdk: ">=2.12.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/stepping_project.dart b/packages/flutter_tools/test/integration.shard/test_data/stepping_project.dart index d3a2bec2ca028..ce51fe469f1dd 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/stepping_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/stepping_project.dart @@ -9,7 +9,7 @@ class SteppingProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: sdk: flutter @@ -65,7 +65,7 @@ class WebSteppingProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.10.0 <3.0.0' + sdk: '>=2.10.0 <4.0.0' dependencies: flutter: sdk: flutter diff --git a/packages/flutter_tools/test/integration.shard/test_data/test_project.dart b/packages/flutter_tools/test/integration.shard/test_data/test_project.dart index 02b3c1a9a612f..77b62a4498256 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/test_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/test_project.dart @@ -10,7 +10,7 @@ class TestProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/test_data/tests_project.dart b/packages/flutter_tools/test/integration.shard/test_data/tests_project.dart index 80984677ee8a1..308e16a38fa0f 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/tests_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/tests_project.dart @@ -13,7 +13,7 @@ class TestsProject extends Project { final String pubspec = ''' name: test environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: diff --git a/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart b/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart index 3702715601e08..9477b7d8ac4fe 100644 --- a/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart +++ b/packages/flutter_tools/test/integration.shard/web_plugin_registrant_test.dart @@ -80,19 +80,17 @@ void main() { ), }); - testUsingContext('generated plugin registrant passes analysis without null safety', () async { + testUsingContext('generated plugin registrant passes analysis with null safety', () async { await _createProject(projectDir, []); // We need a dependency so the plugin registrant is not completely empty. await _editPubspecFile(projectDir, _composeEditors([ _addDependencyEditor('shared_preferences', version: '^2.0.0'), - // This turns null safety off - _setDartSDKVersionEditor('>=2.11.0 <3.0.0'), + _setDartSDKVersionEditor('>=2.12.0 <4.0.0'), ])); - // The generated main.dart file has a bunch of stuff that is invalid without null safety, so - // replace it with a no-op dummy main file. We aren't testing it in this scenario anyway. + // Replace main file with a no-op dummy. We aren't testing it in this scenario anyway. await _replaceMainFile(projectDir, 'void main() {}'); // The plugin registrant is created on build... diff --git a/packages/flutter_web_plugins/pubspec.yaml b/packages/flutter_web_plugins/pubspec.yaml index ab20c5183ce89..d6e576b9078d5 100644 --- a/packages/flutter_web_plugins/pubspec.yaml +++ b/packages/flutter_web_plugins/pubspec.yaml @@ -3,7 +3,7 @@ description: Library to register Flutter Web plugins homepage: https://flutter.dev environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/fuchsia_remote_debug_protocol/pubspec.yaml b/packages/fuchsia_remote_debug_protocol/pubspec.yaml index f99f50e4af278..1eb33b077adc3 100644 --- a/packages/fuchsia_remote_debug_protocol/pubspec.yaml +++ b/packages/fuchsia_remote_debug_protocol/pubspec.yaml @@ -4,7 +4,7 @@ description: Provides an API to test/debug Flutter applications on remote Fuchsi homepage: https://flutter.dev environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: process: 4.2.4 diff --git a/packages/integration_test/example/pubspec.yaml b/packages/integration_test/example/pubspec.yaml index 44957a65ec7bf..a4781a35c6c88 100644 --- a/packages/integration_test/example/pubspec.yaml +++ b/packages/integration_test/example/pubspec.yaml @@ -3,7 +3,7 @@ description: Demonstrates how to use the integration_test plugin. publish_to: 'none' environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' flutter: ">=1.6.7" dependencies: diff --git a/packages/integration_test/integration_test_macos/pubspec.yaml b/packages/integration_test/integration_test_macos/pubspec.yaml index 1860af7706011..ae3911318eb64 100644 --- a/packages/integration_test/integration_test_macos/pubspec.yaml +++ b/packages/integration_test/integration_test_macos/pubspec.yaml @@ -10,7 +10,7 @@ flutter: pluginClass: IntegrationTestPlugin environment: - sdk: ">=2.17.0-0 <3.0.0" + sdk: ">=2.17.0-0 <4.0.0" dependencies: flutter: diff --git a/packages/integration_test/pubspec.yaml b/packages/integration_test/pubspec.yaml index f2215ab4de660..245862d4ea403 100644 --- a/packages/integration_test/pubspec.yaml +++ b/packages/integration_test/pubspec.yaml @@ -3,7 +3,7 @@ description: Runs tests that use the flutter_test API as integration tests. publish_to: none environment: - sdk: '>=2.12.0-0 <3.0.0' + sdk: '>=2.12.0-0 <4.0.0' dependencies: flutter: From eefbe85c8bd4185a087cd83251e552be326568ad Mon Sep 17 00:00:00 2001 From: Parker Lougheed Date: Thu, 8 Dec 2022 22:32:07 -0600 Subject: [PATCH 06/71] Bump dartdoc to 6.1.4 (#116758) --- dev/bots/docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh index e379872f568e4..aee4477510c8f 100755 --- a/dev/bots/docs.sh +++ b/dev/bots/docs.sh @@ -20,7 +20,7 @@ function generate_docs() { # Install and activate dartdoc. # NOTE: When updating to a new dartdoc version, please also update # `dartdoc_options.yaml` to include newly introduced error and warning types. - "$DART" pub global activate dartdoc 6.1.3 + "$DART" pub global activate dartdoc 6.1.4 # Install and activate the snippets tool, which resides in the # assets-for-api-docs repo: From 48cfe2eb008d75858b54ad05bafaec3a948acba5 Mon Sep 17 00:00:00 2001 From: Siva Date: Fri, 9 Dec 2022 10:53:39 -0800 Subject: [PATCH 07/71] Opt dashing_postprocess.dart out of null safety until we figure out why (#116786) it complains about some library being unsound. --- dev/bots/docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh index aee4477510c8f..b68a0636900bb 100755 --- a/dev/bots/docs.sh +++ b/dev/bots/docs.sh @@ -59,7 +59,7 @@ function create_docset() { dashing_pid=$! wait $dashing_pid && \ cp ./doc/flutter/static-assets/favicon.png ./flutter.docset/icon.png && \ - "$DART" --disable-dart-dev --enable-asserts ./dashing_postprocess.dart && \ + "$DART" --no-sound-null-safety --disable-dart-dev --enable-asserts ./dashing_postprocess.dart && \ tar cf flutter.docset.tar.gz --use-compress-program="gzip --best" flutter.docset if [[ $? -ne 0 ]]; then >&2 echo "Dashing docset generation failed" From b4304dadc58264f7cf1d7555040f96920021ff18 Mon Sep 17 00:00:00 2001 From: Jason Simmons Date: Fri, 9 Dec 2022 11:38:20 -0800 Subject: [PATCH 08/71] Update the Dart language version in the pubspec generated by the dartdoc script (#116789) --- dev/bots/docs.sh | 2 +- dev/tools/dartdoc.dart | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh index b68a0636900bb..aee4477510c8f 100755 --- a/dev/bots/docs.sh +++ b/dev/bots/docs.sh @@ -59,7 +59,7 @@ function create_docset() { dashing_pid=$! wait $dashing_pid && \ cp ./doc/flutter/static-assets/favicon.png ./flutter.docset/icon.png && \ - "$DART" --no-sound-null-safety --disable-dart-dev --enable-asserts ./dashing_postprocess.dart && \ + "$DART" --disable-dart-dev --enable-asserts ./dashing_postprocess.dart && \ tar cf flutter.docset.tar.gz --use-compress-program="gzip --best" flutter.docset if [[ $? -ne 0 ]]; then >&2 echo "Dashing docset generation failed" diff --git a/dev/tools/dartdoc.dart b/dev/tools/dartdoc.dart index f577cadd9465c..951210c43d6d2 100644 --- a/dev/tools/dartdoc.dart +++ b/dev/tools/dartdoc.dart @@ -59,7 +59,7 @@ Future main(List arguments) async { buf.writeln('homepage: https://flutter.dev'); buf.writeln('version: 0.0.0'); buf.writeln('environment:'); - buf.writeln(" sdk: '>=2.10.0 <4.0.0'"); + buf.writeln(" sdk: '>=2.12.0 <4.0.0'"); buf.writeln('dependencies:'); for (final String package in findPackageNames()) { buf.writeln(' $package:'); From 55e7501154b75b5507d374b6f8e1aab459b85916 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 14:57:33 -0500 Subject: [PATCH 09/71] Roll Plugins from 51434ec83dde to 6ab7d710d2fb (3 revisions) (#116781) * 929c9a632 [camera] Re-enable ability to concurrently record and stream video (flutter/plugins#6808) * f31a438f3 [video_player] Fix file URI construction (flutter/plugins#6803) * 6ab7d710d Roll Flutter from a570fd25d83b to 521028c80827 (11 revisions) (flutter/plugins#6810) --- bin/internal/flutter_plugins.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/flutter_plugins.version b/bin/internal/flutter_plugins.version index 31e0725a223fb..1666f68e1e73a 100644 --- a/bin/internal/flutter_plugins.version +++ b/bin/internal/flutter_plugins.version @@ -1 +1 @@ -51434ec83ddea23de0ccca359dd08e4586c5e2fe +6ab7d710d2fbd95de7746fadd61fc87a2907ed03 From e57b7f4ea8ab8b348810d0a76f7bcf4aeabbe6d2 Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Fri, 9 Dec 2022 22:05:12 +0200 Subject: [PATCH 10/71] Add Material 3 support for `ListTile` - Part 1 (#116194) * Add Material 3 support for `ListTile` - Part 1 * minor refactor * Add `useMaterial3: false` to M2 tests --- dev/tools/gen_defaults/bin/gen_defaults.dart | 2 + .../gen_defaults/lib/list_tile_template.dart | 37 ++ .../flutter/lib/src/material/list_tile.dart | 308 ++++++++++----- .../lib/src/material/list_tile_theme.dart | 30 ++ .../flutter/test/material/list_tile_test.dart | 360 +++++++++++------- .../test/material/list_tile_theme_test.dart | 240 +++++++++++- 6 files changed, 747 insertions(+), 230 deletions(-) create mode 100644 dev/tools/gen_defaults/lib/list_tile_template.dart diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart index f03fa509d62c9..d2cf5b5fbcf86 100644 --- a/dev/tools/gen_defaults/bin/gen_defaults.dart +++ b/dev/tools/gen_defaults/bin/gen_defaults.dart @@ -35,6 +35,7 @@ import 'package:gen_defaults/filter_chip_template.dart'; import 'package:gen_defaults/icon_button_template.dart'; import 'package:gen_defaults/input_chip_template.dart'; import 'package:gen_defaults/input_decorator_template.dart'; +import 'package:gen_defaults/list_tile_template.dart'; import 'package:gen_defaults/menu_template.dart'; import 'package:gen_defaults/navigation_bar_template.dart'; import 'package:gen_defaults/navigation_drawer_template.dart'; @@ -154,6 +155,7 @@ Future main(List args) async { FilterChipTemplate('FilterChip', '$materialLib/filter_chip.dart', tokens).updateFile(); IconButtonTemplate('IconButton', '$materialLib/icon_button.dart', tokens).updateFile(); InputChipTemplate('InputChip', '$materialLib/input_chip.dart', tokens).updateFile(); + ListTileTemplate('LisTile', '$materialLib/list_tile.dart', tokens).updateFile(); InputDecoratorTemplate('InputDecorator', '$materialLib/input_decorator.dart', tokens).updateFile(); MenuTemplate('Menu', '$materialLib/menu_anchor.dart', tokens).updateFile(); NavigationBarTemplate('NavigationBar', '$materialLib/navigation_bar.dart', tokens).updateFile(); diff --git a/dev/tools/gen_defaults/lib/list_tile_template.dart b/dev/tools/gen_defaults/lib/list_tile_template.dart new file mode 100644 index 0000000000000..d2dc15e241d62 --- /dev/null +++ b/dev/tools/gen_defaults/lib/list_tile_template.dart @@ -0,0 +1,37 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'template.dart'; + +class ListTileTemplate extends TokenTemplate { + const ListTileTemplate(super.blockName, super.fileName, super.tokens); + + @override + String generate() => ''' +class _${blockName}DefaultsM3 extends ListTileThemeData { + const _${blockName}DefaultsM3(this.context) + : super(shape: ${shape("md.comp.list.list-item.container")}); + + final BuildContext context; + + @override + Color? get tileColor => ${componentColor("md.comp.list.list-item.container")}; + + @override + TextStyle? get titleTextStyle => ${textStyle("md.comp.list.list-item.label-text")}; + + @override + TextStyle? get subtitleTextStyle => ${textStyle("md.comp.list.list-item.supporting-text")}; + + @override + TextStyle? get leadingAndTrailingTextStyle => ${textStyle("md.comp.list.list-item.trailing-supporting-text")}; + + @override + Color? get selectedColor => ${componentColor('md.comp.list.list-item.selected.trailing-icon')}; + + @override + Color? get iconColor => ${componentColor('md.comp.list.list-item.unselected.trailing-icon')}; +} +'''; +} diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index a0d79fb324c86..2deb81431c348 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart @@ -15,6 +15,7 @@ import 'ink_decoration.dart'; import 'ink_well.dart'; import 'list_tile_theme.dart'; import 'material_state.dart'; +import 'text_theme.dart'; import 'theme.dart'; import 'theme_data.dart'; @@ -278,6 +279,9 @@ class ListTile extends StatelessWidget { this.selectedColor, this.iconColor, this.textColor, + this.titleTextStyle, + this.subtitleTextStyle, + this.leadingAndTrailingTextStyle, this.contentPadding, this.enabled = true, this.onTap, @@ -364,6 +368,10 @@ class ListTile extends StatelessWidget { /// If this property is null then its value is based on [ListTileTheme.dense]. /// /// Dense list tiles default to a smaller height. + /// + /// When [ThemeData.useMaterial3] is true, ListTile doesn't support [dense] property. + /// If [dense] or [ListTileTheme.dense] and [ThemeData.useMaterial3] are true, + /// ListTile will throw an assertion error. final bool? dense; /// Defines how compact the list tile's layout will be. @@ -421,6 +429,28 @@ class ListTile extends StatelessWidget { /// [ListTileThemeData]. final Color? textColor; + /// The text style for ListTile's [title]. + /// + /// If this property is null, then [ListTileThemeData.titleTextStyle] is used. + /// If that is also null and [ThemeData.useMaterial3] is true, [TextTheme.bodyLarge] + /// will be used. Otherwise, If ListTile style is [ListTileStyle.list], + /// [TextTheme.titleMedium] will be used and if ListTile style is [ListTileStyle.drawer], + /// [TextTheme.bodyLarge] will be used. + final TextStyle? titleTextStyle; + + /// The text style for ListTile's [subtitle]. + /// + /// If this property is null, then [ListTileThemeData.subtitleTextStyle] is used. + /// If that is also null, [TextTheme.bodyMedium] will be used. + final TextStyle? subtitleTextStyle; + + /// The text style for ListTile's [leading] and [trailing]. + /// + /// If this property is null, then [ListTileThemeData.leadingAndTrailingTextStyle] is used. + /// If that is also null and [ThemeData.useMaterial3] is true, [TextTheme.labelSmall] + /// will be used, otherwise [TextTheme.bodyMedium] will be used. + final TextStyle? leadingAndTrailingTextStyle; + /// Defines the font used for the [title]. /// /// If this property is null then [ListTileThemeData.style] is used. If that @@ -588,91 +618,20 @@ class ListTile extends StatelessWidget { ]; } - Color? _iconColor(ThemeData theme, ListTileThemeData tileTheme) { - if (!enabled) { - return theme.disabledColor; - } - - if (selected) { - return selectedColor ?? tileTheme.selectedColor ?? theme.listTileTheme.selectedColor ?? theme.colorScheme.primary; - } - - final Color? color = iconColor - ?? tileTheme.iconColor - ?? theme.listTileTheme.iconColor - // If [ThemeData.useMaterial3] is set to true the disabled icon color - // will be set to Theme.colorScheme.onSurface(0.38), if false, defaults to null, - // as described in: https://m3.material.io/components/icon-buttons/specs. - ?? (theme.useMaterial3 ? theme.colorScheme.onSurface.withOpacity(0.38) : null); - if (color != null) { - return color; - } - - switch (theme.brightness) { - case Brightness.light: - // For the sake of backwards compatibility, the default for unselected - // tiles is Colors.black45 rather than colorScheme.onSurface.withAlpha(0x73). - return Colors.black45; - case Brightness.dark: - return null; // null - use current icon theme color - } - } - - Color? _textColor(ThemeData theme, ListTileThemeData tileTheme, Color? defaultColor) { - if (!enabled) { - return theme.disabledColor; - } - - if (selected) { - return selectedColor ?? tileTheme.selectedColor ?? theme.listTileTheme.selectedColor ?? theme.colorScheme.primary; - } - - return textColor ?? tileTheme.textColor ?? theme.listTileTheme.textColor ?? defaultColor; - } - bool _isDenseLayout(ThemeData theme, ListTileThemeData tileTheme) { - return dense ?? tileTheme.dense ?? theme.listTileTheme.dense ?? false; - } - - TextStyle _titleTextStyle(ThemeData theme, ListTileThemeData tileTheme) { - final TextStyle textStyle; - switch(style ?? tileTheme.style ?? theme.listTileTheme.style ?? ListTileStyle.list) { - case ListTileStyle.drawer: - textStyle = theme.useMaterial3 ? theme.textTheme.bodyMedium! : theme.textTheme.bodyLarge!; - break; - case ListTileStyle.list: - textStyle = theme.useMaterial3 ? theme.textTheme.titleMedium! : theme.textTheme.titleMedium!; - break; + final bool isDense = dense ?? tileTheme.dense ?? theme.listTileTheme.dense ?? false; + if (theme.useMaterial3) { + assert(!isDense, 'ListTile.dense cannot be true while Theme.useMaterial3 is true.'); } - final Color? color = _textColor(theme, tileTheme, textStyle.color); - return _isDenseLayout(theme, tileTheme) - ? textStyle.copyWith(fontSize: 13.0, color: color) - : textStyle.copyWith(color: color); - } - - TextStyle _subtitleTextStyle(ThemeData theme, ListTileThemeData tileTheme) { - final TextStyle textStyle = theme.useMaterial3 ? theme.textTheme.bodyMedium! : theme.textTheme.bodyMedium!; - final Color? color = _textColor( - theme, - tileTheme, - theme.useMaterial3 ? theme.textTheme.bodySmall!.color : theme.textTheme.bodySmall!.color, - ); - return _isDenseLayout(theme, tileTheme) - ? textStyle.copyWith(color: color, fontSize: 12.0) - : textStyle.copyWith(color: color); - } - - TextStyle _trailingAndLeadingTextStyle(ThemeData theme, ListTileThemeData tileTheme) { - final TextStyle textStyle = theme.useMaterial3 ? theme.textTheme.bodyMedium! : theme.textTheme.bodyMedium!; - final Color? color = _textColor(theme, tileTheme, textStyle.color); - return textStyle.copyWith(color: color); + return isDense; } - Color _tileBackgroundColor(ThemeData theme, ListTileThemeData tileTheme) { + // TODO(TahaTesser): Refactor this to support list tile states. + Color _tileBackgroundColor(ThemeData theme, ListTileThemeData tileTheme, ListTileThemeData defaults) { final Color? color = selected ? selectedTileColor ?? tileTheme.selectedTileColor ?? theme.listTileTheme.selectedTileColor : tileColor ?? tileTheme.tileColor ?? theme.listTileTheme.tileColor; - return color ?? Colors.transparent; + return color ?? defaults.tileColor!; } @override @@ -680,23 +639,63 @@ class ListTile extends StatelessWidget { assert(debugCheckHasMaterial(context)); final ThemeData theme = Theme.of(context); final ListTileThemeData tileTheme = ListTileTheme.of(context); - final IconThemeData iconThemeData = IconThemeData(color: _iconColor(theme, tileTheme)); + final ListTileStyle listTileStyle = style + ?? tileTheme.style + ?? theme.listTileTheme.style + ?? ListTileStyle.list; + final ListTileThemeData defaults = theme.useMaterial3 + ? _LisTileDefaultsM3(context) + : _LisTileDefaultsM2(context, listTileStyle); + final Set states = { + if (!enabled) MaterialState.disabled, + if (selected) MaterialState.selected, + }; - TextStyle? leadingAndTrailingTextStyle; + Color? resolveColor(Color? explicitColor, Color? selectedColor, Color? enabledColor, [Color? disabledColor]) { + return _IndividualOverrides( + explicitColor: explicitColor, + selectedColor: selectedColor, + enabledColor: enabledColor, + disabledColor: disabledColor, + ).resolve(states); + } + + final Color? effectiveIconColor = resolveColor(iconColor, selectedColor, iconColor) + ?? resolveColor(tileTheme.iconColor, tileTheme.selectedColor, tileTheme.iconColor) + ?? resolveColor(theme.listTileTheme.iconColor, theme.listTileTheme.selectedColor, theme.listTileTheme.iconColor) + ?? resolveColor(defaults.iconColor, defaults.selectedColor, defaults.iconColor, theme.disabledColor); + final Color? effectiveColor = resolveColor(textColor, selectedColor, textColor) + ?? resolveColor(tileTheme.textColor, tileTheme.selectedColor, tileTheme.textColor) + ?? resolveColor(theme.listTileTheme.textColor, theme.listTileTheme.selectedColor, theme.listTileTheme.textColor) + ?? resolveColor(defaults.textColor, defaults.selectedColor, defaults.textColor, theme.disabledColor); + final IconThemeData iconThemeData = IconThemeData(color: effectiveIconColor); + + TextStyle? leadingAndTrailingStyle; if (leading != null || trailing != null) { - leadingAndTrailingTextStyle = _trailingAndLeadingTextStyle(theme, tileTheme); + leadingAndTrailingStyle = leadingAndTrailingTextStyle + ?? tileTheme.leadingAndTrailingTextStyle + ?? defaults.leadingAndTrailingTextStyle!; + final Color? leadingAndTrailingTextColor = effectiveColor; + leadingAndTrailingStyle = leadingAndTrailingStyle.copyWith(color: leadingAndTrailingTextColor); } Widget? leadingIcon; if (leading != null) { leadingIcon = AnimatedDefaultTextStyle( - style: leadingAndTrailingTextStyle!, + style: leadingAndTrailingStyle!, duration: kThemeChangeDuration, child: leading!, ); } - final TextStyle titleStyle = _titleTextStyle(theme, tileTheme); + TextStyle titleStyle = titleTextStyle + ?? tileTheme.titleTextStyle + ?? defaults.titleTextStyle!; + final Color? titleColor = effectiveColor; + titleStyle = titleStyle.copyWith( + color: titleColor, + fontSize: _isDenseLayout(theme, tileTheme) ? 13.0 : null, + ); final Widget titleText = AnimatedDefaultTextStyle( style: titleStyle, duration: kThemeChangeDuration, @@ -706,7 +705,14 @@ class ListTile extends StatelessWidget { Widget? subtitleText; TextStyle? subtitleStyle; if (subtitle != null) { - subtitleStyle = _subtitleTextStyle(theme, tileTheme); + subtitleStyle = subtitleTextStyle + ?? tileTheme.subtitleTextStyle + ?? defaults.subtitleTextStyle!; + final Color? subtitleColor = effectiveColor ?? theme.textTheme.bodySmall!.color; + subtitleStyle = subtitleStyle.copyWith( + color: subtitleColor, + fontSize: _isDenseLayout(theme, tileTheme) ? 12.0 : null, + ); subtitleText = AnimatedDefaultTextStyle( style: subtitleStyle, duration: kThemeChangeDuration, @@ -717,7 +723,7 @@ class ListTile extends StatelessWidget { Widget? trailingIcon; if (trailing != null) { trailingIcon = AnimatedDefaultTextStyle( - style: leadingAndTrailingTextStyle!, + style: leadingAndTrailingStyle!, duration: kThemeChangeDuration, child: trailing!, ); @@ -728,15 +734,13 @@ class ListTile extends StatelessWidget { final EdgeInsets resolvedContentPadding = contentPadding?.resolve(textDirection) ?? tileTheme.contentPadding?.resolve(textDirection) ?? defaultContentPadding; - - final Set states = { + // Show basic cursor when ListTile isn't enabled or gesture callbacks are null. + final Set mouseStates = { if (!enabled || (onTap == null && onLongPress == null)) MaterialState.disabled, - if (selected) MaterialState.selected, }; - - final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs(mouseCursor, states) - ?? tileTheme.mouseCursor?.resolve(states) - ?? MaterialStateMouseCursor.clickable.resolve(states); + final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs(mouseCursor, mouseStates) + ?? tileTheme.mouseCursor?.resolve(mouseStates) + ?? MaterialStateMouseCursor.clickable.resolve(mouseStates); return InkWell( customBorder: shape ?? tileTheme.shape, @@ -757,7 +761,7 @@ class ListTile extends StatelessWidget { child: Ink( decoration: ShapeDecoration( shape: shape ?? tileTheme.shape ?? const Border(), - color: _tileBackgroundColor(theme, tileTheme), + color: _tileBackgroundColor(theme, tileTheme, defaults), ), child: SafeArea( top: false, @@ -774,8 +778,8 @@ class ListTile extends StatelessWidget { visualDensity: visualDensity ?? tileTheme.visualDensity ?? theme.visualDensity, isThreeLine: isThreeLine, textDirection: textDirection, - titleBaselineType: titleStyle.textBaseline!, - subtitleBaselineType: subtitleStyle?.textBaseline, + titleBaselineType: titleStyle.textBaseline ?? defaults.titleTextStyle!.textBaseline!, + subtitleBaselineType: subtitleStyle?.textBaseline ?? defaults.subtitleTextStyle!.textBaseline!, horizontalTitleGap: horizontalTitleGap ?? tileTheme.horizontalTitleGap ?? 16, minVerticalPadding: minVerticalPadding ?? tileTheme.minVerticalPadding ?? 4, minLeadingWidth: minLeadingWidth ?? tileTheme.minLeadingWidth ?? 40, @@ -821,6 +825,36 @@ class ListTile extends StatelessWidget { } } +class _IndividualOverrides extends MaterialStateProperty { + _IndividualOverrides({ + this.explicitColor, + this.enabledColor, + this.selectedColor, + this.disabledColor, + }); + + final Color? explicitColor; + final Color? enabledColor; + final Color? selectedColor; + final Color? disabledColor; + + @override + Color? resolve(Set states) { + if (explicitColor is MaterialStateColor) { + return MaterialStateProperty.resolveAs(explicitColor, states); + } + + if (states.contains(MaterialState.disabled)) { + return disabledColor; + } + if (states.contains(MaterialState.selected)) { + return selectedColor; + } + + return enabledColor; + } +} + // Identifies the children of a _ListTileElement. enum _ListTileSlot { leading, @@ -1343,3 +1377,87 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_ return false; } } + +class _LisTileDefaultsM2 extends ListTileThemeData { + _LisTileDefaultsM2(this.context, ListTileStyle style) + : _themeData = Theme.of(context), + _textTheme = Theme.of(context).textTheme, + super( + shape: const Border(), + style: style, + ); + + final BuildContext context; + final ThemeData _themeData; + final TextTheme _textTheme; + + @override + Color? get tileColor => Colors.transparent; + + @override + TextStyle? get titleTextStyle { + switch (style!) { + case ListTileStyle.drawer: + return _textTheme.bodyLarge; + case ListTileStyle.list: + return _textTheme.titleMedium; + } + } + + @override + TextStyle? get subtitleTextStyle => _textTheme.bodyMedium; + + @override + TextStyle? get leadingAndTrailingTextStyle => _textTheme.bodyMedium; + + @override + Color? get selectedColor => _themeData.colorScheme.primary; + + @override + Color? get iconColor { + switch (_themeData.brightness) { + case Brightness.light: + // For the sake of backwards compatibility, the default for unselected + // tiles is Colors.black45 rather than colorScheme.onSurface.withAlpha(0x73). + return Colors.black45; + case Brightness.dark: + return null; // null, Use current icon theme color + } + } +} + +// BEGIN GENERATED TOKEN PROPERTIES - LisTile + +// Do not edit by hand. The code between the "BEGIN GENERATED" and +// "END GENERATED" comments are generated from data in the Material +// Design token database by the script: +// dev/tools/gen_defaults/bin/gen_defaults.dart. + +// Token database version: v0_143 + +class _LisTileDefaultsM3 extends ListTileThemeData { + const _LisTileDefaultsM3(this.context) + : super(shape: const RoundedRectangleBorder()); + + final BuildContext context; + + @override + Color? get tileColor => Theme.of(context).colorScheme.surface; + + @override + TextStyle? get titleTextStyle => Theme.of(context).textTheme.bodyLarge; + + @override + TextStyle? get subtitleTextStyle => Theme.of(context).textTheme.bodyMedium; + + @override + TextStyle? get leadingAndTrailingTextStyle => Theme.of(context).textTheme.labelSmall; + + @override + Color? get selectedColor => Theme.of(context).colorScheme.primary; + + @override + Color? get iconColor => Theme.of(context).colorScheme.onSurface; +} + +// END GENERATED TOKEN PROPERTIES - LisTile diff --git a/packages/flutter/lib/src/material/list_tile_theme.dart b/packages/flutter/lib/src/material/list_tile_theme.dart index 501a608dd5119..b5e421708b816 100644 --- a/packages/flutter/lib/src/material/list_tile_theme.dart +++ b/packages/flutter/lib/src/material/list_tile_theme.dart @@ -51,6 +51,9 @@ class ListTileThemeData with Diagnosticable { this.selectedColor, this.iconColor, this.textColor, + this.titleTextStyle, + this.subtitleTextStyle, + this.leadingAndTrailingTextStyle, this.contentPadding, this.tileColor, this.selectedTileColor, @@ -80,6 +83,15 @@ class ListTileThemeData with Diagnosticable { /// Overrides the default value of [ListTile.textColor]. final Color? textColor; + /// Overrides the default value of [ListTile.titleTextStyle]. + final TextStyle? titleTextStyle; + + /// Overrides the default value of [ListTile.subtitleTextStyle]. + final TextStyle? subtitleTextStyle; + + /// Overrides the default value of [ListTile.leadingAndTrailingTextStyle]. + final TextStyle? leadingAndTrailingTextStyle; + /// Overrides the default value of [ListTile.contentPadding]. final EdgeInsetsGeometry? contentPadding; @@ -116,6 +128,9 @@ class ListTileThemeData with Diagnosticable { Color? selectedColor, Color? iconColor, Color? textColor, + TextStyle? titleTextStyle, + TextStyle? subtitleTextStyle, + TextStyle? leadingAndTrailingTextStyle, EdgeInsetsGeometry? contentPadding, Color? tileColor, Color? selectedTileColor, @@ -134,6 +149,9 @@ class ListTileThemeData with Diagnosticable { selectedColor: selectedColor ?? this.selectedColor, iconColor: iconColor ?? this.iconColor, textColor: textColor ?? this.textColor, + titleTextStyle: titleTextStyle ?? this.titleTextStyle, + subtitleTextStyle: titleTextStyle ?? this.subtitleTextStyle, + leadingAndTrailingTextStyle: titleTextStyle ?? this.leadingAndTrailingTextStyle, contentPadding: contentPadding ?? this.contentPadding, tileColor: tileColor ?? this.tileColor, selectedTileColor: selectedTileColor ?? this.selectedTileColor, @@ -159,6 +177,9 @@ class ListTileThemeData with Diagnosticable { selectedColor: Color.lerp(a?.selectedColor, b?.selectedColor, t), iconColor: Color.lerp(a?.iconColor, b?.iconColor, t), textColor: Color.lerp(a?.textColor, b?.textColor, t), + titleTextStyle: TextStyle.lerp(a?.titleTextStyle, b?.titleTextStyle, t), + subtitleTextStyle: TextStyle.lerp(a?.subtitleTextStyle, b?.subtitleTextStyle, t), + leadingAndTrailingTextStyle: TextStyle.lerp(a?.leadingAndTrailingTextStyle, b?.leadingAndTrailingTextStyle, t), contentPadding: EdgeInsetsGeometry.lerp(a?.contentPadding, b?.contentPadding, t), tileColor: Color.lerp(a?.tileColor, b?.tileColor, t), selectedTileColor: Color.lerp(a?.selectedTileColor, b?.selectedTileColor, t), @@ -179,6 +200,9 @@ class ListTileThemeData with Diagnosticable { selectedColor, iconColor, textColor, + titleTextStyle, + subtitleTextStyle, + leadingAndTrailingTextStyle, contentPadding, tileColor, selectedTileColor, @@ -204,6 +228,9 @@ class ListTileThemeData with Diagnosticable { && other.style == style && other.selectedColor == selectedColor && other.iconColor == iconColor + && other.titleTextStyle == titleTextStyle + && other.subtitleTextStyle == subtitleTextStyle + && other.leadingAndTrailingTextStyle == leadingAndTrailingTextStyle && other.textColor == textColor && other.contentPadding == contentPadding && other.tileColor == tileColor @@ -225,6 +252,9 @@ class ListTileThemeData with Diagnosticable { properties.add(ColorProperty('selectedColor', selectedColor, defaultValue: null)); properties.add(ColorProperty('iconColor', iconColor, defaultValue: null)); properties.add(ColorProperty('textColor', textColor, defaultValue: null)); + properties.add(DiagnosticsProperty('titleTextStyle', titleTextStyle, defaultValue: null)); + properties.add(DiagnosticsProperty('subtitleTextStyle', subtitleTextStyle, defaultValue: null)); + properties.add(DiagnosticsProperty('leadingAndTrailingTextStyle', leadingAndTrailingTextStyle, defaultValue: null)); properties.add(DiagnosticsProperty('contentPadding', contentPadding, defaultValue: null)); properties.add(ColorProperty('tileColor', tileColor, defaultValue: null)); properties.add(ColorProperty('selectedTileColor', selectedTileColor, defaultValue: null)); diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index 7cbba67314c50..aa2e5e91cad89 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -1578,10 +1578,11 @@ void main() { testWidgets('ListTile default tile color', (WidgetTester tester) async { bool isSelected = false; - const Color defaultColor = Colors.transparent; + final ThemeData theme = ThemeData(useMaterial3: true); await tester.pumpWidget( MaterialApp( + theme: theme, home: Material( child: Center( child: StatefulBuilder( @@ -1600,13 +1601,13 @@ void main() { ), ); - expect(find.byType(Material), paints..rect(color: defaultColor)); + expect(find.byType(Material), paints..rect(color: theme.colorScheme.surface)); // Tap on tile to change isSelected. await tester.tap(find.byType(ListTile)); await tester.pumpAndSettle(); - expect(find.byType(Material), paints..rect(color: defaultColor)); + expect(find.byType(Material), paints..rect(color: theme.colorScheme.surface)); }); testWidgets('ListTile layout at zero size', (WidgetTester tester) async { @@ -2064,18 +2065,15 @@ void main() { expect(textColor(trailingKey), theme.disabledColor); }); - testWidgets('selected, enabled ListTile default icon color, light and dark themes', (WidgetTester tester) async { - const ColorScheme lightColorScheme = ColorScheme.light(); - const ColorScheme darkColorScheme = ColorScheme.dark(); + testWidgets('selected, enabled ListTile default icon color', (WidgetTester tester) async { + final ThemeData theme = ThemeData(useMaterial3: true); + final ColorScheme colorScheme = theme.colorScheme; final Key leadingKey = UniqueKey(); final Key titleKey = UniqueKey(); final Key subtitleKey = UniqueKey(); final Key trailingKey = UniqueKey(); - Widget buildFrame({ required Brightness brightness, required bool selected }) { - final ThemeData theme = brightness == Brightness.light - ? ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true) - : ThemeData.from(colorScheme: const ColorScheme.dark(), useMaterial3: true); + Widget buildFrame({required bool selected }) { return MaterialApp( theme: theme, home: Material( @@ -2094,56 +2092,32 @@ void main() { Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; - await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: true)); - expect(iconColor(leadingKey), lightColorScheme.primary); - expect(iconColor(titleKey), lightColorScheme.primary); - expect(iconColor(subtitleKey), lightColorScheme.primary); - expect(iconColor(trailingKey), lightColorScheme.primary); - - await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: false)); - expect(iconColor(leadingKey), lightColorScheme.onSurface.withOpacity(0.38)); - expect(iconColor(titleKey), lightColorScheme.onSurface.withOpacity(0.38)); - expect(iconColor(subtitleKey), lightColorScheme.onSurface.withOpacity(0.38)); - expect(iconColor(trailingKey), lightColorScheme.onSurface.withOpacity(0.38)); - - await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: true)); - await tester.pumpAndSettle(); // Animated theme change - expect(iconColor(leadingKey), darkColorScheme.primary); - expect(iconColor(titleKey), darkColorScheme.primary); - expect(iconColor(subtitleKey), darkColorScheme.primary); - expect(iconColor(trailingKey), darkColorScheme.primary); - - // For this configuration, ListTile defers to the default IconTheme. - // The default dark theme's IconTheme has color:white - await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: false)); - expect(iconColor(leadingKey), darkColorScheme.onSurface.withOpacity(0.38)); - expect(iconColor(titleKey), darkColorScheme.onSurface.withOpacity(0.38)); - expect(iconColor(subtitleKey), darkColorScheme.onSurface.withOpacity(0.38)); - expect(iconColor(trailingKey), darkColorScheme.onSurface.withOpacity(0.38)); + await tester.pumpWidget(buildFrame(selected: true)); + expect(iconColor(leadingKey), colorScheme.primary); + expect(iconColor(titleKey), colorScheme.primary); + expect(iconColor(subtitleKey), colorScheme.primary); + expect(iconColor(trailingKey), colorScheme.primary); + + await tester.pumpWidget(buildFrame(selected: false)); + expect(iconColor(leadingKey), colorScheme.onSurface); + expect(iconColor(titleKey), colorScheme.onSurface); + expect(iconColor(subtitleKey), colorScheme.onSurface); + expect(iconColor(trailingKey), colorScheme.onSurface); }); testWidgets('ListTile font size', (WidgetTester tester) async { - Widget buildFrame({ - bool dense = false, - bool enabled = true, - bool selected = false, - ListTileStyle? style, - }) { + Widget buildFrame() { return MaterialApp( theme: ThemeData(useMaterial3: true), home: Material( child: Center( child: Builder( builder: (BuildContext context) { - return ListTile( - dense: dense, - enabled: enabled, - selected: selected, - style: style, - leading: const TestText('leading'), - title: const TestText('title'), - subtitle: const TestText('subtitle') , - trailing: const TestText('trailing'), + return const ListTile( + leading: TestText('leading'), + title: TestText('title'), + subtitle: TestText('subtitle') , + trailing: TestText('trailing'), ); }, ), @@ -2152,76 +2126,31 @@ void main() { ); } - // ListTile - ListTileStyle.list (default). + // ListTile default text sizes. await tester.pumpWidget(buildFrame()); - RenderParagraph leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 14.0); - RenderParagraph title = _getTextRenderObject(tester, 'title'); + final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 11.0); + final RenderParagraph title = _getTextRenderObject(tester, 'title'); expect(title.text.style!.fontSize, 16.0); - RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); + final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); expect(subtitle.text.style!.fontSize, 14.0); - RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 14.0); - - // ListTile - Densed - ListTileStyle.list (default). - await tester.pumpWidget(buildFrame(dense: true)); - await tester.pumpAndSettle(); - leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 14.0); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.fontSize, 13.0); - subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.fontSize, 12.0); - trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 14.0); - - // ListTile - ListTileStyle.drawer. - await tester.pumpWidget(buildFrame(style: ListTileStyle.drawer)); - await tester.pumpAndSettle(); - leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 14.0); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.fontSize, 14.0); - subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.fontSize, 14.0); - trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 14.0); - - // ListTile - Densed - ListTileStyle.drawer. - await tester.pumpWidget(buildFrame(dense: true, style: ListTileStyle.drawer)); - await tester.pumpAndSettle(); - leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 14.0); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.fontSize, 13.0); - subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.fontSize, 12.0); - trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 14.0); + final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 11.0); }); testWidgets('ListTile text color', (WidgetTester tester) async { - Widget buildFrame({ - bool dense = false, - bool enabled = true, - bool selected = false, - ListTileStyle? style, - }) { + Widget buildFrame() { return MaterialApp( theme: ThemeData(useMaterial3: true), home: Material( child: Center( child: Builder( builder: (BuildContext context) { - return ListTile( - dense: dense, - enabled: enabled, - selected: selected, - style: style, - leading: const TestText('leading'), - title: const TestText('title'), - subtitle: const TestText('subtitle') , - trailing: const TestText('trailing'), + return const ListTile( + leading: TestText('leading'), + title: TestText('title'), + subtitle: TestText('subtitle') , + trailing: TestText('trailing'), ); }, ), @@ -2232,28 +2161,16 @@ void main() { final ThemeData theme = ThemeData(useMaterial3: true); - // ListTile - ListTileStyle.list (default). + // ListTile default text colors. await tester.pumpWidget(buildFrame()); - RenderParagraph leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.color, theme.textTheme.bodyMedium!.color); - RenderParagraph title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, theme.textTheme.titleMedium!.color); - RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.color, theme.textTheme.bodySmall!.color); - RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.color, theme.textTheme.bodyMedium!.color); - - // ListTile - ListTileStyle.drawer. - await tester.pumpWidget(buildFrame(style: ListTileStyle.drawer)); - await tester.pumpAndSettle(); - leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.color, theme.textTheme.bodyMedium!.color); - title = _getTextRenderObject(tester, 'title'); + final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.color, theme.textTheme.labelSmall!.color); + final RenderParagraph title = _getTextRenderObject(tester, 'title'); expect(title.text.style!.color, theme.textTheme.bodyLarge!.color); - subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.color, theme.textTheme.bodySmall!.color); - trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.color, theme.textTheme.bodyMedium!.color); + final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.color, theme.textTheme.bodyMedium!.color); + final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.color, theme.textTheme.labelSmall!.color); }); testWidgets('Default ListTile debugFillProperties', (WidgetTester tester) async { @@ -2333,6 +2250,151 @@ void main() { ); }); + testWidgets('ListTile throws assertion when useMaterial3 and dense are true', (WidgetTester tester) async { + Widget buildFrame({required bool useMaterial3}) { + return MaterialApp( + theme: ThemeData(useMaterial3: useMaterial3), + home: Material( + child: Center( + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return const ListTile( + dense: true, + title: Text('Title'), + ); + }, + ), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame(useMaterial3: true)); + final AssertionError exception = tester.takeException() as AssertionError; + expect( + exception.message, + 'ListTile.dense cannot be true while Theme.useMaterial3 is true.', + ); + + await tester.pumpWidget(buildFrame(useMaterial3: false)); + expect(tester.takeException(), isNull); + }); + + testWidgets('ListTile.textColor respects MaterialStateColor', (WidgetTester tester) async { + bool enabled = false; + bool selected = false; + const Color defaultColor = Colors.blue; + const Color selectedColor = Colors.green; + const Color disabledColor = Colors.red; + + Widget buildFrame() { + return MaterialApp( + theme: ThemeData(useMaterial3: true), + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ListTile( + enabled: enabled, + selected: selected, + textColor: MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.disabled)) { + return disabledColor; + } + + if (states.contains(MaterialState.selected)) { + return selectedColor; + } + + return defaultColor; + }), + title: const TestText('title'), + subtitle: const TestText('subtitle') , + ); + }, + ), + ), + ), + ); + } + + // Test disabled state. + await tester.pumpWidget(buildFrame()); + RenderParagraph title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, disabledColor); + + // Test enabled state. + enabled = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, defaultColor); + + // Test selected state. + selected = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, selectedColor); + }); + + testWidgets('ListTile.iconColor respects MaterialStateColor', (WidgetTester tester) async { + bool enabled = false; + bool selected = false; + const Color defaultColor = Colors.blue; + const Color selectedColor = Colors.green; + const Color disabledColor = Colors.red; + final Key leadingKey = UniqueKey(); + + Widget buildFrame() { + return MaterialApp( + theme: ThemeData(useMaterial3: true), + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ListTile( + enabled: enabled, + selected: selected, + iconColor: MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.disabled)) { + return disabledColor; + } + + if (states.contains(MaterialState.selected)) { + return selectedColor; + } + + return defaultColor; + }), + leading: TestIcon(key: leadingKey), + ); + }, + ), + ), + ), + ); + } + + Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; + + // Test disabled state. + await tester.pumpWidget(buildFrame()); + expect(iconColor(leadingKey), disabledColor); + + // Test enabled state. + enabled = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + expect(iconColor(leadingKey), defaultColor); + + // Test selected state. + selected = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + expect(iconColor(leadingKey), selectedColor); + }); + group('Material 2', () { // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3 // is turned on by default, these tests can be removed. @@ -2345,6 +2407,7 @@ void main() { ListTileStyle? style, }) { return MaterialApp( + theme: ThemeData(useMaterial3: false), home: Material( child: Center( child: Builder( @@ -2422,6 +2485,7 @@ void main() { ListTileStyle? style, }) { return MaterialApp( + theme: ThemeData(useMaterial3: false), home: Material( child: Center( child: Builder( @@ -2481,8 +2545,8 @@ void main() { Widget buildFrame({ required Brightness brightness, required bool selected }) { final ThemeData theme = brightness == Brightness.light - ? ThemeData.from(colorScheme: const ColorScheme.light()) - : ThemeData.from(colorScheme: const ColorScheme.dark()); + ? ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: false) + : ThemeData.from(colorScheme: const ColorScheme.dark(), useMaterial3: false); return MaterialApp( theme: theme, home: Material( @@ -2528,6 +2592,40 @@ void main() { expect(iconColor(subtitleKey), Colors.white); expect(iconColor(trailingKey), Colors.white); }); + + testWidgets('ListTile default tile color', (WidgetTester tester) async { + bool isSelected = false; + const Color defaultColor = Colors.transparent; + + await tester.pumpWidget( + MaterialApp( + theme: ThemeData(useMaterial3: false), + home: Material( + child: Center( + child: StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return ListTile( + selected: isSelected, + onTap: () { + setState(()=> isSelected = !isSelected); + }, + title: const Text('Title'), + ); + }, + ), + ), + ), + ), + ); + + expect(find.byType(Material), paints..rect(color: defaultColor)); + + // Tap on tile to change isSelected. + await tester.tap(find.byType(ListTile)); + await tester.pumpAndSettle(); + + expect(find.byType(Material), paints..rect(color: defaultColor)); + }); }); } diff --git a/packages/flutter/test/material/list_tile_theme_test.dart b/packages/flutter/test/material/list_tile_theme_test.dart index 89d43e840189b..dd1cfc459018d 100644 --- a/packages/flutter/test/material/list_tile_theme_test.dart +++ b/packages/flutter/test/material/list_tile_theme_test.dart @@ -59,6 +59,9 @@ void main() { expect(themeData.selectedColor, null); expect(themeData.iconColor, null); expect(themeData.textColor, null); + expect(themeData.titleTextStyle, null); + expect(themeData.subtitleTextStyle, null); + expect(themeData.leadingAndTrailingTextStyle, null); expect(themeData.contentPadding, null); expect(themeData.tileColor, null); expect(themeData.selectedTileColor, null); @@ -91,9 +94,12 @@ void main() { selectedColor: Color(0x00000001), iconColor: Color(0x00000002), textColor: Color(0x00000003), + titleTextStyle: TextStyle(color: Color(0x00000004)), + subtitleTextStyle: TextStyle(color: Color(0x00000005)), + leadingAndTrailingTextStyle: TextStyle(color: Color(0x00000006)), contentPadding: EdgeInsets.all(100), - tileColor: Color(0x00000004), - selectedTileColor: Color(0x00000005), + tileColor: Color(0x00000007), + selectedTileColor: Color(0x00000008), horizontalTitleGap: 200, minVerticalPadding: 300, minLeadingWidth: 400, @@ -116,9 +122,12 @@ void main() { 'selectedColor: Color(0x00000001)', 'iconColor: Color(0x00000002)', 'textColor: Color(0x00000003)', + 'titleTextStyle: TextStyle(inherit: true, color: Color(0x00000004))', + 'subtitleTextStyle: TextStyle(inherit: true, color: Color(0x00000005))', + 'leadingAndTrailingTextStyle: TextStyle(inherit: true, color: Color(0x00000006))', 'contentPadding: EdgeInsets.all(100.0)', - 'tileColor: Color(0x00000004)', - 'selectedTileColor: Color(0x00000005)', + 'tileColor: Color(0x00000007)', + 'selectedTileColor: Color(0x00000008)', 'horizontalTitleGap: 200.0', 'minVerticalPadding: 300.0', 'minLeadingWidth: 400.0', @@ -365,6 +374,99 @@ void main() { expect(textColor(trailingKey), theme.disabledColor); }); + testWidgets( + "ListTile respects ListTileTheme's titleTextStyle, subtitleTextStyle & leadingAndTrailingTextStyle", + (WidgetTester tester) async { + final ThemeData theme = ThemeData( + useMaterial3: true, + listTileTheme: const ListTileThemeData( + titleTextStyle: TextStyle(fontSize: 20.0), + subtitleTextStyle: TextStyle(fontSize: 17.5), + leadingAndTrailingTextStyle: TextStyle(fontSize: 15.0), + ), + ); + + Widget buildFrame() { + return MaterialApp( + theme: theme, + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return const ListTile( + leading: TestText('leading'), + title: TestText('title'), + subtitle: TestText('subtitle') , + trailing: TestText('trailing'), + ); + }, + ), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame()); + final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 15.0); + final RenderParagraph title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.fontSize, 20.0); + final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.fontSize, 17.5); + final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 15.0); + }); + + testWidgets( + "ListTile's titleTextStyle, subtitleTextStyle & leadingAndTrailingTextStyle are overridden by ListTile properties", + (WidgetTester tester) async { + final ThemeData theme = ThemeData( + useMaterial3: true, + listTileTheme: const ListTileThemeData( + titleTextStyle: TextStyle(fontSize: 20.0), + subtitleTextStyle: TextStyle(fontSize: 17.5), + leadingAndTrailingTextStyle: TextStyle(fontSize: 15.0), + ), + ); + + const TextStyle titleTextStyle = TextStyle(fontSize: 23.0); + const TextStyle subtitleTextStyle = TextStyle(fontSize: 20.0); + const TextStyle leadingAndTrailingTextStyle = TextStyle(fontSize: 18.0); + + Widget buildFrame() { + return MaterialApp( + theme: theme, + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return const ListTile( + titleTextStyle: titleTextStyle, + subtitleTextStyle: subtitleTextStyle, + leadingAndTrailingTextStyle: leadingAndTrailingTextStyle, + leading: TestText('leading'), + title: TestText('title'), + subtitle: TestText('subtitle') , + trailing: TestText('trailing'), + ); + }, + ), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame()); + final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 18.0); + final RenderParagraph title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.fontSize, 23.0); + final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.fontSize, 20.0); + final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 18.0); + }); + testWidgets("ListTile respects ListTileTheme's tileColor & selectedTileColor", (WidgetTester tester) async { late ListTileThemeData theme; bool isSelected = false; @@ -479,4 +581,134 @@ void main() { // Test shape. expect(inkWellBorder, shapeBorder); }); + + testWidgets('ListTile respects MaterialStateColor LisTileTheme.textColor', (WidgetTester tester) async { + bool enabled = false; + bool selected = false; + const Color defaultColor = Colors.blue; + const Color selectedColor = Colors.green; + const Color disabledColor = Colors.red; + + final ThemeData theme = ThemeData( + listTileTheme: ListTileThemeData( + textColor: MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.disabled)) { + return disabledColor; + } + + if (states.contains(MaterialState.selected)) { + return selectedColor; + } + + return defaultColor; + }), + ), + ); + Widget buildFrame() { + return MaterialApp( + theme: theme, + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ListTile( + enabled: enabled, + selected: selected, + title: const TestText('title'), + subtitle: const TestText('subtitle') , + ); + }, + ), + ), + ), + ); + } + + // Test disabled state. + await tester.pumpWidget(buildFrame()); + RenderParagraph title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, disabledColor); + + // Test enabled state. + enabled = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, defaultColor); + + // Test selected state. + selected = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, selectedColor); + }); + + testWidgets('ListTile respects MaterialStateColor LisTileTheme.iconColor', (WidgetTester tester) async { + bool enabled = false; + bool selected = false; + const Color defaultColor = Colors.blue; + const Color selectedColor = Colors.green; + const Color disabledColor = Colors.red; + final Key leadingKey = UniqueKey(); + + final ThemeData theme = ThemeData( + listTileTheme: ListTileThemeData( + iconColor: MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.disabled)) { + return disabledColor; + } + + if (states.contains(MaterialState.selected)) { + return selectedColor; + } + + return defaultColor; + }), + ), + ); + Widget buildFrame() { + return MaterialApp( + theme: theme, + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ListTile( + enabled: enabled, + selected: selected, + leading: TestIcon(key: leadingKey), + ); + }, + ), + ), + ), + ); + } + + Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; + + // Test disabled state. + await tester.pumpWidget(buildFrame()); + expect(iconColor(leadingKey), disabledColor); + + // Test enabled state. + enabled = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + expect(iconColor(leadingKey), defaultColor); + + // Test selected state. + selected = true; + await tester.pumpWidget(buildFrame()); + await tester.pumpAndSettle(); + expect(iconColor(leadingKey), selectedColor); + }); +} + +RenderParagraph _getTextRenderObject(WidgetTester tester, String text) { + return tester.renderObject(find.descendant( + of: find.byType(ListTile), + matching: find.text(text), + )); } From 73cb7c2fc5ecab0476ef5d41d1227ed09f73db56 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Fri, 9 Dec 2022 15:59:11 -0500 Subject: [PATCH 11/71] Squashed MediaQuery InheritedModel (#114459) --- .../flutter/lib/fix_data/fix_cupertino.yaml | 11 + .../fix_data/fix_material/fix_material.yaml | 11 + .../lib/fix_data/fix_widgets/fix_widgets.yaml | 11 + .../lib/src/cupertino/bottom_tab_bar.dart | 2 +- .../flutter/lib/src/cupertino/colors.dart | 2 +- .../lib/src/cupertino/context_menu.dart | 4 +- .../src/cupertino/desktop_text_selection.dart | 8 +- .../desktop_text_selection_toolbar.dart | 3 +- .../flutter/lib/src/cupertino/dialog.dart | 20 +- .../lib/src/cupertino/list_section.dart | 2 +- .../flutter/lib/src/cupertino/magnifier.dart | 2 +- .../flutter/lib/src/cupertino/nav_bar.dart | 6 +- packages/flutter/lib/src/cupertino/route.dart | 4 +- .../flutter/lib/src/cupertino/scrollbar.dart | 2 +- .../flutter/lib/src/cupertino/text_field.dart | 2 +- .../lib/src/cupertino/text_selection.dart | 8 +- .../src/cupertino/text_selection_toolbar.dart | 12 +- packages/flutter/lib/src/cupertino/theme.dart | 4 +- packages/flutter/lib/src/material/about.dart | 4 +- .../flutter/lib/src/material/action_chip.dart | 2 +- .../flutter/lib/src/material/app_bar.dart | 4 +- packages/flutter/lib/src/material/banner.dart | 6 +- .../src/material/bottom_navigation_bar.dart | 10 +- .../lib/src/material/bottom_sheet.dart | 3 +- packages/flutter/lib/src/material/chip.dart | 4 +- .../flutter/lib/src/material/choice_chip.dart | 2 +- .../flutter/lib/src/material/date_picker.dart | 23 +- .../src/material/desktop_text_selection.dart | 7 +- .../desktop_text_selection_toolbar.dart | 3 +- packages/flutter/lib/src/material/dialog.dart | 6 +- packages/flutter/lib/src/material/drawer.dart | 6 +- .../lib/src/material/drawer_header.dart | 2 +- .../flutter/lib/src/material/dropdown.dart | 4 +- .../lib/src/material/elevated_button.dart | 8 +- .../lib/src/material/filled_button.dart | 8 +- .../flutter/lib/src/material/filter_chip.dart | 2 +- .../flutter/lib/src/material/ink_well.dart | 4 +- .../flutter/lib/src/material/input_chip.dart | 2 +- .../flutter/lib/src/material/magnifier.dart | 2 +- .../flutter/lib/src/material/menu_anchor.dart | 4 +- .../lib/src/material/navigation_bar.dart | 2 +- .../lib/src/material/outlined_button.dart | 4 +- .../flutter/lib/src/material/popup_menu.dart | 2 +- .../lib/src/material/range_slider.dart | 8 +- .../flutter/lib/src/material/scaffold.dart | 27 +- .../flutter/lib/src/material/scrollbar.dart | 2 +- .../lib/src/material/selectable_text.dart | 6 +- packages/flutter/lib/src/material/slider.dart | 12 +- .../flutter/lib/src/material/snack_bar.dart | 8 +- .../flutter/lib/src/material/text_button.dart | 8 +- .../flutter/lib/src/material/text_field.dart | 6 +- .../src/material/text_selection_toolbar.dart | 2 +- packages/flutter/lib/src/material/time.dart | 2 +- .../flutter/lib/src/material/time_picker.dart | 37 +- .../flutter/lib/src/material/tooltip.dart | 2 +- packages/flutter/lib/src/widgets/actions.dart | 4 +- .../flutter/lib/src/widgets/drag_target.dart | 2 +- .../lib/src/widgets/editable_text.dart | 4 +- .../lib/src/widgets/gesture_detector.dart | 2 +- packages/flutter/lib/src/widgets/image.dart | 4 +- .../flutter/lib/src/widgets/media_query.dart | 375 +++++++++++++++++- .../lib/src/widgets/overscroll_indicator.dart | 2 +- .../lib/src/widgets/reorderable_list.dart | 2 +- .../flutter/lib/src/widgets/safe_area.dart | 7 +- .../flutter/lib/src/widgets/scrollable.dart | 8 +- .../flutter/lib/src/widgets/scrollbar.dart | 2 +- .../lib/src/widgets/selectable_region.dart | 2 +- .../lib/src/widgets/snapshot_widget.dart | 4 +- packages/flutter/lib/src/widgets/text.dart | 2 +- .../test/cupertino/action_sheet_test.dart | 2 +- .../flutter/test/cupertino/dialog_test.dart | 6 +- .../test/cupertino/magnifier_test.dart | 12 +- .../test/cupertino/tab_scaffold_test.dart | 2 +- .../text_selection_toolbar_test.dart | 6 +- packages/flutter/test/material/app_test.dart | 2 +- .../material/flexible_space_bar_test.dart | 2 +- .../test/material/popup_menu_test.dart | 2 +- .../flutter/test/material/scaffold_test.dart | 6 +- .../flutter/test/material/snack_bar_test.dart | 2 +- .../flutter/test/widgets/list_view_test.dart | 2 +- .../test/widgets/media_query_test.dart | 84 +++- .../widgets/render_object_widget_test.dart | 2 +- .../test_fixes/cupertino/cupertino.dart | 3 + .../cupertino/cupertino.dart.expect | 3 + .../flutter/test_fixes/material/material.dart | 3 + .../test_fixes/material/material.dart.expect | 3 + .../flutter/test_fixes/widgets/widgets.dart | 3 + .../test_fixes/widgets/widgets.dart.expect | 3 + 88 files changed, 687 insertions(+), 239 deletions(-) diff --git a/packages/flutter/lib/fix_data/fix_cupertino.yaml b/packages/flutter/lib/fix_data/fix_cupertino.yaml index b9e40b7f1e749..b516113dfff95 100644 --- a/packages/flutter/lib/fix_data/fix_cupertino.yaml +++ b/packages/flutter/lib/fix_data/fix_cupertino.yaml @@ -18,6 +18,17 @@ # * Fixes in this file are from the Cupertino library. * version: 1 transforms: + # Changes made in https://github.com/flutter/flutter/pull/114459 + - title: "Migrate to 'boldTextOf'" + date: 2022-10-28 + element: + uris: ['widgets.dart', 'material.dart', 'cupertino.dart'] + method: 'boldTextOverride' + inClass: 'MediaQuery' + changes: + - kind: 'rename' + newName: 'boldTextOf' + # Change made in https://github.com/flutter/flutter/pull/20649 # TODO(Piinks): Add tests when `bulkApply:false` testing is supported, https://github.com/dart-lang/sdk/issues/44639 - title: "Replace with 'CupertinoPopupSurface'" diff --git a/packages/flutter/lib/fix_data/fix_material/fix_material.yaml b/packages/flutter/lib/fix_data/fix_material/fix_material.yaml index 0359c8da87f0c..fd927f4b4d9f1 100644 --- a/packages/flutter/lib/fix_data/fix_material/fix_material.yaml +++ b/packages/flutter/lib/fix_data/fix_material/fix_material.yaml @@ -25,6 +25,17 @@ # * ThemeData: fix_theme_data.yaml version: 1 transforms: + # Changes made in https://github.com/flutter/flutter/pull/114459 + - title: "Migrate to 'boldTextOf'" + date: 2022-10-28 + element: + uris: ['widgets.dart', 'material.dart', 'cupertino.dart'] + method: 'boldTextOverride' + inClass: 'MediaQuery' + changes: + - kind: 'rename' + newName: 'boldTextOf' + # Changes made in https://github.com/flutter/flutter/pull/15303 - title: "Replace 'child' with 'builder'" date: 2020-12-17 diff --git a/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml b/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml index 47d25d49a7296..38e35c53d0e54 100644 --- a/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml +++ b/packages/flutter/lib/fix_data/fix_widgets/fix_widgets.yaml @@ -23,6 +23,17 @@ # * ListWheelScrollView: fix_list_wheel_scroll_view.yaml version: 1 transforms: + # Changes made in https://github.com/flutter/flutter/pull/114459 + - title: "Migrate to 'boldTextOf'" + date: 2022-10-28 + element: + uris: ['widgets.dart', 'material.dart', 'cupertino.dart'] + method: 'boldTextOverride' + inClass: 'MediaQuery' + changes: + - kind: 'rename' + newName: 'boldTextOf' + # Changes made in https://github.com/flutter/flutter/pull/87839 - title: "Migrate to 'disallowIndicator'" date: 2021-08-06 diff --git a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart index 4e1f18e291470..5dc70c3665c2c 100644 --- a/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart +++ b/packages/flutter/lib/src/cupertino/bottom_tab_bar.dart @@ -156,7 +156,7 @@ class CupertinoTabBar extends StatelessWidget implements PreferredSizeWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final double bottomPadding = MediaQuery.of(context).viewPadding.bottom; + final double bottomPadding = MediaQuery.viewPaddingOf(context).bottom; final Color backgroundColor = CupertinoDynamicColor.resolve( this.backgroundColor ?? CupertinoTheme.of(context).barBackgroundColor, diff --git a/packages/flutter/lib/src/cupertino/colors.dart b/packages/flutter/lib/src/cupertino/colors.dart index 41fe80ea0fd14..06178b921a824 100644 --- a/packages/flutter/lib/src/cupertino/colors.dart +++ b/packages/flutter/lib/src/cupertino/colors.dart @@ -994,7 +994,7 @@ class CupertinoDynamicColor extends Color with Diagnosticable { } bool isHighContrastEnabled = false; if (_isHighContrastDependent) { - isHighContrastEnabled = MediaQuery.maybeOf(context)?.highContrast ?? false; + isHighContrastEnabled = MediaQuery.maybeHighContrastOf(context) ?? false; } final CupertinoUserInterfaceLevelData level = _isInterfaceElevationDependent diff --git a/packages/flutter/lib/src/cupertino/context_menu.dart b/packages/flutter/lib/src/cupertino/context_menu.dart index 87dafbcf7c5b6..9e673355c25a8 100644 --- a/packages/flutter/lib/src/cupertino/context_menu.dart +++ b/packages/flutter/lib/src/cupertino/context_menu.dart @@ -493,7 +493,7 @@ class _CupertinoContextMenuState extends State with Ticker // it. _ContextMenuLocation get _contextMenuLocation { final Rect childRect = _getRect(_childGlobalKey); - final double screenWidth = MediaQuery.of(context).size.width; + final double screenWidth = MediaQuery.sizeOf(context).width; final double center = screenWidth / 2; final bool centerDividesChild = childRect.left < center @@ -1311,7 +1311,7 @@ class _ContextMenuRouteStaticState extends State<_ContextMenuRouteStatic> with T Widget _buildChildAnimation(BuildContext context, Widget? child) { _lastScale = _getScale( widget.orientation, - MediaQuery.of(context).size.height, + MediaQuery.sizeOf(context).height, _moveAnimation.value.dy, ); return Transform.scale( diff --git a/packages/flutter/lib/src/cupertino/desktop_text_selection.dart b/packages/flutter/lib/src/cupertino/desktop_text_selection.dart index 88b9d9b3f7c66..2f053f13fe446 100644 --- a/packages/flutter/lib/src/cupertino/desktop_text_selection.dart +++ b/packages/flutter/lib/src/cupertino/desktop_text_selection.dart @@ -158,12 +158,12 @@ class _CupertinoDesktopTextSelectionControlsToolbarState extends State<_Cupertin } assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); + final EdgeInsets mediaQueryPadding = MediaQuery.paddingOf(context); final Offset midpointAnchor = Offset( clampDouble(widget.selectionMidpoint.dx - widget.globalEditableRegion.left, - mediaQuery.padding.left, - mediaQuery.size.width - mediaQuery.padding.right, + mediaQueryPadding.left, + MediaQuery.sizeOf(context).width - mediaQueryPadding.right, ), widget.selectionMidpoint.dy - widget.globalEditableRegion.top, ); @@ -171,7 +171,7 @@ class _CupertinoDesktopTextSelectionControlsToolbarState extends State<_Cupertin final List items = []; final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); final Widget onePhysicalPixelVerticalDivider = - SizedBox(width: 1.0 / MediaQuery.of(context).devicePixelRatio); + SizedBox(width: 1.0 / MediaQuery.devicePixelRatioOf(context)); void addToolbarButton( String text, diff --git a/packages/flutter/lib/src/cupertino/desktop_text_selection_toolbar.dart b/packages/flutter/lib/src/cupertino/desktop_text_selection_toolbar.dart index 990076500eefd..5a586c80b2907 100644 --- a/packages/flutter/lib/src/cupertino/desktop_text_selection_toolbar.dart +++ b/packages/flutter/lib/src/cupertino/desktop_text_selection_toolbar.dart @@ -85,9 +85,8 @@ class CupertinoDesktopTextSelectionToolbar extends StatelessWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); - final double paddingAbove = mediaQuery.padding.top + _kToolbarScreenPadding; + final double paddingAbove = MediaQuery.paddingOf(context).top + _kToolbarScreenPadding; final Offset localAdjustment = Offset(_kToolbarScreenPadding, paddingAbove); return Padding( diff --git a/packages/flutter/lib/src/cupertino/dialog.dart b/packages/flutter/lib/src/cupertino/dialog.dart index 0a7286bb70d05..ddd491caef058 100644 --- a/packages/flutter/lib/src/cupertino/dialog.dart +++ b/packages/flutter/lib/src/cupertino/dialog.dart @@ -149,8 +149,8 @@ const double _kMaxRegularTextScaleFactor = 1.4; // Accessibility mode on iOS is determined by the text scale factor that the // user has selected. bool _isInAccessibilityMode(BuildContext context) { - final MediaQueryData? data = MediaQuery.maybeOf(context); - return data != null && data.textScaleFactor > _kMaxRegularTextScaleFactor; + final double? factor = MediaQuery.maybeTextScaleFactorOf(context); + return factor != null && factor > _kMaxRegularTextScaleFactor; } /// An iOS-style alert dialog. @@ -257,7 +257,7 @@ class CupertinoAlertDialog extends StatelessWidget { final Curve insetAnimationCurve; Widget _buildContent(BuildContext context) { - final double textScaleFactor = MediaQuery.of(context).textScaleFactor; + final double textScaleFactor = MediaQuery.textScaleFactorOf(context); final List children = [ if (title != null || content != null) @@ -317,7 +317,7 @@ class CupertinoAlertDialog extends StatelessWidget { Widget build(BuildContext context) { final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); final bool isInAccessibilityMode = _isInAccessibilityMode(context); - final double textScaleFactor = MediaQuery.of(context).textScaleFactor; + final double textScaleFactor = MediaQuery.textScaleFactorOf(context); return CupertinoUserInterfaceLevel( data: CupertinoUserInterfaceLevelData.elevated, child: MediaQuery( @@ -331,7 +331,7 @@ class CupertinoAlertDialog extends StatelessWidget { child: LayoutBuilder( builder: (BuildContext context, BoxConstraints constraints) { return AnimatedPadding( - padding: MediaQuery.of(context).viewInsets + + padding: MediaQuery.viewInsetsOf(context) + const EdgeInsets.symmetric(horizontal: 40.0, vertical: 24.0), duration: insetAnimationDuration, curve: insetAnimationCurve, @@ -611,12 +611,12 @@ class CupertinoActionSheet extends StatelessWidget { if (cancelButton != null) _buildCancelButton(), ]; - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); final double actionSheetWidth; if (orientation == Orientation.portrait) { - actionSheetWidth = MediaQuery.of(context).size.width - (_kActionSheetEdgeHorizontalPadding * 2); + actionSheetWidth = MediaQuery.sizeOf(context).width - (_kActionSheetEdgeHorizontalPadding * 2); } else { - actionSheetWidth = MediaQuery.of(context).size.height - (_kActionSheetEdgeHorizontalPadding * 2); + actionSheetWidth = MediaQuery.sizeOf(context).height - (_kActionSheetEdgeHorizontalPadding * 2); } return SafeArea( @@ -797,7 +797,7 @@ class _CupertinoDialogRenderWidget extends RenderObjectWidget { @override RenderObject createRenderObject(BuildContext context) { return _RenderCupertinoDialog( - dividerThickness: _kDividerThickness / MediaQuery.of(context).devicePixelRatio, + dividerThickness: _kDividerThickness / MediaQuery.devicePixelRatioOf(context), isInAccessibilityMode: _isInAccessibilityMode(context) && !isActionSheet, dividerColor: CupertinoDynamicColor.resolve(dividerColor, context), isActionSheet: isActionSheet, @@ -1464,7 +1464,7 @@ class _CupertinoAlertActionSection extends StatelessWidget { @override Widget build(BuildContext context) { - final double devicePixelRatio = MediaQuery.of(context).devicePixelRatio; + final double devicePixelRatio = MediaQuery.devicePixelRatioOf(context); final List interactiveButtons = []; for (int i = 0; i < children.length; i += 1) { diff --git a/packages/flutter/lib/src/cupertino/list_section.dart b/packages/flutter/lib/src/cupertino/list_section.dart index bdcb445c86143..84cac8bb01cf8 100644 --- a/packages/flutter/lib/src/cupertino/list_section.dart +++ b/packages/flutter/lib/src/cupertino/list_section.dart @@ -347,7 +347,7 @@ class CupertinoListSection extends StatelessWidget { @override Widget build(BuildContext context) { final Color dividerColor = CupertinoColors.separator.resolveFrom(context); - final double dividerHeight = 1.0 / MediaQuery.of(context).devicePixelRatio; + final double dividerHeight = 1.0 / MediaQuery.devicePixelRatioOf(context); // Long divider is used for wrapping the top and bottom of rows. // Only used in CupertinoListSectionType.base mode. diff --git a/packages/flutter/lib/src/cupertino/magnifier.dart b/packages/flutter/lib/src/cupertino/magnifier.dart index c7b4b6d54c858..e5e1075170fae 100644 --- a/packages/flutter/lib/src/cupertino/magnifier.dart +++ b/packages/flutter/lib/src/cupertino/magnifier.dart @@ -174,7 +174,7 @@ class _CupertinoTextMagnifierState extends State CupertinoMagnifier.kMagnifierAboveFocalPoint), ); - final Rect screenRect = Offset.zero & MediaQuery.of(context).size; + final Rect screenRect = Offset.zero & MediaQuery.sizeOf(context); // Adjust the magnifier position so that it never exists outside the horizontal // padding. diff --git a/packages/flutter/lib/src/cupertino/nav_bar.dart b/packages/flutter/lib/src/cupertino/nav_bar.dart index d7cfcff1c8064..976366d8d285a 100644 --- a/packages/flutter/lib/src/cupertino/nav_bar.dart +++ b/packages/flutter/lib/src/cupertino/nav_bar.dart @@ -756,7 +756,7 @@ class _CupertinoSliverNavigationBarState extends State extends State<_CupertinoBackGestureD // For devices with notches, the drag area needs to be larger on the side // that has the notch. double dragAreaWidth = Directionality.of(context) == TextDirection.ltr ? - MediaQuery.of(context).padding.left : - MediaQuery.of(context).padding.right; + MediaQuery.paddingOf(context).left : + MediaQuery.paddingOf(context).right; dragAreaWidth = max(dragAreaWidth, _kBackGestureWidth); return Stack( fit: StackFit.passthrough, diff --git a/packages/flutter/lib/src/cupertino/scrollbar.dart b/packages/flutter/lib/src/cupertino/scrollbar.dart index b0112ef7d243c..34109290f12d4 100644 --- a/packages/flutter/lib/src/cupertino/scrollbar.dart +++ b/packages/flutter/lib/src/cupertino/scrollbar.dart @@ -171,7 +171,7 @@ class _CupertinoScrollbarState extends RawScrollbarState { ..mainAxisMargin = _kScrollbarMainAxisMargin ..crossAxisMargin = _kScrollbarCrossAxisMargin ..radius = _radius - ..padding = MediaQuery.of(context).padding + ..padding = MediaQuery.paddingOf(context) ..minLength = _kScrollbarMinLength ..minOverscrollLength = _kScrollbarMinOverscrollLength ..scrollbarOrientation = widget.scrollbarOrientation; diff --git a/packages/flutter/lib/src/cupertino/text_field.dart b/packages/flutter/lib/src/cupertino/text_field.dart index 8fad5ea9f3e8d..977b9df22ecd8 100644 --- a/packages/flutter/lib/src/cupertino/text_field.dart +++ b/packages/flutter/lib/src/cupertino/text_field.dart @@ -1224,7 +1224,7 @@ class _CupertinoTextFieldState extends State with Restoratio } final bool enabled = widget.enabled ?? true; - final Offset cursorOffset = Offset(_iOSHorizontalCursorOffsetPixels / MediaQuery.of(context).devicePixelRatio, 0); + final Offset cursorOffset = Offset(_iOSHorizontalCursorOffsetPixels / MediaQuery.devicePixelRatioOf(context), 0); final List formatters = [ ...?widget.inputFormatters, if (widget.maxLength != null) diff --git a/packages/flutter/lib/src/cupertino/text_selection.dart b/packages/flutter/lib/src/cupertino/text_selection.dart index 512ec71e78890..b70848dadfde9 100644 --- a/packages/flutter/lib/src/cupertino/text_selection.dart +++ b/packages/flutter/lib/src/cupertino/text_selection.dart @@ -260,14 +260,14 @@ class _CupertinoTextSelectionControlsToolbarState extends State<_CupertinoTextSe } assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); + final EdgeInsets mediaQueryPadding = MediaQuery.paddingOf(context); // The toolbar should appear below the TextField when there is not enough // space above the TextField to show it, assuming there's always enough // space at the bottom in this case. final double anchorX = clampDouble(widget.selectionMidpoint.dx + widget.globalEditableRegion.left, - _kArrowScreenPadding + mediaQuery.padding.left, - mediaQuery.size.width - mediaQuery.padding.right - _kArrowScreenPadding, + _kArrowScreenPadding + mediaQueryPadding.left, + MediaQuery.sizeOf(context).width - mediaQueryPadding.right - _kArrowScreenPadding, ); final double topAmountInEditableRegion = widget.endpoints.first.point.dy - widget.textLineHeight; @@ -289,7 +289,7 @@ class _CupertinoTextSelectionControlsToolbarState extends State<_CupertinoTextSe final List items = []; final CupertinoLocalizations localizations = CupertinoLocalizations.of(context); final Widget onePhysicalPixelVerticalDivider = - SizedBox(width: 1.0 / MediaQuery.of(context).devicePixelRatio); + SizedBox(width: 1.0 / MediaQuery.devicePixelRatioOf(context)); void addToolbarButton( String text, diff --git a/packages/flutter/lib/src/cupertino/text_selection_toolbar.dart b/packages/flutter/lib/src/cupertino/text_selection_toolbar.dart index a630b3dddf97b..d0fee95579512 100644 --- a/packages/flutter/lib/src/cupertino/text_selection_toolbar.dart +++ b/packages/flutter/lib/src/cupertino/text_selection_toolbar.dart @@ -53,7 +53,7 @@ typedef CupertinoToolbarBuilder = Widget Function( class _CupertinoToolbarButtonDivider extends StatelessWidget { @override Widget build(BuildContext context) { - return SizedBox(width: 1.0 / MediaQuery.of(context).devicePixelRatio); + return SizedBox(width: 1.0 / MediaQuery.devicePixelRatioOf(context)); } } @@ -132,9 +132,9 @@ class CupertinoTextSelectionToolbar extends StatelessWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); + final EdgeInsets mediaQueryPadding = MediaQuery.paddingOf(context); - final double paddingAbove = mediaQuery.padding.top + _kToolbarScreenPadding; + final double paddingAbove = mediaQueryPadding.top + _kToolbarScreenPadding; final double toolbarHeightNeeded = paddingAbove + _kToolbarContentDistance + _kToolbarHeight; @@ -142,8 +142,8 @@ class CupertinoTextSelectionToolbar extends StatelessWidget { // The arrow, which points to the anchor, has some margin so it can't get // too close to the horizontal edges of the screen. - final double leftMargin = _kArrowScreenPadding + mediaQuery.padding.left; - final double rightMargin = mediaQuery.size.width - mediaQuery.padding.right - _kArrowScreenPadding; + final double leftMargin = _kArrowScreenPadding + mediaQueryPadding.left; + final double rightMargin = MediaQuery.sizeOf(context).width - mediaQueryPadding.right - _kArrowScreenPadding; final Offset anchorAboveAdjusted = Offset( clampDouble(anchorAbove.dx, leftMargin, rightMargin), @@ -480,7 +480,7 @@ class _CupertinoTextSelectionToolbarContentState extends State<_CupertinoTextSel onPressed: _handlePreviousPage, text: '◀', ), - dividerWidth: 1.0 / MediaQuery.of(context).devicePixelRatio, + dividerWidth: 1.0 / MediaQuery.devicePixelRatioOf(context), nextButton: CupertinoTextSelectionToolbarButton.text( onPressed: _handleNextPage, text: '▶', diff --git a/packages/flutter/lib/src/cupertino/theme.dart b/packages/flutter/lib/src/cupertino/theme.dart index 3d8d493cc038a..57e080421094f 100644 --- a/packages/flutter/lib/src/cupertino/theme.dart +++ b/packages/flutter/lib/src/cupertino/theme.dart @@ -87,7 +87,7 @@ class CupertinoTheme extends StatelessWidget { /// [MediaQueryData.platformBrightness] for descendant Cupertino widgets. static Brightness brightnessOf(BuildContext context) { final _InheritedCupertinoTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedCupertinoTheme>(); - return inheritedTheme?.theme.data.brightness ?? MediaQuery.of(context).platformBrightness; + return inheritedTheme?.theme.data.brightness ?? MediaQuery.platformBrightnessOf(context); } /// Retrieves the [Brightness] to use for descendant Cupertino widgets, based @@ -107,7 +107,7 @@ class CupertinoTheme extends StatelessWidget { /// [MediaQuery] exists, instead of returning null. static Brightness? maybeBrightnessOf(BuildContext context) { final _InheritedCupertinoTheme? inheritedTheme = context.dependOnInheritedWidgetOfExactType<_InheritedCupertinoTheme>(); - return inheritedTheme?.theme.data.brightness ?? MediaQuery.maybeOf(context)?.platformBrightness; + return inheritedTheme?.theme.data.brightness ?? MediaQuery.maybePlatformBrightnessOf(context); } /// The widget below this widget in the tree. diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart index 4378f24a546a9..1bab4e8a1a050 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart @@ -976,7 +976,7 @@ const double _wideGutterSize = 24.0; const double _narrowGutterSize = 12.0; double _getGutterSize(BuildContext context) => - MediaQuery.of(context).size.width >= _materialGutterThreshold ? _wideGutterSize : _narrowGutterSize; + MediaQuery.sizeOf(context).width >= _materialGutterThreshold ? _wideGutterSize : _narrowGutterSize; /// Signature for the builder callback used by [_MasterDetailFlow]. typedef _MasterViewBuilder = Widget Function(BuildContext context, bool isLateralUI); @@ -1463,7 +1463,7 @@ class _DetailView extends StatelessWidget { if (_arguments == null) { return const SizedBox.shrink(); } - final double screenHeight = MediaQuery.of(context).size.height; + final double screenHeight = MediaQuery.sizeOf(context).height; final double minHeight = (screenHeight - kToolbarHeight) / screenHeight; return DraggableScrollableSheet( diff --git a/packages/flutter/lib/src/material/action_chip.dart b/packages/flutter/lib/src/material/action_chip.dart index 3cada585951d4..eaa09fd9f8c45 100644 --- a/packages/flutter/lib/src/material/action_chip.dart +++ b/packages/flutter/lib/src/material/action_chip.dart @@ -239,7 +239,7 @@ class _ActionChipDefaultsM3 extends ChipThemeData { EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp( const EdgeInsets.symmetric(horizontal: 8.0), const EdgeInsets.symmetric(horizontal: 4.0), - clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0), + clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0), )!; } diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index 79d6257b19305..781da031627c4 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -2097,7 +2097,7 @@ class _SliverAppBarState extends State with TickerProviderStateMix Widget build(BuildContext context) { assert(!widget.primary || debugCheckHasMediaQuery(context)); final double bottomHeight = widget.bottom?.preferredSize.height ?? 0.0; - final double topPadding = widget.primary ? MediaQuery.of(context).padding.top : 0.0; + final double topPadding = widget.primary ? MediaQuery.paddingOf(context).top : 0.0; final double collapsedHeight = (widget.pinned && widget.floating && widget.bottom != null) ? (widget.collapsedHeight ?? 0.0) + bottomHeight + topPadding : (widget.collapsedHeight ?? widget.toolbarHeight) + bottomHeight + topPadding; @@ -2211,7 +2211,7 @@ class _ScrollUnderFlexibleSpace extends StatelessWidget { Widget build(BuildContext context) { late final ThemeData theme = Theme.of(context); final FlexibleSpaceBarSettings settings = context.dependOnInheritedWidgetOfExactType()!; - final double topPadding = primary ? MediaQuery.of(context).viewPadding.top : 0; + final double topPadding = primary ? MediaQuery.viewPaddingOf(context).top : 0; final double collapsedHeight = settings.minExtent - topPadding; final double scrollUnderHeight = settings.maxExtent - settings.minExtent; final _ScrollUnderFlexibleConfig config; diff --git a/packages/flutter/lib/src/material/banner.dart b/packages/flutter/lib/src/material/banner.dart index a5ca4b96dae25..245ba2ffbd7eb 100644 --- a/packages/flutter/lib/src/material/banner.dart +++ b/packages/flutter/lib/src/material/banner.dart @@ -293,7 +293,7 @@ class _MaterialBannerState extends State { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQueryData = MediaQuery.of(context); + final bool accessibleNavigation = MediaQuery.accessibleNavigationOf(context); assert(widget.actions.isNotEmpty); @@ -399,7 +399,7 @@ class _MaterialBannerState extends State { onDismiss: () { ScaffoldMessenger.of(context).removeCurrentMaterialBanner(reason: MaterialBannerClosedReason.dismiss); }, - child: mediaQueryData.accessibleNavigation + child: accessibleNavigation ? materialBanner : SlideTransition( position: slideOutAnimation, @@ -408,7 +408,7 @@ class _MaterialBannerState extends State { ); final Widget materialBannerTransition; - if (mediaQueryData.accessibleNavigation) { + if (accessibleNavigation) { materialBannerTransition = materialBanner; } else { materialBannerTransition = AnimatedBuilder( diff --git a/packages/flutter/lib/src/material/bottom_navigation_bar.dart b/packages/flutter/lib/src/material/bottom_navigation_bar.dart index 9b027b11d9c8b..eb5557bd898d5 100644 --- a/packages/flutter/lib/src/material/bottom_navigation_bar.dart +++ b/packages/flutter/lib/src/material/bottom_navigation_bar.dart @@ -607,8 +607,7 @@ class _Tile extends StatelessWidget { @override Widget build(BuildContext context) { - final MediaQueryData data = MediaQuery.of(context); - if (data.orientation == Orientation.landscape && layout == BottomNavigationBarLandscapeLayout.linear) { + if (MediaQuery.orientationOf(context) == Orientation.landscape && layout == BottomNavigationBarLandscapeLayout.linear) { return Align( heightFactor: 1, child: Row( @@ -1114,7 +1113,7 @@ class _BottomNavigationBarState extends State with TickerPr final BottomNavigationBarLandscapeLayout layout = widget.landscapeLayout ?? bottomTheme.landscapeLayout ?? BottomNavigationBarLandscapeLayout.spread; - final double additionalBottomPadding = MediaQuery.of(context).viewPadding.bottom; + final double additionalBottomPadding = MediaQuery.viewPaddingOf(context).bottom; Color? backgroundColor; switch (_effectiveType) { @@ -1180,14 +1179,13 @@ class _Bar extends StatelessWidget { @override Widget build(BuildContext context) { - final MediaQueryData data = MediaQuery.of(context); Widget alignedChild = child; - if (data.orientation == Orientation.landscape && layout == BottomNavigationBarLandscapeLayout.centered) { + if (MediaQuery.orientationOf(context) == Orientation.landscape && layout == BottomNavigationBarLandscapeLayout.centered) { alignedChild = Align( alignment: Alignment.bottomCenter, heightFactor: 1, child: SizedBox( - width: data.size.height, + width: MediaQuery.sizeOf(context).height, child: child, ), ); diff --git a/packages/flutter/lib/src/material/bottom_sheet.dart b/packages/flutter/lib/src/material/bottom_sheet.dart index e5995b7fe9c61..fb32eb4fed3b4 100644 --- a/packages/flutter/lib/src/material/bottom_sheet.dart +++ b/packages/flutter/lib/src/material/bottom_sheet.dart @@ -409,7 +409,6 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> { Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasMaterialLocalizations(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String routeLabel = _getRouteLabel(localizations); @@ -436,7 +435,7 @@ class _ModalBottomSheetState extends State<_ModalBottomSheet> { // Disable the initial animation when accessible navigation is on so // that the semantics are added to the tree at the correct time. final double animationValue = animationCurve.transform( - mediaQuery.accessibleNavigation ? 1.0 : widget.route.animation!.value, + MediaQuery.accessibleNavigationOf(context) ? 1.0 : widget.route.animation!.value, ); return Semantics( scopesRoute: true, diff --git a/packages/flutter/lib/src/material/chip.dart b/packages/flutter/lib/src/material/chip.dart index 6179e281bae02..30a2e97b985c5 100644 --- a/packages/flutter/lib/src/material/chip.dart +++ b/packages/flutter/lib/src/material/chip.dart @@ -1158,7 +1158,7 @@ class _RawChipState extends State with MaterialStateMixin, TickerProvid final EdgeInsetsGeometry defaultLabelPadding = EdgeInsets.lerp( const EdgeInsets.symmetric(horizontal: 8.0), const EdgeInsets.symmetric(horizontal: 4.0), - clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0), + clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0), )!; final ThemeData theme = Theme.of(context); @@ -2257,7 +2257,7 @@ class _ChipDefaultsM3 extends ChipThemeData { EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp( const EdgeInsets.symmetric(horizontal: 8.0), const EdgeInsets.symmetric(horizontal: 4.0), - clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0), + clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0), )!; } diff --git a/packages/flutter/lib/src/material/choice_chip.dart b/packages/flutter/lib/src/material/choice_chip.dart index 9111c2c9b2682..4514d7f6ed391 100644 --- a/packages/flutter/lib/src/material/choice_chip.dart +++ b/packages/flutter/lib/src/material/choice_chip.dart @@ -259,7 +259,7 @@ class _ChoiceChipDefaultsM3 extends ChipThemeData { EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp( const EdgeInsets.symmetric(horizontal: 8.0), const EdgeInsets.symmetric(horizontal: 4.0), - clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0), + clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0), )!; } diff --git a/packages/flutter/lib/src/material/date_picker.dart b/packages/flutter/lib/src/material/date_picker.dart index 724b72b581b28..65ae2fc1ac2d5 100644 --- a/packages/flutter/lib/src/material/date_picker.dart +++ b/packages/flutter/lib/src/material/date_picker.dart @@ -425,7 +425,7 @@ class _DatePickerDialogState extends State with RestorationMix } Size _dialogSize(BuildContext context) { - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); switch (_entryMode.value) { case DatePickerEntryMode.calendar: case DatePickerEntryMode.calendarOnly: @@ -456,11 +456,11 @@ class _DatePickerDialogState extends State with RestorationMix final ThemeData theme = Theme.of(context); final ColorScheme colorScheme = theme.colorScheme; final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); final TextTheme textTheme = theme.textTheme; // Constrain the textScaleFactor to the largest supported value to prevent // layout issues. - final double textScaleFactor = math.min(MediaQuery.of(context).textScaleFactor, 1.3); + final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.3); final String dateText = localizations.formatMediumDate(_selectedDate.value); final Color onPrimarySurface = colorScheme.brightness == Brightness.light @@ -1316,9 +1316,8 @@ class _DateRangePickerDialogState extends State with Rest @override Widget build(BuildContext context) { - final MediaQueryData mediaQuery = MediaQuery.of(context); - final Orientation orientation = mediaQuery.orientation; - final double textScaleFactor = math.min(mediaQuery.textScaleFactor, 1.3); + final Orientation orientation = MediaQuery.orientationOf(context); + final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.3); final MaterialLocalizations localizations = MaterialLocalizations.of(context); final ColorScheme colors = Theme.of(context).colorScheme; final Color onPrimarySurface = colors.brightness == Brightness.light @@ -1367,7 +1366,7 @@ class _DateRangePickerDialogState extends State with Rest : localizations.dateRangePickerHelpText.toUpperCase() ), ); - size = mediaQuery.size; + size = MediaQuery.sizeOf(context); insetPadding = EdgeInsets.zero; shape = const RoundedRectangleBorder(); elevation = 0; @@ -1499,7 +1498,7 @@ class _CalendarRangePickerDialog extends StatelessWidget { final ThemeData theme = Theme.of(context); final ColorScheme colorScheme = theme.colorScheme; final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); final TextTheme textTheme = theme.textTheme; final Color headerForeground = colorScheme.brightness == Brightness.light ? colorScheme.onPrimary @@ -1539,7 +1538,7 @@ class _CalendarRangePickerDialog extends StatelessWidget { bottom: PreferredSize( preferredSize: const Size(double.infinity, 64), child: Row(children: [ - SizedBox(width: MediaQuery.of(context).size.width < 360 ? 42 : 72), + SizedBox(width: MediaQuery.sizeOf(context).width < 360 ? 42 : 72), Expanded( child: Semantics( label: '$helpText $startDateText to $endDateText', @@ -2017,7 +2016,7 @@ class _DayHeaders extends StatelessWidget { return Container( constraints: BoxConstraints( - maxWidth: MediaQuery.of(context).orientation == Orientation.landscape + maxWidth: MediaQuery.orientationOf(context) == Orientation.landscape ? _maxCalendarWidthLandscape : _maxCalendarWidthPortrait, maxHeight: _monthItemRowHeight, @@ -2469,7 +2468,7 @@ class _MonthItemState extends State<_MonthItem> { paddedDayItems.addAll(weekList); } - final double maxWidth = MediaQuery.of(context).orientation == Orientation.landscape + final double maxWidth = MediaQuery.orientationOf(context) == Orientation.landscape ? _maxCalendarWidthLandscape : _maxCalendarWidthPortrait; return Column( @@ -2623,7 +2622,7 @@ class _InputDateRangePickerDialog extends StatelessWidget { final ThemeData theme = Theme.of(context); final ColorScheme colorScheme = theme.colorScheme; final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); final TextTheme textTheme = theme.textTheme; final Color onPrimarySurfaceColor = colorScheme.brightness == Brightness.light diff --git a/packages/flutter/lib/src/material/desktop_text_selection.dart b/packages/flutter/lib/src/material/desktop_text_selection.dart index b67c371f8f5a1..f2b029dd111e1 100644 --- a/packages/flutter/lib/src/material/desktop_text_selection.dart +++ b/packages/flutter/lib/src/material/desktop_text_selection.dart @@ -177,12 +177,11 @@ class _DesktopTextSelectionControlsToolbarState extends State<_DesktopTextSelect return const SizedBox.shrink(); } - final MediaQueryData mediaQuery = MediaQuery.of(context); - + final EdgeInsets mediaQueryPadding = MediaQuery.paddingOf(context); final Offset midpointAnchor = Offset( clampDouble(widget.selectionMidpoint.dx - widget.globalEditableRegion.left, - mediaQuery.padding.left, - mediaQuery.size.width - mediaQuery.padding.right, + mediaQueryPadding.left, + MediaQuery.sizeOf(context).width - mediaQueryPadding.right, ), widget.selectionMidpoint.dy - widget.globalEditableRegion.top, ); diff --git a/packages/flutter/lib/src/material/desktop_text_selection_toolbar.dart b/packages/flutter/lib/src/material/desktop_text_selection_toolbar.dart index e1dbfe679a16c..df69b45560011 100644 --- a/packages/flutter/lib/src/material/desktop_text_selection_toolbar.dart +++ b/packages/flutter/lib/src/material/desktop_text_selection_toolbar.dart @@ -64,9 +64,8 @@ class DesktopTextSelectionToolbar extends StatelessWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); - final double paddingAbove = mediaQuery.padding.top + _kToolbarScreenPadding; + final double paddingAbove = MediaQuery.paddingOf(context).top + _kToolbarScreenPadding; final Offset localAdjustment = Offset(_kToolbarScreenPadding, paddingAbove); return Padding( diff --git a/packages/flutter/lib/src/material/dialog.dart b/packages/flutter/lib/src/material/dialog.dart index 4204fac8c896d..95ab97582352e 100644 --- a/packages/flutter/lib/src/material/dialog.dart +++ b/packages/flutter/lib/src/material/dialog.dart @@ -217,7 +217,7 @@ class Dialog extends StatelessWidget { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final DialogTheme dialogTheme = DialogTheme.of(context); - final EdgeInsets effectivePadding = MediaQuery.of(context).viewInsets + (insetPadding ?? EdgeInsets.zero); + final EdgeInsets effectivePadding = MediaQuery.viewInsetsOf(context) + (insetPadding ?? EdgeInsets.zero); final DialogTheme defaults = theme.useMaterial3 ? (_fullscreen ? _DialogFullscreenDefaultsM3(context) : _DialogDefaultsM3(context)) : _DialogDefaultsM2(context); @@ -642,7 +642,7 @@ class AlertDialog extends StatelessWidget { // The paddingScaleFactor is used to adjust the padding of Dialog's // children. - final double paddingScaleFactor = _paddingScaleFactor(MediaQuery.of(context).textScaleFactor); + final double paddingScaleFactor = _paddingScaleFactor(MediaQuery.textScaleFactorOf(context)); final TextDirection? textDirection = Directionality.maybeOf(context); Widget? iconWidget; @@ -1075,7 +1075,7 @@ class SimpleDialog extends StatelessWidget { // The paddingScaleFactor is used to adjust the padding of Dialog // children. - final double paddingScaleFactor = _paddingScaleFactor(MediaQuery.of(context).textScaleFactor); + final double paddingScaleFactor = _paddingScaleFactor(MediaQuery.textScaleFactorOf(context)); final TextDirection? textDirection = Directionality.maybeOf(context); Widget? titleWidget; diff --git a/packages/flutter/lib/src/material/drawer.dart b/packages/flutter/lib/src/material/drawer.dart index 2c571af2b4cd0..c1d3ea3b5d5e4 100644 --- a/packages/flutter/lib/src/material/drawer.dart +++ b/packages/flutter/lib/src/material/drawer.dart @@ -382,11 +382,11 @@ class DrawerController extends StatefulWidget { /// drawer. /// /// By default, the value used is 20.0 added to the padding edge of - /// `MediaQuery.of(context).padding` that corresponds to [alignment]. + /// `MediaQuery.paddingOf(context)` that corresponds to [alignment]. /// This ensures that the drag area for notched devices is not obscured. For /// example, if [alignment] is set to [DrawerAlignment.start] and /// `TextDirection.of(context)` is set to [TextDirection.ltr], - /// 20.0 will be added to `MediaQuery.of(context).padding.left`. + /// 20.0 will be added to `MediaQuery.paddingOf(context).left`. final double? edgeDragWidth; /// Whether or not the drawer is opened or closed. @@ -680,7 +680,6 @@ class DrawerControllerState extends State with SingleTickerPro Widget _buildDrawer(BuildContext context) { final bool drawerIsStart = widget.alignment == DrawerAlignment.start; - final EdgeInsets padding = MediaQuery.of(context).padding; final TextDirection textDirection = Directionality.of(context); final bool isDesktop; switch (Theme.of(context).platform) { @@ -698,6 +697,7 @@ class DrawerControllerState extends State with SingleTickerPro double? dragAreaWidth = widget.edgeDragWidth; if (widget.edgeDragWidth == null) { + final EdgeInsets padding = MediaQuery.paddingOf(context); switch (textDirection) { case TextDirection.ltr: dragAreaWidth = _kEdgeDragWidth + diff --git a/packages/flutter/lib/src/material/drawer_header.dart b/packages/flutter/lib/src/material/drawer_header.dart index bff2c267d87ce..3b579d1f5bcd5 100644 --- a/packages/flutter/lib/src/material/drawer_header.dart +++ b/packages/flutter/lib/src/material/drawer_header.dart @@ -76,7 +76,7 @@ class DrawerHeader extends StatelessWidget { assert(debugCheckHasMaterial(context)); assert(debugCheckHasMediaQuery(context)); final ThemeData theme = Theme.of(context); - final double statusBarHeight = MediaQuery.of(context).padding.top; + final double statusBarHeight = MediaQuery.paddingOf(context).top; return Container( height: statusBarHeight + _kDrawerHeaderHeight, margin: margin, diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 6ff22a186145f..9becd8def0b14 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -1333,7 +1333,7 @@ class _DropdownButtonState extends State> with WidgetsBindi // Similarly, we don't reduce the height of the button so much that its icon // would be clipped. double get _denseButtonHeight { - final double textScaleFactor = MediaQuery.of(context).textScaleFactor; + final double textScaleFactor = MediaQuery.textScaleFactorOf(context); final double fontSize = _textStyle!.fontSize ?? Theme.of(context).textTheme.titleMedium!.fontSize!; final double scaledFontSize = textScaleFactor * fontSize; return math.max(scaledFontSize, math.max(widget.iconSize, _kDenseButtonHeight)); @@ -1369,7 +1369,7 @@ class _DropdownButtonState extends State> with WidgetsBindi bool get _enabled => widget.items != null && widget.items!.isNotEmpty && widget.onChanged != null; Orientation _getOrientation(BuildContext context) { - Orientation? result = MediaQuery.maybeOf(context)?.orientation; + Orientation? result = MediaQuery.maybeOrientationOf(context); if (result == null) { // If there's no MediaQuery, then use the window aspect to determine // orientation. diff --git a/packages/flutter/lib/src/material/elevated_button.dart b/packages/flutter/lib/src/material/elevated_button.dart index 60bb0d35d8182..cb98168e25215 100644 --- a/packages/flutter/lib/src/material/elevated_button.dart +++ b/packages/flutter/lib/src/material/elevated_button.dart @@ -252,7 +252,7 @@ class ElevatedButton extends ButtonStyleButton { /// each state, and "others" means all other states. /// /// The `textScaleFactor` is the value of - /// `MediaQuery.of(context).textScaleFactor` and the names of the + /// `MediaQuery.textScaleFactorOf(context)` and the names of the /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been /// abbreviated for readability. /// @@ -394,7 +394,7 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) { const EdgeInsets.symmetric(horizontal: 16), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 4), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); } @@ -500,7 +500,7 @@ class _ElevatedButtonWithIcon extends ElevatedButton { const EdgeInsetsDirectional.fromSTEB(12, 0, 16, 0), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsetsDirectional.fromSTEB(8, 0, 4, 0), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); return super.defaultStyleOf(context).copyWith( padding: MaterialStatePropertyAll(scaledPadding), @@ -516,7 +516,7 @@ class _ElevatedButtonWithIconChild extends StatelessWidget { @override Widget build(BuildContext context) { - final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1; + final double scale = MediaQuery.textScaleFactorOf(context); final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!; return Row( mainAxisSize: MainAxisSize.min, diff --git a/packages/flutter/lib/src/material/filled_button.dart b/packages/flutter/lib/src/material/filled_button.dart index 5b7662247f907..fa9793a6de29f 100644 --- a/packages/flutter/lib/src/material/filled_button.dart +++ b/packages/flutter/lib/src/material/filled_button.dart @@ -288,7 +288,7 @@ class FilledButton extends ButtonStyleButton { /// each state, and "others" means all other states. /// /// The `textScaleFactor` is the value of - /// `MediaQuery.of(context).textScaleFactor` and the names of the + /// `MediaQuery.textScaleFactorOf(context)` and the names of the /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been /// abbreviated for readability. /// @@ -368,7 +368,7 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) { const EdgeInsets.symmetric(horizontal: 16), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 4), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); } @@ -471,7 +471,7 @@ class _FilledButtonWithIcon extends FilledButton { const EdgeInsetsDirectional.fromSTEB(12, 0, 16, 0), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsetsDirectional.fromSTEB(8, 0, 4, 0), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); return super.defaultStyleOf(context).copyWith( padding: MaterialStatePropertyAll(scaledPadding), @@ -487,7 +487,7 @@ class _FilledButtonWithIconChild extends StatelessWidget { @override Widget build(BuildContext context) { - final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1; + final double scale = MediaQuery.textScaleFactorOf(context); // Adjust the gap based on the text scale factor. Start at 8, and lerp // to 4 based on how large the text is. final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!; diff --git a/packages/flutter/lib/src/material/filter_chip.dart b/packages/flutter/lib/src/material/filter_chip.dart index 5119f8fc63501..43440efdcc3ea 100644 --- a/packages/flutter/lib/src/material/filter_chip.dart +++ b/packages/flutter/lib/src/material/filter_chip.dart @@ -267,7 +267,7 @@ class _FilterChipDefaultsM3 extends ChipThemeData { EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp( const EdgeInsets.symmetric(horizontal: 8.0), const EdgeInsets.symmetric(horizontal: 4.0), - clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0), + clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0), )!; } diff --git a/packages/flutter/lib/src/material/ink_well.dart b/packages/flutter/lib/src/material/ink_well.dart index a5e46985d4d56..bdba4f7cf8595 100644 --- a/packages/flutter/lib/src/material/ink_well.dart +++ b/packages/flutter/lib/src/material/ink_well.dart @@ -1018,7 +1018,7 @@ class _InkResponseState extends State<_InkResponseStateWidget> } bool get _shouldShowFocus { - final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional; + final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: return enabled && _hasFocus; @@ -1166,7 +1166,7 @@ class _InkResponseState extends State<_InkResponseStateWidget> } bool get _canRequestFocus { - final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional; + final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: return enabled && widget.canRequestFocus; diff --git a/packages/flutter/lib/src/material/input_chip.dart b/packages/flutter/lib/src/material/input_chip.dart index 8efd27faa9c91..fd3b6e0d146b6 100644 --- a/packages/flutter/lib/src/material/input_chip.dart +++ b/packages/flutter/lib/src/material/input_chip.dart @@ -310,7 +310,7 @@ class _InputChipDefaultsM3 extends ChipThemeData { EdgeInsetsGeometry? get labelPadding => EdgeInsets.lerp( const EdgeInsets.symmetric(horizontal: 8.0), const EdgeInsets.symmetric(horizontal: 4.0), - clampDouble(MediaQuery.of(context).textScaleFactor - 1.0, 0.0, 1.0), + clampDouble(MediaQuery.textScaleFactorOf(context) - 1.0, 0.0, 1.0), )!; } diff --git a/packages/flutter/lib/src/material/magnifier.dart b/packages/flutter/lib/src/material/magnifier.dart index 64ab312eccfc1..ce6b960ba02b3 100644 --- a/packages/flutter/lib/src/material/magnifier.dart +++ b/packages/flutter/lib/src/material/magnifier.dart @@ -134,7 +134,7 @@ class _TextMagnifierState extends State { void _determineMagnifierPositionAndFocalPoint() { final MagnifierInfo selectionInfo = widget.magnifierInfo.value; - final Rect screenRect = Offset.zero & MediaQuery.of(context).size; + final Rect screenRect = Offset.zero & MediaQuery.sizeOf(context); // Since by default we draw at the top left corner, this offset // shifts the magnifier so we draw at the center, and then also includes diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart index 8d6958c84aa12..c479f85437183 100644 --- a/packages/flutter/lib/src/material/menu_anchor.dart +++ b/packages/flutter/lib/src/material/menu_anchor.dart @@ -315,7 +315,7 @@ class _MenuAnchorState extends State { _position?.isScrollingNotifier.removeListener(_handleScroll); _position = Scrollable.maybeOf(context)?.position; _position?.isScrollingNotifier.addListener(_handleScroll); - final Size newSize = MediaQuery.of(context).size; + final Size newSize = MediaQuery.sizeOf(context); if (_viewSize != null && newSize != _viewSize) { // Close the menus if the view changes size. _root._close(); @@ -3746,7 +3746,7 @@ class _MenuButtonDefaultsM3 extends ButtonStyle { const EdgeInsets.symmetric(horizontal: 12), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 4), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.maybeTextScaleFactorOf(context) ?? 1, ); } } diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart index 54fee797af248..e890eea375645 100644 --- a/packages/flutter/lib/src/material/navigation_bar.dart +++ b/packages/flutter/lib/src/material/navigation_bar.dart @@ -994,7 +994,7 @@ class _ClampTextScaleFactor extends StatelessWidget { Widget build(BuildContext context) { return MediaQuery( data: MediaQuery.of(context).copyWith( - textScaleFactor: clampDouble(MediaQuery.of(context).textScaleFactor, + textScaleFactor: clampDouble(MediaQuery.textScaleFactorOf(context), 0.0, upperLimit, ), diff --git a/packages/flutter/lib/src/material/outlined_button.dart b/packages/flutter/lib/src/material/outlined_button.dart index 0fb39f827e053..d60caaa75ab68 100644 --- a/packages/flutter/lib/src/material/outlined_button.dart +++ b/packages/flutter/lib/src/material/outlined_button.dart @@ -351,7 +351,7 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) { const EdgeInsets.symmetric(horizontal: 16), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 4), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); } @@ -437,7 +437,7 @@ class _OutlinedButtonWithIconChild extends StatelessWidget { @override Widget build(BuildContext context) { - final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1; + final double scale = MediaQuery.textScaleFactorOf(context); final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!; return Row( mainAxisSize: MainAxisSize.min, diff --git a/packages/flutter/lib/src/material/popup_menu.dart b/packages/flutter/lib/src/material/popup_menu.dart index a48ad18b9b9d9..0e26450526399 100644 --- a/packages/flutter/lib/src/material/popup_menu.dart +++ b/packages/flutter/lib/src/material/popup_menu.dart @@ -1292,7 +1292,7 @@ class PopupMenuButtonState extends State> { } bool get _canRequestFocus { - final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional; + final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: return widget.enabled; diff --git a/packages/flutter/lib/src/material/range_slider.dart b/packages/flutter/lib/src/material/range_slider.dart index f9fca210c7a1c..fd26eda179f5f 100644 --- a/packages/flutter/lib/src/material/range_slider.dart +++ b/packages/flutter/lib/src/material/range_slider.dart @@ -619,7 +619,7 @@ class _RangeSliderState extends State with TickerProviderStateMixin // This size is used as the max bounds for the painting of the value // indicators. It must be kept in sync with the function with the same name // in slider.dart. - Size screenSize() => MediaQuery.of(context).size; + Size screenSize() => MediaQuery.sizeOf(context); return CompositedTransformTarget( link: _layerLink, @@ -628,7 +628,7 @@ class _RangeSliderState extends State with TickerProviderStateMixin divisions: widget.divisions, labels: widget.labels, sliderTheme: sliderTheme, - textScaleFactor: MediaQuery.of(context).textScaleFactor, + textScaleFactor: MediaQuery.textScaleFactorOf(context), screenSize: screenSize(), onChanged: (widget.onChanged != null) && (widget.max > widget.min) ? _handleChanged : null, onChangeStart: widget.onChangeStart != null ? _handleDragStart : null, @@ -704,7 +704,7 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget { textDirection: Directionality.of(context), semanticFormatterCallback: semanticFormatterCallback, platform: Theme.of(context).platform, - gestureSettings: MediaQuery.of(context).gestureSettings, + gestureSettings: MediaQuery.gestureSettingsOf(context), ); } @@ -726,7 +726,7 @@ class _RangeSliderRenderObjectWidget extends LeafRenderObjectWidget { ..textDirection = Directionality.of(context) ..semanticFormatterCallback = semanticFormatterCallback ..platform = Theme.of(context).platform - ..gestureSettings = MediaQuery.of(context).gestureSettings; + ..gestureSettings = MediaQuery.gestureSettingsOf(context); } } diff --git a/packages/flutter/lib/src/material/scaffold.dart b/packages/flutter/lib/src/material/scaffold.dart index 650c0c12e5ed3..3e6c02b8ac7e7 100644 --- a/packages/flutter/lib/src/material/scaffold.dart +++ b/packages/flutter/lib/src/material/scaffold.dart @@ -191,18 +191,18 @@ class ScaffoldMessengerState extends State with TickerProvide @override void didChangeDependencies() { - final MediaQueryData mediaQuery = MediaQuery.of(context); + final bool accessibleNavigation = MediaQuery.accessibleNavigationOf(context); // If we transition from accessible navigation to non-accessible navigation // and there is a SnackBar that would have timed out that has already // completed its timer, dismiss that SnackBar. If the timer hasn't finished // yet, let it timeout as normal. if ((_accessibleNavigation ?? false) - && !mediaQuery.accessibleNavigation + && !accessibleNavigation && _snackBarTimer != null && !_snackBarTimer!.isActive) { hideCurrentSnackBar(reason: SnackBarClosedReason.timeout); } - _accessibleNavigation = mediaQuery.accessibleNavigation; + _accessibleNavigation = accessibleNavigation; super.didChangeDependencies(); } @@ -568,8 +568,7 @@ class ScaffoldMessengerState extends State with TickerProvide @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); - _accessibleNavigation = mediaQuery.accessibleNavigation; + _accessibleNavigation = MediaQuery.accessibleNavigationOf(context); if (_snackBars.isNotEmpty) { final ModalRoute? route = ModalRoute.of(context); @@ -582,8 +581,7 @@ class ScaffoldMessengerState extends State with TickerProvide _snackBarController!.status == AnimationStatus.completed, ); // Look up MediaQuery again in case the setting changed. - final MediaQueryData mediaQuery = MediaQuery.of(context); - if (mediaQuery.accessibleNavigation && snackBar.action != null) { + if (snackBar.action != null && MediaQuery.accessibleNavigationOf(context)) { return; } hideCurrentSnackBar(reason: SnackBarClosedReason.timeout); @@ -1824,11 +1822,11 @@ class Scaffold extends StatefulWidget { /// drawer. /// /// By default, the value used is 20.0 added to the padding edge of - /// `MediaQuery.of(context).padding` that corresponds to the surrounding + /// `MediaQuery.paddingOf(context)` that corresponds to the surrounding /// [TextDirection]. This ensures that the drag area for notched devices is /// not obscured. For example, if `TextDirection.of(context)` is set to /// [TextDirection.ltr], 20.0 will be added to - /// `MediaQuery.of(context).padding.left`. + /// `MediaQuery.paddingOf(context).left`. final double? drawerEdgeDragWidth; /// Determines if the [Scaffold.drawer] can be opened with a drag @@ -2761,7 +2759,6 @@ class ScaffoldState extends State with TickerProviderStateMixin, Resto Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); assert(debugCheckHasDirectionality(context)); - final MediaQueryData mediaQuery = MediaQuery.of(context); final ThemeData themeData = Theme.of(context); final TextDirection textDirection = Directionality.of(context); @@ -2796,7 +2793,7 @@ class ScaffoldState extends State with TickerProviderStateMixin, Resto } if (widget.appBar != null) { - final double topPadding = widget.primary ? mediaQuery.padding.top : 0.0; + final double topPadding = widget.primary ? MediaQuery.paddingOf(context).top : 0.0; _appBarMaxHeight = AppBar.preferredHeightFor(context, widget.appBar!.preferredSize) + topPadding; assert(_appBarMaxHeight! >= 0.0 && _appBarMaxHeight!.isFinite); _addIfNonNull( @@ -2973,14 +2970,14 @@ class ScaffoldState extends State with TickerProviderStateMixin, Resto } // The minimum insets for contents of the Scaffold to keep visible. - final EdgeInsets minInsets = mediaQuery.padding.copyWith( - bottom: _resizeToAvoidBottomInset ? mediaQuery.viewInsets.bottom : 0.0, + final EdgeInsets minInsets = MediaQuery.paddingOf(context).copyWith( + bottom: _resizeToAvoidBottomInset ? MediaQuery.viewInsetsOf(context).bottom : 0.0, ); // The minimum viewPadding for interactive elements positioned by the // Scaffold to keep within safe interactive areas. - final EdgeInsets minViewPadding = mediaQuery.viewPadding.copyWith( - bottom: _resizeToAvoidBottomInset && mediaQuery.viewInsets.bottom != 0.0 ? 0.0 : null, + final EdgeInsets minViewPadding = MediaQuery.viewPaddingOf(context).copyWith( + bottom: _resizeToAvoidBottomInset && MediaQuery.viewInsetsOf(context).bottom != 0.0 ? 0.0 : null, ); // extendBody locked when keyboard is open diff --git a/packages/flutter/lib/src/material/scrollbar.dart b/packages/flutter/lib/src/material/scrollbar.dart index e282ccc02e8f1..bf1be77054f0e 100644 --- a/packages/flutter/lib/src/material/scrollbar.dart +++ b/packages/flutter/lib/src/material/scrollbar.dart @@ -427,7 +427,7 @@ class _MaterialScrollbarState extends RawScrollbarState<_MaterialScrollbar> { ..crossAxisMargin = _scrollbarTheme.crossAxisMargin ?? (_useAndroidScrollbar ? 0.0 : _kScrollbarMargin) ..mainAxisMargin = _scrollbarTheme.mainAxisMargin ?? 0.0 ..minLength = _scrollbarTheme.minThumbLength ?? _kScrollbarMinLength - ..padding = MediaQuery.of(context).padding + ..padding = MediaQuery.paddingOf(context) ..scrollbarOrientation = widget.scrollbarOrientation ..ignorePointer = !enableGestures; } diff --git a/packages/flutter/lib/src/material/selectable_text.dart b/packages/flutter/lib/src/material/selectable_text.dart index ccbe7a8e1f938..aeb5a3a367112 100644 --- a/packages/flutter/lib/src/material/selectable_text.dart +++ b/packages/flutter/lib/src/material/selectable_text.dart @@ -659,7 +659,7 @@ class _SelectableTextState extends State implements TextSelectio cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor; selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40); cursorRadius ??= const Radius.circular(2.0); - cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0); + cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0); break; case TargetPlatform.macOS: @@ -671,7 +671,7 @@ class _SelectableTextState extends State implements TextSelectio cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor; selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40); cursorRadius ??= const Radius.circular(2.0); - cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0); + cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0); break; case TargetPlatform.android: @@ -700,7 +700,7 @@ class _SelectableTextState extends State implements TextSelectio if (effectiveTextStyle == null || effectiveTextStyle.inherit) { effectiveTextStyle = defaultTextStyle.style.merge(widget.style ?? _controller._textSpan.style); } - if (MediaQuery.boldTextOverride(context)) { + if (MediaQuery.boldTextOf(context)) { effectiveTextStyle = effectiveTextStyle.merge(const TextStyle(fontWeight: FontWeight.bold)); } final Widget child = RepaintBoundary( diff --git a/packages/flutter/lib/src/material/slider.dart b/packages/flutter/lib/src/material/slider.dart index 3703f9030d53b..5cae288f7d687 100644 --- a/packages/flutter/lib/src/material/slider.dart +++ b/packages/flutter/lib/src/material/slider.dart @@ -826,7 +826,7 @@ class _SliderState extends State with TickerProviderStateMixin { // This size is used as the max bounds for the painting of the value // indicators It must be kept in sync with the function with the same name // in range_slider.dart. - Size screenSize() => MediaQuery.of(context).size; + Size screenSize() => MediaQuery.sizeOf(context); VoidCallback? handleDidGainAccessibilityFocus; switch (theme.platform) { @@ -847,7 +847,7 @@ class _SliderState extends State with TickerProviderStateMixin { } final Map shortcutMap; - switch (MediaQuery.of(context).navigationMode) { + switch (MediaQuery.navigationModeOf(context)) { case NavigationMode.directional: shortcutMap = _directionalNavShortcutMap; break; @@ -861,8 +861,8 @@ class _SliderState extends State with TickerProviderStateMixin { // This needs to be updated when accessibility // guidelines are available on the material specs page // https://m3.material.io/components/sliders/accessibility. - ? math.min(MediaQuery.of(context).textScaleFactor, 1.3) - : MediaQuery.of(context).textScaleFactor; + ? math.min(MediaQuery.textScaleFactorOf(context), 1.3) + : MediaQuery.textScaleFactorOf(context); return Semantics( container: true, @@ -994,7 +994,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { platform: Theme.of(context).platform, hasFocus: hasFocus, hovering: hovering, - gestureSettings: MediaQuery.of(context).gestureSettings, + gestureSettings: MediaQuery.gestureSettingsOf(context), ); } @@ -1018,7 +1018,7 @@ class _SliderRenderObjectWidget extends LeafRenderObjectWidget { ..platform = Theme.of(context).platform ..hasFocus = hasFocus ..hovering = hovering - ..gestureSettings = MediaQuery.of(context).gestureSettings; + ..gestureSettings = MediaQuery.gestureSettingsOf(context); // Ticker provider cannot change since there's a 1:1 relationship between // the _SliderRenderObjectWidget object and the _SliderState object. } diff --git a/packages/flutter/lib/src/material/snack_bar.dart b/packages/flutter/lib/src/material/snack_bar.dart index 6dfea7e2dac43..993450fbbff6d 100644 --- a/packages/flutter/lib/src/material/snack_bar.dart +++ b/packages/flutter/lib/src/material/snack_bar.dart @@ -485,7 +485,7 @@ class _SnackBarState extends State { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData mediaQueryData = MediaQuery.of(context); + final bool accessibleNavigation = MediaQuery.accessibleNavigationOf(context); assert(widget.animation != null); final ThemeData theme = Theme.of(context); final ColorScheme colorScheme = theme.colorScheme; @@ -599,7 +599,7 @@ class _SnackBarState extends State { final EdgeInsets margin = widget.margin?.resolve(TextDirection.ltr) ?? snackBarTheme.insetPadding ?? defaults.insetPadding!; - final double snackBarWidth = widget.width ?? mediaQueryData.size.width - (margin.left + margin.right); + final double snackBarWidth = widget.width ?? MediaQuery.sizeOf(context).width - (margin.left + margin.right); // Action and Icon will overflow to a new line if their width is greater // than one quarter of the total Snack Bar width. final bool actionLineOverflow = @@ -675,7 +675,7 @@ class _SnackBarState extends State { color: backgroundColor, child: Theme( data: effectiveTheme, - child: mediaQueryData.accessibleNavigation || theme.useMaterial3 + child: accessibleNavigation || theme.useMaterial3 ? snackBar : FadeTransition( opacity: fadeOutAnimation, @@ -723,7 +723,7 @@ class _SnackBarState extends State { ); final Widget snackBarTransition; - if (mediaQueryData.accessibleNavigation) { + if (accessibleNavigation) { snackBarTransition = snackBar; } else if (isFloatingSnackBar && !theme.useMaterial3) { snackBarTransition = FadeTransition( diff --git a/packages/flutter/lib/src/material/text_button.dart b/packages/flutter/lib/src/material/text_button.dart index 75afee3663390..03e07176eb0a2 100644 --- a/packages/flutter/lib/src/material/text_button.dart +++ b/packages/flutter/lib/src/material/text_button.dart @@ -249,7 +249,7 @@ class TextButton extends ButtonStyleButton { /// each state and "others" means all other states. /// /// The `textScaleFactor` is the value of - /// `MediaQuery.of(context).textScaleFactor` and the names of the + /// `MediaQuery.textScaleFactorOf(context)` and the names of the /// EdgeInsets constructors and `EdgeInsetsGeometry.lerp` have been /// abbreviated for readability. /// @@ -382,7 +382,7 @@ EdgeInsetsGeometry _scaledPadding(BuildContext context) { const EdgeInsets.all(8), const EdgeInsets.symmetric(horizontal: 8), const EdgeInsets.symmetric(horizontal: 4), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); } @@ -495,7 +495,7 @@ class _TextButtonWithIcon extends TextButton { const EdgeInsets.all(8), const EdgeInsets.symmetric(horizontal: 4), const EdgeInsets.symmetric(horizontal: 4), - MediaQuery.maybeOf(context)?.textScaleFactor ?? 1, + MediaQuery.textScaleFactorOf(context), ); return super.defaultStyleOf(context).copyWith( padding: MaterialStatePropertyAll(scaledPadding), @@ -514,7 +514,7 @@ class _TextButtonWithIconChild extends StatelessWidget { @override Widget build(BuildContext context) { - final double scale = MediaQuery.maybeOf(context)?.textScaleFactor ?? 1; + final double scale = MediaQuery.textScaleFactorOf(context); final double gap = scale <= 1 ? 8 : lerpDouble(8, 4, math.min(scale - 1, 1))!; return Row( mainAxisSize: MainAxisSize.min, diff --git a/packages/flutter/lib/src/material/text_field.dart b/packages/flutter/lib/src/material/text_field.dart index c72828c92a288..4da189a647e9c 100644 --- a/packages/flutter/lib/src/material/text_field.dart +++ b/packages/flutter/lib/src/material/text_field.dart @@ -967,7 +967,7 @@ class _TextFieldState extends State with RestorationMixin implements } bool get _canRequestFocus { - final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional; + final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: return _isEnabled; @@ -1215,7 +1215,7 @@ class _TextFieldState extends State with RestorationMixin implements cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor; selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40); cursorRadius ??= const Radius.circular(2.0); - cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0); + cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0); autocorrectionTextRectColor = selectionColor; break; @@ -1228,7 +1228,7 @@ class _TextFieldState extends State with RestorationMixin implements cursorColor = widget.cursorColor ?? selectionStyle.cursorColor ?? cupertinoTheme.primaryColor; selectionColor = selectionStyle.selectionColor ?? cupertinoTheme.primaryColor.withOpacity(0.40); cursorRadius ??= const Radius.circular(2.0); - cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.of(context).devicePixelRatio, 0); + cursorOffset = Offset(iOSHorizontalOffset / MediaQuery.devicePixelRatioOf(context), 0); handleDidGainAccessibilityFocus = () { // Automatically activate the TextField when it receives accessibility focus. if (!_effectiveFocusNode.hasFocus && _effectiveFocusNode.canRequestFocus) { diff --git a/packages/flutter/lib/src/material/text_selection_toolbar.dart b/packages/flutter/lib/src/material/text_selection_toolbar.dart index a024968c389dc..19bf219dffa2e 100644 --- a/packages/flutter/lib/src/material/text_selection_toolbar.dart +++ b/packages/flutter/lib/src/material/text_selection_toolbar.dart @@ -99,7 +99,7 @@ class TextSelectionToolbar extends StatelessWidget { final Offset anchorBelowPadded = anchorBelow + const Offset(0.0, _kToolbarContentDistanceBelow); - final double paddingAbove = MediaQuery.of(context).padding.top + final double paddingAbove = MediaQuery.paddingOf(context).top + _kToolbarScreenPadding; final double availableHeight = anchorAbovePadded.dy - _kToolbarContentDistance - paddingAbove; final bool fitsAbove = _kToolbarHeight <= availableHeight; diff --git a/packages/flutter/lib/src/material/time.dart b/packages/flutter/lib/src/material/time.dart index 5b9e31945a665..679e80a2616e3 100644 --- a/packages/flutter/lib/src/material/time.dart +++ b/packages/flutter/lib/src/material/time.dart @@ -108,7 +108,7 @@ class TimeOfDay { final MaterialLocalizations localizations = MaterialLocalizations.of(context); return localizations.formatTimeOfDay( this, - alwaysUse24HourFormat: MediaQuery.of(context).alwaysUse24HourFormat, + alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context), ); } diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index b9f33ea6bd9b5..bc9f517ef8229 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -149,7 +149,7 @@ class _TimePickerHeader extends StatelessWidget { assert(debugCheckHasMediaQuery(context)); final ThemeData themeData = Theme.of(context); final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat( - alwaysUse24HourFormat: MediaQuery.of(context).alwaysUse24HourFormat, + alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context), ); final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String timePickerDialHelpText = themeData.useMaterial3 @@ -339,7 +339,7 @@ class _HourControl extends StatelessWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final bool alwaysUse24HourFormat = MediaQuery.of(context).alwaysUse24HourFormat; + final bool alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String formattedHour = localizations.formatHour( fragmentContext.selectedTime, @@ -581,7 +581,7 @@ class _DayPeriodControl extends StatelessWidget { side: borderSide, ); - final double buttonTextScaleFactor = math.min(MediaQuery.of(context).textScaleFactor, 2.0); + final double buttonTextScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 2.0); final Widget amButton = Material( color: MaterialStateProperty.resolveAs(backgroundColor, amStates), @@ -990,7 +990,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { late ThemeData themeData; late MaterialLocalizations localizations; - late MediaQueryData media; + late bool alwaysUse24HourFormat; _DialPainter? painter; @override @@ -999,7 +999,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { assert(debugCheckHasMediaQuery(context)); themeData = Theme.of(context); localizations = MaterialLocalizations.of(context); - media = MediaQuery.of(context); + alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); } @override @@ -1209,7 +1209,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { _TappableLabel _buildTappableLabel(TextTheme textTheme, Color color, int value, String label, VoidCallback onTap) { final TextStyle style = textTheme.bodyLarge!.copyWith(color: color); - final double labelScaleFactor = math.min(MediaQuery.of(context).textScaleFactor, 2.0); + final double labelScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 2.0); return _TappableLabel( value: value, painter: TextPainter( @@ -1227,7 +1227,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { textTheme, color, timeOfDay.hour, - localizations.formatHour(timeOfDay, alwaysUse24HourFormat: media.alwaysUse24HourFormat), + localizations.formatHour(timeOfDay, alwaysUse24HourFormat: alwaysUse24HourFormat), () { _selectHour(timeOfDay.hour); }, @@ -1240,7 +1240,7 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { textTheme, color, timeOfDay.hour, - localizations.formatHour(timeOfDay, alwaysUse24HourFormat: media.alwaysUse24HourFormat), + localizations.formatHour(timeOfDay, alwaysUse24HourFormat: alwaysUse24HourFormat), () { _selectHour(timeOfDay.hour); }, @@ -1406,7 +1406,7 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi return null; } - if (MediaQuery.of(context).alwaysUse24HourFormat) { + if (MediaQuery.alwaysUse24HourFormatOf(context)) { if (newHour >= 0 && newHour < 24) { return newHour; } @@ -1494,8 +1494,7 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData media = MediaQuery.of(context); - final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat(alwaysUse24HourFormat: media.alwaysUse24HourFormat); + final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); final bool use24HourDials = hourFormat(of: timeOfDayFormat) != HourFormat.h; final ThemeData theme = Theme.of(context); final TextStyle hourMinuteStyle = TimePickerTheme.of(context).hourMinuteTextStyle ?? theme.textTheme.displayMedium!; @@ -1759,7 +1758,7 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora } String get _formattedValue { - final bool alwaysUse24HourFormat = MediaQuery.of(context).alwaysUse24HourFormat; + final bool alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); return !widget.isHour ? localizations.formatMinute(widget.selectedTime) : localizations.formatHour( widget.selectedTime, @@ -1805,7 +1804,7 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora // // TODO(rami-a): Once https://github.com/flutter/flutter/issues/67571 is // resolved, remove the window check for semantics being enabled on web. - final String? hintText = MediaQuery.of(context).accessibleNavigation || WidgetsBinding.instance.window.semanticsEnabled + final String? hintText = MediaQuery.accessibleNavigationOf(context) || WidgetsBinding.instance.window.semanticsEnabled ? widget.semanticHintText : (focusNode.hasFocus ? null : _formattedValue); inputDecoration = inputDecoration.copyWith( @@ -2146,11 +2145,10 @@ class _TimePickerDialogState extends State with RestorationMix return; } - final MediaQueryData media = MediaQuery.of(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); _announceToAccessibility( context, - localizations.formatTimeOfDay(widget.initialTime, alwaysUse24HourFormat: media.alwaysUse24HourFormat), + localizations.formatTimeOfDay(widget.initialTime, alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)), ); _announcedInitialTime.value = true; } @@ -2195,12 +2193,12 @@ class _TimePickerDialogState extends State with RestorationMix } Size _dialogSize(BuildContext context) { - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); final ThemeData theme = Theme.of(context); // Constrain the textScaleFactor to prevent layout issues. Since only some // parts of the time picker scale up with textScaleFactor, we cap the factor // to 1.1 as that provides enough space to reasonably fit all the content. - final double textScaleFactor = math.min(MediaQuery.of(context).textScaleFactor, 1.1); + final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.1); final double timePickerWidth; final double timePickerHeight; @@ -2234,12 +2232,11 @@ class _TimePickerDialogState extends State with RestorationMix @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData media = MediaQuery.of(context); - final TimeOfDayFormat timeOfDayFormat = localizations.timeOfDayFormat(alwaysUse24HourFormat: media.alwaysUse24HourFormat); + final TimeOfDayFormat timeOfDayFormat = localizations.timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); final bool use24HourDials = hourFormat(of: timeOfDayFormat) != HourFormat.h; final ThemeData theme = Theme.of(context); final ShapeBorder shape = TimePickerTheme.of(context).shape ?? _kDefaultShape; - final Orientation orientation = media.orientation; + final Orientation orientation = MediaQuery.orientationOf(context); final Widget actions = Row( children: [ diff --git a/packages/flutter/lib/src/material/tooltip.dart b/packages/flutter/lib/src/material/tooltip.dart index 344514d29597e..b0be65d286eff 100644 --- a/packages/flutter/lib/src/material/tooltip.dart +++ b/packages/flutter/lib/src/material/tooltip.dart @@ -884,7 +884,7 @@ class _TooltipOverlay extends StatelessWidget { ); } return Positioned.fill( - bottom: MediaQuery.maybeOf(context)?.viewInsets.bottom ?? 0.0, + bottom: MediaQuery.maybeViewInsetsOf(context)?.bottom ?? 0.0, child: CustomSingleChildLayout( delegate: _TooltipPositionDelegate( target: target, diff --git a/packages/flutter/lib/src/widgets/actions.dart b/packages/flutter/lib/src/widgets/actions.dart index 262ecf212d479..664971b46682d 100644 --- a/packages/flutter/lib/src/widgets/actions.dart +++ b/packages/flutter/lib/src/widgets/actions.dart @@ -1230,7 +1230,7 @@ class _FocusableActionDetectorState extends State { } bool canRequestFocus(FocusableActionDetector target) { - final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional; + final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: return target.enabled; @@ -1271,7 +1271,7 @@ class _FocusableActionDetectorState extends State { } bool get _canRequestFocus { - final NavigationMode mode = MediaQuery.maybeOf(context)?.navigationMode ?? NavigationMode.traditional; + final NavigationMode mode = MediaQuery.maybeNavigationModeOf(context) ?? NavigationMode.traditional; switch (mode) { case NavigationMode.traditional: return widget.enabled; diff --git a/packages/flutter/lib/src/widgets/drag_target.dart b/packages/flutter/lib/src/widgets/drag_target.dart index 259603dc12d0b..b3c280a23d9f4 100644 --- a/packages/flutter/lib/src/widgets/drag_target.dart +++ b/packages/flutter/lib/src/widgets/drag_target.dart @@ -447,7 +447,7 @@ class _DraggableState extends State> { @override void didChangeDependencies() { - _recognizer!.gestureSettings = MediaQuery.maybeOf(context)?.gestureSettings; + _recognizer!.gestureSettings = MediaQuery.maybeGestureSettingsOf(context); super.didChangeDependencies(); } diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index bc17cce872a09..e782b44881ff3 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -2409,7 +2409,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien } // Hide the text selection toolbar on mobile when orientation changes. - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); if (_lastOrientation == null) { _lastOrientation = orientation; return; @@ -3583,7 +3583,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien @override TextEditingValue get textEditingValue => _value; - double get _devicePixelRatio => MediaQuery.of(context).devicePixelRatio; + double get _devicePixelRatio => MediaQuery.devicePixelRatioOf(context); @override void userUpdateTextEditingValue(TextEditingValue value, SelectionChangedCause? cause) { diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 20006d9f45710..098634dee50c1 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -1017,7 +1017,7 @@ class GestureDetector extends StatelessWidget { @override Widget build(BuildContext context) { final Map gestures = {}; - final DeviceGestureSettings? gestureSettings = MediaQuery.maybeOf(context)?.gestureSettings; + final DeviceGestureSettings? gestureSettings = MediaQuery.maybeGestureSettingsOf(context); if (onTapDown != null || onTapUp != null || diff --git a/packages/flutter/lib/src/widgets/image.dart b/packages/flutter/lib/src/widgets/image.dart index b78300494c533..4336f0ab3f61a 100644 --- a/packages/flutter/lib/src/widgets/image.dart +++ b/packages/flutter/lib/src/widgets/image.dart @@ -53,7 +53,7 @@ export 'package:flutter/painting.dart' show ImageConfiguration createLocalImageConfiguration(BuildContext context, { Size? size }) { return ImageConfiguration( bundle: DefaultAssetBundle.of(context), - devicePixelRatio: MediaQuery.maybeOf(context)?.devicePixelRatio ?? 1.0, + devicePixelRatio: MediaQuery.maybeDevicePixelRatioOf(context) ?? 1.0, locale: Localizations.maybeLocaleOf(context), textDirection: Directionality.maybeOf(context), size: size, @@ -1106,7 +1106,7 @@ class _ImageState extends State with WidgetsBindingObserver { } void _updateInvertColors() { - _invertColors = MediaQuery.maybeOf(context)?.invertColors + _invertColors = MediaQuery.maybeInvertColorsOf(context) ?? SemanticsBinding.instance.accessibilityFeatures.invertColors; } diff --git a/packages/flutter/lib/src/widgets/media_query.dart b/packages/flutter/lib/src/widgets/media_query.dart index af6658954c13f..a8fdda349ddd5 100644 --- a/packages/flutter/lib/src/widgets/media_query.dart +++ b/packages/flutter/lib/src/widgets/media_query.dart @@ -12,6 +12,7 @@ import 'basic.dart'; import 'binding.dart'; import 'debug.dart'; import 'framework.dart'; +import 'inherited_model.dart'; // Examples can assume: // late BuildContext context; @@ -25,6 +26,52 @@ enum Orientation { landscape } +/// Specifies a part of MediaQueryData to depend on. +/// +/// [MediaQuery] contains a large number of related properties. Widgets frequently +/// depend on only a few of these attributes. For example, a widget that needs to +/// rebuild when the [MediaQueryData.textScaleFactor] changes does not need to +/// be notified when the [MediaQueryData.size] changes. Specifying an aspect avoids +/// unnecessary rebuilds. +enum _MediaQueryAspect { + /// Specifies the aspect corresponding to [MediaQueryData.size]. + size, + /// Specifies the aspect corresponding to [MediaQueryData.orientation]. + orientation, + /// Specifies the aspect corresponding to [MediaQueryData.devicePixelRatio]. + devicePixelRatio, + /// Specifies the aspect corresponding to [MediaQueryData.textScaleFactor]. + textScaleFactor, + /// Specifies the aspect corresponding to [MediaQueryData.platformBrightness]. + platformBrightness, + /// Specifies the aspect corresponding to [MediaQueryData.padding]. + padding, + /// Specifies the aspect corresponding to [MediaQueryData.viewInsets]. + viewInsets, + /// Specifies the aspect corresponding to [MediaQueryData.systemGestureInsets]. + systemGestureInsets, + /// Specifies the aspect corresponding to [MediaQueryData.viewPadding]. + viewPadding, + /// Specifies the aspect corresponding to [MediaQueryData.alwaysUse24HourFormat]. + alwaysUse24HourFormat, + /// Specifies the aspect corresponding to [MediaQueryData.accessibleNavigation]. + accessibleNavigation, + /// Specifies the aspect corresponding to [MediaQueryData.invertColors]. + invertColors, + /// Specifies the aspect corresponding to [MediaQueryData.highContrast]. + highContrast, + /// Specifies the aspect corresponding to [MediaQueryData.disableAnimations]. + disableAnimations, + /// Specifies the aspect corresponding to [MediaQueryData.boldText]. + boldText, + /// Specifies the aspect corresponding to [MediaQueryData.navigationMode]. + navigationMode, + /// Specifies the aspect corresponding to [MediaQueryData.gestureSettings]. + gestureSettings, + /// Specifies the aspect corresponding to [MediaQueryData.displayFeatures]. + displayFeatures, +} + /// Information about a piece of media (e.g., a window). /// /// For example, the [MediaQueryData.size] property contains the width and @@ -178,6 +225,8 @@ class MediaQueryData { /// See also: /// /// * [FlutterView.physicalSize], which returns the size in physical pixels. + /// * [MediaQuery.sizeOf], a method to find and depend on the size defined + /// for a [BuildContext]. final Size size; /// The number of device pixels for each logical pixel. This number might not @@ -192,7 +241,7 @@ class MediaQueryData { /// /// See also: /// - /// * [MediaQuery.textScaleFactorOf], a convenience method which returns the + /// * [MediaQuery.textScaleFactorOf], a method to find and depend on the /// textScaleFactor defined for a [BuildContext]. final double textScaleFactor; @@ -203,6 +252,11 @@ class MediaQueryData { /// /// Not all platforms necessarily support a concept of brightness mode. Those /// platforms will report [Brightness.light] in this property. + /// + /// See also: + /// + /// * [MediaQuery.platformBrightnessOf], a method to find and depend on the + /// platformBrightness defined for a [BuildContext]. final Brightness platformBrightness; /// The parts of the display that are completely obscured by system UI, @@ -690,7 +744,7 @@ class MediaQueryData { /// * [WidgetsApp] and [MaterialApp], which introduce a [MediaQuery] and keep /// it up to date with the current screen metrics as they change. /// * [MediaQueryData], the data structure that represents the metrics. -class MediaQuery extends InheritedWidget { +class MediaQuery extends InheritedModel<_MediaQueryAspect> { /// Creates a widget that provides [MediaQueryData] to its descendants. /// /// The [data] and [child] arguments must not be null. @@ -873,6 +927,11 @@ class MediaQuery extends InheritedWidget { /// examples). When that information changes, your widget will be scheduled to /// be rebuilt, keeping your widget up-to-date. /// + /// If the widget only requires a subset of properties of the [MediaQueryData] + /// object, it is preferred to use the specific methods (for example: + /// [MediaQuery.sizeOf] and [MediaQuery.paddingOf]), as those methods will not + /// cause a widget to rebuild when unrelated properties are updated. + /// /// Typical usage is as follows: /// /// ```dart @@ -889,8 +948,12 @@ class MediaQuery extends InheritedWidget { /// [MediaQuery] ancestor, it returns null instead. static MediaQueryData of(BuildContext context) { assert(context != null); + return _of(context); + } + + static MediaQueryData _of(BuildContext context, [_MediaQueryAspect? aspect]) { assert(debugCheckHasMediaQuery(context)); - return context.dependOnInheritedWidgetOfExactType()!.data; + return InheritedModel.inheritFrom(context, aspect: aspect)!.data; } /// The data from the closest instance of this class that encloses the given @@ -907,6 +970,11 @@ class MediaQuery extends InheritedWidget { /// examples). When that information changes, your widget will be scheduled to /// be rebuilt, keeping your widget up-to-date. /// + /// If the widget only requires a subset of properties of the [MediaQueryData] + /// object, it is preferred to use the specific methods (for example: + /// [MediaQuery.maybeSizeOf] and [MediaQuery.maybePaddingOf]), as those methods + /// will not cause a widget to rebuild when unrelated properties are updated. + /// /// Typical usage is as follows: /// /// ```dart @@ -922,23 +990,182 @@ class MediaQuery extends InheritedWidget { /// instead of returning null. static MediaQueryData? maybeOf(BuildContext context) { assert(context != null); - return context.dependOnInheritedWidgetOfExactType()?.data; + return _maybeOf(context); } - /// Returns textScaleFactor for the nearest MediaQuery ancestor or 1.0, if - /// no such ancestor exists. - static double textScaleFactorOf(BuildContext context) { - return MediaQuery.maybeOf(context)?.textScaleFactor ?? 1.0; + static MediaQueryData? _maybeOf(BuildContext context, [_MediaQueryAspect? aspect]) { + return InheritedModel.inheritFrom(context, aspect: aspect)?.data; } + /// Returns size for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.size] property of the ancestor [MediaQuery] changes. + static Size sizeOf(BuildContext context) => _of(context, _MediaQueryAspect.size).size; + + /// Returns size for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.size] property of the ancestor [MediaQuery] changes. + static Size? maybeSizeOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.size)?.size; + + /// Returns orientation for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.orientation] property of the ancestor [MediaQuery] changes. + static Orientation orientationOf(BuildContext context) => _of(context, _MediaQueryAspect.orientation).orientation; + + /// Returns orientation for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.orientation] property of the ancestor [MediaQuery] changes. + static Orientation? maybeOrientationOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.orientation)?.orientation; + + /// Returns devicePixelRatio for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.devicePixelRatio] property of the ancestor [MediaQuery] changes. + static double devicePixelRatioOf(BuildContext context) => _of(context, _MediaQueryAspect.devicePixelRatio).devicePixelRatio; + + /// Returns devicePixelRatio for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.devicePixelRatio] property of the ancestor [MediaQuery] changes. + static double? maybeDevicePixelRatioOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.devicePixelRatio)?.devicePixelRatio; + + /// Returns textScaleFactor for the nearest MediaQuery ancestor or + /// 1.0, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.textScaleFactor] property of the ancestor [MediaQuery] changes. + static double textScaleFactorOf(BuildContext context) => maybeTextScaleFactorOf(context) ?? 1.0; + + /// Returns textScaleFactor for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.textScaleFactor] property of the ancestor [MediaQuery] changes. + static double? maybeTextScaleFactorOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.textScaleFactor)?.textScaleFactor; + /// Returns platformBrightness for the nearest MediaQuery ancestor or /// [Brightness.light], if no such ancestor exists. /// /// Use of this method will cause the given [context] to rebuild any time that - /// any property of the ancestor [MediaQuery] changes. - static Brightness platformBrightnessOf(BuildContext context) { - return MediaQuery.maybeOf(context)?.platformBrightness ?? Brightness.light; - } + /// the [MediaQueryData.platformBrightness] property of the ancestor + /// [MediaQuery] changes. + static Brightness platformBrightnessOf(BuildContext context) => maybePlatformBrightnessOf(context) ?? Brightness.light; + + /// Returns platformBrightness for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.platformBrightness] property of the ancestor + /// [MediaQuery] changes. + static Brightness? maybePlatformBrightnessOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.platformBrightness)?.platformBrightness; + + /// Returns padding for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.padding] property of the ancestor [MediaQuery] changes. + static EdgeInsets paddingOf(BuildContext context) => _of(context, _MediaQueryAspect.padding).padding; + + /// Returns viewInsets for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.viewInsets] property of the ancestor [MediaQuery] changes. + static EdgeInsets? maybePaddingOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.padding)?.padding; + + /// Returns viewInsets for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.viewInsets] property of the ancestor [MediaQuery] changes. + static EdgeInsets viewInsetsOf(BuildContext context) => _of(context, _MediaQueryAspect.viewInsets).viewInsets; + + /// Returns viewInsets for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.viewInsets] property of the ancestor [MediaQuery] changes. + static EdgeInsets? maybeViewInsetsOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.viewInsets)?.viewInsets; + + /// Returns systemGestureInsets for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.systemGestureInsets] property of the ancestor [MediaQuery] changes. + static EdgeInsets systemGestureInsetsOf(BuildContext context) => _of(context, _MediaQueryAspect.systemGestureInsets).systemGestureInsets; + + /// Returns systemGestureInsets for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.systemGestureInsets] property of the ancestor [MediaQuery] changes. + static EdgeInsets? maybeSystemGestureInsetsOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.systemGestureInsets)?.systemGestureInsets; + + /// Returns viewPadding for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.viewPadding] property of the ancestor [MediaQuery] changes. + static EdgeInsets viewPaddingOf(BuildContext context) => _of(context, _MediaQueryAspect.viewPadding).viewPadding; + + /// Returns viewPadding for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.viewPadding] property of the ancestor [MediaQuery] changes. + static EdgeInsets? maybeViewPaddingOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.viewPadding)?.viewPadding; + + /// Returns alwaysUse for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.devicePixelRatio] property of the ancestor [MediaQuery] changes. + static bool alwaysUse24HourFormatOf(BuildContext context) => _of(context, _MediaQueryAspect.alwaysUse24HourFormat).alwaysUse24HourFormat; + + /// Returns alwaysUse24HourFormat for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.alwaysUse24HourFormat] property of the ancestor [MediaQuery] changes. + static bool? maybeAlwaysUse24HourFormatOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.alwaysUse24HourFormat)?.alwaysUse24HourFormat; + + /// Returns accessibleNavigationOf for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.accessibleNavigation] property of the ancestor [MediaQuery] changes. + static bool accessibleNavigationOf(BuildContext context) => _of(context, _MediaQueryAspect.accessibleNavigation).accessibleNavigation; + + /// Returns accessibleNavigation for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.accessibleNavigation] property of the ancestor [MediaQuery] changes. + static bool? maybeAccessibleNavigationOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.accessibleNavigation)?.accessibleNavigation; + + /// Returns invertColorsOf for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.invertColors] property of the ancestor [MediaQuery] changes. + static bool invertColorsOf(BuildContext context) => _of(context, _MediaQueryAspect.invertColors).invertColors; + + /// Returns invertColors for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.invertColors] property of the ancestor [MediaQuery] changes. + static bool? maybeInvertColorsOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.invertColors)?.invertColors; /// Returns highContrast for the nearest MediaQuery ancestor or false, if no /// such ancestor exists. @@ -947,15 +1174,102 @@ class MediaQuery extends InheritedWidget { /// /// * [MediaQueryData.highContrast], which indicates the platform's /// desire to increase contrast. - static bool highContrastOf(BuildContext context) { - return MediaQuery.maybeOf(context)?.highContrast ?? false; - } + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.highContrast] property of the ancestor [MediaQuery] changes. + static bool highContrastOf(BuildContext context) => maybeHighContrastOf(context) ?? false; + + /// Returns highContrast for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.highContrast] property of the ancestor [MediaQuery] changes. + static bool? maybeHighContrastOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.highContrast)?.highContrast; + + /// Returns disableAnimations for the nearest MediaQuery ancestor or + /// [Brightness.light], if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.disableAnimations] property of the ancestor + /// [MediaQuery] changes. + static bool disableAnimationsOf(BuildContext context) => _of(context, _MediaQueryAspect.disableAnimations).disableAnimations; + + /// Returns disableAnimations for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.disableAnimations] property of the ancestor [MediaQuery] changes. + static bool? maybeDisableAnimationsOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.disableAnimations)?.disableAnimations; + /// Returns the boldText accessibility setting for the nearest MediaQuery - /// ancestor, or false if no such ancestor exists. - static bool boldTextOverride(BuildContext context) { - return MediaQuery.maybeOf(context)?.boldText ?? false; - } + /// ancestor or false, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.boldText] property of the ancestor [MediaQuery] changes. + static bool boldTextOf(BuildContext context) => maybeBoldTextOf(context) ?? false; + + /// Returns the boldText accessibility setting for the nearest MediaQuery + /// ancestor or false, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.boldText] property of the ancestor [MediaQuery] changes. + /// + /// Deprecated in favor of [boldTextOf]. + @Deprecated( + 'Migrate to boldTextOf. ' + 'This feature was deprecated after v3.5.0-9.0.pre.' + ) + static bool boldTextOverride(BuildContext context) => boldTextOf(context); + + /// Returns the boldText accessibility setting for the nearest MediaQuery + /// ancestor or null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.boldText] property of the ancestor [MediaQuery] changes. + static bool? maybeBoldTextOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.boldText)?.boldText; + + /// Returns navigationMode for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.navigationMode] property of the ancestor [MediaQuery] changes. + static NavigationMode navigationModeOf(BuildContext context) => _of(context, _MediaQueryAspect.navigationMode).navigationMode; + + /// Returns navigationMode for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.navigationMode] property of the ancestor [MediaQuery] changes. + static NavigationMode? maybeNavigationModeOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.navigationMode)?.navigationMode; + + /// Returns gestureSettings for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.gestureSettings] property of the ancestor [MediaQuery] changes. + static DeviceGestureSettings gestureSettingsOf(BuildContext context) => _of(context, _MediaQueryAspect.gestureSettings).gestureSettings; + + /// Returns gestureSettings for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.gestureSettings] property of the ancestor [MediaQuery] changes. + static DeviceGestureSettings? maybeGestureSettingsOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.gestureSettings)?.gestureSettings; + + /// Returns displayFeatures for the nearest MediaQuery ancestor or + /// throws an exception, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.displayFeatures] property of the ancestor [MediaQuery] changes. + static List displayFeaturesOf(BuildContext context) => _of(context, _MediaQueryAspect.displayFeatures).displayFeatures; + + /// Returns displayFeatures for the nearest MediaQuery ancestor or + /// null, if no such ancestor exists. + /// + /// Use of this method will cause the given [context] to rebuild any time that + /// the [MediaQueryData.displayFeatures] property of the ancestor [MediaQuery] changes. + static List? maybeDisplayFeaturesOf(BuildContext context) => _maybeOf(context, _MediaQueryAspect.displayFeatures)?.displayFeatures; @override bool updateShouldNotify(MediaQuery oldWidget) => data != oldWidget.data; @@ -965,6 +1279,27 @@ class MediaQuery extends InheritedWidget { super.debugFillProperties(properties); properties.add(DiagnosticsProperty('data', data, showName: false)); } + + @override + bool updateShouldNotifyDependent(MediaQuery oldWidget, Set dependencies) { + return (data.size != oldWidget.data.size && dependencies.contains(_MediaQueryAspect.size)) + || (data.orientation != oldWidget.data.orientation && dependencies.contains(_MediaQueryAspect.orientation)) + || (data.devicePixelRatio != oldWidget.data.devicePixelRatio && dependencies.contains(_MediaQueryAspect.devicePixelRatio)) + || (data.textScaleFactor != oldWidget.data.textScaleFactor && dependencies.contains(_MediaQueryAspect.textScaleFactor)) + || (data.platformBrightness != oldWidget.data.platformBrightness && dependencies.contains(_MediaQueryAspect.platformBrightness)) + || (data.viewInsets != oldWidget.data.viewInsets && dependencies.contains(_MediaQueryAspect.viewInsets)) + || (data.systemGestureInsets != oldWidget.data.systemGestureInsets && dependencies.contains(_MediaQueryAspect.systemGestureInsets)) + || (data.viewPadding != oldWidget.data.viewPadding && dependencies.contains(_MediaQueryAspect.viewPadding)) + || (data.alwaysUse24HourFormat != oldWidget.data.alwaysUse24HourFormat && dependencies.contains(_MediaQueryAspect.alwaysUse24HourFormat)) + || (data.accessibleNavigation != oldWidget.data.accessibleNavigation && dependencies.contains(_MediaQueryAspect.accessibleNavigation)) + || (data.invertColors != oldWidget.data.invertColors && dependencies.contains(_MediaQueryAspect.invertColors)) + || (data.highContrast != oldWidget.data.highContrast && dependencies.contains(_MediaQueryAspect.highContrast)) + || (data.disableAnimations != oldWidget.data.disableAnimations && dependencies.contains(_MediaQueryAspect.disableAnimations)) + || (data.boldText != oldWidget.data.boldText && dependencies.contains(_MediaQueryAspect.boldText)) + || (data.navigationMode != oldWidget.data.navigationMode && dependencies.contains(_MediaQueryAspect.navigationMode)) + || (data.gestureSettings != oldWidget.data.gestureSettings && dependencies.contains(_MediaQueryAspect.gestureSettings)) + || (data.displayFeatures != oldWidget.data.displayFeatures && dependencies.contains(_MediaQueryAspect.displayFeatures)); + } } /// Describes the navigation mode to be set by a [MediaQuery] widget. @@ -972,7 +1307,7 @@ class MediaQuery extends InheritedWidget { /// The different modes indicate the type of navigation to be used in a widget /// subtree for those widgets sensitive to it. /// -/// Use `MediaQuery.of(context).navigationMode` to determine the navigation mode +/// Use `MediaQuery.navigationModeOf(context)` to determine the navigation mode /// in effect for the given context. Use a [MediaQuery] widget to set the /// navigation mode for its descendant widgets. enum NavigationMode { diff --git a/packages/flutter/lib/src/widgets/overscroll_indicator.dart b/packages/flutter/lib/src/widgets/overscroll_indicator.dart index b8c5eba7fb49f..3af6e58f9a05a 100644 --- a/packages/flutter/lib/src/widgets/overscroll_indicator.dart +++ b/packages/flutter/lib/src/widgets/overscroll_indicator.dart @@ -774,7 +774,7 @@ class _StretchingOverscrollIndicatorState extends State( onNotification: _handleScrollNotification, diff --git a/packages/flutter/lib/src/widgets/reorderable_list.dart b/packages/flutter/lib/src/widgets/reorderable_list.dart index 6521aed13e682..388e340d92afa 100644 --- a/packages/flutter/lib/src/widgets/reorderable_list.dart +++ b/packages/flutter/lib/src/widgets/reorderable_list.dart @@ -1163,7 +1163,7 @@ class ReorderableDragStartListener extends StatelessWidget { } void _startDragging(BuildContext context, PointerDownEvent event) { - final DeviceGestureSettings? gestureSettings = MediaQuery.maybeOf(context)?.gestureSettings; + final DeviceGestureSettings? gestureSettings = MediaQuery.maybeGestureSettingsOf(context); final SliverReorderableListState? list = SliverReorderableList.maybeOf(context); list?.startItemDragReorder( index: index, diff --git a/packages/flutter/lib/src/widgets/safe_area.dart b/packages/flutter/lib/src/widgets/safe_area.dart index f7cd26bde82da..fbdca2467354f 100644 --- a/packages/flutter/lib/src/widgets/safe_area.dart +++ b/packages/flutter/lib/src/widgets/safe_area.dart @@ -93,11 +93,10 @@ class SafeArea extends StatelessWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final MediaQueryData data = MediaQuery.of(context); - EdgeInsets padding = data.padding; + EdgeInsets padding = MediaQuery.paddingOf(context); // Bottom padding has been consumed - i.e. by the keyboard if (maintainBottomViewPadding) { - padding = padding.copyWith(bottom: data.viewPadding.bottom); + padding = padding.copyWith(bottom: MediaQuery.viewPaddingOf(context).bottom); } return Padding( @@ -192,7 +191,7 @@ class SliverSafeArea extends StatelessWidget { @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final EdgeInsets padding = MediaQuery.of(context).padding; + final EdgeInsets padding = MediaQuery.paddingOf(context); return SliverPadding( padding: EdgeInsets.only( left: math.max(left ? padding.left : 0.0, minimum.left), diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index 7f07f2494ab0b..0ddcbc6143a60 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -461,7 +461,7 @@ class ScrollableState extends State with TickerProviderStateMixin, R late ScrollBehavior _configuration; ScrollPhysics? _physics; ScrollController? _fallbackScrollController; - MediaQueryData? _mediaQueryData; + DeviceGestureSettings? _mediaQueryGestureSettings; ScrollController get _effectiveScrollController => widget.controller ?? _fallbackScrollController!; @@ -516,7 +516,7 @@ class ScrollableState extends State with TickerProviderStateMixin, R @override void didChangeDependencies() { - _mediaQueryData = MediaQuery.maybeOf(context); + _mediaQueryGestureSettings = MediaQuery.maybeGestureSettingsOf(context); _updatePosition(); super.didChangeDependencies(); } @@ -635,7 +635,7 @@ class ScrollableState extends State with TickerProviderStateMixin, R ..maxFlingVelocity = _physics?.maxFlingVelocity ..velocityTrackerBuilder = _configuration.velocityTrackerBuilder(context) ..dragStartBehavior = widget.dragStartBehavior - ..gestureSettings = _mediaQueryData?.gestureSettings; + ..gestureSettings = _mediaQueryGestureSettings; }, ), }; @@ -656,7 +656,7 @@ class ScrollableState extends State with TickerProviderStateMixin, R ..maxFlingVelocity = _physics?.maxFlingVelocity ..velocityTrackerBuilder = _configuration.velocityTrackerBuilder(context) ..dragStartBehavior = widget.dragStartBehavior - ..gestureSettings = _mediaQueryData?.gestureSettings; + ..gestureSettings = _mediaQueryGestureSettings; }, ), }; diff --git a/packages/flutter/lib/src/widgets/scrollbar.dart b/packages/flutter/lib/src/widgets/scrollbar.dart index 4a2aea20a607e..467e82a3a50dd 100644 --- a/packages/flutter/lib/src/widgets/scrollbar.dart +++ b/packages/flutter/lib/src/widgets/scrollbar.dart @@ -1675,7 +1675,7 @@ class RawScrollbarState extends State with TickerProv ..textDirection = Directionality.of(context) ..thickness = widget.thickness ?? _kScrollbarThickness ..radius = widget.radius - ..padding = widget.padding ?? MediaQuery.of(context).padding + ..padding = widget.padding ?? MediaQuery.paddingOf(context) ..scrollbarOrientation = widget.scrollbarOrientation ..mainAxisMargin = widget.mainAxisMargin ..shape = widget.shape diff --git a/packages/flutter/lib/src/widgets/selectable_region.dart b/packages/flutter/lib/src/widgets/selectable_region.dart index 0422a86cd9775..a12be125aaf97 100644 --- a/packages/flutter/lib/src/widgets/selectable_region.dart +++ b/packages/flutter/lib/src/widgets/selectable_region.dart @@ -350,7 +350,7 @@ class SelectableRegionState extends State with TextSelectionDe } // Hide the text selection toolbar on mobile when orientation changes. - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); if (_lastOrientation == null) { _lastOrientation = orientation; return; diff --git a/packages/flutter/lib/src/widgets/snapshot_widget.dart b/packages/flutter/lib/src/widgets/snapshot_widget.dart index a2d642201396c..854512f1f4528 100644 --- a/packages/flutter/lib/src/widgets/snapshot_widget.dart +++ b/packages/flutter/lib/src/widgets/snapshot_widget.dart @@ -141,7 +141,7 @@ class SnapshotWidget extends SingleChildRenderObjectWidget { return _RenderSnapshotWidget( controller: controller, mode: mode, - devicePixelRatio: MediaQuery.of(context).devicePixelRatio, + devicePixelRatio: MediaQuery.devicePixelRatioOf(context), painter: painter, autoresize: autoresize, ); @@ -153,7 +153,7 @@ class SnapshotWidget extends SingleChildRenderObjectWidget { (renderObject as _RenderSnapshotWidget) ..controller = controller ..mode = mode - ..devicePixelRatio = MediaQuery.of(context).devicePixelRatio + ..devicePixelRatio = MediaQuery.devicePixelRatioOf(context) ..painter = painter ..autoresize = autoresize; } diff --git a/packages/flutter/lib/src/widgets/text.dart b/packages/flutter/lib/src/widgets/text.dart index 622f1a50e11cd..2ee0e0c5badfc 100644 --- a/packages/flutter/lib/src/widgets/text.dart +++ b/packages/flutter/lib/src/widgets/text.dart @@ -598,7 +598,7 @@ class Text extends StatelessWidget { if (style == null || style!.inherit) { effectiveTextStyle = defaultTextStyle.style.merge(style); } - if (MediaQuery.boldTextOverride(context)) { + if (MediaQuery.boldTextOf(context)) { effectiveTextStyle = effectiveTextStyle!.merge(const TextStyle(fontWeight: FontWeight.bold)); } final SelectionRegistrar? registrar = SelectionContainer.maybeOf(context); diff --git a/packages/flutter/test/cupertino/action_sheet_test.dart b/packages/flutter/test/cupertino/action_sheet_test.dart index 1b849c9903923..80b1fc413e57f 100644 --- a/packages/flutter/test/cupertino/action_sheet_test.dart +++ b/packages/flutter/test/cupertino/action_sheet_test.dart @@ -362,7 +362,7 @@ void main() { await tester.pumpWidget( createAppWithButtonThatLaunchesActionSheet( Builder(builder: (BuildContext context) { - screenHeight = MediaQuery.of(context).size.height; + screenHeight = MediaQuery.sizeOf(context).height; return MediaQuery( data: MediaQuery.of(context).copyWith(textScaleFactor: 3.0), child: CupertinoActionSheet( diff --git a/packages/flutter/test/cupertino/dialog_test.dart b/packages/flutter/test/cupertino/dialog_test.dart index 791cd20e1b6da..d1e1b2b929932 100644 --- a/packages/flutter/test/cupertino/dialog_test.dart +++ b/packages/flutter/test/cupertino/dialog_test.dart @@ -588,7 +588,7 @@ void main() { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { - dividerWidth = 1.0 / MediaQuery.of(context).devicePixelRatio; + dividerWidth = 1.0 / MediaQuery.devicePixelRatioOf(context); return CupertinoAlertDialog( title: const Text('The Title'), content: const Text('The message'), @@ -633,7 +633,7 @@ void main() { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { - dividerThickness = 1.0 / MediaQuery.of(context).devicePixelRatio; + dividerThickness = 1.0 / MediaQuery.devicePixelRatioOf(context); return CupertinoAlertDialog( title: const Text('The Title'), content: const Text('The message'), @@ -841,7 +841,7 @@ void main() { await tester.pumpWidget( createAppWithButtonThatLaunchesDialog( dialogBuilder: (BuildContext context) { - dividerThickness = 1.0 / MediaQuery.of(context).devicePixelRatio; + dividerThickness = 1.0 / MediaQuery.devicePixelRatioOf(context); return CupertinoAlertDialog( title: const Text('The Title'), content: const Text('The message'), diff --git a/packages/flutter/test/cupertino/magnifier_test.dart b/packages/flutter/test/cupertino/magnifier_test.dart index 845de0ff6e9f8..0a7e5e31eb217 100644 --- a/packages/flutter/test/cupertino/magnifier_test.dart +++ b/packages/flutter/test/cupertino/magnifier_test.dart @@ -117,14 +117,14 @@ void main() { caretRect: reasonableTextField, // The tap position is far out of the right side of the app. globalGesturePosition: - Offset(MediaQuery.of(context).size.width + 100, 0), + Offset(MediaQuery.sizeOf(context).width + 100, 0), ), ), ); // Should be less than the right edge, since we have padding. expect(getMagnifierPosition(tester).dx, - lessThan(MediaQuery.of(context).size.width)); + lessThan(MediaQuery.sizeOf(context).width)); }); testWidgets('should have some vertical drag', (WidgetTester tester) async { @@ -150,7 +150,7 @@ void main() { caretRect: reasonableTextField, // The tap position is dragBelow units below the text field. globalGesturePosition: Offset( - MediaQuery.of(context).size.width / 2, + MediaQuery.sizeOf(context).width / 2, dragPositionBelowTextField), ), ), @@ -184,7 +184,7 @@ void main() { caretRect: reasonableTextField, // The tap position is dragBelow units below the text field. globalGesturePosition: Offset( - MediaQuery.of(context).size.width / 2, reasonableTextField.top), + MediaQuery.sizeOf(context).width / 2, reasonableTextField.top), ), ); @@ -223,7 +223,7 @@ void main() { fieldBounds: reasonableTextField, caretRect: reasonableTextField, // The tap position is dragBelow units below the text field. - globalGesturePosition: Offset(MediaQuery.of(context).size.width / 2, reasonableTextField.top), + globalGesturePosition: Offset(MediaQuery.sizeOf(context).width / 2, reasonableTextField.top), ), ); @@ -247,7 +247,7 @@ void main() { currentLineBoundaries: reasonableTextField, fieldBounds: reasonableTextField, caretRect: reasonableTextField, - globalGesturePosition: Offset(MediaQuery.of(context).size.width / 2, + globalGesturePosition: Offset(MediaQuery.sizeOf(context).width / 2, reasonableTextField.top)); await tester.pumpAndSettle(); diff --git a/packages/flutter/test/cupertino/tab_scaffold_test.dart b/packages/flutter/test/cupertino/tab_scaffold_test.dart index 6d17327fded47..9e76489704ff4 100644 --- a/packages/flutter/test/cupertino/tab_scaffold_test.dart +++ b/packages/flutter/test/cupertino/tab_scaffold_test.dart @@ -501,7 +501,7 @@ void main() { items: List.generate(2, tabGenerator), ), tabBuilder: (BuildContext context, int index) { - contentPadding = MediaQuery.of(context).padding; + contentPadding = MediaQuery.paddingOf(context); return const Placeholder(); }, ), diff --git a/packages/flutter/test/cupertino/text_selection_toolbar_test.dart b/packages/flutter/test/cupertino/text_selection_toolbar_test.dart index 5052d1ee91289..5f236968ea008 100644 --- a/packages/flutter/test/cupertino/text_selection_toolbar_test.dart +++ b/packages/flutter/test/cupertino/text_selection_toolbar_test.dart @@ -26,10 +26,10 @@ class _CustomCupertinoTextSelectionControls extends CupertinoTextSelectionContro ValueNotifier? clipboardStatus, Offset? lastSecondaryTapDownPosition, ) { - final MediaQueryData mediaQuery = MediaQuery.of(context); + final EdgeInsets mediaQueryPadding = MediaQuery.paddingOf(context); final double anchorX = (selectionMidpoint.dx + globalEditableRegion.left).clamp( - _kArrowScreenPadding + mediaQuery.padding.left, - mediaQuery.size.width - mediaQuery.padding.right - _kArrowScreenPadding, + _kArrowScreenPadding + mediaQueryPadding.left, + MediaQuery.sizeOf(context).width - mediaQueryPadding.right - _kArrowScreenPadding, ); final Offset anchorAbove = Offset( anchorX, diff --git a/packages/flutter/test/material/app_test.dart b/packages/flutter/test/material/app_test.dart index add149302a3c4..9d6c9c7352292 100644 --- a/packages/flutter/test/material/app_test.dart +++ b/packages/flutter/test/material/app_test.dart @@ -475,7 +475,7 @@ void main() { double? textScaleFactor; await tester.pumpWidget(MaterialApp( home: Builder(builder:(BuildContext context) { - textScaleFactor = MediaQuery.of(context).textScaleFactor; + textScaleFactor = MediaQuery.textScaleFactorOf(context); return Container(); }), )); diff --git a/packages/flutter/test/material/flexible_space_bar_test.dart b/packages/flutter/test/material/flexible_space_bar_test.dart index c55e4252cc712..29629167ccf44 100644 --- a/packages/flutter/test/material/flexible_space_bar_test.dart +++ b/packages/flutter/test/material/flexible_space_bar_test.dart @@ -438,7 +438,7 @@ void main() { home: Scaffold( body: Builder( builder: (BuildContext context) { - width = MediaQuery.of(context).size.width; + width = MediaQuery.sizeOf(context).width; return CustomScrollView( slivers: [ SliverAppBar( diff --git a/packages/flutter/test/material/popup_menu_test.dart b/packages/flutter/test/material/popup_menu_test.dart index 7b28d8836183b..92bcba1cf3318 100644 --- a/packages/flutter/test/material/popup_menu_test.dart +++ b/packages/flutter/test/material/popup_menu_test.dart @@ -2443,7 +2443,7 @@ void main() { value: 1, child: Builder( builder: (BuildContext context) { - mediaQueryPadding = MediaQuery.of(context).padding; + mediaQueryPadding = MediaQuery.paddingOf(context); return Text('-1-' * 500); // A long long text string. }, ), diff --git a/packages/flutter/test/material/scaffold_test.dart b/packages/flutter/test/material/scaffold_test.dart index d63ed5476f3e9..c0a7187c277a9 100644 --- a/packages/flutter/test/material/scaffold_test.dart +++ b/packages/flutter/test/material/scaffold_test.dart @@ -925,7 +925,7 @@ void main() { extendBody: extendBody, body: Builder( builder: (BuildContext context) { - mediaQueryBottom = MediaQuery.of(context).padding.bottom; + mediaQueryBottom = MediaQuery.paddingOf(context).bottom; return Container(key: bodyKey); }, ), @@ -998,7 +998,7 @@ void main() { ), body: Builder( builder: (BuildContext context) { - mediaQueryTop = MediaQuery.of(context).padding.top; + mediaQueryTop = MediaQuery.paddingOf(context).top; return Container(key: bodyKey); }, ), @@ -1957,7 +1957,7 @@ void main() { MaterialApp( home: Builder( builder: (BuildContext context) { - screenWidth = MediaQuery.of(context).size.width; + screenWidth = MediaQuery.sizeOf(context).width; return Scaffold( endDrawer: const Drawer( child: Text('Drawer'), diff --git a/packages/flutter/test/material/snack_bar_test.dart b/packages/flutter/test/material/snack_bar_test.dart index 4d5a847b878d6..7b8009f6759cc 100644 --- a/packages/flutter/test/material/snack_bar_test.dart +++ b/packages/flutter/test/material/snack_bar_test.dart @@ -229,7 +229,7 @@ void main() { home: Scaffold( body: Builder( builder: (BuildContext context) { - width = MediaQuery.of(context).size.width; + width = MediaQuery.sizeOf(context).width; return GestureDetector( key: tapTarget, diff --git a/packages/flutter/test/widgets/list_view_test.dart b/packages/flutter/test/widgets/list_view_test.dart index af9486a80358a..66dcaf47d554a 100644 --- a/packages/flutter/test/widgets/list_view_test.dart +++ b/packages/flutter/test/widgets/list_view_test.dart @@ -502,7 +502,7 @@ void main() { children: [ const Text('top', textDirection: TextDirection.ltr), Builder(builder: (BuildContext context) { - innerMediaQueryPadding = MediaQuery.of(context).padding; + innerMediaQueryPadding = MediaQuery.paddingOf(context); return Container(); }), ], diff --git a/packages/flutter/test/widgets/media_query_test.dart b/packages/flutter/test/widgets/media_query_test.dart index 63069bf5c4a1d..d5d66b38fc338 100644 --- a/packages/flutter/test/widgets/media_query_test.dart +++ b/packages/flutter/test/widgets/media_query_test.dart @@ -5,7 +5,7 @@ import 'dart:ui' show Brightness, DisplayFeature, DisplayFeatureState, DisplayFeatureType, GestureSettings, ViewConfiguration; import 'package:flutter/gestures.dart'; -import 'package:flutter/widgets.dart'; +import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { @@ -686,21 +686,21 @@ void main() { expect(insideHighContrast, true); }); - testWidgets('MediaQuery.boldTextOverride', (WidgetTester tester) async { + testWidgets('MediaQuery.boldTextOf', (WidgetTester tester) async { late bool outsideBoldTextOverride; late bool insideBoldTextOverride; await tester.pumpWidget( Builder( builder: (BuildContext context) { - outsideBoldTextOverride = MediaQuery.boldTextOverride(context); + outsideBoldTextOverride = MediaQuery.boldTextOf(context); return MediaQuery( data: const MediaQueryData( boldText: true, ), child: Builder( builder: (BuildContext context) { - insideBoldTextOverride = MediaQuery.boldTextOverride(context); + insideBoldTextOverride = MediaQuery.boldTextOf(context); return Container(); }, ), @@ -937,4 +937,80 @@ void main() { expect(MediaQueryData.fromWindow(tester.binding.window).gestureSettings.touchSlop, closeTo(33.33, 0.1)); // Repeating, of course tester.binding.window.viewConfigurationTestValue = null; }); + + testWidgets('MediaQuery can be partially depended-on', (WidgetTester tester) async { + MediaQueryData data = const MediaQueryData( + size: Size(800, 600), + textScaleFactor: 1.1 + ); + + int sizeBuildCount = 0; + int textScaleFactorBuildCount = 0; + + final Widget showSize = Builder( + builder: (BuildContext context) { + sizeBuildCount++; + return Text('size: ${MediaQuery.sizeOf(context)}'); + } + ); + + final Widget showTextScaleFactor = Builder( + builder: (BuildContext context) { + textScaleFactorBuildCount++; + return Text('textScaleFactor: ${MediaQuery.textScaleFactorOf(context).toStringAsFixed(1)}'); + } + ); + + final Widget page = StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return MediaQuery( + data: data, + child: Center( + child: Column( + children: [ + showSize, + showTextScaleFactor, + ElevatedButton( + onPressed: () { + setState(() { + data = data.copyWith(size: Size(data.size.width + 100, data.size.height)); + }); + }, + child: const Text('Increase width by 100') + ), + ElevatedButton( + onPressed: () { + setState(() { + data = data.copyWith(textScaleFactor: data.textScaleFactor + 0.1); + }); + }, + child: const Text('Increase textScaleFactor by 0.1') + ) + ] + ) + ) + ); + }, + ); + + await tester.pumpWidget(MaterialApp(home: page)); + expect(find.text('size: Size(800.0, 600.0)'), findsOneWidget); + expect(find.text('textScaleFactor: 1.1'), findsOneWidget); + expect(sizeBuildCount, 1); + expect(textScaleFactorBuildCount, 1); + + await tester.tap(find.text('Increase width by 100')); + await tester.pumpAndSettle(); + expect(find.text('size: Size(900.0, 600.0)'), findsOneWidget); + expect(find.text('textScaleFactor: 1.1'), findsOneWidget); + expect(sizeBuildCount, 2); + expect(textScaleFactorBuildCount, 1); + + await tester.tap(find.text('Increase textScaleFactor by 0.1')); + await tester.pumpAndSettle(); + expect(find.text('size: Size(900.0, 600.0)'), findsOneWidget); + expect(find.text('textScaleFactor: 1.2'), findsOneWidget); + expect(sizeBuildCount, 2); + expect(textScaleFactorBuildCount, 2); + }); } diff --git a/packages/flutter/test/widgets/render_object_widget_test.dart b/packages/flutter/test/widgets/render_object_widget_test.dart index c4e107232042b..39241cb18666a 100644 --- a/packages/flutter/test/widgets/render_object_widget_test.dart +++ b/packages/flutter/test/widgets/render_object_widget_test.dart @@ -28,7 +28,7 @@ class TestOrientedBox extends SingleChildRenderObjectWidget { const TestOrientedBox({ super.key, super.child }); Decoration _getDecoration(BuildContext context) { - final Orientation orientation = MediaQuery.of(context).orientation; + final Orientation orientation = MediaQuery.orientationOf(context); switch (orientation) { case Orientation.landscape: return const BoxDecoration(color: Color(0xFF00FF00)); diff --git a/packages/flutter/test_fixes/cupertino/cupertino.dart b/packages/flutter/test_fixes/cupertino/cupertino.dart index 4ea5c635a44ce..9ee91e3ab067e 100644 --- a/packages/flutter/test_fixes/cupertino/cupertino.dart +++ b/packages/flutter/test_fixes/cupertino/cupertino.dart @@ -235,4 +235,7 @@ void main() { scrollBehavior.buildViewportChrome(context, child, axisDirection); final CupertinoScrollBehavior cupertinoScrollBehavior = CupertinoScrollBehavior(); cupertinoScrollBehavior.buildViewportChrome(context, child, axisDirection); + + // Changes made in https://github.com/flutter/flutter/pull/114459 + MediaQuery.boldTextOverride(context); } diff --git a/packages/flutter/test_fixes/cupertino/cupertino.dart.expect b/packages/flutter/test_fixes/cupertino/cupertino.dart.expect index 28ed8b9042d38..c9ef40bc623c3 100644 --- a/packages/flutter/test_fixes/cupertino/cupertino.dart.expect +++ b/packages/flutter/test_fixes/cupertino/cupertino.dart.expect @@ -235,4 +235,7 @@ void main() { scrollBehavior.buildOverscrollIndicator(context, child, axisDirection); final CupertinoScrollBehavior cupertinoScrollBehavior = CupertinoScrollBehavior(); cupertinoScrollBehavior.buildOverscrollIndicator(context, child, axisDirection); + + // Changes made in https://github.com/flutter/flutter/pull/114459 + MediaQuery.boldTextOf(context); } diff --git a/packages/flutter/test_fixes/material/material.dart b/packages/flutter/test_fixes/material/material.dart index dffef1322f3a1..c5c7a9af54212 100644 --- a/packages/flutter/test_fixes/material/material.dart +++ b/packages/flutter/test_fixes/material/material.dart @@ -307,4 +307,7 @@ void main() { ScrollbarThemeData scrollbarTheme = ScrollbarThemeData(showTrackOnHover: nowShowing); scrollbarTheme.copyWith(showTrackOnHover: nowShowing); scrollbarTheme.showTrackOnHover; + + // Changes made in https://github.com/flutter/flutter/pull/114459 + MediaQuery.boldTextOverride(context); } diff --git a/packages/flutter/test_fixes/material/material.dart.expect b/packages/flutter/test_fixes/material/material.dart.expect index 290e79075d47d..50f044ab94e37 100644 --- a/packages/flutter/test_fixes/material/material.dart.expect +++ b/packages/flutter/test_fixes/material/material.dart.expect @@ -303,4 +303,7 @@ void main() { ScrollbarThemeData scrollbarTheme = ScrollbarThemeData(trackVisibility: nowShowing); scrollbarTheme.copyWith(trackVisibility: nowShowing); scrollbarTheme.trackVisibility; + + // Changes made in https://github.com/flutter/flutter/pull/114459 + MediaQuery.boldTextOf(context); } diff --git a/packages/flutter/test_fixes/widgets/widgets.dart b/packages/flutter/test_fixes/widgets/widgets.dart index b3f777b086e1e..88481964fbd71 100644 --- a/packages/flutter/test_fixes/widgets/widgets.dart +++ b/packages/flutter/test_fixes/widgets/widgets.dart @@ -150,4 +150,7 @@ void main() { // Changes made in https://github.com/flutter/flutter/pull/78588 final ScrollBehavior scrollBehavior = ScrollBehavior(); scrollBehavior.buildViewportChrome(context, child, axisDirection); + + // Changes made in https://github.com/flutter/flutter/pull/114459 + MediaQuery.boldTextOverride(context); } diff --git a/packages/flutter/test_fixes/widgets/widgets.dart.expect b/packages/flutter/test_fixes/widgets/widgets.dart.expect index 79255c6465151..82ad2e146656d 100644 --- a/packages/flutter/test_fixes/widgets/widgets.dart.expect +++ b/packages/flutter/test_fixes/widgets/widgets.dart.expect @@ -150,4 +150,7 @@ void main() { // Changes made in https://github.com/flutter/flutter/pull/78588 final ScrollBehavior scrollBehavior = ScrollBehavior(); scrollBehavior.buildOverscrollIndicator(context, child, axisDirection); + + // Changes made in https://github.com/flutter/flutter/pull/114459 + MediaQuery.boldTextOf(context); } From 1da8f4edc4050d54107d9ee0266caf68aa1c8c97 Mon Sep 17 00:00:00 2001 From: godofredoc Date: Fri, 9 Dec 2022 13:12:24 -0800 Subject: [PATCH 12/71] Several fixes to packaging builders. (#116800) It includes the following changes: * Adds main as the enabled branches. * Adds docs_beta and docs_stable to pass the expected gcp project. * Adds dimensions to packaging arm64 to ensure that it runs on arm64 bot. Bug: https://github.com/orgs/flutter/projects/43 --- .ci.yaml | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index b19031069c2c7..63a7c40a65523 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -4656,6 +4656,7 @@ targets: enabled_branches: - beta - stable + - main properties: task_name: flutter_packaging tags: > @@ -4669,6 +4670,7 @@ targets: enabled_branches: - beta - stable + - main properties: task_name: flutter_packaging tags: > @@ -4683,10 +4685,13 @@ targets: enabled_branches: - beta - stable + - main properties: task_name: flutter_packaging tags: > ["framework", "hostonly", "shard", "mac"] + drone_dimensions: > + ["cpu=arm64"] - name: Windows flutter_packaging @@ -4697,16 +4702,20 @@ targets: enabled_branches: - beta - stable + - main properties: task_name: flutter_packaging tags: > ["framework", "hostonly", "shard", "windows"] - - name: Linux docs_deploy + - name: Linux docs_deploy_beta recipe: flutter/flutter scheduler: release bringup: true + enabled_branches: + - beta + - main presubmit: false timeout: 60 properties: @@ -4720,5 +4729,25 @@ targets: ["framework", "hostonly", "linux"] validation: docs_deploy validation_name: Docs_deploy - # TODO(godofredoc): Update to docs-flutter-dev before release. firebase_project: master-docs-flutter-dev + + - name: Linux docs_deploy_stable + recipe: flutter/flutter + scheduler: release + bringup: true + enabled_branches: + - stable + presubmit: false + timeout: 60 + properties: + cores: "32" + dependencies: >- + [ + {"dependency": "dashing", "version": "0.4.0"}, + {"dependency": "firebase", "version": "v11.0.1"} + ] + tags: > + ["framework", "hostonly", "linux"] + validation: docs_deploy + validation_name: Docs_deploy + firebase_project: docs-flutter-dev From 86fa9e511dd4082058a9d4a391b439bd6cdcce2b Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 16:57:50 -0500 Subject: [PATCH 13/71] Roll Flutter Engine from 8d83b98c55b3 to 030950f3070c (29 revisions) (#116802) * bd8bcf956 Roll Fuchsia Mac SDK from crEcyXdyZ686cAqMV... to pMV6A0ykZQ8aA3NG2... (flutter/engine#38120) * dec8b5221 Preliminary implementation of UIA for A11y on Windows (flutter/engine#37754) * 5545ccf87 Roll Fuchsia Linux SDK from NlJGkMbtZqQ6_BCpu... to xn8ztWtp-zww-jObz... (flutter/engine#38122) * 80a15a419 Create FlutterActivity/FlutterFragment using light weight engine with FlutterEngineGroup (flutter/engine#36963) * 5caef8585 Full implementation of text-input-test (flutter/engine#37986) * 8f6036e58 Reland fix wrong VSYNC event (flutter/engine#37865) * 4101c363c [iOS] Change locale format for spell check (flutter/engine#38080) * 2f5b67e4d [embedder] Ensure destruction called on present (flutter/engine#38078) * 0bddc6045 [Impeller Scene] Depth attachment; baked lighting example (flutter/engine#38118) * 6aa4ccd60 Remove dlCanvasRecorder from flutter::PictureRecorder (flutter/engine#38127) * dbb5284f2 [Windows] Add more cursor plugin tests (flutter/engine#38112) * 6e91204d9 Roll Fuchsia Mac SDK from pMV6A0ykZQ8aA3NG2... to 9SnrQ0vbR8IC7UIoP... (flutter/engine#38135) * 3140ad924 [Impeller] order metal samplers according to declared order and not usage order (flutter/engine#38115) * 84abf21d4 Remove autoninja. (flutter/engine#38136) * 8a113d328 [embedder] Expose metal surface from test context (flutter/engine#38133) * 1ef25b63f Roll Fuchsia Mac SDK from 9SnrQ0vbR8IC7UIoP... to aMW0DjntzFJj4RoR3... (flutter/engine#38139) * 748b3bc15 Revert "Remove dlCanvasRecorder from flutter::PictureRecorder (#38127)" (flutter/engine#38137) * b6daf3d06 [embedder] Consistent naming for GL/Metal tests (flutter/engine#38141) * 339d04baf [web] Trivial fix for non-static interop JS interop class. (flutter/engine#38126) * 1fcbb9c11 [tools] Eliminate version on Obj-C docs (flutter/engine#38145) * 71928b6a4 [Impeller] Use DrawPath instead of Rect geometry when the paint style is stroke (flutter/engine#38146) * 23ce8fdbc Roll Skia from dd3285a80b23 to f84dc9303045 (4 revisions) (flutter/engine#38123) * 366f8663b Roll Skia from f84dc9303045 to 2691cd7b4110 (40 revisions) (flutter/engine#38151) * 447e7013e Roll Skia from 2691cd7b4110 to 711396b81248 (1 revision) (flutter/engine#38152) * cd5d91bf9 Pylint testing/run_tests.py (flutter/engine#38016) * aafac083b Roll Skia from 711396b81248 to b253b10374e7 (7 revisions) (flutter/engine#38157) * 799dc78e8 Roll Fuchsia Linux SDK from xn8ztWtp-zww-jObz... to rRJIjuO-dPNCpCTd9... (flutter/engine#38134) * 3aa3d2a8f Massage the JS interop around `didCreateEngineInitializer` (flutter/engine#38147) * 030950f30 Roll Skia from b253b10374e7 to ec407902999b (3 revisions) (flutter/engine#38158) --- bin/internal/engine.version | 2 +- bin/internal/fuchsia-linux.version | 2 +- bin/internal/fuchsia-mac.version | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 072bbac42d53b..4382a00c6cd48 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -8d83b98c55b3d0839de0c4201f5a8d56dbf92d2f +030950f3070c370b5c76e8c67642c4839ee3dd37 diff --git a/bin/internal/fuchsia-linux.version b/bin/internal/fuchsia-linux.version index f0988f3df04b1..bd8b76f55f2de 100644 --- a/bin/internal/fuchsia-linux.version +++ b/bin/internal/fuchsia-linux.version @@ -1 +1 @@ -NlJGkMbtZqQ6_BCpu5Mif5-14birO7qsn-VP2ULJK8MC +rRJIjuO-dPNCpCTd9fnOc5ISL21HS5JbpRnk-HNpdsQC diff --git a/bin/internal/fuchsia-mac.version b/bin/internal/fuchsia-mac.version index 4db9411ac0af5..d9c37b8261251 100644 --- a/bin/internal/fuchsia-mac.version +++ b/bin/internal/fuchsia-mac.version @@ -1 +1 @@ -crEcyXdyZ686cAqMVp8W2Vm53QVM8cBWs9TabSIKdQkC +aMW0DjntzFJj4RoR38FYLYDD3_0iRHaSyKd3Nfep9K4C From ca3ce3945c27b8d7548637bacdc2fcaadad3b6a2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 17:41:14 -0500 Subject: [PATCH 14/71] 89fd33c62 Don't use sync*, as it is unimplemented in dart2wasm. (flutter/engine#38149) (#116808) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 4382a00c6cd48..069903295c221 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -030950f3070c370b5c76e8c67642c4839ee3dd37 +89fd33c62f2ca7edbdc25e4b28c91c19323bcb82 From 332032ddaeaa9f0c79777d5ff8708dc1c1697745 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 18:26:21 -0500 Subject: [PATCH 15/71] Roll Flutter Engine from 89fd33c62f2c to a259613ab871 (2 revisions) (#116811) * 2121d8187 [web] Use js_util.promiseToFuture with CanvasKitInit() (flutter/engine#38128) * a259613ab Fix premature LayerStateStack layer culling (flutter/engine#38159) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 069903295c221..0913cdbff7287 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -89fd33c62f2ca7edbdc25e4b28c91c19323bcb82 +a259613ab871d301834205d2a4c8da3327d95250 From 9dd30878d9333702096aee8a54ec0a963e6c9e78 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Fri, 9 Dec 2022 15:48:00 -0800 Subject: [PATCH 16/71] Add LookupBoundary to Material (#116736) --- packages/flutter/lib/src/material/debug.dart | 14 +- .../flutter/lib/src/material/material.dart | 16 ++- .../lib/src/widgets/lookup_boundary.dart | 47 +++++++ .../flutter/test/material/debug_test.dart | 3 +- .../flutter/test/material/material_test.dart | 95 ++++++++++++++ .../test/widgets/lookup_boundary_test.dart | 124 ++++++++++++++++++ 6 files changed, 291 insertions(+), 8 deletions(-) diff --git a/packages/flutter/lib/src/material/debug.dart b/packages/flutter/lib/src/material/debug.dart index 7afa028abe016..dbd43a75ae0c3 100644 --- a/packages/flutter/lib/src/material/debug.dart +++ b/packages/flutter/lib/src/material/debug.dart @@ -11,7 +11,8 @@ import 'scaffold.dart' show Scaffold, ScaffoldMessenger; // Examples can assume: // late BuildContext context; -/// Asserts that the given context has a [Material] ancestor. +/// Asserts that the given context has a [Material] ancestor within the closest +/// [LookupBoundary]. /// /// Used by many Material Design widgets to make sure that they are /// only used in contexts where they can print ink onto some material. @@ -32,12 +33,17 @@ import 'scaffold.dart' show Scaffold, ScaffoldMessenger; /// Does nothing if asserts are disabled. Always returns true. bool debugCheckHasMaterial(BuildContext context) { assert(() { - if (context.widget is! Material && context.findAncestorWidgetOfExactType() == null) { + if (LookupBoundary.findAncestorWidgetOfExactType(context) == null) { + final bool hiddenByBoundary = LookupBoundary.debugIsHidingAncestorWidgetOfExactType(context); throw FlutterError.fromParts([ - ErrorSummary('No Material widget found.'), + ErrorSummary('No Material widget found${hiddenByBoundary ? ' within the closest LookupBoundary' : ''}.'), + if (hiddenByBoundary) + ErrorDescription( + 'There is an ancestor Material widget, but it is hidden by a LookupBoundary.' + ), ErrorDescription( '${context.widget.runtimeType} widgets require a Material ' - 'widget ancestor.\n' + 'widget ancestor within the closest LookupBoundary.\n' 'In Material Design, most widgets are conceptually "printed" on ' "a sheet of material. In Flutter's material library, that " 'material is represented by the Material widget. It is the ' diff --git a/packages/flutter/lib/src/material/material.dart b/packages/flutter/lib/src/material/material.dart index 6e2a770dea3a2..481e58fd6d58a 100644 --- a/packages/flutter/lib/src/material/material.dart +++ b/packages/flutter/lib/src/material/material.dart @@ -343,7 +343,7 @@ class Material extends StatefulWidget { final BorderRadiusGeometry? borderRadius; /// The ink controller from the closest instance of this class that - /// encloses the given context. + /// encloses the given context within the closest [LookupBoundary]. /// /// Typical usage is as follows: /// @@ -358,11 +358,11 @@ class Material extends StatefulWidget { /// * [Material.of], which is similar to this method, but asserts if /// no [Material] ancestor is found. static MaterialInkController? maybeOf(BuildContext context) { - return context.findAncestorRenderObjectOfType<_RenderInkFeatures>(); + return LookupBoundary.findAncestorRenderObjectOfType<_RenderInkFeatures>(context); } /// The ink controller from the closest instance of [Material] that encloses - /// the given context. + /// the given context within the closest [LookupBoundary]. /// /// If no [Material] widget ancestor can be found then this method will assert /// in debug mode, and throw an exception in release mode. @@ -383,6 +383,16 @@ class Material extends StatefulWidget { final MaterialInkController? controller = maybeOf(context); assert(() { if (controller == null) { + if (LookupBoundary.debugIsHidingAncestorRenderObjectOfType<_RenderInkFeatures>(context)) { + throw FlutterError( + 'Material.of() was called with a context that does not have access to a Material widget.\n' + 'The context provided to Material.of() does have a Material widget ancestor, but it is ' + 'hidden by a LookupBoundary. This can happen because you are using a widget that looks ' + 'for a Material ancestor, but no such ancestor exists within the closest LookupBoundary.\n' + 'The context used was:\n' + ' $context', + ); + } throw FlutterError( 'Material.of() was called with a context that does not contain a Material widget.\n' 'No Material widget ancestor could be found starting from the context that was passed to ' diff --git a/packages/flutter/lib/src/widgets/lookup_boundary.dart b/packages/flutter/lib/src/widgets/lookup_boundary.dart index dc903dade470b..e839b447c4136 100644 --- a/packages/flutter/lib/src/widgets/lookup_boundary.dart +++ b/packages/flutter/lib/src/widgets/lookup_boundary.dart @@ -250,6 +250,53 @@ class LookupBoundary extends InheritedWidget { }); } + /// Returns true if a [LookupBoundary] is hiding the nearest + /// [Widget] of the specified type `T` from the provided [BuildContext]. + /// + /// This method throws when asserts are disabled. + static bool debugIsHidingAncestorWidgetOfExactType(BuildContext context) { + bool? result; + assert(() { + bool hiddenByBoundary = false; + bool ancestorFound = false; + context.visitAncestorElements((Element ancestor) { + if (ancestor.widget.runtimeType == T) { + ancestorFound = true; + return false; + } + hiddenByBoundary = hiddenByBoundary || ancestor.widget.runtimeType == LookupBoundary; + return true; + }); + result = ancestorFound & hiddenByBoundary; + return true; + } ()); + return result!; + } + + /// Returns true if a [LookupBoundary] is hiding the nearest + /// [RenderObjectWidget] with a [RenderObject] of the specified type `T` + /// from the provided [BuildContext]. + /// + /// This method throws when asserts are disabled. + static bool debugIsHidingAncestorRenderObjectOfType(BuildContext context) { + bool? result; + assert(() { + bool hiddenByBoundary = false; + bool ancestorFound = false; + context.visitAncestorElements((Element ancestor) { + if (ancestor is RenderObjectElement && ancestor.renderObject is T) { + ancestorFound = true; + return false; + } + hiddenByBoundary = hiddenByBoundary || ancestor.widget.runtimeType == LookupBoundary; + return true; + }); + result = ancestorFound & hiddenByBoundary; + return true; + } ()); + return result!; + } + @override bool updateShouldNotify(covariant InheritedWidget oldWidget) => false; } diff --git a/packages/flutter/test/material/debug_test.dart b/packages/flutter/test/material/debug_test.dart index 208ee587989f7..ce7b4dec45ba8 100644 --- a/packages/flutter/test/material/debug_test.dart +++ b/packages/flutter/test/material/debug_test.dart @@ -28,7 +28,8 @@ void main() { error.toStringDeep(), 'FlutterError\n' ' No Material widget found.\n' - ' Chip widgets require a Material widget ancestor.\n' + ' Chip widgets require a Material widget ancestor within the\n' + ' closest LookupBoundary.\n' ' In Material Design, most widgets are conceptually "printed" on a\n' " sheet of material. In Flutter's material library, that material\n" ' is represented by the Material widget. It is the Material widget\n' diff --git a/packages/flutter/test/material/material_test.dart b/packages/flutter/test/material/material_test.dart index ca4a6af5df177..1ad380d440c17 100644 --- a/packages/flutter/test/material/material_test.dart +++ b/packages/flutter/test/material/material_test.dart @@ -1034,6 +1034,101 @@ void main() { materialKey.currentContext!.findRenderObject()!.paint(PaintingContext(ContainerLayer(), Rect.largest), Offset.zero); expect(tracker.paintCount, 2); }); + + group('LookupBoundary', () { + testWidgets('hides Material from Material.maybeOf', (WidgetTester tester) async { + MaterialInkController? material; + + await tester.pumpWidget( + Material( + child: LookupBoundary( + child: Builder( + builder: (BuildContext context) { + material = Material.maybeOf(context); + return Container(); + }, + ), + ), + ), + ); + + expect(material, isNull); + }); + + testWidgets('hides Material from Material.of', (WidgetTester tester) async { + await tester.pumpWidget( + Material( + child: LookupBoundary( + child: Builder( + builder: (BuildContext context) { + Material.of(context); + return Container(); + }, + ), + ), + ), + ); + final Object? exception = tester.takeException(); + expect(exception, isFlutterError); + final FlutterError error = exception! as FlutterError; + + expect( + error.toStringDeep(), + 'FlutterError\n' + ' Material.of() was called with a context that does not have access\n' + ' to a Material widget.\n' + ' The context provided to Material.of() does have a Material widget\n' + ' ancestor, but it is hidden by a LookupBoundary. This can happen\n' + ' because you are using a widget that looks for a Material\n' + ' ancestor, but no such ancestor exists within the closest\n' + ' LookupBoundary.\n' + ' The context used was:\n' + ' Builder(dirty)\n' + ); + }); + + testWidgets('hides Material from debugCheckHasMaterial', (WidgetTester tester) async { + await tester.pumpWidget( + Material( + child: LookupBoundary( + child: Builder( + builder: (BuildContext context) { + debugCheckHasMaterial(context); + return Container(); + }, + ), + ), + ), + ); + final Object? exception = tester.takeException(); + expect(exception, isFlutterError); + final FlutterError error = exception! as FlutterError; + + expect( + error.toStringDeep(), startsWith( + 'FlutterError\n' + ' No Material widget found within the closest LookupBoundary.\n' + ' There is an ancestor Material widget, but it is hidden by a\n' + ' LookupBoundary.\n' + ' Builder widgets require a Material widget ancestor within the\n' + ' closest LookupBoundary.\n' + ' In Material Design, most widgets are conceptually "printed" on a\n' + " sheet of material. In Flutter's material library, that material\n" + ' is represented by the Material widget. It is the Material widget\n' + ' that renders ink splashes, for instance. Because of this, many\n' + ' material library widgets require that there be a Material widget\n' + ' in the tree above them.\n' + ' To introduce a Material widget, you can either directly include\n' + ' one, or use a widget that contains Material itself, such as a\n' + ' Card, Dialog, Drawer, or Scaffold.\n' + ' The specific widget that could not find a Material ancestor was:\n' + ' Builder\n' + ' The ancestors of this widget were:\n' + ' LookupBoundary\n' + ), + ); + }); + }); } class TrackPaintInkFeature extends InkFeature { diff --git a/packages/flutter/test/widgets/lookup_boundary_test.dart b/packages/flutter/test/widgets/lookup_boundary_test.dart index c5b8bfcb5f1d3..41d18f3260f54 100644 --- a/packages/flutter/test/widgets/lookup_boundary_test.dart +++ b/packages/flutter/test/widgets/lookup_boundary_test.dart @@ -958,6 +958,130 @@ void main() { }); }); + + group('LookupBoundary.debugIsHidingAncestorWidgetOfExactType', () { + testWidgets('is hiding', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Container( + color: Colors.blue, + child: LookupBoundary( + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorWidgetOfExactType(context); + return Container(); + }, + ), + ), + )); + expect(isHidden, isTrue); + }); + + testWidgets('is not hiding entity within boundary', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Container( + color: Colors.blue, + child: LookupBoundary( + child: Container( + color: Colors.red, + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorWidgetOfExactType(context); + return Container(); + }, + ), + ), + ), + )); + expect(isHidden, isFalse); + }); + + testWidgets('is not hiding if no boundary exists', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Container( + color: Colors.blue, + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorWidgetOfExactType(context); + return Container(); + }, + ), + )); + expect(isHidden, isFalse); + }); + + testWidgets('is not hiding if no boundary and no entity exists', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorWidgetOfExactType(context); + return Container(); + }, + )); + expect(isHidden, isFalse); + }); + }); + + group('LookupBoundary.debugIsHidingAncestorRenderObjectOfType', () { + testWidgets('is hiding', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Padding( + padding: EdgeInsets.zero, + child: LookupBoundary( + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorRenderObjectOfType(context); + return Container(); + }, + ), + ), + )); + expect(isHidden, isTrue); + }); + + testWidgets('is not hiding entity within boundary', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Padding( + padding: EdgeInsets.zero, + child: LookupBoundary( + child: Padding( + padding: EdgeInsets.zero, + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorRenderObjectOfType(context); + return Container(); + }, + ), + ), + ), + )); + expect(isHidden, isFalse); + }); + + testWidgets('is not hiding if no boundary exists', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Padding( + padding: EdgeInsets.zero, + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorRenderObjectOfType(context); + return Container(); + }, + ), + )); + expect(isHidden, isFalse); + }); + + testWidgets('is not hiding if no boundary and no entity exists', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorRenderObjectOfType(context); + return Container(); + }, + )); + expect(isHidden, isFalse); + }); + }); } class MyStatefulContainer extends StatefulWidget { From cbdc763cfd34f8d3734794881ad274079f3070e4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 19:06:27 -0500 Subject: [PATCH 17/71] Roll Flutter Engine from a259613ab871 to 8b56b5a98ed4 (2 revisions) (#116813) * 59fabcc89 Roll Skia from ec407902999b to 44062eff3e25 (8 revisions) (flutter/engine#38161) * 8b56b5a98 Roll Dart SDK from e517487c5679 to 0940b5e6ccd5 (3 revisions) (flutter/engine#38162) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 0913cdbff7287..626c13d7b9eaf 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -a259613ab871d301834205d2a4c8da3327d95250 +8b56b5a98ed4d9a36ee72aefc08f155c21cdd339 From c4b8046d96ed8a85f3a1afc04127c8ec69162f53 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Fri, 9 Dec 2022 19:24:23 -0500 Subject: [PATCH 18/71] Floating cursor cleanup (#116746) * Floating cursor cleanup * Use TextSelection.fromPosition --- .../flutter/lib/src/rendering/editable.dart | 2 +- .../lib/src/widgets/editable_text.dart | 8 +- .../flutter/test/rendering/editable_test.dart | 74 +++++++++ .../test/widgets/editable_text_test.dart | 157 ++++++++++++++++++ 4 files changed, 237 insertions(+), 4 deletions(-) diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index b5a3b59ea14db..cc06f96fcb80a 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -3102,7 +3102,7 @@ class _FloatingCursorPainter extends RenderEditablePainter { } canvas.drawRRect( - RRect.fromRectAndRadius(floatingCursorRect.shift(renderEditable._paintOffset), _kFloatingCaretRadius), + RRect.fromRectAndRadius(floatingCursorRect, _kFloatingCaretRadius), floatingCursorPaint..color = floatingCursorColor, ); } diff --git a/packages/flutter/lib/src/widgets/editable_text.dart b/packages/flutter/lib/src/widgets/editable_text.dart index e782b44881ff3..94a66ee6a3f2f 100644 --- a/packages/flutter/lib/src/widgets/editable_text.dart +++ b/packages/flutter/lib/src/widgets/editable_text.dart @@ -2671,7 +2671,7 @@ class EditableTextState extends State with AutomaticKeepAliveClien // we cache the position. _pointOffsetOrigin = point.offset; - final TextPosition currentTextPosition = TextPosition(offset: renderEditable.selection!.baseOffset); + final TextPosition currentTextPosition = TextPosition(offset: renderEditable.selection!.baseOffset, affinity: renderEditable.selection!.affinity); _startCaretRect = renderEditable.getLocalRectForCaret(currentTextPosition); _lastBoundedOffset = _startCaretRect!.center - _floatingCursorOffset; @@ -2702,9 +2702,11 @@ class EditableTextState extends State with AutomaticKeepAliveClien final Offset finalPosition = renderEditable.getLocalRectForCaret(_lastTextPosition!).centerLeft - _floatingCursorOffset; if (_floatingCursorResetController!.isCompleted) { renderEditable.setFloatingCursor(FloatingCursorDragState.End, finalPosition, _lastTextPosition!); - if (_lastTextPosition!.offset != renderEditable.selection!.baseOffset) { + // Only change if new position is out of current selection range, as the + // selection may have been modified using the iOS keyboard selection gesture. + if (_lastTextPosition!.offset < renderEditable.selection!.start || _lastTextPosition!.offset >= renderEditable.selection!.end) { // The cause is technically the force cursor, but the cause is listed as tap as the desired functionality is the same. - _handleSelectionChanged(TextSelection.collapsed(offset: _lastTextPosition!.offset), SelectionChangedCause.forcePress); + _handleSelectionChanged(TextSelection.fromPosition(_lastTextPosition!), SelectionChangedCause.forcePress); } _startCaretRect = null; _lastTextPosition = null; diff --git a/packages/flutter/test/rendering/editable_test.dart b/packages/flutter/test/rendering/editable_test.dart index 06839cb169ece..7a96f14027378 100644 --- a/packages/flutter/test/rendering/editable_test.dart +++ b/packages/flutter/test/rendering/editable_test.dart @@ -12,6 +12,7 @@ import 'package:flutter/foundation.dart'; import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/src/services/text_input.dart'; import 'package:flutter_test/flutter_test.dart'; import 'mock_canvas.dart'; @@ -1725,6 +1726,79 @@ void main() { editable.forceLine = false; expect(editable.computeDryLayout(constraints).width, lessThan(initialWidth)); }); + + test('Floating cursor position is independent of viewport offset', () { + final TextSelectionDelegate delegate = _FakeEditableTextState(); + final ValueNotifier showCursor = ValueNotifier(true); + EditableText.debugDeterministicCursor = true; + + const Color cursorColor = Color.fromARGB(0xFF, 0xFF, 0x00, 0x00); + + final RenderEditable editable = RenderEditable( + backgroundCursorColor: Colors.grey, + textDirection: TextDirection.ltr, + cursorColor: cursorColor, + offset: ViewportOffset.zero(), + textSelectionDelegate: delegate, + text: const TextSpan( + text: 'test', + style: TextStyle( + height: 1.0, fontSize: 10.0, fontFamily: 'Ahem', + ), + ), + maxLines: 3, + startHandleLayerLink: LayerLink(), + endHandleLayerLink: LayerLink(), + selection: const TextSelection.collapsed( + offset: 4, + affinity: TextAffinity.upstream, + ), + ); + + layout(editable); + + editable.layout(BoxConstraints.loose(const Size(100, 100))); + // Prepare for painting after layout. + pumpFrame(phase: EnginePhase.compositingBits); + + expect( + editable, + // Draw no cursor by default. + paintsExactlyCountTimes(#drawRect, 0), + ); + + editable.showCursor = showCursor; + editable.setFloatingCursor(FloatingCursorDragState.Start, const Offset(50, 50), const TextPosition( + offset: 4, + affinity: TextAffinity.upstream, + )); + pumpFrame(phase: EnginePhase.compositingBits); + + final RRect expectedRRect = RRect.fromRectAndRadius( + const Rect.fromLTWH(49.5, 51, 2, 8), + const Radius.circular(1) + ); + + expect(editable, paints..rrect( + color: cursorColor.withOpacity(0.75), + rrect: expectedRRect + )); + + // Change the text viewport offset. + editable.offset = ViewportOffset.fixed(200); + + // Floating cursor should be drawn in the same position. + editable.setFloatingCursor(FloatingCursorDragState.Start, const Offset(50, 50), const TextPosition( + offset: 4, + affinity: TextAffinity.upstream, + )); + pumpFrame(phase: EnginePhase.compositingBits); + + expect(editable, paints..rrect( + color: cursorColor.withOpacity(0.75), + rrect: expectedRRect + )); + }); } class _TestRenderEditable extends RenderEditable { diff --git a/packages/flutter/test/widgets/editable_text_test.dart b/packages/flutter/test/widgets/editable_text_test.dart index 5d57ab4627915..818b298f51581 100644 --- a/packages/flutter/test/widgets/editable_text_test.dart +++ b/packages/flutter/test/widgets/editable_text_test.dart @@ -11701,6 +11701,163 @@ void main() { expect(tester.hasRunningAnimations, isFalse); }); + testWidgets('Floating cursor affinity', (WidgetTester tester) async { + EditableText.debugDeterministicCursor = true; + final FocusNode focusNode = FocusNode(); + final GlobalKey key = GlobalKey(); + // Set it up so that there will be word-wrap. + final TextEditingController controller = TextEditingController(text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz'); + await tester.pumpWidget( + MaterialApp( + home: Center( + child: ConstrainedBox( + constraints: const BoxConstraints( + maxWidth: 500, + ), + child: EditableText( + key: key, + autofocus: true, + maxLines: 2, + controller: controller, + focusNode: focusNode, + style: textStyle, + cursorColor: Colors.blue, + backgroundCursorColor: Colors.grey, + cursorOpacityAnimates: true, + ), + ), + ), + ), + ); + + await tester.pump(); + final EditableTextState state = tester.state(find.byType(EditableText)); + + // Select after the first word, with default affinity (downstream). + controller.selection = const TextSelection.collapsed(offset: 27); + await tester.pump(); + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Start, offset: Offset.zero)); + await tester.pump(); + + // The floating cursor should be drawn at the end of the first line. + expect(key.currentContext!.findRenderObject(), paints..rrect( + rrect: RRect.fromRectAndRadius( + const Rect.fromLTWH(0.5, 15, 3, 12), + const Radius.circular(1) + ) + )); + + // Select after the first word, with upstream affinity. + controller.selection = const TextSelection.collapsed(offset: 27, affinity: TextAffinity.upstream); + await tester.pump(); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Start, offset: Offset.zero)); + await tester.pump(); + + // The floating cursor should be drawn at the beginning of the second line. + expect(key.currentContext!.findRenderObject(), paints..rrect( + rrect: RRect.fromRectAndRadius( + const Rect.fromLTWH(378.5, 1, 3, 12), + const Radius.circular(1) + ) + )); + + EditableText.debugDeterministicCursor = false; + }); + +testWidgets('Floating cursor ending with selection', (WidgetTester tester) async { + EditableText.debugDeterministicCursor = true; + final FocusNode focusNode = FocusNode(); + final GlobalKey key = GlobalKey(); + // Set it up so that there will be word-wrap. + final TextEditingController controller = TextEditingController(text: 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'); + controller.selection = const TextSelection.collapsed(offset: 0); + await tester.pumpWidget( + MaterialApp( + home: EditableText( + key: key, + autofocus: true, + controller: controller, + focusNode: focusNode, + style: textStyle, + cursorColor: Colors.blue, + backgroundCursorColor: Colors.grey, + cursorOpacityAnimates: true, + ), + ), + ); + + await tester.pump(); + final EditableTextState state = tester.state(find.byType(EditableText)); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Start, offset: Offset.zero)); + await tester.pump(); + + // The floating cursor should be drawn at the start of the line. + expect(key.currentContext!.findRenderObject(), paints..rrect( + rrect: RRect.fromRectAndRadius( + const Rect.fromLTWH(0.5, 1, 3, 12), + const Radius.circular(1) + ) + )); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Update, offset: const Offset(50, 0))); + await tester.pump(); + + // The floating cursor should be drawn somewhere in the middle of the line + expect(key.currentContext!.findRenderObject(), paints..rrect( + rrect: RRect.fromRectAndRadius( + const Rect.fromLTWH(50.5, 1, 3, 12), + const Radius.circular(1) + ) + )); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.End, offset: Offset.zero)); + await tester.pumpAndSettle(const Duration(milliseconds: 125)); // Floating cursor has an end animation. + + // Selection should be updated based on the floating cursor location. + expect(controller.selection.isCollapsed, true); + expect(controller.selection.baseOffset, 4); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Start, offset: Offset.zero)); + await tester.pump(); + + // The floating cursor should be drawn near to the previous position. + // It's different because it's snapped to exactly between characters. + expect(key.currentContext!.findRenderObject(), paints..rrect( + rrect: RRect.fromRectAndRadius( + const Rect.fromLTWH(56.5, 1, 3, 12), + const Radius.circular(1) + ) + )); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.Update, offset: const Offset(-56, 0))); + await tester.pump(); + + // The floating cursor should be drawn at the start of the line. + expect(key.currentContext!.findRenderObject(), paints..rrect( + rrect: RRect.fromRectAndRadius( + const Rect.fromLTWH(0.5, 1, 3, 12), + const Radius.circular(1) + ) + )); + + // Simulate UIKit setting the selection using keyboard selection. + controller.selection = const TextSelection(baseOffset: 0, extentOffset: 4); + await tester.pump(); + + state.updateFloatingCursor(RawFloatingCursorPoint(state: FloatingCursorDragState.End, offset: Offset.zero)); + await tester.pump(); + + // Selection should not be updated as the new position is within the selection range. + expect(controller.selection.isCollapsed, false); + expect(controller.selection.baseOffset, 0); + expect(controller.selection.extentOffset, 4); + + EditableText.debugDeterministicCursor = false; + }); + + group('Selection changed scroll into view', () { final String text = List.generate(64, (int index) => index).join('\n'); final TextEditingController controller = TextEditingController(text: text); From 7d7848abadbb397926d28632d25b67855b0f1ef3 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 19:49:34 -0500 Subject: [PATCH 19/71] d64a5129a [const_finder] Ignore constructor invocations from generated tear-off declarations (flutter/engine#38131) (#116814) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 626c13d7b9eaf..ae433ca49af97 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -8b56b5a98ed4d9a36ee72aefc08f155c21cdd339 +d64a5129add4f20beab80f1400280bffe16764f5 From be5c389e6ceef9713a6afd9e3f66ac177ff69df1 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Fri, 9 Dec 2022 20:52:32 -0500 Subject: [PATCH 20/71] faae28965 Roll Skia from 44062eff3e25 to 1b194c67700e (2 revisions) (flutter/engine#38166) (#116817) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index ae433ca49af97..79f25f3734942 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -d64a5129add4f20beab80f1400280bffe16764f5 +faae28965a9414d4b7e6951b33e1c9be23e2305f From 7549925c8c44dea92c7bc75be676c17b7613f87f Mon Sep 17 00:00:00 2001 From: Casey Hillers Date: Sat, 10 Dec 2022 18:23:40 -0800 Subject: [PATCH 21/71] Revert "Adds API in semanticsconfiguration to decide how to merge child semanticsConfigurations (#110730)" (#116839) This reverts commit 352ad3a9efccc8c54deb72eb0d7164bfb42b2a49. --- .../lib/src/material/input_decorator.dart | 104 +----- .../flutter/lib/src/rendering/object.dart | 329 +++--------------- .../flutter/lib/src/semantics/semantics.dart | 122 ------- .../test/material/text_field_test.dart | 41 --- .../flutter/test/widgets/scrollable_test.dart | 45 --- ...semantics_child_configs_delegate_test.dart | 259 -------------- 6 files changed, 67 insertions(+), 833 deletions(-) delete mode 100644 packages/flutter/test/widgets/semantics_child_configs_delegate_test.dart diff --git a/packages/flutter/lib/src/material/input_decorator.dart b/packages/flutter/lib/src/material/input_decorator.dart index 773eb235dcb90..4685d88bb606c 100644 --- a/packages/flutter/lib/src/material/input_decorator.dart +++ b/packages/flutter/lib/src/material/input_decorator.dart @@ -1326,35 +1326,6 @@ class _RenderDecoration extends RenderBox with SlottedContainerRenderObjectMixin return Size.zero; } - ChildSemanticsConfigurationsResult _childSemanticsConfigurationDelegate(List childConfigs) { - final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder(); - List? prefixMergeGroup; - List? suffixMergeGroup; - for (final SemanticsConfiguration childConfig in childConfigs) { - if (childConfig.tagsChildrenWith(_InputDecoratorState._kPrefixSemanticsTag)) { - prefixMergeGroup ??= []; - prefixMergeGroup.add(childConfig); - } else if (childConfig.tagsChildrenWith(_InputDecoratorState._kSuffixSemanticsTag)) { - suffixMergeGroup ??= []; - suffixMergeGroup.add(childConfig); - } else { - builder.markAsMergeUp(childConfig); - } - } - if (prefixMergeGroup != null) { - builder.markAsSiblingMergeGroup(prefixMergeGroup); - } - if (suffixMergeGroup != null) { - builder.markAsSiblingMergeGroup(suffixMergeGroup); - } - return builder.build(); - } - - @override - void describeSemanticsConfiguration(SemanticsConfiguration config) { - config.childConfigurationsDelegate = _childSemanticsConfigurationDelegate; - } - @override void performLayout() { final BoxConstraints constraints = this.constraints; @@ -1742,16 +1713,12 @@ class _AffixText extends StatelessWidget { this.text, this.style, this.child, - this.semanticsSortKey, - required this.semanticsTag, }); final bool labelIsFloating; final String? text; final TextStyle? style; final Widget? child; - final SemanticsSortKey? semanticsSortKey; - final SemanticsTag semanticsTag; @override Widget build(BuildContext context) { @@ -1761,11 +1728,7 @@ class _AffixText extends StatelessWidget { duration: _kTransitionDuration, curve: _kTransitionCurve, opacity: labelIsFloating ? 1.0 : 0.0, - child: Semantics( - sortKey: semanticsSortKey, - tagForChildren: semanticsTag, - child: child ?? (text == null ? null : Text(text!, style: style)), - ), + child: child ?? (text == null ? null : Text(text!, style: style)), ), ); } @@ -1936,11 +1899,6 @@ class _InputDecoratorState extends State with TickerProviderStat late AnimationController _floatingLabelController; late AnimationController _shakingLabelController; final _InputBorderGap _borderGap = _InputBorderGap(); - static const OrdinalSortKey _kPrefixSemanticsSortOrder = OrdinalSortKey(0); - static const OrdinalSortKey _kInputSemanticsSortOrder = OrdinalSortKey(1); - static const OrdinalSortKey _kSuffixSemanticsSortOrder = OrdinalSortKey(2); - static const SemanticsTag _kPrefixSemanticsTag = SemanticsTag('_InputDecoratorState.prefix'); - static const SemanticsTag _kSuffixSemanticsTag = SemanticsTag('_InputDecoratorState.suffix'); @override void initState() { @@ -2260,42 +2218,22 @@ class _InputDecoratorState extends State with TickerProviderStat ), ); - final bool hasPrefix = decoration.prefix != null || decoration.prefixText != null; - final bool hasSuffix = decoration.suffix != null || decoration.suffixText != null; - - Widget? input = widget.child; - // If at least two out of the three are visible, it needs semantics sort - // order. - final bool needsSemanticsSortOrder = widget._labelShouldWithdraw && (input != null ? (hasPrefix || hasSuffix) : (hasPrefix && hasSuffix)); - - final Widget? prefix = hasPrefix - ? _AffixText( - labelIsFloating: widget._labelShouldWithdraw, - text: decoration.prefixText, - style: MaterialStateProperty.resolveAs(decoration.prefixStyle, materialState) ?? hintStyle, - semanticsSortKey: needsSemanticsSortOrder ? _kPrefixSemanticsSortOrder : null, - semanticsTag: _kPrefixSemanticsTag, - child: decoration.prefix, - ) - : null; - - final Widget? suffix = hasSuffix - ? _AffixText( - labelIsFloating: widget._labelShouldWithdraw, - text: decoration.suffixText, - style: MaterialStateProperty.resolveAs(decoration.suffixStyle, materialState) ?? hintStyle, - semanticsSortKey: needsSemanticsSortOrder ? _kSuffixSemanticsSortOrder : null, - semanticsTag: _kSuffixSemanticsTag, - child: decoration.suffix, - ) - : null; - - if (input != null && needsSemanticsSortOrder) { - input = Semantics( - sortKey: _kInputSemanticsSortOrder, - child: input, + final Widget? prefix = decoration.prefix == null && decoration.prefixText == null ? null : + _AffixText( + labelIsFloating: widget._labelShouldWithdraw, + text: decoration.prefixText, + style: MaterialStateProperty.resolveAs(decoration.prefixStyle, materialState) ?? hintStyle, + child: decoration.prefix, ); - } + + final Widget? suffix = decoration.suffix == null && decoration.suffixText == null ? null : + _AffixText( + labelIsFloating: widget._labelShouldWithdraw, + text: decoration.suffixText, + style: MaterialStateProperty.resolveAs(decoration.suffixStyle, materialState) ?? hintStyle, + child: decoration.suffix, + ); + final bool decorationIsDense = decoration.isDense ?? false; final double iconSize = decorationIsDense ? 18.0 : 24.0; @@ -2334,9 +2272,7 @@ class _InputDecoratorState extends State with TickerProviderStat color: _getPrefixIconColor(themeData, defaults), size: iconSize, ), - child: Semantics( - child: decoration.prefixIcon, - ), + child: decoration.prefixIcon!, ), ), ), @@ -2361,9 +2297,7 @@ class _InputDecoratorState extends State with TickerProviderStat color: _getSuffixIconColor(themeData, defaults), size: iconSize, ), - child: Semantics( - child: decoration.suffixIcon, - ), + child: decoration.suffixIcon!, ), ), ), @@ -2440,7 +2374,7 @@ class _InputDecoratorState extends State with TickerProviderStat isDense: decoration.isDense, visualDensity: themeData.visualDensity, icon: icon, - input: input, + input: widget.child, label: label, hint: hint, prefix: prefix, diff --git a/packages/flutter/lib/src/rendering/object.dart b/packages/flutter/lib/src/rendering/object.dart index 7449016c46920..c3aa820850dd2 100644 --- a/packages/flutter/lib/src/rendering/object.dart +++ b/packages/flutter/lib/src/rendering/object.dart @@ -3100,10 +3100,6 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im if (_cachedSemanticsConfiguration == null) { _cachedSemanticsConfiguration = SemanticsConfiguration(); describeSemanticsConfiguration(_cachedSemanticsConfiguration!); - assert( - !_cachedSemanticsConfiguration!.explicitChildNodes || _cachedSemanticsConfiguration!.childConfigurationsDelegate == null, - 'A SemanticsConfiguration with explicitChildNode set to true cannot have a non-null childConfigsDelegate.', - ); } return _cachedSemanticsConfiguration!; } @@ -3165,13 +3161,7 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im final bool wasSemanticsBoundary = _semantics != null && (_cachedSemanticsConfiguration?.isSemanticBoundary ?? false); _cachedSemanticsConfiguration = null; - // The childConfigurationsDelegate may produce sibling nodes to be attached - // to the parent of this semantics node, thus it can't be a semantics - // boundary. - bool isEffectiveSemanticsBoundary = - _semanticsConfiguration.childConfigurationsDelegate == null && - _semanticsConfiguration.isSemanticBoundary && - wasSemanticsBoundary; + bool isEffectiveSemanticsBoundary = _semanticsConfiguration.isSemanticBoundary && wasSemanticsBoundary; RenderObject node = this; while (!isEffectiveSemanticsBoundary && node.parent is RenderObject) { @@ -3223,13 +3213,11 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im assert(fragment is _InterestingSemanticsFragment); final _InterestingSemanticsFragment interestingFragment = fragment as _InterestingSemanticsFragment; final List result = []; - final List siblingNodes = []; interestingFragment.compileChildren( parentSemanticsClipRect: _semantics?.parentSemanticsClipRect, parentPaintClipRect: _semantics?.parentPaintClipRect, elevationAdjustment: _semantics?.elevationAdjustment ?? 0.0, result: result, - siblingNodes: siblingNodes, ); final SemanticsNode node = result.single; // Fragment only wants to add this node's SemanticsNode to the parent. @@ -3247,94 +3235,70 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im bool dropSemanticsOfPreviousSiblings = config.isBlockingSemanticsOfPreviouslyPaintedNodes; final bool producesForkingFragment = !config.hasBeenAnnotated && !config.isSemanticBoundary; + final List<_InterestingSemanticsFragment> fragments = <_InterestingSemanticsFragment>[]; + final Set<_InterestingSemanticsFragment> toBeMarkedExplicit = <_InterestingSemanticsFragment>{}; final bool childrenMergeIntoParent = mergeIntoParent || config.isMergingSemanticsOfDescendants; - final List childConfigurations = []; - final bool explicitChildNode = config.explicitChildNodes || parent is! RenderObject; - final bool hasChildConfigurationsDelegate = config.childConfigurationsDelegate != null; - final Map configToFragment = {}; - final List<_InterestingSemanticsFragment> mergeUpFragments = <_InterestingSemanticsFragment>[]; - final List> siblingMergeFragmentGroups = >[]; + visitChildrenForSemantics((RenderObject renderChild) { assert(!_needsLayout); final _SemanticsFragment parentFragment = renderChild._getSemanticsForParent( mergeIntoParent: childrenMergeIntoParent, ); if (parentFragment.dropsSemanticsOfPreviousSiblings) { - childConfigurations.clear(); - mergeUpFragments.clear(); - siblingMergeFragmentGroups.clear(); + fragments.clear(); + toBeMarkedExplicit.clear(); if (!config.isSemanticBoundary) { dropSemanticsOfPreviousSiblings = true; } } - for (final _InterestingSemanticsFragment fragment in parentFragment.mergeUpFragments) { + // Figure out which child fragments are to be made explicit. + for (final _InterestingSemanticsFragment fragment in parentFragment.interestingFragments) { + fragments.add(fragment); fragment.addAncestor(this); fragment.addTags(config.tagsForChildren); - if (hasChildConfigurationsDelegate && fragment.config != null) { - // This fragment need to go through delegate to determine whether it - // merge up or not. - childConfigurations.add(fragment.config!); - configToFragment[fragment.config!] = fragment; - } else { - mergeUpFragments.add(fragment); + if (config.explicitChildNodes || parent is! RenderObject) { + fragment.markAsExplicit(); + continue; } - } - if (parentFragment is _ContainerSemanticsFragment) { - // Container fragments needs to propagate sibling merge group to be - // compiled by _SwitchableSemanticsFragment. - for (final List<_InterestingSemanticsFragment> siblingMergeGroup in parentFragment.siblingMergeGroups) { - for (final _InterestingSemanticsFragment siblingMergingFragment in siblingMergeGroup) { - siblingMergingFragment.addAncestor(this); - siblingMergingFragment.addTags(config.tagsForChildren); + if (!fragment.hasConfigForParent || producesForkingFragment) { + continue; + } + if (!config.isCompatibleWith(fragment.config)) { + toBeMarkedExplicit.add(fragment); + } + final int siblingLength = fragments.length - 1; + for (int i = 0; i < siblingLength; i += 1) { + final _InterestingSemanticsFragment siblingFragment = fragments[i]; + if (!fragment.config!.isCompatibleWith(siblingFragment.config)) { + toBeMarkedExplicit.add(fragment); + toBeMarkedExplicit.add(siblingFragment); } - siblingMergeFragmentGroups.add(siblingMergeGroup); } } }); - assert(hasChildConfigurationsDelegate || configToFragment.isEmpty); - - if (explicitChildNode) { - for (final _InterestingSemanticsFragment fragment in mergeUpFragments) { - fragment.markAsExplicit(); - } - } else if (hasChildConfigurationsDelegate && childConfigurations.isNotEmpty) { - final ChildSemanticsConfigurationsResult result = config.childConfigurationsDelegate!(childConfigurations); - mergeUpFragments.addAll( - result.mergeUp.map<_InterestingSemanticsFragment>((SemanticsConfiguration config) => configToFragment[config]!), - ); - for (final Iterable group in result.siblingMergeGroups) { - siblingMergeFragmentGroups.add( - group.map<_InterestingSemanticsFragment>((SemanticsConfiguration config) => configToFragment[config]!).toList() - ); - } + for (final _InterestingSemanticsFragment fragment in toBeMarkedExplicit) { + fragment.markAsExplicit(); } _needsSemanticsUpdate = false; - final _SemanticsFragment result; + _SemanticsFragment result; if (parent is! RenderObject) { assert(!config.hasBeenAnnotated); assert(!mergeIntoParent); - assert(siblingMergeFragmentGroups.isEmpty); - _marksExplicitInMergeGroup(mergeUpFragments, isMergeUp: true); - siblingMergeFragmentGroups.forEach(_marksExplicitInMergeGroup); result = _RootSemanticsFragment( owner: this, dropsSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings, ); } else if (producesForkingFragment) { result = _ContainerSemanticsFragment( - siblingMergeGroups: siblingMergeFragmentGroups, dropsSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings, ); } else { - _marksExplicitInMergeGroup(mergeUpFragments, isMergeUp: true); - siblingMergeFragmentGroups.forEach(_marksExplicitInMergeGroup); result = _SwitchableSemanticsFragment( config: config, mergeIntoParent: mergeIntoParent, - siblingMergeGroups: siblingMergeFragmentGroups, owner: this, dropsSemanticsOfPreviousSiblings: dropSemanticsOfPreviousSiblings, ); @@ -3343,32 +3307,10 @@ abstract class RenderObject extends AbstractNode with DiagnosticableTreeMixin im fragment.markAsExplicit(); } } - result.addAll(mergeUpFragments); - return result; - } - void _marksExplicitInMergeGroup(List<_InterestingSemanticsFragment> mergeGroup, {bool isMergeUp = false}) { - final Set<_InterestingSemanticsFragment> toBeExplicit = <_InterestingSemanticsFragment>{}; - for (int i = 0; i < mergeGroup.length; i += 1) { - final _InterestingSemanticsFragment fragment = mergeGroup[i]; - if (!fragment.hasConfigForParent) { - continue; - } - if (isMergeUp && !_semanticsConfiguration.isCompatibleWith(fragment.config)) { - toBeExplicit.add(fragment); - } - final int siblingLength = i; - for (int j = 0; j < siblingLength; j += 1) { - final _InterestingSemanticsFragment siblingFragment = mergeGroup[j]; - if (!fragment.config!.isCompatibleWith(siblingFragment.config)) { - toBeExplicit.add(fragment); - toBeExplicit.add(siblingFragment); - } - } - } - for (final _InterestingSemanticsFragment fragment in toBeExplicit) { - fragment.markAsExplicit(); - } + result.addAll(fragments); + + return result; } /// Called when collecting the semantics of this node. @@ -4043,9 +3985,8 @@ mixin RelayoutWhenSystemFontsChangeMixin on RenderObject { /// * [_ContainerSemanticsFragment]: a container class to transport the semantic /// information of multiple [_InterestingSemanticsFragment] to a parent. abstract class _SemanticsFragment { - _SemanticsFragment({ - required this.dropsSemanticsOfPreviousSiblings, - }) : assert (dropsSemanticsOfPreviousSiblings != null); + _SemanticsFragment({ required this.dropsSemanticsOfPreviousSiblings }) + : assert (dropsSemanticsOfPreviousSiblings != null); /// Incorporate the fragments of children into this fragment. void addAll(Iterable<_InterestingSemanticsFragment> fragments); @@ -4061,29 +4002,25 @@ abstract class _SemanticsFragment { /// Returns [_InterestingSemanticsFragment] describing the actual semantic /// information that this fragment wants to add to the parent. - List<_InterestingSemanticsFragment> get mergeUpFragments; + List<_InterestingSemanticsFragment> get interestingFragments; } /// A container used when a [RenderObject] wants to add multiple independent /// [_InterestingSemanticsFragment] to its parent. /// /// The [_InterestingSemanticsFragment] to be added to the parent can be -/// obtained via [mergeUpFragments]. +/// obtained via [interestingFragments]. class _ContainerSemanticsFragment extends _SemanticsFragment { - _ContainerSemanticsFragment({ - required super.dropsSemanticsOfPreviousSiblings, - required this.siblingMergeGroups, - }); - final List> siblingMergeGroups; + _ContainerSemanticsFragment({ required super.dropsSemanticsOfPreviousSiblings }); @override void addAll(Iterable<_InterestingSemanticsFragment> fragments) { - mergeUpFragments.addAll(fragments); + interestingFragments.addAll(fragments); } @override - final List<_InterestingSemanticsFragment> mergeUpFragments = <_InterestingSemanticsFragment>[]; + final List<_InterestingSemanticsFragment> interestingFragments = <_InterestingSemanticsFragment>[]; } /// A [_SemanticsFragment] that describes which concrete semantic information @@ -4120,7 +4057,6 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment { required Rect? parentPaintClipRect, required double elevationAdjustment, required List result, - required List siblingNodes, }); /// The [SemanticsConfiguration] the child wants to merge into the parent's @@ -4150,7 +4086,7 @@ abstract class _InterestingSemanticsFragment extends _SemanticsFragment { bool get hasConfigForParent => config != null; @override - List<_InterestingSemanticsFragment> get mergeUpFragments => <_InterestingSemanticsFragment>[this]; + List<_InterestingSemanticsFragment> get interestingFragments => <_InterestingSemanticsFragment>[this]; Set? _tagsForChildren; @@ -4188,13 +4124,7 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment { }); @override - void compileChildren({ - Rect? parentSemanticsClipRect, - Rect? parentPaintClipRect, - required double elevationAdjustment, - required List result, - required List siblingNodes, - }) { + void compileChildren({ Rect? parentSemanticsClipRect, Rect? parentPaintClipRect, required double elevationAdjustment, required List result }) { assert(_tagsForChildren == null || _tagsForChildren!.isEmpty); assert(parentSemanticsClipRect == null); assert(parentPaintClipRect == null); @@ -4220,11 +4150,8 @@ class _RootSemanticsFragment extends _InterestingSemanticsFragment { parentPaintClipRect: parentPaintClipRect, elevationAdjustment: 0.0, result: children, - siblingNodes: siblingNodes, ); } - // Root node does not have a parent and thus can't attach sibling nodes. - assert(siblingNodes.isEmpty); node.updateWith(config: null, childrenInInversePaintOrder: children); // The root node is the only semantics node allowed to be invisible. This @@ -4274,11 +4201,9 @@ class _SwitchableSemanticsFragment extends _InterestingSemanticsFragment { _SwitchableSemanticsFragment({ required bool mergeIntoParent, required SemanticsConfiguration config, - required List> siblingMergeGroups, required super.owner, required super.dropsSemanticsOfPreviousSiblings, - }) : _siblingMergeGroups = siblingMergeGroups, - _mergeIntoParent = mergeIntoParent, + }) : _mergeIntoParent = mergeIntoParent, _config = config, assert(mergeIntoParent != null), assert(config != null); @@ -4286,126 +4211,14 @@ class _SwitchableSemanticsFragment extends _InterestingSemanticsFragment { final bool _mergeIntoParent; SemanticsConfiguration _config; bool _isConfigWritable = false; - bool _mergesToSibling = false; - - final List> _siblingMergeGroups; - - void _mergeSiblingGroup(Rect? parentSemanticsClipRect, Rect? parentPaintClipRect, List result, Set usedSemanticsIds) { - for (final List<_InterestingSemanticsFragment> group in _siblingMergeGroups) { - Rect? rect; - Rect? semanticsClipRect; - Rect? paintClipRect; - SemanticsConfiguration? configuration; - // Use empty set because the _tagsForChildren may not contains all of the - // tags if this fragment is not explicit. The _tagsForChildren are added - // to sibling nodes at the end of compileChildren if this fragment is - // explicit. - final Set tags = {}; - SemanticsNode? node; - for (final _InterestingSemanticsFragment fragment in group) { - if (fragment.config != null) { - final _SwitchableSemanticsFragment switchableFragment = fragment as _SwitchableSemanticsFragment; - switchableFragment._mergesToSibling = true; - node ??= fragment.owner._semantics; - if (configuration == null) { - switchableFragment._ensureConfigIsWritable(); - configuration = switchableFragment.config; - } else { - configuration.absorb(switchableFragment.config!); - } - // It is a child fragment of a _SwitchableFragment, it must have a - // geometry. - final _SemanticsGeometry geometry = switchableFragment._computeSemanticsGeometry( - parentSemanticsClipRect: parentSemanticsClipRect, - parentPaintClipRect: parentPaintClipRect, - )!; - final Rect fragmentRect = MatrixUtils.transformRect(geometry.transform, geometry.rect); - if (rect == null) { - rect = fragmentRect; - } else { - rect = rect.expandToInclude(fragmentRect); - } - if (geometry.semanticsClipRect != null) { - final Rect rect = MatrixUtils.transformRect(geometry.transform, geometry.semanticsClipRect!); - if (semanticsClipRect == null) { - semanticsClipRect = rect; - } else { - semanticsClipRect = semanticsClipRect.intersect(rect); - } - } - if (geometry.paintClipRect != null) { - final Rect rect = MatrixUtils.transformRect(geometry.transform, geometry.paintClipRect!); - if (paintClipRect == null) { - paintClipRect = rect; - } else { - paintClipRect = paintClipRect.intersect(rect); - } - } - if (switchableFragment._tagsForChildren != null) { - tags.addAll(switchableFragment._tagsForChildren!); - } - } - } - // Can be null if all fragments in group are marked as explicit. - if (configuration != null && !rect!.isEmpty) { - if (node == null || usedSemanticsIds.contains(node.id)) { - node = SemanticsNode(showOnScreen: owner.showOnScreen); - } - usedSemanticsIds.add(node.id); - node - ..tags = tags - ..rect = rect - ..transform = null // Will be set when compiling immediate parent node. - ..parentSemanticsClipRect = semanticsClipRect - ..parentPaintClipRect = paintClipRect; - for (final _InterestingSemanticsFragment fragment in group) { - if (fragment.config != null) { - fragment.owner._semantics = node; - } - } - node.updateWith(config: configuration); - result.add(node); - } - } - } - final List<_InterestingSemanticsFragment> _children = <_InterestingSemanticsFragment>[]; @override - void compileChildren({ - Rect? parentSemanticsClipRect, - Rect? parentPaintClipRect, - required double elevationAdjustment, - required List result, - required List siblingNodes, - }) { - final Set usedSemanticsIds = {}; - Iterable<_InterestingSemanticsFragment> compilingFragments = _children; - for (final List<_InterestingSemanticsFragment> siblingGroup in _siblingMergeGroups) { - compilingFragments = compilingFragments.followedBy(siblingGroup); - } + void compileChildren({ Rect? parentSemanticsClipRect, Rect? parentPaintClipRect, required double elevationAdjustment, required List result }) { if (!_isExplicit) { - if (!_mergesToSibling) { - owner._semantics = null; - } - _mergeSiblingGroup( - parentSemanticsClipRect, - parentPaintClipRect, - siblingNodes, - usedSemanticsIds, - ); - for (final _InterestingSemanticsFragment fragment in compilingFragments) { + owner._semantics = null; + for (final _InterestingSemanticsFragment fragment in _children) { assert(_ancestorChain.first == fragment._ancestorChain.last); - if (fragment is _SwitchableSemanticsFragment) { - // Cached semantics node may be part of sibling merging group prior - // to this update. In this case, the semantics node may continue to - // be reused in that sibling merging group. - if (fragment._isExplicit && - fragment.owner._semantics != null && - usedSemanticsIds.contains(fragment.owner._semantics!.id)) { - fragment.owner._semantics = null; - } - } fragment._ancestorChain.addAll(_ancestorChain.skip(1)); fragment.compileChildren( parentSemanticsClipRect: parentSemanticsClipRect, @@ -4415,16 +4228,14 @@ class _SwitchableSemanticsFragment extends _InterestingSemanticsFragment { // its children are placed at the elevation dictated by this config. elevationAdjustment: elevationAdjustment + _config.elevation, result: result, - siblingNodes: siblingNodes, ); } return; } - final _SemanticsGeometry? geometry = _computeSemanticsGeometry( - parentSemanticsClipRect: parentSemanticsClipRect, - parentPaintClipRect: parentPaintClipRect, - ); + final _SemanticsGeometry? geometry = _needsGeometryUpdate + ? _SemanticsGeometry(parentSemanticsClipRect: parentSemanticsClipRect, parentPaintClipRect: parentPaintClipRect, ancestors: _ancestorChain) + : null; if (!_mergeIntoParent && (geometry?.dropFromTree ?? false)) { return; // Drop the node, it's not going to be visible. @@ -4453,66 +4264,22 @@ class _SwitchableSemanticsFragment extends _InterestingSemanticsFragment { _config.isHidden = true; } } + final List children = []; - _mergeSiblingGroup( - node.parentSemanticsClipRect, - node.parentPaintClipRect, - siblingNodes, - usedSemanticsIds, - ); - for (final _InterestingSemanticsFragment fragment in compilingFragments) { - if (fragment is _SwitchableSemanticsFragment) { - // Cached semantics node may be part of sibling merging group prior - // to this update. In this case, the semantics node may continue to - // be reused in that sibling merging group. - if (fragment._isExplicit && - fragment.owner._semantics != null && - usedSemanticsIds.contains(fragment.owner._semantics!.id)) { - fragment.owner._semantics = null; - } - } - final List childSiblingNodes = []; + for (final _InterestingSemanticsFragment fragment in _children) { fragment.compileChildren( parentSemanticsClipRect: node.parentSemanticsClipRect, parentPaintClipRect: node.parentPaintClipRect, elevationAdjustment: 0.0, result: children, - siblingNodes: childSiblingNodes, ); - siblingNodes.addAll(childSiblingNodes); } - if (_config.isSemanticBoundary) { owner.assembleSemanticsNode(node, _config, children); } else { node.updateWith(config: _config, childrenInInversePaintOrder: children); } result.add(node); - // Sibling node needs to attach to the parent of an explicit node. - for (final SemanticsNode siblingNode in siblingNodes) { - // sibling nodes are in the same coordinate of the immediate explicit node. - // They need to share the same transform if they are going to attach to the - // parent of the immediate explicit node. - assert(siblingNode.transform == null); - siblingNode - ..transform = node.transform - ..isMergedIntoParent = node.isMergedIntoParent; - if (_tagsForChildren != null) { - siblingNode.tags ??= {}; - siblingNode.tags!.addAll(_tagsForChildren!); - } - } - result.addAll(siblingNodes); - siblingNodes.clear(); - } - - _SemanticsGeometry? _computeSemanticsGeometry({ - required Rect? parentSemanticsClipRect, - required Rect? parentPaintClipRect, - }) { - return _needsGeometryUpdate - ? _SemanticsGeometry(parentSemanticsClipRect: parentSemanticsClipRect, parentPaintClipRect: parentPaintClipRect, ancestors: _ancestorChain) - : null; } @override diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart index 041c70df676d9..bed9eea8cb33b 100644 --- a/packages/flutter/lib/src/semantics/semantics.dart +++ b/packages/flutter/lib/src/semantics/semantics.dart @@ -6,7 +6,6 @@ import 'dart:math' as math; import 'dart:ui' as ui; import 'dart:ui' show Offset, Rect, SemanticsAction, SemanticsFlag, StringAttribute, TextDirection; -import 'package:collection/collection.dart'; import 'package:flutter/foundation.dart'; import 'package:flutter/painting.dart' show MatrixUtils, TransformProperty; import 'package:flutter/services.dart'; @@ -54,20 +53,6 @@ typedef SemanticsActionHandler = void Function(Object? args); /// Used by [SemanticsOwner.onSemanticsUpdate]. typedef SemanticsUpdateCallback = void Function(ui.SemanticsUpdate update); -/// Signature for the [SemanticsConfiguration.childConfigurationsDelegate]. -/// -/// The input list contains all [SemanticsConfiguration]s that rendering -/// children want to merge upward. One can tag a render child with a -/// [SemanticsTag] and look up its [SemanticsConfiguration]s through -/// [SemanticsConfiguration.tagsChildrenWith]. -/// -/// The return value is the arrangement of these configs, including which -/// configs continue to merge upward and which configs form sibling merge group. -/// -/// Use [ChildSemanticsConfigurationsResultBuilder] to generate the return -/// value. -typedef ChildSemanticsConfigurationsDelegate = ChildSemanticsConfigurationsResult Function(List); - /// A tag for a [SemanticsNode]. /// /// Tags can be interpreted by the parent of a [SemanticsNode] @@ -100,89 +85,6 @@ class SemanticsTag { String toString() => '${objectRuntimeType(this, 'SemanticsTag')}($name)'; } -/// The result that contains the arrangement for the child -/// [SemanticsConfiguration]s. -/// -/// When the [PipelineOwner] builds the semantics tree, it uses the returned -/// [ChildSemanticsConfigurationsResult] from -/// [SemanticsConfiguration.childConfigurationsDelegate] to decide how semantics nodes -/// should form. -/// -/// Use [ChildSemanticsConfigurationsResultBuilder] to build the result. -class ChildSemanticsConfigurationsResult { - ChildSemanticsConfigurationsResult._(this.mergeUp, this.siblingMergeGroups); - - /// Returns the [SemanticsConfiguration]s that are supposed to be merged into - /// the parent semantics node. - /// - /// [SemanticsConfiguration]s that are either semantics boundaries or are - /// conflicting with other [SemanticsConfiguration]s will form explicit - /// semantics nodes. All others will be merged into the parent. - final List mergeUp; - - /// The groups of child semantics configurations that want to merge together - /// and form a sibling [SemanticsNode]. - /// - /// All the [SemanticsConfiguration]s in a given group that are either - /// semantics boundaries or are conflicting with other - /// [SemanticsConfiguration]s of the same group will be excluded from the - /// sibling merge group and form independent semantics nodes as usual. - /// - /// The result [SemanticsNode]s from the merges are attached as the sibling - /// nodes of the immediate parent semantics node. For example, a `RenderObjectA` - /// has a rendering child, `RenderObjectB`. If both of them form their own - /// semantics nodes, `SemanticsNodeA` and `SemanticsNodeB`, any semantics node - /// created from sibling merge groups of `RenderObjectB` will be attach to - /// `SemanticsNodeA` as a sibling of `SemanticsNodeB`. - final List> siblingMergeGroups; -} - -/// The builder to build a [ChildSemanticsConfigurationsResult] based on its -/// annotations. -/// -/// To use this builder, one can use [markAsMergeUp] and -/// [markAsSiblingMergeGroup] to annotate the arrangement of -/// [SemanticsConfiguration]s. Once all the configs are annotated, use [build] -/// to generate the [ChildSemanticsConfigurationsResult]. -class ChildSemanticsConfigurationsResultBuilder { - /// Creates a [ChildSemanticsConfigurationsResultBuilder]. - ChildSemanticsConfigurationsResultBuilder(); - - final List _mergeUp = []; - final List> _siblingMergeGroups = >[]; - - /// Marks the [SemanticsConfiguration] to be merged into the parent semantics - /// node. - /// - /// The [SemanticsConfiguration] will be added to the - /// [ChildSemanticsConfigurationsResult.mergeUp] that this builder builds. - void markAsMergeUp(SemanticsConfiguration config) => _mergeUp.add(config); - - /// Marks a group of [SemanticsConfiguration]s to merge together - /// and form a sibling [SemanticsNode]. - /// - /// The group of [SemanticsConfiguration]s will be added to the - /// [ChildSemanticsConfigurationsResult.siblingMergeGroups] that this builder builds. - void markAsSiblingMergeGroup(List configs) => _siblingMergeGroups.add(configs); - - /// Builds a [ChildSemanticsConfigurationsResult] contains the arrangement. - ChildSemanticsConfigurationsResult build() { - assert((){ - final Set seenConfigs = {}; - for (final SemanticsConfiguration config in [..._mergeUp, ..._siblingMergeGroups.flattened]) { - assert( - seenConfigs.add(config), - 'Duplicated SemanticsConfigurations. This can happen if the same ' - 'SemanticsConfiguration was marked twice in markAsMergeUp and/or ' - 'markAsSiblingMergeGroup' - ); - } - return true; - }()); - return ChildSemanticsConfigurationsResult._(_mergeUp, _siblingMergeGroups); - } -} - /// An identifier of a custom semantics action. /// /// Custom semantics actions can be provided to make complex user @@ -3822,25 +3724,6 @@ class SemanticsConfiguration { _onDidLoseAccessibilityFocus = value; } - /// A delegate that decides how to handle [SemanticsConfiguration]s produced - /// in the widget subtree. - /// - /// The [SemanticsConfiguration]s are produced by rendering objects in the - /// subtree and want to merge up to their parent. This delegate can decide - /// which of these should be merged together to form sibling SemanticsNodes and - /// which of them should be merged upwards into the parent SemanticsNode. - /// - /// The input list of [SemanticsConfiguration]s can be empty if the rendering - /// object of this semantics configuration is a leaf node. - ChildSemanticsConfigurationsDelegate? get childConfigurationsDelegate => _childConfigurationsDelegate; - ChildSemanticsConfigurationsDelegate? _childConfigurationsDelegate; - set childConfigurationsDelegate(ChildSemanticsConfigurationsDelegate? value) { - assert(value != null); - _childConfigurationsDelegate = value; - // Setting the childConfigsDelegate does not annotate any meaningful - // semantics information of the config. - } - /// Returns the action handler registered for [action] or null if none was /// registered. SemanticsActionHandler? getActionHandler(SemanticsAction action) => _actions[action]; @@ -4565,11 +4448,6 @@ class SemanticsConfiguration { /// * [addTagForChildren] to add a tag and for more information about their /// usage. Iterable? get tagsForChildren => _tagsForChildren; - - /// Whether this configuration will tag the child semantics nodes with a - /// given [SemanticsTag]. - bool tagsChildrenWith(SemanticsTag tag) => _tagsForChildren?.contains(tag) ?? false; - Set? _tagsForChildren; /// Specifies a [SemanticsTag] that this configuration wants to apply to all diff --git a/packages/flutter/test/material/text_field_test.dart b/packages/flutter/test/material/text_field_test.dart index 1cf7a24f7f50e..944e444d11863 100644 --- a/packages/flutter/test/material/text_field_test.dart +++ b/packages/flutter/test/material/text_field_test.dart @@ -4375,47 +4375,6 @@ void main() { expect(prefixText.style, prefixStyle); }); - testWidgets('TextField prefix and suffix create a sibling node', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - await tester.pumpWidget( - overlay( - child: TextField( - controller: TextEditingController(text: 'some text'), - decoration: const InputDecoration( - prefixText: 'Prefix', - suffixText: 'Suffix', - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - id: 2, - textDirection: TextDirection.ltr, - label: 'Prefix', - ), - TestSemantics.rootChild( - id: 1, - textDirection: TextDirection.ltr, - value: 'some text', - actions: [ - SemanticsAction.tap, - ], - flags: [ - SemanticsFlag.isTextField, - ], - ), - TestSemantics.rootChild( - id: 3, - textDirection: TextDirection.ltr, - label: 'Suffix', - ), - ], - ), ignoreTransform: true, ignoreRect: true)); - }); - testWidgets('TextField with specified suffixStyle', (WidgetTester tester) async { final TextStyle suffixStyle = TextStyle( color: Colors.pink[500], diff --git a/packages/flutter/test/widgets/scrollable_test.dart b/packages/flutter/test/widgets/scrollable_test.dart index 4b5e3792904d7..12b1839e0341b 100644 --- a/packages/flutter/test/widgets/scrollable_test.dart +++ b/packages/flutter/test/widgets/scrollable_test.dart @@ -1429,51 +1429,6 @@ void main() { handle.dispose(); }); - testWidgets('Two panel semantics is added to the sibling nodes of direct children', (WidgetTester tester) async { - final SemanticsHandle handle = tester.ensureSemantics(); - final UniqueKey key = UniqueKey(); - await tester.pumpWidget(MaterialApp( - home: Scaffold( - body: ListView( - key: key, - children: const [ - TextField( - autofocus: true, - decoration: InputDecoration( - prefixText: 'prefix', - ), - ), - ], - ), - ), - )); - // Wait for focus. - await tester.pumpAndSettle(); - - final SemanticsNode scrollableNode = tester.getSemantics(find.byKey(key)); - SemanticsNode? intermediateNode; - scrollableNode.visitChildren((SemanticsNode node) { - intermediateNode = node; - return true; - }); - SemanticsNode? syntheticScrollableNode; - intermediateNode!.visitChildren((SemanticsNode node) { - syntheticScrollableNode = node; - return true; - }); - expect(syntheticScrollableNode!.hasFlag(ui.SemanticsFlag.hasImplicitScrolling), isTrue); - - int numberOfChild = 0; - syntheticScrollableNode!.visitChildren((SemanticsNode node) { - expect(node.isTagged(RenderViewport.useTwoPaneSemantics), isTrue); - numberOfChild += 1; - return true; - }); - expect(numberOfChild, 2); - - handle.dispose(); - }); - testWidgets('Scroll inertia cancel event', (WidgetTester tester) async { await pumpTest(tester, null); await tester.fling(find.byType(Scrollable), const Offset(0.0, -dragOffset), 1000.0); diff --git a/packages/flutter/test/widgets/semantics_child_configs_delegate_test.dart b/packages/flutter/test/widgets/semantics_child_configs_delegate_test.dart deleted file mode 100644 index b09c4cd2cf07f..0000000000000 --- a/packages/flutter/test/widgets/semantics_child_configs_delegate_test.dart +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'package:flutter/rendering.dart'; -import 'package:flutter/widgets.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'semantics_tester.dart'; - -void main() { - testWidgets('Semantics can merge sibling group', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - const SemanticsTag first = SemanticsTag('1'); - const SemanticsTag second = SemanticsTag('2'); - const SemanticsTag third = SemanticsTag('3'); - ChildSemanticsConfigurationsResult delegate(List configs) { - expect(configs.length, 3); - final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder(); - final List sibling = []; - // Merge first and third - for (final SemanticsConfiguration config in configs) { - if (config.tagsChildrenWith(first) || config.tagsChildrenWith(third)) { - sibling.add(config); - } else { - builder.markAsMergeUp(config); - } - } - builder.markAsSiblingMergeGroup(sibling); - return builder.build(); - } - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Semantics( - label: 'parent', - child: TestConfigDelegate( - delegate: delegate, - child: Column( - children: [ - Semantics( - label: '1', - tagForChildren: first, - child: const SizedBox(width: 100, height: 100), - // this tests that empty nodes disappear - ), - Semantics( - label: '2', - tagForChildren: second, - child: const SizedBox(width: 100, height: 100), - ), - Semantics( - label: '3', - tagForChildren: third, - child: const SizedBox(width: 100, height: 100), - ), - ], - ), - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - label: 'parent\n2', - ), - TestSemantics.rootChild( - label: '1\n3', - ), - ], - ), ignoreId: true, ignoreRect: true, ignoreTransform: true)); - }); - - testWidgets('Semantics can drop semantics config', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - const SemanticsTag first = SemanticsTag('1'); - const SemanticsTag second = SemanticsTag('2'); - const SemanticsTag third = SemanticsTag('3'); - ChildSemanticsConfigurationsResult delegate(List configs) { - final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder(); - // Merge first and third - for (final SemanticsConfiguration config in configs) { - if (config.tagsChildrenWith(first) || config.tagsChildrenWith(third)) { - continue; - } - builder.markAsMergeUp(config); - } - return builder.build(); - } - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Semantics( - label: 'parent', - child: TestConfigDelegate( - delegate: delegate, - child: Column( - children: [ - Semantics( - label: '1', - tagForChildren: first, - child: const SizedBox(width: 100, height: 100), - // this tests that empty nodes disappear - ), - Semantics( - label: '2', - tagForChildren: second, - child: const SizedBox(width: 100, height: 100), - ), - Semantics( - label: '3', - tagForChildren: third, - child: const SizedBox(width: 100, height: 100), - ), - ], - ), - ), - ), - ), - ); - - expect(semantics, hasSemantics(TestSemantics.root( - children: [ - TestSemantics.rootChild( - label: 'parent\n2', - ), - ], - ), ignoreId: true, ignoreRect: true, ignoreTransform: true)); - }); - - testWidgets('Semantics throws when mark the same config twice case 1', (WidgetTester tester) async { - const SemanticsTag first = SemanticsTag('1'); - const SemanticsTag second = SemanticsTag('2'); - const SemanticsTag third = SemanticsTag('3'); - ChildSemanticsConfigurationsResult delegate(List configs) { - final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder(); - // Marks the same one twice. - builder.markAsMergeUp(configs.first); - builder.markAsMergeUp(configs.first); - return builder.build(); - } - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Semantics( - label: 'parent', - child: TestConfigDelegate( - delegate: delegate, - child: Column( - children: [ - Semantics( - label: '1', - tagForChildren: first, - child: const SizedBox(width: 100, height: 100), - // this tests that empty nodes disappear - ), - Semantics( - label: '2', - tagForChildren: second, - child: const SizedBox(width: 100, height: 100), - ), - Semantics( - label: '3', - tagForChildren: third, - child: const SizedBox(width: 100, height: 100), - ), - ], - ), - ), - ), - ), - ); - - expect(tester.takeException(), isAssertionError); - }); - - testWidgets('Semantics throws when mark the same config twice case 2', (WidgetTester tester) async { - const SemanticsTag first = SemanticsTag('1'); - const SemanticsTag second = SemanticsTag('2'); - const SemanticsTag third = SemanticsTag('3'); - ChildSemanticsConfigurationsResult delegate(List configs) { - final ChildSemanticsConfigurationsResultBuilder builder = ChildSemanticsConfigurationsResultBuilder(); - // Marks the same one twice. - builder.markAsMergeUp(configs.first); - builder.markAsSiblingMergeGroup([configs.first]); - return builder.build(); - } - await tester.pumpWidget( - Directionality( - textDirection: TextDirection.ltr, - child: Semantics( - label: 'parent', - child: TestConfigDelegate( - delegate: delegate, - child: Column( - children: [ - Semantics( - label: '1', - tagForChildren: first, - child: const SizedBox(width: 100, height: 100), - // this tests that empty nodes disappear - ), - Semantics( - label: '2', - tagForChildren: second, - child: const SizedBox(width: 100, height: 100), - ), - Semantics( - label: '3', - tagForChildren: third, - child: const SizedBox(width: 100, height: 100), - ), - ], - ), - ), - ), - ), - ); - - expect(tester.takeException(), isAssertionError); - }); -} - -class TestConfigDelegate extends SingleChildRenderObjectWidget { - const TestConfigDelegate({super.key, required this.delegate, super.child}); - final ChildSemanticsConfigurationsDelegate delegate; - - @override - RenderTestConfigDelegate createRenderObject(BuildContext context) => RenderTestConfigDelegate( - delegate: delegate, - ); - - @override - void updateRenderObject(BuildContext context, RenderTestConfigDelegate renderObject) { - renderObject.delegate = delegate; - } -} - -class RenderTestConfigDelegate extends RenderProxyBox { - RenderTestConfigDelegate({ - ChildSemanticsConfigurationsDelegate? delegate, - }) : _delegate = delegate; - - ChildSemanticsConfigurationsDelegate? get delegate => _delegate; - ChildSemanticsConfigurationsDelegate? _delegate; - set delegate(ChildSemanticsConfigurationsDelegate? value) { - if (value != _delegate) { - markNeedsSemanticsUpdate(); - } - _delegate = value; - } - - @override - void describeSemanticsConfiguration(SemanticsConfiguration config) { - config.childConfigurationsDelegate = _delegate; - } -} From 68f02dd2e83a0c74c331d7004ec1fb5db49617f7 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 10 Dec 2022 21:45:22 -0500 Subject: [PATCH 22/71] Roll Flutter Engine from faae28965a94 to fbb79e704b0a (6 revisions) (#116843) * 6b1c0c23f [Impeller Scene] Convert vertex positions to match Impeller's clip space orientation (flutter/engine#38174) * 41ec2804a Roll Fuchsia Linux SDK from rRJIjuO-dPNCpCTd9... to 8O5rMR_ehMaL3YhZ5... (flutter/engine#38177) * 3dfd13f1d Roll Skia from 1b194c67700e to 491f5c134f76 (2 revisions) (flutter/engine#38182) * 690f0e81c Fix sampler offsets (flutter/engine#38170) * f24f2cc0a Roll Skia from 491f5c134f76 to 0d482f9fa8b3 (1 revision) (flutter/engine#38183) * fbb79e704 [Impeller Scene] Refactor Nodes/Meshes for simplicity and GLTF compatibility (flutter/engine#38180) --- bin/internal/engine.version | 2 +- bin/internal/fuchsia-linux.version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 79f25f3734942..9f015808b5a1d 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -faae28965a9414d4b7e6951b33e1c9be23e2305f +fbb79e704b0a857096b88ec17580446fd7db2574 diff --git a/bin/internal/fuchsia-linux.version b/bin/internal/fuchsia-linux.version index bd8b76f55f2de..c0b0a7f47e9ab 100644 --- a/bin/internal/fuchsia-linux.version +++ b/bin/internal/fuchsia-linux.version @@ -1 +1 @@ -rRJIjuO-dPNCpCTd9fnOc5ISL21HS5JbpRnk-HNpdsQC +8O5rMR_ehMaL3YhZ56B1Q-pS15TRm3MJOichyVr5L7MC From ec02f3bfb77b350e64e9e8d08c04b23dc12647f2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sat, 10 Dec 2022 22:25:24 -0500 Subject: [PATCH 23/71] 656b67796 Roll Dart SDK from 0940b5e6ccd5 to 21f2997a8fc6 (9 revisions) (flutter/engine#38172) (#116844) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 9f015808b5a1d..ef357d80eba47 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -fbb79e704b0a857096b88ec17580446fd7db2574 +656b67796bbb51e648326458b55f897f8148e669 From c02d53fc0eaf23c44f0ebca5fcaae50604de676a Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Sat, 10 Dec 2022 21:17:19 -0800 Subject: [PATCH 24/71] More gracefully handle license loading failures (#87841) --- packages/flutter/lib/src/material/about.dart | 11 +++++++++++ packages/flutter/test/material/about_test.dart | 18 ++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/packages/flutter/lib/src/material/about.dart b/packages/flutter/lib/src/material/about.dart index 1bab4e8a1a050..b6ea5f91020d1 100644 --- a/packages/flutter/lib/src/material/about.dart +++ b/packages/flutter/lib/src/material/about.dart @@ -571,6 +571,17 @@ class _PackagesViewState extends State<_PackagesView> { builder: (BuildContext context, BoxConstraints constraints) { switch (snapshot.connectionState) { case ConnectionState.done: + if (snapshot.hasError) { + assert(() { + FlutterError.reportError(FlutterErrorDetails( + exception: snapshot.error!, + stack: snapshot.stackTrace, + context: ErrorDescription('while decoding the license file'), + )); + return true; + }()); + return Center(child: Text(snapshot.error.toString())); + } _initDefaultDetailPage(snapshot.data!, context); return ValueListenableBuilder( valueListenable: widget.selectedId, diff --git a/packages/flutter/test/material/about_test.dart b/packages/flutter/test/material/about_test.dart index 698dbc4427f31..20e20517ce2b9 100644 --- a/packages/flutter/test/material/about_test.dart +++ b/packages/flutter/test/material/about_test.dart @@ -997,6 +997,24 @@ void main() { final double appIconBottomPadding = tester.getTopLeft(appPowered).dy - tester.getBottomLeft(appIcon).dy; expect(appIconBottomPadding, 18.0); }); + + testWidgets('Error handling test', (WidgetTester tester) async { + LicenseRegistry.addLicense(() => Stream.error(Exception('Injected failure'))); + await tester.pumpWidget(const MaterialApp(home: Material(child: AboutListTile()))); + await tester.tap(find.byType(ListTile)); + await tester.pump(); + await tester.pump(const Duration(seconds: 2)); + await tester.tap(find.text('VIEW LICENSES')); + await tester.pump(); + await tester.pump(const Duration(seconds: 2)); + final Finder finder = find.byWidgetPredicate((Widget widget) => widget.runtimeType.toString() == '_PackagesView'); + // force the stream to complete (has to be done in a runAsync block since it's areal async process) + await tester.runAsync(() => (tester.firstState(finder) as dynamic).licenses as Future); // ignore: avoid_dynamic_calls + expect(tester.takeException().toString(), 'Exception: Injected failure'); + await tester.pumpAndSettle(); + expect(tester.takeException().toString(), 'Exception: Injected failure'); + expect(find.text('Exception: Injected failure'), findsOneWidget); + }); } class FakeLicenseEntry extends LicenseEntry { From 4a15111660c509a7f90b8bd549bad72f62ee38f5 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 11 Dec 2022 07:59:27 -0500 Subject: [PATCH 25/71] 0795bccae Roll Skia from 0d482f9fa8b3 to 80d9e679f909 (2 revisions) (flutter/engine#38195) (#116853) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index ef357d80eba47..f622ef2b387de 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -656b67796bbb51e648326458b55f897f8148e669 +0795bccae84928f205de795471c82434ba139bc4 From 1fc166a5195664317d7e036580233842ac8036fd Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 11 Dec 2022 16:14:16 -0500 Subject: [PATCH 26/71] 3ca497ebb Roll Skia from 80d9e679f909 to 29791c73ae16 (1 revision) (flutter/engine#38200) (#116859) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index f622ef2b387de..815c9f743a16e 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -0795bccae84928f205de795471c82434ba139bc4 +3ca497ebb107157f9c29207f3e25da4f66888040 From 9fdb64b7e772127059aca2ee52ef3778e211f0cb Mon Sep 17 00:00:00 2001 From: Ian Hickson Date: Sun, 11 Dec 2022 14:43:22 -0800 Subject: [PATCH 27/71] Taboo the word "simply" from our API documentation. (#116061) --- .../lib/src/web/bench_image_decoding.dart | 24 ++++++------- dev/bots/analyze.dart | 35 ++++++++++++++++--- .../root/packages/flutter/lib/bar.dart | 3 ++ dev/bots/test/analyze_test.dart | 15 ++++++-- .../flutter/lib/src/animation/animations.dart | 4 +-- .../lib/src/cupertino/context_menu.dart | 17 +++++---- .../flutter/lib/src/cupertino/scrollbar.dart | 2 +- .../src/cupertino/text_form_field_row.dart | 2 +- .../lib/src/foundation/assertions.dart | 2 +- packages/flutter/lib/src/gestures/tap.dart | 2 +- .../lib/src/material/button_theme.dart | 2 +- .../flutter/lib/src/material/dropdown.dart | 2 +- .../lib/src/material/expansion_panel.dart | 6 ++-- .../flutter/lib/src/material/menu_anchor.dart | 12 +++---- .../flutter/lib/src/material/snack_bar.dart | 4 +-- .../lib/src/material/text_form_field.dart | 2 +- .../lib/src/painting/matrix_utils.dart | 6 ++-- packages/flutter/lib/src/rendering/box.dart | 4 +-- packages/flutter/lib/src/rendering/flow.dart | 4 +-- packages/flutter/lib/src/rendering/layer.dart | 6 ++-- .../lib/src/rendering/mouse_tracker.dart | 4 +-- .../flutter/lib/src/rendering/proxy_box.dart | 6 ++-- .../lib/src/rendering/proxy_sliver.dart | 6 ++-- .../rendering/sliver_multi_box_adaptor.dart | 2 +- packages/flutter/lib/src/rendering/table.dart | 2 +- .../flutter/lib/src/scheduler/binding.dart | 2 +- .../flutter/lib/src/semantics/semantics.dart | 2 +- .../lib/src/services/hardware_keyboard.dart | 8 ++--- packages/flutter/lib/src/widgets/actions.dart | 8 ++++- .../flutter/lib/src/widgets/autocomplete.dart | 4 +-- packages/flutter/lib/src/widgets/basic.dart | 14 ++++---- .../lib/src/widgets/focus_traversal.dart | 2 +- packages/flutter/lib/src/widgets/form.dart | 2 +- .../flutter/lib/src/widgets/framework.dart | 10 +++--- .../lib/src/widgets/gesture_detector.dart | 6 ++-- .../lib/src/widgets/icon_theme_data.dart | 2 +- .../lib/src/widgets/interactive_viewer.dart | 2 +- .../flutter/lib/src/widgets/navigator.dart | 18 +++++----- .../lib/src/widgets/orientation_builder.dart | 2 +- packages/flutter/lib/src/widgets/overlay.dart | 2 +- .../lib/src/widgets/platform_menu_bar.dart | 2 +- .../lib/src/widgets/scroll_physics.dart | 4 +-- .../src/widgets/single_child_scroll_view.dart | 13 +++---- packages/flutter/lib/src/widgets/sliver.dart | 6 ++-- .../lib/src/widgets/text_selection.dart | 2 +- .../flutter/lib/src/widgets/transitions.dart | 6 ++-- .../src/widgets/value_listenable_builder.dart | 9 ++--- .../lib/src/base/deferred_component.dart | 2 +- .../flutter_tools/lib/src/base/utils.dart | 2 +- .../localizations/localizations_utils.dart | 3 +- .../test_data/gen_l10n_project.dart | 6 ++-- 51 files changed, 180 insertions(+), 133 deletions(-) diff --git a/dev/benchmarks/macrobenchmarks/lib/src/web/bench_image_decoding.dart b/dev/benchmarks/macrobenchmarks/lib/src/web/bench_image_decoding.dart index 18792d55001aa..fc478993ce450 100644 --- a/dev/benchmarks/macrobenchmarks/lib/src/web/bench_image_decoding.dart +++ b/dev/benchmarks/macrobenchmarks/lib/src/web/bench_image_decoding.dart @@ -8,18 +8,18 @@ import 'dart:ui' as ui; import 'recorder.dart'; -/// Measures the performance of image decoding. -/// -/// The benchmark measures the decoding latency and not impact on jank. It -/// cannot distinguish between blocking and non-blocking decoding. It simply -/// measures the total time it takes to decode image frames. For example, the -/// WASM codecs execute on the main thread and block the UI, leading to jank, -/// but the browser's WebCodecs API is asynchronous running on a separate thread -/// and does not jank. However, the benchmark result may be the same. -/// -/// This benchmark does not support the HTML renderer because the HTML renderer -/// cannot decode image frames (it always returns 1 dummy frame, even for -/// animated images). +// Measures the performance of image decoding. +// +// The benchmark measures the decoding latency and not impact on jank. It +// cannot distinguish between blocking and non-blocking decoding. It naively +// measures the total time it takes to decode image frames. For example, the +// WASM codecs execute on the main thread and block the UI, leading to jank, +// but the browser's WebCodecs API is asynchronous running on a separate thread +// and does not jank. However, the benchmark result may be the same. +// +// This benchmark does not support the HTML renderer because the HTML renderer +// cannot decode image frames (it always returns 1 dummy frame, even for +// animated images). class BenchImageDecoding extends RawRecorder { BenchImageDecoding() : super( name: benchmarkName, diff --git a/dev/bots/analyze.dart b/dev/bots/analyze.dart index f6c9ec227d046..3e098cb4ea00f 100644 --- a/dev/bots/analyze.dart +++ b/dev/bots/analyze.dart @@ -143,6 +143,9 @@ Future run(List arguments) async { printProgress('null initialized debug fields...'); await verifyNullInitializedDebugExpensiveFields(flutterRoot); + printProgress('Taboo words...'); + await verifyTabooDocumentation(flutterRoot); + // Ensure that all package dependencies are in sync. printProgress('Package dependencies...'); await runCommand(flutter, ['update-packages', '--verify-only'], @@ -1815,18 +1818,17 @@ Future verifyNullInitializedDebugExpensiveFields(String workingDirectory, final List errors = []; for (final File file in files) { final List lines = file.readAsLinesSync(); - for (int i = 0; i < lines.length; i += 1) { - final String line = lines[i]; + for (int index = 0; index < lines.length; index += 1) { + final String line = lines[index]; if (!line.contains(_kDebugOnlyAnnotation)) { continue; } - final String nextLine = lines[i + 1]; + final String nextLine = lines[index + 1]; if (_nullInitializedField.firstMatch(nextLine) == null) { - errors.add('${file.path} L$i'); + errors.add('${file.path}:$index'); } } } - if (errors.isNotEmpty) { foundError([ '${bold}ERROR: ${red}fields annotated with @_debugOnly must null initialize.$reset', @@ -1839,6 +1841,29 @@ Future verifyNullInitializedDebugExpensiveFields(String workingDirectory, } } +final RegExp tabooPattern = RegExp(r'^ *///.*\b(simply)\b', caseSensitive: false); + +Future verifyTabooDocumentation(String workingDirectory, { int minimumMatches = 100 }) async { + final List errors = []; + await for (final File file in _allFiles(workingDirectory, 'dart', minimumMatches: minimumMatches)) { + final List lines = file.readAsLinesSync(); + for (int index = 0; index < lines.length; index += 1) { + final String line = lines[index]; + final Match? match = tabooPattern.firstMatch(line); + if (match != null) { + errors.add('${file.path}:${index + 1}: Found use of the taboo word "${match.group(1)}" in documentation string.'); + } + } + } + if (errors.isNotEmpty) { + foundError([ + '${bold}Avoid the word "simply" in documentation. See https://github.com/flutter/flutter/wiki/Style-guide-for-Flutter-repo#use-the-passive-voice-recommend-do-not-require-never-say-things-are-simple for details.$reset', + '${bold}In many cases the word can be omitted without loss of generality; in other cases it may require a bit of rewording to avoid implying that the task is simple.$reset', + ...errors, + ]); + } +} + Future _runFlutterAnalyze(String workingDirectory, { List options = const [], }) async { diff --git a/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart b/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart index 52fe6897a0eb6..2b14c6f4c2d77 100644 --- a/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart +++ b/dev/bots/test/analyze-test-input/root/packages/flutter/lib/bar.dart @@ -16,3 +16,6 @@ class Foo { @_debugOnly final Map? bar = kDebugMode ? null : {}; } + +/// Simply avoid this +/// and simply do that. diff --git a/dev/bots/test/analyze_test.dart b/dev/bots/test/analyze_test.dart index ace3c7a3fead1..1eaa13b1e8ba3 100644 --- a/dev/bots/test/analyze_test.dart +++ b/dev/bots/test/analyze_test.dart @@ -176,7 +176,18 @@ void main() { minimumMatches: 1, ), shouldHaveErrors: true); - expect(result, contains('L15')); - expect(result, isNot(contains('L12'))); + expect(result, contains(':15')); + expect(result, isNot(contains(':12'))); + }); + + test('analyze.dart - verifyTabooDocumentation', () async { + final String result = await capture(() => verifyTabooDocumentation( + testRootPath, + minimumMatches: 1, + ), shouldHaveErrors: true); + + expect(result, isNot(contains(':19'))); + expect(result, contains(':20')); + expect(result, contains(':21')); }); } diff --git a/packages/flutter/lib/src/animation/animations.dart b/packages/flutter/lib/src/animation/animations.dart index 2d6b6003965e4..0eab6b8006510 100644 --- a/packages/flutter/lib/src/animation/animations.dart +++ b/packages/flutter/lib/src/animation/animations.dart @@ -255,8 +255,8 @@ class ProxyAnimation extends Animation /// If the parent animation is running forward from 0.0 to 1.0, this animation /// is running in reverse from 1.0 to 0.0. /// -/// Using a [ReverseAnimation] is different from simply using a [Tween] with a -/// begin of 1.0 and an end of 0.0 because the tween does not change the status +/// Using a [ReverseAnimation] is different from using a [Tween] with a +/// `begin` of 1.0 and an `end` of 0.0 because the tween does not change the status /// or direction of the animation. /// /// See also: diff --git a/packages/flutter/lib/src/cupertino/context_menu.dart b/packages/flutter/lib/src/cupertino/context_menu.dart index 9e673355c25a8..5a6024b0936c0 100644 --- a/packages/flutter/lib/src/cupertino/context_menu.dart +++ b/packages/flutter/lib/src/cupertino/context_menu.dart @@ -99,10 +99,10 @@ enum _ContextMenuLocation { /// [Expanded] widget so that it will grow to fill the Overlay if its size is /// unconstrained. /// -/// When closed, the CupertinoContextMenu simply displays the child as if the -/// CupertinoContextMenu were not there. Sizing and positioning is unaffected. +/// When closed, the [CupertinoContextMenu] displays the child as if the +/// [CupertinoContextMenu] were not there. Sizing and positioning is unaffected. /// The menu can be closed like other [PopupRoute]s, such as by tapping the -/// background or by calling `Navigator.pop(context)`. Unlike PopupRoute, it can +/// background or by calling `Navigator.pop(context)`. Unlike [PopupRoute], it can /// also be closed by swiping downwards. /// /// The [previewBuilder] parameter is most commonly used to display a slight @@ -111,8 +111,8 @@ enum _ContextMenuLocation { /// Photos app on iOS. /// /// {@tool dartpad} -/// This sample shows a very simple CupertinoContextMenu for the Flutter logo. -/// Simply long press on it to open. +/// This sample shows a very simple [CupertinoContextMenu] for the Flutter logo. +/// Long press on it to open. /// /// ** See code in examples/api/lib/cupertino/context_menu/cupertino_context_menu.0.dart ** /// {@end-tool} @@ -256,10 +256,9 @@ class CupertinoContextMenu extends StatefulWidget { /// and when it is fully open. This will overwrite the default animation that /// matches the behavior of an iOS 16.0 context menu. /// - /// This builder can be used instead of the child when either the intended - /// child has a property that would conflict with the default animation, like - /// a border radius or a shadow, or if simply a more custom animation is - /// needed. + /// This builder can be used instead of the child when the intended child has + /// a property that would conflict with the default animation, such as a + /// border radius or a shadow, or if a more custom animation is needed. /// /// In addition to the current [BuildContext], the function is also called /// with an [Animation]. The complete animation goes from 0 to 1 when diff --git a/packages/flutter/lib/src/cupertino/scrollbar.dart b/packages/flutter/lib/src/cupertino/scrollbar.dart index 34109290f12d4..1b847f2499122 100644 --- a/packages/flutter/lib/src/cupertino/scrollbar.dart +++ b/packages/flutter/lib/src/cupertino/scrollbar.dart @@ -29,7 +29,7 @@ const double _kScrollbarCrossAxisMargin = 3.0; /// An iOS style scrollbar. /// -/// To add a scrollbar to a [ScrollView], simply wrap the scroll view widget in +/// To add a scrollbar to a [ScrollView], wrap the scroll view widget in /// a [CupertinoScrollbar] widget. /// /// {@youtube 560 315 https://www.youtube.com/watch?v=DbkIQSvwnZc} diff --git a/packages/flutter/lib/src/cupertino/text_form_field_row.dart b/packages/flutter/lib/src/cupertino/text_form_field_row.dart index a77669da5f1dc..9a8ca8600fb22 100644 --- a/packages/flutter/lib/src/cupertino/text_form_field_row.dart +++ b/packages/flutter/lib/src/cupertino/text_form_field_row.dart @@ -13,7 +13,7 @@ import 'text_field.dart'; /// Creates a [CupertinoFormRow] containing a [FormField] that wraps /// a [CupertinoTextField]. /// -/// A [Form] ancestor is not required. The [Form] simply makes it easier to +/// A [Form] ancestor is not required. The [Form] allows one 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. diff --git a/packages/flutter/lib/src/foundation/assertions.dart b/packages/flutter/lib/src/foundation/assertions.dart index c95e64ce481f0..49d9b953315dd 100644 --- a/packages/flutter/lib/src/foundation/assertions.dart +++ b/packages/flutter/lib/src/foundation/assertions.dart @@ -140,7 +140,7 @@ class RepetitiveStackFrameFilter extends StackFilter { /// The string to replace the frames with. /// /// If the same replacement string is used multiple times in a row, the - /// [FlutterError.defaultStackFilter] will simply update a counter after this + /// [FlutterError.defaultStackFilter] will insert a repeat count after this /// line rather than repeating it. final String replacement; diff --git a/packages/flutter/lib/src/gestures/tap.dart b/packages/flutter/lib/src/gestures/tap.dart index 6d7910832907b..a05d839cfaee0 100644 --- a/packages/flutter/lib/src/gestures/tap.dart +++ b/packages/flutter/lib/src/gestures/tap.dart @@ -216,7 +216,7 @@ abstract class BaseTapGestureRecognizer extends PrimaryPointerGestureRecognizer if (_down != null) { // This happens when this tap gesture has been rejected while the pointer // is down (i.e. due to movement), when another allowed pointer is added, - // in which case all pointers are simply ignored. The `_down` being null + // in which case all pointers are ignored. The `_down` being null // means that _reset() has been called, since it is always set at the // first allowed down event and will not be cleared except for reset(), super.addAllowedPointer(event); diff --git a/packages/flutter/lib/src/material/button_theme.dart b/packages/flutter/lib/src/material/button_theme.dart index 4981926f409ad..18d55569c1d0e 100644 --- a/packages/flutter/lib/src/material/button_theme.dart +++ b/packages/flutter/lib/src/material/button_theme.dart @@ -235,7 +235,7 @@ class ButtonThemeData with Diagnosticable { /// Defaults to [ButtonBarLayoutBehavior.padded]. final ButtonBarLayoutBehavior layoutBehavior; - /// Simply a convenience that returns [minWidth] and [height] as a + /// Convenience that returns [minWidth] and [height] as a /// [BoxConstraints] object. BoxConstraints get constraints { return BoxConstraints( diff --git a/packages/flutter/lib/src/material/dropdown.dart b/packages/flutter/lib/src/material/dropdown.dart index 9becd8def0b14..0fb827634efb9 100644 --- a/packages/flutter/lib/src/material/dropdown.dart +++ b/packages/flutter/lib/src/material/dropdown.dart @@ -1530,7 +1530,7 @@ class _DropdownButtonState extends State> with WidgetsBindi /// This is a convenience widget that wraps a [DropdownButton] widget in a /// [FormField]. /// -/// A [Form] ancestor is not required. The [Form] simply makes it easier to +/// A [Form] ancestor is not required. The [Form] allows one 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. diff --git a/packages/flutter/lib/src/material/expansion_panel.dart b/packages/flutter/lib/src/material/expansion_panel.dart index 10a57c7e129f7..8195653e8c28d 100644 --- a/packages/flutter/lib/src/material/expansion_panel.dart +++ b/packages/flutter/lib/src/material/expansion_panel.dart @@ -207,14 +207,14 @@ class ExpansionPanelList extends StatefulWidget { /// is pressed. The arguments passed to the callback are the index of the /// pressed panel and whether the panel is currently expanded or not. /// - /// If ExpansionPanelList.radio is used, the callback may be called a + /// If [ExpansionPanelList.radio] is used, the callback may be called a /// second time if a different panel was previously open. The arguments /// passed to the second callback are the index of the panel that will close /// and false, marking that it will be closed. /// - /// For ExpansionPanelList, the callback needs to setState when it's notified + /// For [ExpansionPanelList], the callback needs to setState when it's notified /// about the closing/opening panel. On the other hand, the callback for - /// ExpansionPanelList.radio is simply meant to inform the parent widget of + /// [ExpansionPanelList.radio] is intended to inform the parent widget of /// changes, as the radio panels' open/close states are managed internally. /// /// This callback is useful in order to keep track of the expanded/collapsed diff --git a/packages/flutter/lib/src/material/menu_anchor.dart b/packages/flutter/lib/src/material/menu_anchor.dart index c479f85437183..2c11e9bbebdc5 100644 --- a/packages/flutter/lib/src/material/menu_anchor.dart +++ b/packages/flutter/lib/src/material/menu_anchor.dart @@ -700,10 +700,10 @@ class MenuController { /// platform instead of by Flutter (on macOS, for example). /// * [ShortcutRegistry], a registry of shortcuts that apply for the entire /// application. -/// * [VoidCallbackIntent] to define intents that will call a [VoidCallback] and +/// * [VoidCallbackIntent], to define intents that will call a [VoidCallback] and /// work with the [Actions] and [Shortcuts] system. -/// * [CallbackShortcuts] to define shortcuts that simply call a callback and -/// don't involve using [Actions]. +/// * [CallbackShortcuts], to define shortcuts that call a callback without +/// involving [Actions]. class MenuBar extends StatelessWidget { /// Creates a const [MenuBar]. /// @@ -789,10 +789,10 @@ class MenuBar extends StatelessWidget { /// platform instead of by Flutter (on macOS, for example). /// * [ShortcutRegistry], a registry of shortcuts that apply for the entire /// application. -/// * [VoidCallbackIntent] to define intents that will call a [VoidCallback] and +/// * [VoidCallbackIntent], to define intents that will call a [VoidCallback] and /// work with the [Actions] and [Shortcuts] system. -/// * [CallbackShortcuts] to define shortcuts that simply call a callback and -/// don't involve using [Actions]. +/// * [CallbackShortcuts] to define shortcuts that call a callback without +/// involving [Actions]. class MenuItemButton extends StatefulWidget { /// Creates a const [MenuItemButton]. /// diff --git a/packages/flutter/lib/src/material/snack_bar.dart b/packages/flutter/lib/src/material/snack_bar.dart index 993450fbbff6d..79b52bfa68167 100644 --- a/packages/flutter/lib/src/material/snack_bar.dart +++ b/packages/flutter/lib/src/material/snack_bar.dart @@ -71,8 +71,8 @@ enum SnackBarClosedReason { /// A button for a [SnackBar], known as an "action". /// -/// Snack bar actions are always enabled. If you want to disable a snack bar -/// action, simply don't include it in the snack bar. +/// Snack bar actions are always enabled. Instead of disabling a snack bar +/// action, avoid including it in the snack bar in the first place. /// /// Snack bar actions can only be pressed once. Subsequent presses are ignored. /// diff --git a/packages/flutter/lib/src/material/text_form_field.dart b/packages/flutter/lib/src/material/text_form_field.dart index a9addc22a8454..a0e2e3cab3793 100644 --- a/packages/flutter/lib/src/material/text_form_field.dart +++ b/packages/flutter/lib/src/material/text_form_field.dart @@ -17,7 +17,7 @@ export 'package:flutter/services.dart' show SmartDashesType, SmartQuotesType; /// This is a convenience widget that wraps a [TextField] widget in a /// [FormField]. /// -/// A [Form] ancestor is not required. The [Form] simply makes it easier to +/// A [Form] ancestor is not required. The [Form] allows one to /// save, reset, or validate multiple fields at once. To use without a [Form], /// pass a `GlobalKey` (see [GlobalKey]) to the constructor and use /// [GlobalKey.currentState] to save or reset the form field. diff --git a/packages/flutter/lib/src/painting/matrix_utils.dart b/packages/flutter/lib/src/painting/matrix_utils.dart index d7c215213224a..ce259b6683eea 100644 --- a/packages/flutter/lib/src/painting/matrix_utils.dart +++ b/packages/flutter/lib/src/painting/matrix_utils.dart @@ -455,8 +455,8 @@ class MatrixUtils { /// /// The `radius` simulates the radius of the cylinder the plane is being /// wrapped onto. If the transformation is applied to a 0-dimensional dot - /// instead of a plane, the dot would simply translate by +/- `radius` pixels - /// along the `orientation` [Axis] when rotating from 0 to +/- 90 degrees. + /// instead of a plane, the dot would translate by ± `radius` pixels + /// along the `orientation` [Axis] when rotating from 0 to ±90 degrees. /// /// A positive radius means the object is closest at 0 `angle` and a negative /// radius means the object is closest at π `angle` or 180 degrees. @@ -478,7 +478,7 @@ class MatrixUtils { /// The `orientation` is the direction of the rotation axis. /// /// Because the viewing position is a point, it's never possible to see the - /// outer side of the cylinder at or past +/- π / 2 or 90 degrees and it's + /// outer side of the cylinder at or past ±π/2 or 90 degrees and it's /// almost always possible to end up seeing the inner side of the cylinder /// or the back side of the transformed plane before π / 2 when perspective > 0. static Matrix4 createCylindricalProjectionTransform({ diff --git a/packages/flutter/lib/src/rendering/box.dart b/packages/flutter/lib/src/rendering/box.dart index 7efe503b67320..08726e953b5d4 100644 --- a/packages/flutter/lib/src/rendering/box.dart +++ b/packages/flutter/lib/src/rendering/box.dart @@ -1936,7 +1936,7 @@ abstract class RenderBox extends RenderObject { /// may depend on the baseline of a child (which is not available without /// doing a full layout), it may be computed by a callback about which the /// render object cannot reason, or the layout is so complex that it - /// is simply impractical to calculate the size in an efficient way. + /// is impractical to calculate the size in an efficient way. /// /// In such cases, it may be impossible (or at least impractical) to actually /// return a valid answer. In such cases, the function should call @@ -1964,7 +1964,7 @@ abstract class RenderBox extends RenderObject { /// When asserts are enabled and [debugCheckingIntrinsics] is not true, this /// method will either throw the provided [FlutterError] or it will create and /// throw a [FlutterError] with the provided `reason`. Otherwise, it will - /// simply return true. + /// return true. /// /// One of the arguments has to be provided. /// diff --git a/packages/flutter/lib/src/rendering/flow.dart b/packages/flutter/lib/src/rendering/flow.dart index b0f6d0c38eadd..c186ecc4e33c6 100644 --- a/packages/flutter/lib/src/rendering/flow.dart +++ b/packages/flutter/lib/src/rendering/flow.dart @@ -159,8 +159,8 @@ class FlowParentData extends ContainerBoxParentData { /// /// Rather than positioning the children during layout, the children are /// positioned using transformation matrices during the paint phase using the -/// matrices from the [FlowDelegate.paintChildren] function. The children can be -/// repositioned efficiently by simply repainting the flow. +/// matrices from the [FlowDelegate.paintChildren] function. The children are thus +/// repositioned efficiently by repainting the flow, skipping layout. /// /// The most efficient way to trigger a repaint of the flow is to supply a /// repaint argument to the constructor of the [FlowDelegate]. The flow will diff --git a/packages/flutter/lib/src/rendering/layer.dart b/packages/flutter/lib/src/rendering/layer.dart index c46b237b0cd36..0c049f78d831f 100644 --- a/packages/flutter/lib/src/rendering/layer.dart +++ b/packages/flutter/lib/src/rendering/layer.dart @@ -546,7 +546,7 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin { /// should search for annotations, or if the layer has its own annotations to /// add. /// - /// The default implementation simply returns `false`, which means neither + /// The default implementation always returns `false`, which means neither /// the layer nor its children has annotations, and the annotation search /// is not absorbed either (see below for explanation). /// @@ -602,7 +602,7 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin { /// /// Returns null if no matching annotations are found. /// - /// By default this method simply calls [findAnnotations] with `onlyFirst: + /// By default this method calls [findAnnotations] with `onlyFirst: /// true` and returns the annotation of the first result. Prefer overriding /// [findAnnotations] instead of this method, because during an annotation /// search, only [findAnnotations] is recursively called, while custom @@ -628,7 +628,7 @@ abstract class Layer extends AbstractNode with DiagnosticableTreeMixin { /// /// Returns a result with empty entries if no matching annotations are found. /// - /// By default this method simply calls [findAnnotations] with `onlyFirst: + /// By default this method calls [findAnnotations] with `onlyFirst: /// false` and returns the annotations of its result. Prefer overriding /// [findAnnotations] instead of this method, because during an annotation /// search, only [findAnnotations] is recursively called, while custom diff --git a/packages/flutter/lib/src/rendering/mouse_tracker.dart b/packages/flutter/lib/src/rendering/mouse_tracker.dart index 9b9af0fa69559..75229a1d034e7 100644 --- a/packages/flutter/lib/src/rendering/mouse_tracker.dart +++ b/packages/flutter/lib/src/rendering/mouse_tracker.dart @@ -302,8 +302,8 @@ class MouseTracker extends ChangeNotifier { /// [MouseTracker] filter which to react to. /// /// The `getResult` is a function to return the hit test result at the - /// position of the event. It should not simply return cached hit test - /// result, because the cache does not change throughout a tap sequence. + /// position of the event. It should not return a cached hit test + /// result, because the cache would not change during a tap sequence. void updateWithEvent(PointerEvent event, ValueGetter getResult) { if (event.kind != PointerDeviceKind.mouse) { return; diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index 14ac8dbc4a298..f02efb034a20b 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -24,7 +24,7 @@ export 'package:flutter/gestures.dart' show /// A base class for render boxes that resemble their children. /// -/// A proxy box has a single child and simply mimics all the properties of that +/// A proxy box has a single child and mimics all the properties of that /// child by calling through to the child for each function in the render box /// protocol. For example, a proxy box determines its size by asking its child /// to layout with the same constraints and then matching the size. @@ -41,7 +41,7 @@ export 'package:flutter/gestures.dart' show class RenderProxyBox extends RenderBox with RenderObjectWithChildMixin, RenderProxyBoxMixin { /// Creates a proxy render box. /// - /// Proxy render boxes are rarely created directly because they simply proxy + /// Proxy render boxes are rarely created directly because they proxy /// the render box protocol to [child]. Instead, consider using one of the /// subclasses. RenderProxyBox([RenderBox? child]) { @@ -869,7 +869,7 @@ class RenderIntrinsicHeight extends RenderProxyBox { /// /// For values of opacity other than 0.0 and 1.0, this class is relatively /// expensive because it requires painting the child into an intermediate -/// buffer. For the value 0.0, the child is simply not painted at all. For the +/// buffer. For the value 0.0, the child is not painted at all. For the /// value 1.0, the child is painted immediately without an intermediate buffer. class RenderOpacity extends RenderProxyBox { /// Creates a partially transparent render object. diff --git a/packages/flutter/lib/src/rendering/proxy_sliver.dart b/packages/flutter/lib/src/rendering/proxy_sliver.dart index 519d6e539439b..4ed5e22a8c2b4 100644 --- a/packages/flutter/lib/src/rendering/proxy_sliver.dart +++ b/packages/flutter/lib/src/rendering/proxy_sliver.dart @@ -15,7 +15,7 @@ import 'sliver.dart'; /// A base class for sliver render objects that resemble their children. /// -/// A proxy sliver has a single child and simply mimics all the properties of +/// A proxy sliver has a single child and mimics all the properties of /// that child by calling through to the child for each function in the render /// sliver protocol. For example, a proxy sliver determines its geometry by /// asking its sliver child to layout with the same constraints and then @@ -33,7 +33,7 @@ import 'sliver.dart'; abstract class RenderProxySliver extends RenderSliver with RenderObjectWithChildMixin { /// Creates a proxy render sliver. /// - /// Proxy render slivers aren't created directly because they simply proxy + /// Proxy render slivers aren't created directly because they proxy /// the render sliver protocol to their sliver [child]. Instead, use one of /// the subclasses. RenderProxySliver([RenderSliver? child]) { @@ -94,7 +94,7 @@ abstract class RenderProxySliver extends RenderSliver with RenderObjectWithChild /// /// For values of opacity other than 0.0 and 1.0, this class is relatively /// expensive, because it requires painting the sliver child into an intermediate -/// buffer. For the value 0.0, the sliver child is simply not painted at all. +/// buffer. For the value 0.0, the sliver child is not painted at all. /// For the value 1.0, the sliver child is painted immediately without an /// intermediate buffer. class RenderSliverOpacity extends RenderProxySliver { diff --git a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart index 99c8cd6e236a4..500e1b90eef43 100644 --- a/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart +++ b/packages/flutter/lib/src/rendering/sliver_multi_box_adaptor.dart @@ -113,7 +113,7 @@ abstract class RenderSliverBoxChildManager { /// This function always returns true. /// /// The manager is not required to track whether it is expecting modifications - /// to the [RenderSliverMultiBoxAdaptor]'s child list and can simply return + /// to the [RenderSliverMultiBoxAdaptor]'s child list and can return /// true without making any assertions. bool debugAssertChildListLocked() => true; } diff --git a/packages/flutter/lib/src/rendering/table.dart b/packages/flutter/lib/src/rendering/table.dart index f7e340b359c47..274a749301c35 100644 --- a/packages/flutter/lib/src/rendering/table.dart +++ b/packages/flutter/lib/src/rendering/table.dart @@ -617,7 +617,7 @@ class RenderTable extends RenderBox { /// replacing the existing children. /// /// If the new cells contain any existing children of the table, those - /// children are simply moved to their new location in the table rather than + /// children are moved to their new location in the table rather than /// removed from the table and re-added. void setFlatChildren(int columns, List cells) { if (cells == _children && columns == _columns) { diff --git a/packages/flutter/lib/src/scheduler/binding.dart b/packages/flutter/lib/src/scheduler/binding.dart index 8820c7cf04121..0c107caf0c405 100644 --- a/packages/flutter/lib/src/scheduler/binding.dart +++ b/packages/flutter/lib/src/scheduler/binding.dart @@ -1275,7 +1275,7 @@ mixin SchedulerBinding on BindingBase { // Calls the given [callback] with [timestamp] as argument. // // Wraps the callback in a try/catch and forwards any error to - // [debugSchedulerExceptionHandler], if set. If not set, then simply prints + // [debugSchedulerExceptionHandler], if set. If not set, prints // the error. @pragma('vm:notify-debugger-on-exception') void _invokeFrameCallback(FrameCallback callback, Duration timeStamp, [ StackTrace? callbackStack ]) { diff --git a/packages/flutter/lib/src/semantics/semantics.dart b/packages/flutter/lib/src/semantics/semantics.dart index bed9eea8cb33b..001abd19bb71f 100644 --- a/packages/flutter/lib/src/semantics/semantics.dart +++ b/packages/flutter/lib/src/semantics/semantics.dart @@ -4733,7 +4733,7 @@ abstract class SemanticsSortKey with Diagnosticable implements Comparable extends Action { Object? invoke(T intent) => onInvoke(intent); } -/// An action dispatcher that simply invokes the actions given to it. +/// An action dispatcher that invokes the actions given to it. +/// +/// The [invokeAction] method on this class directly calls the [Action.invoke] +/// method on the [Action] object. +/// +/// For [ContextAction] actions, if no `context` is provided, the +/// [BuildContext] of the [primaryFocus] is used instead. /// /// See also: /// diff --git a/packages/flutter/lib/src/widgets/autocomplete.dart b/packages/flutter/lib/src/widgets/autocomplete.dart index 4e4bd957211c7..0dac444432210 100644 --- a/packages/flutter/lib/src/widgets/autocomplete.dart +++ b/packages/flutter/lib/src/widgets/autocomplete.dart @@ -263,8 +263,8 @@ class RawAutocomplete extends StatefulWidget { /// The default way to convert an option to a string in /// [displayStringForOption]. /// - /// Simply uses the `toString` method on the option. - static String defaultStringForOption(dynamic option) { + /// Uses the `toString` method of the given `option`. + static String defaultStringForOption(Object? option) { return option.toString(); } diff --git a/packages/flutter/lib/src/widgets/basic.dart b/packages/flutter/lib/src/widgets/basic.dart index 99179a428e537..50ffb0c0a1046 100644 --- a/packages/flutter/lib/src/widgets/basic.dart +++ b/packages/flutter/lib/src/widgets/basic.dart @@ -222,7 +222,7 @@ class Directionality extends _UbiquitousInheritedWidget { /// /// For values of opacity other than 0.0 and 1.0, this class is relatively /// expensive because it requires painting the child into an intermediate -/// buffer. For the value 0.0, the child is simply not painted at all. For the +/// buffer. For the value 0.0, the child is not painted at all. For the /// value 1.0, the child is painted immediately without an intermediate buffer. /// /// The presence of the intermediate buffer which has a transparent background @@ -1896,7 +1896,7 @@ class RotatedBox extends SingleChildRenderObjectWidget { /// ### Why use a [Padding] widget rather than a [Container] with a [Container.padding] property? /// /// There isn't really any difference between the two. If you supply a -/// [Container.padding] argument, [Container] simply builds a [Padding] widget +/// [Container.padding] argument, [Container] builds a [Padding] widget /// for you. /// /// [Container] doesn't implement its properties directly. Instead, [Container] @@ -1907,7 +1907,7 @@ class RotatedBox extends SingleChildRenderObjectWidget { /// convenient, feel free to use it. If not, feel free to build these simpler /// widgets in whatever combination meets your needs. /// -/// In fact, the majority of widgets in Flutter are simply combinations of other +/// In fact, the majority of widgets in Flutter are combinations of other /// simpler widgets. Composition, rather than inheritance, is the primary /// mechanism for building up widgets. /// @@ -5439,7 +5439,7 @@ class Wrap extends MultiChildRenderObjectWidget { /// Rather than positioning the children during layout, the children are /// positioned using transformation matrices during the paint phase using the /// matrices from the [FlowDelegate.paintChildren] function. The children can be -/// repositioned efficiently by simply repainting the flow, which happens +/// repositioned efficiently by only _repainting_ the flow, which happens /// without the children being laid out again (contrast this with a [Stack], /// which does the sizing and positioning together during layout). /// @@ -6480,7 +6480,7 @@ class MouseRegion extends SingleChildRenderObjectWidget { /// Because the widget conditionally creates the `MouseRegion`, and leaks the /// hover state, it needs to take the restriction into consideration. In this /// case, since it has access to the event that triggers the disappearance of - /// the `MouseRegion`, it simply trigger the exit callback during that event + /// the `MouseRegion`, it triggers the exit callback during that event /// as well. /// /// ** See code in examples/api/lib/widgets/basic/mouse_region.on_exit.1.dart ** @@ -7373,8 +7373,8 @@ class KeyedSubtree extends StatelessWidget { /// ) /// ``` /// -/// The difference between either of the previous examples and simply -/// creating a child directly, without an intervening widget, is the +/// The difference between either of the previous examples and +/// creating a child directly without an intervening widget, is the /// extra [BuildContext] element that the additional widget adds. This /// is particularly noticeable when the tree contains an inherited /// widget that is referred to by a method like [Scaffold.of], diff --git a/packages/flutter/lib/src/widgets/focus_traversal.dart b/packages/flutter/lib/src/widgets/focus_traversal.dart index fdd94f371b309..3eb2dddaa3595 100644 --- a/packages/flutter/lib/src/widgets/focus_traversal.dart +++ b/packages/flutter/lib/src/widgets/focus_traversal.dart @@ -1677,7 +1677,7 @@ class RequestFocusIntent extends Intent { /// logging, or undo and redo functionality. /// /// This [RequestFocusAction] class is the default action associated with the -/// [RequestFocusIntent] in the [WidgetsApp], and it simply requests focus. You +/// [RequestFocusIntent] in the [WidgetsApp]. It requests focus. You /// can redefine the associated action with your own [Actions] widget. /// /// See [FocusTraversalPolicy] for more information about focus traversal. diff --git a/packages/flutter/lib/src/widgets/form.dart b/packages/flutter/lib/src/widgets/form.dart index 24d4afb2ee150..749fb65d1be20 100644 --- a/packages/flutter/lib/src/widgets/form.dart +++ b/packages/flutter/lib/src/widgets/form.dart @@ -296,7 +296,7 @@ typedef FormFieldBuilder = Widget Function(FormFieldState field); /// Use a [GlobalKey] with [FormField] if you want to retrieve its current /// state, for example if you want one form field to depend on another. /// -/// A [Form] ancestor is not required. The [Form] simply makes it easier to +/// A [Form] ancestor is not required. The [Form] allows one 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. diff --git a/packages/flutter/lib/src/widgets/framework.dart b/packages/flutter/lib/src/widgets/framework.dart index a1ef69e3e8837..217828f6dabe9 100644 --- a/packages/flutter/lib/src/widgets/framework.dart +++ b/packages/flutter/lib/src/widgets/framework.dart @@ -654,7 +654,7 @@ abstract class StatelessWidget extends Widget { /// this ideal, the more efficient it will be.) /// /// * If a subtree does not change, cache the widget that represents that -/// subtree and re-use it each time it can be used. To do this, simply assign +/// subtree and re-use it each time it can be used. To do this, assign /// a widget to a `final` state variable and re-use it in the build method. It /// is massively more efficient for a widget to be re-used than for a new (but /// identically-configured) widget to be created. Another caching strategy @@ -3841,7 +3841,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext { /// Remove [renderObject] from the render tree. /// - /// The default implementation of this function simply calls + /// The default implementation of this function calls /// [detachRenderObject] recursively on each child. The /// [RenderObjectElement.detachRenderObject] override does the actual work of /// removing [renderObject] from the render tree. @@ -3856,7 +3856,7 @@ abstract class Element extends DiagnosticableTree implements BuildContext { /// Add [renderObject] to the render tree at the location specified by `newSlot`. /// - /// The default implementation of this function simply calls + /// The default implementation of this function calls /// [attachRenderObject] recursively on each child. The /// [RenderObjectElement.attachRenderObject] override does the actual work of /// adding [renderObject] to the render tree. @@ -5646,7 +5646,7 @@ class InheritedElement extends ProxyElement { /// /// However, the immediate children of the element may not be the ones that /// eventually produce the actual [RenderObject] that they correspond to. For -/// example a [StatelessElement] (the element of a [StatelessWidget]) simply +/// example, a [StatelessElement] (the element of a [StatelessWidget]) /// corresponds to whatever [RenderObject] its child (the element returned by /// its [StatelessWidget.build] method) corresponds to. /// @@ -5923,7 +5923,7 @@ abstract class RenderObjectElement extends Element { /// list after the [RenderObject] associated with the [Element] provided as /// [IndexedSlot.value] in the [slot] object. /// - /// Simply using the previous sibling as a [slot] is not enough, though, because + /// Using the previous sibling as a [slot] is not enough, though, because /// child [RenderObject]s are only moved around when the [slot] of their /// associated [RenderObjectElement]s is updated. When the order of child /// [Element]s is changed, some elements in the list may move to a new index diff --git a/packages/flutter/lib/src/widgets/gesture_detector.dart b/packages/flutter/lib/src/widgets/gesture_detector.dart index 098634dee50c1..2130e0af9e8ff 100644 --- a/packages/flutter/lib/src/widgets/gesture_detector.dart +++ b/packages/flutter/lib/src/widgets/gesture_detector.dart @@ -215,11 +215,11 @@ class GestureDetector extends StatelessWidget { /// Creates a widget that detects gestures. /// /// Pan and scale callbacks cannot be used simultaneously because scale is a - /// superset of pan. Simply use the scale callbacks instead. + /// superset of pan. Use the scale callbacks instead. /// /// Horizontal and vertical drag callbacks cannot be used simultaneously - /// because a combination of a horizontal and vertical drag is a pan. Simply - /// use the pan callbacks instead. + /// because a combination of a horizontal and vertical drag is a pan. + /// Use the pan callbacks instead. /// /// {@youtube 560 315 https://www.youtube.com/watch?v=WhVXkCFPmK4} /// diff --git a/packages/flutter/lib/src/widgets/icon_theme_data.dart b/packages/flutter/lib/src/widgets/icon_theme_data.dart index 9a2cc5d34dc7d..d61d39febd080 100644 --- a/packages/flutter/lib/src/widgets/icon_theme_data.dart +++ b/packages/flutter/lib/src/widgets/icon_theme_data.dart @@ -76,7 +76,7 @@ class IconThemeData with Diagnosticable { /// Returns a new icon theme that matches this icon theme but with some values /// replaced by the non-null parameters of the given icon theme. If the given - /// icon theme is null, simply returns this icon theme. + /// icon theme is null, returns this icon theme. IconThemeData merge(IconThemeData? other) { if (other == null) { return this; diff --git a/packages/flutter/lib/src/widgets/interactive_viewer.dart b/packages/flutter/lib/src/widgets/interactive_viewer.dart index 832bdcd09566d..005bb991609ef 100644 --- a/packages/flutter/lib/src/widgets/interactive_viewer.dart +++ b/packages/flutter/lib/src/widgets/interactive_viewer.dart @@ -1141,7 +1141,7 @@ class _InteractiveViewerState extends State with TickerProvid } } -// This widget simply allows us to easily swap in and out the LayoutBuilder in +// This widget allows us to easily swap in and out the LayoutBuilder in // InteractiveViewer's depending on if it's using a builder or a child. class _InteractiveViewerBuilt extends StatelessWidget { const _InteractiveViewerBuilt({ diff --git a/packages/flutter/lib/src/widgets/navigator.dart b/packages/flutter/lib/src/widgets/navigator.dart index 3d0979f313e86..2fe21397ad2d6 100644 --- a/packages/flutter/lib/src/widgets/navigator.dart +++ b/packages/flutter/lib/src/widgets/navigator.dart @@ -611,15 +611,15 @@ class NavigatorObserver { /// The navigator that the observer is observing, if any. NavigatorState? get navigator => _navigators[this]; - /// Expando mapping instances of NavigatorObserver to their associated - /// NavigatorState (or `null`, if there is no associated NavigatorState). The - /// reason we don't simply use a private instance field of type - /// `NavigatorState?` is because as part of implementing - /// https://github.com/dart-lang/language/issues/2020, it will soon become a - /// runtime error to invoke a private member that is mocked in another - /// library. By using an expando rather than an instance field, we ensure - /// that a mocked NavigatorObserver can still properly keep track of its - /// associated NavigatorState. + // Expando mapping instances of NavigatorObserver to their associated + // NavigatorState (or `null`, if there is no associated NavigatorState). The + // reason we don't use a private instance field of type + // `NavigatorState?` is because as part of implementing + // https://github.com/dart-lang/language/issues/2020, it will soon become a + // runtime error to invoke a private member that is mocked in another + // library. By using an expando rather than an instance field, we ensure + // that a mocked NavigatorObserver can still properly keep track of its + // associated NavigatorState. static final Expando _navigators = Expando(); /// The [Navigator] pushed `route`. diff --git a/packages/flutter/lib/src/widgets/orientation_builder.dart b/packages/flutter/lib/src/widgets/orientation_builder.dart index 189a621c580e5..3bed33422fb98 100644 --- a/packages/flutter/lib/src/widgets/orientation_builder.dart +++ b/packages/flutter/lib/src/widgets/orientation_builder.dart @@ -35,7 +35,7 @@ class OrientationBuilder extends StatelessWidget { /// Builds the widgets below this widget given this widget's orientation. /// - /// A widget's orientation is simply a factor of its width relative to its + /// A widget's orientation is a factor of its width relative to its /// height. For example, a [Column] widget will have a landscape orientation /// if its width exceeds its height, even though it displays its children in /// a vertical array. diff --git a/packages/flutter/lib/src/widgets/overlay.dart b/packages/flutter/lib/src/widgets/overlay.dart index 58e52eb568333..15a5fe2aa4373 100644 --- a/packages/flutter/lib/src/widgets/overlay.dart +++ b/packages/flutter/lib/src/widgets/overlay.dart @@ -280,7 +280,7 @@ class _OverlayEntryWidgetState extends State<_OverlayEntryWidget> { /// The [Overlay] widget uses a custom stack implementation, which is very /// similar to the [Stack] widget. The main use case of [Overlay] is related to /// navigation and being able to insert widgets on top of the pages in an app. -/// To simply display a stack of widgets, consider using [Stack] instead. +/// For layout purposes unrelated to navigation, consider using [Stack] instead. /// /// An [Overlay] widget requires a [Directionality] widget to be in scope, so /// that it can resolve direction-sensitive coordinates of any diff --git a/packages/flutter/lib/src/widgets/platform_menu_bar.dart b/packages/flutter/lib/src/widgets/platform_menu_bar.dart index b670c96000aeb..c4840176b704a 100644 --- a/packages/flutter/lib/src/widgets/platform_menu_bar.dart +++ b/packages/flutter/lib/src/widgets/platform_menu_bar.dart @@ -954,7 +954,7 @@ enum PlatformProvidedMenuItemType { /// /// On macOS, this is the `terminate` default menu. /// - /// This menu item will simply exit the application when activated. + /// This menu item will exit the application when activated. quit, /// The system provided "Services" submenu. diff --git a/packages/flutter/lib/src/widgets/scroll_physics.dart b/packages/flutter/lib/src/widgets/scroll_physics.dart index 0d7db82e1d869..d4519035104dd 100644 --- a/packages/flutter/lib/src/widgets/scroll_physics.dart +++ b/packages/flutter/lib/src/widgets/scroll_physics.dart @@ -236,8 +236,8 @@ class ScrollPhysics { /// the overall scale between the global screen and local scrollable /// coordinate systems. /// - /// The default implementation is stateless, and simply provides a point-in- - /// time decision about how fast the scrollable is scrolling. It would always + /// The default implementation is stateless, and provides a point-in-time + /// decision about how fast the scrollable is scrolling. It would always /// return true for a scrollable that is animating back and forth at high /// velocity in a loop. It is assumed that callers will handle such /// a case, or that a custom stateful implementation would be written that diff --git a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart index d2361d723ab36..b8a7835b6f6d8 100644 --- a/packages/flutter/lib/src/widgets/single_child_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/single_child_scroll_view.dart @@ -48,17 +48,18 @@ import 'scrollable.dart'; /// small window in split-screen mode. In any case, as a result, it might /// make sense to wrap the layout in a [SingleChildScrollView]. /// -/// Simply doing so, however, usually results in a conflict between the [Column], +/// Doing so, however, usually results in a conflict between the [Column], /// which typically tries to grow as big as it can, and the [SingleChildScrollView], /// which provides its children with an infinite amount of space. /// /// To resolve this apparent conflict, there are a couple of techniques, as /// discussed below. These techniques should only be used when the content is -/// normally expected to fit on the screen, so that the lazy instantiation of -/// a sliver-based [ListView] or [CustomScrollView] is not expected to provide -/// any performance benefit. If the viewport is expected to usually contain -/// content beyond the dimensions of the screen, then [SingleChildScrollView] -/// would be very expensive. +/// normally expected to fit on the screen, so that the lazy instantiation of a +/// sliver-based [ListView] or [CustomScrollView] is not expected to provide any +/// performance benefit. If the viewport is expected to usually contain content +/// beyond the dimensions of the screen, then [SingleChildScrollView] would be +/// very expensive (in which case [ListView] may be a better choice than +/// [Column]). /// /// ### Centering, spacing, or aligning fixed-height content /// diff --git a/packages/flutter/lib/src/widgets/sliver.dart b/packages/flutter/lib/src/widgets/sliver.dart index aac5de39243f6..5dce14a159007 100644 --- a/packages/flutter/lib/src/widgets/sliver.dart +++ b/packages/flutter/lib/src/widgets/sliver.dart @@ -417,7 +417,7 @@ class SliverChildBuilderDelegate extends SliverChildDelegate { /// boundaries so that they do not need to be repainted as the list scrolls. /// If the children are easy to repaint (e.g., solid color blocks or a short /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. + /// and instead always repaint the children during scrolling. /// /// Defaults to true. final bool addRepaintBoundaries; @@ -632,7 +632,7 @@ class SliverChildListDelegate extends SliverChildDelegate { /// boundaries so that they do not need to be repainted as the list scrolls. /// If the children are easy to repaint (e.g., solid color blocks or a short /// snippet of text), it might be more efficient to not add a repaint boundary - /// and simply repaint the children during scrolling. + /// and instead always repaint the children during scrolling. /// /// Defaults to true. final bool addRepaintBoundaries; @@ -1681,7 +1681,7 @@ class SliverMultiBoxAdaptorElement extends RenderObjectElement implements Render /// /// For values of opacity other than 0.0 and 1.0, this class is relatively /// expensive because it requires painting the sliver child into an intermediate -/// buffer. For the value 0.0, the sliver child is simply not painted at all. +/// buffer. For the value 0.0, the sliver child is not painted at all. /// For the value 1.0, the sliver child is painted immediately without an /// intermediate buffer. /// diff --git a/packages/flutter/lib/src/widgets/text_selection.dart b/packages/flutter/lib/src/widgets/text_selection.dart index 211036cf80094..733c2e287c071 100644 --- a/packages/flutter/lib/src/widgets/text_selection.dart +++ b/packages/flutter/lib/src/widgets/text_selection.dart @@ -2512,7 +2512,7 @@ class TextSelectionGestureDetectorBuilder { /// Handler for [TextSelectionGestureDetector.onDragSelectionEnd]. /// - /// By default, it simply cleans up the state used for handling certain + /// By default, it cleans up the state used for handling certain /// built-in behaviors. /// /// See also: diff --git a/packages/flutter/lib/src/widgets/transitions.dart b/packages/flutter/lib/src/widgets/transitions.dart index ce9ba3fc8d3fd..4f666d158138e 100644 --- a/packages/flutter/lib/src/widgets/transitions.dart +++ b/packages/flutter/lib/src/widgets/transitions.dart @@ -22,7 +22,7 @@ export 'package:flutter/rendering.dart' show RelativeRect; /// [ChangeNotifier] and [ValueNotifier]. /// /// [AnimatedWidget] is most useful for widgets that are otherwise stateless. To -/// use [AnimatedWidget], simply subclass it and implement the build function. +/// use [AnimatedWidget], subclass it and implement the build function. /// /// {@tool dartpad} /// This code defines a widget called `Spinner` that spins a green square @@ -1003,7 +1003,7 @@ class DefaultTextStyleTransition extends AnimatedWidget { /// /// [ListenableBuilder] is useful for more complex widgets that wish to listen /// to changes in other objects as part of a larger build function. To use -/// [ListenableBuilder], simply construct the widget and pass it a [builder] +/// [ListenableBuilder], construct the widget and pass it a [builder] /// function. /// /// Any subtype of [Listenable] (such as a [ChangeNotifier], [ValueNotifier], or @@ -1100,7 +1100,7 @@ class ListenableBuilder extends AnimatedWidget { /// /// [AnimatedBuilder] is useful for more complex widgets that wish to include /// an animation as part of a larger build function. To use [AnimatedBuilder], -/// simply construct the widget and pass it a builder function. +/// construct the widget and pass it a builder function. /// /// For simple cases without additional state, consider using /// [AnimatedWidget]. diff --git a/packages/flutter/lib/src/widgets/value_listenable_builder.dart b/packages/flutter/lib/src/widgets/value_listenable_builder.dart index 76cd9879288d1..dc4a9bc76142d 100644 --- a/packages/flutter/lib/src/widgets/value_listenable_builder.dart +++ b/packages/flutter/lib/src/widgets/value_listenable_builder.dart @@ -141,10 +141,11 @@ class ValueListenableBuilder extends StatefulWidget { /// A [valueListenable]-independent widget which is passed back to the [builder]. /// - /// This argument is optional and can be null if the entire widget subtree - /// the [builder] builds depends on the value of the [valueListenable]. For - /// example, if the [valueListenable] is a [String] and the [builder] simply - /// returns a [Text] widget with the [String] value. + /// This argument is optional and can be null if the entire widget subtree the + /// [builder] builds depends on the value of the [valueListenable]. For + /// example, in the case where the [valueListenable] is a [String] and the + /// [builder] returns a [Text] widget with the current [String] value, there + /// would be no useful [child]. final Widget? child; @override diff --git a/packages/flutter_tools/lib/src/base/deferred_component.dart b/packages/flutter_tools/lib/src/base/deferred_component.dart index dda7d9c9e9ae9..1d0ad93f3aef8 100644 --- a/packages/flutter_tools/lib/src/base/deferred_component.dart +++ b/packages/flutter_tools/lib/src/base/deferred_component.dart @@ -50,7 +50,7 @@ class DeferredComponent { /// Indicates if the component has loading units assigned. /// - /// Unassigned components simply reflect the pubspec.yaml configuration directly, + /// Unassigned components reflect the pubspec.yaml configuration directly, /// contain no loading unit data, and [loadingUnits] is null. Once assigned, the component /// will contain a set of [loadingUnits] which contains the [LoadingUnit]s that the /// component needs to include. Loading units can be assigned with the [assignLoadingUnits] diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart index 142b801f07cb4..9a437c85ba0e4 100644 --- a/packages/flutter_tools/lib/src/base/utils.dart +++ b/packages/flutter_tools/lib/src/base/utils.dart @@ -288,7 +288,7 @@ class _AnsiRun { /// widths is fine, for instance). /// /// If [outputPreferences.wrapText] is false, then the text will be returned -/// simply split at the newlines, but not wrapped. If [shouldWrap] is specified, +/// split at the newlines, but not wrapped. If [shouldWrap] is specified, /// then it overrides the [outputPreferences.wrapText] setting. List _wrapTextAsLines(String text, { int start = 0, diff --git a/packages/flutter_tools/lib/src/localizations/localizations_utils.dart b/packages/flutter_tools/lib/src/localizations/localizations_utils.dart index c8e29d21b507a..cd4f9e28bc48b 100644 --- a/packages/flutter_tools/lib/src/localizations/localizations_utils.dart +++ b/packages/flutter_tools/lib/src/localizations/localizations_utils.dart @@ -297,7 +297,8 @@ String generateString(String value) { /// Given a list of strings, placeholders, or helper function calls, concatenate /// them into one expression to be returned. -/// If isSingleStringVar is passed, then we want to convert "'$expr'" to simply "expr". +/// +/// If `isSingleStringVar` is passed, then we want to convert "'$expr'" to "expr". String generateReturnExpr(List expressions, { bool isSingleStringVar = false }) { if (expressions.isEmpty) { return "''"; diff --git a/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart b/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart index 13639aa3efb56..b3c55ab71b92e 100644 --- a/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart +++ b/packages/flutter_tools/test/integration.shard/test_data/gen_l10n_project.dart @@ -700,9 +700,9 @@ void main() { } '''; - /// All messages are simply the template language's message with 'ES - ' - /// appended. This makes validating test behavior easier. The interpolated - /// messages are different where applicable. + // All these messages are the template language's message with 'ES - ' + // appended. This makes validating test behavior easier. The interpolated + // messages are different where applicable. final String appEs = r''' { "@@locale": "es", From 92aebc953d555664adece520d343f7ba61165a48 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Sun, 11 Dec 2022 22:24:23 -0500 Subject: [PATCH 28/71] 922546c91 [Impeller] Fix asset names used for the generated entrypoint name can contain invalid identifiers for the target language (flutter/engine#38202) (#116868) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 815c9f743a16e..099338a619722 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -3ca497ebb107157f9c29207f3e25da4f66888040 +922546c9194b75a04c5b09d7a365e95ae98a84a9 From 437f6f86ec9a8158f0e6077fd0d1cde2fef01bae Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 01:46:41 -0500 Subject: [PATCH 29/71] 9e37c9883 Roll Skia from 29791c73ae16 to 7bd37737e35d (1 revision) (flutter/engine#38207) (#116871) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 099338a619722..1189b17145281 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -922546c9194b75a04c5b09d7a365e95ae98a84a9 +9e37c988362ef91bbfdca388d0ad8c890acbf960 From d19f7767473b3bd303726cc9cda8c356bcd94cb8 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 07:08:42 -0500 Subject: [PATCH 30/71] 62a5de2ef Roll Skia from 7bd37737e35d to 0cb546781e89 (4 revisions) (flutter/engine#38213) (#116880) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 1189b17145281..f4bc8a925dd61 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -9e37c988362ef91bbfdca388d0ad8c890acbf960 +62a5de2efc9c9f877d3c2bd7d6844df3da34f7d0 From bd0791be3ff217c4f22e7cc09e1469f973470a9b Mon Sep 17 00:00:00 2001 From: godofredoc Date: Mon, 12 Dec 2022 07:17:51 -0800 Subject: [PATCH 31/71] Pass drone_dimensions as part of the main target. (#116812) * Pass drone_dimensions as part of the main target. Drone dimensions were passed as properties when the sharding utility is expecting them in the target. Bug: https://github.com/flutter/flutter/issues/116794 * Use dimensions instead of drone_dimensions. * Use a map format. --- .ci.yaml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.ci.yaml b/.ci.yaml index 63a7c40a65523..7d92df7395d10 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -4690,9 +4690,8 @@ targets: task_name: flutter_packaging tags: > ["framework", "hostonly", "shard", "mac"] - drone_dimensions: > - ["cpu=arm64"] - + dimensions: + cpu: "arm64" - name: Windows flutter_packaging recipe: packaging_v2/packaging_v2 From d1436d1df21ee2d40713af682446c66ee3165a58 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 10:44:52 -0500 Subject: [PATCH 32/71] Roll Plugins from 6ab7d710d2fb to 0609adb457fd (2 revisions) (#116891) * 2dab59dc5 Roll Flutter from 521028c80827 to eefbe85c8bd4 (10 revisions) (flutter/plugins#6820) * 0609adb45 [tools] Recognize Pigeon tests in version-check (flutter/plugins#6813) --- bin/internal/flutter_plugins.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/flutter_plugins.version b/bin/internal/flutter_plugins.version index 1666f68e1e73a..2a6413e8085d8 100644 --- a/bin/internal/flutter_plugins.version +++ b/bin/internal/flutter_plugins.version @@ -1 +1 @@ -6ab7d710d2fbd95de7746fadd61fc87a2907ed03 +0609adb457fd448be2f3c3684cc7582814fef4f8 From 84ed058b429a2974ec729f60fee9243240fddc6c Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 12 Dec 2022 09:44:32 -0800 Subject: [PATCH 33/71] [flutter_tools] Add remap sampler support (#116861) --- .../lib/src/build_system/targets/shader_compiler.dart | 1 + .../test/general.shard/asset_bundle_test.dart | 2 ++ .../test/general.shard/build_system/targets/ios_test.dart | 1 + .../build_system/targets/shader_compiler_test.dart | 8 ++++++++ 4 files changed, 12 insertions(+) diff --git a/packages/flutter_tools/lib/src/build_system/targets/shader_compiler.dart b/packages/flutter_tools/lib/src/build_system/targets/shader_compiler.dart index 5ca39654a68e7..56216a71933c4 100644 --- a/packages/flutter_tools/lib/src/build_system/targets/shader_compiler.dart +++ b/packages/flutter_tools/lib/src/build_system/targets/shader_compiler.dart @@ -186,6 +186,7 @@ class ShaderCompiler { '--spirv=$outputPath.spirv', '--input=${input.path}', '--input-type=frag', + '--remap-samplers', '--include=${input.parent.path}', '--include=$shaderLibPath', ]; diff --git a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart index bd272f884ffc5..2b34bafed973a 100644 --- a/packages/flutter_tools/test/general.shard/asset_bundle_test.dart +++ b/packages/flutter_tools/test/general.shard/asset_bundle_test.dart @@ -453,6 +453,7 @@ flutter: '--spirv=$outputPath.spirv', '--input=/$shaderPath', '--input-type=frag', + '--remap-samplers', '--include=/$assetsPath', '--include=$shaderLibDir', ], @@ -541,6 +542,7 @@ flutter: '--spirv=${fileSystem.path.join(output.path, 'shaders', 'ink_sparkle.frag.spirv')}', '--input=${fileSystem.path.join(materialDir.path, 'shaders', 'ink_sparkle.frag')}', '--input-type=frag', + '--remap-samplers', '--include=${fileSystem.path.join(materialDir.path, 'shaders')}', '--include=$shaderLibDir', ], diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart index 314242fa83fb1..ce1c831959e1d 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart @@ -262,6 +262,7 @@ void main() { '--spirv=/App.framework/flutter_assets/shader.glsl.spirv', '--input=/shader.glsl', '--input-type=frag', + '--remap-samplers', '--include=/', '--include=/./shader_lib', ]), diff --git a/packages/flutter_tools/test/general.shard/build_system/targets/shader_compiler_test.dart b/packages/flutter_tools/test/general.shard/build_system/targets/shader_compiler_test.dart index 481a5148507b0..7482bcd97191a 100644 --- a/packages/flutter_tools/test/general.shard/build_system/targets/shader_compiler_test.dart +++ b/packages/flutter_tools/test/general.shard/build_system/targets/shader_compiler_test.dart @@ -50,6 +50,7 @@ void main() { '--spirv=$outputSpirvPath', '--input=$fragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -90,6 +91,7 @@ void main() { '--spirv=$outputPath.spirv', '--input=$fragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -128,6 +130,7 @@ void main() { '--spirv=$outputPath.spirv', '--input=$fragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -166,6 +169,7 @@ void main() { '--spirv=$outputSpirvPath', '--input=$notFragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -206,6 +210,7 @@ void main() { '--spirv=$outputSpirvPath', '--input=$notFragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -248,6 +253,7 @@ void main() { '--spirv=/.tmp_rand0/0.8255140718871702.temp.spirv', '--input=$fragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -293,6 +299,7 @@ void main() { '--spirv=/.tmp_rand0/0.8255140718871702.temp.spirv', '--input=$fragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], @@ -339,6 +346,7 @@ void main() { '--spirv=/.tmp_rand0/0.8255140718871702.temp.spirv', '--input=$fragPath', '--input-type=frag', + '--remap-samplers', '--include=$fragDir', '--include=$shaderLibDir', ], From a8c9f9c6f4b1e7980d904a85a99c2ece16d4e96d Mon Sep 17 00:00:00 2001 From: Taha Tesser Date: Mon, 12 Dec 2022 20:02:20 +0200 Subject: [PATCH 34/71] Fix `NavigationBar` ripple for non-default `NavigationDestinationLabelBehavior` (#116888) --- .../lib/src/material/navigation_bar.dart | 45 ++++- .../test/material/navigation_bar_test.dart | 167 ++++++++++++++++-- 2 files changed, 185 insertions(+), 27 deletions(-) diff --git a/packages/flutter/lib/src/material/navigation_bar.dart b/packages/flutter/lib/src/material/navigation_bar.dart index e890eea375645..0b24712426df8 100644 --- a/packages/flutter/lib/src/material/navigation_bar.dart +++ b/packages/flutter/lib/src/material/navigation_bar.dart @@ -17,8 +17,8 @@ import 'text_theme.dart'; import 'theme.dart'; import 'tooltip.dart'; -const double _kIndicatorHeight = 64; -const double _kIndicatorWidth = 32; +const double _kIndicatorHeight = 32; +const double _kIndicatorWidth = 64; // Examples can assume: // late BuildContext context; @@ -212,6 +212,7 @@ class NavigationBar extends StatelessWidget { builder: (BuildContext context, Animation animation) { return _NavigationDestinationInfo( index: i, + selectedIndex: selectedIndex, totalNumberOfDestinations: destinations.length, selectedAnimation: animation, labelBehavior: effectiveLabelBehavior, @@ -435,10 +436,25 @@ class _NavigationDestinationBuilder extends StatelessWidget { final NavigationBarThemeData navigationBarTheme = NavigationBarTheme.of(context); final NavigationBarThemeData defaults = _defaultsFor(context); + final bool selected = info.selectedIndex == info.index; + final double labelPadding; + switch (info.labelBehavior) { + case NavigationDestinationLabelBehavior.alwaysShow: + labelPadding = 10; + break; + case NavigationDestinationLabelBehavior.onlyShowSelected: + labelPadding = selected ? 10 : 0; + break; + case NavigationDestinationLabelBehavior.alwaysHide: + labelPadding = 0; + break; + } return _NavigationBarDestinationSemantics( child: _NavigationBarDestinationTooltip( message: tooltip ?? label, child: _IndicatorInkWell( + key: UniqueKey(), + labelPadding: labelPadding, customBorder: navigationBarTheme.indicatorShape ?? defaults.indicatorShape, onTap: info.onTap, child: Row( @@ -459,24 +475,28 @@ class _NavigationDestinationBuilder extends StatelessWidget { class _IndicatorInkWell extends InkResponse { const _IndicatorInkWell({ - super.child, - super.onTap, + super.key, + required this.labelPadding, super.customBorder, + super.onTap, + super.child, }) : super( containedInkWell: true, highlightColor: Colors.transparent, ); + final double labelPadding; + @override RectCallback? getRectCallback(RenderBox referenceBox) { final double indicatorOffsetX = referenceBox.size.width / 2; - const double indicatorOffsetY = 30.0; + final double indicatorOffsetY = referenceBox.size.height / 2 - labelPadding; return () { return Rect.fromCenter( center: Offset(indicatorOffsetX, indicatorOffsetY), - width: _kIndicatorHeight, - height: _kIndicatorWidth, + width: _kIndicatorWidth, + height: _kIndicatorHeight, ); }; } @@ -492,6 +512,7 @@ class _NavigationDestinationInfo extends InheritedWidget { /// [child] and descendants. const _NavigationDestinationInfo({ required this.index, + required this.selectedIndex, required this.totalNumberOfDestinations, required this.selectedAnimation, required this.labelBehavior, @@ -529,6 +550,12 @@ class _NavigationDestinationInfo extends InheritedWidget { /// "Tab 1 of 3", for example. final int index; + /// This is the index of the currently selected destination. + /// + /// This is required for `_IndicatorInkWell` to apply label padding to ripple animations + /// when label behavior is [NavigationDestinationLabelBehavior.onlyShowSelected]. + final int selectedIndex; + /// How many total destinations are are in this navigation bar. /// /// This is required for semantics, so that each destination can have a label @@ -593,8 +620,8 @@ class NavigationIndicator extends StatelessWidget { super.key, required this.animation, this.color, - this.width = _kIndicatorHeight, - this.height = _kIndicatorWidth, + this.width = _kIndicatorWidth, + this.height = _kIndicatorHeight, this.borderRadius = const BorderRadius.all(Radius.circular(16)), this.shape, }); diff --git a/packages/flutter/test/material/navigation_bar_test.dart b/packages/flutter/test/material/navigation_bar_test.dart index 93d1afb478a2f..17028c66a23d7 100644 --- a/packages/flutter/test/material/navigation_bar_test.dart +++ b/packages/flutter/test/material/navigation_bar_test.dart @@ -558,24 +558,30 @@ void main() { }); testWidgets('Navigation indicator renders ripple', (WidgetTester tester) async { - final Widget widget = _buildWidget( - NavigationBar( - destinations: const [ - NavigationDestination( - icon: Icon(Icons.ac_unit), - label: 'AC', - ), - NavigationDestination( - icon: Icon(Icons.access_alarm), - label: 'Alarm', - ), - ], - onDestinationSelected: (int i) { - }, - ), - ); + // This is a regression test for https://github.com/flutter/flutter/issues/116751. + int selectedIndex = 0; - await tester.pumpWidget(widget); + Widget buildWidget({ NavigationDestinationLabelBehavior? labelBehavior }) { + return _buildWidget( + NavigationBar( + selectedIndex: selectedIndex, + labelBehavior: labelBehavior, + destinations: const [ + NavigationDestination( + icon: Icon(Icons.ac_unit), + label: 'AC', + ), + NavigationDestination( + icon: Icon(Icons.access_alarm), + label: 'Alarm', + ), + ], + onDestinationSelected: (int i) { }, + ), + ); + } + + await tester.pumpWidget(buildWidget()); final TestGesture gesture = await tester.createGesture(kind: PointerDeviceKind.mouse); await gesture.addPointer(); @@ -583,9 +589,134 @@ void main() { await tester.pumpAndSettle(); final RenderObject inkFeatures = tester.allRenderObjects.firstWhere((RenderObject object) => object.runtimeType.toString() == '_RenderInkFeatures'); - const Offset indicatorCenter = Offset(600, 30); + Offset indicatorCenter = const Offset(600, 30); const Size includedIndicatorSize = Size(64, 32); const Size excludedIndicatorSize = Size(74, 40); + + // Test ripple when NavigationBar is using `NavigationDestinationLabelBehavior.alwaysShow` (default). + expect( + inkFeatures, + paints + ..clipPath( + pathMatcher: isPathThat( + includes: [ + // Left center. + Offset(indicatorCenter.dx - (includedIndicatorSize.width / 2), indicatorCenter.dy), + // Top center. + Offset(indicatorCenter.dx, indicatorCenter.dy - (includedIndicatorSize.height / 2)), + // Right center. + Offset(indicatorCenter.dx + (includedIndicatorSize.width / 2), indicatorCenter.dy), + // Bottom center. + Offset(indicatorCenter.dx, indicatorCenter.dy + (includedIndicatorSize.height / 2)), + ], + excludes: [ + // Left center. + Offset(indicatorCenter.dx - (excludedIndicatorSize.width / 2), indicatorCenter.dy), + // Top center. + Offset(indicatorCenter.dx, indicatorCenter.dy - (excludedIndicatorSize.height / 2)), + // Right center. + Offset(indicatorCenter.dx + (excludedIndicatorSize.width / 2), indicatorCenter.dy), + // Bottom center. + Offset(indicatorCenter.dx, indicatorCenter.dy + (excludedIndicatorSize.height / 2)), + ], + ), + ) + ..circle( + x: indicatorCenter.dx, + y: indicatorCenter.dy, + radius: 35.0, + color: const Color(0x0a000000), + ) + ); + + // Test ripple when NavigationBar is using `NavigationDestinationLabelBehavior.alwaysHide`. + await tester.pumpWidget(buildWidget(labelBehavior: NavigationDestinationLabelBehavior.alwaysHide)); + await gesture.moveTo(tester.getCenter(find.byIcon(Icons.access_alarm))); + await tester.pumpAndSettle(); + + indicatorCenter = const Offset(600, 40); + + expect( + inkFeatures, + paints + ..clipPath( + pathMatcher: isPathThat( + includes: [ + // Left center. + Offset(indicatorCenter.dx - (includedIndicatorSize.width / 2), indicatorCenter.dy), + // Top center. + Offset(indicatorCenter.dx, indicatorCenter.dy - (includedIndicatorSize.height / 2)), + // Right center. + Offset(indicatorCenter.dx + (includedIndicatorSize.width / 2), indicatorCenter.dy), + // Bottom center. + Offset(indicatorCenter.dx, indicatorCenter.dy + (includedIndicatorSize.height / 2)), + ], + excludes: [ + // Left center. + Offset(indicatorCenter.dx - (excludedIndicatorSize.width / 2), indicatorCenter.dy), + // Top center. + Offset(indicatorCenter.dx, indicatorCenter.dy - (excludedIndicatorSize.height / 2)), + // Right center. + Offset(indicatorCenter.dx + (excludedIndicatorSize.width / 2), indicatorCenter.dy), + // Bottom center. + Offset(indicatorCenter.dx, indicatorCenter.dy + (excludedIndicatorSize.height / 2)), + ], + ), + ) + ..circle( + x: indicatorCenter.dx, + y: indicatorCenter.dy, + radius: 35.0, + color: const Color(0x0a000000), + ) + ); + + // Test ripple when NavigationBar is using `NavigationDestinationLabelBehavior.onlyShowSelected`. + await tester.pumpWidget(buildWidget(labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected)); + await gesture.moveTo(tester.getCenter(find.byIcon(Icons.access_alarm))); + await tester.pumpAndSettle(); + + expect( + inkFeatures, + paints + ..clipPath( + pathMatcher: isPathThat( + includes: [ + // Left center. + Offset(indicatorCenter.dx - (includedIndicatorSize.width / 2), indicatorCenter.dy), + // Top center. + Offset(indicatorCenter.dx, indicatorCenter.dy - (includedIndicatorSize.height / 2)), + // Right center. + Offset(indicatorCenter.dx + (includedIndicatorSize.width / 2), indicatorCenter.dy), + // Bottom center. + Offset(indicatorCenter.dx, indicatorCenter.dy + (includedIndicatorSize.height / 2)), + ], + excludes: [ + // Left center. + Offset(indicatorCenter.dx - (excludedIndicatorSize.width / 2), indicatorCenter.dy), + // Top center. + Offset(indicatorCenter.dx, indicatorCenter.dy - (excludedIndicatorSize.height / 2)), + // Right center. + Offset(indicatorCenter.dx + (excludedIndicatorSize.width / 2), indicatorCenter.dy), + // Bottom center. + Offset(indicatorCenter.dx, indicatorCenter.dy + (excludedIndicatorSize.height / 2)), + ], + ), + ) + ..circle( + x: indicatorCenter.dx, + y: indicatorCenter.dy, + radius: 35.0, + color: const Color(0x0a000000), + ) + ); + + // Make sure ripple is shifted when selectedIndex changes. + selectedIndex = 1; + await tester.pumpWidget(buildWidget(labelBehavior: NavigationDestinationLabelBehavior.onlyShowSelected)); + await tester.pumpAndSettle(); + indicatorCenter = const Offset(600, 30); + expect( inkFeatures, paints From 5a229e28278be2dae8048ebb6f46053251686932 Mon Sep 17 00:00:00 2001 From: Michael Goderbauer Date: Mon, 12 Dec 2022 10:16:06 -0800 Subject: [PATCH 35/71] Add LookupBoundary to Overlay (#116741) * Add LookupBoundary to Overlay * fix analysis --- packages/flutter/lib/src/widgets/debug.dart | 12 +- .../lib/src/widgets/lookup_boundary.dart | 23 ++++ packages/flutter/lib/src/widgets/overlay.dart | 17 ++- .../test/widgets/lookup_boundary_test.dart | 58 +++++++++ .../flutter/test/widgets/overlay_test.dart | 119 ++++++++++++++++++ 5 files changed, 221 insertions(+), 8 deletions(-) diff --git a/packages/flutter/lib/src/widgets/debug.dart b/packages/flutter/lib/src/widgets/debug.dart index 9e346c1a086be..4ca4cb7833238 100644 --- a/packages/flutter/lib/src/widgets/debug.dart +++ b/packages/flutter/lib/src/widgets/debug.dart @@ -10,6 +10,7 @@ import 'package:flutter/foundation.dart'; import 'basic.dart'; import 'framework.dart'; import 'localizations.dart'; +import 'lookup_boundary.dart'; import 'media_query.dart'; import 'overlay.dart'; import 'table.dart'; @@ -468,12 +469,17 @@ bool debugCheckHasWidgetsLocalizations(BuildContext context) { /// Does nothing if asserts are disabled. Always returns true. bool debugCheckHasOverlay(BuildContext context) { assert(() { - if (context.widget is! Overlay && context.findAncestorWidgetOfExactType() == null) { + if (LookupBoundary.findAncestorWidgetOfExactType(context) == null) { + final bool hiddenByBoundary = LookupBoundary.debugIsHidingAncestorWidgetOfExactType(context); throw FlutterError.fromParts([ - ErrorSummary('No Overlay widget found.'), + ErrorSummary('No Overlay widget found${hiddenByBoundary ? ' within the closest LookupBoundary' : ''}.'), + if (hiddenByBoundary) + ErrorDescription( + 'There is an ancestor Overlay widget, but it is hidden by a LookupBoundary.' + ), ErrorDescription( '${context.widget.runtimeType} widgets require an Overlay ' - 'widget ancestor.\n' + 'widget ancestor within the closest LookupBoundary.\n' 'An overlay lets widgets float on top of other widget children.', ), ErrorHint( diff --git a/packages/flutter/lib/src/widgets/lookup_boundary.dart b/packages/flutter/lib/src/widgets/lookup_boundary.dart index e839b447c4136..40ebaeaf66928 100644 --- a/packages/flutter/lib/src/widgets/lookup_boundary.dart +++ b/packages/flutter/lib/src/widgets/lookup_boundary.dart @@ -273,6 +273,29 @@ class LookupBoundary extends InheritedWidget { return result!; } + /// Returns true if a [LookupBoundary] is hiding the nearest [StatefulWidget] + /// with a [State] of the specified type `T` from the provided [BuildContext]. + /// + /// This method throws when asserts are disabled. + static bool debugIsHidingAncestorStateOfType(BuildContext context) { + bool? result; + assert(() { + bool hiddenByBoundary = false; + bool ancestorFound = false; + context.visitAncestorElements((Element ancestor) { + if (ancestor is StatefulElement && ancestor.state is T) { + ancestorFound = true; + return false; + } + hiddenByBoundary = hiddenByBoundary || ancestor.widget.runtimeType == LookupBoundary; + return true; + }); + result = ancestorFound & hiddenByBoundary; + return true; + } ()); + return result!; + } + /// Returns true if a [LookupBoundary] is hiding the nearest /// [RenderObjectWidget] with a [RenderObject] of the specified type `T` /// from the provided [BuildContext]. diff --git a/packages/flutter/lib/src/widgets/overlay.dart b/packages/flutter/lib/src/widgets/overlay.dart index 15a5fe2aa4373..e32479c128280 100644 --- a/packages/flutter/lib/src/widgets/overlay.dart +++ b/packages/flutter/lib/src/widgets/overlay.dart @@ -11,6 +11,7 @@ import 'package:flutter/scheduler.dart'; import 'basic.dart'; import 'framework.dart'; +import 'lookup_boundary.dart'; import 'ticker_provider.dart'; // Examples can assume: @@ -338,7 +339,8 @@ class Overlay extends StatefulWidget { final Clip clipBehavior; /// The [OverlayState] from the closest instance of [Overlay] that encloses - /// the given context, and, in debug mode, will throw if one is not found. + /// the given context within the closest [LookupBoundary], and, in debug mode, + /// will throw if one is not found. /// /// In debug mode, if the `debugRequiredFor` argument is provided and an /// overlay isn't found, then this function will throw an exception containing @@ -372,8 +374,13 @@ class Overlay extends StatefulWidget { final OverlayState? result = maybeOf(context, rootOverlay: rootOverlay); assert(() { if (result == null) { + final bool hiddenByBoundary = LookupBoundary.debugIsHidingAncestorStateOfType(context); final List information = [ - ErrorSummary('No Overlay widget found.'), + ErrorSummary('No Overlay widget found${hiddenByBoundary ? ' within the closest LookupBoundary' : ''}.'), + if (hiddenByBoundary) + ErrorDescription( + 'There is an ancestor Overlay widget, but it is hidden by a LookupBoundary.' + ), ErrorDescription('${debugRequiredFor?.runtimeType ?? 'Some'} widgets require an Overlay widget ancestor for correct operation.'), ErrorHint('The most common way to add an Overlay to an application is to include a MaterialApp, CupertinoApp or Navigator widget in the runApp() call.'), if (debugRequiredFor != null) DiagnosticsProperty('The specific widget that failed to find an overlay was', debugRequiredFor, style: DiagnosticsTreeStyle.errorProperty), @@ -389,7 +396,7 @@ class Overlay extends StatefulWidget { } /// The [OverlayState] from the closest instance of [Overlay] that encloses - /// the given context, if any. + /// the given context within the closest [LookupBoundary], if any. /// /// Typical usage is as follows: /// @@ -413,8 +420,8 @@ class Overlay extends StatefulWidget { bool rootOverlay = false, }) { return rootOverlay - ? context.findRootAncestorStateOfType() - : context.findAncestorStateOfType(); + ? LookupBoundary.findRootAncestorStateOfType(context) + : LookupBoundary.findAncestorStateOfType(context); } @override diff --git a/packages/flutter/test/widgets/lookup_boundary_test.dart b/packages/flutter/test/widgets/lookup_boundary_test.dart index 41d18f3260f54..9b75cc98540ca 100644 --- a/packages/flutter/test/widgets/lookup_boundary_test.dart +++ b/packages/flutter/test/widgets/lookup_boundary_test.dart @@ -1021,6 +1021,64 @@ void main() { }); }); + group('LookupBoundary.debugIsHidingAncestorStateOfType', () { + testWidgets('is hiding', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(MyStatefulContainer( + child: LookupBoundary( + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorStateOfType(context); + return Container(); + }, + ), + ), + )); + expect(isHidden, isTrue); + }); + + testWidgets('is not hiding entity within boundary', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(MyStatefulContainer( + child: LookupBoundary( + child: MyStatefulContainer( + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorStateOfType(context); + return Container(); + }, + ), + ), + ), + )); + expect(isHidden, isFalse); + }); + + testWidgets('is not hiding if no boundary exists', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(MyStatefulContainer( + child: Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorStateOfType(context); + return Container(); + }, + ), + )); + expect(isHidden, isFalse); + }); + + testWidgets('is not hiding if no boundary and no entity exists', (WidgetTester tester) async { + bool? isHidden; + await tester.pumpWidget(Builder( + builder: (BuildContext context) { + isHidden = LookupBoundary.debugIsHidingAncestorStateOfType(context); + return Container(); + }, + )); + expect(isHidden, isFalse); + }); + }); + group('LookupBoundary.debugIsHidingAncestorRenderObjectOfType', () { testWidgets('is hiding', (WidgetTester tester) async { bool? isHidden; diff --git a/packages/flutter/test/widgets/overlay_test.dart b/packages/flutter/test/widgets/overlay_test.dart index 98c3ba24b191c..eef17b9c6ad7b 100644 --- a/packages/flutter/test/widgets/overlay_test.dart +++ b/packages/flutter/test/widgets/overlay_test.dart @@ -1227,6 +1227,125 @@ void main() { expect(error, isAssertionError); }); }); + + group('LookupBoundary', () { + testWidgets('hides Overlay from Overlay.maybeOf', (WidgetTester tester) async { + OverlayState? overlay; + + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Overlay( + initialEntries: [ + OverlayEntry( + builder: (BuildContext context) { + return LookupBoundary( + child: Builder( + builder: (BuildContext context) { + overlay = Overlay.maybeOf(context); + return Container(); + }, + ), + ); + }, + ), + ], + ), + ), + ); + + expect(overlay, isNull); + }); + + testWidgets('hides Overlay from Overlay.of', (WidgetTester tester) async { + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Overlay( + initialEntries: [ + OverlayEntry( + builder: (BuildContext context) { + return LookupBoundary( + child: Builder( + builder: (BuildContext context) { + Overlay.of(context); + return Container(); + }, + ), + ); + }, + ), + ], + ), + ), + ); + final Object? exception = tester.takeException(); + expect(exception, isFlutterError); + final FlutterError error = exception! as FlutterError; + + expect( + error.toStringDeep(), + 'FlutterError\n' + ' No Overlay widget found within the closest LookupBoundary.\n' + ' There is an ancestor Overlay widget, but it is hidden by a\n' + ' LookupBoundary.\n' + ' Some widgets require an Overlay widget ancestor for correct\n' + ' operation.\n' + ' The most common way to add an Overlay to an application is to\n' + ' include a MaterialApp, CupertinoApp or Navigator widget in the\n' + ' runApp() call.\n' + ' The context from which that widget was searching for an overlay\n' + ' was:\n' + ' Builder\n' + ); + }); + + testWidgets('hides Overlay from debugCheckHasOverlay', (WidgetTester tester) async { + await tester.pumpWidget( + Directionality( + textDirection: TextDirection.ltr, + child: Overlay( + initialEntries: [ + OverlayEntry( + builder: (BuildContext context) { + return LookupBoundary( + child: Builder( + builder: (BuildContext context) { + debugCheckHasOverlay(context); + return Container(); + }, + ), + ); + }, + ), + ], + ), + ), + ); + final Object? exception = tester.takeException(); + expect(exception, isFlutterError); + final FlutterError error = exception! as FlutterError; + + expect( + error.toStringDeep(), startsWith( + 'FlutterError\n' + ' No Overlay widget found within the closest LookupBoundary.\n' + ' There is an ancestor Overlay widget, but it is hidden by a\n' + ' LookupBoundary.\n' + ' Builder widgets require an Overlay widget ancestor within the\n' + ' closest LookupBoundary.\n' + ' An overlay lets widgets float on top of other widget children.\n' + ' To introduce an Overlay widget, you can either directly include\n' + ' one, or use a widget that contains an Overlay itself, such as a\n' + ' Navigator, WidgetApp, MaterialApp, or CupertinoApp.\n' + ' The specific widget that could not find a Overlay ancestor was:\n' + ' Builder\n' + ' The ancestors of this widget were:\n' + ' LookupBoundary\n' + ), + ); + }); + }); } class StatefulTestWidget extends StatefulWidget { From d19047d8a175dd913533f79dda22a85f3ab28247 Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 12 Dec 2022 10:40:13 -0800 Subject: [PATCH 36/71] [framework] make opacity widget create a repaint boundary (#116788) --- .../flutter/lib/src/rendering/proxy_box.dart | 27 +++--- .../test/widgets/opacity_repaint_test.dart | 96 +++++++++++++++++++ 2 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 packages/flutter/test/widgets/opacity_repaint_test.dart diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index f02efb034a20b..f39436bc20077 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -890,6 +890,9 @@ class RenderOpacity extends RenderProxyBox { @override bool get alwaysNeedsCompositing => child != null && _alpha > 0; + @override + bool get isRepaintBoundary => alwaysNeedsCompositing; + int _alpha; /// The fraction to scale the child's alpha value. @@ -917,7 +920,7 @@ class RenderOpacity extends RenderProxyBox { if (didNeedCompositing != alwaysNeedsCompositing) { markNeedsCompositingBitsUpdate(); } - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); if (wasVisible != (_alpha != 0) && !alwaysIncludeSemantics) { markNeedsSemanticsUpdate(); } @@ -944,23 +947,19 @@ class RenderOpacity extends RenderProxyBox { return _alpha > 0; } + @override + OffsetLayer updateCompositedLayer({required covariant OpacityLayer? oldLayer}) { + final OpacityLayer layer = oldLayer ?? OpacityLayer(); + layer.alpha = _alpha; + return layer; + } + @override void paint(PaintingContext context, Offset offset) { - if (child == null) { - return; - } - if (_alpha == 0) { - // No need to keep the layer. We'll create a new one if necessary. - layer = null; + if (child == null || _alpha == 0) { return; } - - assert(needsCompositing); - layer = context.pushOpacity(offset, _alpha, super.paint, oldLayer: layer as OpacityLayer?); - assert(() { - layer!.debugCreator = debugCreator; - return true; - }()); + super.paint(context, offset); } @override diff --git a/packages/flutter/test/widgets/opacity_repaint_test.dart b/packages/flutter/test/widgets/opacity_repaint_test.dart new file mode 100644 index 0000000000000..c285075c5a57b --- /dev/null +++ b/packages/flutter/test/widgets/opacity_repaint_test.dart @@ -0,0 +1,96 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter/rendering.dart'; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('RenderOpacity avoids repainting and does not drop layer at fully opaque', (WidgetTester tester) async { + RenderTestObject.paintCount = 0; + await tester.pumpWidget( + Container( + color: Colors.red, + child: const Opacity( + opacity: 0.0, + child: TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 0); + + await tester.pumpWidget( + Container( + color: Colors.red, + child: const Opacity( + opacity: 0.1, + child: TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 1); + + await tester.pumpWidget( + Container( + color: Colors.red, + child: const Opacity( + opacity: 1, + child: TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 1); + }); + + testWidgets('RenderOpacity allows opacity layer to be dropped at 0 opacity', (WidgetTester tester) async { + RenderTestObject.paintCount = 0; + + await tester.pumpWidget( + Container( + color: Colors.red, + child: const Opacity( + opacity: 0.5, + child: TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 1); + + await tester.pumpWidget( + Container( + color: Colors.red, + child: const Opacity( + opacity: 0.0, + child: TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 1); + expect(tester.layers, isNot(contains(isA()))); + }); +} + +class TestWidget extends SingleChildRenderObjectWidget { + const TestWidget({super.key, super.child}); + + @override + RenderObject createRenderObject(BuildContext context) { + return RenderTestObject(); + } +} + +class RenderTestObject extends RenderProxyBox { + static int paintCount = 0; + + @override + void paint(PaintingContext context, Offset offset) { + paintCount += 1; + super.paint(context, offset); + } +} From 882e105a4c1b7c6f8e873c7235f8ee5b6069bb32 Mon Sep 17 00:00:00 2001 From: Hans Muller Date: Mon, 12 Dec 2022 12:06:21 -0800 Subject: [PATCH 37/71] Revert "Add Material 3 support for `ListTile` - Part 1 (#116194)" (#116908) This reverts commit e57b7f4ea8ab8b348810d0a76f7bcf4aeabbe6d2. --- dev/tools/gen_defaults/bin/gen_defaults.dart | 2 - .../gen_defaults/lib/list_tile_template.dart | 37 -- .../flutter/lib/src/material/list_tile.dart | 308 +++++---------- .../lib/src/material/list_tile_theme.dart | 30 -- .../flutter/test/material/list_tile_test.dart | 360 +++++++----------- .../test/material/list_tile_theme_test.dart | 240 +----------- 6 files changed, 230 insertions(+), 747 deletions(-) delete mode 100644 dev/tools/gen_defaults/lib/list_tile_template.dart diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart index d2cf5b5fbcf86..f03fa509d62c9 100644 --- a/dev/tools/gen_defaults/bin/gen_defaults.dart +++ b/dev/tools/gen_defaults/bin/gen_defaults.dart @@ -35,7 +35,6 @@ import 'package:gen_defaults/filter_chip_template.dart'; import 'package:gen_defaults/icon_button_template.dart'; import 'package:gen_defaults/input_chip_template.dart'; import 'package:gen_defaults/input_decorator_template.dart'; -import 'package:gen_defaults/list_tile_template.dart'; import 'package:gen_defaults/menu_template.dart'; import 'package:gen_defaults/navigation_bar_template.dart'; import 'package:gen_defaults/navigation_drawer_template.dart'; @@ -155,7 +154,6 @@ Future main(List args) async { FilterChipTemplate('FilterChip', '$materialLib/filter_chip.dart', tokens).updateFile(); IconButtonTemplate('IconButton', '$materialLib/icon_button.dart', tokens).updateFile(); InputChipTemplate('InputChip', '$materialLib/input_chip.dart', tokens).updateFile(); - ListTileTemplate('LisTile', '$materialLib/list_tile.dart', tokens).updateFile(); InputDecoratorTemplate('InputDecorator', '$materialLib/input_decorator.dart', tokens).updateFile(); MenuTemplate('Menu', '$materialLib/menu_anchor.dart', tokens).updateFile(); NavigationBarTemplate('NavigationBar', '$materialLib/navigation_bar.dart', tokens).updateFile(); diff --git a/dev/tools/gen_defaults/lib/list_tile_template.dart b/dev/tools/gen_defaults/lib/list_tile_template.dart deleted file mode 100644 index d2dc15e241d62..0000000000000 --- a/dev/tools/gen_defaults/lib/list_tile_template.dart +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -import 'template.dart'; - -class ListTileTemplate extends TokenTemplate { - const ListTileTemplate(super.blockName, super.fileName, super.tokens); - - @override - String generate() => ''' -class _${blockName}DefaultsM3 extends ListTileThemeData { - const _${blockName}DefaultsM3(this.context) - : super(shape: ${shape("md.comp.list.list-item.container")}); - - final BuildContext context; - - @override - Color? get tileColor => ${componentColor("md.comp.list.list-item.container")}; - - @override - TextStyle? get titleTextStyle => ${textStyle("md.comp.list.list-item.label-text")}; - - @override - TextStyle? get subtitleTextStyle => ${textStyle("md.comp.list.list-item.supporting-text")}; - - @override - TextStyle? get leadingAndTrailingTextStyle => ${textStyle("md.comp.list.list-item.trailing-supporting-text")}; - - @override - Color? get selectedColor => ${componentColor('md.comp.list.list-item.selected.trailing-icon')}; - - @override - Color? get iconColor => ${componentColor('md.comp.list.list-item.unselected.trailing-icon')}; -} -'''; -} diff --git a/packages/flutter/lib/src/material/list_tile.dart b/packages/flutter/lib/src/material/list_tile.dart index 2deb81431c348..a0d79fb324c86 100644 --- a/packages/flutter/lib/src/material/list_tile.dart +++ b/packages/flutter/lib/src/material/list_tile.dart @@ -15,7 +15,6 @@ import 'ink_decoration.dart'; import 'ink_well.dart'; import 'list_tile_theme.dart'; import 'material_state.dart'; -import 'text_theme.dart'; import 'theme.dart'; import 'theme_data.dart'; @@ -279,9 +278,6 @@ class ListTile extends StatelessWidget { this.selectedColor, this.iconColor, this.textColor, - this.titleTextStyle, - this.subtitleTextStyle, - this.leadingAndTrailingTextStyle, this.contentPadding, this.enabled = true, this.onTap, @@ -368,10 +364,6 @@ class ListTile extends StatelessWidget { /// If this property is null then its value is based on [ListTileTheme.dense]. /// /// Dense list tiles default to a smaller height. - /// - /// When [ThemeData.useMaterial3] is true, ListTile doesn't support [dense] property. - /// If [dense] or [ListTileTheme.dense] and [ThemeData.useMaterial3] are true, - /// ListTile will throw an assertion error. final bool? dense; /// Defines how compact the list tile's layout will be. @@ -429,28 +421,6 @@ class ListTile extends StatelessWidget { /// [ListTileThemeData]. final Color? textColor; - /// The text style for ListTile's [title]. - /// - /// If this property is null, then [ListTileThemeData.titleTextStyle] is used. - /// If that is also null and [ThemeData.useMaterial3] is true, [TextTheme.bodyLarge] - /// will be used. Otherwise, If ListTile style is [ListTileStyle.list], - /// [TextTheme.titleMedium] will be used and if ListTile style is [ListTileStyle.drawer], - /// [TextTheme.bodyLarge] will be used. - final TextStyle? titleTextStyle; - - /// The text style for ListTile's [subtitle]. - /// - /// If this property is null, then [ListTileThemeData.subtitleTextStyle] is used. - /// If that is also null, [TextTheme.bodyMedium] will be used. - final TextStyle? subtitleTextStyle; - - /// The text style for ListTile's [leading] and [trailing]. - /// - /// If this property is null, then [ListTileThemeData.leadingAndTrailingTextStyle] is used. - /// If that is also null and [ThemeData.useMaterial3] is true, [TextTheme.labelSmall] - /// will be used, otherwise [TextTheme.bodyMedium] will be used. - final TextStyle? leadingAndTrailingTextStyle; - /// Defines the font used for the [title]. /// /// If this property is null then [ListTileThemeData.style] is used. If that @@ -618,20 +588,91 @@ class ListTile extends StatelessWidget { ]; } + Color? _iconColor(ThemeData theme, ListTileThemeData tileTheme) { + if (!enabled) { + return theme.disabledColor; + } + + if (selected) { + return selectedColor ?? tileTheme.selectedColor ?? theme.listTileTheme.selectedColor ?? theme.colorScheme.primary; + } + + final Color? color = iconColor + ?? tileTheme.iconColor + ?? theme.listTileTheme.iconColor + // If [ThemeData.useMaterial3] is set to true the disabled icon color + // will be set to Theme.colorScheme.onSurface(0.38), if false, defaults to null, + // as described in: https://m3.material.io/components/icon-buttons/specs. + ?? (theme.useMaterial3 ? theme.colorScheme.onSurface.withOpacity(0.38) : null); + if (color != null) { + return color; + } + + switch (theme.brightness) { + case Brightness.light: + // For the sake of backwards compatibility, the default for unselected + // tiles is Colors.black45 rather than colorScheme.onSurface.withAlpha(0x73). + return Colors.black45; + case Brightness.dark: + return null; // null - use current icon theme color + } + } + + Color? _textColor(ThemeData theme, ListTileThemeData tileTheme, Color? defaultColor) { + if (!enabled) { + return theme.disabledColor; + } + + if (selected) { + return selectedColor ?? tileTheme.selectedColor ?? theme.listTileTheme.selectedColor ?? theme.colorScheme.primary; + } + + return textColor ?? tileTheme.textColor ?? theme.listTileTheme.textColor ?? defaultColor; + } + bool _isDenseLayout(ThemeData theme, ListTileThemeData tileTheme) { - final bool isDense = dense ?? tileTheme.dense ?? theme.listTileTheme.dense ?? false; - if (theme.useMaterial3) { - assert(!isDense, 'ListTile.dense cannot be true while Theme.useMaterial3 is true.'); + return dense ?? tileTheme.dense ?? theme.listTileTheme.dense ?? false; + } + + TextStyle _titleTextStyle(ThemeData theme, ListTileThemeData tileTheme) { + final TextStyle textStyle; + switch(style ?? tileTheme.style ?? theme.listTileTheme.style ?? ListTileStyle.list) { + case ListTileStyle.drawer: + textStyle = theme.useMaterial3 ? theme.textTheme.bodyMedium! : theme.textTheme.bodyLarge!; + break; + case ListTileStyle.list: + textStyle = theme.useMaterial3 ? theme.textTheme.titleMedium! : theme.textTheme.titleMedium!; + break; } - return isDense; + final Color? color = _textColor(theme, tileTheme, textStyle.color); + return _isDenseLayout(theme, tileTheme) + ? textStyle.copyWith(fontSize: 13.0, color: color) + : textStyle.copyWith(color: color); + } + + TextStyle _subtitleTextStyle(ThemeData theme, ListTileThemeData tileTheme) { + final TextStyle textStyle = theme.useMaterial3 ? theme.textTheme.bodyMedium! : theme.textTheme.bodyMedium!; + final Color? color = _textColor( + theme, + tileTheme, + theme.useMaterial3 ? theme.textTheme.bodySmall!.color : theme.textTheme.bodySmall!.color, + ); + return _isDenseLayout(theme, tileTheme) + ? textStyle.copyWith(color: color, fontSize: 12.0) + : textStyle.copyWith(color: color); + } + + TextStyle _trailingAndLeadingTextStyle(ThemeData theme, ListTileThemeData tileTheme) { + final TextStyle textStyle = theme.useMaterial3 ? theme.textTheme.bodyMedium! : theme.textTheme.bodyMedium!; + final Color? color = _textColor(theme, tileTheme, textStyle.color); + return textStyle.copyWith(color: color); } - // TODO(TahaTesser): Refactor this to support list tile states. - Color _tileBackgroundColor(ThemeData theme, ListTileThemeData tileTheme, ListTileThemeData defaults) { + Color _tileBackgroundColor(ThemeData theme, ListTileThemeData tileTheme) { final Color? color = selected ? selectedTileColor ?? tileTheme.selectedTileColor ?? theme.listTileTheme.selectedTileColor : tileColor ?? tileTheme.tileColor ?? theme.listTileTheme.tileColor; - return color ?? defaults.tileColor!; + return color ?? Colors.transparent; } @override @@ -639,63 +680,23 @@ class ListTile extends StatelessWidget { assert(debugCheckHasMaterial(context)); final ThemeData theme = Theme.of(context); final ListTileThemeData tileTheme = ListTileTheme.of(context); - final ListTileStyle listTileStyle = style - ?? tileTheme.style - ?? theme.listTileTheme.style - ?? ListTileStyle.list; - final ListTileThemeData defaults = theme.useMaterial3 - ? _LisTileDefaultsM3(context) - : _LisTileDefaultsM2(context, listTileStyle); - final Set states = { - if (!enabled) MaterialState.disabled, - if (selected) MaterialState.selected, - }; + final IconThemeData iconThemeData = IconThemeData(color: _iconColor(theme, tileTheme)); - Color? resolveColor(Color? explicitColor, Color? selectedColor, Color? enabledColor, [Color? disabledColor]) { - return _IndividualOverrides( - explicitColor: explicitColor, - selectedColor: selectedColor, - enabledColor: enabledColor, - disabledColor: disabledColor, - ).resolve(states); - } - - final Color? effectiveIconColor = resolveColor(iconColor, selectedColor, iconColor) - ?? resolveColor(tileTheme.iconColor, tileTheme.selectedColor, tileTheme.iconColor) - ?? resolveColor(theme.listTileTheme.iconColor, theme.listTileTheme.selectedColor, theme.listTileTheme.iconColor) - ?? resolveColor(defaults.iconColor, defaults.selectedColor, defaults.iconColor, theme.disabledColor); - final Color? effectiveColor = resolveColor(textColor, selectedColor, textColor) - ?? resolveColor(tileTheme.textColor, tileTheme.selectedColor, tileTheme.textColor) - ?? resolveColor(theme.listTileTheme.textColor, theme.listTileTheme.selectedColor, theme.listTileTheme.textColor) - ?? resolveColor(defaults.textColor, defaults.selectedColor, defaults.textColor, theme.disabledColor); - final IconThemeData iconThemeData = IconThemeData(color: effectiveIconColor); - - TextStyle? leadingAndTrailingStyle; + TextStyle? leadingAndTrailingTextStyle; if (leading != null || trailing != null) { - leadingAndTrailingStyle = leadingAndTrailingTextStyle - ?? tileTheme.leadingAndTrailingTextStyle - ?? defaults.leadingAndTrailingTextStyle!; - final Color? leadingAndTrailingTextColor = effectiveColor; - leadingAndTrailingStyle = leadingAndTrailingStyle.copyWith(color: leadingAndTrailingTextColor); + leadingAndTrailingTextStyle = _trailingAndLeadingTextStyle(theme, tileTheme); } Widget? leadingIcon; if (leading != null) { leadingIcon = AnimatedDefaultTextStyle( - style: leadingAndTrailingStyle!, + style: leadingAndTrailingTextStyle!, duration: kThemeChangeDuration, child: leading!, ); } - TextStyle titleStyle = titleTextStyle - ?? tileTheme.titleTextStyle - ?? defaults.titleTextStyle!; - final Color? titleColor = effectiveColor; - titleStyle = titleStyle.copyWith( - color: titleColor, - fontSize: _isDenseLayout(theme, tileTheme) ? 13.0 : null, - ); + final TextStyle titleStyle = _titleTextStyle(theme, tileTheme); final Widget titleText = AnimatedDefaultTextStyle( style: titleStyle, duration: kThemeChangeDuration, @@ -705,14 +706,7 @@ class ListTile extends StatelessWidget { Widget? subtitleText; TextStyle? subtitleStyle; if (subtitle != null) { - subtitleStyle = subtitleTextStyle - ?? tileTheme.subtitleTextStyle - ?? defaults.subtitleTextStyle!; - final Color? subtitleColor = effectiveColor ?? theme.textTheme.bodySmall!.color; - subtitleStyle = subtitleStyle.copyWith( - color: subtitleColor, - fontSize: _isDenseLayout(theme, tileTheme) ? 12.0 : null, - ); + subtitleStyle = _subtitleTextStyle(theme, tileTheme); subtitleText = AnimatedDefaultTextStyle( style: subtitleStyle, duration: kThemeChangeDuration, @@ -723,7 +717,7 @@ class ListTile extends StatelessWidget { Widget? trailingIcon; if (trailing != null) { trailingIcon = AnimatedDefaultTextStyle( - style: leadingAndTrailingStyle!, + style: leadingAndTrailingTextStyle!, duration: kThemeChangeDuration, child: trailing!, ); @@ -734,13 +728,15 @@ class ListTile extends StatelessWidget { final EdgeInsets resolvedContentPadding = contentPadding?.resolve(textDirection) ?? tileTheme.contentPadding?.resolve(textDirection) ?? defaultContentPadding; - // Show basic cursor when ListTile isn't enabled or gesture callbacks are null. - final Set mouseStates = { + + final Set states = { if (!enabled || (onTap == null && onLongPress == null)) MaterialState.disabled, + if (selected) MaterialState.selected, }; - final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs(mouseCursor, mouseStates) - ?? tileTheme.mouseCursor?.resolve(mouseStates) - ?? MaterialStateMouseCursor.clickable.resolve(mouseStates); + + final MouseCursor effectiveMouseCursor = MaterialStateProperty.resolveAs(mouseCursor, states) + ?? tileTheme.mouseCursor?.resolve(states) + ?? MaterialStateMouseCursor.clickable.resolve(states); return InkWell( customBorder: shape ?? tileTheme.shape, @@ -761,7 +757,7 @@ class ListTile extends StatelessWidget { child: Ink( decoration: ShapeDecoration( shape: shape ?? tileTheme.shape ?? const Border(), - color: _tileBackgroundColor(theme, tileTheme, defaults), + color: _tileBackgroundColor(theme, tileTheme), ), child: SafeArea( top: false, @@ -778,8 +774,8 @@ class ListTile extends StatelessWidget { visualDensity: visualDensity ?? tileTheme.visualDensity ?? theme.visualDensity, isThreeLine: isThreeLine, textDirection: textDirection, - titleBaselineType: titleStyle.textBaseline ?? defaults.titleTextStyle!.textBaseline!, - subtitleBaselineType: subtitleStyle?.textBaseline ?? defaults.subtitleTextStyle!.textBaseline!, + titleBaselineType: titleStyle.textBaseline!, + subtitleBaselineType: subtitleStyle?.textBaseline, horizontalTitleGap: horizontalTitleGap ?? tileTheme.horizontalTitleGap ?? 16, minVerticalPadding: minVerticalPadding ?? tileTheme.minVerticalPadding ?? 4, minLeadingWidth: minLeadingWidth ?? tileTheme.minLeadingWidth ?? 40, @@ -825,36 +821,6 @@ class ListTile extends StatelessWidget { } } -class _IndividualOverrides extends MaterialStateProperty { - _IndividualOverrides({ - this.explicitColor, - this.enabledColor, - this.selectedColor, - this.disabledColor, - }); - - final Color? explicitColor; - final Color? enabledColor; - final Color? selectedColor; - final Color? disabledColor; - - @override - Color? resolve(Set states) { - if (explicitColor is MaterialStateColor) { - return MaterialStateProperty.resolveAs(explicitColor, states); - } - - if (states.contains(MaterialState.disabled)) { - return disabledColor; - } - if (states.contains(MaterialState.selected)) { - return selectedColor; - } - - return enabledColor; - } -} - // Identifies the children of a _ListTileElement. enum _ListTileSlot { leading, @@ -1377,87 +1343,3 @@ class _RenderListTile extends RenderBox with SlottedContainerRenderObjectMixin<_ return false; } } - -class _LisTileDefaultsM2 extends ListTileThemeData { - _LisTileDefaultsM2(this.context, ListTileStyle style) - : _themeData = Theme.of(context), - _textTheme = Theme.of(context).textTheme, - super( - shape: const Border(), - style: style, - ); - - final BuildContext context; - final ThemeData _themeData; - final TextTheme _textTheme; - - @override - Color? get tileColor => Colors.transparent; - - @override - TextStyle? get titleTextStyle { - switch (style!) { - case ListTileStyle.drawer: - return _textTheme.bodyLarge; - case ListTileStyle.list: - return _textTheme.titleMedium; - } - } - - @override - TextStyle? get subtitleTextStyle => _textTheme.bodyMedium; - - @override - TextStyle? get leadingAndTrailingTextStyle => _textTheme.bodyMedium; - - @override - Color? get selectedColor => _themeData.colorScheme.primary; - - @override - Color? get iconColor { - switch (_themeData.brightness) { - case Brightness.light: - // For the sake of backwards compatibility, the default for unselected - // tiles is Colors.black45 rather than colorScheme.onSurface.withAlpha(0x73). - return Colors.black45; - case Brightness.dark: - return null; // null, Use current icon theme color - } - } -} - -// BEGIN GENERATED TOKEN PROPERTIES - LisTile - -// Do not edit by hand. The code between the "BEGIN GENERATED" and -// "END GENERATED" comments are generated from data in the Material -// Design token database by the script: -// dev/tools/gen_defaults/bin/gen_defaults.dart. - -// Token database version: v0_143 - -class _LisTileDefaultsM3 extends ListTileThemeData { - const _LisTileDefaultsM3(this.context) - : super(shape: const RoundedRectangleBorder()); - - final BuildContext context; - - @override - Color? get tileColor => Theme.of(context).colorScheme.surface; - - @override - TextStyle? get titleTextStyle => Theme.of(context).textTheme.bodyLarge; - - @override - TextStyle? get subtitleTextStyle => Theme.of(context).textTheme.bodyMedium; - - @override - TextStyle? get leadingAndTrailingTextStyle => Theme.of(context).textTheme.labelSmall; - - @override - Color? get selectedColor => Theme.of(context).colorScheme.primary; - - @override - Color? get iconColor => Theme.of(context).colorScheme.onSurface; -} - -// END GENERATED TOKEN PROPERTIES - LisTile diff --git a/packages/flutter/lib/src/material/list_tile_theme.dart b/packages/flutter/lib/src/material/list_tile_theme.dart index b5e421708b816..501a608dd5119 100644 --- a/packages/flutter/lib/src/material/list_tile_theme.dart +++ b/packages/flutter/lib/src/material/list_tile_theme.dart @@ -51,9 +51,6 @@ class ListTileThemeData with Diagnosticable { this.selectedColor, this.iconColor, this.textColor, - this.titleTextStyle, - this.subtitleTextStyle, - this.leadingAndTrailingTextStyle, this.contentPadding, this.tileColor, this.selectedTileColor, @@ -83,15 +80,6 @@ class ListTileThemeData with Diagnosticable { /// Overrides the default value of [ListTile.textColor]. final Color? textColor; - /// Overrides the default value of [ListTile.titleTextStyle]. - final TextStyle? titleTextStyle; - - /// Overrides the default value of [ListTile.subtitleTextStyle]. - final TextStyle? subtitleTextStyle; - - /// Overrides the default value of [ListTile.leadingAndTrailingTextStyle]. - final TextStyle? leadingAndTrailingTextStyle; - /// Overrides the default value of [ListTile.contentPadding]. final EdgeInsetsGeometry? contentPadding; @@ -128,9 +116,6 @@ class ListTileThemeData with Diagnosticable { Color? selectedColor, Color? iconColor, Color? textColor, - TextStyle? titleTextStyle, - TextStyle? subtitleTextStyle, - TextStyle? leadingAndTrailingTextStyle, EdgeInsetsGeometry? contentPadding, Color? tileColor, Color? selectedTileColor, @@ -149,9 +134,6 @@ class ListTileThemeData with Diagnosticable { selectedColor: selectedColor ?? this.selectedColor, iconColor: iconColor ?? this.iconColor, textColor: textColor ?? this.textColor, - titleTextStyle: titleTextStyle ?? this.titleTextStyle, - subtitleTextStyle: titleTextStyle ?? this.subtitleTextStyle, - leadingAndTrailingTextStyle: titleTextStyle ?? this.leadingAndTrailingTextStyle, contentPadding: contentPadding ?? this.contentPadding, tileColor: tileColor ?? this.tileColor, selectedTileColor: selectedTileColor ?? this.selectedTileColor, @@ -177,9 +159,6 @@ class ListTileThemeData with Diagnosticable { selectedColor: Color.lerp(a?.selectedColor, b?.selectedColor, t), iconColor: Color.lerp(a?.iconColor, b?.iconColor, t), textColor: Color.lerp(a?.textColor, b?.textColor, t), - titleTextStyle: TextStyle.lerp(a?.titleTextStyle, b?.titleTextStyle, t), - subtitleTextStyle: TextStyle.lerp(a?.subtitleTextStyle, b?.subtitleTextStyle, t), - leadingAndTrailingTextStyle: TextStyle.lerp(a?.leadingAndTrailingTextStyle, b?.leadingAndTrailingTextStyle, t), contentPadding: EdgeInsetsGeometry.lerp(a?.contentPadding, b?.contentPadding, t), tileColor: Color.lerp(a?.tileColor, b?.tileColor, t), selectedTileColor: Color.lerp(a?.selectedTileColor, b?.selectedTileColor, t), @@ -200,9 +179,6 @@ class ListTileThemeData with Diagnosticable { selectedColor, iconColor, textColor, - titleTextStyle, - subtitleTextStyle, - leadingAndTrailingTextStyle, contentPadding, tileColor, selectedTileColor, @@ -228,9 +204,6 @@ class ListTileThemeData with Diagnosticable { && other.style == style && other.selectedColor == selectedColor && other.iconColor == iconColor - && other.titleTextStyle == titleTextStyle - && other.subtitleTextStyle == subtitleTextStyle - && other.leadingAndTrailingTextStyle == leadingAndTrailingTextStyle && other.textColor == textColor && other.contentPadding == contentPadding && other.tileColor == tileColor @@ -252,9 +225,6 @@ class ListTileThemeData with Diagnosticable { properties.add(ColorProperty('selectedColor', selectedColor, defaultValue: null)); properties.add(ColorProperty('iconColor', iconColor, defaultValue: null)); properties.add(ColorProperty('textColor', textColor, defaultValue: null)); - properties.add(DiagnosticsProperty('titleTextStyle', titleTextStyle, defaultValue: null)); - properties.add(DiagnosticsProperty('subtitleTextStyle', subtitleTextStyle, defaultValue: null)); - properties.add(DiagnosticsProperty('leadingAndTrailingTextStyle', leadingAndTrailingTextStyle, defaultValue: null)); properties.add(DiagnosticsProperty('contentPadding', contentPadding, defaultValue: null)); properties.add(ColorProperty('tileColor', tileColor, defaultValue: null)); properties.add(ColorProperty('selectedTileColor', selectedTileColor, defaultValue: null)); diff --git a/packages/flutter/test/material/list_tile_test.dart b/packages/flutter/test/material/list_tile_test.dart index aa2e5e91cad89..7cbba67314c50 100644 --- a/packages/flutter/test/material/list_tile_test.dart +++ b/packages/flutter/test/material/list_tile_test.dart @@ -1578,11 +1578,10 @@ void main() { testWidgets('ListTile default tile color', (WidgetTester tester) async { bool isSelected = false; - final ThemeData theme = ThemeData(useMaterial3: true); + const Color defaultColor = Colors.transparent; await tester.pumpWidget( MaterialApp( - theme: theme, home: Material( child: Center( child: StatefulBuilder( @@ -1601,13 +1600,13 @@ void main() { ), ); - expect(find.byType(Material), paints..rect(color: theme.colorScheme.surface)); + expect(find.byType(Material), paints..rect(color: defaultColor)); // Tap on tile to change isSelected. await tester.tap(find.byType(ListTile)); await tester.pumpAndSettle(); - expect(find.byType(Material), paints..rect(color: theme.colorScheme.surface)); + expect(find.byType(Material), paints..rect(color: defaultColor)); }); testWidgets('ListTile layout at zero size', (WidgetTester tester) async { @@ -2065,15 +2064,18 @@ void main() { expect(textColor(trailingKey), theme.disabledColor); }); - testWidgets('selected, enabled ListTile default icon color', (WidgetTester tester) async { - final ThemeData theme = ThemeData(useMaterial3: true); - final ColorScheme colorScheme = theme.colorScheme; + testWidgets('selected, enabled ListTile default icon color, light and dark themes', (WidgetTester tester) async { + const ColorScheme lightColorScheme = ColorScheme.light(); + const ColorScheme darkColorScheme = ColorScheme.dark(); final Key leadingKey = UniqueKey(); final Key titleKey = UniqueKey(); final Key subtitleKey = UniqueKey(); final Key trailingKey = UniqueKey(); - Widget buildFrame({required bool selected }) { + Widget buildFrame({ required Brightness brightness, required bool selected }) { + final ThemeData theme = brightness == Brightness.light + ? ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: true) + : ThemeData.from(colorScheme: const ColorScheme.dark(), useMaterial3: true); return MaterialApp( theme: theme, home: Material( @@ -2092,32 +2094,56 @@ void main() { Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; - await tester.pumpWidget(buildFrame(selected: true)); - expect(iconColor(leadingKey), colorScheme.primary); - expect(iconColor(titleKey), colorScheme.primary); - expect(iconColor(subtitleKey), colorScheme.primary); - expect(iconColor(trailingKey), colorScheme.primary); - - await tester.pumpWidget(buildFrame(selected: false)); - expect(iconColor(leadingKey), colorScheme.onSurface); - expect(iconColor(titleKey), colorScheme.onSurface); - expect(iconColor(subtitleKey), colorScheme.onSurface); - expect(iconColor(trailingKey), colorScheme.onSurface); + await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: true)); + expect(iconColor(leadingKey), lightColorScheme.primary); + expect(iconColor(titleKey), lightColorScheme.primary); + expect(iconColor(subtitleKey), lightColorScheme.primary); + expect(iconColor(trailingKey), lightColorScheme.primary); + + await tester.pumpWidget(buildFrame(brightness: Brightness.light, selected: false)); + expect(iconColor(leadingKey), lightColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(titleKey), lightColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(subtitleKey), lightColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(trailingKey), lightColorScheme.onSurface.withOpacity(0.38)); + + await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: true)); + await tester.pumpAndSettle(); // Animated theme change + expect(iconColor(leadingKey), darkColorScheme.primary); + expect(iconColor(titleKey), darkColorScheme.primary); + expect(iconColor(subtitleKey), darkColorScheme.primary); + expect(iconColor(trailingKey), darkColorScheme.primary); + + // For this configuration, ListTile defers to the default IconTheme. + // The default dark theme's IconTheme has color:white + await tester.pumpWidget(buildFrame(brightness: Brightness.dark, selected: false)); + expect(iconColor(leadingKey), darkColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(titleKey), darkColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(subtitleKey), darkColorScheme.onSurface.withOpacity(0.38)); + expect(iconColor(trailingKey), darkColorScheme.onSurface.withOpacity(0.38)); }); testWidgets('ListTile font size', (WidgetTester tester) async { - Widget buildFrame() { + Widget buildFrame({ + bool dense = false, + bool enabled = true, + bool selected = false, + ListTileStyle? style, + }) { return MaterialApp( theme: ThemeData(useMaterial3: true), home: Material( child: Center( child: Builder( builder: (BuildContext context) { - return const ListTile( - leading: TestText('leading'), - title: TestText('title'), - subtitle: TestText('subtitle') , - trailing: TestText('trailing'), + return ListTile( + dense: dense, + enabled: enabled, + selected: selected, + style: style, + leading: const TestText('leading'), + title: const TestText('title'), + subtitle: const TestText('subtitle') , + trailing: const TestText('trailing'), ); }, ), @@ -2126,31 +2152,76 @@ void main() { ); } - // ListTile default text sizes. + // ListTile - ListTileStyle.list (default). await tester.pumpWidget(buildFrame()); - final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 11.0); - final RenderParagraph title = _getTextRenderObject(tester, 'title'); + RenderParagraph leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 14.0); + RenderParagraph title = _getTextRenderObject(tester, 'title'); expect(title.text.style!.fontSize, 16.0); - final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); + RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); expect(subtitle.text.style!.fontSize, 14.0); - final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 11.0); + RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 14.0); + + // ListTile - Densed - ListTileStyle.list (default). + await tester.pumpWidget(buildFrame(dense: true)); + await tester.pumpAndSettle(); + leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 14.0); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.fontSize, 13.0); + subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.fontSize, 12.0); + trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 14.0); + + // ListTile - ListTileStyle.drawer. + await tester.pumpWidget(buildFrame(style: ListTileStyle.drawer)); + await tester.pumpAndSettle(); + leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 14.0); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.fontSize, 14.0); + subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.fontSize, 14.0); + trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 14.0); + + // ListTile - Densed - ListTileStyle.drawer. + await tester.pumpWidget(buildFrame(dense: true, style: ListTileStyle.drawer)); + await tester.pumpAndSettle(); + leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.fontSize, 14.0); + title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.fontSize, 13.0); + subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.fontSize, 12.0); + trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.fontSize, 14.0); }); testWidgets('ListTile text color', (WidgetTester tester) async { - Widget buildFrame() { + Widget buildFrame({ + bool dense = false, + bool enabled = true, + bool selected = false, + ListTileStyle? style, + }) { return MaterialApp( theme: ThemeData(useMaterial3: true), home: Material( child: Center( child: Builder( builder: (BuildContext context) { - return const ListTile( - leading: TestText('leading'), - title: TestText('title'), - subtitle: TestText('subtitle') , - trailing: TestText('trailing'), + return ListTile( + dense: dense, + enabled: enabled, + selected: selected, + style: style, + leading: const TestText('leading'), + title: const TestText('title'), + subtitle: const TestText('subtitle') , + trailing: const TestText('trailing'), ); }, ), @@ -2161,16 +2232,28 @@ void main() { final ThemeData theme = ThemeData(useMaterial3: true); - // ListTile default text colors. + // ListTile - ListTileStyle.list (default). await tester.pumpWidget(buildFrame()); - final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.color, theme.textTheme.labelSmall!.color); - final RenderParagraph title = _getTextRenderObject(tester, 'title'); + RenderParagraph leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.color, theme.textTheme.bodyMedium!.color); + RenderParagraph title = _getTextRenderObject(tester, 'title'); + expect(title.text.style!.color, theme.textTheme.titleMedium!.color); + RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.color, theme.textTheme.bodySmall!.color); + RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.color, theme.textTheme.bodyMedium!.color); + + // ListTile - ListTileStyle.drawer. + await tester.pumpWidget(buildFrame(style: ListTileStyle.drawer)); + await tester.pumpAndSettle(); + leading = _getTextRenderObject(tester, 'leading'); + expect(leading.text.style!.color, theme.textTheme.bodyMedium!.color); + title = _getTextRenderObject(tester, 'title'); expect(title.text.style!.color, theme.textTheme.bodyLarge!.color); - final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.color, theme.textTheme.bodyMedium!.color); - final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.color, theme.textTheme.labelSmall!.color); + subtitle = _getTextRenderObject(tester, 'subtitle'); + expect(subtitle.text.style!.color, theme.textTheme.bodySmall!.color); + trailing = _getTextRenderObject(tester, 'trailing'); + expect(trailing.text.style!.color, theme.textTheme.bodyMedium!.color); }); testWidgets('Default ListTile debugFillProperties', (WidgetTester tester) async { @@ -2250,151 +2333,6 @@ void main() { ); }); - testWidgets('ListTile throws assertion when useMaterial3 and dense are true', (WidgetTester tester) async { - Widget buildFrame({required bool useMaterial3}) { - return MaterialApp( - theme: ThemeData(useMaterial3: useMaterial3), - home: Material( - child: Center( - child: StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return const ListTile( - dense: true, - title: Text('Title'), - ); - }, - ), - ), - ), - ); - } - - await tester.pumpWidget(buildFrame(useMaterial3: true)); - final AssertionError exception = tester.takeException() as AssertionError; - expect( - exception.message, - 'ListTile.dense cannot be true while Theme.useMaterial3 is true.', - ); - - await tester.pumpWidget(buildFrame(useMaterial3: false)); - expect(tester.takeException(), isNull); - }); - - testWidgets('ListTile.textColor respects MaterialStateColor', (WidgetTester tester) async { - bool enabled = false; - bool selected = false; - const Color defaultColor = Colors.blue; - const Color selectedColor = Colors.green; - const Color disabledColor = Colors.red; - - Widget buildFrame() { - return MaterialApp( - theme: ThemeData(useMaterial3: true), - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ListTile( - enabled: enabled, - selected: selected, - textColor: MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return disabledColor; - } - - if (states.contains(MaterialState.selected)) { - return selectedColor; - } - - return defaultColor; - }), - title: const TestText('title'), - subtitle: const TestText('subtitle') , - ); - }, - ), - ), - ), - ); - } - - // Test disabled state. - await tester.pumpWidget(buildFrame()); - RenderParagraph title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, disabledColor); - - // Test enabled state. - enabled = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, defaultColor); - - // Test selected state. - selected = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, selectedColor); - }); - - testWidgets('ListTile.iconColor respects MaterialStateColor', (WidgetTester tester) async { - bool enabled = false; - bool selected = false; - const Color defaultColor = Colors.blue; - const Color selectedColor = Colors.green; - const Color disabledColor = Colors.red; - final Key leadingKey = UniqueKey(); - - Widget buildFrame() { - return MaterialApp( - theme: ThemeData(useMaterial3: true), - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ListTile( - enabled: enabled, - selected: selected, - iconColor: MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return disabledColor; - } - - if (states.contains(MaterialState.selected)) { - return selectedColor; - } - - return defaultColor; - }), - leading: TestIcon(key: leadingKey), - ); - }, - ), - ), - ), - ); - } - - Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; - - // Test disabled state. - await tester.pumpWidget(buildFrame()); - expect(iconColor(leadingKey), disabledColor); - - // Test enabled state. - enabled = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - expect(iconColor(leadingKey), defaultColor); - - // Test selected state. - selected = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - expect(iconColor(leadingKey), selectedColor); - }); - group('Material 2', () { // Tests that are only relevant for Material 2. Once ThemeData.useMaterial3 // is turned on by default, these tests can be removed. @@ -2407,7 +2345,6 @@ void main() { ListTileStyle? style, }) { return MaterialApp( - theme: ThemeData(useMaterial3: false), home: Material( child: Center( child: Builder( @@ -2485,7 +2422,6 @@ void main() { ListTileStyle? style, }) { return MaterialApp( - theme: ThemeData(useMaterial3: false), home: Material( child: Center( child: Builder( @@ -2545,8 +2481,8 @@ void main() { Widget buildFrame({ required Brightness brightness, required bool selected }) { final ThemeData theme = brightness == Brightness.light - ? ThemeData.from(colorScheme: const ColorScheme.light(), useMaterial3: false) - : ThemeData.from(colorScheme: const ColorScheme.dark(), useMaterial3: false); + ? ThemeData.from(colorScheme: const ColorScheme.light()) + : ThemeData.from(colorScheme: const ColorScheme.dark()); return MaterialApp( theme: theme, home: Material( @@ -2592,40 +2528,6 @@ void main() { expect(iconColor(subtitleKey), Colors.white); expect(iconColor(trailingKey), Colors.white); }); - - testWidgets('ListTile default tile color', (WidgetTester tester) async { - bool isSelected = false; - const Color defaultColor = Colors.transparent; - - await tester.pumpWidget( - MaterialApp( - theme: ThemeData(useMaterial3: false), - home: Material( - child: Center( - child: StatefulBuilder( - builder: (BuildContext context, StateSetter setState) { - return ListTile( - selected: isSelected, - onTap: () { - setState(()=> isSelected = !isSelected); - }, - title: const Text('Title'), - ); - }, - ), - ), - ), - ), - ); - - expect(find.byType(Material), paints..rect(color: defaultColor)); - - // Tap on tile to change isSelected. - await tester.tap(find.byType(ListTile)); - await tester.pumpAndSettle(); - - expect(find.byType(Material), paints..rect(color: defaultColor)); - }); }); } diff --git a/packages/flutter/test/material/list_tile_theme_test.dart b/packages/flutter/test/material/list_tile_theme_test.dart index dd1cfc459018d..89d43e840189b 100644 --- a/packages/flutter/test/material/list_tile_theme_test.dart +++ b/packages/flutter/test/material/list_tile_theme_test.dart @@ -59,9 +59,6 @@ void main() { expect(themeData.selectedColor, null); expect(themeData.iconColor, null); expect(themeData.textColor, null); - expect(themeData.titleTextStyle, null); - expect(themeData.subtitleTextStyle, null); - expect(themeData.leadingAndTrailingTextStyle, null); expect(themeData.contentPadding, null); expect(themeData.tileColor, null); expect(themeData.selectedTileColor, null); @@ -94,12 +91,9 @@ void main() { selectedColor: Color(0x00000001), iconColor: Color(0x00000002), textColor: Color(0x00000003), - titleTextStyle: TextStyle(color: Color(0x00000004)), - subtitleTextStyle: TextStyle(color: Color(0x00000005)), - leadingAndTrailingTextStyle: TextStyle(color: Color(0x00000006)), contentPadding: EdgeInsets.all(100), - tileColor: Color(0x00000007), - selectedTileColor: Color(0x00000008), + tileColor: Color(0x00000004), + selectedTileColor: Color(0x00000005), horizontalTitleGap: 200, minVerticalPadding: 300, minLeadingWidth: 400, @@ -122,12 +116,9 @@ void main() { 'selectedColor: Color(0x00000001)', 'iconColor: Color(0x00000002)', 'textColor: Color(0x00000003)', - 'titleTextStyle: TextStyle(inherit: true, color: Color(0x00000004))', - 'subtitleTextStyle: TextStyle(inherit: true, color: Color(0x00000005))', - 'leadingAndTrailingTextStyle: TextStyle(inherit: true, color: Color(0x00000006))', 'contentPadding: EdgeInsets.all(100.0)', - 'tileColor: Color(0x00000007)', - 'selectedTileColor: Color(0x00000008)', + 'tileColor: Color(0x00000004)', + 'selectedTileColor: Color(0x00000005)', 'horizontalTitleGap: 200.0', 'minVerticalPadding: 300.0', 'minLeadingWidth: 400.0', @@ -374,99 +365,6 @@ void main() { expect(textColor(trailingKey), theme.disabledColor); }); - testWidgets( - "ListTile respects ListTileTheme's titleTextStyle, subtitleTextStyle & leadingAndTrailingTextStyle", - (WidgetTester tester) async { - final ThemeData theme = ThemeData( - useMaterial3: true, - listTileTheme: const ListTileThemeData( - titleTextStyle: TextStyle(fontSize: 20.0), - subtitleTextStyle: TextStyle(fontSize: 17.5), - leadingAndTrailingTextStyle: TextStyle(fontSize: 15.0), - ), - ); - - Widget buildFrame() { - return MaterialApp( - theme: theme, - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return const ListTile( - leading: TestText('leading'), - title: TestText('title'), - subtitle: TestText('subtitle') , - trailing: TestText('trailing'), - ); - }, - ), - ), - ), - ); - } - - await tester.pumpWidget(buildFrame()); - final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 15.0); - final RenderParagraph title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.fontSize, 20.0); - final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.fontSize, 17.5); - final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 15.0); - }); - - testWidgets( - "ListTile's titleTextStyle, subtitleTextStyle & leadingAndTrailingTextStyle are overridden by ListTile properties", - (WidgetTester tester) async { - final ThemeData theme = ThemeData( - useMaterial3: true, - listTileTheme: const ListTileThemeData( - titleTextStyle: TextStyle(fontSize: 20.0), - subtitleTextStyle: TextStyle(fontSize: 17.5), - leadingAndTrailingTextStyle: TextStyle(fontSize: 15.0), - ), - ); - - const TextStyle titleTextStyle = TextStyle(fontSize: 23.0); - const TextStyle subtitleTextStyle = TextStyle(fontSize: 20.0); - const TextStyle leadingAndTrailingTextStyle = TextStyle(fontSize: 18.0); - - Widget buildFrame() { - return MaterialApp( - theme: theme, - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return const ListTile( - titleTextStyle: titleTextStyle, - subtitleTextStyle: subtitleTextStyle, - leadingAndTrailingTextStyle: leadingAndTrailingTextStyle, - leading: TestText('leading'), - title: TestText('title'), - subtitle: TestText('subtitle') , - trailing: TestText('trailing'), - ); - }, - ), - ), - ), - ); - } - - await tester.pumpWidget(buildFrame()); - final RenderParagraph leading = _getTextRenderObject(tester, 'leading'); - expect(leading.text.style!.fontSize, 18.0); - final RenderParagraph title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.fontSize, 23.0); - final RenderParagraph subtitle = _getTextRenderObject(tester, 'subtitle'); - expect(subtitle.text.style!.fontSize, 20.0); - final RenderParagraph trailing = _getTextRenderObject(tester, 'trailing'); - expect(trailing.text.style!.fontSize, 18.0); - }); - testWidgets("ListTile respects ListTileTheme's tileColor & selectedTileColor", (WidgetTester tester) async { late ListTileThemeData theme; bool isSelected = false; @@ -581,134 +479,4 @@ void main() { // Test shape. expect(inkWellBorder, shapeBorder); }); - - testWidgets('ListTile respects MaterialStateColor LisTileTheme.textColor', (WidgetTester tester) async { - bool enabled = false; - bool selected = false; - const Color defaultColor = Colors.blue; - const Color selectedColor = Colors.green; - const Color disabledColor = Colors.red; - - final ThemeData theme = ThemeData( - listTileTheme: ListTileThemeData( - textColor: MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return disabledColor; - } - - if (states.contains(MaterialState.selected)) { - return selectedColor; - } - - return defaultColor; - }), - ), - ); - Widget buildFrame() { - return MaterialApp( - theme: theme, - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ListTile( - enabled: enabled, - selected: selected, - title: const TestText('title'), - subtitle: const TestText('subtitle') , - ); - }, - ), - ), - ), - ); - } - - // Test disabled state. - await tester.pumpWidget(buildFrame()); - RenderParagraph title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, disabledColor); - - // Test enabled state. - enabled = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, defaultColor); - - // Test selected state. - selected = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - title = _getTextRenderObject(tester, 'title'); - expect(title.text.style!.color, selectedColor); - }); - - testWidgets('ListTile respects MaterialStateColor LisTileTheme.iconColor', (WidgetTester tester) async { - bool enabled = false; - bool selected = false; - const Color defaultColor = Colors.blue; - const Color selectedColor = Colors.green; - const Color disabledColor = Colors.red; - final Key leadingKey = UniqueKey(); - - final ThemeData theme = ThemeData( - listTileTheme: ListTileThemeData( - iconColor: MaterialStateColor.resolveWith((Set states) { - if (states.contains(MaterialState.disabled)) { - return disabledColor; - } - - if (states.contains(MaterialState.selected)) { - return selectedColor; - } - - return defaultColor; - }), - ), - ); - Widget buildFrame() { - return MaterialApp( - theme: theme, - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ListTile( - enabled: enabled, - selected: selected, - leading: TestIcon(key: leadingKey), - ); - }, - ), - ), - ), - ); - } - - Color iconColor(Key key) => tester.state(find.byKey(key)).iconTheme.color!; - - // Test disabled state. - await tester.pumpWidget(buildFrame()); - expect(iconColor(leadingKey), disabledColor); - - // Test enabled state. - enabled = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - expect(iconColor(leadingKey), defaultColor); - - // Test selected state. - selected = true; - await tester.pumpWidget(buildFrame()); - await tester.pumpAndSettle(); - expect(iconColor(leadingKey), selectedColor); - }); -} - -RenderParagraph _getTextRenderObject(WidgetTester tester, String text) { - return tester.renderObject(find.descendant( - of: find.byType(ListTile), - matching: find.text(text), - )); } From 7a743c8816fd8ff7df99858c545c1dbe396d1103 Mon Sep 17 00:00:00 2001 From: Christopher Fujino Date: Mon, 12 Dec 2022 13:02:07 -0800 Subject: [PATCH 38/71] [flutter_tools] Pin and roll pub (#116745) * pin path_provider_android * make path_provider_android non-transitive * roll --- dev/automated_tests/pubspec.yaml | 12 ++++++------ dev/benchmarks/complex_layout/pubspec.yaml | 12 ++++++------ dev/benchmarks/macrobenchmarks/pubspec.yaml | 12 ++++++------ dev/benchmarks/microbenchmarks/pubspec.yaml | 12 ++++++------ .../multiple_flutters/module/pubspec.yaml | 6 +++--- .../platform_channels_benchmarks/pubspec.yaml | 12 ++++++------ .../platform_views_layout/pubspec.yaml | 12 ++++++------ .../pubspec.yaml | 12 ++++++------ dev/benchmarks/test_apps/stocks/pubspec.yaml | 12 ++++++------ dev/bots/pubspec.yaml | 12 ++++++------ dev/conductor/core/pubspec.yaml | 12 ++++++------ dev/customer_testing/pubspec.yaml | 12 ++++++------ dev/devicelab/pubspec.yaml | 12 ++++++------ dev/forbidden_from_release_tests/pubspec.yaml | 4 ++-- .../pubspec.yaml | 4 ++-- .../android_semantics_testing/pubspec.yaml | 12 ++++++------ .../android_views/pubspec.yaml | 18 ++++++++++-------- dev/integration_tests/channels/pubspec.yaml | 12 ++++++------ .../deferred_components_test/pubspec.yaml | 12 ++++++------ dev/integration_tests/external_ui/pubspec.yaml | 12 ++++++------ dev/integration_tests/flavors/pubspec.yaml | 12 ++++++------ .../flutter_gallery/pubspec.yaml | 12 ++++++------ .../gradle_deprecated_settings/pubspec.yaml | 6 +++--- .../hybrid_android_views/pubspec.yaml | 14 +++++++------- .../flutterapp/pubspec.yaml | 4 ++-- .../ios_app_with_extensions/pubspec.yaml | 4 ++-- .../ios_platform_view_tests/pubspec.yaml | 12 ++++++------ .../non_nullable/pubspec.yaml | 4 ++-- .../platform_interaction/pubspec.yaml | 12 ++++++------ .../release_smoke_test/pubspec.yaml | 4 ++-- dev/integration_tests/spell_check/pubspec.yaml | 4 ++-- dev/integration_tests/ui/pubspec.yaml | 12 ++++++------ .../web_e2e_tests/pubspec.yaml | 12 ++++++------ .../windows_startup_test/pubspec.yaml | 12 ++++++------ dev/manual_tests/pubspec.yaml | 4 ++-- dev/tools/gen_defaults/pubspec.yaml | 12 ++++++------ dev/tools/gen_keycodes/pubspec.yaml | 12 ++++++------ dev/tools/pubspec.yaml | 12 ++++++------ dev/tools/vitool/pubspec.yaml | 4 ++-- dev/tracing_tests/pubspec.yaml | 4 ++-- examples/api/pubspec.yaml | 12 ++++++------ examples/hello_world/pubspec.yaml | 12 ++++++------ examples/image_list/pubspec.yaml | 4 ++-- examples/layers/pubspec.yaml | 4 ++-- examples/platform_channel/pubspec.yaml | 12 ++++++------ examples/platform_channel_swift/pubspec.yaml | 12 ++++++------ examples/splash/pubspec.yaml | 4 ++-- packages/flutter/pubspec.yaml | 4 ++-- packages/flutter/test_private/pubspec.yaml | 4 ++-- .../flutter/test_private/test/pubspec.yaml | 4 ++-- packages/flutter_driver/pubspec.yaml | 12 ++++++------ packages/flutter_goldens/pubspec.yaml | 4 ++-- packages/flutter_goldens_client/pubspec.yaml | 4 ++-- packages/flutter_localizations/pubspec.yaml | 4 ++-- packages/flutter_test/pubspec.yaml | 4 ++-- .../lib/src/commands/update_packages.dart | 2 ++ packages/flutter_tools/pubspec.yaml | 14 +++++++------- packages/flutter_web_plugins/pubspec.yaml | 4 ++-- .../fuchsia_remote_debug_protocol/pubspec.yaml | 12 ++++++------ packages/integration_test/example/pubspec.yaml | 12 ++++++------ packages/integration_test/pubspec.yaml | 4 ++-- 61 files changed, 274 insertions(+), 270 deletions(-) diff --git a/dev/automated_tests/pubspec.yaml b/dev/automated_tests/pubspec.yaml index 5ddc94cd7f58d..755c385be1310 100644 --- a/dev/automated_tests/pubspec.yaml +++ b/dev/automated_tests/pubspec.yaml @@ -15,8 +15,8 @@ dependencies: platform: 3.1.0 test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -28,7 +28,7 @@ dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -38,10 +38,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" process: 4.2.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -71,4 +71,4 @@ flutter: assets: - icon/test.png -# PUBSPEC CHECKSUM: 2dac +# PUBSPEC CHECKSUM: acb2 diff --git a/dev/benchmarks/complex_layout/pubspec.yaml b/dev/benchmarks/complex_layout/pubspec.yaml index 2b87c0adc9aae..776fba6a5d407 100644 --- a/dev/benchmarks/complex_layout/pubspec.yaml +++ b/dev/benchmarks/complex_layout/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,8 +40,8 @@ dev_dependencies: integration_test: sdk: flutter - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -49,13 +49,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -83,4 +83,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: feb8 +# PUBSPEC CHECKSUM: a5be diff --git a/dev/benchmarks/macrobenchmarks/pubspec.yaml b/dev/benchmarks/macrobenchmarks/pubspec.yaml index d8f6b9887184b..651528e86c61d 100644 --- a/dev/benchmarks/macrobenchmarks/pubspec.yaml +++ b/dev/benchmarks/macrobenchmarks/pubspec.yaml @@ -33,7 +33,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" plugin_platform_interface: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -59,18 +59,18 @@ dev_dependencies: integration_test: sdk: flutter - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -219,4 +219,4 @@ flutter: fonts: - asset: packages/flutter_gallery_assets/fonts/GalleryIcons.ttf -# PUBSPEC CHECKSUM: b6a3 +# PUBSPEC CHECKSUM: c0a9 diff --git a/dev/benchmarks/microbenchmarks/pubspec.yaml b/dev/benchmarks/microbenchmarks/pubspec.yaml index 8cac43e6f38db..76eb2d99d07b0 100644 --- a/dev/benchmarks/microbenchmarks/pubspec.yaml +++ b/dev/benchmarks/microbenchmarks/pubspec.yaml @@ -15,8 +15,8 @@ dependencies: test: 1.22.0 flutter_gallery_assets: 1.0.2 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -28,7 +28,7 @@ dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http: 0.13.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,10 +40,10 @@ dependencies: logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -136,4 +136,4 @@ flutter: - packages/flutter_gallery_assets/people/square/stella.png - packages/flutter_gallery_assets/people/square/trevor.png -# PUBSPEC CHECKSUM: 27f9 +# PUBSPEC CHECKSUM: 2900 diff --git a/dev/benchmarks/multiple_flutters/module/pubspec.yaml b/dev/benchmarks/multiple_flutters/module/pubspec.yaml index f601fd8abc805..c6fc45328b819 100644 --- a/dev/benchmarks/multiple_flutters/module/pubspec.yaml +++ b/dev/benchmarks/multiple_flutters/module/pubspec.yaml @@ -24,7 +24,7 @@ dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_android: 2.0.21 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_ios: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,7 +40,7 @@ dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - win32: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + win32: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" xdg_directories: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter: @@ -51,4 +51,4 @@ flutter: androidPackage: com.example.multiple_flutters_module iosBundleIdentifier: com.example.multipleFluttersModule -# PUBSPEC CHECKSUM: 1df8 +# PUBSPEC CHECKSUM: 4efa diff --git a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml index 7a65a39e6c25d..1898ca3e46da4 100644 --- a/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml +++ b/dev/benchmarks/platform_channels_benchmarks/pubspec.yaml @@ -16,8 +16,8 @@ dependencies: path: ../microbenchmarks cupertino_icons: 1.0.5 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -30,7 +30,7 @@ dependencies: fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" flutter_gallery_assets: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -40,10 +40,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -73,4 +73,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 0be7 +# PUBSPEC CHECKSUM: 60ed diff --git a/dev/benchmarks/platform_views_layout/pubspec.yaml b/dev/benchmarks/platform_views_layout/pubspec.yaml index ecbe18af8f5be..6b780559d4c33 100644 --- a/dev/benchmarks/platform_views_layout/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -38,8 +38,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -47,13 +47,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -81,4 +81,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: feb8 +# PUBSPEC CHECKSUM: a5be diff --git a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml index 563229913acc2..b9dc889475301 100644 --- a/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml +++ b/dev/benchmarks/platform_views_layout_hybrid_composition/pubspec.yaml @@ -25,7 +25,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -38,8 +38,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -47,13 +47,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -81,4 +81,4 @@ flutter: - packages/flutter_gallery_assets/people/square/ali.png - packages/flutter_gallery_assets/places/india_chettinad_silk_maker.png -# PUBSPEC CHECKSUM: feb8 +# PUBSPEC CHECKSUM: a5be diff --git a/dev/benchmarks/test_apps/stocks/pubspec.yaml b/dev/benchmarks/test_apps/stocks/pubspec.yaml index e62b8ff9a998b..d6b559e071031 100644 --- a/dev/benchmarks/test_apps/stocks/pubspec.yaml +++ b/dev/benchmarks/test_apps/stocks/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" string_scanner: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,8 +34,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -44,13 +44,13 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +76,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 0bd7 +# PUBSPEC CHECKSUM: 34dd diff --git a/dev/bots/pubspec.yaml b/dev/bots/pubspec.yaml index 9747eff910263..9052ba4840280 100644 --- a/dev/bots/pubspec.yaml +++ b/dev/bots/pubspec.yaml @@ -12,14 +12,14 @@ dependencies: path: ../devicelab http_parser: 4.0.2 meta: 1.8.0 - path: 1.8.2 + path: 1.8.3 platform: 3.1.0 process: 4.2.4 test: 1.22.0 _discoveryapis_commons: 1.0.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -30,7 +30,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" equatable: 2.0.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" gcloud: 0.8.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" googleapis: 3.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -43,7 +43,7 @@ dependencies: logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" metrics_center: 1.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -71,4 +71,4 @@ dependencies: dev_dependencies: test_api: 0.4.16 -# PUBSPEC CHECKSUM: de71 +# PUBSPEC CHECKSUM: 9377 diff --git a/dev/conductor/core/pubspec.yaml b/dev/conductor/core/pubspec.yaml index d06e422805299..859b0179feb70 100644 --- a/dev/conductor/core/pubspec.yaml +++ b/dev/conductor/core/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: http: 0.13.5 intl: 0.17.0 meta: 1.8.0 - path: 1.8.2 + path: 1.8.3 process: 4.2.4 protobuf: 2.1.0 yaml: 3.1.1 @@ -34,19 +34,19 @@ dev_dependencies: test: 1.22.0 test_api: 0.4.16 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -65,4 +65,4 @@ dev_dependencies: web_socket_channel: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: f4de +# PUBSPEC CHECKSUM: ede4 diff --git a/dev/customer_testing/pubspec.yaml b/dev/customer_testing/pubspec.yaml index fa0c77905e886..45d2da21c0688 100644 --- a/dev/customer_testing/pubspec.yaml +++ b/dev/customer_testing/pubspec.yaml @@ -6,7 +6,7 @@ environment: dependencies: args: 2.3.1 - path: 1.8.2 + path: 1.8.3 glob: 2.1.1 meta: 1.8.0 @@ -20,20 +20,20 @@ dependencies: dev_dependencies: test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -55,4 +55,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 742f +# PUBSPEC CHECKSUM: af35 diff --git a/dev/devicelab/pubspec.yaml b/dev/devicelab/pubspec.yaml index a6dc98aaeb3b0..fb4f9df00dd2f 100644 --- a/dev/devicelab/pubspec.yaml +++ b/dev/devicelab/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: logging: 1.1.0 meta: 1.8.0 metrics_center: 1.0.6 - path: 1.8.2 + path: 1.8.3 platform: 3.1.0 process: 4.2.4 pubspec_parse: 1.2.1 @@ -35,7 +35,7 @@ dependencies: googleapis_auth: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" json_annotation: 4.7.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -47,11 +47,11 @@ dependencies: dev_dependencies: test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -69,4 +69,4 @@ dev_dependencies: watcher: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" web_socket_channel: 2.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: cde0 +# PUBSPEC CHECKSUM: d9e6 diff --git a/dev/forbidden_from_release_tests/pubspec.yaml b/dev/forbidden_from_release_tests/pubspec.yaml index cdee1c9ba6463..f0b0b57019dcf 100644 --- a/dev/forbidden_from_release_tests/pubspec.yaml +++ b/dev/forbidden_from_release_tests/pubspec.yaml @@ -8,7 +8,7 @@ dependencies: args: 2.3.1 file: 6.1.4 package_config: 2.1.0 - path: 1.8.2 + path: 1.8.3 process: 4.2.4 vm_snapshot_analysis: 0.7.2 @@ -16,4 +16,4 @@ dependencies: meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: bd60 +# PUBSPEC CHECKSUM: 2f61 diff --git a/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml b/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml index bf241be79f0e6..7fc2a77ee67b8 100644 --- a/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml +++ b/dev/integration_tests/android_embedding_v2_smoke_test/pubspec.yaml @@ -50,7 +50,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -96,4 +96,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 88ee +# PUBSPEC CHECKSUM: ceef diff --git a/dev/integration_tests/android_semantics_testing/pubspec.yaml b/dev/integration_tests/android_semantics_testing/pubspec.yaml index bacd717bab8b6..a865d1993d4f3 100644 --- a/dev/integration_tests/android_semantics_testing/pubspec.yaml +++ b/dev/integration_tests/android_semantics_testing/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: pub_semver: 2.1.3 test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -27,7 +27,7 @@ dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -37,10 +37,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf_packages_handler: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -68,4 +68,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/dev/integration_tests/android_views/pubspec.yaml b/dev/integration_tests/android_views/pubspec.yaml index d02be80efb9a6..d649019d9fb44 100644 --- a/dev/integration_tests/android_views/pubspec.yaml +++ b/dev/integration_tests/android_views/pubspec.yaml @@ -12,6 +12,9 @@ dependencies: flutter_driver: sdk: flutter path_provider: 2.0.11 + # This made non-transitive to allow exact pinning + # https://github.com/flutter/flutter/issues/116376 + path_provider_android: 2.0.21 collection: 1.17.0 assets_for_android_views: git: @@ -28,8 +31,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path_provider_android: 2.0.21 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_ios: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_linux: 2.1.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_macos: 2.0.6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -44,7 +46,7 @@ dependencies: vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 9.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - win32: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + win32: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" xdg_directories: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: @@ -52,8 +54,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -61,13 +63,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -92,4 +94,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 92a2 +# PUBSPEC CHECKSUM: bfa9 diff --git a/dev/integration_tests/channels/pubspec.yaml b/dev/integration_tests/channels/pubspec.yaml index a4f5fbbcd5364..758d51a018d99 100644 --- a/dev/integration_tests/channels/pubspec.yaml +++ b/dev/integration_tests/channels/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -23,7 +23,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,10 +33,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -65,4 +65,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8761 +# PUBSPEC CHECKSUM: d067 diff --git a/dev/integration_tests/deferred_components_test/pubspec.yaml b/dev/integration_tests/deferred_components_test/pubspec.yaml index 55e62304ae957..021c09f7c5c63 100644 --- a/dev/integration_tests/deferred_components_test/pubspec.yaml +++ b/dev/integration_tests/deferred_components_test/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,8 +33,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -42,13 +42,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -80,4 +80,4 @@ flutter: assets: - customassets/flutter_logo.png -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/dev/integration_tests/external_ui/pubspec.yaml b/dev/integration_tests/external_ui/pubspec.yaml index 36f3db98a8788..19f77889bf1df 100644 --- a/dev/integration_tests/external_ui/pubspec.yaml +++ b/dev/integration_tests/external_ui/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -23,7 +23,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,10 +33,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -65,4 +65,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8761 +# PUBSPEC CHECKSUM: d067 diff --git a/dev/integration_tests/flavors/pubspec.yaml b/dev/integration_tests/flavors/pubspec.yaml index 94293f889a836..ff283bfac0f0c 100644 --- a/dev/integration_tests/flavors/pubspec.yaml +++ b/dev/integration_tests/flavors/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -25,7 +25,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -35,10 +35,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -74,4 +74,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/dev/integration_tests/flutter_gallery/pubspec.yaml b/dev/integration_tests/flutter_gallery/pubspec.yaml index cfc5f5a7dfdfc..c687806993ce1 100644 --- a/dev/integration_tests/flutter_gallery/pubspec.yaml +++ b/dev/integration_tests/flutter_gallery/pubspec.yaml @@ -37,7 +37,7 @@ dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" plugin_platform_interface: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -62,8 +62,8 @@ dev_dependencies: integration_test: sdk: flutter - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -73,14 +73,14 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -276,4 +276,4 @@ flutter: - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Regular.ttf - asset: packages/flutter_gallery_assets/fonts/merriweather/Merriweather-Light.ttf -# PUBSPEC CHECKSUM: 4e24 +# PUBSPEC CHECKSUM: 1f2a diff --git a/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml b/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml index 527084dd4348f..228c1129726c7 100644 --- a/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml +++ b/dev/integration_tests/gradle_deprecated_settings/pubspec.yaml @@ -9,7 +9,7 @@ dependencies: sdk: flutter camera: 0.10.0+4 - camera_android: 0.10.0+4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + camera_android: 0.10.0+5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" camera_avfoundation: 0.9.8+6 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" camera_platform_interface: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" camera_web: 0.3.0+1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -21,7 +21,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" plugin_platform_interface: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" quiver: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -31,4 +31,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8709 +# PUBSPEC CHECKSUM: 250b diff --git a/dev/integration_tests/hybrid_android_views/pubspec.yaml b/dev/integration_tests/hybrid_android_views/pubspec.yaml index 1f7dab5296908..1c736e5690f89 100644 --- a/dev/integration_tests/hybrid_android_views/pubspec.yaml +++ b/dev/integration_tests/hybrid_android_views/pubspec.yaml @@ -28,7 +28,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_android: 2.0.21 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_ios: 2.0.11 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" path_provider_linux: 2.1.7 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -44,7 +44,7 @@ dependencies: vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 9.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" webdriver: 3.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - win32: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + win32: 3.1.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" xdg_directories: 0.2.0+2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: @@ -52,8 +52,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -61,13 +61,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -92,4 +92,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 92a2 +# PUBSPEC CHECKSUM: bfa9 diff --git a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml index 82dd676480ad6..2c6cb68e700da 100644 --- a/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml +++ b/dev/integration_tests/ios_add2app_life_cycle/flutterapp/pubspec.yaml @@ -43,7 +43,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -99,4 +99,4 @@ flutter: androidPackage: com.example.iosadd2appflutter iosBundleIdentifier: com.example.iosAdd2appFlutter -# PUBSPEC CHECKSUM: 31c5 +# PUBSPEC CHECKSUM: 37c6 diff --git a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml index b9912f64ead0d..a918677d4daac 100644 --- a/dev/integration_tests/ios_app_with_extensions/pubspec.yaml +++ b/dev/integration_tests/ios_app_with_extensions/pubspec.yaml @@ -45,7 +45,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -91,4 +91,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: ea4a +# PUBSPEC CHECKSUM: 314b diff --git a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml index 1e2af467372ad..2405a87a11301 100644 --- a/dev/integration_tests/ios_platform_view_tests/pubspec.yaml +++ b/dev/integration_tests/ios_platform_view_tests/pubspec.yaml @@ -20,7 +20,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" sync_http: 0.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,8 +33,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -42,13 +42,13 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -77,4 +77,4 @@ flutter: # the material Icons class. uses-material-design: true -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/dev/integration_tests/non_nullable/pubspec.yaml b/dev/integration_tests/non_nullable/pubspec.yaml index daea3229aca89..790a896b1574f 100644 --- a/dev/integration_tests/non_nullable/pubspec.yaml +++ b/dev/integration_tests/non_nullable/pubspec.yaml @@ -28,7 +28,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -39,4 +39,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 31c5 +# PUBSPEC CHECKSUM: 37c6 diff --git a/dev/integration_tests/platform_interaction/pubspec.yaml b/dev/integration_tests/platform_interaction/pubspec.yaml index b6ce71adad2bf..e05edad1671ee 100644 --- a/dev/integration_tests/platform_interaction/pubspec.yaml +++ b/dev/integration_tests/platform_interaction/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -23,7 +23,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,10 +33,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -65,4 +65,4 @@ dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 8761 +# PUBSPEC CHECKSUM: d067 diff --git a/dev/integration_tests/release_smoke_test/pubspec.yaml b/dev/integration_tests/release_smoke_test/pubspec.yaml index 37b2a4feb5efd..2ebabe94c1a67 100644 --- a/dev/integration_tests/release_smoke_test/pubspec.yaml +++ b/dev/integration_tests/release_smoke_test/pubspec.yaml @@ -25,7 +25,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,4 +34,4 @@ dev_dependencies: test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vm_service: 9.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 73a9 +# PUBSPEC CHECKSUM: 9baa diff --git a/dev/integration_tests/spell_check/pubspec.yaml b/dev/integration_tests/spell_check/pubspec.yaml index 816bbcdf08207..6057911d26bc6 100644 --- a/dev/integration_tests/spell_check/pubspec.yaml +++ b/dev/integration_tests/spell_check/pubspec.yaml @@ -59,7 +59,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -106,4 +106,4 @@ flutter: # For details regarding fonts from package dependencies, # see https://flutter.dev/custom-fonts/#from-packages -# PUBSPEC CHECKSUM: 9651 +# PUBSPEC CHECKSUM: be52 diff --git a/dev/integration_tests/ui/pubspec.yaml b/dev/integration_tests/ui/pubspec.yaml index 94d7a13eecabe..c948d915ed3f2 100644 --- a/dev/integration_tests/ui/pubspec.yaml +++ b/dev/integration_tests/ui/pubspec.yaml @@ -13,8 +13,8 @@ dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -25,7 +25,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -35,10 +35,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -76,4 +76,4 @@ flutter: assets: - assets/foo.png -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/dev/integration_tests/web_e2e_tests/pubspec.yaml b/dev/integration_tests/web_e2e_tests/pubspec.yaml index 16c32a05f5dcd..94552b2873fc1 100644 --- a/dev/integration_tests/web_e2e_tests/pubspec.yaml +++ b/dev/integration_tests/web_e2e_tests/pubspec.yaml @@ -35,7 +35,7 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -54,18 +54,18 @@ dev_dependencies: http: 0.13.5 test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -84,4 +84,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 9002 +# PUBSPEC CHECKSUM: f408 diff --git a/dev/integration_tests/windows_startup_test/pubspec.yaml b/dev/integration_tests/windows_startup_test/pubspec.yaml index c364506ec7f2c..24833815a7a70 100644 --- a/dev/integration_tests/windows_startup_test/pubspec.yaml +++ b/dev/integration_tests/windows_startup_test/pubspec.yaml @@ -11,8 +11,8 @@ dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -23,7 +23,7 @@ dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,10 +33,10 @@ dependencies: matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -62,4 +62,4 @@ dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 8761 +# PUBSPEC CHECKSUM: d067 diff --git a/dev/manual_tests/pubspec.yaml b/dev/manual_tests/pubspec.yaml index fd31fe3bf797f..c4036ea40c092 100644 --- a/dev/manual_tests/pubspec.yaml +++ b/dev/manual_tests/pubspec.yaml @@ -23,7 +23,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,4 +34,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 521e +# PUBSPEC CHECKSUM: 581f diff --git a/dev/tools/gen_defaults/pubspec.yaml b/dev/tools/gen_defaults/pubspec.yaml index ed298d766c6e6..2061c52fa17cd 100644 --- a/dev/tools/gen_defaults/pubspec.yaml +++ b/dev/tools/gen_defaults/pubspec.yaml @@ -8,11 +8,11 @@ environment: dependencies: dev_dependencies: - path: 1.8.2 + path: 1.8.3 test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -21,7 +21,7 @@ dev_dependencies: coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -30,7 +30,7 @@ dev_dependencies: logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -55,4 +55,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 742f +# PUBSPEC CHECKSUM: af35 diff --git a/dev/tools/gen_keycodes/pubspec.yaml b/dev/tools/gen_keycodes/pubspec.yaml index 08c98045681b5..1e99152ba3af0 100644 --- a/dev/tools/gen_keycodes/pubspec.yaml +++ b/dev/tools/gen_keycodes/pubspec.yaml @@ -8,7 +8,7 @@ dependencies: args: 2.3.1 http: 0.13.5 meta: 1.8.0 - path: 1.8.2 + path: 1.8.3 platform: 3.1.0 async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -23,21 +23,21 @@ dev_dependencies: test: 1.22.0 test_api: 0.4.16 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -57,4 +57,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 8e25 +# PUBSPEC CHECKSUM: de2b diff --git a/dev/tools/pubspec.yaml b/dev/tools/pubspec.yaml index 7fb6252daaa9a..3609348b1cae5 100644 --- a/dev/tools/pubspec.yaml +++ b/dev/tools/pubspec.yaml @@ -10,7 +10,7 @@ dependencies: http: 0.13.5 intl: 0.17.0 meta: 1.8.0 - path: 1.8.2 + path: 1.8.3 process: 4.2.4 async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -29,19 +29,19 @@ dev_dependencies: test: 1.22.0 test_api: 0.4.16 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -61,4 +61,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: ec3d +# PUBSPEC CHECKSUM: e243 diff --git a/dev/tools/vitool/pubspec.yaml b/dev/tools/vitool/pubspec.yaml index d2e8ee37fc2ba..789a4374533a8 100644 --- a/dev/tools/vitool/pubspec.yaml +++ b/dev/tools/vitool/pubspec.yaml @@ -29,7 +29,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -37,4 +37,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 4fc2 +# PUBSPEC CHECKSUM: 8dc3 diff --git a/dev/tracing_tests/pubspec.yaml b/dev/tracing_tests/pubspec.yaml index e8937ed969142..7cfd5dd8a2548 100644 --- a/dev/tracing_tests/pubspec.yaml +++ b/dev/tracing_tests/pubspec.yaml @@ -26,7 +26,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,4 +34,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 73a9 +# PUBSPEC CHECKSUM: 9baa diff --git a/examples/api/pubspec.yaml b/examples/api/pubspec.yaml index 40695ebde9547..be7a841ce9cec 100644 --- a/examples/api/pubspec.yaml +++ b/examples/api/pubspec.yaml @@ -33,8 +33,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -45,17 +45,17 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" process: 4.2.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -85,4 +85,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 1acc +# PUBSPEC CHECKSUM: f3d2 diff --git a/examples/hello_world/pubspec.yaml b/examples/hello_world/pubspec.yaml index 1fd49c7803b9e..2f08231e5a6a8 100644 --- a/examples/hello_world/pubspec.yaml +++ b/examples/hello_world/pubspec.yaml @@ -21,8 +21,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,17 +33,17 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -68,4 +68,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/examples/image_list/pubspec.yaml b/examples/image_list/pubspec.yaml index 12a7b7ddf577e..cb03e247c7a9f 100644 --- a/examples/image_list/pubspec.yaml +++ b/examples/image_list/pubspec.yaml @@ -35,7 +35,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -54,4 +54,4 @@ flutter: assets: - images/coast.jpg -# PUBSPEC CHECKSUM: 31c5 +# PUBSPEC CHECKSUM: 37c6 diff --git a/examples/layers/pubspec.yaml b/examples/layers/pubspec.yaml index 7a32fc09fa073..131aed9c570b5 100644 --- a/examples/layers/pubspec.yaml +++ b/examples/layers/pubspec.yaml @@ -23,7 +23,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +36,4 @@ flutter: - services/data.json uses-material-design: true -# PUBSPEC CHECKSUM: 521e +# PUBSPEC CHECKSUM: 581f diff --git a/examples/platform_channel/pubspec.yaml b/examples/platform_channel/pubspec.yaml index 166c45ad02973..afad521a0df5c 100644 --- a/examples/platform_channel/pubspec.yaml +++ b/examples/platform_channel/pubspec.yaml @@ -21,8 +21,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,17 +33,17 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -71,4 +71,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/examples/platform_channel_swift/pubspec.yaml b/examples/platform_channel_swift/pubspec.yaml index c6b168a170e62..c8cf645630b73 100644 --- a/examples/platform_channel_swift/pubspec.yaml +++ b/examples/platform_channel_swift/pubspec.yaml @@ -21,8 +21,8 @@ dev_dependencies: sdk: flutter test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -33,17 +33,17 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -71,4 +71,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 691e +# PUBSPEC CHECKSUM: 6124 diff --git a/examples/splash/pubspec.yaml b/examples/splash/pubspec.yaml index 8a5ccb9ecf24a..12e137aa4175a 100644 --- a/examples/splash/pubspec.yaml +++ b/examples/splash/pubspec.yaml @@ -23,7 +23,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -31,4 +31,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 521e +# PUBSPEC CHECKSUM: 581f diff --git a/packages/flutter/pubspec.yaml b/packages/flutter/pubspec.yaml index 559f90ebc0460..b4b577226746e 100644 --- a/packages/flutter/pubspec.yaml +++ b/packages/flutter/pubspec.yaml @@ -28,7 +28,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" process: 4.2.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -38,4 +38,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: b319 +# PUBSPEC CHECKSUM: f31a diff --git a/packages/flutter/test_private/pubspec.yaml b/packages/flutter/test_private/pubspec.yaml index ebfc1efaffae2..b4e71b3f0679a 100644 --- a/packages/flutter/test_private/pubspec.yaml +++ b/packages/flutter/test_private/pubspec.yaml @@ -7,7 +7,7 @@ environment: dependencies: # To update these, use "flutter update-packages --force-upgrade". meta: 1.8.0 - path: 1.8.2 + path: 1.8.3 process: 4.2.4 process_runner: 4.1.2 @@ -17,4 +17,4 @@ dependencies: file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 6d88 +# PUBSPEC CHECKSUM: d289 diff --git a/packages/flutter/test_private/test/pubspec.yaml b/packages/flutter/test_private/test/pubspec.yaml index 0a62c7c130f4a..f4ff30c750038 100644 --- a/packages/flutter/test_private/test/pubspec.yaml +++ b/packages/flutter/test_private/test/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -39,4 +39,4 @@ dev_dependencies: platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" process: 4.2.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: b319 +# PUBSPEC CHECKSUM: f31a diff --git a/packages/flutter_driver/pubspec.yaml b/packages/flutter_driver/pubspec.yaml index 7b7295a8be142..7906679f40f24 100644 --- a/packages/flutter_driver/pubspec.yaml +++ b/packages/flutter_driver/pubspec.yaml @@ -13,7 +13,7 @@ dependencies: sdk: flutter fuchsia_remote_debug_protocol: sdk: flutter - path: 1.8.2 + path: 1.8.3 meta: 1.8.0 vm_service: 9.4.0 webdriver: 3.0.1 @@ -44,18 +44,18 @@ dev_dependencies: fake_async: 1.3.1 test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -72,4 +72,4 @@ dev_dependencies: webkit_inspection_protocol: 1.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" yaml: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: b925 +# PUBSPEC CHECKSUM: 0f2b diff --git a/packages/flutter_goldens/pubspec.yaml b/packages/flutter_goldens/pubspec.yaml index 26b5489159d93..673755cd27411 100644 --- a/packages/flutter_goldens/pubspec.yaml +++ b/packages/flutter_goldens/pubspec.yaml @@ -26,7 +26,7 @@ dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -36,4 +36,4 @@ dependencies: typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 3978 +# PUBSPEC CHECKSUM: 9b79 diff --git a/packages/flutter_goldens_client/pubspec.yaml b/packages/flutter_goldens_client/pubspec.yaml index 9cfec36f06ce5..83bf65ef6a084 100644 --- a/packages/flutter_goldens_client/pubspec.yaml +++ b/packages/flutter_goldens_client/pubspec.yaml @@ -12,11 +12,11 @@ dependencies: collection: 1.17.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" typed_data: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 6b05 +# PUBSPEC CHECKSUM: c806 diff --git a/packages/flutter_localizations/pubspec.yaml b/packages/flutter_localizations/pubspec.yaml index 79da1e0d363b5..56f0ee165f21b 100644 --- a/packages/flutter_localizations/pubspec.yaml +++ b/packages/flutter_localizations/pubspec.yaml @@ -16,7 +16,7 @@ dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" material_color_utilities: 0.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" vector_math: 2.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: @@ -34,4 +34,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: d256 +# PUBSPEC CHECKSUM: d857 diff --git a/packages/flutter_test/pubspec.yaml b/packages/flutter_test/pubspec.yaml index 71b0e31f37846..d077cdec467cd 100644 --- a/packages/flutter_test/pubspec.yaml +++ b/packages/flutter_test/pubspec.yaml @@ -15,7 +15,7 @@ dependencies: test_api: 0.4.16 # Used by golden file comparator - path: 1.8.2 + path: 1.8.3 # Testing utilities for dealing with async calls and time. fake_async: 1.3.1 @@ -45,4 +45,4 @@ dependencies: dev_dependencies: file: 6.1.4 -# PUBSPEC CHECKSUM: 6112 +# PUBSPEC CHECKSUM: 6713 diff --git a/packages/flutter_tools/lib/src/commands/update_packages.dart b/packages/flutter_tools/lib/src/commands/update_packages.dart index aa83b5a8564bb..86815301c6202 100644 --- a/packages/flutter_tools/lib/src/commands/update_packages.dart +++ b/packages/flutter_tools/lib/src/commands/update_packages.dart @@ -39,6 +39,8 @@ const Map kManuallyPinnedDependencies = { 'url_launcher_android': '6.0.17', // https://github.com/flutter/flutter/issues/115660 'archive': '3.3.2', + // https://github.com/flutter/flutter/issues/116376 + 'path_provider_android': '2.0.21', }; class UpdatePackagesCommand extends FlutterCommand { diff --git a/packages/flutter_tools/pubspec.yaml b/packages/flutter_tools/pubspec.yaml index acacff48c6860..a3434d13584a2 100644 --- a/packages/flutter_tools/pubspec.yaml +++ b/packages/flutter_tools/pubspec.yaml @@ -42,8 +42,8 @@ dependencies: shelf_static: 1.1.1 pub_semver: 2.1.3 pool: 1.5.1 - path: 1.8.2 - mime: 1.0.2 + path: 1.8.3 + mime: 1.0.3 logging: 1.1.0 http_multi_server: 3.2.1 convert: 3.1.1 @@ -57,17 +57,17 @@ dependencies: vm_service: 9.4.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" built_collection: 5.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" built_value: 8.4.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" csslib: 0.17.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dds_service_extensions: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - devtools_shared: 2.18.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + devtools_shared: 2.20.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fixnum: 1.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -102,4 +102,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 65eb +# PUBSPEC CHECKSUM: 48ea diff --git a/packages/flutter_web_plugins/pubspec.yaml b/packages/flutter_web_plugins/pubspec.yaml index d6e576b9078d5..70b7eb6914a33 100644 --- a/packages/flutter_web_plugins/pubspec.yaml +++ b/packages/flutter_web_plugins/pubspec.yaml @@ -26,7 +26,7 @@ dev_dependencies: clock: 1.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" source_span: 1.9.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stack_trace: 1.11.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" stream_channel: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -34,4 +34,4 @@ dev_dependencies: term_glyph: 1.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" test_api: 0.4.16 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" -# PUBSPEC CHECKSUM: 521e +# PUBSPEC CHECKSUM: 581f diff --git a/packages/fuchsia_remote_debug_protocol/pubspec.yaml b/packages/fuchsia_remote_debug_protocol/pubspec.yaml index 1eb33b077adc3..ac89c7908a25d 100644 --- a/packages/fuchsia_remote_debug_protocol/pubspec.yaml +++ b/packages/fuchsia_remote_debug_protocol/pubspec.yaml @@ -12,14 +12,14 @@ dependencies: file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" meta: 1.8.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" platform: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" dev_dependencies: test: 1.22.0 - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" boolean_selector: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -27,7 +27,7 @@ dev_dependencies: convert: 3.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" coverage: 1.6.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -35,7 +35,7 @@ dev_dependencies: js: 0.6.5 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -63,4 +63,4 @@ dartdoc: # Exclude this package from the hosted API docs. nodoc: true -# PUBSPEC CHECKSUM: 7d36 +# PUBSPEC CHECKSUM: 163c diff --git a/packages/integration_test/example/pubspec.yaml b/packages/integration_test/example/pubspec.yaml index a4781a35c6c88..407779189231e 100644 --- a/packages/integration_test/example/pubspec.yaml +++ b/packages/integration_test/example/pubspec.yaml @@ -34,8 +34,8 @@ dev_dependencies: # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec - _fe_analyzer_shared: 50.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - analyzer: 5.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + _fe_analyzer_shared: 51.0.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + analyzer: 5.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" args: 2.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" async: 2.10.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -46,17 +46,17 @@ dev_dependencies: crypto: 3.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" fake_async: 1.3.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" file: 6.1.4 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - frontend_server_client: 3.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + frontend_server_client: 3.2.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" glob: 2.1.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_multi_server: 3.2.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" http_parser: 4.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" io: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" logging: 1.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" matcher: 0.12.13 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - mime: 1.0.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + mime: 1.0.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" node_preamble: 2.0.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" package_config: 2.1.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" - path: 1.8.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" + path: 1.8.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pool: 1.5.1 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" pub_semver: 2.1.3 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" shelf: 1.4.0 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -84,4 +84,4 @@ dev_dependencies: flutter: uses-material-design: true -# PUBSPEC CHECKSUM: 228c +# PUBSPEC CHECKSUM: 5f92 diff --git a/packages/integration_test/pubspec.yaml b/packages/integration_test/pubspec.yaml index 245862d4ea403..cd43a6e05fd97 100644 --- a/packages/integration_test/pubspec.yaml +++ b/packages/integration_test/pubspec.yaml @@ -12,7 +12,7 @@ dependencies: sdk: flutter flutter_test: sdk: flutter - path: 1.8.2 + path: 1.8.3 vm_service: 9.4.0 archive: 3.3.2 # THIS LINE IS AUTOGENERATED - TO UPDATE USE "flutter update-packages --force-upgrade" @@ -48,4 +48,4 @@ flutter: ios: pluginClass: IntegrationTestPlugin -# PUBSPEC CHECKSUM: 5175 +# PUBSPEC CHECKSUM: db76 From 558b7e0042678c786fa73de993ea1a9aea50e73a Mon Sep 17 00:00:00 2001 From: Zachary Anderson Date: Mon, 12 Dec 2022 13:21:24 -0800 Subject: [PATCH 39/71] Adjust test to tolerate additional trace fields (#116914) * Adjust test to tolerate additional trace fields * Fix syntax error --- dev/tracing_tests/test/image_painting_event_test.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dev/tracing_tests/test/image_painting_event_test.dart b/dev/tracing_tests/test/image_painting_event_test.dart index 6be6d543714b6..774a2fa45e32c 100644 --- a/dev/tracing_tests/test/image_painting_event_test.dart +++ b/dev/tracing_tests/test/image_painting_event_test.dart @@ -72,7 +72,7 @@ void main() { expect(event.extensionKind, 'Flutter.ImageSizesForFrame'); expect( jsonEncode(event.extensionData!.data), - '{"test.png":{"source":"test.png","displaySize":{"width":600.0,"height":300.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":960000,"decodedSizeInBytes":480000}}', + contains('"test.png":{"source":"test.png","displaySize":{"width":600.0,"height":300.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":960000,"decodedSizeInBytes":480000}'), ); }, skip: isBrowser); // [intended] uses dart:isolate and io. @@ -104,7 +104,7 @@ void main() { expect(event.extensionKind, 'Flutter.ImageSizesForFrame'); expect( jsonEncode(event.extensionData!.data), - '{"test.png":{"source":"test.png","displaySize":{"width":900.0,"height":900.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":4320000,"decodedSizeInBytes":480000}}', + contains('"test.png":{"source":"test.png","displaySize":{"width":900.0,"height":900.0},"imageSize":{"width":300.0,"height":300.0},"displaySizeInBytes":4320000,"decodedSizeInBytes":480000}'), ); }, skip: isBrowser); // [intended] uses dart:isolate and io. } From c420562ef356731562a04dc3fae1550278128d96 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Mon, 12 Dec 2022 13:28:54 -0800 Subject: [PATCH 40/71] Fix output match (#116912) --- .../test/commands.shard/permeable/packages_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart index 9fedc204e7a13..af21d227cc398 100644 --- a/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart +++ b/packages/flutter_tools/test/commands.shard/permeable/packages_test.dart @@ -211,7 +211,7 @@ void main() { expect(mockStdio.stdout.writes.map(utf8.decode), allOf( contains(matches(RegExp(r'Resolving dependencies in .+flutter_project\.\.\.'))), - contains(matches(RegExp(r'\+ flutter 0.0.0 from sdk flutter\n'))), + contains(matches(RegExp(r'\+ flutter 0\.0\.0 from sdk flutter'))), contains(matches(RegExp(r'Changed \d+ dependencies in .+flutter_project!'))), ), ); From 8e1f8352bff620a4bd64f47d7a3949e1e57529ce Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Mon, 12 Dec 2022 16:54:54 -0500 Subject: [PATCH 41/71] Fix MediaQuery.paddingOf (#116858) * Fix MediaQuery.paddingOf * Try to fix test overflow on web --- .../flutter/lib/src/widgets/media_query.dart | 116 +++++++++++++--- .../test/widgets/media_query_test.dart | 131 ++++++++++++++++++ 2 files changed, 230 insertions(+), 17 deletions(-) diff --git a/packages/flutter/lib/src/widgets/media_query.dart b/packages/flutter/lib/src/widgets/media_query.dart index a8fdda349ddd5..e128baf8e0b34 100644 --- a/packages/flutter/lib/src/widgets/media_query.dart +++ b/packages/flutter/lib/src/widgets/media_query.dart @@ -667,6 +667,7 @@ class MediaQueryData { && other.padding == padding && other.viewPadding == viewPadding && other.viewInsets == viewInsets + && other.systemGestureInsets == systemGestureInsets && other.alwaysUse24HourFormat == alwaysUse24HourFormat && other.highContrast == highContrast && other.disableAnimations == disableAnimations @@ -708,6 +709,7 @@ class MediaQueryData { 'padding: $padding', 'viewPadding: $viewPadding', 'viewInsets: $viewInsets', + 'systemGestureInsets: $systemGestureInsets', 'alwaysUse24HourFormat: $alwaysUse24HourFormat', 'accessibleNavigation: $accessibleNavigation', 'highContrast: $highContrast', @@ -1282,23 +1284,103 @@ class MediaQuery extends InheritedModel<_MediaQueryAspect> { @override bool updateShouldNotifyDependent(MediaQuery oldWidget, Set dependencies) { - return (data.size != oldWidget.data.size && dependencies.contains(_MediaQueryAspect.size)) - || (data.orientation != oldWidget.data.orientation && dependencies.contains(_MediaQueryAspect.orientation)) - || (data.devicePixelRatio != oldWidget.data.devicePixelRatio && dependencies.contains(_MediaQueryAspect.devicePixelRatio)) - || (data.textScaleFactor != oldWidget.data.textScaleFactor && dependencies.contains(_MediaQueryAspect.textScaleFactor)) - || (data.platformBrightness != oldWidget.data.platformBrightness && dependencies.contains(_MediaQueryAspect.platformBrightness)) - || (data.viewInsets != oldWidget.data.viewInsets && dependencies.contains(_MediaQueryAspect.viewInsets)) - || (data.systemGestureInsets != oldWidget.data.systemGestureInsets && dependencies.contains(_MediaQueryAspect.systemGestureInsets)) - || (data.viewPadding != oldWidget.data.viewPadding && dependencies.contains(_MediaQueryAspect.viewPadding)) - || (data.alwaysUse24HourFormat != oldWidget.data.alwaysUse24HourFormat && dependencies.contains(_MediaQueryAspect.alwaysUse24HourFormat)) - || (data.accessibleNavigation != oldWidget.data.accessibleNavigation && dependencies.contains(_MediaQueryAspect.accessibleNavigation)) - || (data.invertColors != oldWidget.data.invertColors && dependencies.contains(_MediaQueryAspect.invertColors)) - || (data.highContrast != oldWidget.data.highContrast && dependencies.contains(_MediaQueryAspect.highContrast)) - || (data.disableAnimations != oldWidget.data.disableAnimations && dependencies.contains(_MediaQueryAspect.disableAnimations)) - || (data.boldText != oldWidget.data.boldText && dependencies.contains(_MediaQueryAspect.boldText)) - || (data.navigationMode != oldWidget.data.navigationMode && dependencies.contains(_MediaQueryAspect.navigationMode)) - || (data.gestureSettings != oldWidget.data.gestureSettings && dependencies.contains(_MediaQueryAspect.gestureSettings)) - || (data.displayFeatures != oldWidget.data.displayFeatures && dependencies.contains(_MediaQueryAspect.displayFeatures)); + for (final Object dependency in dependencies) { + if (dependency is _MediaQueryAspect) { + switch (dependency) { + case _MediaQueryAspect.size: + if (data.size != oldWidget.data.size) { + return true; + } + break; + case _MediaQueryAspect.orientation: + if (data.orientation != oldWidget.data.orientation) { + return true; + } + break; + case _MediaQueryAspect.devicePixelRatio: + if (data.devicePixelRatio != oldWidget.data.devicePixelRatio) { + return true; + } + break; + case _MediaQueryAspect.textScaleFactor: + if (data.textScaleFactor != oldWidget.data.textScaleFactor) { + return true; + } + break; + case _MediaQueryAspect.platformBrightness: + if (data.platformBrightness != oldWidget.data.platformBrightness) { + return true; + } + break; + case _MediaQueryAspect.padding: + if (data.padding != oldWidget.data.padding) { + return true; + } + break; + case _MediaQueryAspect.viewInsets: + if (data.viewInsets != oldWidget.data.viewInsets) { + return true; + } + break; + case _MediaQueryAspect.systemGestureInsets: + if (data.systemGestureInsets != oldWidget.data.systemGestureInsets) { + return true; + } + break; + case _MediaQueryAspect.viewPadding: + if (data.viewPadding != oldWidget.data.viewPadding) { + return true; + } + break; + case _MediaQueryAspect.alwaysUse24HourFormat: + if (data.alwaysUse24HourFormat != oldWidget.data.alwaysUse24HourFormat) { + return true; + } + break; + case _MediaQueryAspect.accessibleNavigation: + if (data.accessibleNavigation != oldWidget.data.accessibleNavigation) { + return true; + } + break; + case _MediaQueryAspect.invertColors: + if (data.invertColors != oldWidget.data.invertColors) { + return true; + } + break; + case _MediaQueryAspect.highContrast: + if (data.highContrast != oldWidget.data.highContrast) { + return true; + } + break; + case _MediaQueryAspect.disableAnimations: + if (data.disableAnimations != oldWidget.data.disableAnimations) { + return true; + } + break; + case _MediaQueryAspect.boldText: + if (data.boldText != oldWidget.data.boldText) { + return true; + } + break; + case _MediaQueryAspect.navigationMode: + if (data.navigationMode != oldWidget.data.navigationMode) { + return true; + } + break; + case _MediaQueryAspect.gestureSettings: + if (data.gestureSettings != oldWidget.data.gestureSettings) { + return true; + } + break; + case _MediaQueryAspect.displayFeatures: + if (data.displayFeatures != oldWidget.data.displayFeatures) { + return true; + } + break; + } + } + } + return false; } } diff --git a/packages/flutter/test/widgets/media_query_test.dart b/packages/flutter/test/widgets/media_query_test.dart index d5d66b38fc338..9be4d65223877 100644 --- a/packages/flutter/test/widgets/media_query_test.dart +++ b/packages/flutter/test/widgets/media_query_test.dart @@ -8,6 +8,40 @@ import 'package:flutter/gestures.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; +class _MediaQueryAspectCase { + const _MediaQueryAspectCase(this.method, this.data); + final Function(BuildContext) method; + final MediaQueryData data; +} + +class _MediaQueryAspectVariant extends TestVariant<_MediaQueryAspectCase> { + _MediaQueryAspectVariant({ + required this.values + }); + + @override + final List<_MediaQueryAspectCase> values; + + static _MediaQueryAspectCase? aspect; + + @override + String describeValue(_MediaQueryAspectCase value) { + return value.method.toString(); + } + + @override + Future<_MediaQueryAspectCase?> setUp(_MediaQueryAspectCase value) async { + final _MediaQueryAspectCase? oldAspect = aspect; + aspect = value; + return oldAspect; + } + + @override + Future tearDown(_MediaQueryAspectCase value, _MediaQueryAspectCase? memento) async { + aspect = memento; + } +} + void main() { testWidgets('MediaQuery does not have a default', (WidgetTester tester) async { bool tested = false; @@ -1013,4 +1047,101 @@ void main() { expect(sizeBuildCount, 2); expect(textScaleFactorBuildCount, 2); }); + + testWidgets('MediaQuery partial dependencies', (WidgetTester tester) async { + MediaQueryData data = const MediaQueryData(); + + int buildCount = 0; + + final Widget builder = Builder( + builder: (BuildContext context) { + _MediaQueryAspectVariant.aspect!.method(context); + buildCount++; + return const SizedBox.shrink(); + } + ); + + final Widget page = StatefulBuilder( + builder: (BuildContext context, StateSetter setState) { + return MediaQuery( + data: data, + child: ListView( + children: [ + builder, + ElevatedButton( + onPressed: () { + setState(() { + data = _MediaQueryAspectVariant.aspect!.data; + }); + }, + child: const Text('Change data') + ), + ElevatedButton( + onPressed: () { + setState(() { + data = data.copyWith(); + }); + }, + child: const Text('Copy data') + ) + ] + ) + ); + }, + ); + + await tester.pumpWidget(MaterialApp(home: page)); + expect(buildCount, 1); + + await tester.tap(find.text('Copy data')); + await tester.pumpAndSettle(); + expect(buildCount, 1); + + await tester.tap(find.text('Change data')); + await tester.pumpAndSettle(); + expect(buildCount, 2); + + await tester.tap(find.text('Copy data')); + await tester.pumpAndSettle(); + expect(buildCount, 2); + }, variant: _MediaQueryAspectVariant( + values: <_MediaQueryAspectCase>[ + const _MediaQueryAspectCase(MediaQuery.sizeOf, MediaQueryData(size: Size(1, 1))), + const _MediaQueryAspectCase(MediaQuery.maybeSizeOf, MediaQueryData(size: Size(1, 1))), + const _MediaQueryAspectCase(MediaQuery.orientationOf, MediaQueryData(size: Size(2, 1))), + const _MediaQueryAspectCase(MediaQuery.maybeOrientationOf, MediaQueryData(size: Size(2, 1))), + const _MediaQueryAspectCase(MediaQuery.devicePixelRatioOf, MediaQueryData(devicePixelRatio: 1.1)), + const _MediaQueryAspectCase(MediaQuery.maybeDevicePixelRatioOf, MediaQueryData(devicePixelRatio: 1.1)), + const _MediaQueryAspectCase(MediaQuery.textScaleFactorOf, MediaQueryData(textScaleFactor: 1.1)), + const _MediaQueryAspectCase(MediaQuery.maybeTextScaleFactorOf, MediaQueryData(textScaleFactor: 1.1)), + const _MediaQueryAspectCase(MediaQuery.platformBrightnessOf, MediaQueryData(platformBrightness: Brightness.dark)), + const _MediaQueryAspectCase(MediaQuery.maybePlatformBrightnessOf, MediaQueryData(platformBrightness: Brightness.dark)), + const _MediaQueryAspectCase(MediaQuery.paddingOf, MediaQueryData(padding: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.maybePaddingOf, MediaQueryData(padding: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.viewInsetsOf, MediaQueryData(viewInsets: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.maybeViewInsetsOf, MediaQueryData(viewInsets: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.systemGestureInsetsOf, MediaQueryData(systemGestureInsets: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.maybeSystemGestureInsetsOf, MediaQueryData(systemGestureInsets: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.viewPaddingOf, MediaQueryData(viewPadding: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.maybeViewPaddingOf, MediaQueryData(viewPadding: EdgeInsets.all(1))), + const _MediaQueryAspectCase(MediaQuery.alwaysUse24HourFormatOf, MediaQueryData(alwaysUse24HourFormat: true)), + const _MediaQueryAspectCase(MediaQuery.maybeAlwaysUse24HourFormatOf, MediaQueryData(alwaysUse24HourFormat: true)), + const _MediaQueryAspectCase(MediaQuery.accessibleNavigationOf, MediaQueryData(accessibleNavigation: true)), + const _MediaQueryAspectCase(MediaQuery.maybeAccessibleNavigationOf, MediaQueryData(accessibleNavigation: true)), + const _MediaQueryAspectCase(MediaQuery.invertColorsOf, MediaQueryData(invertColors: true)), + const _MediaQueryAspectCase(MediaQuery.maybeInvertColorsOf, MediaQueryData(invertColors: true)), + const _MediaQueryAspectCase(MediaQuery.highContrastOf, MediaQueryData(highContrast: true)), + const _MediaQueryAspectCase(MediaQuery.maybeHighContrastOf, MediaQueryData(highContrast: true)), + const _MediaQueryAspectCase(MediaQuery.disableAnimationsOf, MediaQueryData(disableAnimations: true)), + const _MediaQueryAspectCase(MediaQuery.maybeDisableAnimationsOf, MediaQueryData(disableAnimations: true)), + const _MediaQueryAspectCase(MediaQuery.boldTextOf, MediaQueryData(boldText: true)), + const _MediaQueryAspectCase(MediaQuery.maybeBoldTextOf, MediaQueryData(boldText: true)), + const _MediaQueryAspectCase(MediaQuery.navigationModeOf, MediaQueryData(navigationMode: NavigationMode.directional)), + const _MediaQueryAspectCase(MediaQuery.maybeNavigationModeOf, MediaQueryData(navigationMode: NavigationMode.directional)), + const _MediaQueryAspectCase(MediaQuery.gestureSettingsOf, MediaQueryData(gestureSettings: DeviceGestureSettings(touchSlop: 1))), + const _MediaQueryAspectCase(MediaQuery.maybeGestureSettingsOf, MediaQueryData(gestureSettings: DeviceGestureSettings(touchSlop: 1))), + const _MediaQueryAspectCase(MediaQuery.displayFeaturesOf, MediaQueryData(displayFeatures: [DisplayFeature(bounds: Rect.zero, type: DisplayFeatureType.unknown, state: DisplayFeatureState.unknown)])), + const _MediaQueryAspectCase(MediaQuery.maybeDisplayFeaturesOf, MediaQueryData(displayFeatures: [DisplayFeature(bounds: Rect.zero, type: DisplayFeatureType.unknown, state: DisplayFeatureState.unknown)])), + ] + )); } From 8cfc6061ed44c67f64289625a2494b441af2886d Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 17:08:07 -0500 Subject: [PATCH 42/71] Roll Flutter Engine from 62a5de2efc9c to 2148fc003077 (5 revisions) (#116920) * d72975724 Roll Skia from 0cb546781e89 to 833bfcb2b52f (1 revision) (flutter/engine#38218) * c7a95c510 Roll Dart SDK from 21f2997a8fc6 to 6334e84d6956 (4 revisions) (flutter/engine#38187) * d945c155c Roll Skia from 833bfcb2b52f to bb9378b61c4f (5 revisions) (flutter/engine#38221) * 8fe61bc32 Roll Dart SDK from 6334e84d6956 to f32c7b011906 (3 revisions) (flutter/engine#38223) * 2148fc003 [Windows] Fix headless mode crash (flutter/engine#38173) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index f4bc8a925dd61..6a34c705f6173 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -62a5de2efc9c9f877d3c2bd7d6844df3da34f7d0 +2148fc003077f611bdf668e3631ad7f9b763333a From 601f48cd952c10a038d6d97da60741d7801187d2 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Mon, 12 Dec 2022 17:34:24 -0500 Subject: [PATCH 43/71] InteractiveViewer discrete trackpad panning (#112171) * InteractiveViewer web trackpad panning * Address feedback --- packages/flutter/lib/src/gestures/events.dart | 3 +- .../lib/src/widgets/interactive_viewer.dart | 50 ++++++++++++++++++- .../flutter/test/gestures/events_test.dart | 5 +- .../test/widgets/interactive_viewer_test.dart | 44 ++++++++++++++++ 4 files changed, 97 insertions(+), 5 deletions(-) diff --git a/packages/flutter/lib/src/gestures/events.dart b/packages/flutter/lib/src/gestures/events.dart index 373a94094472c..1db53193269ef 100644 --- a/packages/flutter/lib/src/gestures/events.dart +++ b/packages/flutter/lib/src/gestures/events.dart @@ -1788,8 +1788,7 @@ class PointerScrollEvent extends PointerSignalEvent with _PointerEventDescriptio assert(kind != null), assert(device != null), assert(position != null), - assert(scrollDelta != null), - assert(!identical(kind, PointerDeviceKind.trackpad)); + assert(scrollDelta != null); @override final Offset scrollDelta; diff --git a/packages/flutter/lib/src/widgets/interactive_viewer.dart b/packages/flutter/lib/src/widgets/interactive_viewer.dart index 005bb991609ef..f4bf64efd430e 100644 --- a/packages/flutter/lib/src/widgets/interactive_viewer.dart +++ b/packages/flutter/lib/src/widgets/interactive_viewer.dart @@ -954,11 +954,57 @@ class _InteractiveViewerState extends State with TickerProvid _controller.forward(); } - // Handle mousewheel scroll events. + // Handle mousewheel and web trackpad scroll events. void _receivedPointerSignal(PointerSignalEvent event) { final double scaleChange; if (event is PointerScrollEvent) { - // Ignore left and right scroll. + if (event.kind == PointerDeviceKind.trackpad) { + // Trackpad scroll, so treat it as a pan. + widget.onInteractionStart?.call( + ScaleStartDetails( + focalPoint: event.position, + localFocalPoint: event.localPosition, + ), + ); + + final Offset localDelta = PointerEvent.transformDeltaViaPositions( + untransformedEndPosition: event.position + event.scrollDelta, + untransformedDelta: event.scrollDelta, + transform: event.transform, + ); + + if (!_gestureIsSupported(_GestureType.pan)) { + widget.onInteractionUpdate?.call(ScaleUpdateDetails( + focalPoint: event.position - event.scrollDelta, + localFocalPoint: event.localPosition - event.scrollDelta, + focalPointDelta: -localDelta, + )); + widget.onInteractionEnd?.call(ScaleEndDetails()); + return; + } + + final Offset focalPointScene = _transformationController!.toScene( + event.localPosition, + ); + + final Offset newFocalPointScene = _transformationController!.toScene( + event.localPosition - localDelta, + ); + + _transformationController!.value = _matrixTranslate( + _transformationController!.value, + newFocalPointScene - focalPointScene + ); + + widget.onInteractionUpdate?.call(ScaleUpdateDetails( + focalPoint: event.position - event.scrollDelta, + localFocalPoint: event.localPosition - localDelta, + focalPointDelta: -localDelta + )); + widget.onInteractionEnd?.call(ScaleEndDetails()); + return; + } + // Ignore left and right mouse wheel scroll. if (event.scrollDelta.dy == 0.0) { return; } diff --git a/packages/flutter/test/gestures/events_test.dart b/packages/flutter/test/gestures/events_test.dart index 6cbd72e717f0a..21f8bbc3150da 100644 --- a/packages/flutter/test/gestures/events_test.dart +++ b/packages/flutter/test/gestures/events_test.dart @@ -850,12 +850,15 @@ void main() { expect(const PointerHoverEvent(kind: PointerDeviceKind.trackpad), isNotNull); // Regression test for https://github.com/flutter/flutter/issues/108176 expect(const PointerScrollInertiaCancelEvent(kind: PointerDeviceKind.trackpad), isNotNull); + + expect(const PointerScrollEvent(kind: PointerDeviceKind.trackpad), isNotNull); // The test passes if it compiles. }); test('Ensure certain event types are not allowed', () { expect(() => PointerDownEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError); - expect(() => PointerScrollEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError); + expect(() => PointerMoveEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError); + expect(() => PointerUpEvent(kind: PointerDeviceKind.trackpad), throwsAssertionError); }); } diff --git a/packages/flutter/test/widgets/interactive_viewer_test.dart b/packages/flutter/test/widgets/interactive_viewer_test.dart index db8a2e70fc72e..61bad52f2bc32 100644 --- a/packages/flutter/test/widgets/interactive_viewer_test.dart +++ b/packages/flutter/test/widgets/interactive_viewer_test.dart @@ -1722,6 +1722,50 @@ void main() { expect(translation2.y, lessThan(translation1.y)); }); + testWidgets('discrete scroll pointer events', (WidgetTester tester) async { + final TransformationController transformationController = TransformationController(); + const double boundaryMargin = 50.0; + await tester.pumpWidget( + MaterialApp( + home: Scaffold( + body: Center( + child: InteractiveViewer( + boundaryMargin: const EdgeInsets.all(boundaryMargin), + transformationController: transformationController, + child: const SizedBox(width: 200.0, height: 200.0), + ), + ), + ), + ), + ); + + expect(transformationController.value.getMaxScaleOnAxis(), 1.0); + Vector3 translation = transformationController.value.getTranslation(); + expect(translation.x, 0); + expect(translation.y, 0); + + // Send a mouse scroll event, it should cause a scale. + final TestPointer mouse = TestPointer(1, PointerDeviceKind.mouse); + await tester.sendEventToBinding(mouse.hover(tester.getCenter(find.byType(SizedBox)))); + await tester.sendEventToBinding(mouse.scroll(const Offset(300, -200))); + await tester.pump(); + expect(transformationController.value.getMaxScaleOnAxis(), 2.5); + translation = transformationController.value.getTranslation(); + // Will be translated to maintain centering. + expect(translation.x, -150); + expect(translation.y, -150); + + // Send a trackpad scroll event, it should cause a pan and no scale. + final TestPointer trackpad = TestPointer(1, PointerDeviceKind.trackpad); + await tester.sendEventToBinding(trackpad.hover(tester.getCenter(find.byType(SizedBox)))); + await tester.sendEventToBinding(trackpad.scroll(const Offset(100, -25))); + await tester.pump(); + expect(transformationController.value.getMaxScaleOnAxis(), 2.5); + translation = transformationController.value.getTranslation(); + expect(translation.x, -250); + expect(translation.y, -125); + }); + testWidgets('discrete scale pointer event', (WidgetTester tester) async { final TransformationController transformationController = TransformationController(); const double boundaryMargin = 50.0; From 41625b66209f61b7c23dc596da910816d689ea61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Sharma?= <737941+loic-sharma@users.noreply.github.com> Date: Mon, 12 Dec 2022 14:39:49 -0800 Subject: [PATCH 44/71] Test flutter run does not have unexpected engine logs (#116798) --- .ci.yaml | 80 ++++++++++++++++++- TESTOWNERS | 3 + dev/devicelab/README.md | 2 +- .../bin/tasks/run_debug_test_android.dart | 10 +++ .../bin/tasks/run_debug_test_macos.dart | 12 +++ .../bin/tasks/run_debug_test_windows.dart | 12 +++ dev/devicelab/lib/tasks/run_tests.dart | 34 ++++++++ 7 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 dev/devicelab/bin/tasks/run_debug_test_android.dart create mode 100644 dev/devicelab/bin/tasks/run_debug_test_macos.dart create mode 100644 dev/devicelab/bin/tasks/run_debug_test_windows.dart diff --git a/.ci.yaml b/.ci.yaml index 7d92df7395d10..7914a7da8e81c 100644 --- a/.ci.yaml +++ b/.ci.yaml @@ -3206,6 +3206,30 @@ targets: ["devicelab", "android", "mac"] task_name: microbenchmarks + - name: Mac_android run_debug_test_android + recipe: devicelab/devicelab_drone + bringup: true + presubmit: false + runIf: + - dev/** + timeout: 60 + properties: + tags: > + ["devicelab", "android", "mac"] + task_name: run_debug_test_android + + - name: Mac_arm64_android run_debug_test_android + recipe: devicelab/devicelab_drone + bringup: true + presubmit: false + runIf: + - dev/** + timeout: 60 + properties: + tags: > + ["devicelab", "android", "mac", "arm64"] + task_name: run_debug_test_android + - name: Mac_android run_release_test recipe: devicelab/devicelab_drone presubmit: false @@ -3879,8 +3903,42 @@ targets: - bin/** - .ci.yaml + - name: Mac run_debug_test_macos + recipe: devicelab/devicelab_drone + bringup: true + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "xcode", "version": "14a5294e"}, + {"dependency": "gems", "version": "v3.3.14"} + ] + tags: > + ["devicelab", "hostonly", "mac"] + task_name: run_debug_test_macos + runIf: + - dev/** + - packages/flutter_tools/** + - bin/** + - .ci.yaml + + - name: Mac_arm64_ios run_debug_test_macos + recipe: devicelab/devicelab_drone + bringup: true + timeout: 60 + properties: + tags: > + ["devicelab", "ios", "mac", "arm64"] + task_name: run_debug_test_macos + runIf: + - dev/** + - packages/flutter_tools/** + - bin/** + - .ci.yaml + - name: Mac run_release_test_macos recipe: devicelab/devicelab_drone + presubmit: false timeout: 60 properties: dependencies: >- @@ -4193,9 +4251,29 @@ targets: - bin/** - .ci.yaml - - name: Windows run_release_test_windows + - name: Windows run_debug_test_windows + recipe: devicelab/devicelab_drone bringup: true + presubmit: false + timeout: 60 + properties: + dependencies: >- + [ + {"dependency": "vs_build", "version": "version:vs2019"} + ] + tags: > + ["devicelab", "hostonly", "windows"] + task_name: run_debug_test_windows + runIf: + - dev/** + - packages/flutter_tools/** + - bin/** + - .ci.yaml + + - name: Windows run_release_test_windows recipe: devicelab/devicelab_drone + bringup: true + presubmit: false timeout: 60 properties: dependencies: >- diff --git a/TESTOWNERS b/TESTOWNERS index 0043b10ae757b..60fac112cf25b 100644 --- a/TESTOWNERS +++ b/TESTOWNERS @@ -249,6 +249,9 @@ /dev/devicelab/bin/tasks/plugin_lint_mac.dart @stuartmorgan @flutter/plugin /dev/devicelab/bin/tasks/plugin_test_ios.dart @jmagman @flutter/ios /dev/devicelab/bin/tasks/plugin_test.dart @stuartmorgan @flutter/plugin +/dev/devicelab/bin/tasks/run_debug_test_android.dart @zanderso @flutter/tool +/dev/devicelab/bin/tasks/run_debug_test_macos.dart @cbracken @flutter/tool +/dev/devicelab/bin/tasks/run_debug_test_windows.dart @loic-sharma @flutter/tool /dev/devicelab/bin/tasks/run_release_test_macos.dart @cbracken @flutter/tool /dev/devicelab/bin/tasks/run_release_test_windows.dart @loic-sharma @flutter/tool /dev/devicelab/bin/tasks/technical_debt__cost.dart @HansMuller @flutter/framework diff --git a/dev/devicelab/README.md b/dev/devicelab/README.md index d6e75d7d3cf4d..760d640d0fcd0 100644 --- a/dev/devicelab/README.md +++ b/dev/devicelab/README.md @@ -44,7 +44,7 @@ You must set the `ANDROID_SDK_ROOT` environment variable to run tests on Android. If you have a local build of the Flutter engine, then you have a copy of the Android SDK at `.../engine/src/third_party/android_tools/sdk`. -You can find where your Android SDK is using `flutter doctor`. +You can find where your Android SDK is using `flutter doctor -v`. ### Warnings diff --git a/dev/devicelab/bin/tasks/run_debug_test_android.dart b/dev/devicelab/bin/tasks/run_debug_test_android.dart new file mode 100644 index 0000000000000..93ac75612e75a --- /dev/null +++ b/dev/devicelab/bin/tasks/run_debug_test_android.dart @@ -0,0 +1,10 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/run_tests.dart'; + +void main() { + task(createAndroidRunDebugTest()); +} diff --git a/dev/devicelab/bin/tasks/run_debug_test_macos.dart b/dev/devicelab/bin/tasks/run_debug_test_macos.dart new file mode 100644 index 0000000000000..7fc3eaf1e3097 --- /dev/null +++ b/dev/devicelab/bin/tasks/run_debug_test_macos.dart @@ -0,0 +1,12 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/run_tests.dart'; + +void main() { + deviceOperatingSystem = DeviceOperatingSystem.macos; + task(createMacOSRunDebugTest()); +} diff --git a/dev/devicelab/bin/tasks/run_debug_test_windows.dart b/dev/devicelab/bin/tasks/run_debug_test_windows.dart new file mode 100644 index 0000000000000..bff954ccead40 --- /dev/null +++ b/dev/devicelab/bin/tasks/run_debug_test_windows.dart @@ -0,0 +1,12 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter_devicelab/framework/devices.dart'; +import 'package:flutter_devicelab/framework/framework.dart'; +import 'package:flutter_devicelab/tasks/run_tests.dart'; + +Future main() async { + deviceOperatingSystem = DeviceOperatingSystem.windows; + await task(createWindowsRunDebugTest()); +} diff --git a/dev/devicelab/lib/tasks/run_tests.dart b/dev/devicelab/lib/tasks/run_tests.dart index bec82e55a2ce9..cbf1c81d26efb 100644 --- a/dev/devicelab/lib/tasks/run_tests.dart +++ b/dev/devicelab/lib/tasks/run_tests.dart @@ -11,10 +11,25 @@ import '../framework/framework.dart'; import '../framework/task_result.dart'; import '../framework/utils.dart'; +TaskFunction createAndroidRunDebugTest() { + return AndroidRunOutputTest(release: false); +} + TaskFunction createAndroidRunReleaseTest() { return AndroidRunOutputTest(release: true); } +TaskFunction createMacOSRunDebugTest() { + return DesktopRunOutputTest( + // TODO(cbracken): https://github.com/flutter/flutter/issues/87508#issuecomment-1043753201 + // Switch to dev/integration_tests/ui once we have CocoaPods working on M1 Macs. + '${flutterDirectory.path}/examples/hello_world', + 'lib/main.dart', + release: false, + allowStderr: true, + ); +} + TaskFunction createMacOSRunReleaseTest() { return DesktopRunOutputTest( // TODO(cbracken): https://github.com/flutter/flutter/issues/87508#issuecomment-1043753201 @@ -26,6 +41,14 @@ TaskFunction createMacOSRunReleaseTest() { ); } +TaskFunction createWindowsRunDebugTest() { + return DesktopRunOutputTest( + '${flutterDirectory.path}/dev/integration_tests/ui', + 'lib/empty.dart', + release: false, + ); +} + TaskFunction createWindowsRunReleaseTest() { return DesktopRunOutputTest( '${flutterDirectory.path}/dev/integration_tests/ui', @@ -168,6 +191,10 @@ abstract class RunOutputTask { } ); + static final RegExp _engineLogRegex = RegExp( + r'\[(VERBOSE|INFO|WARNING|ERROR|FATAL):.+\(\d+\)\]', + ); + /// The directory where the app under test is defined. final String testDirectory; /// The main entry-point file of the application, as run on the device. @@ -232,6 +259,13 @@ abstract class RunOutputTask { throw 'flutter run ${release ? '--release' : ''} had unexpected output on standard error.'; } + final List engineLogs = List.from( + stdout.where(_engineLogRegex.hasMatch), + ); + if (engineLogs.isNotEmpty) { + throw 'flutter run had unexpected Flutter engine logs $engineLogs'; + } + return verify(stdout, stderr); }); } From 9b46f2a6910ced9d6ce05716520583ce141b5cad Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 17:51:16 -0500 Subject: [PATCH 45/71] ff2fe8381 [cpp20] Fix incompatible aggregate initialization (flutter/engine#38165) (#116927) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 6a34c705f6173..75d76340325a1 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -2148fc003077f611bdf668e3631ad7f9b763333a +ff2fe8381f4624f6c6d35f6b8d9c40385bdd87d3 From 15939b4772bfcec4beaade71bf3915d35532b3b7 Mon Sep 17 00:00:00 2001 From: Kate Lovett Date: Mon, 12 Dec 2022 17:17:51 -0600 Subject: [PATCH 46/71] Remove duped fix rules (#116933) --- packages/flutter/lib/fix_data/fix_cupertino.yaml | 11 ----------- .../lib/fix_data/fix_material/fix_material.yaml | 11 ----------- 2 files changed, 22 deletions(-) diff --git a/packages/flutter/lib/fix_data/fix_cupertino.yaml b/packages/flutter/lib/fix_data/fix_cupertino.yaml index b516113dfff95..b9e40b7f1e749 100644 --- a/packages/flutter/lib/fix_data/fix_cupertino.yaml +++ b/packages/flutter/lib/fix_data/fix_cupertino.yaml @@ -18,17 +18,6 @@ # * Fixes in this file are from the Cupertino library. * version: 1 transforms: - # Changes made in https://github.com/flutter/flutter/pull/114459 - - title: "Migrate to 'boldTextOf'" - date: 2022-10-28 - element: - uris: ['widgets.dart', 'material.dart', 'cupertino.dart'] - method: 'boldTextOverride' - inClass: 'MediaQuery' - changes: - - kind: 'rename' - newName: 'boldTextOf' - # Change made in https://github.com/flutter/flutter/pull/20649 # TODO(Piinks): Add tests when `bulkApply:false` testing is supported, https://github.com/dart-lang/sdk/issues/44639 - title: "Replace with 'CupertinoPopupSurface'" diff --git a/packages/flutter/lib/fix_data/fix_material/fix_material.yaml b/packages/flutter/lib/fix_data/fix_material/fix_material.yaml index fd927f4b4d9f1..0359c8da87f0c 100644 --- a/packages/flutter/lib/fix_data/fix_material/fix_material.yaml +++ b/packages/flutter/lib/fix_data/fix_material/fix_material.yaml @@ -25,17 +25,6 @@ # * ThemeData: fix_theme_data.yaml version: 1 transforms: - # Changes made in https://github.com/flutter/flutter/pull/114459 - - title: "Migrate to 'boldTextOf'" - date: 2022-10-28 - element: - uris: ['widgets.dart', 'material.dart', 'cupertino.dart'] - method: 'boldTextOverride' - inClass: 'MediaQuery' - changes: - - kind: 'rename' - newName: 'boldTextOf' - # Changes made in https://github.com/flutter/flutter/pull/15303 - title: "Replace 'child' with 'builder'" date: 2020-12-17 From e331dcda1785982a9efbc762e08e9c617cd3f6ae Mon Sep 17 00:00:00 2001 From: Jonah Williams Date: Mon, 12 Dec 2022 15:30:17 -0800 Subject: [PATCH 47/71] [framework] make transform with filterQuality a rpb (#116792) * [framework] make transform with filterQuality a rpb * fix tests * ++ --- .../flutter/lib/src/rendering/proxy_box.dart | 104 +++++++++--------- .../animated_image_filtered_repaint_test.dart | 29 +++++ .../flutter/test/widgets/transform_test.dart | 2 +- 3 files changed, 81 insertions(+), 54 deletions(-) diff --git a/packages/flutter/lib/src/rendering/proxy_box.dart b/packages/flutter/lib/src/rendering/proxy_box.dart index f39436bc20077..15653800d6bb1 100644 --- a/packages/flutter/lib/src/rendering/proxy_box.dart +++ b/packages/flutter/lib/src/rendering/proxy_box.dart @@ -2430,7 +2430,7 @@ class RenderTransform extends RenderProxyBox { return; } _origin = value; - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } @@ -2452,7 +2452,7 @@ class RenderTransform extends RenderProxyBox { return; } _alignment = value; - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } @@ -2467,10 +2467,13 @@ class RenderTransform extends RenderProxyBox { return; } _textDirection = value; - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } + @override + bool get isRepaintBoundary => alwaysNeedsCompositing; + @override bool get alwaysNeedsCompositing => child != null && _filterQuality != null; @@ -2495,7 +2498,7 @@ class RenderTransform extends RenderProxyBox { return; } _transform = Matrix4.copy(value); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } @@ -2513,48 +2516,48 @@ class RenderTransform extends RenderProxyBox { if (didNeedCompositing != alwaysNeedsCompositing) { markNeedsCompositingBitsUpdate(); } - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); } /// Sets the transform to the identity matrix. void setIdentity() { _transform!.setIdentity(); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } /// Concatenates a rotation about the x axis into the transform. void rotateX(double radians) { _transform!.rotateX(radians); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } /// Concatenates a rotation about the y axis into the transform. void rotateY(double radians) { _transform!.rotateY(radians); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } /// Concatenates a rotation about the z axis into the transform. void rotateZ(double radians) { _transform!.rotateZ(radians); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } /// Concatenates a translation by (x, y, z) into the transform. void translate(double x, [ double y = 0.0, double z = 0.0 ]) { _transform!.translate(x, y, z); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } /// Concatenates a scale into the transform. void scale(double x, [ double? y, double? z ]) { _transform!.scale(x, y, z); - markNeedsPaint(); + markNeedsCompositedLayerUpdate(); markNeedsSemanticsUpdate(); } @@ -2603,51 +2606,46 @@ class RenderTransform extends RenderProxyBox { ); } + @override + OffsetLayer updateCompositedLayer({required covariant ImageFilterLayer? oldLayer}) { + final ImageFilterLayer layer = oldLayer ?? ImageFilterLayer(); + layer.imageFilter = ui.ImageFilter.matrix( + _effectiveTransform!.storage, + filterQuality: filterQuality! + ); + return layer; + } + @override void paint(PaintingContext context, Offset offset) { - if (child != null) { - final Matrix4 transform = _effectiveTransform!; - if (filterQuality == null) { - final Offset? childOffset = MatrixUtils.getAsTranslation(transform); - if (childOffset == null) { - // if the matrix is singular the children would be compressed to a line or - // single point, instead short-circuit and paint nothing. - final double det = transform.determinant(); - if (det == 0 || !det.isFinite) { - layer = null; - return; - } - layer = context.pushTransform( - needsCompositing, - offset, - transform, - super.paint, - oldLayer: layer is TransformLayer ? layer as TransformLayer? : null, - ); - } else { - super.paint(context, offset + childOffset); - layer = null; - } - } else { - final Matrix4 effectiveTransform = Matrix4.translationValues(offset.dx, offset.dy, 0.0) - ..multiply(transform)..translate(-offset.dx, -offset.dy); - final ui.ImageFilter filter = ui.ImageFilter.matrix( - effectiveTransform.storage, - filterQuality: filterQuality!, - ); - if (layer is ImageFilterLayer) { - final ImageFilterLayer filterLayer = layer! as ImageFilterLayer; - filterLayer.imageFilter = filter; - } else { - layer = ImageFilterLayer(imageFilter: filter); - } - context.pushLayer(layer!, super.paint, offset); - assert(() { - layer!.debugCreator = debugCreator; - return true; - }()); - } + if (child == null) { + return; } + if (isRepaintBoundary) { + return super.paint(context, offset); + } + + final Matrix4 transform = _effectiveTransform!; + final Offset? childOffset = MatrixUtils.getAsTranslation(transform); + if (childOffset != null) { + super.paint(context, offset + childOffset); + layer = null; + return; + } + // if the matrix is singular the children would be compressed to a line or + // single point, instead short-circuit and paint nothing. + final double det = transform.determinant(); + if (det == 0 || !det.isFinite) { + layer = null; + return; + } + layer = context.pushTransform( + needsCompositing, + offset, + transform, + super.paint, + oldLayer: layer is TransformLayer ? layer as TransformLayer? : null, + ); } @override diff --git a/packages/flutter/test/widgets/animated_image_filtered_repaint_test.dart b/packages/flutter/test/widgets/animated_image_filtered_repaint_test.dart index afeecc8ad7165..d3ed5601f4999 100644 --- a/packages/flutter/test/widgets/animated_image_filtered_repaint_test.dart +++ b/packages/flutter/test/widgets/animated_image_filtered_repaint_test.dart @@ -35,6 +35,35 @@ void main() { expect(RenderTestObject.paintCount, 1); }); + + testWidgets('Transform with FilterQuality avoids repainting child as it animates', (WidgetTester tester) async { + RenderTestObject.paintCount = 0; + await tester.pumpWidget( + Container( + color: Colors.red, + child: Transform.translate( + offset: const Offset(5.0, 10.0), + filterQuality: FilterQuality.low, + child: const TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 1); + + await tester.pumpWidget( + Container( + color: Colors.red, + child: Transform.translate( + offset: const Offset(25.0, 0.0), + filterQuality: FilterQuality.low, + child: const TestWidget(), + ), + ) + ); + + expect(RenderTestObject.paintCount, 1); + }); } class TestWidget extends SingleChildRenderObjectWidget { diff --git a/packages/flutter/test/widgets/transform_test.dart b/packages/flutter/test/widgets/transform_test.dart index e677361c0b179..98758f8014c07 100644 --- a/packages/flutter/test/widgets/transform_test.dart +++ b/packages/flutter/test/widgets/transform_test.dart @@ -616,7 +616,7 @@ void main() { moreOrLessEquals(0.7071067811865476), moreOrLessEquals(0.7071067811865475), 0.0, 0.0, moreOrLessEquals(-0.7071067811865475), moreOrLessEquals(0.7071067811865476), 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, - moreOrLessEquals(329.28932188134524), moreOrLessEquals(-194.97474683058329), 0.0, 1.0, + moreOrLessEquals(50), moreOrLessEquals(-20.710678118654755), 0.0, 1.0, ]); }); From 6432fd1b15db2ac493618608c6e6d79ed581c33a Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 18:33:33 -0500 Subject: [PATCH 48/71] 6c190ea1e Roll Skia from bb9378b61c4f to 788fe69e7ade (6 revisions) (flutter/engine#38226) (#116935) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 75d76340325a1..1114d07cc9d9b 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -ff2fe8381f4624f6c6d35f6b8d9c40385bdd87d3 +6c190ea1e8df1a424c94dccf31ef470b86067407 From 97df2b3191bc3d1381632ed852f939ac0f6055ae Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Mon, 12 Dec 2022 19:20:30 -0500 Subject: [PATCH 49/71] Fix scroll jump when NestedScrollPosition is inertia-cancelled. (#116689) * Fix scroll jump when NestedScrollPosition is inertia-cancelled. * Switch to using pointerScroll(0) --- .../lib/src/widgets/nested_scroll_view.dart | 5 +- .../scroll_position_with_single_context.dart | 5 +- .../flutter/lib/src/widgets/scrollable.dart | 2 +- .../test/widgets/nested_scroll_view_test.dart | 48 +++++++++++++++++++ 4 files changed, 57 insertions(+), 3 deletions(-) diff --git a/packages/flutter/lib/src/widgets/nested_scroll_view.dart b/packages/flutter/lib/src/widgets/nested_scroll_view.dart index 83b871b436a4a..2987b2b04c423 100644 --- a/packages/flutter/lib/src/widgets/nested_scroll_view.dart +++ b/packages/flutter/lib/src/widgets/nested_scroll_view.dart @@ -903,7 +903,10 @@ class _NestedScrollCoordinator implements ScrollActivityDelegate, ScrollHoldCont // If an update is made to pointer scrolling here, consider if the same // (or similar) change should be made in // ScrollPositionWithSingleContext.pointerScroll. - assert(delta != 0.0); + if (delta == 0.0) { + goBallistic(0.0); + return; + } goIdle(); updateUserScrollDirection( diff --git a/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart b/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart index 417a79b4a16ac..86ef6966d8f3e 100644 --- a/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart +++ b/packages/flutter/lib/src/widgets/scroll_position_with_single_context.dart @@ -212,7 +212,10 @@ class ScrollPositionWithSingleContext extends ScrollPosition implements ScrollAc // If an update is made to pointer scrolling here, consider if the same // (or similar) change should be made in // _NestedScrollCoordinator.pointerScroll. - assert(delta != 0.0); + if (delta == 0.0) { + goBallistic(0.0); + return; + } final double targetPixels = math.min(math.max(pixels + delta, minScrollExtent), maxScrollExtent); diff --git a/packages/flutter/lib/src/widgets/scrollable.dart b/packages/flutter/lib/src/widgets/scrollable.dart index 0ddcbc6143a60..5f32d8ffd3183 100644 --- a/packages/flutter/lib/src/widgets/scrollable.dart +++ b/packages/flutter/lib/src/widgets/scrollable.dart @@ -780,7 +780,7 @@ class ScrollableState extends State with TickerProviderStateMixin, R GestureBinding.instance.pointerSignalResolver.register(event, _handlePointerScroll); } } else if (event is PointerScrollInertiaCancelEvent) { - position.jumpTo(position.pixels); + position.pointerScroll(0); // Don't use the pointer signal resolver, all hit-tested scrollables should stop. } } diff --git a/packages/flutter/test/widgets/nested_scroll_view_test.dart b/packages/flutter/test/widgets/nested_scroll_view_test.dart index 535f1d65cd6bb..cc42bcd9cbef8 100644 --- a/packages/flutter/test/widgets/nested_scroll_view_test.dart +++ b/packages/flutter/test/widgets/nested_scroll_view_test.dart @@ -1012,6 +1012,54 @@ void main() { ); }); + testWidgets('Inertia-cancel event does not modify either position.', (WidgetTester tester) async { + final GlobalKey globalKey = GlobalKey(); + await tester.pumpWidget(buildTest( + key: globalKey, + expanded: false, + )); + + double appBarHeight = tester.renderObject(find.byType(AppBar)).size.height; + expect(appBarHeight, 104.0); + final double scrollExtent = appBarHeight + 50.0; + expect(globalKey.currentState!.outerController.offset, 0.0); + expect(globalKey.currentState!.innerController.offset, 0.0); + + // The scroll gesture should occur in the inner body, so the whole + // scroll view is scrolled. + final TestGesture gesture = await tester.startGesture(Offset( + 0.0, + appBarHeight + 1.0, + )); + await gesture.moveBy(Offset(0.0, -scrollExtent)); + await tester.pump(); + + appBarHeight = tester.renderObject(find.byType(AppBar)).size.height; + // This is not an expanded AppBar. + expect(appBarHeight, 104.0); + // The outer scroll controller should show an offset of the applied + // scrollExtent. + expect(globalKey.currentState!.outerController.offset, appBarHeight); + // the inner scroll controller should have scrolled equivalent to the + // difference between the applied scrollExtent and the outer extent. + expect( + globalKey.currentState!.innerController.offset, + scrollExtent - appBarHeight, + ); + + final TestPointer testPointer = TestPointer(3, ui.PointerDeviceKind.trackpad); + await tester.sendEventToBinding(testPointer.addPointer( + location: Offset(0.0, appBarHeight + 1.0) + )); + await tester.sendEventToBinding(testPointer.scrollInertiaCancel()); + // ensure no change. + expect(globalKey.currentState!.outerController.offset, appBarHeight); + expect( + globalKey.currentState!.innerController.offset, + scrollExtent - appBarHeight, + ); + }); + testWidgets('scrolling by less than the expanded outer extent does not scroll the inner body', (WidgetTester tester) async { final GlobalKey globalKey = GlobalKey(); await tester.pumpWidget(buildTest(key: globalKey)); From ca7fe334853b7bed74c8fb5b881c1a61a3757375 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 20:02:29 -0500 Subject: [PATCH 50/71] Roll Flutter Engine from 6c190ea1e8df to e144f81e92d8 (3 revisions) (#116939) * b97d3545f [Impeller] Speculatively attempt to fix Metal PSO construction errors on host targets. (flutter/engine#38229) * 99f3d1931 Web trackpad pan (flutter/engine#36346) * e144f81e9 [impeller] Fix typo in compiller help (flutter/engine#38214) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 1114d07cc9d9b..805b0f6f08255 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -6c190ea1e8df1a424c94dccf31ef470b86067407 +e144f81e92d813eaa6477cbb4bfb9d93e6c128c6 From b713edceec80c68f0f7693bd2a91409482a9d6da Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Mon, 12 Dec 2022 20:58:13 -0500 Subject: [PATCH 51/71] 290f3a35e Roll Skia from 788fe69e7ade to 2e417d4f7993 (3 revisions) (flutter/engine#38235) (#116941) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 805b0f6f08255..a87d75fc77671 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -e144f81e92d813eaa6477cbb4bfb9d93e6c128c6 +290f3a35eeb24b879e1b4478fd7254d5cecbd2eb From 04ee5926a2a6aaabfa0dde0279223952d2de3e3e Mon Sep 17 00:00:00 2001 From: LongCatIsLooong <31859944+LongCatIsLooong@users.noreply.github.com> Date: Mon, 12 Dec 2022 19:42:23 -0800 Subject: [PATCH 52/71] Remove RenderEditable textPainter height hack (#113301) * remove RenderEditable textPainter height hack * Still applies the hack on web * unskip web --- .../flutter/lib/src/rendering/editable.dart | 13 +------ .../test/painting/text_painter_test.dart | 34 ++++++++++++++++--- 2 files changed, 30 insertions(+), 17 deletions(-) diff --git a/packages/flutter/lib/src/rendering/editable.dart b/packages/flutter/lib/src/rendering/editable.dart index cc06f96fcb80a..d6fad8c988b20 100644 --- a/packages/flutter/lib/src/rendering/editable.dart +++ b/packages/flutter/lib/src/rendering/editable.dart @@ -1878,21 +1878,10 @@ class RenderEditable extends RenderBox with RelayoutWhenSystemFontsChangeMixin, return math.max(estimatedHeight, minHeight); } - // TODO(LongCatIsLooong): this is a workaround for - // https://github.com/flutter/flutter/issues/112123. - // Use preferredLineHeight since SkParagraph currently returns an incorrect - // height. - final TextHeightBehavior? textHeightBehavior = this.textHeightBehavior; - final bool usePreferredLineHeightHack = maxLines == 1 - && text?.codeUnitAt(0) == null - && strutStyle != null && strutStyle != StrutStyle.disabled - && textHeightBehavior != null - && (!textHeightBehavior.applyHeightToFirstAscent || !textHeightBehavior.applyHeightToLastDescent); - // Special case maxLines == 1 since it forces the scrollable direction // to be horizontal. Report the real height to prevent the text from being // clipped. - if (maxLines == 1 && !usePreferredLineHeightHack) { + if (maxLines == 1) { // The _layoutText call lays out the paragraph using infinite width when // maxLines == 1. Also _textPainter.maxLines will be set to 1 so should // there be any line breaks only the first line is shown. diff --git a/packages/flutter/test/painting/text_painter_test.dart b/packages/flutter/test/painting/text_painter_test.dart index f4831e3e2e97e..9c890b4940a50 100644 --- a/packages/flutter/test/painting/text_painter_test.dart +++ b/packages/flutter/test/painting/text_painter_test.dart @@ -1217,12 +1217,36 @@ void main() { textDirection: TextDirection.ltr, ); - textPainter.layout(); - expect( - textPainter.getWordBoundary(const TextPosition(offset: 8)), - const TextRange(start: 8, end: 16), + textPainter.layout(); + expect( + textPainter.getWordBoundary(const TextPosition(offset: 8)), + const TextRange(start: 8, end: 16), + ); + }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61017 + + test('TextHeightBehavior with strut on empty paragraph', () { + // Regression test for https://github.com/flutter/flutter/issues/112123 + const TextStyle style = TextStyle(height: 11, fontSize: 7); + const TextSpan simple = TextSpan(text: 'x', style: style); + const TextSpan emptyString = TextSpan(text: '', style: style); + const TextSpan emptyParagraph = TextSpan(style: style); + + final TextPainter painter = TextPainter( + textDirection: TextDirection.ltr, + strutStyle: StrutStyle.fromTextStyle(style, forceStrutHeight: true), + textHeightBehavior: const TextHeightBehavior(applyHeightToFirstAscent: false, applyHeightToLastDescent: false), ); - }, skip: isBrowser); // https://github.com/flutter/flutter/issues/61017 + + painter.text = simple; + painter.layout(); + final double height = painter.height; + for (final TextSpan span in [simple, emptyString, emptyParagraph]) { + painter.text = span; + painter.layout(); + expect(painter.height, height, reason: '$span is expected to have a height of $height'); + expect(painter.preferredLineHeight, height, reason: '$span is expected to have a height of $height'); + } + }); test('TextPainter plainText getter', () { final TextPainter painter = TextPainter() From 7211ca09d1f82ae452321c49fc81051eb645bb59 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 01:54:24 -0500 Subject: [PATCH 53/71] a33e699de Roll Skia from 2e417d4f7993 to 08dc0c9e4e70 (1 revision) (flutter/engine#38239) (#116952) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index a87d75fc77671..db8f4ecaca9eb 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -290f3a35eeb24b879e1b4478fd7254d5cecbd2eb +a33e699de29b47832861af30050b9d78a6cbe74c From f5249bcb0a26118cffad7413325ab42cbe6c7c09 Mon Sep 17 00:00:00 2001 From: Michael Thomsen Date: Tue, 13 Dec 2022 08:42:54 +0100 Subject: [PATCH 54/71] Remove use of NullThrownError (#116122) --- .../lib/src/foundation/assertions.dart | 4 +-- .../_compute_caller_unsound_null_safety.dart | 30 ------------------- ...pute_caller_unsound_null_safety_error.dart | 22 -------------- .../test/foundation/assertions_test.dart | 22 -------------- .../test/foundation/isolates_test.dart | 11 ------- 5 files changed, 1 insertion(+), 88 deletions(-) delete mode 100644 packages/flutter/test/foundation/_compute_caller_unsound_null_safety.dart delete mode 100644 packages/flutter/test/foundation/_compute_caller_unsound_null_safety_error.dart diff --git a/packages/flutter/lib/src/foundation/assertions.dart b/packages/flutter/lib/src/foundation/assertions.dart index 49d9b953315dd..253b0ae6b1257 100644 --- a/packages/flutter/lib/src/foundation/assertions.dart +++ b/packages/flutter/lib/src/foundation/assertions.dart @@ -672,9 +672,7 @@ class FlutterErrorDetails with Diagnosticable { super.debugFillProperties(properties); final DiagnosticsNode verb = ErrorDescription('thrown${ context != null ? ErrorDescription(" $context") : ""}'); final Diagnosticable? diagnosticable = _exceptionToDiagnosticable(); - if (exception is NullThrownError) { // ignore: deprecated_member_use - properties.add(ErrorDescription('The null value was $verb.')); - } else if (exception is num) { + if (exception is num) { properties.add(ErrorDescription('The number $exception was $verb.')); } else { final DiagnosticsNode errorName; diff --git a/packages/flutter/test/foundation/_compute_caller_unsound_null_safety.dart b/packages/flutter/test/foundation/_compute_caller_unsound_null_safety.dart deleted file mode 100644 index 19c0dfb02fff9..0000000000000 --- a/packages/flutter/test/foundation/_compute_caller_unsound_null_safety.dart +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Running in unsound null-safety mode is intended to test for potential miscasts -// or invalid assertions. - -import 'package:flutter/src/foundation/_isolates_io.dart'; -import 'package:flutter/src/foundation/isolates.dart' as isolates; - -int? returnInt(int? arg) { - return arg; -} - -Future returnIntAsync(int? arg) { - return Future.value(arg); -} - -Future testCompute(isolates.ComputeCallback callback, T input) async { - if (input != await compute(callback, input)) { - throw Exception('compute returned bad result'); - } -} - -void main() async { - await testCompute(returnInt, 10); - await testCompute(returnInt, null); - await testCompute(returnIntAsync, 10); - await testCompute(returnIntAsync, null); -} diff --git a/packages/flutter/test/foundation/_compute_caller_unsound_null_safety_error.dart b/packages/flutter/test/foundation/_compute_caller_unsound_null_safety_error.dart deleted file mode 100644 index 3f9be4efb23f4..0000000000000 --- a/packages/flutter/test/foundation/_compute_caller_unsound_null_safety_error.dart +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 The Flutter Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -// Running in unsound null-safety mode is intended to test for potential miscasts -// or invalid assertions. - -import 'package:flutter/src/foundation/_isolates_io.dart'; - -int throwNull(dynamic arg) { - throw arg as Object; -} - -void main() async { - try { - await compute(throwNull, null); - } catch (e) { - if (e is! TypeError && e is! NullThrownError) { // ignore: deprecated_member_use - throw Exception('compute returned bad result'); - } - } -} diff --git a/packages/flutter/test/foundation/assertions_test.dart b/packages/flutter/test/foundation/assertions_test.dart index 7428e38e494c1..3f238828c09ad 100644 --- a/packages/flutter/test/foundation/assertions_test.dart +++ b/packages/flutter/test/foundation/assertions_test.dart @@ -58,21 +58,6 @@ void main() { 'INFO\n' '═════════════════════════════════════════════════════════════════\n', ); - expect( - FlutterErrorDetails( - exception: NullThrownError(), // ignore: deprecated_member_use - library: 'LIBRARY', - context: ErrorDescription('CONTEXTING'), - informationCollector: () sync* { - yield ErrorDescription('INFO'); - }, - ).toString(), - '══╡ EXCEPTION CAUGHT BY LIBRARY ╞════════════════════════════════\n' - 'The null value was thrown CONTEXTING.\n' - '\n' - 'INFO\n' - '═════════════════════════════════════════════════════════════════\n', - ); expect( FlutterErrorDetails( exception: 'MESSAGE', @@ -112,13 +97,6 @@ void main() { 'MESSAGE\n' '═════════════════════════════════════════════════════════════════\n', ); - expect( - // ignore: deprecated_member_use - FlutterErrorDetails(exception: NullThrownError()).toString(), - '══╡ EXCEPTION CAUGHT BY FLUTTER FRAMEWORK ╞══════════════════════\n' - 'The null value was thrown.\n' - '═════════════════════════════════════════════════════════════════\n', - ); }); test('FlutterErrorDetails.toStringShort', () { diff --git a/packages/flutter/test/foundation/isolates_test.dart b/packages/flutter/test/foundation/isolates_test.dart index 101c0dbd2222f..912e9baa6693c 100644 --- a/packages/flutter/test/foundation/isolates_test.dart +++ b/packages/flutter/test/foundation/isolates_test.dart @@ -221,15 +221,4 @@ void main() { '_compute_caller_invalid_message.dart'); }); }, skip: kIsWeb); // [intended] isn't supported on the web. - - group('compute() works with unsound null safety caller', () { - test('returning', () async { - await expectFileSuccessfullyCompletes( - '_compute_caller_unsound_null_safety.dart', true); - }); - test('erroring', () async { - await expectFileSuccessfullyCompletes( - '_compute_caller_unsound_null_safety_error.dart', true); - }); - }, skip: kIsWeb); // [intended] isn't supported on the web. } From 9ccfb87ad151d966a6fa3729c6dc811eee711de4 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 02:59:07 -0500 Subject: [PATCH 55/71] 64a661d6d Roll Skia from 08dc0c9e4e70 to 9abf4b1bf242 (4 revisions) (flutter/engine#38240) (#116955) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index db8f4ecaca9eb..81deb2ca96db2 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -a33e699de29b47832861af30050b9d78a6cbe74c +64a661d6d621d8fe388e6677e9d7a95eaba3eca3 From 256d54e1701d09b0ff78e1aea222fb49ea433441 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 04:17:08 -0500 Subject: [PATCH 56/71] 47417ce80 [Impeller Scene] Node deserialization (flutter/engine#38190) (#116959) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 81deb2ca96db2..0c9e299d156ed 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -64a661d6d621d8fe388e6677e9d7a95eaba3eca3 +47417ce80b7872403432627fe9667f60f61cc647 From db26f486eb919573fdb34b5b61b4194f34ae24e6 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 07:17:16 -0500 Subject: [PATCH 57/71] afc2f9559 Roll Skia from 9abf4b1bf242 to c83eef7dc2a3 (3 revisions) (flutter/engine#38243) (#116970) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 0c9e299d156ed..06e47ed291d03 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -47417ce80b7872403432627fe9667f60f61cc647 +afc2f95595d612be5b7837baca7183a8e1bcf46a From 15af81782e19ebe7273872f8b07ac71df4e749f2 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 08:04:25 -0500 Subject: [PATCH 58/71] 74a9c7e0f Roll Fuchsia Mac SDK from aMW0DjntzFJj4RoR3... to Cd_ZtrDVcpQ85HRL3... (flutter/engine#38242) (#116973) --- bin/internal/engine.version | 2 +- bin/internal/fuchsia-mac.version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 06e47ed291d03..2777c2bec0dc9 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -afc2f95595d612be5b7837baca7183a8e1bcf46a +74a9c7e0f9b16cf8e26b2d1ce7b7777ae2a07572 diff --git a/bin/internal/fuchsia-mac.version b/bin/internal/fuchsia-mac.version index d9c37b8261251..d3cb88386a95d 100644 --- a/bin/internal/fuchsia-mac.version +++ b/bin/internal/fuchsia-mac.version @@ -1 +1 @@ -aMW0DjntzFJj4RoR38FYLYDD3_0iRHaSyKd3Nfep9K4C +Cd_ZtrDVcpQ85HRL369HMqcPFqX0ozKaRaYXp1AisEsC From dbb9aa8b92d719e1cf7fe852ae45e150c0457974 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 14:39:06 -0500 Subject: [PATCH 59/71] Roll Plugins from 0609adb457fd to ec2041f82584 (6 revisions) (#116996) * 13818023c [camera] Attempt to fix flaky new Android test (flutter/plugins#6831) * da4321d01 [google_maps_flutter] Modified `README.md` to fix minor syntax issues (flutter/plugins#6631) * e8c9731f1 Roll Flutter from eefbe85c8bd4 to bd0791be3ff2 (25 revisions) (flutter/plugins#6832) * 2eb616545 Reland "[google_maps_flutter] ios: re-enable test with popup #5312" (flutter/plugins#6783) * 738bd91d8 Update FlutterFire link (flutter/plugins#6835) * ec2041f82 Roll Flutter from bd0791be3ff2 to 15af81782e19 (27 revisions) (flutter/plugins#6837) --- bin/internal/flutter_plugins.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/flutter_plugins.version b/bin/internal/flutter_plugins.version index 2a6413e8085d8..6501b726065e0 100644 --- a/bin/internal/flutter_plugins.version +++ b/bin/internal/flutter_plugins.version @@ -1 +1 @@ -0609adb457fd448be2f3c3684cc7582814fef4f8 +ec2041f82584303d640fc7a3bc4ae31ebd76bc8e From cd9a439c356c89ecf53091ab14f4e087d18555aa Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 14:40:48 -0500 Subject: [PATCH 60/71] Roll Flutter Engine from 74a9c7e0f9b1 to 9872cc7addce (2 revisions) (#116997) * f863b1978 Roll Skia from c83eef7dc2a3 to 971c342c3030 (2 revisions) (flutter/engine#38248) * 9872cc7ad Clarify file sharing flags in FML filesystem APIs on Windows (flutter/engine#38164) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 2777c2bec0dc9..75ca7b966c612 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -74a9c7e0f9b16cf8e26b2d1ce7b7777ae2a07572 +9872cc7addce2dbb13220b38056242c9e4d2b435 From 4e137208041ce8153d139598d9f1f262505b6913 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 16:15:18 -0500 Subject: [PATCH 61/71] dd1dc7258 Always set orientation preferences on iOS 16+ (flutter/engine#38230) (#117005) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 75ca7b966c612..2226e94314015 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -9872cc7addce2dbb13220b38056242c9e4d2b435 +dd1dc7258b996af218c49c3f69a5f3ea95d6546f From 51945a0df0ec0af023f6eec55e5abb0e37052e8f Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 17:26:09 -0500 Subject: [PATCH 62/71] Roll Flutter Engine from dd1dc7258b99 to 82dafdfc1c3d (2 revisions) (#117015) * b5d19f86e Manual roll of Skia from 971c342c3030 to 280ac8882cff (flutter/engine#38257) * 82dafdfc1 Roll Fuchsia Linux SDK from 8O5rMR_ehMaL3YhZ5... to A0jnUUORf2LQu1z2V... (flutter/engine#38258) --- bin/internal/engine.version | 2 +- bin/internal/fuchsia-linux.version | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 2226e94314015..8ce31c5364b0b 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -dd1dc7258b996af218c49c3f69a5f3ea95d6546f +82dafdfc1c3d482307f33ea4b4eee684c1645907 diff --git a/bin/internal/fuchsia-linux.version b/bin/internal/fuchsia-linux.version index c0b0a7f47e9ab..44c177ea923eb 100644 --- a/bin/internal/fuchsia-linux.version +++ b/bin/internal/fuchsia-linux.version @@ -1 +1 @@ -8O5rMR_ehMaL3YhZ56B1Q-pS15TRm3MJOichyVr5L7MC +A0jnUUORf2LQu1z2Vh3xlfkzRpnMOlKCuJnzesf0hJ0C From 96597c25e61762b8584a0f957dd0adb2c6ff3eb2 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 13 Dec 2022 14:28:01 -0800 Subject: [PATCH 63/71] Bump Dartdoc version to 6.1.5 (#117014) --- dev/bots/docs.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dev/bots/docs.sh b/dev/bots/docs.sh index aee4477510c8f..576bc28337466 100755 --- a/dev/bots/docs.sh +++ b/dev/bots/docs.sh @@ -20,7 +20,7 @@ function generate_docs() { # Install and activate dartdoc. # NOTE: When updating to a new dartdoc version, please also update # `dartdoc_options.yaml` to include newly introduced error and warning types. - "$DART" pub global activate dartdoc 6.1.4 + "$DART" pub global activate dartdoc 6.1.5 # Install and activate the snippets tool, which resides in the # assets-for-api-docs repo: From 0c7d84aa789e649cb05b0f5f890c04e482f43737 Mon Sep 17 00:00:00 2001 From: Daniel Iglesia Date: Tue, 13 Dec 2022 14:45:50 -0800 Subject: [PATCH 64/71] Add AppBar.forceMaterialTransparency (#101248) (#116867) * Add AppBar.forceMaterialTransparency (#101248) Allows gestures to reach widgets beneath the AppBar (when Scaffold.extendBodyBehindAppBar is true). --- .../flutter/lib/src/material/app_bar.dart | 32 +++- .../flutter/test/material/app_bar_test.dart | 163 ++++++++++++++++++ 2 files changed, 194 insertions(+), 1 deletion(-) diff --git a/packages/flutter/lib/src/material/app_bar.dart b/packages/flutter/lib/src/material/app_bar.dart index 781da031627c4..ac25e22226034 100644 --- a/packages/flutter/lib/src/material/app_bar.dart +++ b/packages/flutter/lib/src/material/app_bar.dart @@ -220,6 +220,7 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { this.toolbarTextStyle, this.titleTextStyle, this.systemOverlayStyle, + this.forceMaterialTransparency = false, }) : assert(automaticallyImplyLeading != null), assert(elevation == null || elevation >= 0.0), assert(notificationPredicate != null), @@ -789,6 +790,21 @@ class AppBar extends StatefulWidget implements PreferredSizeWidget { /// * [SystemChrome.setSystemUIOverlayStyle] final SystemUiOverlayStyle? systemOverlayStyle; + /// {@template flutter.material.appbar.forceMaterialTransparency} + /// Forces the AppBar's Material widget type to be [MaterialType.transparency] + /// (instead of Material's default type). + /// + /// This will remove the visual display of [backgroundColor] and [elevation], + /// and affect other characteristics of the AppBar's Material widget. + /// + /// Provided for cases where the app bar is to be transparent, and gestures + /// must pass through the app bar to widgets beneath the app bar (i.e. with + /// [Scaffold.extendBodyBehindAppBar] set to true). + /// + /// Defaults to false. + /// {@endtemplate} + final bool forceMaterialTransparency; + bool _getEffectiveCenterTitle(ThemeData theme) { bool platformCenter() { assert(theme.platform != null); @@ -1193,6 +1209,9 @@ class _AppBarState extends State { child: Material( color: backgroundColor, elevation: effectiveElevation, + type: widget.forceMaterialTransparency + ? MaterialType.transparency + : MaterialType.canvas, shadowColor: widget.shadowColor ?? appBarTheme.shadowColor ?? defaults.shadowColor, @@ -1249,6 +1268,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { required this.toolbarTextStyle, required this.titleTextStyle, required this.systemOverlayStyle, + required this.forceMaterialTransparency, }) : assert(primary || topPadding == 0.0), assert( !floating || (snapConfiguration == null && showOnScreenConfiguration == null) || vsync != null, @@ -1290,6 +1310,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { final TextStyle? titleTextStyle; final SystemUiOverlayStyle? systemOverlayStyle; final double _bottomHeight; + final bool forceMaterialTransparency; @override double get minExtent => collapsedHeight; @@ -1362,6 +1383,7 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { toolbarTextStyle: toolbarTextStyle, titleTextStyle: titleTextStyle, systemOverlayStyle: systemOverlayStyle, + forceMaterialTransparency: forceMaterialTransparency, ), ); return appBar; @@ -1401,7 +1423,8 @@ class _SliverAppBarDelegate extends SliverPersistentHeaderDelegate { || backwardsCompatibility != oldDelegate.backwardsCompatibility || toolbarTextStyle != oldDelegate.toolbarTextStyle || titleTextStyle != oldDelegate.titleTextStyle - || systemOverlayStyle != oldDelegate.systemOverlayStyle; + || systemOverlayStyle != oldDelegate.systemOverlayStyle + || forceMaterialTransparency != oldDelegate.forceMaterialTransparency; } @override @@ -1550,6 +1573,7 @@ class SliverAppBar extends StatefulWidget { this.toolbarTextStyle, this.titleTextStyle, this.systemOverlayStyle, + this.forceMaterialTransparency = false, }) : assert(automaticallyImplyLeading != null), assert(forceElevated != null), assert(primary != null), @@ -2038,6 +2062,11 @@ class SliverAppBar extends StatefulWidget { /// This property is used to configure an [AppBar]. final SystemUiOverlayStyle? systemOverlayStyle; + /// {@macro flutter.material.appbar.forceMaterialTransparency} + /// + /// This property is used to configure an [AppBar]. + final bool forceMaterialTransparency; + @override State createState() => _SliverAppBarState(); } @@ -2146,6 +2175,7 @@ class _SliverAppBarState extends State with TickerProviderStateMix toolbarTextStyle: widget.toolbarTextStyle, titleTextStyle: widget.titleTextStyle, systemOverlayStyle: widget.systemOverlayStyle, + forceMaterialTransparency: widget.forceMaterialTransparency, ), ), ); diff --git a/packages/flutter/test/material/app_bar_test.dart b/packages/flutter/test/material/app_bar_test.dart index b99029cab5787..97720224b9f7a 100644 --- a/packages/flutter/test/material/app_bar_test.dart +++ b/packages/flutter/test/material/app_bar_test.dart @@ -1390,6 +1390,95 @@ void main() { }); }); + group('SliverAppBar.forceMaterialTransparency', () { + Material getSliverAppBarMaterial(WidgetTester tester) { + return tester.widget(find + .descendant(of: find.byType(SliverAppBar), matching: find.byType(Material)) + .first); + } + + // Generates a MaterialApp with a SliverAppBar in a CustomScrollView. + // The first cell of the scroll view contains a button at its top, and is + // initially scrolled so that it is beneath the SliverAppBar. + Widget buildWidget({ + required bool forceMaterialTransparency, + required VoidCallback onPressed + }) { + const double appBarHeight = 120; + return MaterialApp( + home: Scaffold( + body: CustomScrollView( + controller: ScrollController(initialScrollOffset:appBarHeight), + slivers: [ + SliverAppBar( + collapsedHeight: appBarHeight, + expandedHeight: appBarHeight, + pinned: true, + elevation: 0, + backgroundColor: Colors.transparent, + forceMaterialTransparency: forceMaterialTransparency, + title: const Text('AppBar'), + ), + SliverList( + delegate: SliverChildBuilderDelegate((BuildContext context, int index) { + return SizedBox( + height: appBarHeight, + child: index == 0 + ? Align( + alignment: Alignment.topCenter, + child: TextButton(onPressed: onPressed, child: const Text('press'))) + : const SizedBox(), + ); + }, + childCount: 20, + ), + ), + ]), + ), + ); + } + + testWidgets( + 'forceMaterialTransparency == true allows gestures beneath the app bar', (WidgetTester tester) async { + bool buttonWasPressed = false; + final Widget widget = buildWidget( + forceMaterialTransparency:true, + onPressed:() { buttonWasPressed = true; }, + ); + await tester.pumpWidget(widget); + + final Material material = getSliverAppBarMaterial(tester); + expect(material.type, MaterialType.transparency); + + final Finder buttonFinder = find.byType(TextButton); + await tester.tap(buttonFinder); + await tester.pump(); + expect(buttonWasPressed, isTrue); + }); + + testWidgets( + 'forceMaterialTransparency == false does not allow gestures beneath the app bar', (WidgetTester tester) async { + // Set this, and tester.tap(warnIfMissed:false), to suppress + // errors/warning that the button is not hittable (which is expected). + WidgetController.hitTestWarningShouldBeFatal = false; + + bool buttonWasPressed = false; + final Widget widget = buildWidget( + forceMaterialTransparency:false, + onPressed:() { buttonWasPressed = true; }, + ); + await tester.pumpWidget(widget); + + final Material material = getSliverAppBarMaterial(tester); + expect(material.type, MaterialType.canvas); + + final Finder buttonFinder = find.byType(TextButton); + await tester.tap(buttonFinder, warnIfMissed:false); + await tester.pump(); + expect(buttonWasPressed, isFalse); + }); + }); + testWidgets('AppBar dimensions, with and without bottom, primary', (WidgetTester tester) async { const MediaQueryData topPadding100 = MediaQueryData(padding: EdgeInsets.only(top: 100.0)); @@ -3760,4 +3849,78 @@ void main() { expect(tester.getTopLeft(find.byKey(titleKey)).dx, leadingWidth + 16.0); expect(tester.getSize(find.byKey(leadingKey)).width, leadingWidth); }); + + group('AppBar.forceMaterialTransparency', () { + Material getAppBarMaterial(WidgetTester tester) { + return tester.widget(find + .descendant(of: find.byType(AppBar), matching: find.byType(Material)) + .first); + } + + // Generates a MaterialApp with an AppBar with a TextButton beneath it + // (via extendBodyBehindAppBar = true). + Widget buildWidget({ + required bool forceMaterialTransparency, + required VoidCallback onPressed + }) { + return MaterialApp( + home: Scaffold( + extendBodyBehindAppBar: true, + appBar: AppBar( + forceMaterialTransparency: forceMaterialTransparency, + elevation: 3, + backgroundColor: Colors.red, + title: const Text('AppBar'), + ), + body: Align( + alignment: Alignment.topCenter, + child: TextButton( + onPressed: onPressed, + child: const Text('press me'), + ), + ), + ), + ); + } + + testWidgets( + 'forceMaterialTransparency == true allows gestures beneath the app bar', (WidgetTester tester) async { + bool buttonWasPressed = false; + final Widget widget = buildWidget( + forceMaterialTransparency:true, + onPressed:() { buttonWasPressed = true; }, + ); + await tester.pumpWidget(widget); + + final Material material = getAppBarMaterial(tester); + expect(material.type, MaterialType.transparency); + + final Finder buttonFinder = find.byType(TextButton); + await tester.tap(buttonFinder); + await tester.pump(); + expect(buttonWasPressed, isTrue); + }); + + testWidgets( + 'forceMaterialTransparency == false does not allow gestures beneath the app bar', (WidgetTester tester) async { + // Set this, and tester.tap(warnIfMissed:false), to suppress + // errors/warning that the button is not hittable (which is expected). + WidgetController.hitTestWarningShouldBeFatal = false; + + bool buttonWasPressed = false; + final Widget widget = buildWidget( + forceMaterialTransparency:false, + onPressed:() { buttonWasPressed = true; }, + ); + await tester.pumpWidget(widget); + + final Material material = getAppBarMaterial(tester); + expect(material.type, MaterialType.canvas); + + final Finder buttonFinder = find.byType(TextButton); + await tester.tap(buttonFinder, warnIfMissed:false); + await tester.pump(); + expect(buttonWasPressed, isFalse); + }); + }); } From a59dd83d721460b2fea09a6b332d5c761fcf5b38 Mon Sep 17 00:00:00 2001 From: engine-flutter-autoroll Date: Tue, 13 Dec 2022 18:47:06 -0500 Subject: [PATCH 65/71] a327b48ca Roll Skia from 280ac8882cff to 537e1e8c1ca6 (9 revisions) (flutter/engine#38264) (#117025) --- bin/internal/engine.version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/internal/engine.version b/bin/internal/engine.version index 8ce31c5364b0b..f899af3acfc3e 100644 --- a/bin/internal/engine.version +++ b/bin/internal/engine.version @@ -1 +1 @@ -82dafdfc1c3d482307f33ea4b4eee684c1645907 +a327b48caaeb4730205a0b5e6c3ec8182e3d9bf2 From fae458b925e5bfa21a6cecbe69b8e6d33d7c0d5a Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Tue, 13 Dec 2022 16:09:52 -0800 Subject: [PATCH 66/71] Convert TimePicker to Material 3 (#116396) * Make some minor changes in preparation for updating the Time Picker to M3 * Revert OutlineInputBorder.borderRadius type change * Revert more OutlineInputBorder.borderRadius changes. * Convert TimePicker to Material 3 * Add example test * Revert OutlineInputBorder.borderRadius type change * Fix test * Review Changes * Merge changes * Some sizing and elevation fixes * Fix localization tests --- dev/tools/gen_defaults/bin/gen_defaults.dart | 2 + .../lib/time_picker_template.dart | 349 ++ .../time_picker/show_time_picker.0.dart | 357 ++ .../time_picker/show_time_picker.0_test.dart | 67 + .../flutter/lib/src/material/time_picker.dart | 3361 +++++++++++------ .../lib/src/material/time_picker_theme.dart | 330 +- .../test/material/time_picker_test.dart | 2910 +++++++------- .../test/material/time_picker_theme_test.dart | 33 +- .../test/material/time_picker_test.dart | 132 +- 9 files changed, 4940 insertions(+), 2601 deletions(-) create mode 100644 dev/tools/gen_defaults/lib/time_picker_template.dart create mode 100644 examples/api/lib/material/time_picker/show_time_picker.0.dart create mode 100644 examples/api/test/material/time_picker/show_time_picker.0_test.dart diff --git a/dev/tools/gen_defaults/bin/gen_defaults.dart b/dev/tools/gen_defaults/bin/gen_defaults.dart index f03fa509d62c9..6bda89e0a56ea 100644 --- a/dev/tools/gen_defaults/bin/gen_defaults.dart +++ b/dev/tools/gen_defaults/bin/gen_defaults.dart @@ -49,6 +49,7 @@ import 'package:gen_defaults/surface_tint.dart'; import 'package:gen_defaults/switch_template.dart'; import 'package:gen_defaults/tabs_template.dart'; import 'package:gen_defaults/text_field_template.dart'; +import 'package:gen_defaults/time_picker_template.dart'; import 'package:gen_defaults/typography_template.dart'; Map _readTokenFile(String fileName) { @@ -167,6 +168,7 @@ Future main(List args) async { SliderTemplate('md.comp.slider', 'Slider', '$materialLib/slider.dart', tokens).updateFile(); SurfaceTintTemplate('SurfaceTint', '$materialLib/elevation_overlay.dart', tokens).updateFile(); SwitchTemplate('Switch', '$materialLib/switch.dart', tokens).updateFile(); + TimePickerTemplate('TimePicker', '$materialLib/time_picker.dart', tokens).updateFile(); TextFieldTemplate('TextField', '$materialLib/text_field.dart', tokens).updateFile(); TabsTemplate('Tabs', '$materialLib/tabs.dart', tokens).updateFile(); TypographyTemplate('Typography', '$materialLib/typography.dart', tokens).updateFile(); diff --git a/dev/tools/gen_defaults/lib/time_picker_template.dart b/dev/tools/gen_defaults/lib/time_picker_template.dart new file mode 100644 index 0000000000000..ee15f185199a8 --- /dev/null +++ b/dev/tools/gen_defaults/lib/time_picker_template.dart @@ -0,0 +1,349 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'template.dart'; + +class TimePickerTemplate extends TokenTemplate { + const TimePickerTemplate(super.blockName, super.fileName, super.tokens, { + super.colorSchemePrefix = '_colors.', + super.textThemePrefix = '_textTheme.' + }); + + static const String tokenGroup = 'md.comp.time-picker'; + static const String hourMinuteComponent = '$tokenGroup.time-selector'; + static const String dayPeriodComponent = '$tokenGroup.period-selector'; + static const String dialComponent = '$tokenGroup.clock-dial'; + static const String variant = ''; + + @override + String generate() => ''' +// Generated version ${tokens["version"]} +class _${blockName}DefaultsM3 extends _TimePickerDefaults { + _${blockName}DefaultsM3(this.context); + + final BuildContext context; + + late final ColorScheme _colors = Theme.of(context).colorScheme; + late final TextTheme _textTheme = Theme.of(context).textTheme; + + @override + Color get backgroundColor { + return ${componentColor("$tokenGroup.container")}; + } + + @override + ButtonStyle get cancelButtonStyle { + return TextButton.styleFrom(); + } + + @override + ButtonStyle get confirmButtonStyle { + return TextButton.styleFrom(); + } + + @override + BorderSide get dayPeriodBorderSide { + return ${border('$dayPeriodComponent.outline')}; + } + + @override + Color get dayPeriodColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return ${componentColor("$dayPeriodComponent.selected.container")}; + } + // The unselected day period should match the overall picker dialog color. + // Making it transparent enables that without being redundant and allows + // the optional elevation overlay for dark mode to be visible. + return Colors.transparent; + }); + } + + @override + OutlinedBorder get dayPeriodShape { + return ${shape("$dayPeriodComponent.container")}.copyWith(side: dayPeriodBorderSide); + } + + @override + Size get dayPeriodPortraitSize { + return ${size('$dayPeriodComponent.vertical.container')}; + } + + @override + Size get dayPeriodLandscapeSize { + return ${size('$dayPeriodComponent.horizontal.container')}; + } + + @override + Size get dayPeriodInputSize { + // Input size is eight pixels smaller than the portrait size in the spec, + // but there's not token for it yet. + return Size(dayPeriodPortraitSize.width, dayPeriodPortraitSize.height - 8); + } + + @override + Color get dayPeriodTextColor { + return MaterialStateColor.resolveWith((Set states) { + return _dayPeriodForegroundColor.resolve(states); + }); + } + + MaterialStateProperty get _dayPeriodForegroundColor { + return MaterialStateProperty.resolveWith((Set states) { + Color? textColor; + if (states.contains(MaterialState.selected)) { + if (states.contains(MaterialState.pressed)) { + textColor = ${componentColor("$dayPeriodComponent.selected.pressed.label-text")}; + } else { + // not pressed + if (states.contains(MaterialState.focused)) { + textColor = ${componentColor("$dayPeriodComponent.selected.focus.label-text")}; + } else { + // not focused + if (states.contains(MaterialState.hovered)) { + textColor = ${componentColor("$dayPeriodComponent.selected.hover.label-text")}; + } + } + } + } else { + // unselected + if (states.contains(MaterialState.pressed)) { + textColor = ${componentColor("$dayPeriodComponent.unselected.pressed.label-text")}; + } else { + // not pressed + if (states.contains(MaterialState.focused)) { + textColor = ${componentColor("$dayPeriodComponent.unselected.focus.label-text")}; + } else { + // not focused + if (states.contains(MaterialState.hovered)) { + textColor = ${componentColor("$dayPeriodComponent.unselected.hover.label-text")}; + } + } + } + } + return textColor ?? ${componentColor("$dayPeriodComponent.selected.label-text")}; + }); + } + + @override + TextStyle get dayPeriodTextStyle { + return ${textStyle("$dayPeriodComponent.label-text")}!.copyWith(color: dayPeriodTextColor); + } + + @override + Color get dialBackgroundColor { + return ${componentColor(dialComponent)}.withOpacity(_colors.brightness == Brightness.dark ? 0.12 : 0.08); + } + + @override + Color get dialHandColor { + return ${componentColor('$dialComponent.selector.handle.container')}; + } + + @override + Size get dialSize { + return ${size("$dialComponent.container")}; + } + + @override + double get handWidth { + return ${size("$dialComponent.selector.track.container")}.width; + } + + @override + double get dotRadius { + return ${size("$dialComponent.selector.handle.container")}.width / 2; + } + + @override + double get centerRadius { + return ${size("$dialComponent.selector.center.container")}.width / 2; + } + + @override + Color get dialTextColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return ${componentColor('$dialComponent.selected.label-text')}; + } + return ${componentColor('$dialComponent.unselected.label-text')}; + }); + } + + @override + TextStyle get dialTextStyle { + return ${textStyle('$dialComponent.label-text')}!; + } + + @override + double get elevation { + return ${elevation("$tokenGroup.container")}; + } + + @override + Color get entryModeIconColor { + return _colors.onSurface; + } + + @override + TextStyle get helpTextStyle { + return MaterialStateTextStyle.resolveWith((Set states) { + final TextStyle textStyle = ${textStyle('$tokenGroup.headline')}!; + return textStyle.copyWith(color: ${componentColor('$tokenGroup.headline')}); + }); + } + + @override + EdgeInsetsGeometry get padding { + return const EdgeInsets.all(24); + } + + @override + Color get hourMinuteColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + Color overlayColor = ${componentColor('$hourMinuteComponent.selected.container')}; + if (states.contains(MaterialState.pressed)) { + overlayColor = ${componentColor('$hourMinuteComponent.selected.pressed.state-layer')}; + } else if (states.contains(MaterialState.focused)) { + const double focusOpacity = ${opacity('$hourMinuteComponent.focus.state-layer.opacity')}; + overlayColor = ${componentColor('$hourMinuteComponent.selected.focus.state-layer')}.withOpacity(focusOpacity); + } else if (states.contains(MaterialState.hovered)) { + const double hoverOpacity = ${opacity('$hourMinuteComponent.hover.state-layer.opacity')}; + overlayColor = ${componentColor('$hourMinuteComponent.selected.hover.state-layer')}.withOpacity(hoverOpacity); + } + return Color.alphaBlend(overlayColor, ${componentColor('$hourMinuteComponent.selected.container')}); + } else { + Color overlayColor = ${componentColor('$hourMinuteComponent.unselected.container')}; + if (states.contains(MaterialState.pressed)) { + overlayColor = ${componentColor('$hourMinuteComponent.unselected.pressed.state-layer')}; + } else if (states.contains(MaterialState.focused)) { + const double focusOpacity = ${opacity('$hourMinuteComponent.focus.state-layer.opacity')}; + overlayColor = ${componentColor('$hourMinuteComponent.unselected.focus.state-layer')}.withOpacity(focusOpacity); + } else if (states.contains(MaterialState.hovered)) { + const double hoverOpacity = ${opacity('$hourMinuteComponent.hover.state-layer.opacity')}; + overlayColor = ${componentColor('$hourMinuteComponent.unselected.hover.state-layer')}.withOpacity(hoverOpacity); + } + return Color.alphaBlend(overlayColor, ${componentColor('$hourMinuteComponent.unselected.container')}); + } + }); + } + + @override + ShapeBorder get hourMinuteShape { + return ${shape('$hourMinuteComponent.container')}; + } + + @override + Size get hourMinuteSize { + return ${size('$hourMinuteComponent.container')}; + } + + @override + Size get hourMinuteSize24Hour { + return Size(${size('$hourMinuteComponent.24h-vertical.container')}.width, hourMinuteSize.height); + } + + @override + Size get hourMinuteInputSize { + // Input size is eight pixels smaller than the regular size in the spec, but + // there's not token for it yet. + return Size(hourMinuteSize.width, hourMinuteSize.height - 8); + } + + @override + Size get hourMinuteInputSize24Hour { + // Input size is eight pixels smaller than the regular size in the spec, but + // there's not token for it yet. + return Size(hourMinuteSize24Hour.width, hourMinuteSize24Hour.height - 8); + } + + @override + Color get hourMinuteTextColor { + return MaterialStateColor.resolveWith((Set states) { + return _hourMinuteTextColor.resolve(states); + }); + } + + MaterialStateProperty get _hourMinuteTextColor { + return MaterialStateProperty.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + if (states.contains(MaterialState.pressed)) { + return ${componentColor("$hourMinuteComponent.selected.pressed.label-text")}; + } + if (states.contains(MaterialState.focused)) { + return ${componentColor("$hourMinuteComponent.selected.focus.label-text")}; + } + if (states.contains(MaterialState.hovered)) { + return ${componentColor("$hourMinuteComponent.selected.hover.label-text")}; + } + return ${componentColor("$hourMinuteComponent.selected.label-text")}; + } else { + // unselected + if (states.contains(MaterialState.pressed)) { + return ${componentColor("$hourMinuteComponent.unselected.pressed.label-text")}; + } + if (states.contains(MaterialState.focused)) { + return ${componentColor("$hourMinuteComponent.unselected.focus.label-text")}; + } + if (states.contains(MaterialState.hovered)) { + return ${componentColor("$hourMinuteComponent.unselected.hover.label-text")}; + } + return ${componentColor("$hourMinuteComponent.unselected.label-text")}; + } + }); + } + + @override + TextStyle get hourMinuteTextStyle { + return MaterialStateTextStyle.resolveWith((Set states) { + return ${textStyle('$hourMinuteComponent.label-text')}!.copyWith(color: _hourMinuteTextColor.resolve(states)); + }); + } + + @override + InputDecorationTheme get inputDecorationTheme { + // This is NOT correct, but there's no token for + // 'time-input.container.shape', so this is using the radius from the shape + // for the hour/minute selector. + final BorderRadiusGeometry selectorRadius = ${shape('$hourMinuteComponent.container')}.borderRadius; + return InputDecorationTheme( + contentPadding: EdgeInsets.zero, + filled: true, + // This should be derived from a token, but there isn't one for 'time-input'. + fillColor: hourMinuteColor, + // This should be derived from a token, but there isn't one for 'time-input'. + focusColor: _colors.primaryContainer, + enabledBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: const BorderSide(color: Colors.transparent), + ), + errorBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: BorderSide(color: _colors.error, width: 2), + ), + focusedBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: BorderSide(color: _colors.primary, width: 2), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: BorderSide(color: _colors.error, width: 2), + ), + hintStyle: hourMinuteTextStyle.copyWith(color: _colors.onSurface.withOpacity(0.36)), + // Prevent the error text from appearing. + // TODO(rami-a): Remove this workaround once + // https://github.com/flutter/flutter/issues/54104 + // is fixed. + errorStyle: const TextStyle(fontSize: 0, height: 0), + ); + } + + @override + ShapeBorder get shape { + return ${shape("$tokenGroup.container")}; + } +} +'''; +} diff --git a/examples/api/lib/material/time_picker/show_time_picker.0.dart b/examples/api/lib/material/time_picker/show_time_picker.0.dart new file mode 100644 index 0000000000000..02401a6fcb92d --- /dev/null +++ b/examples/api/lib/material/time_picker/show_time_picker.0.dart @@ -0,0 +1,357 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +/// Flutter code sample for [showTimePicker]. + +import 'package:flutter/material.dart'; + +void main() { + runApp(const ShowTimePickerApp()); +} + +class ShowTimePickerApp extends StatefulWidget { + const ShowTimePickerApp({super.key}); + + @override + State createState() => _ShowTimePickerAppState(); +} + +class _ShowTimePickerAppState extends State { + ThemeMode themeMode = ThemeMode.dark; + bool useMaterial3 = true; + + void setThemeMode(ThemeMode mode) { + setState(() { + themeMode = mode; + }); + } + + void setUseMaterial3(bool? value) { + setState(() { + useMaterial3 = value!; + }); + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + theme: ThemeData.light(useMaterial3: useMaterial3), + darkTheme: ThemeData.dark(useMaterial3: useMaterial3), + themeMode: themeMode, + home: TimePickerOptions( + themeMode: themeMode, + useMaterial3: useMaterial3, + setThemeMode: setThemeMode, + setUseMaterial3: setUseMaterial3, + ), + ); + } +} + +class TimePickerOptions extends StatefulWidget { + const TimePickerOptions({ + super.key, + required this.themeMode, + required this.useMaterial3, + required this.setThemeMode, + required this.setUseMaterial3, + }); + + final ThemeMode themeMode; + final bool useMaterial3; + final ValueChanged setThemeMode; + final ValueChanged setUseMaterial3; + + @override + State createState() => _TimePickerOptionsState(); +} + +class _TimePickerOptionsState extends State { + TimeOfDay? selectedTime; + TimePickerEntryMode entryMode = TimePickerEntryMode.dial; + Orientation? orientation; + TextDirection textDirection = TextDirection.ltr; + MaterialTapTargetSize tapTargetSize = MaterialTapTargetSize.padded; + bool use24HourTime = false; + + void _entryModeChanged(TimePickerEntryMode? value) { + if (value != entryMode) { + setState(() { + entryMode = value!; + }); + } + } + + void _orientationChanged(Orientation? value) { + if (value != orientation) { + setState(() { + orientation = value; + }); + } + } + + void _textDirectionChanged(TextDirection? value) { + if (value != textDirection) { + setState(() { + textDirection = value!; + }); + } + } + + void _tapTargetSizeChanged(MaterialTapTargetSize? value) { + if (value != tapTargetSize) { + setState(() { + tapTargetSize = value!; + }); + } + } + + void _use24HourTimeChanged(bool? value) { + if (value != use24HourTime) { + setState(() { + use24HourTime = value!; + }); + } + } + + void _themeModeChanged(ThemeMode? value) { + widget.setThemeMode(value!); + } + + @override + Widget build(BuildContext context) { + return Material( + child: Column( + children: [ + Expanded( + child: GridView( + gridDelegate: SliverGridDelegateWithMaxCrossAxisExtent( + maxCrossAxisExtent: 350, + mainAxisSpacing: 4, + mainAxisExtent: 200 * MediaQuery.textScaleFactorOf(context), + crossAxisSpacing: 4, + ), + children: [ + EnumCard( + choices: TimePickerEntryMode.values, + value: entryMode, + onChanged: _entryModeChanged, + ), + EnumCard( + choices: ThemeMode.values, + value: widget.themeMode, + onChanged: _themeModeChanged, + ), + EnumCard( + choices: TextDirection.values, + value: textDirection, + onChanged: _textDirectionChanged, + ), + EnumCard( + choices: MaterialTapTargetSize.values, + value: tapTargetSize, + onChanged: _tapTargetSizeChanged, + ), + ChoiceCard( + choices: const [...Orientation.values, null], + value: orientation, + title: '$Orientation', + choiceLabels: { + for (final Orientation choice in Orientation.values) choice: choice.name, + null: 'from MediaQuery', + }, + onChanged: _orientationChanged, + ), + ChoiceCard( + choices: const [false, true], + value: use24HourTime, + onChanged: _use24HourTimeChanged, + title: 'Time Mode', + choiceLabels: const { + false: '12-hour am/pm time', + true: '24-hour time', + }, + ), + ChoiceCard( + choices: const [false, true], + value: widget.useMaterial3, + onChanged: widget.setUseMaterial3, + title: 'Material Version', + choiceLabels: const { + false: 'Material 2', + true: 'Material 3', + }, + ), + ], + ), + ), + SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Padding( + padding: const EdgeInsets.all(12.0), + child: ElevatedButton( + child: const Text('Open time picker'), + onPressed: () async { + final TimeOfDay? time = await showTimePicker( + context: context, + initialTime: selectedTime ?? TimeOfDay.now(), + initialEntryMode: entryMode, + orientation: orientation, + builder: (BuildContext context, Widget? child) { + // We just wrap these environmental changes around the + // child in this builder so that we can apply the + // options selected above. In regular usage, this is + // rarely necessary, because the default values are + // usually used as-is. + return Theme( + data: Theme.of(context).copyWith( + materialTapTargetSize: tapTargetSize, + ), + child: Directionality( + textDirection: textDirection, + child: MediaQuery( + data: MediaQuery.of(context).copyWith( + alwaysUse24HourFormat: use24HourTime, + ), + child: child!, + ), + ), + ); + }, + ); + setState(() { + selectedTime = time; + }); + }, + ), + ), + if (selectedTime != null) Text('Selected time: ${selectedTime!.format(context)}'), + ], + ), + ), + ], + ), + ); + } +} + +// This is a simple card that presents a set of radio buttons (inside of a +// RadioSelection, defined below) for the user to select from. +class ChoiceCard extends StatelessWidget { + const ChoiceCard({ + super.key, + required this.value, + required this.choices, + required this.onChanged, + required this.choiceLabels, + required this.title, + }); + + final T value; + final Iterable choices; + final Map choiceLabels; + final String title; + final ValueChanged onChanged; + + @override + Widget build(BuildContext context) { + return Card( + // If the card gets too small, let it scroll both directions. + child: SingleChildScrollView( + child: SingleChildScrollView( + scrollDirection: Axis.horizontal, + child: Padding( + padding: const EdgeInsets.all(8.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Padding( + padding: const EdgeInsets.all(8.0), + child: Text(title), + ), + for (final T choice in choices) + RadioSelection( + value: choice, + groupValue: value, + onChanged: onChanged, + child: Text(choiceLabels[choice]!), + ), + ], + ), + ), + ), + ), + ); + } +} + +// This aggregates a ChoiceCard so that it presents a set of radio buttons for +// the allowed enum values for the user to select from. +class EnumCard extends StatelessWidget { + const EnumCard({ + super.key, + required this.value, + required this.choices, + required this.onChanged, + }); + + final T value; + final Iterable choices; + final ValueChanged onChanged; + + @override + Widget build(BuildContext context) { + return ChoiceCard( + value: value, + choices: choices, + onChanged: onChanged, + choiceLabels: { + for (final T choice in choices) choice: choice.name, + }, + title: value.runtimeType.toString()); + } +} + +// A button that has a radio button on one side and a label child. Tapping on +// the label or the radio button selects the item. +class RadioSelection extends StatefulWidget { + const RadioSelection({ + super.key, + required this.value, + required this.groupValue, + required this.onChanged, + required this.child, + }); + + final T value; + final T? groupValue; + final ValueChanged onChanged; + final Widget child; + + @override + State> createState() => _RadioSelectionState(); +} + +class _RadioSelectionState extends State> { + @override + Widget build(BuildContext context) { + return Row( + mainAxisSize: MainAxisSize.min, + children: [ + Padding( + padding: const EdgeInsetsDirectional.only(end: 8), + child: Radio( + groupValue: widget.groupValue, + value: widget.value, + onChanged: widget.onChanged, + ), + ), + GestureDetector(onTap: () => widget.onChanged(widget.value), child: widget.child), + ], + ); + } +} diff --git a/examples/api/test/material/time_picker/show_time_picker.0_test.dart b/examples/api/test/material/time_picker/show_time_picker.0_test.dart new file mode 100644 index 0000000000000..0c69ab9527494 --- /dev/null +++ b/examples/api/test/material/time_picker/show_time_picker.0_test.dart @@ -0,0 +1,67 @@ +// Copyright 2014 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'package:flutter/material.dart'; +import 'package:flutter_api_samples/material/time_picker/show_time_picker.0.dart' as example; +import 'package:flutter_test/flutter_test.dart'; + +void main() { + testWidgets('Can open and modify time picker', (WidgetTester tester) async { + const String openPicker = 'Open time picker'; + final List options = [ + '$TimePickerEntryMode', + ... TimePickerEntryMode.values.map((TimePickerEntryMode value) => value.name), + '$ThemeMode', + ... ThemeMode.values.map((ThemeMode value) => value.name), + '$TextDirection', + ... TextDirection.values.map((TextDirection value) => value.name), + '$MaterialTapTargetSize', + ... MaterialTapTargetSize.values.map((MaterialTapTargetSize value) => value.name), + '$Orientation', + ... Orientation.values.map((Orientation value) => value.name), + 'Time Mode', + '12-hour am/pm time', + '24-hour time', + 'Material Version', + 'Material 2', + 'Material 3', + openPicker, + ]; + + await tester.pumpWidget( + const MaterialApp( + home: Scaffold( + body: example.ShowTimePickerApp(), + ), + ), + ); + + for (final String option in options) { + expect(find.text(option), findsOneWidget, reason: 'Unable to find $option widget in example.'); + } + + // Open time picker + await tester.tap(find.text(openPicker)); + await tester.pumpAndSettle(); + expect(find.text('Select time'), findsOneWidget); + expect(find.text('Cancel'), findsOneWidget); + expect(find.text('OK'), findsOneWidget); + + // Close time picker + await tester.tapAt(const Offset(1, 1)); + await tester.pumpAndSettle(); + expect(find.text('Select time'), findsNothing); + expect(find.text('Cancel'), findsNothing); + expect(find.text('OK'), findsNothing); + + // Change an option. + await tester.tap(find.text('Material 2')); + await tester.pumpAndSettle(); + await tester.tap(find.text(openPicker)); + await tester.pumpAndSettle(); + expect(find.text('SELECT TIME'), findsOneWidget); + expect(find.text('CANCEL'), findsOneWidget); + expect(find.text('OK'), findsOneWidget); + }); +} diff --git a/packages/flutter/lib/src/material/time_picker.dart b/packages/flutter/lib/src/material/time_picker.dart index bc9f517ef8229..d12ffe82fd240 100644 --- a/packages/flutter/lib/src/material/time_picker.dart +++ b/packages/flutter/lib/src/material/time_picker.dart @@ -4,14 +4,15 @@ import 'dart:async'; import 'dart:math' as math; +import 'dart:ui'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:flutter/widgets.dart'; +import 'button_style.dart'; import 'color_scheme.dart'; import 'colors.dart'; -import 'constants.dart'; import 'curves.dart'; import 'debug.dart'; import 'dialog.dart'; @@ -40,30 +41,22 @@ const Duration _kDialAnimateDuration = Duration(milliseconds: 200); const double _kTwoPi = 2 * math.pi; const Duration _kVibrateCommitDelay = Duration(milliseconds: 100); -enum _TimePickerMode { hour, minute } - -const double _kTimePickerHeaderLandscapeWidth = 264.0; -const double _kTimePickerHeaderControlHeight = 80.0; - -const double _kTimePickerWidthPortrait = 328.0; -const double _kTimePickerWidthLandscape = 528.0; - -const double _kTimePickerHeightInput = 226.0; -const double _kTimePickerHeightPortrait = 496.0; -const double _kTimePickerHeightLandscape = 316.0; - -const double _kTimePickerHeightPortraitCollapsed = 484.0; -const double _kTimePickerHeightLandscapeCollapsed = 304.0; - -const BorderRadius _kDefaultBorderRadius = BorderRadius.all(Radius.circular(4.0)); -const ShapeBorder _kDefaultShape = RoundedRectangleBorder(borderRadius: _kDefaultBorderRadius); +const double _kTimePickerHeaderLandscapeWidth = 216; +const double _kTimePickerInnerDialOffset = 28; +const double _kTimePickerDialMinRadius = 50; +const double _kTimePickerDialPadding = 28; /// Interactive input mode of the time picker dialog. /// -/// In [TimePickerEntryMode.dial] mode, a clock dial is displayed and -/// the user taps or drags the time they wish to select. In -/// TimePickerEntryMode.input] mode, [TextField]s are displayed and the user -/// types in the time they wish to select. +/// In [TimePickerEntryMode.dial] mode, a clock dial is displayed and the user +/// taps or drags the time they wish to select. In TimePickerEntryMode.input] +/// mode, [TextField]s are displayed and the user types in the time they wish to +/// select. +/// +/// See also: +/// +/// * [showTimePicker], a function that shows a [TimePickerDialog] and returns +/// the selected time as a [Future]. enum TimePickerEntryMode { /// User picks time from a clock dial. /// @@ -86,186 +79,217 @@ enum TimePickerEntryMode { inputOnly } -/// Provides properties for rendering time picker header fragments. -@immutable -class _TimePickerFragmentContext { - const _TimePickerFragmentContext({ - required this.selectedTime, - required this.mode, - required this.onTimeChange, - required this.onModeChange, - required this.onHourDoubleTapped, - required this.onMinuteDoubleTapped, - required this.use24HourDials, - }) : assert(selectedTime != null), - assert(mode != null), - assert(onTimeChange != null), - assert(onModeChange != null), - assert(use24HourDials != null); - - final TimeOfDay selectedTime; - final _TimePickerMode mode; - final ValueChanged onTimeChange; - final ValueChanged<_TimePickerMode> onModeChange; - final GestureTapCallback onHourDoubleTapped; - final GestureTapCallback onMinuteDoubleTapped; - final bool use24HourDials; +// Whether the dial-mode time picker is currently selecting the hour or the +// minute. +enum _HourMinuteMode { hour, minute } + +// Aspects of _TimePickerModel that can be depended upon. +enum _TimePickerAspect { + use24HourFormat, + useMaterial3, + entryMode, + hourMinuteMode, + onHourMinuteModeChanged, + onHourDoubleTapped, + onMinuteDoubleTapped, + hourDialType, + selectedTime, + onSelectedTimeChanged, + orientation, + theme, + defaultTheme, } -class _TimePickerHeader extends StatelessWidget { - const _TimePickerHeader({ - required this.selectedTime, - required this.mode, - required this.orientation, - required this.onModeChanged, - required this.onChanged, +class _TimePickerModel extends InheritedModel<_TimePickerAspect> { + const _TimePickerModel({ + required this.entryMode, + required this.hourMinuteMode, + required this.onHourMinuteModeChanged, required this.onHourDoubleTapped, required this.onMinuteDoubleTapped, - required this.use24HourDials, - required this.helpText, - }) : assert(selectedTime != null), - assert(mode != null), - assert(orientation != null), - assert(use24HourDials != null); + required this.selectedTime, + required this.onSelectedTimeChanged, + required this.use24HourFormat, + required this.useMaterial3, + required this.hourDialType, + required this.orientation, + required this.theme, + required this.defaultTheme, + required super.child, + }); - final TimeOfDay selectedTime; - final _TimePickerMode mode; - final Orientation orientation; - final ValueChanged<_TimePickerMode> onModeChanged; - final ValueChanged onChanged; + final TimePickerEntryMode entryMode; + final _HourMinuteMode hourMinuteMode; + final ValueChanged<_HourMinuteMode> onHourMinuteModeChanged; final GestureTapCallback onHourDoubleTapped; final GestureTapCallback onMinuteDoubleTapped; - final bool use24HourDials; - final String? helpText; + final TimeOfDay selectedTime; + final ValueChanged onSelectedTimeChanged; + final bool use24HourFormat; + final bool useMaterial3; + final _HourDialType hourDialType; + final Orientation orientation; + final TimePickerThemeData theme; + final _TimePickerDefaults defaultTheme; + + static _TimePickerModel of(BuildContext context, [_TimePickerAspect? aspect]) => InheritedModel.inheritFrom<_TimePickerModel>(context, aspect: aspect)!; + static TimePickerEntryMode entryModeOf(BuildContext context) => of(context, _TimePickerAspect.entryMode).entryMode; + static _HourMinuteMode hourMinuteModeOf(BuildContext context) => of(context, _TimePickerAspect.hourMinuteMode).hourMinuteMode; + static TimeOfDay selectedTimeOf(BuildContext context) => of(context, _TimePickerAspect.selectedTime).selectedTime; + static bool use24HourFormatOf(BuildContext context) => of(context, _TimePickerAspect.use24HourFormat).use24HourFormat; + static bool useMaterial3Of(BuildContext context) => of(context, _TimePickerAspect.useMaterial3).useMaterial3; + static _HourDialType hourDialTypeOf(BuildContext context) => of(context, _TimePickerAspect.hourDialType).hourDialType; + static Orientation orientationOf(BuildContext context) => of(context, _TimePickerAspect.orientation).orientation; + static TimePickerThemeData themeOf(BuildContext context) => of(context, _TimePickerAspect.theme).theme; + static _TimePickerDefaults defaultThemeOf(BuildContext context) => of(context, _TimePickerAspect.defaultTheme).defaultTheme; + + static void setSelectedTime(BuildContext context, TimeOfDay value) => of(context, _TimePickerAspect.onSelectedTimeChanged).onSelectedTimeChanged(value); + static void setHourMinuteMode(BuildContext context, _HourMinuteMode value) => of(context, _TimePickerAspect.onHourMinuteModeChanged).onHourMinuteModeChanged(value); - void _handleChangeMode(_TimePickerMode value) { - if (value != mode) { - onModeChanged(value); + @override + bool updateShouldNotifyDependent(_TimePickerModel oldWidget, Set<_TimePickerAspect> dependencies) { + if (use24HourFormat != oldWidget.use24HourFormat && dependencies.contains(_TimePickerAspect.use24HourFormat)) { + return true; } + if (useMaterial3 != oldWidget.useMaterial3 && dependencies.contains(_TimePickerAspect.useMaterial3)) { + return true; + } + if (entryMode != oldWidget.entryMode && dependencies.contains(_TimePickerAspect.entryMode)) { + return true; + } + if (hourMinuteMode != oldWidget.hourMinuteMode && dependencies.contains(_TimePickerAspect.hourMinuteMode)) { + return true; + } + if (onHourMinuteModeChanged != oldWidget.onHourMinuteModeChanged && dependencies.contains(_TimePickerAspect.onHourMinuteModeChanged)) { + return true; + } + if (onHourMinuteModeChanged != oldWidget.onHourDoubleTapped && dependencies.contains(_TimePickerAspect.onHourDoubleTapped)) { + return true; + } + if (onHourMinuteModeChanged != oldWidget.onMinuteDoubleTapped && dependencies.contains(_TimePickerAspect.onMinuteDoubleTapped)) { + return true; + } + if (hourDialType != oldWidget.hourDialType && dependencies.contains(_TimePickerAspect.hourDialType)) { + return true; + } + if (selectedTime != oldWidget.selectedTime && dependencies.contains(_TimePickerAspect.selectedTime)) { + return true; + } + if (onSelectedTimeChanged != oldWidget.onSelectedTimeChanged && dependencies.contains(_TimePickerAspect.onSelectedTimeChanged)) { + return true; + } + if (orientation != oldWidget.orientation && dependencies.contains(_TimePickerAspect.orientation)) { + return true; + } + if (theme != oldWidget.theme && dependencies.contains(_TimePickerAspect.theme)) { + return true; + } + if (defaultTheme != oldWidget.defaultTheme && dependencies.contains(_TimePickerAspect.defaultTheme)) { + return true; + } + return false; } + @override + bool updateShouldNotify(_TimePickerModel oldWidget) { + return use24HourFormat != oldWidget.use24HourFormat + || useMaterial3 != oldWidget.useMaterial3 + || entryMode != oldWidget.entryMode + || hourMinuteMode != oldWidget.hourMinuteMode + || onHourMinuteModeChanged != oldWidget.onHourMinuteModeChanged + || onHourDoubleTapped != oldWidget.onHourDoubleTapped + || onMinuteDoubleTapped != oldWidget.onMinuteDoubleTapped + || hourDialType != oldWidget.hourDialType + || selectedTime != oldWidget.selectedTime + || onSelectedTimeChanged != oldWidget.onSelectedTimeChanged + || orientation != oldWidget.orientation + || theme != oldWidget.theme + || defaultTheme != oldWidget.defaultTheme; + } +} + +class _TimePickerHeader extends StatelessWidget { + const _TimePickerHeader({ required this.helpText }); + + final String helpText; + @override Widget build(BuildContext context) { - assert(debugCheckHasMediaQuery(context)); - final ThemeData themeData = Theme.of(context); final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat( - alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context), - ); - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final String timePickerDialHelpText = themeData.useMaterial3 - ? localizations.timePickerDialHelpText - : localizations.timePickerDialHelpText.toUpperCase(); - - final _TimePickerFragmentContext fragmentContext = _TimePickerFragmentContext( - selectedTime: selectedTime, - mode: mode, - onTimeChange: onChanged, - onModeChange: _handleChangeMode, - onHourDoubleTapped: onHourDoubleTapped, - onMinuteDoubleTapped: onMinuteDoubleTapped, - use24HourDials: use24HourDials, + alwaysUse24HourFormat: _TimePickerModel.use24HourFormatOf(context), ); - final EdgeInsets padding; - double? width; - final Widget controls; - - switch (orientation) { + final _HourDialType hourDialType = _TimePickerModel.hourDialTypeOf(context); + switch (_TimePickerModel.orientationOf(context)) { case Orientation.portrait: - // Keep width null because in portrait we don't cap the width. - padding = const EdgeInsets.symmetric(horizontal: 24.0); - controls = Column( + return Column( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 16.0), - SizedBox( - height: kMinInteractiveDimension * 2, - child: Row( - children: [ - if (!use24HourDials && timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) ...[ - _DayPeriodControl( - selectedTime: selectedTime, - orientation: orientation, - onChanged: onChanged, - ), - const SizedBox(width: 12.0), + Padding(padding: EdgeInsetsDirectional.only(bottom: _TimePickerModel.useMaterial3Of(context) ? 20 : 24), + child: Text( + helpText, + style: _TimePickerModel.themeOf(context).helpTextStyle ?? _TimePickerModel.defaultThemeOf(context).helpTextStyle, + ), + ), + Row( + children: [ + if (hourDialType == _HourDialType.twelveHour && timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) + const _DayPeriodControl(), + Expanded( + child: Row( + // Hour/minutes should not change positions in RTL locales. + textDirection: TextDirection.ltr, + children: [ + const Expanded(child: _HourControl()), + _StringFragment(timeOfDayFormat: timeOfDayFormat), + const Expanded(child: _MinuteControl()), + ], + ), + ), + if (hourDialType == _HourDialType.twelveHour && timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) + ...[ + const SizedBox(width: 12), + const _DayPeriodControl(), ], - Expanded( + ], + ), + ], + ); + case Orientation.landscape: + return SizedBox( + width: _kTimePickerHeaderLandscapeWidth, + child: Stack( + children: [ + Text( + helpText, + style: _TimePickerModel.themeOf(context).helpTextStyle ?? _TimePickerModel.defaultThemeOf(context).helpTextStyle, + ), + Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + if (hourDialType == _HourDialType.twelveHour && timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) + const _DayPeriodControl(), + Padding( + padding: EdgeInsets.only(bottom: hourDialType == _HourDialType.twelveHour ? 12 : 0), child: Row( // Hour/minutes should not change positions in RTL locales. textDirection: TextDirection.ltr, children: [ - Expanded(child: _HourControl(fragmentContext: fragmentContext)), + const Expanded(child: _HourControl()), _StringFragment(timeOfDayFormat: timeOfDayFormat), - Expanded(child: _MinuteControl(fragmentContext: fragmentContext)), + const Expanded(child: _MinuteControl()), ], ), ), - if (!use24HourDials && timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) ...[ - const SizedBox(width: 12.0), - _DayPeriodControl( - selectedTime: selectedTime, - orientation: orientation, - onChanged: onChanged, - ), - ], + if (hourDialType == _HourDialType.twelveHour && timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) + const _DayPeriodControl(), ], ), - ), - ], - ); - break; - case Orientation.landscape: - width = _kTimePickerHeaderLandscapeWidth; - padding = const EdgeInsets.symmetric(horizontal: 24.0); - controls = Expanded( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - if (!use24HourDials && timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) - _DayPeriodControl( - selectedTime: selectedTime, - orientation: orientation, - onChanged: onChanged, - ), - SizedBox( - height: kMinInteractiveDimension * 2, - child: Row( - // Hour/minutes should not change positions in RTL locales. - textDirection: TextDirection.ltr, - children: [ - Expanded(child: _HourControl(fragmentContext: fragmentContext)), - _StringFragment(timeOfDayFormat: timeOfDayFormat), - Expanded(child: _MinuteControl(fragmentContext: fragmentContext)), - ], - ), - ), - if (!use24HourDials && timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) - _DayPeriodControl( - selectedTime: selectedTime, - orientation: orientation, - onChanged: onChanged, - ), ], ), ); - break; } - - return Container( - width: width, - padding: padding, - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - const SizedBox(height: 16.0), - Text( - helpText ?? timePickerDialHelpText, - style: TimePickerTheme.of(context).helpTextStyle ?? themeData.textTheme.labelSmall, - ), - controls, - ], - ), - ); } } @@ -275,9 +299,7 @@ class _HourMinuteControl extends StatelessWidget { required this.onTap, required this.onDoubleTap, required this.isSelected, - }) : assert(text != null), - assert(onTap != null), - assert(isSelected != null); + }); final String text; final GestureTapCallback onTap; @@ -286,27 +308,37 @@ class _HourMinuteControl extends StatelessWidget { @override Widget build(BuildContext context) { - final ThemeData themeData = Theme.of(context); - final TimePickerThemeData timePickerTheme = TimePickerTheme.of(context); - final bool isDark = themeData.colorScheme.brightness == Brightness.dark; - final Color textColor = timePickerTheme.hourMinuteTextColor - ?? MaterialStateColor.resolveWith((Set states) { - return states.contains(MaterialState.selected) - ? themeData.colorScheme.primary - : themeData.colorScheme.onSurface; - }); - final Color backgroundColor = timePickerTheme.hourMinuteColor - ?? MaterialStateColor.resolveWith((Set states) { - return states.contains(MaterialState.selected) - ? themeData.colorScheme.primary.withOpacity(isDark ? 0.24 : 0.12) - : themeData.colorScheme.onSurface.withOpacity(0.12); - }); - final TextStyle style = timePickerTheme.hourMinuteTextStyle ?? themeData.textTheme.displayMedium!; - final ShapeBorder shape = timePickerTheme.hourMinuteShape ?? _kDefaultShape; + final TimePickerThemeData timePickerTheme = _TimePickerModel.themeOf(context); + final _TimePickerDefaults defaultTheme = _TimePickerModel.defaultThemeOf(context); + final Color backgroundColor = timePickerTheme.hourMinuteColor ?? defaultTheme.hourMinuteColor; + final ShapeBorder shape = timePickerTheme.hourMinuteShape ?? defaultTheme.hourMinuteShape; + + final Set states = { + if (isSelected) MaterialState.selected, + }; + final Color effectiveTextColor = MaterialStateProperty.resolveAs( + _TimePickerModel.themeOf(context).hourMinuteTextColor ?? _TimePickerModel.defaultThemeOf(context).hourMinuteTextColor, + states, + ); + final TextStyle effectiveStyle = MaterialStateProperty.resolveAs( + timePickerTheme.hourMinuteTextStyle ?? defaultTheme.hourMinuteTextStyle, + states, + ).copyWith(color: effectiveTextColor); + + final double height; + switch (_TimePickerModel.entryModeOf(context)) { + case TimePickerEntryMode.dial: + case TimePickerEntryMode.dialOnly: + height = defaultTheme.hourMinuteSize.height; + break; + case TimePickerEntryMode.input: + case TimePickerEntryMode.inputOnly: + height = defaultTheme.hourMinuteInputSize.height; + break; + } - final Set states = isSelected ? {MaterialState.selected} : {}; return SizedBox( - height: _kTimePickerHeaderControlHeight, + height: height, child: Material( color: MaterialStateProperty.resolveAs(backgroundColor, states), clipBehavior: Clip.antiAlias, @@ -317,8 +349,8 @@ class _HourMinuteControl extends StatelessWidget { child: Center( child: Text( text, - style: style.copyWith(color: MaterialStateProperty.resolveAs(textColor, states)), - textScaleFactor: 1.0, + style: effectiveStyle, + textScaleFactor: 1, ), ), ), @@ -326,39 +358,39 @@ class _HourMinuteControl extends StatelessWidget { ); } } + /// Displays the hour fragment. /// -/// When tapped changes time picker dial mode to [_TimePickerMode.hour]. +/// When tapped changes time picker dial mode to [_HourMinuteMode.hour]. class _HourControl extends StatelessWidget { - const _HourControl({ - required this.fragmentContext, - }); - - final _TimePickerFragmentContext fragmentContext; + const _HourControl(); @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); final bool alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); + final TimeOfDay selectedTime = _TimePickerModel.selectedTimeOf(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); final String formattedHour = localizations.formatHour( - fragmentContext.selectedTime, - alwaysUse24HourFormat: alwaysUse24HourFormat, + selectedTime, + alwaysUse24HourFormat: _TimePickerModel.use24HourFormatOf(context), ); TimeOfDay hoursFromSelected(int hoursToAdd) { - if (fragmentContext.use24HourDials) { - final int selectedHour = fragmentContext.selectedTime.hour; - return fragmentContext.selectedTime.replacing( - hour: (selectedHour + hoursToAdd) % TimeOfDay.hoursPerDay, - ); - } else { - // Cycle 1 through 12 without changing day period. - final int periodOffset = fragmentContext.selectedTime.periodOffset; - final int hours = fragmentContext.selectedTime.hourOfPeriod; - return fragmentContext.selectedTime.replacing( - hour: periodOffset + (hours + hoursToAdd) % TimeOfDay.hoursPerPeriod, - ); + switch (_TimePickerModel.hourDialTypeOf(context)) { + case _HourDialType.twentyFourHour: + case _HourDialType.twentyFourHourDoubleRing: + final int selectedHour = selectedTime.hour; + return selectedTime.replacing( + hour: (selectedHour + hoursToAdd) % TimeOfDay.hoursPerDay, + ); + case _HourDialType.twelveHour: + // Cycle 1 through 12 without changing day period. + final int periodOffset = selectedTime.periodOffset; + final int hours = selectedTime.hourOfPeriod; + return selectedTime.replacing( + hour: periodOffset + (hours + hoursToAdd) % TimeOfDay.hoursPerPeriod, + ); } } @@ -378,27 +410,27 @@ class _HourControl extends StatelessWidget { excludeSemantics: true, increasedValue: formattedNextHour, onIncrease: () { - fragmentContext.onTimeChange(nextHour); + _TimePickerModel.setSelectedTime(context, nextHour); }, decreasedValue: formattedPreviousHour, onDecrease: () { - fragmentContext.onTimeChange(previousHour); + _TimePickerModel.setSelectedTime(context, previousHour); }, child: _HourMinuteControl( - isSelected: fragmentContext.mode == _TimePickerMode.hour, + isSelected: _TimePickerModel.hourMinuteModeOf(context) == _HourMinuteMode.hour, text: formattedHour, - onTap: Feedback.wrapForTap(() => fragmentContext.onModeChange(_TimePickerMode.hour), context)!, - onDoubleTap: fragmentContext.onHourDoubleTapped, + onTap: Feedback.wrapForTap(() => _TimePickerModel.setHourMinuteMode(context, _HourMinuteMode.hour), context)!, + onDoubleTap: _TimePickerModel.of(context, _TimePickerAspect.onHourDoubleTapped).onHourDoubleTapped, ), ); } } /// A passive fragment showing a string value. +/// +/// Used to display the appropriate separator between the input fields. class _StringFragment extends StatelessWidget { - const _StringFragment({ - required this.timeOfDayFormat, - }); + const _StringFragment({ required this.timeOfDayFormat }); final TimeOfDayFormat timeOfDayFormat; @@ -420,18 +452,39 @@ class _StringFragment extends StatelessWidget { Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final TimePickerThemeData timePickerTheme = TimePickerTheme.of(context); - final TextStyle hourMinuteStyle = timePickerTheme.hourMinuteTextStyle ?? theme.textTheme.displayMedium!; - final Color textColor = timePickerTheme.hourMinuteTextColor ?? theme.colorScheme.onSurface; + final _TimePickerDefaults defaultTheme = theme.useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + final Set states = {}; + + final Color effectiveTextColor = MaterialStateProperty.resolveAs( + timePickerTheme.hourMinuteTextColor ?? defaultTheme.hourMinuteTextColor, + states, + ); + final TextStyle effectiveStyle = MaterialStateProperty.resolveAs( + timePickerTheme.hourMinuteTextStyle ?? defaultTheme.hourMinuteTextStyle, + states, + ).copyWith(color: effectiveTextColor); + + final double height; + switch (_TimePickerModel.entryModeOf(context)) { + case TimePickerEntryMode.dial: + case TimePickerEntryMode.dialOnly: + height = defaultTheme.hourMinuteSize.height; + break; + case TimePickerEntryMode.input: + case TimePickerEntryMode.inputOnly: + height = defaultTheme.hourMinuteInputSize.height; + break; + } return ExcludeSemantics( - child: Padding( - padding: const EdgeInsets.symmetric(horizontal: 6.0), - child: Center( - child: Text( - _stringFragmentValue(timeOfDayFormat), - style: hourMinuteStyle.apply(color: MaterialStateProperty.resolveAs(textColor, {})), - textScaleFactor: 1.0, - ), + child: SizedBox( + width: timeOfDayFormat == TimeOfDayFormat.frenchCanadian ? 36 : 24, + height: height, + child: Text( + _stringFragmentValue(timeOfDayFormat), + style: effectiveStyle, + textScaleFactor: 1, + textAlign: TextAlign.center, ), ), ); @@ -440,24 +493,21 @@ class _StringFragment extends StatelessWidget { /// Displays the minute fragment. /// -/// When tapped changes time picker dial mode to [_TimePickerMode.minute]. +/// When tapped changes time picker dial mode to [_HourMinuteMode.minute]. class _MinuteControl extends StatelessWidget { - const _MinuteControl({ - required this.fragmentContext, - }); - - final _TimePickerFragmentContext fragmentContext; + const _MinuteControl(); @override Widget build(BuildContext context) { final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final String formattedMinute = localizations.formatMinute(fragmentContext.selectedTime); - final TimeOfDay nextMinute = fragmentContext.selectedTime.replacing( - minute: (fragmentContext.selectedTime.minute + 1) % TimeOfDay.minutesPerHour, + final TimeOfDay selectedTime = _TimePickerModel.selectedTimeOf(context); + final String formattedMinute = localizations.formatMinute(selectedTime); + final TimeOfDay nextMinute = selectedTime.replacing( + minute: (selectedTime.minute + 1) % TimeOfDay.minutesPerHour, ); final String formattedNextMinute = localizations.formatMinute(nextMinute); - final TimeOfDay previousMinute = fragmentContext.selectedTime.replacing( - minute: (fragmentContext.selectedTime.minute - 1) % TimeOfDay.minutesPerHour, + final TimeOfDay previousMinute = selectedTime.replacing( + minute: (selectedTime.minute - 1) % TimeOfDay.minutesPerHour, ); final String formattedPreviousMinute = localizations.formatMinute(previousMinute); @@ -466,43 +516,42 @@ class _MinuteControl extends StatelessWidget { value: '${localizations.timePickerMinuteModeAnnouncement} $formattedMinute', increasedValue: formattedNextMinute, onIncrease: () { - fragmentContext.onTimeChange(nextMinute); + _TimePickerModel.setSelectedTime(context, nextMinute); }, decreasedValue: formattedPreviousMinute, onDecrease: () { - fragmentContext.onTimeChange(previousMinute); + _TimePickerModel.setSelectedTime(context, previousMinute); }, child: _HourMinuteControl( - isSelected: fragmentContext.mode == _TimePickerMode.minute, + isSelected: _TimePickerModel.hourMinuteModeOf(context) == _HourMinuteMode.minute, text: formattedMinute, - onTap: Feedback.wrapForTap(() => fragmentContext.onModeChange(_TimePickerMode.minute), context)!, - onDoubleTap: fragmentContext.onMinuteDoubleTapped, + onTap: Feedback.wrapForTap(() => _TimePickerModel.setHourMinuteMode(context, _HourMinuteMode.minute), context)!, + onDoubleTap: _TimePickerModel.of(context, _TimePickerAspect.onMinuteDoubleTapped).onMinuteDoubleTapped, ), ); } } - /// Displays the am/pm fragment and provides controls for switching between am /// and pm. class _DayPeriodControl extends StatelessWidget { - const _DayPeriodControl({ - required this.selectedTime, - required this.onChanged, - required this.orientation, - }); + const _DayPeriodControl({ this.onPeriodChanged }); - final TimeOfDay selectedTime; - final Orientation orientation; - final ValueChanged onChanged; + final ValueChanged? onPeriodChanged; - void _togglePeriod() { + void _togglePeriod(BuildContext context) { + final TimeOfDay selectedTime = _TimePickerModel.selectedTimeOf(context); final int newHour = (selectedTime.hour + TimeOfDay.hoursPerPeriod) % TimeOfDay.hoursPerDay; final TimeOfDay newTime = selectedTime.replacing(hour: newHour); - onChanged(newTime); + if (onPeriodChanged != null) { + onPeriodChanged!.call(newTime); + } else { + _TimePickerModel.setSelectedTime(context, newTime); + } } void _setAm(BuildContext context) { + final TimeOfDay selectedTime = _TimePickerModel.selectedTimeOf(context); if (selectedTime.period == DayPeriod.am) { return; } @@ -517,10 +566,11 @@ class _DayPeriodControl extends StatelessWidget { case TargetPlatform.macOS: break; } - _togglePeriod(); + _togglePeriod(context); } void _setPm(BuildContext context) { + final TimeOfDay selectedTime = _TimePickerModel.selectedTimeOf(context); if (selectedTime.period == DayPeriod.pm) { return; } @@ -535,113 +585,72 @@ class _DayPeriodControl extends StatelessWidget { case TargetPlatform.macOS: break; } - _togglePeriod(); + _togglePeriod(context); } @override Widget build(BuildContext context) { final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(context); - final ColorScheme colorScheme = Theme.of(context).colorScheme; - final TimePickerThemeData timePickerTheme = TimePickerTheme.of(context); - final bool isDark = colorScheme.brightness == Brightness.dark; - final Color textColor = timePickerTheme.dayPeriodTextColor - ?? MaterialStateColor.resolveWith((Set states) { - return states.contains(MaterialState.selected) - ? colorScheme.primary - : colorScheme.onSurface.withOpacity(0.60); - }); - final Color backgroundColor = timePickerTheme.dayPeriodColor - ?? MaterialStateColor.resolveWith((Set states) { - // The unselected day period should match the overall picker dialog - // color. Making it transparent enables that without being redundant - // and allows the optional elevation overlay for dark mode to be - // visible. - return states.contains(MaterialState.selected) - ? colorScheme.primary.withOpacity(isDark ? 0.24 : 0.12) - : Colors.transparent; - }); + final TimePickerThemeData timePickerTheme = _TimePickerModel.themeOf(context); + final _TimePickerDefaults defaultTheme = _TimePickerModel.defaultThemeOf(context); + final TimeOfDay selectedTime = _TimePickerModel.selectedTimeOf(context); final bool amSelected = selectedTime.period == DayPeriod.am; - final Set amStates = amSelected ? {MaterialState.selected} : {}; final bool pmSelected = !amSelected; - final Set pmStates = pmSelected ? {MaterialState.selected} : {}; - final TextStyle textStyle = timePickerTheme.dayPeriodTextStyle ?? Theme.of(context).textTheme.titleMedium!; - final TextStyle amStyle = textStyle.copyWith( - color: MaterialStateProperty.resolveAs(textColor, amStates), - ); - final TextStyle pmStyle = textStyle.copyWith( - color: MaterialStateProperty.resolveAs(textColor, pmStates), - ); - OutlinedBorder shape = timePickerTheme.dayPeriodShape ?? - const RoundedRectangleBorder(borderRadius: _kDefaultBorderRadius); - final BorderSide borderSide = timePickerTheme.dayPeriodBorderSide ?? BorderSide( - color: Color.alphaBlend(colorScheme.onBackground.withOpacity(0.38), colorScheme.surface), - ); - // Apply the custom borderSide. - shape = shape.copyWith( - side: borderSide, + final BorderSide resolvedSide = timePickerTheme.dayPeriodBorderSide ?? defaultTheme.dayPeriodBorderSide; + final OutlinedBorder resolvedShape = (timePickerTheme.dayPeriodShape ?? defaultTheme.dayPeriodShape) + .copyWith(side: resolvedSide); + + final Widget amButton = _AmPmButton( + selected: amSelected, + onPressed: () => _setAm(context), + label: materialLocalizations.anteMeridiemAbbreviation, ); - final double buttonTextScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 2.0); - - final Widget amButton = Material( - color: MaterialStateProperty.resolveAs(backgroundColor, amStates), - child: InkWell( - onTap: Feedback.wrapForTap(() => _setAm(context), context), - child: Semantics( - checked: amSelected, - inMutuallyExclusiveGroup: true, - button: true, - child: Center( - child: Text( - materialLocalizations.anteMeridiemAbbreviation, - style: amStyle, - textScaleFactor: buttonTextScaleFactor, - ), - ), - ), - ), + final Widget pmButton = _AmPmButton( + selected: pmSelected, + onPressed: () => _setPm(context), + label: materialLocalizations.postMeridiemAbbreviation, ); - final Widget pmButton = Material( - color: MaterialStateProperty.resolveAs(backgroundColor, pmStates), - child: InkWell( - onTap: Feedback.wrapForTap(() => _setPm(context), context), - child: Semantics( - checked: pmSelected, - inMutuallyExclusiveGroup: true, - button: true, - child: Center( - child: Text( - materialLocalizations.postMeridiemAbbreviation, - style: pmStyle, - textScaleFactor: buttonTextScaleFactor, - ), - ), - ), - ), - ); + Size dayPeriodSize; + final Orientation orientation; + switch (_TimePickerModel.entryModeOf(context)) { + case TimePickerEntryMode.dial: + case TimePickerEntryMode.dialOnly: + orientation = _TimePickerModel.orientationOf(context); + switch (orientation) { + case Orientation.portrait: + dayPeriodSize = defaultTheme.dayPeriodPortraitSize; + break; + case Orientation.landscape: + dayPeriodSize = defaultTheme.dayPeriodLandscapeSize; + break; + } + break; + case TimePickerEntryMode.input: + case TimePickerEntryMode.inputOnly: + orientation = Orientation.portrait; + dayPeriodSize = defaultTheme.dayPeriodInputSize; + break; + } final Widget result; switch (orientation) { case Orientation.portrait: - const double width = 52.0; result = _DayPeriodInputPadding( - minSize: const Size(width, kMinInteractiveDimension * 2), + minSize: dayPeriodSize, orientation: orientation, - child: SizedBox( - width: width, - height: _kTimePickerHeaderControlHeight, + child: SizedBox.fromSize( + size: dayPeriodSize, child: Material( clipBehavior: Clip.antiAlias, color: Colors.transparent, - shape: shape, + shape: resolvedShape, child: Column( children: [ Expanded(child: amButton), Container( - decoration: BoxDecoration( - border: Border(top: borderSide), - ), + decoration: BoxDecoration(border: Border(top: resolvedSide)), height: 1, ), Expanded(child: pmButton), @@ -653,21 +662,19 @@ class _DayPeriodControl extends StatelessWidget { break; case Orientation.landscape: result = _DayPeriodInputPadding( - minSize: const Size(0.0, kMinInteractiveDimension), + minSize: dayPeriodSize, orientation: orientation, child: SizedBox( - height: 40.0, + height: dayPeriodSize.height, child: Material( clipBehavior: Clip.antiAlias, color: Colors.transparent, - shape: shape, + shape: resolvedShape, child: Row( children: [ Expanded(child: amButton), Container( - decoration: BoxDecoration( - border: Border(left: borderSide), - ), + decoration: BoxDecoration(border: Border(left: resolvedSide)), width: 1, ), Expanded(child: pmButton), @@ -682,6 +689,48 @@ class _DayPeriodControl extends StatelessWidget { } } +class _AmPmButton extends StatelessWidget { + const _AmPmButton({ + required this.onPressed, + required this.selected, + required this.label, + }); + + final bool selected; + final VoidCallback onPressed; + final String label; + + @override + Widget build(BuildContext context) { + final Set states = { if (selected) MaterialState.selected }; + final TimePickerThemeData timePickerTheme = _TimePickerModel.themeOf(context); + final _TimePickerDefaults defaultTheme = _TimePickerModel.defaultThemeOf(context); + final Color resolvedBackgroundColor = MaterialStateProperty.resolveAs(timePickerTheme.dayPeriodColor ?? defaultTheme.dayPeriodColor, states); + final Color resolvedTextColor = MaterialStateProperty.resolveAs(timePickerTheme.dayPeriodTextColor ?? defaultTheme.dayPeriodTextColor, states); + final TextStyle? resolvedTextStyle = MaterialStateProperty.resolveAs(timePickerTheme.dayPeriodTextStyle ?? defaultTheme.dayPeriodTextStyle, states)?.copyWith(color: resolvedTextColor); + final double buttonTextScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 2); + + return Material( + color: resolvedBackgroundColor, + child: InkWell( + onTap: Feedback.wrapForTap(onPressed, context), + child: Semantics( + checked: selected, + inMutuallyExclusiveGroup: true, + button: true, + child: Center( + child: Text( + label, + style: resolvedTextStyle, + textScaleFactor: buttonTextScaleFactor, + ), + ), + ), + ), + ); + } +} + /// A widget to pad the area around the [_DayPeriodControl]'s inner [Material]. class _DayPeriodInputPadding extends SingleChildRenderObjectWidget { const _DayPeriodInputPadding({ @@ -734,7 +783,7 @@ class _RenderInputPadding extends RenderShiftedBox { if (child != null) { return math.max(child!.getMinIntrinsicWidth(height), minSize.width); } - return 0.0; + return 0; } @override @@ -742,7 +791,7 @@ class _RenderInputPadding extends RenderShiftedBox { if (child != null) { return math.max(child!.getMinIntrinsicHeight(width), minSize.height); } - return 0.0; + return 0; } @override @@ -750,7 +799,7 @@ class _RenderInputPadding extends RenderShiftedBox { if (child != null) { return math.max(child!.getMaxIntrinsicWidth(height), minSize.width); } - return 0.0; + return 0; } @override @@ -758,7 +807,7 @@ class _RenderInputPadding extends RenderShiftedBox { if (child != null) { return math.max(child!.getMaxIntrinsicHeight(width), minSize.height); } - return 0.0; + return 0; } Size _computeSize({required BoxConstraints constraints, required ChildLayouter layoutChild}) { @@ -797,9 +846,9 @@ class _RenderInputPadding extends RenderShiftedBox { return true; } - if (position.dx < 0.0 || + if (position.dx < 0 || position.dx > math.max(child!.size.width, minSize.width) || - position.dy < 0.0 || + position.dy < 0 || position.dy > math.max(child!.size.height, minSize.height)) { return false; } @@ -808,21 +857,20 @@ class _RenderInputPadding extends RenderShiftedBox { switch (orientation) { case Orientation.portrait: if (position.dy > newPosition.dy) { - newPosition += const Offset(0.0, 1.0); + newPosition += const Offset(0, 1); } else { - newPosition += const Offset(0.0, -1.0); + newPosition += const Offset(0, -1); } break; case Orientation.landscape: if (position.dx > newPosition.dx) { - newPosition += const Offset(1.0, 0.0); + newPosition += const Offset(1, 0); } else { - newPosition += const Offset(-1.0, 0.0); + newPosition += const Offset(-1, 0); } break; } - return result.addWithRawTransform( transform: MatrixUtils.forceToPoint(newPosition), position: newPosition, @@ -837,6 +885,7 @@ class _RenderInputPadding extends RenderShiftedBox { class _TappableLabel { _TappableLabel({ required this.value, + required this.inner, required this.painter, required this.onTap, }); @@ -844,6 +893,10 @@ class _TappableLabel { /// The value this label is displaying. final int value; + /// This value is part of the "inner" ring of values on the dial, used for 24 + /// hour input. + final bool inner; + /// Paints the text of the label. final TextPainter painter; @@ -854,73 +907,88 @@ class _TappableLabel { class _DialPainter extends CustomPainter { _DialPainter({ required this.primaryLabels, - required this.secondaryLabels, + required this.selectedLabels, required this.backgroundColor, - required this.accentColor, + required this.handColor, + required this.handWidth, required this.dotColor, + required this.dotRadius, + required this.centerRadius, required this.theta, + required this.radius, required this.textDirection, required this.selectedValue, }) : super(repaint: PaintingBinding.instance.systemFonts); final List<_TappableLabel> primaryLabels; - final List<_TappableLabel> secondaryLabels; + final List<_TappableLabel> selectedLabels; final Color backgroundColor; - final Color accentColor; + final Color handColor; + final double handWidth; final Color dotColor; + final double dotRadius; + final double centerRadius; final double theta; + final double radius; final TextDirection textDirection; final int selectedValue; - static const double _labelPadding = 28.0; - void dispose() { for (final _TappableLabel label in primaryLabels) { label.painter.dispose(); } - for (final _TappableLabel label in secondaryLabels) { + for (final _TappableLabel label in selectedLabels) { label.painter.dispose(); } primaryLabels.clear(); - secondaryLabels.clear(); + selectedLabels.clear(); } @override void paint(Canvas canvas, Size size) { - final double radius = size.shortestSide / 2.0; - final Offset center = Offset(size.width / 2.0, size.height / 2.0); + final double dialRadius = clampDouble(size.shortestSide / 2, _kTimePickerDialMinRadius + dotRadius, double.infinity); + final double labelRadius = clampDouble(dialRadius - _kTimePickerDialPadding, _kTimePickerDialMinRadius, double.infinity); + final double innerLabelRadius = clampDouble(labelRadius - _kTimePickerInnerDialOffset, 0, double.infinity); + final double handleRadius = clampDouble(labelRadius - (radius < 0.5 ? 1 : 0) * (labelRadius - innerLabelRadius), _kTimePickerDialMinRadius, double.infinity); + final Offset center = Offset(size.width / 2, size.height / 2); final Offset centerPoint = center; - canvas.drawCircle(centerPoint, radius, Paint()..color = backgroundColor); + canvas.drawCircle(centerPoint, dialRadius, Paint()..color = backgroundColor); - final double labelRadius = radius - _labelPadding; - Offset getOffsetForTheta(double theta) { - return center + Offset(labelRadius * math.cos(theta), -labelRadius * math.sin(theta)); + Offset getOffsetForTheta(double theta, double radius) { + return center + Offset(radius * math.cos(theta), -radius * math.sin(theta)); } - void paintLabels(List<_TappableLabel>? labels) { - if (labels == null) { + void paintLabels(List<_TappableLabel> labels, double radius) { + if (labels.isEmpty) { return; } final double labelThetaIncrement = -_kTwoPi / labels.length; - double labelTheta = math.pi / 2.0; + double labelTheta = math.pi / 2; for (final _TappableLabel label in labels) { final TextPainter labelPainter = label.painter; - final Offset labelOffset = Offset(-labelPainter.width / 2.0, -labelPainter.height / 2.0); - labelPainter.paint(canvas, getOffsetForTheta(labelTheta) + labelOffset); + final Offset labelOffset = Offset(-labelPainter.width / 2, -labelPainter.height / 2); + labelPainter.paint(canvas, getOffsetForTheta(labelTheta, radius) + labelOffset); labelTheta += labelThetaIncrement; } } - paintLabels(primaryLabels); + void paintInnerOuterLabels(List<_TappableLabel>? labels) { + if (labels == null) { + return; + } + + paintLabels(labels.where((_TappableLabel label) => !label.inner).toList(), labelRadius); + paintLabels(labels.where((_TappableLabel label) => label.inner).toList(), innerLabelRadius); + } + + paintInnerOuterLabels(primaryLabels); - final Paint selectorPaint = Paint() - ..color = accentColor; - final Offset focusedPoint = getOffsetForTheta(theta); - const double focusedRadius = _labelPadding - 4.0; - canvas.drawCircle(centerPoint, 4.0, selectorPaint); - canvas.drawCircle(focusedPoint, focusedRadius, selectorPaint); - selectorPaint.strokeWidth = 2.0; + final Paint selectorPaint = Paint()..color = handColor; + final Offset focusedPoint = getOffsetForTheta(theta, handleRadius); + canvas.drawCircle(centerPoint, centerRadius, selectorPaint); + canvas.drawCircle(focusedPoint, dotRadius, selectorPaint); + selectorPaint.strokeWidth = handWidth; canvas.drawLine(centerPoint, focusedPoint, selectorPaint); // Add a dot inside the selector but only when it isn't over the labels. @@ -929,43 +997,52 @@ class _DialPainter extends CustomPainter { // labels. The values were derived by manually testing the dial. final double labelThetaIncrement = -_kTwoPi / primaryLabels.length; if (theta % labelThetaIncrement > 0.1 && theta % labelThetaIncrement < 0.45) { - canvas.drawCircle(focusedPoint, 2.0, selectorPaint..color = dotColor); + canvas.drawCircle(focusedPoint, 2, selectorPaint..color = dotColor); } final Rect focusedRect = Rect.fromCircle( - center: focusedPoint, radius: focusedRadius, + center: focusedPoint, + radius: dotRadius, ); canvas ..save() ..clipPath(Path()..addOval(focusedRect)); - paintLabels(secondaryLabels); + paintInnerOuterLabels(selectedLabels); canvas.restore(); } @override bool shouldRepaint(_DialPainter oldPainter) { return oldPainter.primaryLabels != primaryLabels - || oldPainter.secondaryLabels != secondaryLabels + || oldPainter.selectedLabels != selectedLabels || oldPainter.backgroundColor != backgroundColor - || oldPainter.accentColor != accentColor + || oldPainter.handColor != handColor || oldPainter.theta != theta; } } +// Which kind of hour dial being presented. +enum _HourDialType { + twentyFourHour, + twentyFourHourDoubleRing, + twelveHour, +} + class _Dial extends StatefulWidget { const _Dial({ required this.selectedTime, - required this.mode, - required this.use24HourDials, + required this.hourMinuteMode, + required this.hourDialType, required this.onChanged, required this.onHourSelected, }) : assert(selectedTime != null), - assert(mode != null), - assert(use24HourDials != null); + assert(hourMinuteMode != null), + assert(hourMinuteMode != null), + assert(hourDialType != null); final TimeOfDay selectedTime; - final _TimePickerMode mode; - final bool use24HourDials; + final _HourMinuteMode hourMinuteMode; + final _HourDialType hourDialType; final ValueChanged? onChanged; final VoidCallback? onHourSelected; @@ -974,103 +1051,174 @@ class _Dial extends StatefulWidget { } class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { + late ThemeData themeData; + late MaterialLocalizations localizations; + _DialPainter? painter; + late AnimationController _animationController; + late Tween _thetaTween; + late Animation _theta; + late Tween _radiusTween; + late Animation _radius; + bool _dragging = false; + @override void initState() { super.initState(); - _thetaController = AnimationController( + _animationController = AnimationController( duration: _kDialAnimateDuration, vsync: this, ); _thetaTween = Tween(begin: _getThetaForTime(widget.selectedTime)); - _theta = _thetaController + _radiusTween = Tween(begin: _getRadiusForTime(widget.selectedTime)); + _theta = _animationController .drive(CurveTween(curve: standardEasing)) .drive(_thetaTween) ..addListener(() => setState(() { /* _theta.value has changed */ })); + _radius = _animationController + .drive(CurveTween(curve: standardEasing)) + .drive(_radiusTween) + ..addListener(() => setState(() { /* _radius.value has changed */ })); } - late ThemeData themeData; - late MaterialLocalizations localizations; - late bool alwaysUse24HourFormat; - _DialPainter? painter; - @override void didChangeDependencies() { super.didChangeDependencies(); assert(debugCheckHasMediaQuery(context)); themeData = Theme.of(context); localizations = MaterialLocalizations.of(context); - alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); } @override void didUpdateWidget(_Dial oldWidget) { super.didUpdateWidget(oldWidget); - if (widget.mode != oldWidget.mode || widget.selectedTime != oldWidget.selectedTime) { + if (widget.hourMinuteMode != oldWidget.hourMinuteMode || widget.selectedTime != oldWidget.selectedTime) { if (!_dragging) { - _animateTo(_getThetaForTime(widget.selectedTime)); + _animateTo(_getThetaForTime(widget.selectedTime), _getRadiusForTime(widget.selectedTime)); } } } @override void dispose() { - _thetaController.dispose(); + _animationController.dispose(); painter?.dispose(); super.dispose(); } - late Tween _thetaTween; - late Animation _theta; - late AnimationController _thetaController; - bool _dragging = false; - static double _nearest(double target, double a, double b) { return ((target - a).abs() < (target - b).abs()) ? a : b; } - void _animateTo(double targetTheta) { - final double currentTheta = _theta.value; - double beginTheta = _nearest(targetTheta, currentTheta, currentTheta + _kTwoPi); - beginTheta = _nearest(targetTheta, beginTheta, currentTheta - _kTwoPi); - _thetaTween - ..begin = beginTheta - ..end = targetTheta; - _thetaController - ..value = 0.0 - ..forward(); + void _animateTo(double targetTheta, double targetRadius) { + void animateToValue({ + required double target, + required Animation animation, + required Tween tween, + required AnimationController controller, + required double min, + required double max, + }) { + double beginValue = _nearest(target, animation.value, max); + beginValue = _nearest(target, beginValue, min); + tween + ..begin = beginValue + ..end = target; + controller + ..value = 0 + ..forward(); + } + + animateToValue( + target: targetTheta, + animation: _theta, + tween: _thetaTween, + controller: _animationController, + min: _theta.value - _kTwoPi, + max: _theta.value + _kTwoPi, + ); + animateToValue( + target: targetRadius, + animation: _radius, + tween: _radiusTween, + controller: _animationController, + min: 0, + max: 1, + ); + } + + double _getRadiusForTime(TimeOfDay time) { + switch (widget.hourMinuteMode) { + case _HourMinuteMode.hour: + switch (widget.hourDialType) { + case _HourDialType.twentyFourHourDoubleRing: + return time.hour >= 12 ? 0 : 1; + case _HourDialType.twentyFourHour: + case _HourDialType.twelveHour: + return 1; + } + case _HourMinuteMode.minute: + return 1; + } } double _getThetaForTime(TimeOfDay time) { - final int hoursFactor = widget.use24HourDials ? TimeOfDay.hoursPerDay : TimeOfDay.hoursPerPeriod; - final double fraction = widget.mode == _TimePickerMode.hour - ? (time.hour / hoursFactor) % hoursFactor - : (time.minute / TimeOfDay.minutesPerHour) % TimeOfDay.minutesPerHour; - return (math.pi / 2.0 - fraction * _kTwoPi) % _kTwoPi; - } - - TimeOfDay _getTimeForTheta(double theta, {bool roundMinutes = false}) { - final double fraction = (0.25 - (theta % _kTwoPi) / _kTwoPi) % 1.0; - if (widget.mode == _TimePickerMode.hour) { - int newHour; - if (widget.use24HourDials) { - newHour = (fraction * TimeOfDay.hoursPerDay).round() % TimeOfDay.hoursPerDay; - } else { - newHour = (fraction * TimeOfDay.hoursPerPeriod).round() % TimeOfDay.hoursPerPeriod; - newHour = newHour + widget.selectedTime.periodOffset; - } - return widget.selectedTime.replacing(hour: newHour); - } else { - int minute = (fraction * TimeOfDay.minutesPerHour).round() % TimeOfDay.minutesPerHour; - if (roundMinutes) { - // Round the minutes to nearest 5 minute interval. - minute = ((minute + 2) ~/ 5) * 5 % TimeOfDay.minutesPerHour; - } - return widget.selectedTime.replacing(minute: minute); + final int hoursFactor; + switch (widget.hourDialType) { + case _HourDialType.twentyFourHour: + hoursFactor = TimeOfDay.hoursPerDay; + break; + case _HourDialType.twentyFourHourDoubleRing: + hoursFactor = TimeOfDay.hoursPerPeriod; + break; + case _HourDialType.twelveHour: + hoursFactor = TimeOfDay.hoursPerPeriod; + break; + } + final double fraction; + switch (widget.hourMinuteMode) { + case _HourMinuteMode.hour: + fraction = (time.hour / hoursFactor) % hoursFactor; + break; + case _HourMinuteMode.minute: + fraction = (time.minute / TimeOfDay.minutesPerHour) % TimeOfDay.minutesPerHour; + break; + } + return (math.pi / 2 - fraction * _kTwoPi) % _kTwoPi; + } + + TimeOfDay _getTimeForTheta(double theta, {bool roundMinutes = false, required double radius}) { + final double fraction = (0.25 - (theta % _kTwoPi) / _kTwoPi) % 1; + switch (widget.hourMinuteMode) { + case _HourMinuteMode.hour: + int newHour; + switch (widget.hourDialType) { + case _HourDialType.twentyFourHour: + newHour = (fraction * TimeOfDay.hoursPerDay).round() % TimeOfDay.hoursPerDay; + break; + case _HourDialType.twentyFourHourDoubleRing: + newHour = (fraction * TimeOfDay.hoursPerPeriod).round() % TimeOfDay.hoursPerPeriod; + if (radius < 0.5) { + newHour = newHour + TimeOfDay.hoursPerPeriod; + } + break; + case _HourDialType.twelveHour: + newHour = (fraction * TimeOfDay.hoursPerPeriod).round() % TimeOfDay.hoursPerPeriod; + newHour = newHour + widget.selectedTime.periodOffset; + break; + } + return widget.selectedTime.replacing(hour: newHour); + case _HourMinuteMode.minute: + int minute = (fraction * TimeOfDay.minutesPerHour).round() % TimeOfDay.minutesPerHour; + if (roundMinutes) { + // Round the minutes to nearest 5 minute interval. + minute = ((minute + 2) ~/ 5) * 5 % TimeOfDay.minutesPerHour; + } + return widget.selectedTime.replacing(minute: minute); } } TimeOfDay _notifyOnChangedIfNeeded({ bool roundMinutes = false }) { - final TimeOfDay current = _getTimeForTheta(_theta.value, roundMinutes: roundMinutes); + final TimeOfDay current = _getTimeForTheta(_theta.value, roundMinutes: roundMinutes, radius: _radius.value); if (widget.onChanged == null) { return current; } @@ -1083,25 +1231,34 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { void _updateThetaForPan({ bool roundMinutes = false }) { setState(() { final Offset offset = _position! - _center!; - double angle = (math.atan2(offset.dx, offset.dy) - math.pi / 2.0) % _kTwoPi; + final double labelRadius = _dialSize!.shortestSide / 2 - _kTimePickerDialPadding; + final double innerRadius = labelRadius - _kTimePickerInnerDialOffset; + double angle = (math.atan2(offset.dx, offset.dy) - math.pi / 2) % _kTwoPi; + final double radius = clampDouble((offset.distance - innerRadius) / _kTimePickerInnerDialOffset, 0, 1); if (roundMinutes) { - angle = _getThetaForTime(_getTimeForTheta(angle, roundMinutes: roundMinutes)); + angle = _getThetaForTime(_getTimeForTheta(angle, roundMinutes: roundMinutes, radius: radius)); } + // The controller doesn't animate during the pan gesture. _thetaTween ..begin = angle - ..end = angle; // The controller doesn't animate during the pan gesture. + ..end = angle; + _radiusTween + ..begin = radius + ..end = radius; }); } Offset? _position; Offset? _center; + Size? _dialSize; void _handlePanStart(DragStartDetails details) { assert(!_dragging); _dragging = true; final RenderBox box = context.findRenderObject()! as RenderBox; _position = box.globalToLocal(details.globalPosition); - _center = box.size.center(Offset.zero); + _dialSize = box.size; + _center = _dialSize!.center(Offset.zero); _updateThetaForPan(); _notifyOnChangedIfNeeded(); } @@ -1117,8 +1274,9 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { _dragging = false; _position = null; _center = null; - _animateTo(_getThetaForTime(widget.selectedTime)); - if (widget.mode == _TimePickerMode.hour) { + _dialSize = null; + _animateTo(_getThetaForTime(widget.selectedTime), _getRadiusForTime(widget.selectedTime)); + if (widget.hourMinuteMode == _HourMinuteMode.hour) { widget.onHourSelected?.call(); } } @@ -1127,36 +1285,60 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { final RenderBox box = context.findRenderObject()! as RenderBox; _position = box.globalToLocal(details.globalPosition); _center = box.size.center(Offset.zero); + _dialSize = box.size; _updateThetaForPan(roundMinutes: true); final TimeOfDay newTime = _notifyOnChangedIfNeeded(roundMinutes: true); - if (widget.mode == _TimePickerMode.hour) { - if (widget.use24HourDials) { - _announceToAccessibility(context, localizations.formatDecimal(newTime.hour)); - } else { - _announceToAccessibility(context, localizations.formatDecimal(newTime.hourOfPeriod)); + if (widget.hourMinuteMode == _HourMinuteMode.hour) { + switch (widget.hourDialType) { + case _HourDialType.twentyFourHour: + case _HourDialType.twentyFourHourDoubleRing: + _announceToAccessibility(context, localizations.formatDecimal(newTime.hour)); + break; + case _HourDialType.twelveHour: + _announceToAccessibility(context, localizations.formatDecimal(newTime.hourOfPeriod)); + break; } widget.onHourSelected?.call(); } else { _announceToAccessibility(context, localizations.formatDecimal(newTime.minute)); } - _animateTo(_getThetaForTime(_getTimeForTheta(_theta.value, roundMinutes: true))); + final TimeOfDay time = _getTimeForTheta(_theta.value, roundMinutes: true, radius: _radius.value); + _animateTo(_getThetaForTime(time), _getRadiusForTime(time)); _dragging = false; _position = null; _center = null; + _dialSize = null; } void _selectHour(int hour) { _announceToAccessibility(context, localizations.formatDecimal(hour)); final TimeOfDay time; - if (widget.mode == _TimePickerMode.hour && widget.use24HourDials) { - time = TimeOfDay(hour: hour, minute: widget.selectedTime.minute); - } else { - if (widget.selectedTime.period == DayPeriod.am) { - time = TimeOfDay(hour: hour, minute: widget.selectedTime.minute); - } else { - time = TimeOfDay(hour: hour + TimeOfDay.hoursPerPeriod, minute: widget.selectedTime.minute); + + TimeOfDay getAmPmTime() { + switch (widget.selectedTime.period) { + case DayPeriod.am: + return TimeOfDay(hour: hour, minute: widget.selectedTime.minute); + case DayPeriod.pm: + return TimeOfDay(hour: hour + TimeOfDay.hoursPerPeriod, minute: widget.selectedTime.minute); } } + + switch (widget.hourMinuteMode) { + case _HourMinuteMode.hour: + switch (widget.hourDialType) { + case _HourDialType.twentyFourHour: + case _HourDialType.twentyFourHourDoubleRing: + time = TimeOfDay(hour: hour, minute: widget.selectedTime.minute); + break; + case _HourDialType.twelveHour: + time = getAmPmTime(); + break; + } + break; + case _HourMinuteMode.minute: + time = getAmPmTime(); + break; + } final double angle = _getThetaForTime(time); _thetaTween ..begin = angle @@ -1192,28 +1374,63 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { TimeOfDay(hour: 11, minute: 0), ]; + // On M2, there's no inner ring of numbers. + static const List _twentyFourHoursM2 = [ + TimeOfDay(hour: 0, minute: 0), + TimeOfDay(hour: 2, minute: 0), + TimeOfDay(hour: 4, minute: 0), + TimeOfDay(hour: 6, minute: 0), + TimeOfDay(hour: 8, minute: 0), + TimeOfDay(hour: 10, minute: 0), + TimeOfDay(hour: 12, minute: 0), + TimeOfDay(hour: 14, minute: 0), + TimeOfDay(hour: 16, minute: 0), + TimeOfDay(hour: 18, minute: 0), + TimeOfDay(hour: 20, minute: 0), + TimeOfDay(hour: 22, minute: 0), + ]; + static const List _twentyFourHours = [ TimeOfDay(hour: 0, minute: 0), + TimeOfDay(hour: 1, minute: 0), TimeOfDay(hour: 2, minute: 0), + TimeOfDay(hour: 3, minute: 0), TimeOfDay(hour: 4, minute: 0), + TimeOfDay(hour: 5, minute: 0), TimeOfDay(hour: 6, minute: 0), + TimeOfDay(hour: 7, minute: 0), TimeOfDay(hour: 8, minute: 0), + TimeOfDay(hour: 9, minute: 0), TimeOfDay(hour: 10, minute: 0), + TimeOfDay(hour: 11, minute: 0), TimeOfDay(hour: 12, minute: 0), + TimeOfDay(hour: 13, minute: 0), TimeOfDay(hour: 14, minute: 0), + TimeOfDay(hour: 15, minute: 0), TimeOfDay(hour: 16, minute: 0), + TimeOfDay(hour: 17, minute: 0), TimeOfDay(hour: 18, minute: 0), + TimeOfDay(hour: 19, minute: 0), TimeOfDay(hour: 20, minute: 0), + TimeOfDay(hour: 21, minute: 0), TimeOfDay(hour: 22, minute: 0), + TimeOfDay(hour: 23, minute: 0), ]; - _TappableLabel _buildTappableLabel(TextTheme textTheme, Color color, int value, String label, VoidCallback onTap) { - final TextStyle style = textTheme.bodyLarge!.copyWith(color: color); - final double labelScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 2.0); + _TappableLabel _buildTappableLabel({ + required TextStyle? textStyle, + required int selectedValue, + required int value, + required bool inner, + required String label, + required VoidCallback onTap, + }) { + final double labelScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 2); return _TappableLabel( value: value, + inner: inner, painter: TextPainter( - text: TextSpan(style: style, text: label), + text: TextSpan(style: textStyle, text: label), textDirection: TextDirection.ltr, textScaleFactor: labelScaleFactor, )..layout(), @@ -1221,33 +1438,63 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { ); } - List<_TappableLabel> _build24HourRing(TextTheme textTheme, Color color) => <_TappableLabel>[ - for (final TimeOfDay timeOfDay in _twentyFourHours) - _buildTappableLabel( - textTheme, - color, - timeOfDay.hour, - localizations.formatHour(timeOfDay, alwaysUse24HourFormat: alwaysUse24HourFormat), - () { - _selectHour(timeOfDay.hour); - }, - ), - ]; + List<_TappableLabel> _build24HourRing({ + required TextStyle? textStyle, + required int selectedValue, + }) { + return <_TappableLabel>[ + if (themeData.useMaterial3) + for (final TimeOfDay timeOfDay in _twentyFourHours) + _buildTappableLabel( + textStyle: textStyle, + selectedValue: selectedValue, + inner: timeOfDay.hour >= 12, + value: timeOfDay.hour, + label: timeOfDay.hour != 0 + ? '${timeOfDay.hour}' + : localizations.formatHour(timeOfDay, alwaysUse24HourFormat: true), + onTap: () { + _selectHour(timeOfDay.hour); + }, + ), + if (!themeData.useMaterial3) + for (final TimeOfDay timeOfDay in _twentyFourHoursM2) + _buildTappableLabel( + textStyle: textStyle, + selectedValue: selectedValue, + inner: false, + value: timeOfDay.hour, + label: localizations.formatHour(timeOfDay, alwaysUse24HourFormat: true), + onTap: () { + _selectHour(timeOfDay.hour); + }, + ), + ]; + } - List<_TappableLabel> _build12HourRing(TextTheme textTheme, Color color) => <_TappableLabel>[ - for (final TimeOfDay timeOfDay in _amHours) - _buildTappableLabel( - textTheme, - color, - timeOfDay.hour, - localizations.formatHour(timeOfDay, alwaysUse24HourFormat: alwaysUse24HourFormat), - () { - _selectHour(timeOfDay.hour); - }, - ), - ]; + List<_TappableLabel> _build12HourRing({ + required TextStyle? textStyle, + required int selectedValue, + }) { + return <_TappableLabel>[ + for (final TimeOfDay timeOfDay in _amHours) + _buildTappableLabel( + textStyle: textStyle, + selectedValue: selectedValue, + inner: false, + value: timeOfDay.hour, + label: localizations.formatHour(timeOfDay, alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)), + onTap: () { + _selectHour(timeOfDay.hour); + }, + ), + ]; + } - List<_TappableLabel> _buildMinutes(TextTheme textTheme, Color color) { + List<_TappableLabel> _buildMinutes({ + required TextStyle? textStyle, + required int selectedValue, + }) { const List minuteMarkerValues = [ TimeOfDay(hour: 0, minute: 0), TimeOfDay(hour: 0, minute: 5), @@ -1266,11 +1513,12 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { return <_TappableLabel>[ for (final TimeOfDay timeOfDay in minuteMarkerValues) _buildTappableLabel( - textTheme, - color, - timeOfDay.minute, - localizations.formatMinute(timeOfDay), - () { + textStyle: textStyle, + selectedValue: selectedValue, + inner: false, + value: timeOfDay.minute, + label: localizations.formatMinute(timeOfDay), + onTap: () { _selectMinute(timeOfDay.minute); }, ), @@ -1280,42 +1528,79 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); - final TimePickerThemeData pickerTheme = TimePickerTheme.of(context); - final Color backgroundColor = pickerTheme.dialBackgroundColor ?? themeData.colorScheme.onBackground.withOpacity(0.12); - final Color accentColor = pickerTheme.dialHandColor ?? themeData.colorScheme.primary; - final Color primaryLabelColor = MaterialStateProperty.resolveAs(pickerTheme.dialTextColor, {}) ?? themeData.colorScheme.onSurface; - final Color secondaryLabelColor = MaterialStateProperty.resolveAs(pickerTheme.dialTextColor, {MaterialState.selected}) ?? themeData.colorScheme.onPrimary; + final TimePickerThemeData timePickerTheme = TimePickerTheme.of(context); + final _TimePickerDefaults defaultTheme = theme.useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + final Color backgroundColor = timePickerTheme.dialBackgroundColor ?? defaultTheme.dialBackgroundColor; + final Color dialHandColor = timePickerTheme.dialHandColor ?? defaultTheme.dialHandColor; + final TextStyle labelStyle = timePickerTheme.dialTextStyle ?? defaultTheme.dialTextStyle; + final Color dialTextUnselectedColor = MaterialStateProperty + .resolveAs(timePickerTheme.dialTextColor ?? defaultTheme.dialTextColor, { }); + final Color dialTextSelectedColor = MaterialStateProperty + .resolveAs(timePickerTheme.dialTextColor ?? defaultTheme.dialTextColor, { MaterialState.selected }); + final TextStyle resolvedUnselectedLabelStyle = labelStyle.copyWith(color: dialTextUnselectedColor); + final TextStyle resolvedSelectedLabelStyle = labelStyle.copyWith(color: dialTextSelectedColor); + final Color dotColor = dialTextSelectedColor; + List<_TappableLabel> primaryLabels; - List<_TappableLabel> secondaryLabels; + List<_TappableLabel> selectedLabels; final int selectedDialValue; - switch (widget.mode) { - case _TimePickerMode.hour: - if (widget.use24HourDials) { - selectedDialValue = widget.selectedTime.hour; - primaryLabels = _build24HourRing(theme.textTheme, primaryLabelColor); - secondaryLabels = _build24HourRing(theme.textTheme, secondaryLabelColor); - } else { - selectedDialValue = widget.selectedTime.hourOfPeriod; - primaryLabels = _build12HourRing(theme.textTheme, primaryLabelColor); - secondaryLabels = _build12HourRing(theme.textTheme, secondaryLabelColor); + final double radiusValue; + switch (widget.hourMinuteMode) { + case _HourMinuteMode.hour: + switch (widget.hourDialType) { + case _HourDialType.twentyFourHour: + case _HourDialType.twentyFourHourDoubleRing: + selectedDialValue = widget.selectedTime.hour; + primaryLabels = _build24HourRing( + textStyle: resolvedUnselectedLabelStyle, + selectedValue: selectedDialValue, + ); + selectedLabels = _build24HourRing( + textStyle: resolvedSelectedLabelStyle, + selectedValue: selectedDialValue, + ); + radiusValue = theme.useMaterial3 ? _radius.value : 1; + break; + case _HourDialType.twelveHour: + selectedDialValue = widget.selectedTime.hourOfPeriod; + primaryLabels = _build12HourRing( + textStyle: resolvedUnselectedLabelStyle, + selectedValue: selectedDialValue, + ); + selectedLabels = _build12HourRing( + textStyle: resolvedSelectedLabelStyle, + selectedValue: selectedDialValue, + ); + radiusValue = 1; + break; } break; - case _TimePickerMode.minute: + case _HourMinuteMode.minute: selectedDialValue = widget.selectedTime.minute; - primaryLabels = _buildMinutes(theme.textTheme, primaryLabelColor); - secondaryLabels = _buildMinutes(theme.textTheme, secondaryLabelColor); + primaryLabels = _buildMinutes( + textStyle: resolvedUnselectedLabelStyle, + selectedValue: selectedDialValue, + ); + selectedLabels = _buildMinutes( + textStyle: resolvedSelectedLabelStyle, + selectedValue: selectedDialValue, + ); + radiusValue = 1; break; } - painter?.dispose(); painter = _DialPainter( selectedValue: selectedDialValue, primaryLabels: primaryLabels, - secondaryLabels: secondaryLabels, + selectedLabels: selectedLabels, backgroundColor: backgroundColor, - accentColor: accentColor, - dotColor: theme.colorScheme.surface, + handColor: dialHandColor, + handWidth: defaultTheme.handWidth, + dotColor: dotColor, + dotRadius: defaultTheme.dotRadius, + centerRadius: defaultTheme.centerRadius, theta: _theta.value, + radius: radiusValue, textDirection: Directionality.of(context), ); @@ -1336,23 +1621,18 @@ class _DialState extends State<_Dial> with SingleTickerProviderStateMixin { class _TimePickerInput extends StatefulWidget { const _TimePickerInput({ required this.initialSelectedTime, - required this.helpText, required this.errorInvalidText, required this.hourLabelText, required this.minuteLabelText, + required this.helpText, required this.autofocusHour, required this.autofocusMinute, - required this.onChanged, this.restorationId, - }) : assert(initialSelectedTime != null), - assert(onChanged != null); + }); /// The time initially selected when the dialog is shown. final TimeOfDay initialSelectedTime; - /// Optionally provide your own help text to the time picker. - final String? helpText; - /// Optionally provide your own validation error text. final String? errorInvalidText; @@ -1362,12 +1642,12 @@ class _TimePickerInput extends StatefulWidget { /// Optionally provide your own minute label text. final String? minuteLabelText; + final String helpText; + final bool? autofocusHour; final bool? autofocusMinute; - final ValueChanged onChanged; - /// Restoration ID to save and restore the state of the time picker input /// widget. /// @@ -1412,8 +1692,8 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi } } else { if (newHour > 0 && newHour < 13) { - if ((_selectedTime.value.period == DayPeriod.pm && newHour != 12) - || (_selectedTime.value.period == DayPeriod.am && newHour == 12)) { + if ((_selectedTime.value.period == DayPeriod.pm && newHour != 12) || + (_selectedTime.value.period == DayPeriod.am && newHour == 12)) { newHour = (newHour + TimeOfDay.hoursPerPeriod) % TimeOfDay.hoursPerDay; } return newHour; @@ -1442,7 +1722,7 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi final int? newHour = _parseHour(value); if (newHour != null) { _selectedTime.value = TimeOfDay(hour: newHour, minute: _selectedTime.value.minute); - widget.onChanged(_selectedTime.value); + _TimePickerModel.setSelectedTime(context, _selectedTime.value); FocusScope.of(context).requestFocus(); } } @@ -1459,14 +1739,14 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi final int? newMinute = _parseMinute(value); if (newMinute != null) { _selectedTime.value = TimeOfDay(hour: _selectedTime.value.hour, minute: int.parse(value!)); - widget.onChanged(_selectedTime.value); + _TimePickerModel.setSelectedTime(context, _selectedTime.value); FocusScope.of(context).unfocus(); } } void _handleDayPeriodChanged(TimeOfDay value) { _selectedTime.value = value; - widget.onChanged(_selectedTime.value); + _TimePickerModel.setSelectedTime(context, _selectedTime.value); } String? _validateHour(String? value) { @@ -1494,35 +1774,32 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); + final TimeOfDayFormat timeOfDayFormat = MaterialLocalizations.of(context).timeOfDayFormat(alwaysUse24HourFormat: _TimePickerModel.use24HourFormatOf(context)); final bool use24HourDials = hourFormat(of: timeOfDayFormat) != HourFormat.h; final ThemeData theme = Theme.of(context); - final TextStyle hourMinuteStyle = TimePickerTheme.of(context).hourMinuteTextStyle ?? theme.textTheme.displayMedium!; - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final String timePickerInputHelpText = theme.useMaterial3 - ? localizations.timePickerInputHelpText - : localizations.timePickerInputHelpText.toUpperCase(); + final TimePickerThemeData timePickerTheme = _TimePickerModel.themeOf(context); + final _TimePickerDefaults defaultTheme = _TimePickerModel.defaultThemeOf(context); + final TextStyle hourMinuteStyle = timePickerTheme.hourMinuteTextStyle ?? defaultTheme.hourMinuteTextStyle; return Padding( - padding: const EdgeInsets.symmetric(horizontal: 24.0, vertical: 16.0), + padding: _TimePickerModel.useMaterial3Of(context) ? EdgeInsets.zero : const EdgeInsets.symmetric(horizontal: 16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text( - widget.helpText ?? timePickerInputHelpText, - style: TimePickerTheme.of(context).helpTextStyle ?? theme.textTheme.labelSmall, + Padding(padding: EdgeInsetsDirectional.only(bottom: _TimePickerModel.useMaterial3Of(context) ? 20 : 24), + child: Text( + widget.helpText, + style: _TimePickerModel.themeOf(context).helpTextStyle ?? _TimePickerModel.defaultThemeOf(context).helpTextStyle, + ), ), - const SizedBox(height: 16.0), Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ if (!use24HourDials && timeOfDayFormat == TimeOfDayFormat.a_space_h_colon_mm) ...[ - _DayPeriodControl( - selectedTime: _selectedTime.value, - orientation: Orientation.portrait, - onChanged: _handleDayPeriodChanged, + Padding( + padding: const EdgeInsetsDirectional.only(end: 12), + child: _DayPeriodControl(onPeriodChanged: _handleDayPeriodChanged), ), - const SizedBox(width: 12.0), ], Expanded( child: Row( @@ -1534,19 +1811,20 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 8.0), - _HourTextField( - restorationId: 'hour_text_field', - selectedTime: _selectedTime.value, - style: hourMinuteStyle, - autofocus: widget.autofocusHour, - inputAction: TextInputAction.next, - validator: _validateHour, - onSavedSubmitted: _handleHourSavedSubmitted, - onChanged: _handleHourChanged, - hourLabelText: widget.hourLabelText, + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _HourTextField( + restorationId: 'hour_text_field', + selectedTime: _selectedTime.value, + style: hourMinuteStyle, + autofocus: widget.autofocusHour, + inputAction: TextInputAction.next, + validator: _validateHour, + onSavedSubmitted: _handleHourSavedSubmitted, + onChanged: _handleHourChanged, + hourLabelText: widget.hourLabelText, + ), ), - const SizedBox(height: 8.0), if (!hourHasError.value && !minuteHasError.value) ExcludeSemantics( child: Text( @@ -1559,27 +1837,24 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi ], ), ), - Container( - margin: const EdgeInsets.only(top: 8.0), - height: _kTimePickerHeaderControlHeight, - child: _StringFragment(timeOfDayFormat: timeOfDayFormat), - ), + _StringFragment(timeOfDayFormat: timeOfDayFormat), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - const SizedBox(height: 8.0), - _MinuteTextField( - restorationId: 'minute_text_field', - selectedTime: _selectedTime.value, - style: hourMinuteStyle, - autofocus: widget.autofocusMinute, - inputAction: TextInputAction.done, - validator: _validateMinute, - onSavedSubmitted: _handleMinuteSavedSubmitted, - minuteLabelText: widget.minuteLabelText, + Padding( + padding: const EdgeInsets.only(bottom: 10), + child: _MinuteTextField( + restorationId: 'minute_text_field', + selectedTime: _selectedTime.value, + style: hourMinuteStyle, + autofocus: widget.autofocusMinute, + inputAction: TextInputAction.done, + validator: _validateMinute, + onSavedSubmitted: _handleMinuteSavedSubmitted, + minuteLabelText: widget.minuteLabelText, + ), ), - const SizedBox(height: 8.0), if (!hourHasError.value && !minuteHasError.value) ExcludeSemantics( child: Text( @@ -1596,11 +1871,9 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi ), ), if (!use24HourDials && timeOfDayFormat != TimeOfDayFormat.a_space_h_colon_mm) ...[ - const SizedBox(width: 12.0), - _DayPeriodControl( - selectedTime: _selectedTime.value, - orientation: Orientation.portrait, - onChanged: _handleDayPeriodChanged, + Padding( + padding: const EdgeInsetsDirectional.only(start: 12), + child: _DayPeriodControl(onPeriodChanged: _handleDayPeriodChanged), ), ], ], @@ -1611,7 +1884,7 @@ class _TimePickerInputState extends State<_TimePickerInput> with RestorationMixi style: theme.textTheme.bodyMedium!.copyWith(color: theme.colorScheme.error), ) else - const SizedBox(height: 2.0), + const SizedBox(height: 2), ], ), ); @@ -1650,7 +1923,7 @@ class _HourTextField extends StatelessWidget { autofocus: autofocus, inputAction: inputAction, style: style, - semanticHintText: hourLabelText ?? MaterialLocalizations.of(context).timePickerHourLabel, + semanticHintText: hourLabelText ?? MaterialLocalizations.of(context).timePickerHourLabel, validator: validator, onSavedSubmitted: onSavedSubmitted, onChanged: onChanged, @@ -1732,9 +2005,12 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora @override void initState() { super.initState(); - focusNode = FocusNode()..addListener(() { - setState(() { }); // Rebuild. - }); + focusNode = FocusNode() + ..addListener(() { + setState(() { + // Rebuild when focus changes. + }); + }); } @override @@ -1760,62 +2036,73 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora String get _formattedValue { final bool alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); final MaterialLocalizations localizations = MaterialLocalizations.of(context); - return !widget.isHour ? localizations.formatMinute(widget.selectedTime) : localizations.formatHour( - widget.selectedTime, - alwaysUse24HourFormat: alwaysUse24HourFormat, - ); + return !widget.isHour + ? localizations.formatMinute(widget.selectedTime) + : localizations.formatHour( + widget.selectedTime, + alwaysUse24HourFormat: alwaysUse24HourFormat, + ); } @override Widget build(BuildContext context) { final ThemeData theme = Theme.of(context); final TimePickerThemeData timePickerTheme = TimePickerTheme.of(context); - final ColorScheme colorScheme = theme.colorScheme; + final _TimePickerDefaults defaultTheme = theme.useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + final bool alwaysUse24HourFormat = MediaQuery.alwaysUse24HourFormatOf(context); - final InputDecorationTheme? inputDecorationTheme = timePickerTheme.inputDecorationTheme; - InputDecoration inputDecoration; - if (inputDecorationTheme != null) { - inputDecoration = const InputDecoration().applyDefaults(inputDecorationTheme); - } else { - inputDecoration = InputDecoration( - contentPadding: EdgeInsets.zero, - filled: true, - enabledBorder: const OutlineInputBorder( - borderSide: BorderSide(color: Colors.transparent), - ), - errorBorder: OutlineInputBorder( - borderSide: BorderSide(color: colorScheme.error, width: 2.0), - ), - focusedBorder: OutlineInputBorder( - borderSide: BorderSide(color: colorScheme.primary, width: 2.0), - ), - focusedErrorBorder: OutlineInputBorder( - borderSide: BorderSide(color: colorScheme.error, width: 2.0), - ), - hintStyle: widget.style.copyWith(color: colorScheme.onSurface.withOpacity(0.36)), - // TODO(rami-a): Remove this logic once https://github.com/flutter/flutter/issues/54104 is fixed. - errorStyle: const TextStyle(fontSize: 0.0, height: 0.0), // Prevent the error text from appearing. - ); - } - final Color unfocusedFillColor = timePickerTheme.hourMinuteColor ?? colorScheme.onSurface.withOpacity(0.12); + final InputDecorationTheme inputDecorationTheme = timePickerTheme.inputDecorationTheme ?? defaultTheme.inputDecorationTheme; + InputDecoration inputDecoration = const InputDecoration().applyDefaults(inputDecorationTheme); // If screen reader is in use, make the hint text say hours/minutes. // Otherwise, remove the hint text when focused because the centered cursor // appears odd above the hint text. - // - // TODO(rami-a): Once https://github.com/flutter/flutter/issues/67571 is - // resolved, remove the window check for semantics being enabled on web. final String? hintText = MediaQuery.accessibleNavigationOf(context) || WidgetsBinding.instance.window.semanticsEnabled ? widget.semanticHintText : (focusNode.hasFocus ? null : _formattedValue); + + // Because the fill color is specified in both the inputDecorationTheme and + // the TimePickerTheme, if there's one in the user's input decoration theme, + // use that. If not, but there's one in the user's + // timePickerTheme.hourMinuteColor, use that, and otherwise use the default. + // We ignore the value in the fillColor of the input decoration in the + // default theme here, but it's the same as the hourMinuteColor. + final Color startingFillColor = + timePickerTheme.inputDecorationTheme?.fillColor ?? + timePickerTheme.hourMinuteColor ?? + defaultTheme.hourMinuteColor; + final Color fillColor; + if (theme.useMaterial3) { + fillColor = MaterialStateProperty.resolveAs( + startingFillColor, + { + if (focusNode.hasFocus) MaterialState.focused, + if (focusNode.hasFocus) MaterialState.selected, + }, + ); + } else { + fillColor = focusNode.hasFocus ? Colors.transparent : startingFillColor; + } + inputDecoration = inputDecoration.copyWith( hintText: hintText, - fillColor: focusNode.hasFocus ? Colors.transparent : inputDecorationTheme?.fillColor ?? unfocusedFillColor, + fillColor: fillColor, ); - return SizedBox( - height: _kTimePickerHeaderControlHeight, + final Set states = { + if (focusNode.hasFocus) MaterialState.focused, + if (focusNode.hasFocus) MaterialState.selected, + }; + final Color effectiveTextColor = MaterialStateProperty.resolveAs( + timePickerTheme.hourMinuteTextColor ?? defaultTheme.hourMinuteTextColor, + states, + ); + final TextStyle effectiveStyle = MaterialStateProperty.resolveAs(widget.style, states) + .copyWith(color: effectiveTextColor); + + return SizedBox.fromSize( + size: alwaysUse24HourFormat ? defaultTheme.hourMinuteInputSize24Hour : defaultTheme.hourMinuteInputSize, child: MediaQuery( - data: MediaQuery.of(context).copyWith(textScaleFactor: 1.0), + data: MediaQuery.of(context).copyWith(textScaleFactor: 1), child: UnmanagedRestorationScope( bucket: bucket, child: TextFormField( @@ -1830,7 +2117,7 @@ class _HourMinuteTextFieldState extends State<_HourMinuteTextField> with Restora textAlign: TextAlign.center, textInputAction: widget.inputAction, keyboardType: TextInputType.number, - style: widget.style.copyWith(color: timePickerTheme.hourMinuteTextColor ?? colorScheme.onSurface), + style: effectiveStyle, controller: controller.value, decoration: inputDecoration, validator: widget.validator, @@ -1869,15 +2156,13 @@ class TimePickerDialog extends StatefulWidget { this.minuteLabelText, this.restorationId, this.initialEntryMode = TimePickerEntryMode.dial, + this.orientation, this.onEntryModeChanged, }) : assert(initialTime != null); /// The time initially selected when the dialog is shown. final TimeOfDay initialTime; - /// The entry mode for the picker. Whether it's text input or a dial. - final TimePickerEntryMode initialEntryMode; - /// Optionally provide your own text for the cancel button. /// /// If null, the button uses [MaterialLocalizations.cancelButtonLabel]. @@ -1914,6 +2199,22 @@ class TimePickerDialog extends StatefulWidget { /// Flutter. final String? restorationId; + /// The entry mode for the picker. Whether it's text input or a dial. + final TimePickerEntryMode initialEntryMode; + + /// The optional [orientation] parameter sets the [Orientation] to use when + /// displaying the dialog. + /// + /// By default, the orientation is derived from the [MediaQueryData.size] of + /// the ambient [MediaQuery]. If the aspect of the size is tall, then + /// [Orientation.portrait] is used, if the size is wide, then + /// [Orientation.landscape] is used. + /// + /// Use this parameter to override the default and force the dialog to appear + /// in either portrait or landscape mode regardless of the aspect of the + /// [MediaQueryData.size]. + final Orientation? orientation; + /// Callback called when the selected entry mode is changed. final EntryModeChangeCallback? onEntryModeChanged; @@ -1921,270 +2222,91 @@ class TimePickerDialog extends StatefulWidget { State createState() => _TimePickerDialogState(); } -// A restorable [TimePickerEntryMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableTimePickerEntryMode extends RestorableValue { - _RestorableTimePickerEntryMode( - TimePickerEntryMode defaultValue, - ) : _defaultValue = defaultValue; - - final TimePickerEntryMode _defaultValue; - - @override - TimePickerEntryMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(TimePickerEntryMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - TimePickerEntryMode fromPrimitives(Object? data) => TimePickerEntryMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -// A restorable [_RestorableTimePickerEntryMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableTimePickerMode extends RestorableValue<_TimePickerMode> { - _RestorableTimePickerMode( - _TimePickerMode defaultValue, - ) : _defaultValue = defaultValue; - - final _TimePickerMode _defaultValue; - - @override - _TimePickerMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(_TimePickerMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - _TimePickerMode fromPrimitives(Object? data) => _TimePickerMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -// A restorable [AutovalidateMode] value. -// -// This serializes each entry as a unique `int` value. -class _RestorableAutovalidateMode extends RestorableValue { - _RestorableAutovalidateMode( - AutovalidateMode defaultValue, - ) : _defaultValue = defaultValue; - - final AutovalidateMode _defaultValue; - - @override - AutovalidateMode createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(AutovalidateMode? oldValue) { - assert(debugIsSerializableForRestoration(value.index)); - notifyListeners(); - } - - @override - AutovalidateMode fromPrimitives(Object? data) => AutovalidateMode.values[data! as int]; - - @override - Object? toPrimitives() => value.index; -} - -// A restorable [_RestorableTimePickerEntryMode] value. -// -// This serializes each entry as a unique `int` value. -// -// This value can be null. -class _RestorableTimePickerModeN extends RestorableValue<_TimePickerMode?> { - _RestorableTimePickerModeN( - _TimePickerMode? defaultValue, - ) : _defaultValue = defaultValue; - - final _TimePickerMode? _defaultValue; - - @override - _TimePickerMode? createDefaultValue() => _defaultValue; - - @override - void didUpdateValue(_TimePickerMode? oldValue) { - assert(debugIsSerializableForRestoration(value?.index)); - notifyListeners(); - } - - @override - _TimePickerMode fromPrimitives(Object? data) => _TimePickerMode.values[data! as int]; - - @override - Object? toPrimitives() => value?.index; -} - class _TimePickerDialogState extends State with RestorationMixin { + late final RestorableEnum _entryMode = RestorableEnum(widget.initialEntryMode, values: TimePickerEntryMode.values); + late final RestorableTimeOfDay _selectedTime = RestorableTimeOfDay(widget.initialTime); final GlobalKey _formKey = GlobalKey(); + final RestorableEnum _autovalidateMode = RestorableEnum(AutovalidateMode.disabled, values: AutovalidateMode.values); + late final RestorableEnumN _orientation = RestorableEnumN(widget.orientation, values: Orientation.values); - late final _RestorableTimePickerEntryMode _entryMode = _RestorableTimePickerEntryMode(widget.initialEntryMode); - final _RestorableTimePickerMode _mode = _RestorableTimePickerMode(_TimePickerMode.hour); - final _RestorableTimePickerModeN _lastModeAnnounced = _RestorableTimePickerModeN(null); - final _RestorableAutovalidateMode _autovalidateMode = _RestorableAutovalidateMode(AutovalidateMode.disabled); - final RestorableBoolN _autofocusHour = RestorableBoolN(null); - final RestorableBoolN _autofocusMinute = RestorableBoolN(null); - final RestorableBool _announcedInitialTime = RestorableBool(false); - - late final VoidCallback _entryModeListener; + // Base sizes + static const Size _kTimePickerPortraitSize = Size(310, 468); + static const Size _kTimePickerLandscapeSize = Size(524, 342); + static const Size _kTimePickerLandscapeSizeM2 = Size(508, 300); + static const Size _kTimePickerInputSize = Size(312, 216); - @override - void didChangeDependencies() { - super.didChangeDependencies(); - localizations = MaterialLocalizations.of(context); - _announceInitialTimeOnce(); - _announceModeOnce(); - } - - @override - void initState() { - super.initState(); - _entryModeListener = () => widget.onEntryModeChanged?.call(_entryMode.value); - _entryMode.addListener(_entryModeListener); - } + // Absolute minimum dialog sizes, which is the point at which it begins + // scrolling to fit everything in. + static const Size _kTimePickerMinPortraitSize = Size(238, 326); + static const Size _kTimePickerMinLandscapeSize = Size(416, 248); + static const Size _kTimePickerMinInputSize = Size(312, 196); @override String? get restorationId => widget.restorationId; @override void restoreState(RestorationBucket? oldBucket, bool initialRestore) { - registerForRestoration(_entryMode, 'entry_mode'); - registerForRestoration(_mode, 'mode'); - registerForRestoration(_lastModeAnnounced, 'last_mode_announced'); - registerForRestoration(_autovalidateMode, 'autovalidateMode'); - registerForRestoration(_autofocusHour, 'autofocus_hour'); - registerForRestoration(_autofocusMinute, 'autofocus_minute'); - registerForRestoration(_announcedInitialTime, 'announced_initial_time'); registerForRestoration(_selectedTime, 'selected_time'); + registerForRestoration(_entryMode, 'entry_mode'); + registerForRestoration(_autovalidateMode, 'autovalidate_mode'); + registerForRestoration(_orientation, 'orientation'); } - RestorableTimeOfDay get selectedTime => _selectedTime; - late final RestorableTimeOfDay _selectedTime = RestorableTimeOfDay(widget.initialTime); + void _handleTimeChanged(TimeOfDay value) { + if (value != _selectedTime.value) { + setState(() { + _selectedTime.value = value; + }); + } + } - Timer? _vibrateTimer; - late MaterialLocalizations localizations; + void _handleEntryModeChanged(TimePickerEntryMode value) { + if (value != _entryMode.value) { + setState(() { + switch (_entryMode.value) { + case TimePickerEntryMode.dial: + _autovalidateMode.value = AutovalidateMode.disabled; + break; + case TimePickerEntryMode.input: + _formKey.currentState!.save(); + break; + case TimePickerEntryMode.dialOnly: + break; + case TimePickerEntryMode.inputOnly: + break; + } + _entryMode.value = value; + widget.onEntryModeChanged?.call(value); + }); + } + } - void _vibrate() { - switch (Theme.of(context).platform) { - case TargetPlatform.android: - case TargetPlatform.fuchsia: - case TargetPlatform.linux: - case TargetPlatform.windows: - _vibrateTimer?.cancel(); - _vibrateTimer = Timer(_kVibrateCommitDelay, () { - HapticFeedback.vibrate(); - _vibrateTimer = null; - }); + void _toggleEntryMode() { + switch (_entryMode.value) { + case TimePickerEntryMode.dial: + _handleEntryModeChanged(TimePickerEntryMode.input); break; - case TargetPlatform.iOS: - case TargetPlatform.macOS: + case TimePickerEntryMode.input: + _handleEntryModeChanged(TimePickerEntryMode.dial); + break; + case TimePickerEntryMode.dialOnly: + case TimePickerEntryMode.inputOnly: + FlutterError('Can not change entry mode from $_entryMode'); break; } } - void _handleModeChanged(_TimePickerMode mode) { - _vibrate(); - setState(() { - _mode.value = mode; - _announceModeOnce(); - }); - } - - void _handleEntryModeToggle() { - setState(() { - switch (_entryMode.value) { - case TimePickerEntryMode.dial: - _autovalidateMode.value = AutovalidateMode.disabled; - _entryMode.value = TimePickerEntryMode.input; - break; - case TimePickerEntryMode.input: - _formKey.currentState!.save(); - _autofocusHour.value = false; - _autofocusMinute.value = false; - _entryMode.value = TimePickerEntryMode.dial; - break; - case TimePickerEntryMode.dialOnly: - case TimePickerEntryMode.inputOnly: - FlutterError('Can not change entry mode from $_entryMode'); - break; - } - }); - } - - void _announceModeOnce() { - if (_lastModeAnnounced.value == _mode.value) { - // Already announced it. - return; - } - - switch (_mode.value) { - case _TimePickerMode.hour: - _announceToAccessibility(context, localizations.timePickerHourModeAnnouncement); - break; - case _TimePickerMode.minute: - _announceToAccessibility(context, localizations.timePickerMinuteModeAnnouncement); - break; - } - _lastModeAnnounced.value = _mode.value; - } - - void _announceInitialTimeOnce() { - if (_announcedInitialTime.value) { - return; - } - - final MaterialLocalizations localizations = MaterialLocalizations.of(context); - _announceToAccessibility( - context, - localizations.formatTimeOfDay(widget.initialTime, alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)), - ); - _announcedInitialTime.value = true; - } - - void _handleTimeChanged(TimeOfDay value) { - _vibrate(); - setState(() { - _selectedTime.value = value; - }); - } - - void _handleHourDoubleTapped() { - _autofocusHour.value = true; - _handleEntryModeToggle(); - } - - void _handleMinuteDoubleTapped() { - _autofocusMinute.value = true; - _handleEntryModeToggle(); - } - - void _handleHourSelected() { - setState(() { - _mode.value = _TimePickerMode.minute; - }); - } - - void _handleCancel() { - Navigator.pop(context); + void _handleCancel() { + Navigator.pop(context); } void _handleOk() { if (_entryMode.value == TimePickerEntryMode.input || _entryMode.value == TimePickerEntryMode.inputOnly) { final FormState form = _formKey.currentState!; if (!form.validate()) { - setState(() { _autovalidateMode.value = AutovalidateMode.always; }); + setState(() { + _autovalidateMode.value = AutovalidateMode.always; + }); return; } form.save(); @@ -2192,258 +2314,663 @@ class _TimePickerDialogState extends State with RestorationMix Navigator.pop(context, _selectedTime.value); } - Size _dialogSize(BuildContext context) { - final Orientation orientation = MediaQuery.orientationOf(context); - final ThemeData theme = Theme.of(context); + Size _minDialogSize(BuildContext context, {required bool useMaterial3}) { + final Orientation orientation = _orientation.value ?? MediaQuery.orientationOf(context); + + switch (_entryMode.value) { + case TimePickerEntryMode.dial: + case TimePickerEntryMode.dialOnly: + switch (orientation) { + case Orientation.portrait: + return _kTimePickerMinPortraitSize; + case Orientation.landscape: + return _kTimePickerMinLandscapeSize; + } + case TimePickerEntryMode.input: + case TimePickerEntryMode.inputOnly: + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + final TimeOfDayFormat timeOfDayFormat = localizations.timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); + final double timePickerWidth; + switch(timeOfDayFormat) { + case TimeOfDayFormat.HH_colon_mm: + case TimeOfDayFormat.HH_dot_mm: + case TimeOfDayFormat.frenchCanadian: + case TimeOfDayFormat.H_colon_mm: + final _TimePickerDefaults defaultTheme = useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + timePickerWidth = _kTimePickerMinInputSize.width - defaultTheme.dayPeriodPortraitSize.width - 12; + break; + case TimeOfDayFormat.a_space_h_colon_mm: + case TimeOfDayFormat.h_colon_mm_space_a: + timePickerWidth = _kTimePickerMinInputSize.width; + break; + } + return Size(timePickerWidth, _kTimePickerMinInputSize.height); + } + } + + Size _dialogSize(BuildContext context, {required bool useMaterial3}) { + final Orientation orientation = _orientation.value ?? MediaQuery.orientationOf(context); // Constrain the textScaleFactor to prevent layout issues. Since only some // parts of the time picker scale up with textScaleFactor, we cap the factor // to 1.1 as that provides enough space to reasonably fit all the content. final double textScaleFactor = math.min(MediaQuery.textScaleFactorOf(context), 1.1); - final double timePickerWidth; - final double timePickerHeight; + final Size timePickerSize; switch (_entryMode.value) { case TimePickerEntryMode.dial: case TimePickerEntryMode.dialOnly: switch (orientation) { case Orientation.portrait: - timePickerWidth = _kTimePickerWidthPortrait; - timePickerHeight = theme.materialTapTargetSize == MaterialTapTargetSize.padded - ? _kTimePickerHeightPortrait - : _kTimePickerHeightPortraitCollapsed; + timePickerSize = _kTimePickerPortraitSize; break; case Orientation.landscape: - timePickerWidth = _kTimePickerWidthLandscape * textScaleFactor; - timePickerHeight = theme.materialTapTargetSize == MaterialTapTargetSize.padded - ? _kTimePickerHeightLandscape - : _kTimePickerHeightLandscapeCollapsed; + timePickerSize = Size( + _kTimePickerLandscapeSize.width * textScaleFactor, + useMaterial3 ? _kTimePickerLandscapeSize.height : _kTimePickerLandscapeSizeM2.height + ); break; } break; case TimePickerEntryMode.input: case TimePickerEntryMode.inputOnly: - timePickerWidth = _kTimePickerWidthPortrait; - timePickerHeight = _kTimePickerHeightInput; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + final TimeOfDayFormat timeOfDayFormat = localizations.timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); + final double timePickerWidth; + switch(timeOfDayFormat) { + case TimeOfDayFormat.HH_colon_mm: + case TimeOfDayFormat.HH_dot_mm: + case TimeOfDayFormat.frenchCanadian: + case TimeOfDayFormat.H_colon_mm: + final _TimePickerDefaults defaultTheme = useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + timePickerWidth = _kTimePickerInputSize.width - defaultTheme.dayPeriodPortraitSize.width - 12; + break; + case TimeOfDayFormat.a_space_h_colon_mm: + case TimeOfDayFormat.h_colon_mm_space_a: + timePickerWidth = _kTimePickerInputSize.width; + break; + } + timePickerSize = Size(timePickerWidth, _kTimePickerInputSize.height); break; } - return Size(timePickerWidth, timePickerHeight * textScaleFactor); + return Size(timePickerSize.width, timePickerSize.height * textScaleFactor); } @override Widget build(BuildContext context) { assert(debugCheckHasMediaQuery(context)); - final TimeOfDayFormat timeOfDayFormat = localizations.timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); - final bool use24HourDials = hourFormat(of: timeOfDayFormat) != HourFormat.h; final ThemeData theme = Theme.of(context); - final ShapeBorder shape = TimePickerTheme.of(context).shape ?? _kDefaultShape; - final Orientation orientation = MediaQuery.orientationOf(context); - - final Widget actions = Row( - children: [ - const SizedBox(width: 10.0), - if (_entryMode.value == TimePickerEntryMode.dial || _entryMode.value == TimePickerEntryMode.input) - IconButton( - color: TimePickerTheme.of(context).entryModeIconColor ?? theme.colorScheme.onSurface.withOpacity( - theme.colorScheme.brightness == Brightness.dark ? 1.0 : 0.6, - ), - onPressed: _handleEntryModeToggle, - icon: Icon(_entryMode.value == TimePickerEntryMode.dial ? Icons.keyboard : Icons.access_time), - tooltip: _entryMode.value == TimePickerEntryMode.dial - ? MaterialLocalizations.of(context).inputTimeModeButtonLabel - : MaterialLocalizations.of(context).dialModeButtonLabel, - ), - Expanded( - child: Container( - alignment: AlignmentDirectional.centerEnd, - constraints: const BoxConstraints(minHeight: 52.0), - padding: const EdgeInsets.symmetric(horizontal: 8), - child: OverflowBar( - spacing: 8, - overflowAlignment: OverflowBarAlignment.end, - children: [ - TextButton( - onPressed: _handleCancel, - child: Text(widget.cancelText ?? ( - theme.useMaterial3 - ? localizations.cancelButtonLabel - : localizations.cancelButtonLabel.toUpperCase() - )), - ), - TextButton( - onPressed: _handleOk, - child: Text(widget.confirmText ?? localizations.okButtonLabel), - ), - ], - ), - ), - ), - ], - ); + final TimePickerThemeData pickerTheme = TimePickerTheme.of(context); + final _TimePickerDefaults defaultTheme = theme.useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + final ShapeBorder shape = pickerTheme.shape ?? defaultTheme.shape; + final Color entryModeIconColor = pickerTheme.entryModeIconColor ?? defaultTheme.entryModeIconColor; + final MaterialLocalizations localizations = MaterialLocalizations.of(context); - final Widget picker; - switch (_entryMode.value) { - case TimePickerEntryMode.dial: - case TimePickerEntryMode.dialOnly: - final Widget dial = Padding( - padding: orientation == Orientation.portrait ? const EdgeInsets.symmetric(horizontal: 36, vertical: 24) : const EdgeInsets.all(24), - child: ExcludeSemantics( - child: AspectRatio( - aspectRatio: 1.0, - child: _Dial( - mode: _mode.value, - use24HourDials: use24HourDials, - selectedTime: _selectedTime.value, - onChanged: _handleTimeChanged, - onHourSelected: _handleHourSelected, - ), + final Widget actions = Padding( + padding: EdgeInsetsDirectional.only(start: theme.useMaterial3 ? 0 : 4), + child: Row( + children: [ + if (_entryMode.value == TimePickerEntryMode.dial || _entryMode.value == TimePickerEntryMode.input) + IconButton( + // In material3 mode, we want to use the color as part of the + // button style which applies its own opacity. In material2 mode, + // we want to use the color as the color, which already includes + // the opacity. + color: theme.useMaterial3 ? null : entryModeIconColor, + style: theme.useMaterial3 ? IconButton.styleFrom(foregroundColor: entryModeIconColor) : null, + onPressed: _toggleEntryMode, + icon: Icon(_entryMode.value == TimePickerEntryMode.dial ? Icons.keyboard_outlined : Icons.access_time), + tooltip: _entryMode.value == TimePickerEntryMode.dial + ? MaterialLocalizations.of(context).inputTimeModeButtonLabel + : MaterialLocalizations.of(context).dialModeButtonLabel, ), - ), - ); - - final Widget header = _TimePickerHeader( - selectedTime: _selectedTime.value, - mode: _mode.value, - orientation: orientation, - onModeChanged: _handleModeChanged, - onChanged: _handleTimeChanged, - onHourDoubleTapped: _handleHourDoubleTapped, - onMinuteDoubleTapped: _handleMinuteDoubleTapped, - use24HourDials: use24HourDials, - helpText: widget.helpText, - ); - - switch (orientation) { - case Orientation.portrait: - picker = Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - header, - Expanded( - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - // Dial grows and shrinks with the available space. - Expanded(child: dial), - actions, - ], + Expanded( + child: Container( + alignment: AlignmentDirectional.centerEnd, + constraints: const BoxConstraints(minHeight: 36), + child: OverflowBar( + spacing: 8, + overflowAlignment: OverflowBarAlignment.end, + children: [ + TextButton( + onPressed: _handleCancel, + child: Text(widget.cancelText ?? + (theme.useMaterial3 + ? localizations.cancelButtonLabel + : localizations.cancelButtonLabel.toUpperCase())), ), - ), - ], - ); - break; - case Orientation.landscape: - picker = Column( - children: [ - Expanded( - child: Row( - children: [ - header, - Expanded(child: dial), - ], + TextButton( + onPressed: _handleOk, + child: Text(widget.confirmText ?? localizations.okButtonLabel), ), - ), - actions, - ], - ); - break; - } - break; - case TimePickerEntryMode.input: - case TimePickerEntryMode.inputOnly: - picker = Form( - key: _formKey, - autovalidateMode: _autovalidateMode.value, - child: SingleChildScrollView( - restorationId: 'time_picker_scroll_view', - child: Column( - mainAxisSize: MainAxisSize.min, - children: [ - _TimePickerInput( - initialSelectedTime: _selectedTime.value, - helpText: widget.helpText, - errorInvalidText: widget.errorInvalidText, - hourLabelText: widget.hourLabelText, - minuteLabelText: widget.minuteLabelText, - autofocusHour: _autofocusHour.value, - autofocusMinute: _autofocusMinute.value, - onChanged: _handleTimeChanged, - restorationId: 'time_picker_input', - ), - actions, - ], + ], + ), ), ), - ); + ], + ), + ); + + final Offset tapTargetSizeOffset; + switch (theme.materialTapTargetSize) { + case MaterialTapTargetSize.padded: + tapTargetSizeOffset = Offset.zero; + break; + case MaterialTapTargetSize.shrinkWrap: + // _dialogSize returns "padded" sizes. + tapTargetSizeOffset = const Offset(0, -12); break; } - final Size dialogSize = _dialogSize(context); + final Size dialogSize = _dialogSize(context, useMaterial3: theme.useMaterial3) + tapTargetSizeOffset; + final Size minDialogSize = _minDialogSize(context, useMaterial3: theme.useMaterial3) + tapTargetSizeOffset; return Dialog( shape: shape, - backgroundColor: TimePickerTheme.of(context).backgroundColor ?? theme.colorScheme.surface, + elevation: pickerTheme.elevation ?? defaultTheme.elevation, + backgroundColor: pickerTheme.backgroundColor ?? defaultTheme.backgroundColor, insetPadding: EdgeInsets.symmetric( - horizontal: 16.0, - vertical: (_entryMode.value == TimePickerEntryMode.input || _entryMode.value == TimePickerEntryMode.inputOnly) ? 0.0 : 24.0, + horizontal: 16, + vertical: (_entryMode.value == TimePickerEntryMode.input || _entryMode.value == TimePickerEntryMode.inputOnly) ? 0 : 24, ), - child: AnimatedContainer( - width: dialogSize.width, - height: dialogSize.height, - duration: _kDialogSizeAnimationDuration, - curve: Curves.easeIn, - child: picker, + child: Padding( + padding: pickerTheme.padding ?? defaultTheme.padding, + child: LayoutBuilder(builder: (BuildContext context, BoxConstraints constraints) { + final Size constrainedSize = constraints.constrain(dialogSize); + final Size allowedSize = Size( + constrainedSize.width < minDialogSize.width ? minDialogSize.width : constrainedSize.width, + constrainedSize.height < minDialogSize.height ? minDialogSize.height : constrainedSize.height, + ); + return SingleChildScrollView( + restorationId: 'time_picker_scroll_view_horizontal', + scrollDirection: Axis.horizontal, + child: SingleChildScrollView( + restorationId: 'time_picker_scroll_view_vertical', + child: AnimatedContainer( + width: allowedSize.width, + height: allowedSize.height, + duration: _kDialogSizeAnimationDuration, + curve: Curves.easeIn, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Expanded( + child: Form( + key: _formKey, + autovalidateMode: _autovalidateMode.value, + child: _TimePicker( + time: widget.initialTime, + onTimeChanged: _handleTimeChanged, + helpText: widget.helpText, + cancelText: widget.cancelText, + confirmText: widget.confirmText, + errorInvalidText: widget.errorInvalidText, + hourLabelText: widget.hourLabelText, + minuteLabelText: widget.minuteLabelText, + restorationId: 'time_picker', + entryMode: _entryMode.value, + orientation: widget.orientation, + onEntryModeChanged: _handleEntryModeChanged, + ), + ), + ), + actions, + ], + ), + ), + ), + ); + }), ), ); } +} + +// The _TimePicker widget is constructed so that in the future we could expose +// this as a public API for embedding time pickers into other non-dialog +// widgets, once we're sure we want to support that. + +/// A Time Picker widget that can be embedded into another widget. +class _TimePicker extends StatefulWidget { + /// Creates a const Material Design time picker. + const _TimePicker({ + required this.time, + required this.onTimeChanged, + this.helpText, + this.cancelText, + this.confirmText, + this.errorInvalidText, + this.hourLabelText, + this.minuteLabelText, + this.restorationId, + this.entryMode = TimePickerEntryMode.dial, + this.orientation, + this.onEntryModeChanged, + }); + + /// Optionally provide your own text for the help text at the top of the + /// control. + /// + /// If null, the widget uses [MaterialLocalizations.timePickerDialHelpText] + /// when the [entryMode] is [TimePickerEntryMode.dial], and + /// [MaterialLocalizations.timePickerInputHelpText] when the [entryMode] is + /// [TimePickerEntryMode.input]. + final String? helpText; + + /// Optionally provide your own text for the cancel button. + /// + /// If null, the button uses [MaterialLocalizations.cancelButtonLabel]. + final String? cancelText; + + /// Optionally provide your own text for the confirm button. + /// + /// If null, the button uses [MaterialLocalizations.okButtonLabel]. + final String? confirmText; + + /// Optionally provide your own validation error text. + final String? errorInvalidText; + + /// Optionally provide your own hour label text. + final String? hourLabelText; + + /// Optionally provide your own minute label text. + final String? minuteLabelText; + + /// Restoration ID to save and restore the state of the [TimePickerDialog]. + /// + /// If it is non-null, the time picker will persist and restore the + /// dialog's state. + /// + /// The state of this widget is persisted in a [RestorationBucket] claimed + /// from the surrounding [RestorationScope] using the provided restoration ID. + /// + /// See also: + /// + /// * [RestorationManager], which explains how state restoration works in + /// Flutter. + final String? restorationId; + + /// The initial entry mode for the picker. Whether it's text input or a dial. + final TimePickerEntryMode entryMode; + + /// The currently selected time of day. + final TimeOfDay time; + + final ValueChanged? onTimeChanged; + + /// The optional [orientation] parameter sets the [Orientation] to use when + /// displaying the dialog. + /// + /// By default, the orientation is derived from the [MediaQueryData.size] of + /// the ambient [MediaQuery]. If the aspect of the size is tall, then + /// [Orientation.portrait] is used, if the size is wide, then + /// [Orientation.landscape] is used. + /// + /// Use this parameter to override the default and force the dialog to appear + /// in either portrait or landscape mode regardless of the aspect of the + /// [MediaQueryData.size]. + final Orientation? orientation; + + /// Callback called when the selected entry mode is changed. + final EntryModeChangeCallback? onEntryModeChanged; + + @override + State<_TimePicker> createState() => _TimePickerState(); +} + +class _TimePickerState extends State<_TimePicker> with RestorationMixin { + Timer? _vibrateTimer; + late MaterialLocalizations localizations; + final RestorableEnum<_HourMinuteMode> _hourMinuteMode = + RestorableEnum<_HourMinuteMode>(_HourMinuteMode.hour, values: _HourMinuteMode.values); + final RestorableEnumN<_HourMinuteMode> _lastModeAnnounced = + RestorableEnumN<_HourMinuteMode>(null, values: _HourMinuteMode.values); + final RestorableBoolN _autofocusHour = RestorableBoolN(null); + final RestorableBoolN _autofocusMinute = RestorableBoolN(null); + final RestorableBool _announcedInitialTime = RestorableBool(false); + late final RestorableEnumN _orientation = + RestorableEnumN(widget.orientation, values: Orientation.values); + RestorableTimeOfDay get selectedTime => _selectedTime; + late final RestorableTimeOfDay _selectedTime = RestorableTimeOfDay(widget.time); @override void dispose() { _vibrateTimer?.cancel(); _vibrateTimer = null; - _entryMode.removeListener(_entryModeListener); super.dispose(); } -} -/// Shows a dialog containing a Material Design time picker. -/// -/// The returned Future resolves to the time selected by the user when the user -/// closes the dialog. If the user cancels the dialog, null is returned. -/// -/// {@tool snippet} -/// Show a dialog with [initialTime] equal to the current time. -/// -/// ```dart -/// Future selectedTime = showTimePicker( -/// initialTime: TimeOfDay.now(), -/// context: context, -/// ); -/// ``` -/// {@end-tool} -/// -/// The [context], [useRootNavigator] and [routeSettings] arguments are passed to -/// [showDialog], the documentation for which discusses how it is used. -/// -/// The [builder] parameter can be used to wrap the dialog widget -/// to add inherited widgets like [Localizations.override], -/// [Directionality], or [MediaQuery]. -/// -/// The `initialEntryMode` parameter can be used to -/// determine the initial time entry selection of the picker (either a clock -/// dial or text input). -/// -/// Optional strings for the [helpText], [cancelText], [errorInvalidText], -/// [hourLabelText], [minuteLabelText] and [confirmText] can be provided to -/// override the default values. -/// -/// {@macro flutter.widgets.RawDialogRoute} -/// -/// By default, the time picker gets its colors from the overall theme's -/// [ColorScheme]. The time picker can be further customized by providing a + @override + void didChangeDependencies() { + super.didChangeDependencies(); + localizations = MaterialLocalizations.of(context); + _announceInitialTimeOnce(); + _announceModeOnce(); + } + + @override + void didUpdateWidget (_TimePicker oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.orientation != widget.orientation) { + _orientation.value = widget.orientation; + } + if (oldWidget.time != widget.time) { + _selectedTime.value = widget.time; + } + } + + void _setEntryMode(TimePickerEntryMode mode){ + widget.onEntryModeChanged?.call(mode); + } + + @override + String? get restorationId => widget.restorationId; + + @override + void restoreState(RestorationBucket? oldBucket, bool initialRestore) { + registerForRestoration(_hourMinuteMode, 'hour_minute_mode'); + registerForRestoration(_lastModeAnnounced, 'last_mode_announced'); + registerForRestoration(_autofocusHour, 'autofocus_hour'); + registerForRestoration(_autofocusMinute, 'autofocus_minute'); + registerForRestoration(_announcedInitialTime, 'announced_initial_time'); + registerForRestoration(_selectedTime, 'selected_time'); + registerForRestoration(_orientation, 'orientation'); + } + + void _vibrate() { + switch (Theme.of(context).platform) { + case TargetPlatform.android: + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.windows: + _vibrateTimer?.cancel(); + _vibrateTimer = Timer(_kVibrateCommitDelay, () { + HapticFeedback.vibrate(); + _vibrateTimer = null; + }); + break; + case TargetPlatform.iOS: + case TargetPlatform.macOS: + break; + } + } + + void _handleHourMinuteModeChanged(_HourMinuteMode mode) { + _vibrate(); + setState(() { + _hourMinuteMode.value = mode; + _announceModeOnce(); + }); + } + + void _handleEntryModeToggle() { + setState(() { + TimePickerEntryMode newMode = widget.entryMode; + switch (widget.entryMode) { + case TimePickerEntryMode.dial: + newMode = TimePickerEntryMode.input; + break; + case TimePickerEntryMode.input: + _autofocusHour.value = false; + _autofocusMinute.value = false; + newMode = TimePickerEntryMode.dial; + break; + case TimePickerEntryMode.dialOnly: + case TimePickerEntryMode.inputOnly: + FlutterError('Can not change entry mode from ${widget.entryMode}'); + break; + } + _setEntryMode(newMode); + }); + } + + void _announceModeOnce() { + if (_lastModeAnnounced.value == _hourMinuteMode.value) { + // Already announced it. + return; + } + + switch (_hourMinuteMode.value) { + case _HourMinuteMode.hour: + _announceToAccessibility(context, localizations.timePickerHourModeAnnouncement); + break; + case _HourMinuteMode.minute: + _announceToAccessibility(context, localizations.timePickerMinuteModeAnnouncement); + break; + } + _lastModeAnnounced.value = _hourMinuteMode.value; + } + + void _announceInitialTimeOnce() { + if (_announcedInitialTime.value) { + return; + } + + final MaterialLocalizations localizations = MaterialLocalizations.of(context); + _announceToAccessibility( + context, + localizations.formatTimeOfDay(_selectedTime.value, alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)), + ); + _announcedInitialTime.value = true; + } + + void _handleTimeChanged(TimeOfDay value) { + _vibrate(); + setState(() { + _selectedTime.value = value; + widget.onTimeChanged?.call(value); + }); + } + + void _handleHourDoubleTapped() { + _autofocusHour.value = true; + _handleEntryModeToggle(); + } + + void _handleMinuteDoubleTapped() { + _autofocusMinute.value = true; + _handleEntryModeToggle(); + } + + void _handleHourSelected() { + setState(() { + _hourMinuteMode.value = _HourMinuteMode.minute; + }); + } + + @override + Widget build(BuildContext context) { + assert(debugCheckHasMediaQuery(context)); + final TimeOfDayFormat timeOfDayFormat = localizations.timeOfDayFormat(alwaysUse24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context)); + final ThemeData theme = Theme.of(context); + final _TimePickerDefaults defaultTheme = theme.useMaterial3 ? _TimePickerDefaultsM3(context) : _TimePickerDefaultsM2(context); + final Orientation orientation = _orientation.value ?? MediaQuery.orientationOf(context); + final HourFormat timeOfDayHour = hourFormat(of: timeOfDayFormat); + final _HourDialType hourMode; + switch (timeOfDayHour) { + case HourFormat.HH: + case HourFormat.H: + hourMode = theme.useMaterial3 ? _HourDialType.twentyFourHourDoubleRing : _HourDialType.twentyFourHour; + break; + case HourFormat.h: + hourMode = _HourDialType.twelveHour; + break; + } + + final String helpText; + final Widget picker; + switch (widget.entryMode) { + case TimePickerEntryMode.dial: + case TimePickerEntryMode.dialOnly: + helpText = widget.helpText ?? (theme.useMaterial3 + ? localizations.timePickerDialHelpText + : localizations.timePickerDialHelpText.toUpperCase()); + + final EdgeInsetsGeometry dialPadding; + switch (orientation) { + case Orientation.portrait: + dialPadding = const EdgeInsets.only(left: 12, right: 12, top: 36); + break; + case Orientation.landscape: + switch (theme.materialTapTargetSize) { + case MaterialTapTargetSize.padded: + dialPadding = const EdgeInsetsDirectional.only(start: 64); + break; + case MaterialTapTargetSize.shrinkWrap: + dialPadding = const EdgeInsetsDirectional.only(start: 64); + break; + } + break; + } + final Widget dial = Padding( + padding: dialPadding, + child: ExcludeSemantics( + child: SizedBox.fromSize( + size: defaultTheme.dialSize, + child: AspectRatio( + aspectRatio: 1, + child: _Dial( + hourMinuteMode: _hourMinuteMode.value, + hourDialType: hourMode, + selectedTime: _selectedTime.value, + onChanged: _handleTimeChanged, + onHourSelected: _handleHourSelected, + ), + ), + ), + ), + ); + + switch (orientation) { + case Orientation.portrait: + picker = Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Padding( + padding: EdgeInsets.symmetric(horizontal: theme.useMaterial3 ? 0 : 16), + child: _TimePickerHeader(helpText: helpText), + ), + Expanded( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Dial grows and shrinks with the available space. + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: theme.useMaterial3 ? 0 : 16), + child: dial, + ), + ), + ], + ), + ), + ], + ); + break; + case Orientation.landscape: + picker = Column( + children: [ + Expanded( + child: Padding( + padding: EdgeInsets.symmetric(horizontal: theme.useMaterial3 ? 0 : 16), + child: Row( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + _TimePickerHeader(helpText: helpText), + Expanded(child: dial), + ], + ), + ), + ), + ], + ); + break; + } + break; + case TimePickerEntryMode.input: + case TimePickerEntryMode.inputOnly: + final String helpText = widget.helpText ?? (theme.useMaterial3 + ? localizations.timePickerInputHelpText + : localizations.timePickerInputHelpText.toUpperCase()); + + picker = Column( + mainAxisSize: MainAxisSize.min, + children: [ + _TimePickerInput( + initialSelectedTime: _selectedTime.value, + errorInvalidText: widget.errorInvalidText, + hourLabelText: widget.hourLabelText, + minuteLabelText: widget.minuteLabelText, + helpText: helpText, + autofocusHour: _autofocusHour.value, + autofocusMinute: _autofocusMinute.value, + restorationId: 'time_picker_input', + ), + ], + ); + } + return _TimePickerModel( + entryMode: widget.entryMode, + selectedTime: _selectedTime.value, + hourMinuteMode: _hourMinuteMode.value, + orientation: orientation, + onHourMinuteModeChanged: _handleHourMinuteModeChanged, + onHourDoubleTapped: _handleHourDoubleTapped, + onMinuteDoubleTapped: _handleMinuteDoubleTapped, + hourDialType: hourMode, + onSelectedTimeChanged: _handleTimeChanged, + useMaterial3: theme.useMaterial3, + use24HourFormat: MediaQuery.alwaysUse24HourFormatOf(context), + theme: TimePickerTheme.of(context), + defaultTheme: defaultTheme, + child: picker, + ); + } +} + +/// Shows a dialog containing a Material Design time picker. +/// +/// The returned Future resolves to the time selected by the user when the user +/// closes the dialog. If the user cancels the dialog, null is returned. +/// +/// {@tool snippet} Show a dialog with [initialTime] equal to the current time. +/// +/// ```dart +/// Future selectedTime = showTimePicker( +/// initialTime: TimeOfDay.now(), +/// context: context, +/// ); +/// ``` +/// {@end-tool} +/// +/// The [context], [useRootNavigator] and [routeSettings] arguments are passed +/// to [showDialog], the documentation for which discusses how it is used. +/// +/// The [builder] parameter can be used to wrap the dialog widget to add +/// inherited widgets like [Localizations.override], [Directionality], or +/// [MediaQuery]. +/// +/// The `initialEntryMode` parameter can be used to determine the initial time +/// entry selection of the picker (either a clock dial or text input). +/// +/// Optional strings for the [helpText], [cancelText], [errorInvalidText], +/// [hourLabelText], [minuteLabelText] and [confirmText] can be provided to +/// override the default values. +/// +/// The optional [orientation] parameter sets the [Orientation] to use when +/// displaying the dialog. By default, the orientation is derived from the +/// [MediaQueryData.size] of the ambient [MediaQuery]: wide sizes use the +/// landscape orientation, and tall sizes use the portrait orientation. Use this +/// parameter to override the default and force the dialog to appear in either +/// portrait or landscape mode. +/// +/// {@macro flutter.widgets.RawDialogRoute} +/// +/// By default, the time picker gets its colors from the overall theme's +/// [ColorScheme]. The time picker can be further customized by providing a /// [TimePickerThemeData] to the overall theme. /// -/// {@tool snippet} -/// Show a dialog with the text direction overridden to be [TextDirection.rtl]. +/// {@tool snippet} Show a dialog with the text direction overridden to be +/// [TextDirection.rtl]. /// /// ```dart /// Future selectedTimeRTL = showTimePicker( @@ -2459,8 +2986,8 @@ class _TimePickerDialogState extends State with RestorationMix /// ``` /// {@end-tool} /// -/// {@tool snippet} -/// Show a dialog with time unconditionally displayed in 24 hour format. +/// {@tool snippet} Show a dialog with time unconditionally displayed in 24 hour +/// format. /// /// ```dart /// Future selectedTime24Hour = showTimePicker( @@ -2476,14 +3003,21 @@ class _TimePickerDialogState extends State with RestorationMix /// ``` /// {@end-tool} /// +/// {@tool dartpad} +/// This example illustrates how to open a time picker, and allows exploring +/// some of the variations in the types of time pickers that may be shown. +/// +/// ** See code in examples/api/lib/material/time_picker/show_time_picker.0.dart ** +/// {@end-tool} +/// /// See also: /// -/// * [showDatePicker], which shows a dialog that contains a Material Design -/// date picker. -/// * [TimePickerThemeData], which allows you to customize the colors, -/// typography, and shape of the time picker. -/// * [DisplayFeatureSubScreen], which documents the specifics of how -/// [DisplayFeature]s can split the screen into sub-screens. +/// * [showDatePicker], which shows a dialog that contains a Material Design +/// date picker. +/// * [TimePickerThemeData], which allows you to customize the colors, +/// typography, and shape of the time picker. +/// * [DisplayFeatureSubScreen], which documents the specifics of how +/// [DisplayFeature]s can split the screen into sub-screens. Future showTimePicker({ required BuildContext context, required TimeOfDay initialTime, @@ -2499,6 +3033,7 @@ Future showTimePicker({ RouteSettings? routeSettings, EntryModeChangeCallback? onEntryModeChanged, Offset? anchorPoint, + Orientation? orientation, }) async { assert(context != null); assert(initialTime != null); @@ -2515,6 +3050,7 @@ Future showTimePicker({ errorInvalidText: errorInvalidText, hourLabelText: hourLabelText, minuteLabelText: minuteLabelText, + orientation: orientation, onEntryModeChanged: onEntryModeChanged, ); return showDialog( @@ -2531,3 +3067,664 @@ Future showTimePicker({ void _announceToAccessibility(BuildContext context, String message) { SemanticsService.announce(message, Directionality.of(context)); } + +// An abstract base class for the M2 and M3 defaults below, so that their return +// types can be non-nullable. +abstract class _TimePickerDefaults extends TimePickerThemeData { + @override + Color get backgroundColor; + + @override + ButtonStyle get cancelButtonStyle; + + @override + ButtonStyle get confirmButtonStyle; + + @override + BorderSide get dayPeriodBorderSide; + + @override + Color get dayPeriodColor; + + @override + OutlinedBorder get dayPeriodShape; + + Size get dayPeriodInputSize; + Size get dayPeriodLandscapeSize; + Size get dayPeriodPortraitSize; + + @override + Color get dayPeriodTextColor; + + @override + TextStyle get dayPeriodTextStyle; + + @override + Color get dialBackgroundColor; + + @override + Color get dialHandColor; + + // Sizes that are generated from the tokens, but these aren't ones we're ready + // to expose in the theme. + Size get dialSize; + double get handWidth; + double get dotRadius; + double get centerRadius; + + @override + Color get dialTextColor; + + @override + TextStyle get dialTextStyle; + + @override + double get elevation; + + @override + Color get entryModeIconColor; + + @override + TextStyle get helpTextStyle; + + @override + Color get hourMinuteColor; + + @override + ShapeBorder get hourMinuteShape; + + Size get hourMinuteSize; + Size get hourMinuteSize24Hour; + Size get hourMinuteInputSize; + Size get hourMinuteInputSize24Hour; + + @override + Color get hourMinuteTextColor; + + @override + TextStyle get hourMinuteTextStyle; + + @override + InputDecorationTheme get inputDecorationTheme; + + @override + EdgeInsetsGeometry get padding; + + @override + ShapeBorder get shape; +} + +// These theme defaults are not auto-generated: they match the values for the +// Material 2 spec, which are not expected to change. +class _TimePickerDefaultsM2 extends _TimePickerDefaults { + _TimePickerDefaultsM2(this.context) : super(); + + final BuildContext context; + + late final ColorScheme _colors = Theme.of(context).colorScheme; + late final TextTheme _textTheme = Theme.of(context).textTheme; + static const OutlinedBorder _kDefaultShape = RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4))); + + @override + Color get backgroundColor { + return _colors.surface; + } + + @override + ButtonStyle get cancelButtonStyle { + return TextButton.styleFrom(); + } + + @override + ButtonStyle get confirmButtonStyle { + return TextButton.styleFrom(); + } + + @override + BorderSide get dayPeriodBorderSide { + return BorderSide( + color: Color.alphaBlend(_colors.onSurface.withOpacity(0.38), _colors.surface), + ); + } + + @override + Color get dayPeriodColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return _colors.primary.withOpacity(_colors.brightness == Brightness.dark ? 0.24 : 0.12); + } + // The unselected day period should match the overall picker dialog color. + // Making it transparent enables that without being redundant and allows + // the optional elevation overlay for dark mode to be visible. + return Colors.transparent; + }); + } + + @override + OutlinedBorder get dayPeriodShape { + return _kDefaultShape; + } + + @override + Size get dayPeriodPortraitSize { + return const Size(52, 80); + } + + @override + Size get dayPeriodLandscapeSize { + return const Size(0, 40); + } + + @override + Size get dayPeriodInputSize { + return const Size(52, 70); + } + + @override + Color get dayPeriodTextColor { + return MaterialStateColor.resolveWith((Set states) { + return states.contains(MaterialState.selected) ? _colors.primary : _colors.onSurface.withOpacity(0.60); + }); + } + + @override + TextStyle get dayPeriodTextStyle { + return _textTheme.titleMedium!.copyWith(color: dayPeriodTextColor); + } + + @override + Color get dialBackgroundColor { + return _colors.onSurface.withOpacity(_colors.brightness == Brightness.dark ? 0.12 : 0.08); + } + + @override + Color get dialHandColor { + return _colors.primary; + } + + @override + Size get dialSize { + return const Size.square(280); + } + + @override + double get handWidth { + return 2; + } + + @override + double get dotRadius { + return 22; + } + + @override + double get centerRadius { + return 4; + } + + @override + Color get dialTextColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return _colors.surface; + } + return _colors.onSurface; + }); + } + + @override + TextStyle get dialTextStyle { + return _textTheme.bodyLarge!; + } + + @override + double get elevation { + return 6; + } + + @override + Color get entryModeIconColor { + return _colors.onSurface.withOpacity(_colors.brightness == Brightness.dark ? 1.0 : 0.6); + } + + @override + TextStyle get helpTextStyle { + return _textTheme.labelSmall!; + } + + @override + Color get hourMinuteColor { + return MaterialStateColor.resolveWith((Set states) { + return states.contains(MaterialState.selected) + ? _colors.primary.withOpacity(_colors.brightness == Brightness.dark ? 0.24 : 0.12) + : _colors.onSurface.withOpacity(0.12); + }); + } + + @override + ShapeBorder get hourMinuteShape { + return _kDefaultShape; + } + + @override + Size get hourMinuteSize { + return const Size(96, 80); + } + + @override + Size get hourMinuteSize24Hour { + return const Size(114, 80); + } + + @override + Size get hourMinuteInputSize { + return const Size(96, 70); + } + + @override + Size get hourMinuteInputSize24Hour { + return const Size(114, 70); + } + + @override + Color get hourMinuteTextColor { + return MaterialStateColor.resolveWith((Set states) { + return states.contains(MaterialState.selected) ? _colors.primary : _colors.onSurface; + }); + } + + @override + TextStyle get hourMinuteTextStyle { + return _textTheme.displayMedium!; + } + + Color get _hourMinuteInputColor { + return MaterialStateColor.resolveWith((Set states) { + return states.contains(MaterialState.selected) + ? Colors.transparent + : _colors.onSurface.withOpacity(0.12); + }); + } + + @override + InputDecorationTheme get inputDecorationTheme { + return InputDecorationTheme( + contentPadding: EdgeInsets.zero, + filled: true, + fillColor: _hourMinuteInputColor, + focusColor: Colors.transparent, + enabledBorder: const OutlineInputBorder( + borderSide: BorderSide(color: Colors.transparent), + ), + errorBorder: OutlineInputBorder( + borderSide: BorderSide(color: _colors.error, width: 2), + ), + focusedBorder: OutlineInputBorder( + borderSide: BorderSide(color: _colors.primary, width: 2), + ), + focusedErrorBorder: OutlineInputBorder( + borderSide: BorderSide(color: _colors.error, width: 2), + ), + hintStyle: hourMinuteTextStyle.copyWith(color: _colors.onSurface.withOpacity(0.36)), + // Prevent the error text from appearing. + // TODO(rami-a): Remove this workaround once + // https://github.com/flutter/flutter/issues/54104 + // is fixed. + errorStyle: const TextStyle(fontSize: 0, height: 0), + ); + } + + @override + EdgeInsetsGeometry get padding { + return const EdgeInsets.fromLTRB(8, 18, 8, 8); + } + + @override + ShapeBorder get shape { + return _kDefaultShape; + } +} + +// BEGIN GENERATED TOKEN PROPERTIES - TimePicker + +// Do not edit by hand. The code between the "BEGIN GENERATED" and +// "END GENERATED" comments are generated from data in the Material +// Design token database by the script: +// dev/tools/gen_defaults/bin/gen_defaults.dart. + +// Token database version: v0_143 + +// Generated version v0_143 +class _TimePickerDefaultsM3 extends _TimePickerDefaults { + _TimePickerDefaultsM3(this.context); + + final BuildContext context; + + late final ColorScheme _colors = Theme.of(context).colorScheme; + late final TextTheme _textTheme = Theme.of(context).textTheme; + + @override + Color get backgroundColor { + return _colors.surface; + } + + @override + ButtonStyle get cancelButtonStyle { + return TextButton.styleFrom(); + } + + @override + ButtonStyle get confirmButtonStyle { + return TextButton.styleFrom(); + } + + @override + BorderSide get dayPeriodBorderSide { + return BorderSide(color: _colors.outline); + } + + @override + Color get dayPeriodColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return _colors.tertiaryContainer; + } + // The unselected day period should match the overall picker dialog color. + // Making it transparent enables that without being redundant and allows + // the optional elevation overlay for dark mode to be visible. + return Colors.transparent; + }); + } + + @override + OutlinedBorder get dayPeriodShape { + return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))).copyWith(side: dayPeriodBorderSide); + } + + @override + Size get dayPeriodPortraitSize { + return const Size(52, 80); + } + + @override + Size get dayPeriodLandscapeSize { + return const Size(216, 38); + } + + @override + Size get dayPeriodInputSize { + // Input size is eight pixels smaller than the portrait size in the spec, + // but there's not token for it yet. + return Size(dayPeriodPortraitSize.width, dayPeriodPortraitSize.height - 8); + } + + @override + Color get dayPeriodTextColor { + return MaterialStateColor.resolveWith((Set states) { + return _dayPeriodForegroundColor.resolve(states); + }); + } + + MaterialStateProperty get _dayPeriodForegroundColor { + return MaterialStateProperty.resolveWith((Set states) { + Color? textColor; + if (states.contains(MaterialState.selected)) { + if (states.contains(MaterialState.pressed)) { + textColor = _colors.onTertiaryContainer; + } else { + // not pressed + if (states.contains(MaterialState.focused)) { + textColor = _colors.onTertiaryContainer; + } else { + // not focused + if (states.contains(MaterialState.hovered)) { + textColor = _colors.onTertiaryContainer; + } + } + } + } else { + // unselected + if (states.contains(MaterialState.pressed)) { + textColor = _colors.onSurfaceVariant; + } else { + // not pressed + if (states.contains(MaterialState.focused)) { + textColor = _colors.onSurfaceVariant; + } else { + // not focused + if (states.contains(MaterialState.hovered)) { + textColor = _colors.onSurfaceVariant; + } + } + } + } + return textColor ?? _colors.onTertiaryContainer; + }); + } + + @override + TextStyle get dayPeriodTextStyle { + return _textTheme.titleMedium!.copyWith(color: dayPeriodTextColor); + } + + @override + Color get dialBackgroundColor { + return _colors.onSurfaceVariant.withOpacity(_colors.brightness == Brightness.dark ? 0.12 : 0.08); + } + + @override + Color get dialHandColor { + return _colors.primary; + } + + @override + Size get dialSize { + return const Size.square(256.0); + } + + @override + double get handWidth { + return const Size(2, double.infinity).width; + } + + @override + double get dotRadius { + return const Size.square(48.0).width / 2; + } + + @override + double get centerRadius { + return const Size.square(8.0).width / 2; + } + + @override + Color get dialTextColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + return _colors.onPrimary; + } + return _colors.onSurface; + }); + } + + @override + TextStyle get dialTextStyle { + return _textTheme.bodyLarge!; + } + + @override + double get elevation { + return 6.0; + } + + @override + Color get entryModeIconColor { + return _colors.onSurface; + } + + @override + TextStyle get helpTextStyle { + return MaterialStateTextStyle.resolveWith((Set states) { + final TextStyle textStyle = _textTheme.labelMedium!; + return textStyle.copyWith(color: _colors.onSurfaceVariant); + }); + } + + @override + EdgeInsetsGeometry get padding { + return const EdgeInsets.all(24); + } + + @override + Color get hourMinuteColor { + return MaterialStateColor.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + Color overlayColor = _colors.primaryContainer; + if (states.contains(MaterialState.pressed)) { + overlayColor = _colors.onPrimaryContainer; + } else if (states.contains(MaterialState.focused)) { + const double focusOpacity = 0.12; + overlayColor = _colors.onPrimaryContainer.withOpacity(focusOpacity); + } else if (states.contains(MaterialState.hovered)) { + const double hoverOpacity = 0.08; + overlayColor = _colors.onPrimaryContainer.withOpacity(hoverOpacity); + } + return Color.alphaBlend(overlayColor, _colors.primaryContainer); + } else { + Color overlayColor = _colors.surfaceVariant; + if (states.contains(MaterialState.pressed)) { + overlayColor = _colors.onSurface; + } else if (states.contains(MaterialState.focused)) { + const double focusOpacity = 0.12; + overlayColor = _colors.onSurface.withOpacity(focusOpacity); + } else if (states.contains(MaterialState.hovered)) { + const double hoverOpacity = 0.08; + overlayColor = _colors.onSurface.withOpacity(hoverOpacity); + } + return Color.alphaBlend(overlayColor, _colors.surfaceVariant); + } + }); + } + + @override + ShapeBorder get hourMinuteShape { + return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(8.0))); + } + + @override + Size get hourMinuteSize { + return const Size(96, 80); + } + + @override + Size get hourMinuteSize24Hour { + return Size(const Size(114, double.infinity).width, hourMinuteSize.height); + } + + @override + Size get hourMinuteInputSize { + // Input size is eight pixels smaller than the regular size in the spec, but + // there's not token for it yet. + return Size(hourMinuteSize.width, hourMinuteSize.height - 8); + } + + @override + Size get hourMinuteInputSize24Hour { + // Input size is eight pixels smaller than the regular size in the spec, but + // there's not token for it yet. + return Size(hourMinuteSize24Hour.width, hourMinuteSize24Hour.height - 8); + } + + @override + Color get hourMinuteTextColor { + return MaterialStateColor.resolveWith((Set states) { + return _hourMinuteTextColor.resolve(states); + }); + } + + MaterialStateProperty get _hourMinuteTextColor { + return MaterialStateProperty.resolveWith((Set states) { + if (states.contains(MaterialState.selected)) { + if (states.contains(MaterialState.pressed)) { + return _colors.onPrimaryContainer; + } + if (states.contains(MaterialState.focused)) { + return _colors.onPrimaryContainer; + } + if (states.contains(MaterialState.hovered)) { + return _colors.onPrimaryContainer; + } + return _colors.onPrimaryContainer; + } else { + // unselected + if (states.contains(MaterialState.pressed)) { + return _colors.onSurface; + } + if (states.contains(MaterialState.focused)) { + return _colors.onSurface; + } + if (states.contains(MaterialState.hovered)) { + return _colors.onSurface; + } + return _colors.onSurface; + } + }); + } + + @override + TextStyle get hourMinuteTextStyle { + return MaterialStateTextStyle.resolveWith((Set states) { + return _textTheme.displayLarge!.copyWith(color: _hourMinuteTextColor.resolve(states)); + }); + } + + @override + InputDecorationTheme get inputDecorationTheme { + // This is NOT correct, but there's no token for + // 'time-input.container.shape', so this is using the radius from the shape + // for the hour/minute selector. It's a BorderRadiusGeometry, so we have to + // resolve it before we can use it. + final BorderRadius selectorRadius = const RoundedRectangleBorder( + borderRadius: BorderRadius.all(Radius.circular(8.0)), + ) + .borderRadius + .resolve(Directionality.of(context)); + return InputDecorationTheme( + contentPadding: EdgeInsets.zero, + filled: true, + // This should be derived from a token, but there isn't one for 'time-input'. + fillColor: hourMinuteColor, + // This should be derived from a token, but there isn't one for 'time-input'. + focusColor: _colors.primaryContainer, + enabledBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: const BorderSide(color: Colors.transparent), + ), + errorBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: BorderSide(color: _colors.error, width: 2), + ), + focusedBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: BorderSide(color: _colors.primary, width: 2), + ), + focusedErrorBorder: OutlineInputBorder( + borderRadius: selectorRadius, + borderSide: BorderSide(color: _colors.error, width: 2), + ), + hintStyle: hourMinuteTextStyle.copyWith(color: _colors.onSurface.withOpacity(0.36)), + // Prevent the error text from appearing. + // TODO(rami-a): Remove this workaround once + // https://github.com/flutter/flutter/issues/54104 + // is fixed. + errorStyle: const TextStyle(fontSize: 0, height: 0), + ); + } + + @override + ShapeBorder get shape { + return const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(28.0))); + } +} + +// END GENERATED TOKEN PROPERTIES - TimePicker diff --git a/packages/flutter/lib/src/material/time_picker_theme.dart b/packages/flutter/lib/src/material/time_picker_theme.dart index ea6cc946d2f88..1f44f35244292 100644 --- a/packages/flutter/lib/src/material/time_picker_theme.dart +++ b/packages/flutter/lib/src/material/time_picker_theme.dart @@ -2,10 +2,14 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'dart:ui'; + import 'package:flutter/foundation.dart'; import 'package:flutter/widgets.dart'; +import 'button_style.dart'; import 'input_decorator.dart'; +import 'material_state.dart'; import 'theme.dart'; // Examples can assume: @@ -36,22 +40,27 @@ class TimePickerThemeData with Diagnosticable { /// [ThemeData.timePickerTheme]. const TimePickerThemeData({ this.backgroundColor, - this.hourMinuteTextColor, - this.hourMinuteColor, - this.dayPeriodTextColor, + this.cancelButtonStyle, + this.confirmButtonStyle, + this.dayPeriodBorderSide, this.dayPeriodColor, - this.dialHandColor, + this.dayPeriodShape, + this.dayPeriodTextColor, + this.dayPeriodTextStyle, this.dialBackgroundColor, + this.dialHandColor, this.dialTextColor, + this.dialTextStyle, + this.elevation, this.entryModeIconColor, - this.hourMinuteTextStyle, - this.dayPeriodTextStyle, this.helpTextStyle, - this.shape, + this.hourMinuteColor, this.hourMinuteShape, - this.dayPeriodShape, - this.dayPeriodBorderSide, + this.hourMinuteTextColor, + this.hourMinuteTextStyle, this.inputDecorationTheme, + this.padding, + this.shape, }); /// The background color of a time picker. @@ -60,19 +69,29 @@ class TimePickerThemeData with Diagnosticable { /// [ColorScheme.background]. final Color? backgroundColor; - /// The color of the header text that represents hours and minutes. + /// The style of the cancel button of a [TimePickerDialog]. + final ButtonStyle? cancelButtonStyle; + + /// The style of the conform (OK) button of a [TimePickerDialog]. + final ButtonStyle? confirmButtonStyle; + + /// The color and weight of the day period's outline. /// - /// If [hourMinuteTextColor] is a [MaterialStateColor], then the effective - /// text color can depend on the [MaterialState.selected] state, i.e. if the - /// text is selected or not. + /// If this is null, the time picker defaults to: /// - /// By default the overall theme's [ColorScheme.primary] color is used when - /// the text is selected and [ColorScheme.onSurface] when it's not selected. - final Color? hourMinuteTextColor; + /// ```dart + /// BorderSide( + /// color: Color.alphaBlend( + /// Theme.of(context).colorScheme.onBackground.withOpacity(0.38), + /// Theme.of(context).colorScheme.surface, + /// ), + /// ), + /// ``` + final BorderSide? dayPeriodBorderSide; - /// The background color of the hour and minutes header segments. + /// The background color of the AM/PM toggle. /// - /// If [hourMinuteColor] is a [MaterialStateColor], then the effective + /// If [dayPeriodColor] is a [MaterialStateColor], then the effective /// background color can depend on the [MaterialState.selected] state, i.e. /// if the segment is selected or not. /// @@ -81,9 +100,21 @@ class TimePickerThemeData with Diagnosticable { /// brightness is [Brightness.light] and /// `ColorScheme.primary.withOpacity(0.24)` is used when the overall theme's /// brightness is [Brightness.dark]. - /// If the segment is not selected, the overall theme's - /// `ColorScheme.onSurface.withOpacity(0.12)` is used. - final Color? hourMinuteColor; + /// If the segment is not selected, [Colors.transparent] is used to allow the + /// [Dialog]'s color to be used. + final Color? dayPeriodColor; + + /// The shape of the day period that the time picker uses. + /// + /// If this is null, the time picker defaults to: + /// + /// ```dart + /// const RoundedRectangleBorder( + /// borderRadius: BorderRadius.all(Radius.circular(4.0)), + /// side: BorderSide(), + /// ) + /// ``` + final OutlinedBorder? dayPeriodShape; /// The color of the day period text that represents AM/PM. /// @@ -96,32 +127,25 @@ class TimePickerThemeData with Diagnosticable { /// it's not selected. final Color? dayPeriodTextColor; - /// The background color of the AM/PM toggle. - /// - /// If [dayPeriodColor] is a [MaterialStateColor], then the effective - /// background color can depend on the [MaterialState.selected] state, i.e. - /// if the segment is selected or not. + /// Used to configure the [TextStyle]s for the day period control. /// - /// By default, if the segment is selected, the overall theme's - /// `ColorScheme.primary.withOpacity(0.12)` is used when the overall theme's - /// brightness is [Brightness.light] and - /// `ColorScheme.primary.withOpacity(0.24)` is used when the overall theme's - /// brightness is [Brightness.dark]. - /// If the segment is not selected, [Colors.transparent] is used to allow the - /// [Dialog]'s color to be used. - final Color? dayPeriodColor; + /// If this is null, the time picker defaults to the overall theme's + /// [TextTheme.titleMedium]. + final TextStyle? dayPeriodTextStyle; - /// The color of the time picker dial's hand. + /// The background color of the time picker dial when the entry mode is + /// [TimePickerEntryMode.dial] or [TimePickerEntryMode.dialOnly]. /// /// If this is null, the time picker defaults to the overall theme's /// [ColorScheme.primary]. - final Color? dialHandColor; + final Color? dialBackgroundColor; - /// The background color of the time picker dial. + /// The color of the time picker dial's hand when the entry mode is + /// [TimePickerEntryMode.dial] or [TimePickerEntryMode.dialOnly]. /// /// If this is null, the time picker defaults to the overall theme's /// [ColorScheme.primary]. - final Color? dialBackgroundColor; + final Color? dialHandColor; /// The color of the dial text that represents specific hours and minutes. /// @@ -133,10 +157,24 @@ class TimePickerThemeData with Diagnosticable { /// theme's [ThemeData.colorScheme]. final Color? dialTextColor; + /// The [TextStyle] for the numbers on the time selection dial. + /// + /// If [dialTextStyle]'s [TextStyle.color] is a [MaterialStateColor], then the + /// effective text color can depend on the [MaterialState.selected] state, + /// i.e. if the text is selected or not. + /// + /// If this style is null then the dial's text style is based on the theme's + /// [ThemeData.textTheme]. + final TextStyle? dialTextStyle; + + /// The Material elevation for the time picker dialog. + final double? elevation; + /// The color of the entry mode [IconButton]. /// /// If this is null, the time picker defaults to: /// + /// /// ```dart /// Theme.of(context).colorScheme.onSurface.withOpacity( /// Theme.of(context).colorScheme.brightness == Brightness.dark ? 1.0 : 0.6, @@ -144,29 +182,26 @@ class TimePickerThemeData with Diagnosticable { /// ``` final Color? entryModeIconColor; - /// Used to configure the [TextStyle]s for the hour/minute controls. - /// - /// If this is null, the time picker defaults to the overall theme's - /// [TextTheme.headline3]. - final TextStyle? hourMinuteTextStyle; - - /// Used to configure the [TextStyle]s for the day period control. - /// - /// If this is null, the time picker defaults to the overall theme's - /// [TextTheme.titleMedium]. - final TextStyle? dayPeriodTextStyle; - /// Used to configure the [TextStyle]s for the helper text in the header. /// /// If this is null, the time picker defaults to the overall theme's /// [TextTheme.labelSmall]. final TextStyle? helpTextStyle; - /// The shape of the [Dialog] that the time picker is presented in. + /// The background color of the hour and minute header segments. /// - /// If this is null, the time picker defaults to - /// `RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))`. - final ShapeBorder? shape; + /// If [hourMinuteColor] is a [MaterialStateColor], then the effective + /// background color can depend on the [MaterialState.selected] state, i.e. + /// if the segment is selected or not. + /// + /// By default, if the segment is selected, the overall theme's + /// `ColorScheme.primary.withOpacity(0.12)` is used when the overall theme's + /// brightness is [Brightness.light] and + /// `ColorScheme.primary.withOpacity(0.24)` is used when the overall theme's + /// brightness is [Brightness.dark]. + /// If the segment is not selected, the overall theme's + /// `ColorScheme.onSurface.withOpacity(0.12)` is used. + final Color? hourMinuteColor; /// The shape of the hour and minute controls that the time picker uses. /// @@ -174,76 +209,87 @@ class TimePickerThemeData with Diagnosticable { /// `RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))`. final ShapeBorder? hourMinuteShape; - /// The shape of the day period that the time picker uses. + /// The color of the header text that represents hours and minutes. /// - /// If this is null, the time picker defaults to: + /// If [hourMinuteTextColor] is a [MaterialStateColor], then the effective + /// text color can depend on the [MaterialState.selected] state, i.e. if the + /// text is selected or not. /// - /// ```dart - /// const RoundedRectangleBorder( - /// borderRadius: BorderRadius.all(Radius.circular(4.0)), - /// side: BorderSide(), - /// ) - /// ``` - final OutlinedBorder? dayPeriodShape; + /// By default the overall theme's [ColorScheme.primary] color is used when + /// the text is selected and [ColorScheme.onSurface] when it's not selected. + final Color? hourMinuteTextColor; - /// The color and weight of the day period's outline. - /// - /// If this is null, the time picker defaults to: + /// Used to configure the [TextStyle]s for the hour/minute controls. /// - /// ```dart - /// BorderSide( - /// color: Color.alphaBlend( - /// Theme.of(context).colorScheme.onBackground.withOpacity(0.38), - /// Theme.of(context).colorScheme.surface, - /// ), - /// ), - /// ``` - final BorderSide? dayPeriodBorderSide; + /// If this is null, the time picker defaults to the overall theme's + /// [TextTheme.headline3]. + final TextStyle? hourMinuteTextStyle; /// The input decoration theme for the [TextField]s in the time picker. /// /// If this is null, the time picker provides its own defaults. final InputDecorationTheme? inputDecorationTheme; + /// The padding around the time picker dialog when the entry mode is + /// [TimePickerEntryMode.dial] or [TimePickerEntryMode.dialOnly]. + final EdgeInsetsGeometry? padding; + + /// The shape of the [Dialog] that the time picker is presented in. + /// + /// If this is null, the time picker defaults to + /// `RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))`. + final ShapeBorder? shape; + /// Creates a copy of this object with the given fields replaced with the /// new values. TimePickerThemeData copyWith({ Color? backgroundColor, - Color? hourMinuteTextColor, - Color? hourMinuteColor, - Color? dayPeriodTextColor, + ButtonStyle? cancelButtonStyle, + ButtonStyle? confirmButtonStyle, + ButtonStyle? dayPeriodButtonStyle, + BorderSide? dayPeriodBorderSide, Color? dayPeriodColor, - Color? dialHandColor, + OutlinedBorder? dayPeriodShape, + Color? dayPeriodTextColor, + TextStyle? dayPeriodTextStyle, Color? dialBackgroundColor, + Color? dialHandColor, Color? dialTextColor, + TextStyle? dialTextStyle, + double? elevation, Color? entryModeIconColor, - TextStyle? hourMinuteTextStyle, - TextStyle? dayPeriodTextStyle, TextStyle? helpTextStyle, - ShapeBorder? shape, + Color? hourMinuteColor, ShapeBorder? hourMinuteShape, - OutlinedBorder? dayPeriodShape, - BorderSide? dayPeriodBorderSide, + Color? hourMinuteTextColor, + TextStyle? hourMinuteTextStyle, InputDecorationTheme? inputDecorationTheme, + EdgeInsetsGeometry? padding, + ShapeBorder? shape, }) { return TimePickerThemeData( backgroundColor: backgroundColor ?? this.backgroundColor, - hourMinuteTextColor: hourMinuteTextColor ?? this.hourMinuteTextColor, - hourMinuteColor: hourMinuteColor ?? this.hourMinuteColor, - dayPeriodTextColor: dayPeriodTextColor ?? this.dayPeriodTextColor, + cancelButtonStyle: cancelButtonStyle ?? this.cancelButtonStyle, + confirmButtonStyle: confirmButtonStyle ?? this.confirmButtonStyle, + dayPeriodBorderSide: dayPeriodBorderSide ?? this.dayPeriodBorderSide, dayPeriodColor: dayPeriodColor ?? this.dayPeriodColor, - dialHandColor: dialHandColor ?? this.dialHandColor, + dayPeriodShape: dayPeriodShape ?? this.dayPeriodShape, + dayPeriodTextColor: dayPeriodTextColor ?? this.dayPeriodTextColor, + dayPeriodTextStyle: dayPeriodTextStyle ?? this.dayPeriodTextStyle, dialBackgroundColor: dialBackgroundColor ?? this.dialBackgroundColor, + dialHandColor: dialHandColor ?? this.dialHandColor, dialTextColor: dialTextColor ?? this.dialTextColor, + dialTextStyle: dialTextStyle ?? this.dialTextStyle, + elevation: elevation ?? this.elevation, entryModeIconColor: entryModeIconColor ?? this.entryModeIconColor, - hourMinuteTextStyle: hourMinuteTextStyle ?? this.hourMinuteTextStyle, - dayPeriodTextStyle: dayPeriodTextStyle ?? this.dayPeriodTextStyle, helpTextStyle: helpTextStyle ?? this.helpTextStyle, - shape: shape ?? this.shape, + hourMinuteColor: hourMinuteColor ?? this.hourMinuteColor, hourMinuteShape: hourMinuteShape ?? this.hourMinuteShape, - dayPeriodShape: dayPeriodShape ?? this.dayPeriodShape, - dayPeriodBorderSide: dayPeriodBorderSide ?? this.dayPeriodBorderSide, + hourMinuteTextColor: hourMinuteTextColor ?? this.hourMinuteTextColor, + hourMinuteTextStyle: hourMinuteTextStyle ?? this.hourMinuteTextStyle, inputDecorationTheme: inputDecorationTheme ?? this.inputDecorationTheme, + padding: padding ?? this.padding, + shape: shape ?? this.shape, ); } @@ -268,45 +314,55 @@ class TimePickerThemeData with Diagnosticable { } return TimePickerThemeData( backgroundColor: Color.lerp(a?.backgroundColor, b?.backgroundColor, t), - hourMinuteTextColor: Color.lerp(a?.hourMinuteTextColor, b?.hourMinuteTextColor, t), - hourMinuteColor: Color.lerp(a?.hourMinuteColor, b?.hourMinuteColor, t), - dayPeriodTextColor: Color.lerp(a?.dayPeriodTextColor, b?.dayPeriodTextColor, t), + cancelButtonStyle: ButtonStyle.lerp(a?.cancelButtonStyle, b?.cancelButtonStyle, t), + confirmButtonStyle: ButtonStyle.lerp(a?.confirmButtonStyle, b?.confirmButtonStyle, t), + dayPeriodBorderSide: lerpedBorderSide, dayPeriodColor: Color.lerp(a?.dayPeriodColor, b?.dayPeriodColor, t), - dialHandColor: Color.lerp(a?.dialHandColor, b?.dialHandColor, t), + dayPeriodShape: ShapeBorder.lerp(a?.dayPeriodShape, b?.dayPeriodShape, t) as OutlinedBorder?, + dayPeriodTextColor: Color.lerp(a?.dayPeriodTextColor, b?.dayPeriodTextColor, t), + dayPeriodTextStyle: TextStyle.lerp(a?.dayPeriodTextStyle, b?.dayPeriodTextStyle, t), dialBackgroundColor: Color.lerp(a?.dialBackgroundColor, b?.dialBackgroundColor, t), + dialHandColor: Color.lerp(a?.dialHandColor, b?.dialHandColor, t), dialTextColor: Color.lerp(a?.dialTextColor, b?.dialTextColor, t), + dialTextStyle: TextStyle.lerp(a?.dialTextStyle, b?.dialTextStyle, t), + elevation: lerpDouble(a?.elevation, b?.elevation, t), entryModeIconColor: Color.lerp(a?.entryModeIconColor, b?.entryModeIconColor, t), - hourMinuteTextStyle: TextStyle.lerp(a?.hourMinuteTextStyle, b?.hourMinuteTextStyle, t), - dayPeriodTextStyle: TextStyle.lerp(a?.dayPeriodTextStyle, b?.dayPeriodTextStyle, t), helpTextStyle: TextStyle.lerp(a?.helpTextStyle, b?.helpTextStyle, t), - shape: ShapeBorder.lerp(a?.shape, b?.shape, t), + hourMinuteColor: Color.lerp(a?.hourMinuteColor, b?.hourMinuteColor, t), hourMinuteShape: ShapeBorder.lerp(a?.hourMinuteShape, b?.hourMinuteShape, t), - dayPeriodShape: ShapeBorder.lerp(a?.dayPeriodShape, b?.dayPeriodShape, t) as OutlinedBorder?, - dayPeriodBorderSide: lerpedBorderSide, + hourMinuteTextColor: Color.lerp(a?.hourMinuteTextColor, b?.hourMinuteTextColor, t), + hourMinuteTextStyle: TextStyle.lerp(a?.hourMinuteTextStyle, b?.hourMinuteTextStyle, t), inputDecorationTheme: t < 0.5 ? a?.inputDecorationTheme : b?.inputDecorationTheme, + padding: EdgeInsetsGeometry.lerp(a?.padding, b?.padding, t), + shape: ShapeBorder.lerp(a?.shape, b?.shape, t), ); } @override - int get hashCode => Object.hash( + int get hashCode => Object.hashAll([ backgroundColor, - hourMinuteTextColor, - hourMinuteColor, - dayPeriodTextColor, + cancelButtonStyle, + confirmButtonStyle, + dayPeriodBorderSide, dayPeriodColor, - dialHandColor, + dayPeriodShape, + dayPeriodTextColor, + dayPeriodTextStyle, dialBackgroundColor, + dialHandColor, dialTextColor, + dialTextStyle, + elevation, entryModeIconColor, - hourMinuteTextStyle, - dayPeriodTextStyle, helpTextStyle, - shape, + hourMinuteColor, hourMinuteShape, - dayPeriodShape, - dayPeriodBorderSide, + hourMinuteTextColor, + hourMinuteTextStyle, inputDecorationTheme, - ); + padding, + shape, + ]); @override bool operator ==(Object other) { @@ -318,44 +374,54 @@ class TimePickerThemeData with Diagnosticable { } return other is TimePickerThemeData && other.backgroundColor == backgroundColor - && other.hourMinuteTextColor == hourMinuteTextColor - && other.hourMinuteColor == hourMinuteColor - && other.dayPeriodTextColor == dayPeriodTextColor + && other.cancelButtonStyle == cancelButtonStyle + && other.confirmButtonStyle == confirmButtonStyle + && other.dayPeriodBorderSide == dayPeriodBorderSide && other.dayPeriodColor == dayPeriodColor - && other.dialHandColor == dialHandColor + && other.dayPeriodShape == dayPeriodShape + && other.dayPeriodTextColor == dayPeriodTextColor + && other.dayPeriodTextStyle == dayPeriodTextStyle && other.dialBackgroundColor == dialBackgroundColor + && other.dialHandColor == dialHandColor && other.dialTextColor == dialTextColor + && other.dialTextStyle == dialTextStyle + && other.elevation == elevation && other.entryModeIconColor == entryModeIconColor - && other.hourMinuteTextStyle == hourMinuteTextStyle - && other.dayPeriodTextStyle == dayPeriodTextStyle && other.helpTextStyle == helpTextStyle - && other.shape == shape + && other.hourMinuteColor == hourMinuteColor && other.hourMinuteShape == hourMinuteShape - && other.dayPeriodShape == dayPeriodShape - && other.dayPeriodBorderSide == dayPeriodBorderSide - && other.inputDecorationTheme == inputDecorationTheme; + && other.hourMinuteTextColor == hourMinuteTextColor + && other.hourMinuteTextStyle == hourMinuteTextStyle + && other.inputDecorationTheme == inputDecorationTheme + && other.padding == padding + && other.shape == shape; } @override void debugFillProperties(DiagnosticPropertiesBuilder properties) { super.debugFillProperties(properties); properties.add(ColorProperty('backgroundColor', backgroundColor, defaultValue: null)); - properties.add(ColorProperty('hourMinuteTextColor', hourMinuteTextColor, defaultValue: null)); - properties.add(ColorProperty('hourMinuteColor', hourMinuteColor, defaultValue: null)); - properties.add(ColorProperty('dayPeriodTextColor', dayPeriodTextColor, defaultValue: null)); + properties.add(DiagnosticsProperty('cancelButtonStyle', cancelButtonStyle, defaultValue: null)); + properties.add(DiagnosticsProperty('confirmButtonStyle', confirmButtonStyle, defaultValue: null)); + properties.add(DiagnosticsProperty('dayPeriodBorderSide', dayPeriodBorderSide, defaultValue: null)); properties.add(ColorProperty('dayPeriodColor', dayPeriodColor, defaultValue: null)); - properties.add(ColorProperty('dialHandColor', dialHandColor, defaultValue: null)); + properties.add(DiagnosticsProperty('dayPeriodShape', dayPeriodShape, defaultValue: null)); + properties.add(ColorProperty('dayPeriodTextColor', dayPeriodTextColor, defaultValue: null)); + properties.add(DiagnosticsProperty('dayPeriodTextStyle', dayPeriodTextStyle, defaultValue: null)); properties.add(ColorProperty('dialBackgroundColor', dialBackgroundColor, defaultValue: null)); + properties.add(ColorProperty('dialHandColor', dialHandColor, defaultValue: null)); properties.add(ColorProperty('dialTextColor', dialTextColor, defaultValue: null)); + properties.add(DiagnosticsProperty('dialTextStyle', dialTextStyle, defaultValue: null)); + properties.add(DoubleProperty('elevation', elevation, defaultValue: null)); properties.add(ColorProperty('entryModeIconColor', entryModeIconColor, defaultValue: null)); - properties.add(DiagnosticsProperty('hourMinuteTextStyle', hourMinuteTextStyle, defaultValue: null)); - properties.add(DiagnosticsProperty('dayPeriodTextStyle', dayPeriodTextStyle, defaultValue: null)); properties.add(DiagnosticsProperty('helpTextStyle', helpTextStyle, defaultValue: null)); - properties.add(DiagnosticsProperty('shape', shape, defaultValue: null)); + properties.add(ColorProperty('hourMinuteColor', hourMinuteColor, defaultValue: null)); properties.add(DiagnosticsProperty('hourMinuteShape', hourMinuteShape, defaultValue: null)); - properties.add(DiagnosticsProperty('dayPeriodShape', dayPeriodShape, defaultValue: null)); - properties.add(DiagnosticsProperty('dayPeriodBorderSide', dayPeriodBorderSide, defaultValue: null)); + properties.add(ColorProperty('hourMinuteTextColor', hourMinuteTextColor, defaultValue: null)); + properties.add(DiagnosticsProperty('hourMinuteTextStyle', hourMinuteTextStyle, defaultValue: null)); properties.add(DiagnosticsProperty('inputDecorationTheme', inputDecorationTheme, defaultValue: null)); + properties.add(DiagnosticsProperty('padding', padding, defaultValue: null)); + properties.add(DiagnosticsProperty('shape', shape, defaultValue: null)); } } diff --git a/packages/flutter/test/material/time_picker_test.dart b/packages/flutter/test/material/time_picker_test.dart index 1d1fb78120b33..2b5a322fed826 100644 --- a/packages/flutter/test/material/time_picker_test.dart +++ b/packages/flutter/test/material/time_picker_test.dart @@ -12,9 +12,1583 @@ import 'package:flutter_test/flutter_test.dart'; import '../widgets/semantics_tester.dart'; import 'feedback_tester.dart'; +void main() { + for (final MaterialType materialType in MaterialType.values) { + final String selectTimeString; + final String enterTimeString; + final String cancelString; + const String okString = 'OK'; + const String amString = 'AM'; + const String pmString = 'PM'; + switch (materialType) { + case MaterialType.material2: + selectTimeString = 'SELECT TIME'; + enterTimeString = 'ENTER TIME'; + cancelString = 'CANCEL'; + break; + case MaterialType.material3: + selectTimeString = 'Select time'; + enterTimeString = 'Enter time'; + cancelString = 'Cancel'; + break; + } + + group('Dial (${materialType.name})', () { + testWidgets('tap-select an hour', (WidgetTester tester) async { + TimeOfDay? result; + + Offset center = (await startPicker(tester, (TimeOfDay? time) { + result = time; + }, materialType: materialType))!; + await tester.tapAt(Offset(center.dx, center.dy - 50)); // 12:00 AM + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 0, minute: 0))); + + center = (await startPicker(tester, (TimeOfDay? time) { + result = time; + }, materialType: materialType))!; + await tester.tapAt(Offset(center.dx + 50, center.dy)); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 3, minute: 0))); + + center = (await startPicker(tester, (TimeOfDay? time) { + result = time; + }, materialType: materialType))!; + await tester.tapAt(Offset(center.dx, center.dy + 50)); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 0))); + + center = (await startPicker(tester, (TimeOfDay? time) { + result = time; + }, materialType: materialType))!; + await tester.tapAt(Offset(center.dx, center.dy + 50)); + await tester.tapAt(Offset(center.dx - 50, center.dy)); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 9, minute: 0))); + }); + + testWidgets('drag-select an hour', (WidgetTester tester) async { + late TimeOfDay result; + + final Offset center = (await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType))!; + final Offset hour0 = Offset(center.dx, center.dy - 50); // 12:00 AM + final Offset hour3 = Offset(center.dx + 50, center.dy); + final Offset hour6 = Offset(center.dx, center.dy + 50); + final Offset hour9 = Offset(center.dx - 50, center.dy); + + TestGesture gesture; + + gesture = await tester.startGesture(hour3); + await gesture.moveBy(hour0 - hour3); + await gesture.up(); + await finishPicker(tester); + expect(result.hour, 0); + + expect( + await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType), + equals(center), + ); + gesture = await tester.startGesture(hour0); + await gesture.moveBy(hour3 - hour0); + await gesture.up(); + await finishPicker(tester); + expect(result.hour, 3); + + expect( + await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType), + equals(center), + ); + gesture = await tester.startGesture(hour3); + await gesture.moveBy(hour6 - hour3); + await gesture.up(); + await finishPicker(tester); + expect(result.hour, equals(6)); + + expect( + await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType), + equals(center), + ); + gesture = await tester.startGesture(hour6); + await gesture.moveBy(hour9 - hour6); + await gesture.up(); + await finishPicker(tester); + expect(result.hour, equals(9)); + }); + + testWidgets('tap-select switches from hour to minute', (WidgetTester tester) async { + late TimeOfDay result; + + final Offset center = (await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType))!; + final Offset hour6 = Offset(center.dx, center.dy + 50); // 6:00 + final Offset min45 = Offset(center.dx - 50, center.dy); // 45 mins (or 9:00 hours) + + await tester.tapAt(hour6); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tapAt(min45); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); + }); + + testWidgets('drag-select switches from hour to minute', (WidgetTester tester) async { + late TimeOfDay result; + + final Offset center = (await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType))!; + final Offset hour3 = Offset(center.dx + 50, center.dy); + final Offset hour6 = Offset(center.dx, center.dy + 50); + final Offset hour9 = Offset(center.dx - 50, center.dy); + + TestGesture gesture = await tester.startGesture(hour6); + await gesture.moveBy(hour9 - hour6); + await gesture.up(); + await tester.pump(const Duration(milliseconds: 50)); + gesture = await tester.startGesture(hour6); + await gesture.moveBy(hour3 - hour6); + await gesture.up(); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 9, minute: 15))); + }); + + testWidgets('tap-select rounds down to nearest 5 minute increment', (WidgetTester tester) async { + late TimeOfDay result; + + final Offset center = (await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType))!; + final Offset hour6 = Offset(center.dx, center.dy + 50); // 6:00 + final Offset min46 = Offset(center.dx - 50, center.dy - 5); // 46 mins + + await tester.tapAt(hour6); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tapAt(min46); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); + }); + + testWidgets('tap-select rounds up to nearest 5 minute increment', (WidgetTester tester) async { + late TimeOfDay result; + + final Offset center = (await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, materialType: materialType))!; + final Offset hour6 = Offset(center.dx, center.dy + 50); // 6:00 + final Offset min48 = Offset(center.dx - 50, center.dy - 15); // 48 mins + + await tester.tapAt(hour6); + await tester.pump(const Duration(milliseconds: 50)); + await tester.tapAt(min48); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 50))); + }); + }); + + group('Dial Haptic Feedback (${materialType.name})', () { + const Duration kFastFeedbackInterval = Duration(milliseconds: 10); + const Duration kSlowFeedbackInterval = Duration(milliseconds: 200); + late FeedbackTester feedback; + + setUp(() { + feedback = FeedbackTester(); + }); + + tearDown(() { + feedback.dispose(); + }); + + testWidgets('tap-select vibrates once', (WidgetTester tester) async { + final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!; + await tester.tapAt(Offset(center.dx, center.dy - 50)); + await finishPicker(tester); + expect(feedback.hapticCount, 1); + }); + + testWidgets('quick successive tap-selects vibrate once', (WidgetTester tester) async { + final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!; + await tester.tapAt(Offset(center.dx, center.dy - 50)); + await tester.pump(kFastFeedbackInterval); + await tester.tapAt(Offset(center.dx, center.dy + 50)); + await finishPicker(tester); + expect(feedback.hapticCount, 1); + }); + + testWidgets('slow successive tap-selects vibrate once per tap', (WidgetTester tester) async { + final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!; + await tester.tapAt(Offset(center.dx, center.dy - 50)); + await tester.pump(kSlowFeedbackInterval); + await tester.tapAt(Offset(center.dx, center.dy + 50)); + await tester.pump(kSlowFeedbackInterval); + await tester.tapAt(Offset(center.dx, center.dy - 50)); + await finishPicker(tester); + expect(feedback.hapticCount, 3); + }); + + testWidgets('drag-select vibrates once', (WidgetTester tester) async { + final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!; + final Offset hour0 = Offset(center.dx, center.dy - 50); + final Offset hour3 = Offset(center.dx + 50, center.dy); + + final TestGesture gesture = await tester.startGesture(hour3); + await gesture.moveBy(hour0 - hour3); + await gesture.up(); + await finishPicker(tester); + expect(feedback.hapticCount, 1); + }); + + testWidgets('quick drag-select vibrates once', (WidgetTester tester) async { + final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!; + final Offset hour0 = Offset(center.dx, center.dy - 50); + final Offset hour3 = Offset(center.dx + 50, center.dy); + + final TestGesture gesture = await tester.startGesture(hour3); + await gesture.moveBy(hour0 - hour3); + await tester.pump(kFastFeedbackInterval); + await gesture.moveBy(hour3 - hour0); + await tester.pump(kFastFeedbackInterval); + await gesture.moveBy(hour0 - hour3); + await gesture.up(); + await finishPicker(tester); + expect(feedback.hapticCount, 1); + }); + + testWidgets('slow drag-select vibrates once', (WidgetTester tester) async { + final Offset center = (await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType))!; + final Offset hour0 = Offset(center.dx, center.dy - 50); + final Offset hour3 = Offset(center.dx + 50, center.dy); + + final TestGesture gesture = await tester.startGesture(hour3); + await gesture.moveBy(hour0 - hour3); + await tester.pump(kSlowFeedbackInterval); + await gesture.moveBy(hour3 - hour0); + await tester.pump(kSlowFeedbackInterval); + await gesture.moveBy(hour0 - hour3); + await gesture.up(); + await finishPicker(tester); + expect(feedback.hapticCount, 3); + }); + }); + + group('Dialog (${materialType.name})', () { + testWidgets('Widgets have correct label capitalization', (WidgetTester tester) async { + await startPicker(tester, (TimeOfDay? time) {}, materialType: materialType); + expect(find.text(selectTimeString), findsOneWidget); + expect(find.text(cancelString), findsOneWidget); + }); + + testWidgets('Widgets have correct label capitalization in input mode', (WidgetTester tester) async { + await startPicker(tester, (TimeOfDay? time) {}, + entryMode: TimePickerEntryMode.input, materialType: materialType); + expect(find.text(enterTimeString), findsOneWidget); + expect(find.text(cancelString), findsOneWidget); + }); + + testWidgets('respects MediaQueryData.alwaysUse24HourFormat == false', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, materialType: materialType); + const List labels12To11 = ['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']; + + final CustomPaint dialPaint = tester.widget(findDialPaint); + final dynamic dialPainter = dialPaint.painter; + // ignore: avoid_dynamic_calls + final List primaryLabels = dialPainter.primaryLabels as List; + // ignore: avoid_dynamic_calls + expect(primaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels12To11); + + // ignore: avoid_dynamic_calls + final List selectedLabels = dialPainter.selectedLabels as List; + // ignore: avoid_dynamic_calls + expect(selectedLabels.map((dynamic tp) => tp.painter.text.text as String), labels12To11); + }); + + switch (materialType) { + case MaterialType.material2: + testWidgets('respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: materialType); + + final List labels00To22 = List.generate(12, (int index) { + return (index * 2).toString().padLeft(2, '0'); + }); + final CustomPaint dialPaint = tester.widget(findDialPaint); + final dynamic dialPainter = dialPaint.painter; + // ignore: avoid_dynamic_calls + final List primaryLabels = dialPainter.primaryLabels as List; + // ignore: avoid_dynamic_calls + expect(primaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels00To22); + + // ignore: avoid_dynamic_calls + final List selectedLabels = dialPainter.selectedLabels as List; + // ignore: avoid_dynamic_calls + expect(selectedLabels.map((dynamic tp) => tp.painter.text.text as String), labels00To22); + }); + break; + case MaterialType.material3: + testWidgets('respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: materialType); + + final List labels00To23 = List.generate(24, (int index) { + return index == 0 ? '00' : index.toString(); + }); + final List inner0To23 = List.generate(24, (int index) => index >= 12); + + final CustomPaint dialPaint = tester.widget(findDialPaint); + final dynamic dialPainter = dialPaint.painter; + // ignore: avoid_dynamic_calls + final List primaryLabels = dialPainter.primaryLabels as List; + // ignore: avoid_dynamic_calls + expect(primaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels00To23); + // ignore: avoid_dynamic_calls + expect(primaryLabels.map((dynamic tp) => tp.inner as bool), inner0To23); + + // ignore: avoid_dynamic_calls + final List selectedLabels = dialPainter.selectedLabels as List; + // ignore: avoid_dynamic_calls + expect(selectedLabels.map((dynamic tp) => tp.painter.text.text as String), labels00To23); + // ignore: avoid_dynamic_calls + expect(selectedLabels.map((dynamic tp) => tp.inner as bool), inner0To23); + }); + break; + } + + testWidgets('when change orientation, should reflect in render objects', (WidgetTester tester) async { + // portrait + tester.binding.window.physicalSizeTestValue = const Size(800, 800.5); + tester.binding.window.devicePixelRatioTestValue = 1; + await mediaQueryBoilerplate(tester, materialType: materialType); + + RenderObject render = tester.renderObject( + find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodInputPadding'), + ); + expect((render as dynamic).orientation, Orientation.portrait); // ignore: avoid_dynamic_calls + + // landscape + tester.binding.window.physicalSizeTestValue = const Size(800.5, 800); + tester.binding.window.devicePixelRatioTestValue = 1; + await mediaQueryBoilerplate(tester, tapButton: false, materialType: materialType); + + render = tester.renderObject( + find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodInputPadding'), + ); + expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls + + tester.binding.window.clearPhysicalSizeTestValue(); + tester.binding.window.clearDevicePixelRatioTestValue(); + }); + + testWidgets('setting orientation should override MediaQuery orientation', (WidgetTester tester) async { + // portrait media query + tester.binding.window.physicalSizeTestValue = const Size(800, 800.5); + tester.binding.window.devicePixelRatioTestValue = 1; + await mediaQueryBoilerplate(tester, orientation: Orientation.landscape, materialType: materialType); + + final RenderObject render = tester.renderObject( + find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodInputPadding'), + ); + expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls + + tester.binding.window.clearPhysicalSizeTestValue(); + tester.binding.window.clearDevicePixelRatioTestValue(); + }); + + testWidgets('builder parameter', (WidgetTester tester) async { + Widget buildFrame(TextDirection textDirection) { + return MaterialApp( + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + child: const Text('X'), + onPressed: () { + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + builder: (BuildContext context, Widget? child) { + return Directionality( + textDirection: textDirection, + child: child!, + ); + }, + ); + }, + ); + }, + ), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame(TextDirection.ltr)); + await tester.tap(find.text('X')); + await tester.pumpAndSettle(); + final double ltrOkRight = tester.getBottomRight(find.text(okString)).dx; + + await tester.tap(find.text(okString)); // dismiss the dialog + await tester.pumpAndSettle(); + + await tester.pumpWidget(buildFrame(TextDirection.rtl)); + await tester.tap(find.text('X')); + await tester.pumpAndSettle(); + + // Verify that the time picker is being laid out RTL. + // We expect the left edge of the 'OK' button in the RTL + // layout to match the gap between right edge of the 'OK' + // button and the right edge of the 800 wide window. + expect(tester.getBottomLeft(find.text(okString)).dx, 800 - ltrOkRight); + }); + + testWidgets('uses root navigator by default', (WidgetTester tester) async { + final PickerObserver rootObserver = PickerObserver(); + final PickerObserver nestedObserver = PickerObserver(); + + await tester.pumpWidget(MaterialApp( + navigatorObservers: [rootObserver], + home: Navigator( + observers: [nestedObserver], + onGenerateRoute: (RouteSettings settings) { + return MaterialPageRoute( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + ); + }, + child: const Text('Show Picker'), + ); + }, + ); + }, + ), + )); + + // Open the dialog. + await tester.tap(find.byType(ElevatedButton)); + + expect(rootObserver.pickerCount, 1); + expect(nestedObserver.pickerCount, 0); + }); + + testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async { + final PickerObserver rootObserver = PickerObserver(); + final PickerObserver nestedObserver = PickerObserver(); + + await tester.pumpWidget(MaterialApp( + navigatorObservers: [rootObserver], + home: Navigator( + observers: [nestedObserver], + onGenerateRoute: (RouteSettings settings) { + return MaterialPageRoute( + builder: (BuildContext context) { + return ElevatedButton( + onPressed: () { + showTimePicker( + context: context, + useRootNavigator: false, + initialTime: const TimeOfDay(hour: 7, minute: 0), + ); + }, + child: const Text('Show Picker'), + ); + }, + ); + }, + ), + )); + + // Open the dialog. + await tester.tap(find.byType(ElevatedButton)); + + expect(rootObserver.pickerCount, 0); + expect(nestedObserver.pickerCount, 1); + }); + + testWidgets('optional text parameters are utilized', (WidgetTester tester) async { + const String cancelText = 'Custom Cancel'; + const String confirmText = 'Custom OK'; + const String helperText = 'Custom Help'; + await tester.pumpWidget(MaterialApp( + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + child: const Text('X'), + onPressed: () async { + await showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + cancelText: cancelText, + confirmText: confirmText, + helpText: helperText, + ); + }, + ); + }, + ), + ), + ), + )); + + // Open the picker. + await tester.tap(find.text('X')); + await tester.pumpAndSettle(const Duration(seconds: 1)); + + expect(find.text(cancelText), findsOneWidget); + expect(find.text(confirmText), findsOneWidget); + expect(find.text(helperText), findsOneWidget); + }); + + testWidgets('OK Cancel button and helpText layout', (WidgetTester tester) async { + Widget buildFrame(TextDirection textDirection) { + return MaterialApp( + theme: ThemeData.light().copyWith(useMaterial3: materialType == MaterialType.material3), + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + child: const Text('X'), + onPressed: () { + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + builder: (BuildContext context, Widget? child) { + return Directionality( + textDirection: textDirection, + child: child!, + ); + }, + ); + }, + ); + }, + ), + ), + ), + ); + } + + await tester.pumpWidget(buildFrame(TextDirection.ltr)); + await tester.tap(find.text('X')); + await tester.pumpAndSettle(); + + switch (materialType) { + case MaterialType.material2: + expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(154, 155))); + expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(281, 165))); + break; + case MaterialType.material3: + expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(138, 129))); + expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(292.0, 143.0))); + break; + } + expect(tester.getBottomRight(find.text(okString)).dx, 644); + expect(tester.getBottomLeft(find.text(okString)).dx, 616); + expect(tester.getBottomRight(find.text(cancelString)).dx, 582); + + await tester.tap(find.text(okString)); + await tester.pumpAndSettle(); + + await tester.pumpWidget(buildFrame(TextDirection.rtl)); + await tester.tap(find.text('X')); + await tester.pumpAndSettle(); + + switch (materialType) { + case MaterialType.material2: + expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(519, 155))); + expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(646, 165))); + break; + case MaterialType.material3: + expect(tester.getTopLeft(find.text(selectTimeString)), equals(const Offset(508, 129))); + expect(tester.getBottomRight(find.text(selectTimeString)), equals(const Offset(662, 143))); + break; + } + expect(tester.getBottomLeft(find.text(okString)).dx, 156); + expect(tester.getBottomRight(find.text(okString)).dx, 184); + expect(tester.getBottomLeft(find.text(cancelString)).dx, 218); + + await tester.tap(find.text(okString)); + await tester.pumpAndSettle(); + }); + + testWidgets('text scale affects certain elements and not others', (WidgetTester tester) async { + await mediaQueryBoilerplate( + tester, + initialTime: const TimeOfDay(hour: 7, minute: 41), + materialType: materialType, + ); + + final double minutesDisplayHeight = tester.getSize(find.text('41')).height; + final double amHeight = tester.getSize(find.text(amString)).height; + + await tester.tap(find.text(okString)); // dismiss the dialog + await tester.pumpAndSettle(); + + // Verify that the time display is not affected by text scale. + await mediaQueryBoilerplate( + tester, + textScaleFactor: 2, + initialTime: const TimeOfDay(hour: 7, minute: 41), + materialType: materialType, + ); + + final double amHeight2x = tester.getSize(find.text(amString)).height; + expect(tester.getSize(find.text('41')).height, equals(minutesDisplayHeight)); + expect(amHeight2x, greaterThanOrEqualTo(amHeight * 2)); + + await tester.tap(find.text(okString)); // dismiss the dialog + await tester.pumpAndSettle(); + + // Verify that text scale for AM/PM is at most 2x. + await mediaQueryBoilerplate( + tester, + textScaleFactor: 3, + initialTime: const TimeOfDay(hour: 7, minute: 41), + materialType: materialType, + ); + + expect(tester.getSize(find.text('41')).height, equals(minutesDisplayHeight)); + expect(tester.getSize(find.text(amString)).height, equals(amHeight2x)); + }); + + group('showTimePicker avoids overlapping display features', () { + testWidgets('positioning with anchorPoint', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + builder: (BuildContext context, Widget? child) { + return MediaQuery( + // Display has a vertical hinge down the middle + data: const MediaQueryData( + size: Size(800, 600), + displayFeatures: [ + DisplayFeature( + bounds: Rect.fromLTRB(390, 0, 410, 600), + type: DisplayFeatureType.hinge, + state: DisplayFeatureState.unknown, + ), + ], + ), + child: child!, + ); + }, + home: const Center(child: Text('Test')), + ), + ); + final BuildContext context = tester.element(find.text('Test')); + + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + anchorPoint: const Offset(1000, 0), + ); + + await tester.pumpAndSettle(); + // Should take the right side of the screen + expect(tester.getTopLeft(find.byType(TimePickerDialog)), const Offset(410, 0)); + expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800, 600)); + }); + + testWidgets('positioning with Directionality', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + builder: (BuildContext context, Widget? child) { + return MediaQuery( + // Display has a vertical hinge down the middle + data: const MediaQueryData( + size: Size(800, 600), + displayFeatures: [ + DisplayFeature( + bounds: Rect.fromLTRB(390, 0, 410, 600), + type: DisplayFeatureType.hinge, + state: DisplayFeatureState.unknown, + ), + ], + ), + child: Directionality( + textDirection: TextDirection.rtl, + child: child!, + ), + ); + }, + home: const Center(child: Text('Test')), + ), + ); + final BuildContext context = tester.element(find.text('Test')); + + // By default it should place the dialog on the right screen + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + ); + + await tester.pumpAndSettle(); + expect(tester.getTopLeft(find.byType(TimePickerDialog)), const Offset(410, 0)); + expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800, 600)); + }); + + testWidgets('positioning with defaults', (WidgetTester tester) async { + await tester.pumpWidget( + MaterialApp( + builder: (BuildContext context, Widget? child) { + return MediaQuery( + // Display has a vertical hinge down the middle + data: const MediaQueryData( + size: Size(800, 600), + displayFeatures: [ + DisplayFeature( + bounds: Rect.fromLTRB(390, 0, 410, 600), + type: DisplayFeatureType.hinge, + state: DisplayFeatureState.unknown, + ), + ], + ), + child: child!, + ); + }, + home: const Center(child: Text('Test')), + ), + ); + final BuildContext context = tester.element(find.text('Test')); + + // By default it should place the dialog on the left screen + showTimePicker( + context: context, + initialTime: const TimeOfDay(hour: 7, minute: 0), + ); + + await tester.pumpAndSettle(); + expect(tester.getTopLeft(find.byType(TimePickerDialog)), Offset.zero); + expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(390, 600)); + }); + }); + + group('Works for various view sizes', () { + for (final Size size in const [Size(100, 100), Size(300, 300), Size(800, 600)]) { + testWidgets('Draws dial without overflows at $size', (WidgetTester tester) async { + tester.binding.window.physicalSizeTestValue = size; + await mediaQueryBoilerplate(tester, entryMode: TimePickerEntryMode.input, materialType: materialType); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNot(throwsAssertionError)); + tester.binding.window.clearPhysicalSizeTestValue(); + }); + + testWidgets('Draws input without overflows at $size', (WidgetTester tester) async { + tester.binding.window.physicalSizeTestValue = size; + await mediaQueryBoilerplate(tester, materialType: materialType); + await tester.pumpAndSettle(); + expect(tester.takeException(), isNot(throwsAssertionError)); + tester.binding.window.clearPhysicalSizeTestValue(); + }); + } + }); + }); + + group('Time picker - A11y and Semantics (${materialType.name})', () { + testWidgets('provides semantics information for AM/PM indicator', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + await mediaQueryBoilerplate(tester, materialType: materialType); + + expect( + semantics, + includesNodeWith( + label: amString, + actions: [SemanticsAction.tap], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.isChecked, + SemanticsFlag.isInMutuallyExclusiveGroup, + SemanticsFlag.hasCheckedState, + SemanticsFlag.isFocusable, + ], + ), + ); + expect( + semantics, + includesNodeWith( + label: pmString, + actions: [SemanticsAction.tap], + flags: [ + SemanticsFlag.isButton, + SemanticsFlag.isInMutuallyExclusiveGroup, + SemanticsFlag.hasCheckedState, + SemanticsFlag.isFocusable, + ], + ), + ); + + semantics.dispose(); + }); + + testWidgets('provides semantics information for header and footer', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: materialType); + + expect(semantics, isNot(includesNodeWith(label: ':'))); + expect( + semantics.nodesWith(value: 'Select minutes 00'), + hasLength(1), + reason: '00 appears once in the header', + ); + expect( + semantics.nodesWith(value: 'Select hours 07'), + hasLength(1), + reason: '07 appears once in the header', + ); + expect(semantics, includesNodeWith(label: cancelString)); + expect(semantics, includesNodeWith(label: okString)); + + // In 24-hour mode we don't have AM/PM control. + expect(semantics, isNot(includesNodeWith(label: amString))); + expect(semantics, isNot(includesNodeWith(label: pmString))); + + semantics.dispose(); + }); + + testWidgets('provides semantics information for text fields', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + accessibleNavigation: true, + materialType: materialType, + ); + + expect( + semantics, + includesNodeWith( + label: 'Hour', + value: '07', + actions: [SemanticsAction.tap], + flags: [SemanticsFlag.isTextField, SemanticsFlag.isMultiline], + ), + ); + expect( + semantics, + includesNodeWith( + label: 'Minute', + value: '00', + actions: [SemanticsAction.tap], + flags: [SemanticsFlag.isTextField, SemanticsFlag.isMultiline], + ), + ); + + semantics.dispose(); + }); + + testWidgets('can increment and decrement hours', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + + Future actAndExpect({ + required String initialValue, + required SemanticsAction action, + required String finalValue, + }) async { + final SemanticsNode elevenHours = semantics + .nodesWith( + value: 'Select hours $initialValue', + ancestor: tester.renderObject(_hourControl).debugSemantics, + ) + .single; + tester.binding.pipelineOwner.semanticsOwner!.performAction(elevenHours.id, action); + await tester.pumpAndSettle(); + expect( + find.descendant(of: _hourControl, matching: find.text(finalValue)), + findsOneWidget, + ); + } + + // 12-hour format + await mediaQueryBoilerplate( + tester, + initialTime: const TimeOfDay(hour: 11, minute: 0), + materialType: materialType, + ); + await actAndExpect( + initialValue: '11', + action: SemanticsAction.increase, + finalValue: '12', + ); + await actAndExpect( + initialValue: '12', + action: SemanticsAction.increase, + finalValue: '1', + ); + + // Ensure we preserve day period as we roll over. + final dynamic pickerState = tester.state(_timePicker); + // ignore: avoid_dynamic_calls + expect(pickerState.selectedTime.value, const TimeOfDay(hour: 1, minute: 0)); + + await actAndExpect( + initialValue: '1', + action: SemanticsAction.decrease, + finalValue: '12', + ); + await tester.pumpWidget(Container()); // clear old boilerplate + + // 24-hour format + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + initialTime: const TimeOfDay(hour: 23, minute: 0), + materialType: materialType, + ); + await actAndExpect( + initialValue: '23', + action: SemanticsAction.increase, + finalValue: '00', + ); + await actAndExpect( + initialValue: '00', + action: SemanticsAction.increase, + finalValue: '01', + ); + await actAndExpect( + initialValue: '01', + action: SemanticsAction.decrease, + finalValue: '00', + ); + await actAndExpect( + initialValue: '00', + action: SemanticsAction.decrease, + finalValue: '23', + ); + + semantics.dispose(); + }); + + testWidgets('can increment and decrement minutes', (WidgetTester tester) async { + final SemanticsTester semantics = SemanticsTester(tester); + + Future actAndExpect({ + required String initialValue, + required SemanticsAction action, + required String finalValue, + }) async { + final SemanticsNode elevenHours = semantics + .nodesWith( + value: 'Select minutes $initialValue', + ancestor: tester.renderObject(_minuteControl).debugSemantics, + ) + .single; + tester.binding.pipelineOwner.semanticsOwner!.performAction(elevenHours.id, action); + await tester.pumpAndSettle(); + expect( + find.descendant(of: _minuteControl, matching: find.text(finalValue)), + findsOneWidget, + ); + } + + await mediaQueryBoilerplate( + tester, + initialTime: const TimeOfDay(hour: 11, minute: 58), + materialType: materialType, + ); + await actAndExpect( + initialValue: '58', + action: SemanticsAction.increase, + finalValue: '59', + ); + await actAndExpect( + initialValue: '59', + action: SemanticsAction.increase, + finalValue: '00', + ); + + // Ensure we preserve hour period as we roll over. + final dynamic pickerState = tester.state(_timePicker); + // ignore: avoid_dynamic_calls + expect(pickerState.selectedTime.value, const TimeOfDay(hour: 11, minute: 0)); + + await actAndExpect( + initialValue: '00', + action: SemanticsAction.decrease, + finalValue: '59', + ); + await actAndExpect( + initialValue: '59', + action: SemanticsAction.decrease, + finalValue: '58', + ); + + semantics.dispose(); + }); + + testWidgets('header touch regions are large enough', (WidgetTester tester) async { + // Ensure picker is displayed in portrait mode. + tester.binding.window.physicalSizeTestValue = const Size(400, 800); + tester.binding.window.devicePixelRatioTestValue = 1; + await mediaQueryBoilerplate(tester, materialType: materialType); + + final Size dayPeriodControlSize = tester.getSize( + find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodControl'), + ); + expect(dayPeriodControlSize.width, greaterThanOrEqualTo(48)); + expect(dayPeriodControlSize.height, greaterThanOrEqualTo(80)); + + final Size hourSize = tester.getSize(find.ancestor( + of: find.text('7'), + matching: find.byType(InkWell), + )); + expect(hourSize.width, greaterThanOrEqualTo(48)); + expect(hourSize.height, greaterThanOrEqualTo(48)); + + final Size minuteSize = tester.getSize(find.ancestor( + of: find.text('00'), + matching: find.byType(InkWell), + )); + expect(minuteSize.width, greaterThanOrEqualTo(48)); + expect(minuteSize.height, greaterThanOrEqualTo(48)); + + tester.binding.window.clearPhysicalSizeTestValue(); + tester.binding.window.clearDevicePixelRatioTestValue(); + }); + }); + + group('Time picker - Input (${materialType.name})', () { + testWidgets('Initial entry mode is used', (WidgetTester tester) async { + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + materialType: materialType, + ); + expect(find.byType(TextField), findsNWidgets(2)); + }); + + testWidgets('Initial time is the default', (WidgetTester tester) async { + late TimeOfDay result; + await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, entryMode: TimePickerEntryMode.input, materialType: materialType); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 7, minute: 0))); + }); + + testWidgets('Help text is used - Input', (WidgetTester tester) async { + const String helpText = 'help'; + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + helpText: helpText, + materialType: materialType, + ); + expect(find.text(helpText), findsOneWidget); + }); + + testWidgets('Help text is used in Material3 - Input', (WidgetTester tester) async { + const String helpText = 'help'; + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + helpText: helpText, + materialType: materialType, + ); + expect(find.text(helpText), findsOneWidget); + }); + + testWidgets('Hour label text is used - Input', (WidgetTester tester) async { + const String hourLabelText = 'Custom hour label'; + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + hourLabelText: hourLabelText, + materialType: materialType, + ); + expect(find.text(hourLabelText), findsOneWidget); + }); + + testWidgets('Minute label text is used - Input', (WidgetTester tester) async { + const String minuteLabelText = 'Custom minute label'; + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + minuteLabelText: minuteLabelText, + materialType: materialType, + ); + expect(find.text(minuteLabelText), findsOneWidget); + }); + + testWidgets('Invalid error text is used - Input', (WidgetTester tester) async { + const String errorInvalidText = 'Custom validation error'; + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + errorInvalidText: errorInvalidText, + materialType: materialType, + ); + // Input invalid time (hour) to force validation error + await tester.enterText(find.byType(TextField).first, '88'); + final MaterialLocalizations materialLocalizations = MaterialLocalizations.of( + tester.element(find.byType(TextButton).first), + ); + // Tap the ok button to trigger the validation error with custom translation + await tester.tap(find.text(materialLocalizations.okButtonLabel)); + await tester.pumpAndSettle(const Duration(seconds: 1)); + expect(find.text(errorInvalidText), findsOneWidget); + }); + + testWidgets('Can switch from input to dial entry mode', (WidgetTester tester) async { + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + materialType: materialType, + ); + await tester.tap(find.byIcon(Icons.access_time)); + await tester.pumpAndSettle(); + expect(find.byType(TextField), findsNothing); + }); + + testWidgets('Can switch from dial to input entry mode', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, materialType: materialType); + await tester.tap(find.byIcon(Icons.keyboard_outlined)); + await tester.pumpAndSettle(); + expect(find.byType(TextField), findsWidgets); + }); + + testWidgets('Can not switch out of inputOnly mode', (WidgetTester tester) async { + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.inputOnly, + materialType: materialType, + ); + expect(find.byType(TextField), findsWidgets); + expect(find.byIcon(Icons.access_time), findsNothing); + }); + + testWidgets('Can not switch out of dialOnly mode', (WidgetTester tester) async { + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.dialOnly, + materialType: materialType, + ); + expect(find.byType(TextField), findsNothing); + expect(find.byIcon(Icons.keyboard_outlined), findsNothing); + }); + + testWidgets('Switching to dial entry mode triggers entry callback', (WidgetTester tester) async { + bool triggeredCallback = false; + + await mediaQueryBoilerplate( + tester, + alwaysUse24HourFormat: true, + entryMode: TimePickerEntryMode.input, + onEntryModeChange: (TimePickerEntryMode mode) { + if (mode == TimePickerEntryMode.dial) { + triggeredCallback = true; + } + }, + materialType: materialType, + ); + + await tester.tap(find.byIcon(Icons.access_time)); + await tester.pumpAndSettle(); + expect(triggeredCallback, true); + }); + + testWidgets('Switching to input entry mode triggers entry callback', (WidgetTester tester) async { + bool triggeredCallback = false; + + await mediaQueryBoilerplate(tester, alwaysUse24HourFormat: true, onEntryModeChange: (TimePickerEntryMode mode) { + if (mode == TimePickerEntryMode.input) { + triggeredCallback = true; + } + }, materialType: materialType); + + await tester.tap(find.byIcon(Icons.keyboard_outlined)); + await tester.pumpAndSettle(); + expect(triggeredCallback, true); + }); + + testWidgets('Can double tap hours (when selected) to enter input mode', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, materialType: materialType); + final Finder hourFinder = find.ancestor( + of: find.text('7'), + matching: find.byType(InkWell), + ); + + expect(find.byType(TextField), findsNothing); + + // Double tap the hour. + await tester.tap(hourFinder); + await tester.pump(const Duration(milliseconds: 100)); + await tester.tap(hourFinder); + await tester.pumpAndSettle(); + + expect(find.byType(TextField), findsWidgets); + }); + + testWidgets('Can not double tap hours (when not selected) to enter input mode', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, materialType: materialType); + final Finder hourFinder = find.ancestor( + of: find.text('7'), + matching: find.byType(InkWell), + ); + final Finder minuteFinder = find.ancestor( + of: find.text('00'), + matching: find.byType(InkWell), + ); + + expect(find.byType(TextField), findsNothing); + + // Switch to minutes mode. + await tester.tap(minuteFinder); + await tester.pumpAndSettle(); + + // Double tap the hour. + await tester.tap(hourFinder); + await tester.pump(const Duration(milliseconds: 100)); + await tester.tap(hourFinder); + await tester.pumpAndSettle(); + + expect(find.byType(TextField), findsNothing); + }); + + testWidgets('Can double tap minutes (when selected) to enter input mode', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, materialType: materialType); + final Finder minuteFinder = find.ancestor( + of: find.text('00'), + matching: find.byType(InkWell), + ); + + expect(find.byType(TextField), findsNothing); + + // Switch to minutes mode. + await tester.tap(minuteFinder); + await tester.pumpAndSettle(); + + // Double tap the minutes. + await tester.tap(minuteFinder); + await tester.pump(const Duration(milliseconds: 100)); + await tester.tap(minuteFinder); + await tester.pumpAndSettle(); + + expect(find.byType(TextField), findsWidgets); + }); + + testWidgets('Can not double tap minutes (when not selected) to enter input mode', (WidgetTester tester) async { + await mediaQueryBoilerplate(tester, materialType: materialType); + final Finder minuteFinder = find.ancestor( + of: find.text('00'), + matching: find.byType(InkWell), + ); + + expect(find.byType(TextField), findsNothing); + + // Double tap the minutes. + await tester.tap(minuteFinder); + await tester.pump(const Duration(milliseconds: 100)); + await tester.tap(minuteFinder); + await tester.pumpAndSettle(); + + expect(find.byType(TextField), findsNothing); + }); + + testWidgets('Entered text returns time', (WidgetTester tester) async { + late TimeOfDay result; + await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, entryMode: TimePickerEntryMode.input, materialType: materialType); + await tester.enterText(find.byType(TextField).first, '9'); + await tester.enterText(find.byType(TextField).last, '12'); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); + }); + + testWidgets('Toggle to dial mode keeps selected time', (WidgetTester tester) async { + late TimeOfDay result; + await startPicker(tester, (TimeOfDay? time) { + result = time!; + }, entryMode: TimePickerEntryMode.input, materialType: materialType); + await tester.enterText(find.byType(TextField).first, '8'); + await tester.enterText(find.byType(TextField).last, '15'); + await tester.tap(find.byIcon(Icons.access_time)); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 8, minute: 15))); + }); + + testWidgets('Invalid text prevents dismissing', (WidgetTester tester) async { + TimeOfDay? result; + await startPicker(tester, (TimeOfDay? time) { + result = time; + }, entryMode: TimePickerEntryMode.input, materialType: materialType); + + // Invalid hour. + await tester.enterText(find.byType(TextField).first, '88'); + await tester.enterText(find.byType(TextField).last, '15'); + await finishPicker(tester); + expect(result, null); + + // Invalid minute. + await tester.enterText(find.byType(TextField).first, '8'); + await tester.enterText(find.byType(TextField).last, '95'); + await finishPicker(tester); + expect(result, null); + + await tester.enterText(find.byType(TextField).first, '8'); + await tester.enterText(find.byType(TextField).last, '15'); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 8, minute: 15))); + }); + + // Fixes regression that was reverted in https://github.com/flutter/flutter/pull/64094#pullrequestreview-469836378. + testWidgets('Ensure hour/minute fields are top-aligned with the separator', (WidgetTester tester) async { + await startPicker(tester, (TimeOfDay? time) {}, + entryMode: TimePickerEntryMode.input, materialType: materialType); + final double hourFieldTop = + tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourTextField')).dy; + final double minuteFieldTop = + tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_MinuteTextField')).dy; + final double separatorTop = + tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_StringFragment')).dy; + expect(hourFieldTop, separatorTop); + expect(minuteFieldTop, separatorTop); + }); + + testWidgets('Can switch between hour/minute fields using keyboard input action', (WidgetTester tester) async { + await startPicker(tester, (TimeOfDay? time) {}, + entryMode: TimePickerEntryMode.input, materialType: materialType); + + final Finder hourFinder = find.byType(TextField).first; + final TextField hourField = tester.widget(hourFinder); + await tester.tap(hourFinder); + expect(hourField.focusNode!.hasFocus, isTrue); + + await tester.enterText(find.byType(TextField).first, '08'); + final Finder minuteFinder = find.byType(TextField).last; + final TextField minuteField = tester.widget(minuteFinder); + expect(hourField.focusNode!.hasFocus, isFalse); + expect(minuteField.focusNode!.hasFocus, isTrue); + + expect(tester.testTextInput.setClientArgs!['inputAction'], equals('TextInputAction.done')); + await tester.testTextInput.receiveAction(TextInputAction.done); + expect(hourField.focusNode!.hasFocus, isFalse); + expect(minuteField.focusNode!.hasFocus, isFalse); + }); + }); + + group('Time picker - Restoration (${materialType.name})', () { + testWidgets('Time Picker state restoration test - dial mode', (WidgetTester tester) async { + TimeOfDay? result; + final Offset center = (await startPicker( + tester, + (TimeOfDay? time) { + result = time; + }, + restorationId: 'restorable_time_picker', + materialType: materialType, + ))!; + final Offset hour6 = Offset(center.dx, center.dy + 50); // 6:00 + final Offset min45 = Offset(center.dx - 50, center.dy); // 45 mins (or 9:00 hours) + + await tester.tapAt(hour6); + await tester.pump(const Duration(milliseconds: 50)); + await tester.restartAndRestore(); + await tester.tapAt(min45); + await tester.pump(const Duration(milliseconds: 50)); + final TestRestorationData restorationData = await tester.getRestorationData(); + await tester.restartAndRestore(); + // Setting to PM adds 12 hours (18:45) + await tester.tap(find.text(pmString)); + await tester.pump(const Duration(milliseconds: 50)); + await tester.restartAndRestore(); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 18, minute: 45))); + + // Test restoring from before PM was selected (6:45) + await tester.restoreFrom(restorationData); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); + }); + + testWidgets('Time Picker state restoration test - input mode', (WidgetTester tester) async { + TimeOfDay? result; + await startPicker( + tester, + (TimeOfDay? time) { + result = time; + }, + entryMode: TimePickerEntryMode.input, + restorationId: 'restorable_time_picker', + materialType: materialType, + ); + await tester.enterText(find.byType(TextField).first, '9'); + await tester.pump(const Duration(milliseconds: 50)); + await tester.restartAndRestore(); + + await tester.enterText(find.byType(TextField).last, '12'); + await tester.pump(const Duration(milliseconds: 50)); + final TestRestorationData restorationData = await tester.getRestorationData(); + await tester.restartAndRestore(); + + // Setting to PM adds 12 hours (21:12) + await tester.tap(find.text(pmString)); + await tester.pump(const Duration(milliseconds: 50)); + await tester.restartAndRestore(); + + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 21, minute: 12))); + + // Restoring from before PM was set (9:12) + await tester.restoreFrom(restorationData); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); + }); + + testWidgets('Time Picker state restoration test - switching modes', (WidgetTester tester) async { + TimeOfDay? result; + final Offset center = (await startPicker( + tester, + (TimeOfDay? time) { + result = time; + }, + restorationId: 'restorable_time_picker', + materialType: materialType, + ))!; + + final TestRestorationData restorationData = await tester.getRestorationData(); + // Switch to input mode from dial mode. + await tester.tap(find.byIcon(Icons.keyboard_outlined)); + await tester.pump(const Duration(milliseconds: 50)); + await tester.restartAndRestore(); + + // Select time using input mode controls. + await tester.enterText(find.byType(TextField).first, '9'); + await tester.enterText(find.byType(TextField).last, '12'); + await tester.pump(const Duration(milliseconds: 50)); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); + + // Restoring from dial mode. + await tester.restoreFrom(restorationData); + final Offset hour6 = Offset(center.dx, center.dy + 50); // 6:00 + final Offset min45 = Offset(center.dx - 50, center.dy); // 45 mins (or 9:00 hours) + + await tester.tapAt(hour6); + await tester.pump(const Duration(milliseconds: 50)); + await tester.restartAndRestore(); + await tester.tapAt(min45); + await tester.pump(const Duration(milliseconds: 50)); + await finishPicker(tester); + expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); + }); + }); + } +} + +final Finder findDialPaint = find.descendant( + of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Dial'), + matching: find.byWidgetPredicate((Widget w) => w is CustomPaint), +); + +class PickerObserver extends NavigatorObserver { + int pickerCount = 0; + + @override + void didPush(Route route, Route? previousRoute) { + if (route is DialogRoute) { + pickerCount++; + } + super.didPush(route, previousRoute); + } +} + +Future mediaQueryBoilerplate( + WidgetTester tester, { + bool alwaysUse24HourFormat = false, + TimeOfDay initialTime = const TimeOfDay(hour: 7, minute: 0), + double textScaleFactor = 1, + TimePickerEntryMode entryMode = TimePickerEntryMode.dial, + String? helpText, + String? hourLabelText, + String? minuteLabelText, + String? errorInvalidText, + bool accessibleNavigation = false, + EntryModeChangeCallback? onEntryModeChange, + bool tapButton = true, + required MaterialType materialType, + Orientation? orientation, +}) async { + await tester.pumpWidget( + Builder(builder: (BuildContext context) { + return Theme( + data: Theme.of(context).copyWith(useMaterial3: materialType == MaterialType.material3), + child: Localizations( + locale: const Locale('en', 'US'), + delegates: const >[ + DefaultMaterialLocalizations.delegate, + DefaultWidgetsLocalizations.delegate, + ], + child: MediaQuery( + data: MediaQueryData( + alwaysUse24HourFormat: alwaysUse24HourFormat, + textScaleFactor: textScaleFactor, + accessibleNavigation: accessibleNavigation, + size: tester.binding.window.physicalSize / tester.binding.window.devicePixelRatio, + ), + child: Material( + child: Center( + child: Directionality( + textDirection: TextDirection.ltr, + child: Navigator( + onGenerateRoute: (RouteSettings settings) { + return MaterialPageRoute(builder: (BuildContext context) { + return TextButton( + onPressed: () { + showTimePicker( + context: context, + initialTime: initialTime, + initialEntryMode: entryMode, + helpText: helpText, + hourLabelText: hourLabelText, + minuteLabelText: minuteLabelText, + errorInvalidText: errorInvalidText, + onEntryModeChanged: onEntryModeChange, + orientation: orientation, + ); + }, + child: const Text('X'), + ); + }); + }, + ), + ), + ), + ), + ), + ), + ); + }), + ); + if (tapButton) { + await tester.tap(find.text('X')); + } + await tester.pumpAndSettle(); +} + final Finder _hourControl = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourControl'); final Finder _minuteControl = find.byWidgetPredicate((Widget widget) => '${widget.runtimeType}' == '_MinuteControl'); -final Finder _timePickerDialog = find.byWidgetPredicate((Widget widget) => '${widget.runtimeType}' == 'TimePickerDialog'); +final Finder _timePicker = find.byWidgetPredicate((Widget widget) => '${widget.runtimeType}' == '_TimePicker'); class _TimePickerLauncher extends StatefulWidget { const _TimePickerLauncher({ @@ -40,8 +1614,8 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati onPresent: (NavigatorState navigator, Object? arguments) { return navigator.restorablePush( _timePickerRoute, - arguments: { - 'entryMode': widget.entryMode.index, + arguments: { + 'entry_mode': widget.entryMode.name, }, ); }, @@ -52,7 +1626,9 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati Object? arguments, ) { final Map args = arguments! as Map; - final TimePickerEntryMode entryMode = TimePickerEntryMode.values[args['entryMode'] as int]; + final TimePickerEntryMode entryMode = TimePickerEntryMode.values.firstWhere( + (TimePickerEntryMode element) => element.name == args['entry_mode'], + ); return DialogRoute( context: context, builder: (BuildContext context) { @@ -101,15 +1677,24 @@ class _TimePickerLauncherState extends State<_TimePickerLauncher> with Restorati } } +// The version of material design layout, etc. to test. Corresponds to +// useMaterial3 true/false in the ThemeData, but used an enum here so that it +// wasn't just a boolean, for easier identification of the name of the mode in +// tests. +enum MaterialType { + material2, + material3, +} + Future startPicker( - WidgetTester tester, - ValueChanged onChanged, { - TimePickerEntryMode entryMode = TimePickerEntryMode.dial, - String? restorationId, - bool useMaterial3 = false, - }) async { + WidgetTester tester, + ValueChanged onChanged, { + TimePickerEntryMode entryMode = TimePickerEntryMode.dial, + String? restorationId, + required MaterialType materialType, +}) async { await tester.pumpWidget(MaterialApp( - theme: ThemeData(useMaterial3: useMaterial3), + theme: ThemeData(useMaterial3: materialType == MaterialType.material3), restorationScopeId: 'app', locale: const Locale('en', 'US'), home: _TimePickerLauncher( @@ -120,1306 +1705,15 @@ Future startPicker( )); await tester.tap(find.text('X')); await tester.pumpAndSettle(const Duration(seconds: 1)); - return entryMode == TimePickerEntryMode.dial ? tester.getCenter(find.byKey(const ValueKey('time-picker-dial'))) : null; + return entryMode == TimePickerEntryMode.dial + ? tester.getCenter(find.byKey(const ValueKey('time-picker-dial'))) + : null; } Future finishPicker(WidgetTester tester) async { - final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(tester.element(find.byType(ElevatedButton))); + final MaterialLocalizations materialLocalizations = MaterialLocalizations.of( + tester.element(find.byType(ElevatedButton)), + ); await tester.tap(find.text(materialLocalizations.okButtonLabel)); await tester.pumpAndSettle(const Duration(seconds: 1)); } - -void main() { - group('Time picker - Dial', () { - _tests(); - }); - - group('Time picker - Input', () { - _testsInput(); - }); -} - -void _tests() { - testWidgets('Material3 has sentence case labels', (WidgetTester tester) async { - await startPicker(tester, (TimeOfDay? time) { - expect(find.text('Select time'), findsOneWidget); - expect(find.text('Enter time'), findsOneWidget); - expect(find.text('Cancel'), findsOneWidget); - }, useMaterial3: true); - }); - - testWidgets('tap-select an hour', (WidgetTester tester) async { - TimeOfDay? result; - - Offset center = (await startPicker(tester, (TimeOfDay? time) { result = time; }))!; - await tester.tapAt(Offset(center.dx, center.dy - 50.0)); // 12:00 AM - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 0, minute: 0))); - - center = (await startPicker(tester, (TimeOfDay? time) { result = time; }))!; - await tester.tapAt(Offset(center.dx + 50.0, center.dy)); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 3, minute: 0))); - - center = (await startPicker(tester, (TimeOfDay? time) { result = time; }))!; - await tester.tapAt(Offset(center.dx, center.dy + 50.0)); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 6, minute: 0))); - - center = (await startPicker(tester, (TimeOfDay? time) { result = time; }))!; - await tester.tapAt(Offset(center.dx, center.dy + 50.0)); - await tester.tapAt(Offset(center.dx - 50, center.dy)); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 9, minute: 0))); - }); - - testWidgets('drag-select an hour', (WidgetTester tester) async { - late TimeOfDay result; - - final Offset center = (await startPicker(tester, (TimeOfDay? time) { result = time!; }))!; - final Offset hour0 = Offset(center.dx, center.dy - 50.0); // 12:00 AM - final Offset hour3 = Offset(center.dx + 50.0, center.dy); - final Offset hour6 = Offset(center.dx, center.dy + 50.0); - final Offset hour9 = Offset(center.dx - 50.0, center.dy); - - TestGesture gesture; - - gesture = await tester.startGesture(hour3); - await gesture.moveBy(hour0 - hour3); - await gesture.up(); - await finishPicker(tester); - expect(result.hour, 0); - - expect(await startPicker(tester, (TimeOfDay? time) { result = time!; }), equals(center)); - gesture = await tester.startGesture(hour0); - await gesture.moveBy(hour3 - hour0); - await gesture.up(); - await finishPicker(tester); - expect(result.hour, 3); - - expect(await startPicker(tester, (TimeOfDay? time) { result = time!; }), equals(center)); - gesture = await tester.startGesture(hour3); - await gesture.moveBy(hour6 - hour3); - await gesture.up(); - await finishPicker(tester); - expect(result.hour, equals(6)); - - expect(await startPicker(tester, (TimeOfDay? time) { result = time!; }), equals(center)); - gesture = await tester.startGesture(hour6); - await gesture.moveBy(hour9 - hour6); - await gesture.up(); - await finishPicker(tester); - expect(result.hour, equals(9)); - }); - - testWidgets('tap-select switches from hour to minute', (WidgetTester tester) async { - late TimeOfDay result; - - final Offset center = (await startPicker(tester, (TimeOfDay? time) { result = time!; }))!; - final Offset hour6 = Offset(center.dx, center.dy + 50.0); // 6:00 - final Offset min45 = Offset(center.dx - 50.0, center.dy); // 45 mins (or 9:00 hours) - - await tester.tapAt(hour6); - await tester.pump(const Duration(milliseconds: 50)); - await tester.tapAt(min45); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); - }); - - testWidgets('drag-select switches from hour to minute', (WidgetTester tester) async { - late TimeOfDay result; - - final Offset center = (await startPicker(tester, (TimeOfDay? time) { result = time!; }))!; - final Offset hour3 = Offset(center.dx + 50.0, center.dy); - final Offset hour6 = Offset(center.dx, center.dy + 50.0); - final Offset hour9 = Offset(center.dx - 50.0, center.dy); - - TestGesture gesture = await tester.startGesture(hour6); - await gesture.moveBy(hour9 - hour6); - await gesture.up(); - await tester.pump(const Duration(milliseconds: 50)); - gesture = await tester.startGesture(hour6); - await gesture.moveBy(hour3 - hour6); - await gesture.up(); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 9, minute: 15))); - }); - - testWidgets('tap-select rounds down to nearest 5 minute increment', (WidgetTester tester) async { - late TimeOfDay result; - - final Offset center = (await startPicker(tester, (TimeOfDay? time) { result = time!; }))!; - final Offset hour6 = Offset(center.dx, center.dy + 50.0); // 6:00 - final Offset min46 = Offset(center.dx - 50.0, center.dy - 5); // 46 mins - - await tester.tapAt(hour6); - await tester.pump(const Duration(milliseconds: 50)); - await tester.tapAt(min46); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); - }); - - testWidgets('tap-select rounds up to nearest 5 minute increment', (WidgetTester tester) async { - late TimeOfDay result; - - final Offset center = (await startPicker(tester, (TimeOfDay? time) { result = time!; }))!; - final Offset hour6 = Offset(center.dx, center.dy + 50.0); // 6:00 - final Offset min48 = Offset(center.dx - 50.0, center.dy - 15); // 48 mins - - await tester.tapAt(hour6); - await tester.pump(const Duration(milliseconds: 50)); - await tester.tapAt(min48); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 6, minute: 50))); - }); - - group('haptic feedback', () { - const Duration kFastFeedbackInterval = Duration(milliseconds: 10); - const Duration kSlowFeedbackInterval = Duration(milliseconds: 200); - late FeedbackTester feedback; - - setUp(() { - feedback = FeedbackTester(); - }); - - tearDown(() { - feedback.dispose(); - }); - - testWidgets('tap-select vibrates once', (WidgetTester tester) async { - final Offset center = (await startPicker(tester, (TimeOfDay? time) { }))!; - await tester.tapAt(Offset(center.dx, center.dy - 50.0)); - await finishPicker(tester); - expect(feedback.hapticCount, 1); - }); - - testWidgets('quick successive tap-selects vibrate once', (WidgetTester tester) async { - final Offset center = (await startPicker(tester, (TimeOfDay? time) { }))!; - await tester.tapAt(Offset(center.dx, center.dy - 50.0)); - await tester.pump(kFastFeedbackInterval); - await tester.tapAt(Offset(center.dx, center.dy + 50.0)); - await finishPicker(tester); - expect(feedback.hapticCount, 1); - }); - - testWidgets('slow successive tap-selects vibrate once per tap', (WidgetTester tester) async { - final Offset center = (await startPicker(tester, (TimeOfDay? time) { }))!; - await tester.tapAt(Offset(center.dx, center.dy - 50.0)); - await tester.pump(kSlowFeedbackInterval); - await tester.tapAt(Offset(center.dx, center.dy + 50.0)); - await tester.pump(kSlowFeedbackInterval); - await tester.tapAt(Offset(center.dx, center.dy - 50.0)); - await finishPicker(tester); - expect(feedback.hapticCount, 3); - }); - - testWidgets('drag-select vibrates once', (WidgetTester tester) async { - final Offset center = (await startPicker(tester, (TimeOfDay? time) { }))!; - final Offset hour0 = Offset(center.dx, center.dy - 50.0); - final Offset hour3 = Offset(center.dx + 50.0, center.dy); - - final TestGesture gesture = await tester.startGesture(hour3); - await gesture.moveBy(hour0 - hour3); - await gesture.up(); - await finishPicker(tester); - expect(feedback.hapticCount, 1); - }); - - testWidgets('quick drag-select vibrates once', (WidgetTester tester) async { - final Offset center = (await startPicker(tester, (TimeOfDay? time) { }))!; - final Offset hour0 = Offset(center.dx, center.dy - 50.0); - final Offset hour3 = Offset(center.dx + 50.0, center.dy); - - final TestGesture gesture = await tester.startGesture(hour3); - await gesture.moveBy(hour0 - hour3); - await tester.pump(kFastFeedbackInterval); - await gesture.moveBy(hour3 - hour0); - await tester.pump(kFastFeedbackInterval); - await gesture.moveBy(hour0 - hour3); - await gesture.up(); - await finishPicker(tester); - expect(feedback.hapticCount, 1); - }); - - testWidgets('slow drag-select vibrates once', (WidgetTester tester) async { - final Offset center = (await startPicker(tester, (TimeOfDay? time) { }))!; - final Offset hour0 = Offset(center.dx, center.dy - 50.0); - final Offset hour3 = Offset(center.dx + 50.0, center.dy); - - final TestGesture gesture = await tester.startGesture(hour3); - await gesture.moveBy(hour0 - hour3); - await tester.pump(kSlowFeedbackInterval); - await gesture.moveBy(hour3 - hour0); - await tester.pump(kSlowFeedbackInterval); - await gesture.moveBy(hour0 - hour3); - await gesture.up(); - await finishPicker(tester); - expect(feedback.hapticCount, 3); - }); - }); - - const List labels12To11 = ['12', '1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11']; - const List labels00To22 = ['00', '02', '04', '06', '08', '10', '12', '14', '16', '18', '20', '22']; - - testWidgets('respects MediaQueryData.alwaysUse24HourFormat == false', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, false); - - final CustomPaint dialPaint = tester.widget(findDialPaint); - final dynamic dialPainter = dialPaint.painter; - // ignore: avoid_dynamic_calls - final List primaryLabels = dialPainter.primaryLabels as List; - // ignore: avoid_dynamic_calls - expect(primaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels12To11); - - // ignore: avoid_dynamic_calls - final List secondaryLabels = dialPainter.secondaryLabels as List; - // ignore: avoid_dynamic_calls - expect(secondaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels12To11); - }); - - testWidgets('respects MediaQueryData.alwaysUse24HourFormat == true', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, true); - - final CustomPaint dialPaint = tester.widget(findDialPaint); - final dynamic dialPainter = dialPaint.painter; - // ignore: avoid_dynamic_calls - final List primaryLabels = dialPainter.primaryLabels as List; - // ignore: avoid_dynamic_calls - expect(primaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels00To22); - - // ignore: avoid_dynamic_calls - final List secondaryLabels = dialPainter.secondaryLabels as List; - // ignore: avoid_dynamic_calls - expect(secondaryLabels.map((dynamic tp) => tp.painter.text.text as String), labels00To22); - }); - - testWidgets('provides semantics information for AM/PM indicator', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - await mediaQueryBoilerplate(tester, false); - - expect( - semantics, - includesNodeWith( - label: 'AM', - actions: [SemanticsAction.tap], - flags: [ - SemanticsFlag.isButton, - SemanticsFlag.isChecked, - SemanticsFlag.isInMutuallyExclusiveGroup, - SemanticsFlag.hasCheckedState, - SemanticsFlag.isFocusable, - ], - ), - ); - expect( - semantics, - includesNodeWith( - label: 'PM', - actions: [SemanticsAction.tap], - flags: [ - SemanticsFlag.isButton, - SemanticsFlag.isInMutuallyExclusiveGroup, - SemanticsFlag.hasCheckedState, - SemanticsFlag.isFocusable, - ], - ), - ); - - semantics.dispose(); - }); - - testWidgets('provides semantics information for header and footer', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - await mediaQueryBoilerplate(tester, true); - - expect(semantics, isNot(includesNodeWith(label: ':'))); - expect( - semantics.nodesWith(value: 'Select minutes 00'), - hasLength(1), - reason: '00 appears once in the header', - ); - expect( - semantics.nodesWith(value: 'Select hours 07'), - hasLength(1), - reason: '07 appears once in the header', - ); - expect(semantics, includesNodeWith(label: 'CANCEL')); - expect(semantics, includesNodeWith(label: 'OK')); - - // In 24-hour mode we don't have AM/PM control. - expect(semantics, isNot(includesNodeWith(label: 'AM'))); - expect(semantics, isNot(includesNodeWith(label: 'PM'))); - - semantics.dispose(); - }); - - testWidgets('provides semantics information for text fields', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, accessibleNavigation: true); - - expect( - semantics, - includesNodeWith( - label: 'Hour', - value: '07', - actions: [SemanticsAction.tap], - flags: [SemanticsFlag.isTextField, SemanticsFlag.isMultiline], - ), - ); - expect( - semantics, - includesNodeWith( - label: 'Minute', - value: '00', - actions: [SemanticsAction.tap], - flags: [SemanticsFlag.isTextField, SemanticsFlag.isMultiline], - ), - ); - - semantics.dispose(); - }); - - testWidgets('can increment and decrement hours', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - - Future actAndExpect({ required String initialValue, required SemanticsAction action, required String finalValue }) async { - final SemanticsNode elevenHours = semantics.nodesWith( - value: 'Select hours $initialValue', - ancestor: tester.renderObject(_hourControl).debugSemantics, - ).single; - tester.binding.pipelineOwner.semanticsOwner!.performAction(elevenHours.id, action); - await tester.pumpAndSettle(); - expect( - find.descendant(of: _hourControl, matching: find.text(finalValue)), - findsOneWidget, - ); - } - - // 12-hour format - await mediaQueryBoilerplate(tester, false, initialTime: const TimeOfDay(hour: 11, minute: 0)); - await actAndExpect( - initialValue: '11', - action: SemanticsAction.increase, - finalValue: '12', - ); - await actAndExpect( - initialValue: '12', - action: SemanticsAction.increase, - finalValue: '1', - ); - - // Ensure we preserve day period as we roll over. - final dynamic pickerState = tester.state(_timePickerDialog); - // ignore: avoid_dynamic_calls - expect(pickerState.selectedTime.value, const TimeOfDay(hour: 1, minute: 0)); - - await actAndExpect( - initialValue: '1', - action: SemanticsAction.decrease, - finalValue: '12', - ); - await tester.pumpWidget(Container()); // clear old boilerplate - - // 24-hour format - await mediaQueryBoilerplate(tester, true, initialTime: const TimeOfDay(hour: 23, minute: 0)); - await actAndExpect( - initialValue: '23', - action: SemanticsAction.increase, - finalValue: '00', - ); - await actAndExpect( - initialValue: '00', - action: SemanticsAction.increase, - finalValue: '01', - ); - await actAndExpect( - initialValue: '01', - action: SemanticsAction.decrease, - finalValue: '00', - ); - await actAndExpect( - initialValue: '00', - action: SemanticsAction.decrease, - finalValue: '23', - ); - - semantics.dispose(); - }); - - testWidgets('can increment and decrement minutes', (WidgetTester tester) async { - final SemanticsTester semantics = SemanticsTester(tester); - - Future actAndExpect({ required String initialValue, required SemanticsAction action, required String finalValue }) async { - final SemanticsNode elevenHours = semantics.nodesWith( - value: 'Select minutes $initialValue', - ancestor: tester.renderObject(_minuteControl).debugSemantics, - ).single; - tester.binding.pipelineOwner.semanticsOwner!.performAction(elevenHours.id, action); - await tester.pumpAndSettle(); - expect( - find.descendant(of: _minuteControl, matching: find.text(finalValue)), - findsOneWidget, - ); - } - - await mediaQueryBoilerplate(tester, false, initialTime: const TimeOfDay(hour: 11, minute: 58)); - await actAndExpect( - initialValue: '58', - action: SemanticsAction.increase, - finalValue: '59', - ); - await actAndExpect( - initialValue: '59', - action: SemanticsAction.increase, - finalValue: '00', - ); - - // Ensure we preserve hour period as we roll over. - final dynamic pickerState = tester.state(_timePickerDialog); - // ignore: avoid_dynamic_calls - expect(pickerState.selectedTime.value, const TimeOfDay(hour: 11, minute: 0)); - - await actAndExpect( - initialValue: '00', - action: SemanticsAction.decrease, - finalValue: '59', - ); - await actAndExpect( - initialValue: '59', - action: SemanticsAction.decrease, - finalValue: '58', - ); - - semantics.dispose(); - }); - - testWidgets('header touch regions are large enough', (WidgetTester tester) async { - // Ensure picker is displayed in portrait mode. - tester.binding.window.physicalSizeTestValue = const Size(400, 800); - tester.binding.window.devicePixelRatioTestValue = 1; - await mediaQueryBoilerplate(tester, false); - - final Size dayPeriodControlSize = tester.getSize(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodControl')); - expect(dayPeriodControlSize.width, greaterThanOrEqualTo(48.0)); - // Height should be double the minimum size to account for both AM/PM stacked. - expect(dayPeriodControlSize.height, greaterThanOrEqualTo(48.0 * 2)); - - final Size hourSize = tester.getSize(find.ancestor( - of: find.text('7'), - matching: find.byType(InkWell), - )); - expect(hourSize.width, greaterThanOrEqualTo(48.0)); - expect(hourSize.height, greaterThanOrEqualTo(48.0)); - - final Size minuteSize = tester.getSize(find.ancestor( - of: find.text('00'), - matching: find.byType(InkWell), - )); - expect(minuteSize.width, greaterThanOrEqualTo(48.0)); - expect(minuteSize.height, greaterThanOrEqualTo(48.0)); - - tester.binding.window.clearPhysicalSizeTestValue(); - tester.binding.window.clearDevicePixelRatioTestValue(); - }); - - testWidgets('when change orientation, should reflect in render objects', (WidgetTester tester) async { - // portrait - tester.binding.window.physicalSizeTestValue = const Size(800, 800.5); - tester.binding.window.devicePixelRatioTestValue = 1; - await mediaQueryBoilerplate(tester, false); - - RenderObject render = tester.renderObject(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodInputPadding')); - expect((render as dynamic).orientation, Orientation.portrait); // ignore: avoid_dynamic_calls - - // landscape - tester.binding.window.physicalSizeTestValue = const Size(800.5, 800); - tester.binding.window.devicePixelRatioTestValue = 1; - await mediaQueryBoilerplate(tester, false, tapButton: false); - - render = tester.renderObject(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodInputPadding')); - expect((render as dynamic).orientation, Orientation.landscape); // ignore: avoid_dynamic_calls - - tester.binding.window.clearPhysicalSizeTestValue(); - tester.binding.window.clearDevicePixelRatioTestValue(); - }); - - testWidgets('builder parameter', (WidgetTester tester) async { - Widget buildFrame(TextDirection textDirection) { - return MaterialApp( - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ElevatedButton( - child: const Text('X'), - onPressed: () { - showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - builder: (BuildContext context, Widget? child) { - return Directionality( - textDirection: textDirection, - child: child!, - ); - }, - ); - }, - ); - }, - ), - ), - ), - ); - } - - await tester.pumpWidget(buildFrame(TextDirection.ltr)); - await tester.tap(find.text('X')); - await tester.pumpAndSettle(); - final double ltrOkRight = tester.getBottomRight(find.text('OK')).dx; - - await tester.tap(find.text('OK')); // dismiss the dialog - await tester.pumpAndSettle(); - - await tester.pumpWidget(buildFrame(TextDirection.rtl)); - await tester.tap(find.text('X')); - await tester.pumpAndSettle(); - - // Verify that the time picker is being laid out RTL. - // We expect the left edge of the 'OK' button in the RTL - // layout to match the gap between right edge of the 'OK' - // button and the right edge of the 800 wide window. - expect(tester.getBottomLeft(find.text('OK')).dx, 800 - ltrOkRight); - }); - - testWidgets('uses root navigator by default', (WidgetTester tester) async { - final PickerObserver rootObserver = PickerObserver(); - final PickerObserver nestedObserver = PickerObserver(); - - await tester.pumpWidget(MaterialApp( - navigatorObservers: [rootObserver], - home: Navigator( - observers: [nestedObserver], - onGenerateRoute: (RouteSettings settings) { - return MaterialPageRoute( - builder: (BuildContext context) { - return ElevatedButton( - onPressed: () { - showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - ); - }, - child: const Text('Show Picker'), - ); - }, - ); - }, - ), - )); - - // Open the dialog. - await tester.tap(find.byType(ElevatedButton)); - - expect(rootObserver.pickerCount, 1); - expect(nestedObserver.pickerCount, 0); - }); - - testWidgets('uses nested navigator if useRootNavigator is false', (WidgetTester tester) async { - final PickerObserver rootObserver = PickerObserver(); - final PickerObserver nestedObserver = PickerObserver(); - - await tester.pumpWidget(MaterialApp( - navigatorObservers: [rootObserver], - home: Navigator( - observers: [nestedObserver], - onGenerateRoute: (RouteSettings settings) { - return MaterialPageRoute( - builder: (BuildContext context) { - return ElevatedButton( - onPressed: () { - showTimePicker( - context: context, - useRootNavigator: false, - initialTime: const TimeOfDay(hour: 7, minute: 0), - ); - }, - child: const Text('Show Picker'), - ); - }, - ); - }, - ), - )); - - // Open the dialog. - await tester.tap(find.byType(ElevatedButton)); - - expect(rootObserver.pickerCount, 0); - expect(nestedObserver.pickerCount, 1); - }); - - testWidgets('optional text parameters are utilized', (WidgetTester tester) async { - const String cancelText = 'Custom Cancel'; - const String confirmText = 'Custom OK'; - const String helperText = 'Custom Help'; - await tester.pumpWidget(MaterialApp( - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ElevatedButton( - child: const Text('X'), - onPressed: () async { - await showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - cancelText: cancelText, - confirmText: confirmText, - helpText: helperText, - ); - }, - ); - }, - ), - ), - ), - )); - - // Open the picker. - await tester.tap(find.text('X')); - await tester.pumpAndSettle(const Duration(seconds: 1)); - - expect(find.text(cancelText), findsOneWidget); - expect(find.text(confirmText), findsOneWidget); - expect(find.text(helperText), findsOneWidget); - }); - - testWidgets('OK Cancel button layout', (WidgetTester tester) async { - Widget buildFrame(TextDirection textDirection) { - return MaterialApp( - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ElevatedButton( - child: const Text('X'), - onPressed: () { - showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - builder: (BuildContext context, Widget? child) { - return Directionality( - textDirection: textDirection, - child: child!, - ); - }, - ); - }, - ); - }, - ), - ), - ), - ); - } - - await tester.pumpWidget(buildFrame(TextDirection.ltr)); - await tester.tap(find.text('X')); - await tester.pumpAndSettle(); - expect(tester.getBottomRight(find.text('OK')).dx, 638); - expect(tester.getBottomLeft(find.text('OK')).dx, 610); - expect(tester.getBottomRight(find.text('CANCEL')).dx, 576); - await tester.tap(find.text('OK')); - await tester.pumpAndSettle(); - - await tester.pumpWidget(buildFrame(TextDirection.rtl)); - await tester.tap(find.text('X')); - await tester.pumpAndSettle(); - expect(tester.getBottomLeft(find.text('OK')).dx, 162); - expect(tester.getBottomRight(find.text('OK')).dx, 190); - expect(tester.getBottomLeft(find.text('CANCEL')).dx, 224); - await tester.tap(find.text('OK')); - await tester.pumpAndSettle(); - }); - - testWidgets('text scale affects certain elements and not others', (WidgetTester tester) async { - await mediaQueryBoilerplate( - tester, - false, - initialTime: const TimeOfDay(hour: 7, minute: 41), - ); - - final double minutesDisplayHeight = tester.getSize(find.text('41')).height; - final double amHeight = tester.getSize(find.text('AM')).height; - - await tester.tap(find.text('OK')); // dismiss the dialog - await tester.pumpAndSettle(); - - // Verify that the time display is not affected by text scale. - await mediaQueryBoilerplate( - tester, - false, - textScaleFactor: 2.0, - initialTime: const TimeOfDay(hour: 7, minute: 41), - ); - - final double amHeight2x = tester.getSize(find.text('AM')).height; - expect(tester.getSize(find.text('41')).height, equals(minutesDisplayHeight)); - expect(amHeight2x, greaterThanOrEqualTo(amHeight * 2)); - - await tester.tap(find.text('OK')); // dismiss the dialog - await tester.pumpAndSettle(); - - // Verify that text scale for AM/PM is at most 2x. - await mediaQueryBoilerplate( - tester, - false, - textScaleFactor: 3.0, - initialTime: const TimeOfDay(hour: 7, minute: 41), - ); - - expect(tester.getSize(find.text('41')).height, equals(minutesDisplayHeight)); - expect(tester.getSize(find.text('AM')).height, equals(amHeight2x)); - }); - - group('showTimePicker avoids overlapping display features', () { - testWidgets('positioning with anchorPoint', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - builder: (BuildContext context, Widget? child) { - return MediaQuery( - // Display has a vertical hinge down the middle - data: const MediaQueryData( - size: Size(800, 600), - displayFeatures: [ - DisplayFeature( - bounds: Rect.fromLTRB(390, 0, 410, 600), - type: DisplayFeatureType.hinge, - state: DisplayFeatureState.unknown, - ), - ], - ), - child: child!, - ); - }, - home: const Center(child: Text('Test')), - ), - ); - final BuildContext context = tester.element(find.text('Test')); - - showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - anchorPoint: const Offset(1000, 0), - ); - - await tester.pumpAndSettle(); - // Should take the right side of the screen - expect(tester.getTopLeft(find.byType(TimePickerDialog)), const Offset(410.0, 0.0)); - expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800.0, 600.0)); - }); - - testWidgets('positioning with Directionality', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - builder: (BuildContext context, Widget? child) { - return MediaQuery( - // Display has a vertical hinge down the middle - data: const MediaQueryData( - size: Size(800, 600), - displayFeatures: [ - DisplayFeature( - bounds: Rect.fromLTRB(390, 0, 410, 600), - type: DisplayFeatureType.hinge, - state: DisplayFeatureState.unknown, - ), - ], - ), - child: Directionality( - textDirection: TextDirection.rtl, - child: child!, - ), - ); - }, - home: const Center(child: Text('Test')), - ), - ); - final BuildContext context = tester.element(find.text('Test')); - - // By default it should place the dialog on the right screen - showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - ); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.byType(TimePickerDialog)), const Offset(410.0, 0.0)); - expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(800.0, 600.0)); - }); - - testWidgets('positioning with defaults', (WidgetTester tester) async { - await tester.pumpWidget( - MaterialApp( - builder: (BuildContext context, Widget? child) { - return MediaQuery( - // Display has a vertical hinge down the middle - data: const MediaQueryData( - size: Size(800, 600), - displayFeatures: [ - DisplayFeature( - bounds: Rect.fromLTRB(390, 0, 410, 600), - type: DisplayFeatureType.hinge, - state: DisplayFeatureState.unknown, - ), - ], - ), - child: child!, - ); - }, - home: const Center(child: Text('Test')), - ), - ); - final BuildContext context = tester.element(find.text('Test')); - - // By default it should place the dialog on the left screen - showTimePicker( - context: context, - initialTime: const TimeOfDay(hour: 7, minute: 0), - ); - - await tester.pumpAndSettle(); - expect(tester.getTopLeft(find.byType(TimePickerDialog)), Offset.zero); - expect(tester.getBottomRight(find.byType(TimePickerDialog)), const Offset(390.0, 600.0)); - }); - }); -} - -void _testsInput() { - testWidgets('Initial entry mode is used', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input); - expect(find.byType(TextField), findsNWidgets(2)); - }); - - testWidgets('Initial time is the default', (WidgetTester tester) async { - late TimeOfDay result; - await startPicker(tester, (TimeOfDay? time) { result = time!; }, entryMode: TimePickerEntryMode.input); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 7, minute: 0))); - }); - - testWidgets('Help text is used - Input', (WidgetTester tester) async { - const String helpText = 'help'; - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, helpText: helpText); - expect(find.text(helpText), findsOneWidget); - }); - - testWidgets('Hour label text is used - Input', (WidgetTester tester) async { - const String hourLabelText = 'Custom hour label'; - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, hourLabelText: hourLabelText); - expect(find.text(hourLabelText), findsOneWidget); - }); - - - testWidgets('Minute label text is used - Input', (WidgetTester tester) async { - const String minuteLabelText = 'Custom minute label'; - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, minuteLabelText: minuteLabelText); - expect(find.text(minuteLabelText), findsOneWidget); - }); - - testWidgets('Invalid error text is used - Input', (WidgetTester tester) async { - const String errorInvalidText = 'Custom validation error'; - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, errorInvalidText: errorInvalidText); - // Input invalid time (hour) to force validation error - await tester.enterText(find.byType(TextField).first, '88'); - final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(tester.element(find.byType(TextButton).first)); - // Tap the ok button to trigger the validation error with custom translation - await tester.tap(find.text(materialLocalizations.okButtonLabel)); - await tester.pumpAndSettle(const Duration(seconds: 1)); - expect(find.text(errorInvalidText), findsOneWidget); - }); - - testWidgets('Can switch from input to dial entry mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input); - await tester.tap(find.byIcon(Icons.access_time)); - await tester.pumpAndSettle(); - expect(find.byType(TextField), findsNothing); - }); - - testWidgets('Can switch from dial to input entry mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, true); - await tester.tap(find.byIcon(Icons.keyboard)); - await tester.pumpAndSettle(); - expect(find.byType(TextField), findsWidgets); - }); - - testWidgets('Can not switch out of inputOnly mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.inputOnly); - expect(find.byType(TextField), findsWidgets); - expect(find.byIcon(Icons.access_time), findsNothing); - }); - - testWidgets('Can not switch out of dialOnly mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.dialOnly); - expect(find.byType(TextField), findsNothing); - expect(find.byIcon(Icons.keyboard), findsNothing); - }); - - testWidgets('Switching to dial entry mode triggers entry callback', (WidgetTester tester) async { - bool triggeredCallback = false; - - await mediaQueryBoilerplate(tester, true, entryMode: TimePickerEntryMode.input, onEntryModeChange: (TimePickerEntryMode mode) { - if (mode == TimePickerEntryMode.dial) { - triggeredCallback = true; - } - }); - - await tester.tap(find.byIcon(Icons.access_time)); - await tester.pumpAndSettle(); - expect(triggeredCallback, true); - }); - - testWidgets('Switching to input entry mode triggers entry callback', (WidgetTester tester) async { - bool triggeredCallback = false; - - await mediaQueryBoilerplate(tester, true, onEntryModeChange: (TimePickerEntryMode mode) { - if (mode == TimePickerEntryMode.input) { - triggeredCallback = true; - } - }); - - await tester.tap(find.byIcon(Icons.keyboard)); - await tester.pumpAndSettle(); - expect(triggeredCallback, true); - }); - - testWidgets('Can double tap hours (when selected) to enter input mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, false); - final Finder hourFinder = find.ancestor( - of: find.text('7'), - matching: find.byType(InkWell), - ); - - expect(find.byType(TextField), findsNothing); - - // Double tap the hour. - await tester.tap(hourFinder); - await tester.pump(const Duration(milliseconds: 100)); - await tester.tap(hourFinder); - await tester.pumpAndSettle(); - - expect(find.byType(TextField), findsWidgets); - }); - - testWidgets('Can not double tap hours (when not selected) to enter input mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, false); - final Finder hourFinder = find.ancestor( - of: find.text('7'), - matching: find.byType(InkWell), - ); - final Finder minuteFinder = find.ancestor( - of: find.text('00'), - matching: find.byType(InkWell), - ); - - expect(find.byType(TextField), findsNothing); - - // Switch to minutes mode. - await tester.tap(minuteFinder); - await tester.pumpAndSettle(); - - // Double tap the hour. - await tester.tap(hourFinder); - await tester.pump(const Duration(milliseconds: 100)); - await tester.tap(hourFinder); - await tester.pumpAndSettle(); - - expect(find.byType(TextField), findsNothing); - }); - - testWidgets('Can double tap minutes (when selected) to enter input mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, false); - final Finder minuteFinder = find.ancestor( - of: find.text('00'), - matching: find.byType(InkWell), - ); - - expect(find.byType(TextField), findsNothing); - - // Switch to minutes mode. - await tester.tap(minuteFinder); - await tester.pumpAndSettle(); - - // Double tap the minutes. - await tester.tap(minuteFinder); - await tester.pump(const Duration(milliseconds: 100)); - await tester.tap(minuteFinder); - await tester.pumpAndSettle(); - - expect(find.byType(TextField), findsWidgets); - }); - - testWidgets('Can not double tap minutes (when not selected) to enter input mode', (WidgetTester tester) async { - await mediaQueryBoilerplate(tester, false); - final Finder minuteFinder = find.ancestor( - of: find.text('00'), - matching: find.byType(InkWell), - ); - - expect(find.byType(TextField), findsNothing); - - // Double tap the minutes. - await tester.tap(minuteFinder); - await tester.pump(const Duration(milliseconds: 100)); - await tester.tap(minuteFinder); - await tester.pumpAndSettle(); - - expect(find.byType(TextField), findsNothing); - }); - - testWidgets('Entered text returns time', (WidgetTester tester) async { - late TimeOfDay result; - await startPicker(tester, (TimeOfDay? time) { result = time!; }, entryMode: TimePickerEntryMode.input); - await tester.enterText(find.byType(TextField).first, '9'); - await tester.enterText(find.byType(TextField).last, '12'); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); - }); - - testWidgets('Toggle to dial mode keeps selected time', (WidgetTester tester) async { - late TimeOfDay result; - await startPicker(tester, (TimeOfDay? time) { result = time!; }, entryMode: TimePickerEntryMode.input); - await tester.enterText(find.byType(TextField).first, '8'); - await tester.enterText(find.byType(TextField).last, '15'); - await tester.tap(find.byIcon(Icons.access_time)); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 8, minute: 15))); - }); - - testWidgets('Invalid text prevents dismissing', (WidgetTester tester) async { - TimeOfDay? result; - await startPicker(tester, (TimeOfDay? time) { result = time; }, entryMode: TimePickerEntryMode.input); - - // Invalid hour. - await tester.enterText(find.byType(TextField).first, '88'); - await tester.enterText(find.byType(TextField).last, '15'); - await finishPicker(tester); - expect(result, null); - - // Invalid minute. - await tester.enterText(find.byType(TextField).first, '8'); - await tester.enterText(find.byType(TextField).last, '95'); - await finishPicker(tester); - expect(result, null); - - await tester.enterText(find.byType(TextField).first, '8'); - await tester.enterText(find.byType(TextField).last, '15'); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 8, minute: 15))); - }); - - // Fixes regression that was reverted in https://github.com/flutter/flutter/pull/64094#pullrequestreview-469836378. - testWidgets('Ensure hour/minute fields are top-aligned with the separator', (WidgetTester tester) async { - await startPicker(tester, (TimeOfDay? time) { }, entryMode: TimePickerEntryMode.input); - final double hourFieldTop = tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourTextField')).dy; - final double minuteFieldTop = tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_MinuteTextField')).dy; - final double separatorTop = tester.getTopLeft(find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_StringFragment')).dy; - expect(hourFieldTop, separatorTop); - expect(minuteFieldTop, separatorTop); - }); - - testWidgets('Time Picker state restoration test - dial mode', (WidgetTester tester) async { - TimeOfDay? result; - final Offset center = (await startPicker( - tester, - (TimeOfDay? time) { result = time; }, - restorationId: 'restorable_time_picker', - ))!; - final Offset hour6 = Offset(center.dx, center.dy + 50.0); // 6:00 - final Offset min45 = Offset(center.dx - 50.0, center.dy); // 45 mins (or 9:00 hours) - - await tester.tapAt(hour6); - await tester.pump(const Duration(milliseconds: 50)); - await tester.restartAndRestore(); - await tester.tapAt(min45); - await tester.pump(const Duration(milliseconds: 50)); - final TestRestorationData restorationData = await tester.getRestorationData(); - await tester.restartAndRestore(); - // Setting to PM adds 12 hours (18:45) - await tester.tap(find.text('PM')); - await tester.pump(const Duration(milliseconds: 50)); - await tester.restartAndRestore(); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 18, minute: 45))); - - // Test restoring from before PM was selected (6:45) - await tester.restoreFrom(restorationData); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); - }); - - testWidgets('Time Picker state restoration test - input mode', (WidgetTester tester) async { - TimeOfDay? result; - await startPicker( - tester, - (TimeOfDay? time) { result = time; }, - entryMode: TimePickerEntryMode.input, - restorationId: 'restorable_time_picker', - ); - await tester.enterText(find.byType(TextField).first, '9'); - await tester.pump(const Duration(milliseconds: 50)); - await tester.restartAndRestore(); - - await tester.enterText(find.byType(TextField).last, '12'); - await tester.pump(const Duration(milliseconds: 50)); - final TestRestorationData restorationData = await tester.getRestorationData(); - await tester.restartAndRestore(); - - // Setting to PM adds 12 hours (21:12) - await tester.tap(find.text('PM')); - await tester.pump(const Duration(milliseconds: 50)); - await tester.restartAndRestore(); - - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 21, minute: 12))); - - // Restoring from before PM was set (9:12) - await tester.restoreFrom(restorationData); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); - }); - - testWidgets('Time Picker state restoration test - switching modes', (WidgetTester tester) async { - TimeOfDay? result; - final Offset center = (await startPicker( - tester, - (TimeOfDay? time) { result = time; }, - restorationId: 'restorable_time_picker', - ))!; - - final TestRestorationData restorationData = await tester.getRestorationData(); - // Switch to input mode from dial mode. - await tester.tap(find.byIcon(Icons.keyboard)); - await tester.pump(const Duration(milliseconds: 50)); - await tester.restartAndRestore(); - - // Select time using input mode controls. - await tester.enterText(find.byType(TextField).first, '9'); - await tester.enterText(find.byType(TextField).last, '12'); - await tester.pump(const Duration(milliseconds: 50)); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 9, minute: 12))); - - // Restoring from dial mode. - await tester.restoreFrom(restorationData); - final Offset hour6 = Offset(center.dx, center.dy + 50.0); // 6:00 - final Offset min45 = Offset(center.dx - 50.0, center.dy); // 45 mins (or 9:00 hours) - - await tester.tapAt(hour6); - await tester.pump(const Duration(milliseconds: 50)); - await tester.restartAndRestore(); - await tester.tapAt(min45); - await tester.pump(const Duration(milliseconds: 50)); - await finishPicker(tester); - expect(result, equals(const TimeOfDay(hour: 6, minute: 45))); - }); - - testWidgets('Can switch between hour/minute fields using keyboard input action', (WidgetTester tester) async { - await startPicker(tester, (TimeOfDay? time) { }, entryMode: TimePickerEntryMode.input); - - final Finder hourFinder = find.byType(TextField).first; - final TextField hourField = tester.widget(hourFinder); - await tester.tap(hourFinder); - expect(hourField.focusNode!.hasFocus, isTrue); - - await tester.enterText(find.byType(TextField).first, '08'); - final Finder minuteFinder = find.byType(TextField).last; - final TextField minuteField = tester.widget(minuteFinder); - expect(hourField.focusNode!.hasFocus, isFalse); - expect(minuteField.focusNode!.hasFocus, isTrue); - - expect(tester.testTextInput.setClientArgs!['inputAction'], equals('TextInputAction.done')); - await tester.testTextInput.receiveAction(TextInputAction.done); - expect(hourField.focusNode!.hasFocus, isFalse); - expect(minuteField.focusNode!.hasFocus, isFalse); - }); -} - -final Finder findDialPaint = find.descendant( - of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_Dial'), - matching: find.byWidgetPredicate((Widget w) => w is CustomPaint), -); - -class PickerObserver extends NavigatorObserver { - int pickerCount = 0; - - @override - void didPush(Route route, Route? previousRoute) { - if (route is DialogRoute) { - pickerCount++; - } - super.didPush(route, previousRoute); - } -} - -Future mediaQueryBoilerplate( - WidgetTester tester, - bool alwaysUse24HourFormat, { - TimeOfDay initialTime = const TimeOfDay(hour: 7, minute: 0), - double textScaleFactor = 1.0, - TimePickerEntryMode entryMode = TimePickerEntryMode.dial, - String? helpText, - String? hourLabelText, - String? minuteLabelText, - String? errorInvalidText, - bool accessibleNavigation = false, - EntryModeChangeCallback? onEntryModeChange, - bool tapButton = true, -}) async { - await tester.pumpWidget( - Localizations( - locale: const Locale('en', 'US'), - delegates: const >[ - DefaultMaterialLocalizations.delegate, - DefaultWidgetsLocalizations.delegate, - ], - child: MediaQuery( - data: MediaQueryData( - alwaysUse24HourFormat: alwaysUse24HourFormat, - textScaleFactor: textScaleFactor, - accessibleNavigation: accessibleNavigation, - size: tester.binding.window.physicalSize / tester.binding.window.devicePixelRatio, - ), - child: Material( - child: Directionality( - textDirection: TextDirection.ltr, - child: Navigator( - onGenerateRoute: (RouteSettings settings) { - return MaterialPageRoute(builder: (BuildContext context) { - return TextButton( - onPressed: () { - showTimePicker( - context: context, - initialTime: initialTime, - initialEntryMode: entryMode, - helpText: helpText, - hourLabelText: hourLabelText, - minuteLabelText: minuteLabelText, - errorInvalidText: errorInvalidText, - onEntryModeChanged: onEntryModeChange, - ); - }, - child: const Text('X'), - ); - }); - }, - ), - ), - ), - ), - ), - ); - if (tapButton) { - await tester.tap(find.text('X')); - } - await tester.pumpAndSettle(); -} diff --git a/packages/flutter/test/material/time_picker_theme_test.dart b/packages/flutter/test/material/time_picker_theme_test.dart index a437b72c4d2ac..12dc98c90886b 100644 --- a/packages/flutter/test/material/time_picker_theme_test.dart +++ b/packages/flutter/test/material/time_picker_theme_test.dart @@ -75,21 +75,21 @@ void main() { expect(description, [ 'backgroundColor: Color(0xffffffff)', - 'hourMinuteTextColor: Color(0xffffffff)', - 'hourMinuteColor: Color(0xffffffff)', - 'dayPeriodTextColor: Color(0xffffffff)', + 'dayPeriodBorderSide: BorderSide', 'dayPeriodColor: Color(0xffffffff)', - 'dialHandColor: Color(0xffffffff)', + 'dayPeriodShape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)', + 'dayPeriodTextColor: Color(0xffffffff)', + 'dayPeriodTextStyle: TextStyle()', 'dialBackgroundColor: Color(0xffffffff)', + 'dialHandColor: Color(0xffffffff)', 'dialTextColor: Color(0xffffffff)', 'entryModeIconColor: Color(0xffffffff)', - 'hourMinuteTextStyle: TextStyle()', - 'dayPeriodTextStyle: TextStyle()', 'helpTextStyle: TextStyle()', - 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)', + 'hourMinuteColor: Color(0xffffffff)', 'hourMinuteShape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)', - 'dayPeriodShape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)', - 'dayPeriodBorderSide: BorderSide', + 'hourMinuteTextColor: Color(0xffffffff)', + 'hourMinuteTextStyle: TextStyle()', + 'shape: RoundedRectangleBorder(BorderSide(width: 0.0, style: none), BorderRadius.zero)' ]); }); @@ -104,10 +104,11 @@ void main() { expect(dialogMaterial.shape, const RoundedRectangleBorder(borderRadius: BorderRadius.all(Radius.circular(4.0)))); final RenderBox dial = tester.firstRenderObject(find.byType(CustomPaint)); + debugPrint('Color: ${defaultTheme.colorScheme.onSurface.withOpacity(0.08)}'); expect( dial, paints - ..circle(color: defaultTheme.colorScheme.onBackground.withOpacity(0.12)) // Dial background color. + ..circle(color: defaultTheme.colorScheme.onSurface.withOpacity(0.08)) // Dial background color. ..circle(color: Color(defaultTheme.colorScheme.primary.value)), // Dial hand color. ); @@ -162,10 +163,10 @@ void main() { .copyWith(color: defaultTheme.colorScheme.onSurface), ); // ignore: avoid_dynamic_calls - final List secondaryLabels = dialPainter.secondaryLabels as List; + final List selectedLabels = dialPainter.selectedLabels as List; expect( // ignore: avoid_dynamic_calls - secondaryLabels.first.painter.text.style, + selectedLabels.first.painter.text.style, Typography.material2014().englishLike.bodyLarge! .merge(Typography.material2014().white.bodyLarge) .copyWith(color: defaultTheme.colorScheme.onPrimary), @@ -186,7 +187,7 @@ void main() { expect(pmMaterial.color, Colors.transparent); final Color expectedBorderColor = Color.alphaBlend( - defaultTheme.colorScheme.onBackground.withOpacity(0.38), + defaultTheme.colorScheme.onSurface.withOpacity(0.38), defaultTheme.colorScheme.surface, ); final Material dayPeriodMaterial = _dayPeriodMaterial(tester); @@ -220,7 +221,7 @@ void main() { final InputDecoration hourDecoration = _textField(tester, '7').decoration!; expect(hourDecoration.filled, true); - expect(hourDecoration.fillColor, defaultTheme.colorScheme.onSurface.withOpacity(0.12)); + expect(hourDecoration.fillColor, MaterialStateColor.resolveWith((Set states) => defaultTheme.colorScheme.onSurface.withOpacity(0.12))); expect(hourDecoration.enabledBorder, const OutlineInputBorder(borderSide: BorderSide(color: Colors.transparent))); expect(hourDecoration.errorBorder, OutlineInputBorder(borderSide: BorderSide(color: defaultTheme.colorScheme.error, width: 2))); expect(hourDecoration.focusedBorder, OutlineInputBorder(borderSide: BorderSide(color: defaultTheme.colorScheme.primary, width: 2))); @@ -307,10 +308,10 @@ void main() { .copyWith(color: _unselectedColor), ); // ignore: avoid_dynamic_calls - final List secondaryLabels = dialPainter.secondaryLabels as List; + final List selectedLabels = dialPainter.selectedLabels as List; expect( // ignore: avoid_dynamic_calls - secondaryLabels.first.painter.text.style, + selectedLabels.first.painter.text.style, Typography.material2014().englishLike.bodyLarge! .merge(Typography.material2014().white.bodyLarge) .copyWith(color: _selectedColor), diff --git a/packages/flutter_localizations/test/material/time_picker_test.dart b/packages/flutter_localizations/test/material/time_picker_test.dart index 4c5cd1d8e3354..5f850379c9e50 100644 --- a/packages/flutter_localizations/test/material/time_picker_test.dart +++ b/packages/flutter_localizations/test/material/time_picker_test.dart @@ -6,62 +6,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:flutter_test/flutter_test.dart'; -class _TimePickerLauncher extends StatelessWidget { - const _TimePickerLauncher({ - this.onChanged, - required this.locale, - this.entryMode = TimePickerEntryMode.dial, - }); - - final ValueChanged? onChanged; - final Locale locale; - final TimePickerEntryMode entryMode; - - @override - Widget build(BuildContext context) { - return MaterialApp( - locale: locale, - supportedLocales: [locale], - localizationsDelegates: GlobalMaterialLocalizations.delegates, - home: Material( - child: Center( - child: Builder( - builder: (BuildContext context) { - return ElevatedButton( - child: const Text('X'), - onPressed: () async { - onChanged?.call(await showTimePicker( - context: context, - initialEntryMode: entryMode, - initialTime: const TimeOfDay(hour: 7, minute: 0), - )); - }, - ); - } - ), - ), - ), - ); - } -} - -Future startPicker( - WidgetTester tester, - ValueChanged onChanged, { - Locale locale = const Locale('en', 'US'), -}) async { - await tester.pumpWidget(_TimePickerLauncher(onChanged: onChanged, locale: locale,)); - await tester.tap(find.text('X')); - await tester.pumpAndSettle(const Duration(seconds: 1)); - return tester.getCenter(find.byKey(const Key('time-picker-dial'))); -} - -Future finishPicker(WidgetTester tester) async { - final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(tester.element(find.byType(ElevatedButton))); - await tester.tap(find.text(materialLocalizations.okButtonLabel)); - await tester.pumpAndSettle(const Duration(seconds: 1)); -} - void main() { testWidgets('can localize the header in all known formats - portrait', (WidgetTester tester) async { // Ensure picker is displayed in portrait mode. @@ -213,13 +157,13 @@ void main() { }); testWidgets('can localize input mode in all known formats', (WidgetTester tester) async { + final Finder hourControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourTextField'); + final Finder minuteControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_MinuteTextField'); + final Finder dayPeriodControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodControl'); final Finder stringFragmentTextFinder = find.descendant( of: find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_StringFragment'), matching: find.byType(Text), ).first; - final Finder hourControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_HourTextField'); - final Finder minuteControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_MinuteTextField'); - final Finder dayPeriodControlFinder = find.byWidgetPredicate((Widget w) => '${w.runtimeType}' == '_DayPeriodControl'); // TODO(yjbanov): also test `HH.mm` (in_ID), `a h:mm` (ko_KR) and `HH:mm น.` (th_TH) when we have .arb files for them final List locales = [ @@ -276,6 +220,7 @@ void main() { expect(dayPeriodControlFinder, findsNothing); } await finishPicker(tester); + expect(tester.takeException(), isNot(throwsFlutterError)); } }); @@ -353,10 +298,10 @@ void main() { ); // ignore: avoid_dynamic_calls - final List secondaryLabels = dialPainter.secondaryLabels as List; + final List selectedLabels = dialPainter.selectedLabels as List; expect( // ignore: avoid_dynamic_calls - secondaryLabels.map((dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!), + selectedLabels.map((dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!), labels12To11, ); }); @@ -375,11 +320,72 @@ void main() { ); // ignore: avoid_dynamic_calls - final List secondaryLabels = dialPainter.secondaryLabels as List; + final List selectedLabels = dialPainter.selectedLabels as List; expect( // ignore: avoid_dynamic_calls - secondaryLabels.map((dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!), + selectedLabels.map((dynamic tp) => ((tp.painter as TextPainter).text! as TextSpan).text!), labels00To22TwoDigit, ); }); } + +class _TimePickerLauncher extends StatelessWidget { + const _TimePickerLauncher({ + this.onChanged, + required this.locale, + this.entryMode = TimePickerEntryMode.dial, + }); + + final ValueChanged? onChanged; + final Locale locale; + final TimePickerEntryMode entryMode; + + @override + Widget build(BuildContext context) { + return MaterialApp( + locale: locale, + supportedLocales: [locale], + localizationsDelegates: GlobalMaterialLocalizations.delegates, + home: Material( + child: Center( + child: Builder( + builder: (BuildContext context) { + return ElevatedButton( + child: const Text('X'), + onPressed: () async { + onChanged?.call(await showTimePicker( + context: context, + initialEntryMode: entryMode, + initialTime: const TimeOfDay(hour: 7, minute: 0), + )); + }, + ); + } + ), + ), + ), + ); + } +} + +Future startPicker( + WidgetTester tester, + ValueChanged onChanged, { + Locale locale = const Locale('en', 'US'), +}) async { + await tester.pumpWidget( + _TimePickerLauncher( + onChanged: onChanged, + locale: locale, + ), + ); + await tester.tap(find.text('X')); + await tester.pumpAndSettle(const Duration(seconds: 1)); + return tester.getCenter(find.byKey(const Key('time-picker-dial'))); +} + +Future finishPicker(WidgetTester tester) async { + final MaterialLocalizations materialLocalizations = MaterialLocalizations.of(tester.element(find.byType(ElevatedButton))); + await tester.tap(find.text(materialLocalizations.okButtonLabel)); + await tester.pumpAndSettle(const Duration(seconds: 1)); +} From ba917d6159b4b54dd70641ecae540462a33889c6 Mon Sep 17 00:00:00 2001 From: Daniel Flores Medina <78251680+Dev-dfm@users.noreply.github.com> Date: Wed, 14 Dec 2022 01:25:07 +0100 Subject: [PATCH 67/71] Remove workaround because issue is closed (#110854) --- .../android/app/src/main/AndroidManifest.xml | 2 -- 1 file changed, 2 deletions(-) diff --git a/dev/integration_tests/abstract_method_smoke_test/android/app/src/main/AndroidManifest.xml b/dev/integration_tests/abstract_method_smoke_test/android/app/src/main/AndroidManifest.xml index 555c2343e01a8..a84f324464a1e 100644 --- a/dev/integration_tests/abstract_method_smoke_test/android/app/src/main/AndroidManifest.xml +++ b/dev/integration_tests/abstract_method_smoke_test/android/app/src/main/AndroidManifest.xml @@ -4,8 +4,6 @@ found in the LICENSE file. --> - -